- - Selected port is missing! Please define a valid one using - System settings - panel. - + + + + No ports are detected. Please define a valid ones using + + + Selected port is missing! Please define a valid one using + + System settings + panel. + + diff --git a/modules/ui/src/app/app.component.spec.ts b/modules/ui/src/app/app.component.spec.ts index 6cc2da48d..901c08795 100644 --- a/modules/ui/src/app/app.component.spec.ts +++ b/modules/ui/src/app/app.component.spec.ts @@ -568,12 +568,17 @@ describe('AppComponent', () => { }); describe('error', () => { - describe('with error', () => { + describe('with settingMissedError with one port is missed', () => { beforeEach(() => { - component.error$ = of(true); + component.settingMissedError$ = of({ + isSettingMissed: true, + devicePortMissed: true, + internetPortMissed: false, + }); component.ngOnInit(); fixture.detectChanges(); }); + it('should have callout component', () => { const callout = compiled.querySelector('app-callout'); const calloutContent = callout?.innerHTML.trim(); @@ -583,9 +588,29 @@ describe('AppComponent', () => { }); }); - describe('with no error', () => { + describe('with settingMissedError with two ports are missed', () => { + beforeEach(() => { + component.settingMissedError$ = of({ + isSettingMissed: true, + devicePortMissed: true, + internetPortMissed: true, + }); + component.ngOnInit(); + fixture.detectChanges(); + }); + + it('should have callout component', () => { + const callout = compiled.querySelector('app-callout'); + const calloutContent = callout?.innerHTML.trim(); + + expect(callout).toBeTruthy(); + expect(calloutContent).toContain('No ports are detected.'); + }); + }); + + describe('with no settingMissedError', () => { beforeEach(() => { - component.error$ = of(false); + component.settingMissedError$ = of(null); store.overrideSelector(selectHasDevices, true); fixture.detectChanges(); }); diff --git a/modules/ui/src/app/app.component.ts b/modules/ui/src/app/app.component.ts index c8b6d8a0b..55d6ab9dc 100644 --- a/modules/ui/src/app/app.component.ts +++ b/modules/ui/src/app/app.component.ts @@ -39,7 +39,7 @@ import { updateFocusNavigation, } from './store/actions'; import { appFeatureKey } from './store/reducers'; -import { SystemInterfaces } from './model/setting'; +import { SettingMissedError, SystemInterfaces } from './model/setting'; import { GeneralSettingsComponent } from './pages/settings/general-settings.component'; import { AppStore } from './app.store'; @@ -65,12 +65,12 @@ export class AppComponent implements OnInit { selectHasConnectionSettings ); isStatusLoaded = false; - isConnectionSettingsLoaded = false; private openedSettingFromToggleBtn = true; isMenuOpen: Observable = this.store.select(selectMenuOpened); interfaces: Observable = this.store.select(selectInterfaces); - error$: Observable = this.store.select(selectError); + settingMissedError$: Observable = + this.store.select(selectError); @ViewChild('settingsDrawer') public settingsDrawer!: MatDrawer; @ViewChild('toggleSettingsBtn') public toggleSettingsBtn!: HTMLButtonElement; diff --git a/modules/ui/src/app/model/setting.ts b/modules/ui/src/app/model/setting.ts index e27f8ba8a..101ae6ded 100644 --- a/modules/ui/src/app/model/setting.ts +++ b/modules/ui/src/app/model/setting.ts @@ -22,6 +22,18 @@ export interface SystemConfig { monitor_period?: number; } +export interface InterfacesValidation { + hasSetInterfaces: boolean; + deviceValid: boolean; + internetValid: boolean; +} + +export interface SettingMissedError { + isSettingMissed: boolean; + devicePortMissed: boolean; + internetPortMissed: boolean; +} + export interface SettingOption { key: string; value: string; diff --git a/modules/ui/src/app/store/actions.ts b/modules/ui/src/app/store/actions.ts index 9b31d84ff..f4582b7d0 100644 --- a/modules/ui/src/app/store/actions.ts +++ b/modules/ui/src/app/store/actions.ts @@ -15,7 +15,11 @@ */ import { createAction, props } from '@ngrx/store'; -import { SystemConfig } from '../model/setting'; +import { + InterfacesValidation, + SettingMissedError, + SystemConfig, +} from '../model/setting'; import { SystemInterfaces } from '../model/setting'; import { Device } from '../model/device'; @@ -36,12 +40,12 @@ export const updateFocusNavigation = createAction( export const updateValidInterfaces = createAction( '[App Component] Update Valid Interfaces', - props<{ validInterfaces: boolean }>() + props<{ validInterfaces: InterfacesValidation }>() ); export const updateError = createAction( - '[App Component] Update Error', - props<{ error: boolean }>() + '[App Component] Update Setting Missed Error', + props<{ settingMissedError: SettingMissedError }>() ); // Settings diff --git a/modules/ui/src/app/store/effects.spec.ts b/modules/ui/src/app/store/effects.spec.ts index 74ce2cf82..819959c3f 100644 --- a/modules/ui/src/app/store/effects.spec.ts +++ b/modules/ui/src/app/store/effects.spec.ts @@ -77,27 +77,59 @@ describe('Effects', () => { }); describe('onValidateInterfaces$', () => { - it('should call updateError with false if interfaces are valid', done => { - actions$ = of(actions.updateValidInterfaces({ validInterfaces: true })); + it('should call updateError and set false if interfaces are not missed', done => { + actions$ = of( + actions.updateValidInterfaces({ + validInterfaces: { + hasSetInterfaces: false, + deviceValid: false, + internetValid: false, + }, + }) + ); effects.onValidateInterfaces$.subscribe(action => { - expect(action).toEqual(actions.updateError({ error: false })); + expect(action).toEqual( + actions.updateError({ + settingMissedError: { + isSettingMissed: false, + devicePortMissed: false, + internetPortMissed: false, + }, + }) + ); done(); }); }); - it('should call updateError with true if interfaces are not valid', done => { - actions$ = of(actions.updateValidInterfaces({ validInterfaces: false })); + it('should call updateError and set true if interfaces are missed', done => { + actions$ = of( + actions.updateValidInterfaces({ + validInterfaces: { + hasSetInterfaces: true, + deviceValid: false, + internetValid: false, + }, + }) + ); effects.onValidateInterfaces$.subscribe(action => { - expect(action).toEqual(actions.updateError({ error: true })); + expect(action).toEqual( + actions.updateError({ + settingMissedError: { + isSettingMissed: true, + devicePortMissed: true, + internetPortMissed: true, + }, + }) + ); done(); }); }); }); describe('checkInterfacesInConfig$', () => { - it('should call updateValidInterfaces with false if interface is no longer available', done => { + it('should call updateValidInterfaces and set deviceValid as false if device interface is no longer available', done => { actions$ = of( actions.fetchInterfacesSuccess({ interfaces: { @@ -117,13 +149,19 @@ describe('Effects', () => { effects.checkInterfacesInConfig$.subscribe(action => { expect(action).toEqual( - actions.updateValidInterfaces({ validInterfaces: false }) + actions.updateValidInterfaces({ + validInterfaces: { + hasSetInterfaces: true, + deviceValid: false, + internetValid: true, + }, + }) ); done(); }); }); - it('should call updateValidInterfaces with true if interface is available', done => { + it('should call updateValidInterfaces and set all true if interface is set and valid', done => { actions$ = of( actions.fetchInterfacesSuccess({ interfaces: { @@ -143,7 +181,13 @@ describe('Effects', () => { effects.checkInterfacesInConfig$.subscribe(action => { expect(action).toEqual( - actions.updateValidInterfaces({ validInterfaces: true }) + actions.updateValidInterfaces({ + validInterfaces: { + hasSetInterfaces: true, + deviceValid: true, + internetValid: true, + }, + }) ); done(); }); diff --git a/modules/ui/src/app/store/effects.ts b/modules/ui/src/app/store/effects.ts index 21994cded..f3b2c602a 100644 --- a/modules/ui/src/app/store/effects.ts +++ b/modules/ui/src/app/store/effects.ts @@ -40,13 +40,18 @@ export class AppEffects { }, ]) => AppActions.updateValidInterfaces({ - validInterfaces: - network != null && - // @ts-expect-error network is not null - interfaces[network.device_intf] != null && - (network.internet_intf == '' || - // @ts-expect-error network is not null - interfaces[network.internet_intf] != null), + validInterfaces: { + hasSetInterfaces: network != null, + deviceValid: + !!network && + !!network.device_intf && + !!interfaces[network.device_intf], + internetValid: + !!network && + (network?.internet_intf == '' || + (!!network.internet_intf && + !!interfaces[network.internet_intf])), + }, }) ) ) @@ -56,7 +61,18 @@ export class AppEffects { return this.actions$.pipe( ofType(AppActions.updateValidInterfaces), map(({ validInterfaces }) => - AppActions.updateError({ error: !validInterfaces }) + AppActions.updateError({ + settingMissedError: { + isSettingMissed: + validInterfaces.hasSetInterfaces && + (!validInterfaces.deviceValid || !validInterfaces.internetValid), + devicePortMissed: + validInterfaces.hasSetInterfaces && !validInterfaces.deviceValid, + internetPortMissed: + validInterfaces.hasSetInterfaces && + !validInterfaces.internetValid, + }, + }) ) ); }); diff --git a/modules/ui/src/app/store/reducers.spec.ts b/modules/ui/src/app/store/reducers.spec.ts index 2c9436b56..3fccf462f 100644 --- a/modules/ui/src/app/store/reducers.spec.ts +++ b/modules/ui/src/app/store/reducers.spec.ts @@ -94,10 +94,20 @@ describe('Reducer', () => { describe('updateError action', () => { it('should update state', () => { + const mockSettingMissedError = { + isSettingMissed: true, + devicePortMissed: true, + internetPortMissed: true, + }; const initialState = initialAppComponentState; - const action = updateError({ error: true }); + const action = updateError({ + settingMissedError: mockSettingMissedError, + }); const state = fromReducer.appComponentReducer(initialState, action); - const newState = { ...initialState, ...{ error: true } }; + const newState = { + ...initialState, + ...{ settingMissedError: mockSettingMissedError }, + }; 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 e5fc95a31..fff777847 100644 --- a/modules/ui/src/app/store/reducers.ts +++ b/modules/ui/src/app/store/reducers.ts @@ -33,9 +33,9 @@ export const appComponentReducer = createReducer( ...state, focusNavigation, })), - on(Actions.updateError, (state, { error }) => ({ + on(Actions.updateError, (state, { settingMissedError }) => ({ ...state, - error, + settingMissedError, })) ); diff --git a/modules/ui/src/app/store/selectors.spec.ts b/modules/ui/src/app/store/selectors.spec.ts index 8aa55f69f..9443829b8 100644 --- a/modules/ui/src/app/store/selectors.spec.ts +++ b/modules/ui/src/app/store/selectors.spec.ts @@ -33,7 +33,7 @@ describe('Selectors', () => { isStatusLoaded: false, devicesLength: 0, focusNavigation: false, - error: false, + settingMissedError: null, }, shared: { hasConnectionSettings: false, @@ -58,9 +58,9 @@ describe('Selectors', () => { expect(result).toEqual(false); }); - it('should select error', () => { + it('should select settingMissedError', () => { const result = selectError.projector(initialState); - expect(result).toEqual(false); + expect(result).toEqual(null); }); it('should select devices', () => { diff --git a/modules/ui/src/app/store/selectors.ts b/modules/ui/src/app/store/selectors.ts index 0367229e3..29173bcc8 100644 --- a/modules/ui/src/app/store/selectors.ts +++ b/modules/ui/src/app/store/selectors.ts @@ -54,5 +54,5 @@ export const selectDevices = createSelector( export const selectError = createSelector( selectAppState, - (state: AppState) => state.appComponent.error + (state: AppState) => state.appComponent.settingMissedError ); diff --git a/modules/ui/src/app/store/state.ts b/modules/ui/src/app/store/state.ts index e8b98ad6a..0693cdfd4 100644 --- a/modules/ui/src/app/store/state.ts +++ b/modules/ui/src/app/store/state.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import { TestrunStatus } from '../model/testrun-status'; -import { SystemInterfaces } from '../model/setting'; +import { SettingMissedError, SystemInterfaces } from '../model/setting'; import { Device } from '../model/device'; export interface AppState { @@ -29,7 +29,7 @@ export interface AppComponentState { * Indicates, if side menu should be focused on keyboard navigation after menu is opened */ focusNavigation: boolean; - error: boolean; + settingMissedError: SettingMissedError | null; isStatusLoaded: boolean; // TODO should be updated in effect when fetch status devicesLength: number; // TODO should be renamed to focusToggleSettingsBtn (true when devices.length > 0) and updated in effect when fetch device } @@ -54,7 +54,7 @@ export const initialAppComponentState: AppComponentState = { focusNavigation: false, isStatusLoaded: false, devicesLength: 0, - error: false, + settingMissedError: null, }; export const initialSharedState: SharedState = {