{ }); }); + it('#profileClicked should call openForm with profile', fakeAsync(() => { + spyOn(component, 'openForm'); + + component.profileClicked(PROFILE_MOCK); + tick(); + + expect(component.openForm).toHaveBeenCalledWith(PROFILE_MOCK); + })); + + it('#copyProfileAndOpenForm should call openForm with copy of profile', fakeAsync(() => { + spyOn(component, 'openForm'); + + component.copyProfileAndOpenForm(PROFILE_MOCK); + tick(); + + expect(component.openForm).toHaveBeenCalledWith(COPY_PROFILE_MOCK); + })); + describe('#saveProfile', () => { describe('with no profile selected', () => { beforeEach(() => { @@ -384,6 +402,7 @@ class FakeProfileItemComponent { }) class FakeProfileFormComponent { @Input() profiles!: Profile[]; + @Input() isCopyProfile!: boolean; @Input() selectedProfile!: Profile; @Input() profileFormat!: ProfileFormat[]; } diff --git a/modules/ui/src/app/pages/risk-assessment/risk-assessment.component.ts b/modules/ui/src/app/pages/risk-assessment/risk-assessment.component.ts index b6eae14e2..e30efe710 100644 --- a/modules/ui/src/app/pages/risk-assessment/risk-assessment.component.ts +++ b/modules/ui/src/app/pages/risk-assessment/risk-assessment.component.ts @@ -40,6 +40,7 @@ import { SuccessDialogComponent } from './components/success-dialog/success-dial export class RiskAssessmentComponent implements OnInit, OnDestroy { viewModel$ = this.store.viewModel$; isOpenProfileForm = false; + isCopyProfile = false; private destroy$: Subject = new Subject(); constructor( private store: RiskAssessmentStore, @@ -71,6 +72,7 @@ export class RiskAssessmentComponent implements OnInit, OnDestroy { } async copyProfileAndOpenForm(profile: Profile) { + this.isCopyProfile = true; await this.openForm(this.getCopyOfProfile(profile)); } @@ -124,7 +126,10 @@ export class RiskAssessmentComponent implements OnInit, OnDestroy { this.liveAnnouncer.clear(); if (!selectedProfile) { this.saveProfile(profile, this.store.setFocusOnCreateButton); - } else if (this.compareProfiles(profile, selectedProfile)) { + } else if ( + this.compareProfiles(profile, selectedProfile) || + this.isCopyProfile + ) { this.saveProfile(profile, this.store.setFocusOnSelectedProfile); } else { this.openSaveDialog( @@ -187,6 +192,7 @@ export class RiskAssessmentComponent implements OnInit, OnDestroy { discard(selectedProfile: Profile | null) { this.liveAnnouncer.clear(); this.isOpenProfileForm = false; + this.isCopyProfile = false; if (selectedProfile) { timer(100).subscribe(() => { this.store.setFocusOnSelectedProfile(); @@ -220,6 +226,7 @@ export class RiskAssessmentComponent implements OnInit, OnDestroy { }, }); this.isOpenProfileForm = false; + this.isCopyProfile = false; } private setFocus(index: number): void { diff --git a/modules/ui/src/app/pages/settings/components/settings-dropdown/settings-dropdown.component.scss b/modules/ui/src/app/pages/settings/components/settings-dropdown/settings-dropdown.component.scss index 16093e336..38e82686c 100644 --- a/modules/ui/src/app/pages/settings/components/settings-dropdown/settings-dropdown.component.scss +++ b/modules/ui/src/app/pages/settings/components/settings-dropdown/settings-dropdown.component.scss @@ -48,6 +48,11 @@ .setting-field { width: 100%; + + &.mat-form-field-disabled { + opacity: 0.6; + } + ::ng-deep .mat-mdc-form-field-infix { min-height: 76px; display: flex; diff --git a/modules/ui/src/app/pages/settings/settings.component.html b/modules/ui/src/app/pages/settings/settings.component.html index 089ebd5eb..9a7671171 100644 --- a/modules/ui/src/app/pages/settings/settings.component.html +++ b/modules/ui/src/app/pages/settings/settings.component.html @@ -34,13 +34,6 @@

System settings

- - Warning! Testrun requires two ports to operate correctly. - - { fixture.detectChanges(); }); - it('should have callout component', () => { - const callout = compiled.querySelector('app-callout'); - - expect(callout).toBeTruthy(); - }); - it('should have disabled "Save" button', () => { component.deviceControl.setValue( MOCK_SYSTEM_CONFIG_WITH_DATA?.network?.device_intf @@ -331,7 +324,7 @@ describe('GeneralSettingsComponent', () => { isLessThanOneInterface: false, interfaces: MOCK_INTERFACES, deviceOptions: MOCK_INTERFACES, - internetOptions: MOCK_INTERNET_OPTIONS, + internetOptions: MOCK_INTERFACES, logLevelOptions: {}, monitoringPeriodOptions: {}, }); diff --git a/modules/ui/src/app/pages/settings/settings.component.ts b/modules/ui/src/app/pages/settings/settings.component.ts index b4fb17c9e..4c9d0dffe 100644 --- a/modules/ui/src/app/pages/settings/settings.component.ts +++ b/modules/ui/src/app/pages/settings/settings.component.ts @@ -83,7 +83,14 @@ export class SettingsComponent implements OnInit, OnDestroy { } get isFormValues(): boolean { - return this.internetControl?.value.value && this.deviceControl?.value.value; + return ( + this.deviceControl?.value?.value && + (this.isInternetControlDisabled || this.internetControl?.value?.value) + ); + } + + get isInternetControlDisabled(): boolean { + return this.internetControl?.disabled; } get isFormError(): boolean { @@ -102,7 +109,7 @@ export class SettingsComponent implements OnInit, OnDestroy { this.createSettingForm(); this.cleanFormErrorMessage(); this.settingsStore.getInterfaces(); - this.settingsStore.getSystemConfig(); + this.getSystemConfig(); this.setDefaultFormValues(); } @@ -112,7 +119,7 @@ export class SettingsComponent implements OnInit, OnDestroy { } this.showLoading(); this.getSystemInterfaces(); - this.settingsStore.getSystemConfig(); + this.getSystemConfig(); this.setDefaultFormValues(); } closeSetting(message: string): void { @@ -177,7 +184,7 @@ export class SettingsComponent implements OnInit, OnDestroy { const data: SystemConfig = { network: { device_intf: device_intf.key, - internet_intf: internet_intf.key, + internet_intf: this.isInternetControlDisabled ? '' : internet_intf.key, }, log_level: log_level.key, monitor_period: Number(monitor_period.key), diff --git a/modules/ui/src/app/pages/settings/settings.store.spec.ts b/modules/ui/src/app/pages/settings/settings.store.spec.ts index b51e1f2a6..969f5cb9f 100644 --- a/modules/ui/src/app/pages/settings/settings.store.spec.ts +++ b/modules/ui/src/app/pages/settings/settings.store.spec.ts @@ -13,12 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { - DEFAULT_INTERNET_OPTION, - LOG_LEVELS, - MONITORING_PERIOD, - SettingsStore, -} from './settings.store'; +import { LOG_LEVELS, MONITORING_PERIOD, SettingsStore } from './settings.store'; import { TestRunService } from '../../services/test-run.service'; import SpyObj = jasmine.SpyObj; import { TestBed } from '@angular/core/testing'; @@ -39,11 +34,11 @@ import { MOCK_DEVICE_VALUE, MOCK_INTERFACE_VALUE, MOCK_INTERFACES, - MOCK_INTERNET_OPTIONS, MOCK_LOG_VALUE, MOCK_PERIOD_VALUE, MOCK_SYSTEM_CONFIG_WITH_DATA, MOCK_SYSTEM_CONFIG_WITH_NO_DATA, + MOCK_SYSTEM_CONFIG_WITH_SINGLE_PORT, } from '../../mocks/settings.mock'; describe('SettingsStore', () => { @@ -120,7 +115,6 @@ describe('SettingsStore', () => { expect(store.interfaces).toEqual(MOCK_INTERFACES); expect(store.deviceOptions).toEqual(MOCK_INTERFACES); expect(store.internetOptions).toEqual({ - '': 'Not specified', mockDeviceKey: 'mockDeviceValue', mockInternetKey: 'mockInternetValue', }); @@ -193,7 +187,7 @@ describe('SettingsStore', () => { settingsStore.viewModel$.pipe(skip(3), take(1)).subscribe(store => { expect(store.interfaces).toEqual(interfaces); expect(store.deviceOptions).toEqual(interfaces); - expect(store.internetOptions).toEqual(MOCK_INTERNET_OPTIONS); + expect(store.internetOptions).toEqual(interfaces); done(); }); @@ -279,6 +273,27 @@ describe('SettingsStore', () => { }); }); + describe('with single port mode', () => { + beforeEach(() => { + settingsStore.setSystemConfig(MOCK_SYSTEM_CONFIG_WITH_SINGLE_PORT); + settingsStore.setInterfaces(MOCK_INTERFACES); + }); + + it('should disable internet control', () => { + const form = fb.group({ + device_intf: ['value'], + internet_intf: [''], + log_level: [''], + monitor_period: ['value'], + }); + settingsStore.setDefaultFormValues(form); + + expect( + (form.get(FormKey.INTERNET) as FormControl).disabled + ).toBeTrue(); + }); + }); + describe('when values are empty', () => { beforeEach(() => { settingsStore.setSystemConfig(MOCK_SYSTEM_CONFIG_WITH_NO_DATA); @@ -300,7 +315,7 @@ describe('SettingsStore', () => { }); expect((form.get(FormKey.INTERNET) as FormControl).value).toEqual({ key: '', - value: DEFAULT_INTERNET_OPTION[''], + value: undefined, }); expect((form.get(FormKey.LOG_LEVEL) as FormControl).value).toEqual({ key: 'INFO', @@ -322,7 +337,6 @@ describe('SettingsStore', () => { mockNewInternetKey: 'mockNewInternetValue', }; const updateInternetOptions = { - '': 'Not specified', mockDeviceKey: 'mockDeviceValue', mockNewInternetKey: 'mockNewInternetValue', }; diff --git a/modules/ui/src/app/pages/settings/settings.store.ts b/modules/ui/src/app/pages/settings/settings.store.ts index fc4a00dc9..87071b222 100644 --- a/modules/ui/src/app/pages/settings/settings.store.ts +++ b/modules/ui/src/app/pages/settings/settings.store.ts @@ -46,10 +46,6 @@ export interface SettingsComponentState { monitoringPeriodOptions: SystemInterfaces; } -export const DEFAULT_INTERNET_OPTION = { - '': 'Not specified', -}; - export const LOG_LEVELS = { DEBUG: 'Every event will be logged', INFO: 'Normal events and issues', @@ -118,10 +114,7 @@ export class SettingsStore extends ComponentStore { ...state, interfaces, deviceOptions: interfaces, - internetOptions: { - ...DEFAULT_INTERNET_OPTION, - ...interfaces, - }, + internetOptions: interfaces, isLessThanOneInterface: Object.keys(interfaces).length < 1, }; }); @@ -180,16 +173,21 @@ export class SettingsStore extends ComponentStore { this.systemConfig$.pipe( withLatestFrom(this.deviceOptions$, this.internetOptions$), tap(([config, deviceOptions, internetOptions]) => { + if (config.single_intf) { + this.disableInternetInterface(formGroup); + } else { + this.setDefaultInternetInterfaceValue( + config.network?.internet_intf, + internetOptions, + formGroup + ); + } + this.setDefaultDeviceInterfaceValue( config.network?.device_intf, deviceOptions, formGroup ); - this.setDefaultInternetInterfaceValue( - config.network?.internet_intf, - internetOptions, - formGroup - ); this.setDefaultLogLevelValue( config.log_level, LOG_LEVELS, @@ -300,6 +298,11 @@ export class SettingsStore extends ComponentStore { ); } + private disableInternetInterface(formGroup: FormGroup) { + const internetControl = formGroup.get(FormKey.INTERNET) as FormControl; + internetControl.disable(); + } + private setDefaultValue( value: string | undefined, defaultValue: string | undefined, diff --git a/modules/ui/src/app/pages/testrun/components/testrun-status-card/testrun-status-card.component.html b/modules/ui/src/app/pages/testrun/components/testrun-status-card/testrun-status-card.component.html index be4d3b259..9ca1d80b2 100644 --- a/modules/ui/src/app/pages/testrun/components/testrun-status-card/testrun-status-card.component.html +++ b/modules/ui/src/app/pages/testrun/components/testrun-status-card/testrun-status-card.component.html @@ -27,6 +27,10 @@