diff --git a/modules/ui/src/app/app.component.spec.ts b/modules/ui/src/app/app.component.spec.ts index e54eece26..df531c8b7 100644 --- a/modules/ui/src/app/app.component.spec.ts +++ b/modules/ui/src/app/app.component.spec.ts @@ -56,6 +56,7 @@ import { selectHasDevices, selectHasRiskProfiles, selectInterfaces, + selectInternetConnection, selectIsOpenStartTestrun, selectIsOpenWaitSnackBar, selectMenuOpened, @@ -124,10 +125,7 @@ describe('AppComponent', () => { 'focusFirstElementInContainer', ]); mockLiveAnnouncer = jasmine.createSpyObj('mockLiveAnnouncer', ['announce']); - mockMqttService = jasmine.createSpyObj([ - 'getNetworkAdapters', - 'getInternetConnection', - ]); + mockMqttService = jasmine.createSpyObj(['getNetworkAdapters']); TestBed.configureTestingModule({ imports: [ @@ -166,6 +164,7 @@ describe('AppComponent', () => { selectors: [ { selector: selectInterfaces, value: {} }, { selector: selectHasConnectionSettings, value: true }, + { selector: selectInternetConnection, value: true }, { selector: selectError, value: null }, { selector: selectMenuOpened, value: false }, { selector: selectHasDevices, value: false }, diff --git a/modules/ui/src/app/app.component.ts b/modules/ui/src/app/app.component.ts index e80f04ad0..2214b8927 100644 --- a/modules/ui/src/app/app.component.ts +++ b/modules/ui/src/app/app.component.ts @@ -83,7 +83,6 @@ export class AppComponent { this.appStore.getReports(); this.appStore.getTestModules(); this.appStore.getNetworkAdapters(); - this.appStore.getInternetConnection(); this.matIconRegistry.addSvgIcon( 'devices', this.domSanitizer.bypassSecurityTrustResourceUrl(DEVICES_LOGO_URL) diff --git a/modules/ui/src/app/app.store.spec.ts b/modules/ui/src/app/app.store.spec.ts index 7d114e8be..e26db7eb3 100644 --- a/modules/ui/src/app/app.store.spec.ts +++ b/modules/ui/src/app/app.store.spec.ts @@ -24,6 +24,7 @@ import { selectHasDevices, selectHasRiskProfiles, selectInterfaces, + selectInternetConnection, selectIsOpenWaitSnackBar, selectMenuOpened, selectReports, @@ -85,10 +86,7 @@ describe('AppStore', () => { mockFocusManagerService = jasmine.createSpyObj([ 'focusFirstElementInContainer', ]); - mockMqttService = jasmine.createSpyObj([ - 'getNetworkAdapters', - 'getInternetConnection', - ]); + mockMqttService = jasmine.createSpyObj(['getNetworkAdapters']); TestBed.configureTestingModule({ providers: [ @@ -98,6 +96,7 @@ describe('AppStore', () => { { selector: selectStatus, value: null }, { selector: selectIsOpenWaitSnackBar, value: false }, { selector: selectTestModules, value: MOCK_TEST_MODULES }, + { selector: selectInternetConnection, value: false }, ], }), { provide: TestRunService, useValue: mockService }, @@ -165,7 +164,7 @@ describe('AppStore', () => { isMenuOpen: true, interfaces: {}, settingMissedError: null, - hasInternetConnection: null, + hasInternetConnection: false, }); done(); }); @@ -307,19 +306,5 @@ describe('AppStore', () => { ); }); }); - - describe('getInternetConnection', () => { - it('should update store', done => { - mockMqttService.getInternetConnection.and.returnValue( - of({ connection: false }) - ); - appStore.getInternetConnection(); - - appStore.viewModel$.pipe(take(1)).subscribe(store => { - expect(store.hasInternetConnection).toEqual(false); - done(); - }); - }); - }); }); }); diff --git a/modules/ui/src/app/app.store.ts b/modules/ui/src/app/app.store.ts index 3584ec8d1..6e338968f 100644 --- a/modules/ui/src/app/app.store.ts +++ b/modules/ui/src/app/app.store.ts @@ -16,13 +16,14 @@ import { Injectable } from '@angular/core'; import { ComponentStore } from '@ngrx/component-store'; -import { tap, withLatestFrom } from 'rxjs/operators'; +import { tap } from 'rxjs/operators'; import { selectError, selectHasConnectionSettings, selectHasDevices, selectHasRiskProfiles, selectInterfaces, + selectInternetConnection, selectMenuOpened, selectReports, selectStatus, @@ -55,16 +56,13 @@ export const CONSENT_SHOWN_KEY = 'CONSENT_SHOWN'; export interface AppComponentState { consentShown: boolean; isStatusLoaded: boolean; - hasInternetConnection: boolean | null; systemStatus: TestrunStatus | null; } @Injectable() export class AppStore extends ComponentStore { private consentShown$ = this.select(state => state.consentShown); private isStatusLoaded$ = this.select(state => state.isStatusLoaded); - private hasInternetConnection$ = this.select( - state => state.hasInternetConnection - ); + private hasInternetConnection$ = this.store.select(selectInternetConnection); private hasDevices$ = this.store.select(selectHasDevices); private hasRiskProfiles$ = this.store.select(selectHasRiskProfiles); private reports$ = this.store.select(selectReports); @@ -102,13 +100,6 @@ export class AppStore extends ComponentStore { isStatusLoaded, })); - updateHasInternetConnection = this.updater( - (state, hasInternetConnection: boolean | null) => ({ - ...state, - hasInternetConnection, - }) - ); - setContent = this.effect(trigger$ => { return trigger$.pipe( tap(() => { @@ -170,19 +161,6 @@ export class AppStore extends ComponentStore { ); }); - getInternetConnection = this.effect(trigger$ => { - return trigger$.pipe( - exhaustMap(() => { - return this.testRunMqttService.getInternetConnection().pipe( - withLatestFrom(this.hasInternetConnection$), - tap(([{ connection }]) => { - this.updateHasInternetConnection(connection); - }) - ); - }) - ); - }); - private notifyAboutTheAdapters(adapters: SystemInterfaces) { this.notificationService.notify( `New network adapter(s) ${Object.keys(adapters).join(', ')} has been detected. You can switch to using it in the System settings menu` @@ -250,7 +228,6 @@ export class AppStore extends ComponentStore { consentShown: sessionStorage.getItem(CONSENT_SHOWN_KEY) !== null, isStatusLoaded: false, systemStatus: null, - hasInternetConnection: null, }); } } diff --git a/modules/ui/src/app/store/actions.ts b/modules/ui/src/app/store/actions.ts index 9235f980f..806618932 100644 --- a/modules/ui/src/app/store/actions.ts +++ b/modules/ui/src/app/store/actions.ts @@ -142,3 +142,8 @@ export const setTestModules = createAction( '[Shared] Set Test Modules', props<{ testModules: TestModule[] }>() ); + +export const updateInternetConnection = createAction( + '[Shared] Fetch internet connection', + props<{ internetConnection: boolean | null }>() +); diff --git a/modules/ui/src/app/store/effects.spec.ts b/modules/ui/src/app/store/effects.spec.ts index 9d7984dd7..7d33cc209 100644 --- a/modules/ui/src/app/store/effects.spec.ts +++ b/modules/ui/src/app/store/effects.spec.ts @@ -67,7 +67,10 @@ describe('Effects', () => { 'openSnackBar', ]); const mockMqttService: jasmine.SpyObj = - jasmine.createSpyObj('mockMqttService', ['getStatus']); + jasmine.createSpyObj('mockMqttService', [ + 'getStatus', + 'getInternetConnection', + ]); beforeEach(() => { testRunServiceMock = jasmine.createSpyObj('testRunServiceMock', [ @@ -88,6 +91,9 @@ describe('Effects', () => { ); testRunServiceMock.fetchProfiles.and.returnValue(of([])); testRunServiceMock.getHistory.and.returnValue(of([])); + mockMqttService.getInternetConnection.and.returnValue( + of({ connection: false }) + ); mockMqttService.getStatus.and.returnValue( of(MOCK_PROGRESS_DATA_IN_PROGRESS) @@ -445,6 +451,12 @@ describe('Effects', () => { done(); }); }); + + it('should call fetchInternetConnection for status "in progress"', () => { + effects.onFetchSystemStatusSuccess$.subscribe(() => { + expect(mockMqttService.getInternetConnection).toHaveBeenCalled(); + }); + }); }); describe('with status "waiting for device"', () => { diff --git a/modules/ui/src/app/store/effects.ts b/modules/ui/src/app/store/effects.ts index c5528c695..5fdb4d461 100644 --- a/modules/ui/src/app/store/effects.ts +++ b/modules/ui/src/app/store/effects.ts @@ -51,17 +51,20 @@ import { setStatus, setTestrunStatus, stopInterval, + updateInternetConnection, } from './actions'; import { takeUntil } from 'rxjs/internal/operators/takeUntil'; import { NotificationService } from '../services/notification.service'; import { Profile } from '../model/profile'; import { TestRunMqttService } from '../services/test-run-mqtt.service'; +import { InternetConnection } from '../model/topic'; const WAIT_TO_OPEN_SNACKBAR_MS = 60 * 1000; @Injectable() export class AppEffects { private statusSubscription: Subscription | undefined; + private internetSubscription: Subscription | undefined; private destroyWaitDeviceInterval$: Subject = new Subject(); checkInterfacesInConfig$ = createEffect(() => @@ -208,6 +211,7 @@ export class AppEffects { ofType(AppActions.stopInterval), tap(() => { this.statusSubscription?.unsubscribe(); + this.internetSubscription?.unsubscribe(); }) ); }, @@ -221,6 +225,7 @@ export class AppEffects { tap(({ systemStatus }) => { if (this.testrunService.testrunInProgress(systemStatus.status)) { this.pullingSystemStatusData(); + this.fetchInternetConnection(); } else if ( !this.testrunService.testrunInProgress(systemStatus.status) ) { @@ -358,6 +363,23 @@ export class AppEffects { } } + private fetchInternetConnection() { + if ( + this.internetSubscription === undefined || + this.internetSubscription?.closed + ) { + this.internetSubscription = this.testrunMqttService + .getInternetConnection() + .subscribe((internetConnection: InternetConnection) => { + this.store.dispatch( + updateInternetConnection({ + internetConnection: internetConnection.connection, + }) + ); + }); + } + } + constructor( private actions$: Actions, private testrunService: TestRunService, diff --git a/modules/ui/src/app/store/reducers.spec.ts b/modules/ui/src/app/store/reducers.spec.ts index 7d7d5219c..b6fe9d675 100644 --- a/modules/ui/src/app/store/reducers.spec.ts +++ b/modules/ui/src/app/store/reducers.spec.ts @@ -34,6 +34,7 @@ import { updateAdapters, updateError, updateFocusNavigation, + updateInternetConnection, } from './actions'; import { device, MOCK_TEST_MODULES } from '../mocks/device.mock'; import { MOCK_PROGRESS_DATA_CANCELLING } from '../mocks/testrun.mock'; @@ -314,4 +315,16 @@ describe('Reducer', () => { expect(state).not.toBe(initialState); }); }); + + describe('updateInternetConnection action', () => { + it('should update state', () => { + const initialState = initialSharedState; + const action = updateInternetConnection({ internetConnection: true }); + const state = fromReducer.sharedReducer(initialState, action); + const newState = { ...initialState, ...{ internetConnection: true } }; + + expect(state).toEqual(newState); + expect(state).not.toBe(initialState); + }); + }); }); diff --git a/modules/ui/src/app/store/reducers.ts b/modules/ui/src/app/store/reducers.ts index 9d8bc7cde..dfc54b11f 100644 --- a/modules/ui/src/app/store/reducers.ts +++ b/modules/ui/src/app/store/reducers.ts @@ -124,6 +124,12 @@ export const sharedReducer = createReducer( ...state, adapters, }; + }), + on(Actions.updateInternetConnection, (state, { internetConnection }) => { + return { + ...state, + internetConnection, + }; }) ); diff --git a/modules/ui/src/app/store/selectors.spec.ts b/modules/ui/src/app/store/selectors.spec.ts index b6c709c57..facc8bb74 100644 --- a/modules/ui/src/app/store/selectors.spec.ts +++ b/modules/ui/src/app/store/selectors.spec.ts @@ -33,6 +33,7 @@ import { selectStatus, selectSystemStatus, selectTestModules, + selectInternetConnection, } from './selectors'; describe('Selectors', () => { @@ -61,6 +62,7 @@ describe('Selectors', () => { reports: [], testModules: [], adapters: {}, + internetConnection: null, }, }; @@ -148,4 +150,9 @@ describe('Selectors', () => { const result = selectAdapters.projector(initialState); expect(result).toEqual({}); }); + + it('should select internetConnection', () => { + const result = selectInternetConnection.projector(initialState); + expect(result).toEqual(null); + }); }); diff --git a/modules/ui/src/app/store/selectors.ts b/modules/ui/src/app/store/selectors.ts index 9671bcd10..383fee1b9 100644 --- a/modules/ui/src/app/store/selectors.ts +++ b/modules/ui/src/app/store/selectors.ts @@ -108,3 +108,8 @@ export const selectAdapters = createSelector( selectAppState, (state: AppState) => state.shared.adapters ); + +export const selectInternetConnection = createSelector( + selectAppState, + (state: AppState) => state.shared.internetConnection +); diff --git a/modules/ui/src/app/store/state.ts b/modules/ui/src/app/store/state.ts index 63e0c5eff..76e2d3254 100644 --- a/modules/ui/src/app/store/state.ts +++ b/modules/ui/src/app/store/state.ts @@ -61,6 +61,7 @@ export interface SharedState { reports: TestrunStatus[]; testModules: TestModule[]; adapters: Adapters; + internetConnection: boolean | null; } export const initialAppComponentState: AppComponentState = { @@ -88,4 +89,5 @@ export const initialSharedState: SharedState = { reports: [], testModules: [], adapters: {}, + internetConnection: null, };