diff --git a/modules/ui/src/app/app-routing.module.ts b/modules/ui/src/app/app-routing.module.ts index ca3555322..abf61afef 100644 --- a/modules/ui/src/app/app-routing.module.ts +++ b/modules/ui/src/app/app-routing.module.ts @@ -22,6 +22,7 @@ import { RiskAssessmentComponent } from './pages/risk-assessment/risk-assessment import { CertificatesComponent } from './pages/certificates/certificates.component'; import { SettingsComponent } from './pages/settings/settings.component'; import { GeneralSettingsComponent } from './pages/general-settings/general-settings.component'; +import { CanActivateGuard } from './guards/can-activate.guard'; export const routes: Routes = [ { @@ -70,7 +71,7 @@ export const routes: Routes = [ }, { path: '', - redirectTo: 'devices', - pathMatch: 'full', + canActivate: [CanActivateGuard], + component: DevicesComponent, }, ]; diff --git a/modules/ui/src/app/guards/can-activate.guard.spec.ts b/modules/ui/src/app/guards/can-activate.guard.spec.ts new file mode 100644 index 000000000..0dd88753f --- /dev/null +++ b/modules/ui/src/app/guards/can-activate.guard.spec.ts @@ -0,0 +1,100 @@ +/** + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; +import { Store, StoreModule } from '@ngrx/store'; +import { provideMockStore, MockStore } from '@ngrx/store/testing'; + +import { CanActivateGuard } from './can-activate.guard'; +import { AppState } from '../store/state'; +import { selectSystemConfig } from '../store/selectors'; +import { Routes } from '../model/routes'; + +describe('CanActivateGuard', () => { + let guard: CanActivateGuard; + let store: MockStore; + let router: Router; + + const initialState: AppState = { + hasConnectionSettings: false, + isAllDevicesOutdated: false, + devices: [], + hasDevices: false, + hasExpiredDevices: false, + isOpenAddDevice: false, + riskProfiles: [], + hasRiskProfiles: false, + isStopTestrun: false, + isOpenWaitSnackBar: false, + isOpenStartTestrun: false, + systemStatus: null, + deviceInProgress: null, + status: null, + isTestingComplete: false, + reports: [], + testModules: [], + adapters: {}, + internetConnection: null, + interfaces: {}, + systemConfig: { network: {} }, + isOpenCreateProfile: false, + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [StoreModule.forRoot({})], + providers: [ + CanActivateGuard, + provideMockStore({ initialState }), + { + provide: Router, + useValue: { + navigate: jasmine.createSpy('navigate'), + }, + }, + ], + }); + guard = TestBed.inject(CanActivateGuard); + store = TestBed.inject(Store) as MockStore; + router = TestBed.inject(Router); + }); + + it('should be created', () => { + expect(guard).toBeTruthy(); + }); + + it('should navigate to Devices if device_intf is not empty', done => { + const mockConfig = { network: { device_intf: 'eth0' } }; + store.overrideSelector(selectSystemConfig, mockConfig); + + guard.canActivate().subscribe(canActivate => { + expect(canActivate).toBe(false); + expect(router.navigate).toHaveBeenCalledWith([Routes.Devices]); + done(); + }); + }); + + it('should navigate to Settings if device_intf is an empty string', done => { + const mockConfig = { network: { device_intf: '' } }; + store.overrideSelector(selectSystemConfig, mockConfig); + + guard.canActivate().subscribe(canActivate => { + expect(canActivate).toBe(false); + expect(router.navigate).toHaveBeenCalledWith([Routes.Settings]); + done(); + }); + }); +}); diff --git a/modules/ui/src/app/guards/can-activate.guard.ts b/modules/ui/src/app/guards/can-activate.guard.ts new file mode 100644 index 000000000..9953453af --- /dev/null +++ b/modules/ui/src/app/guards/can-activate.guard.ts @@ -0,0 +1,50 @@ +/** + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { inject, Injectable } from '@angular/core'; +import { CanActivate, Router } from '@angular/router'; +import { Observable, of } from 'rxjs'; +import { switchMap, catchError, tap, filter, take } from 'rxjs/operators'; +import { AppState } from '../store/state'; +import { Store } from '@ngrx/store'; +import { selectSystemConfig } from '../store/selectors'; +import { Routes } from '../model/routes'; + +@Injectable({ + providedIn: 'root', +}) +export class CanActivateGuard implements CanActivate { + private store = inject>(Store); + private readonly router = inject(Router); + + canActivate(): Observable { + return this.store.select(selectSystemConfig).pipe( + filter(config => config.network?.device_intf !== undefined), + tap(config => { + if (config.network?.device_intf === '') { + this.router.navigate([Routes.Settings]); + } else { + this.router.navigate([Routes.Devices]); + } + }), + take(1), + switchMap(() => of(false)), + catchError(() => { + this.router.navigate([Routes.Settings]); + return of(false); + }) + ); + } +}