+
+
+
+ No ports are detected. Please define a valid ones using
+
+
+ Selected port is missing! Please define a valid one using
+
+ System settings
+ panel.
+
+
+
+ Step 1: To perform a device test, please, select ports in
Testrun
>
panel.
-
-
- Step 1: To perform a device test, please, select ports in
- System settings
- panel.
-
-
- Step 2: To perform a device test please
- Create a Device
- first.
-
-
- Step 3: Once device is created, you are able to
- start testing.
-
-
-
-
+
+ Step 2: To perform a device test please
+ Create a Device
+ first.
+
+
+ Step 3: Once device is created, you are able to
+ start testing.
+
+
+
+
+
+
Testrun
(closeSettingEvent)="closeSetting(vm.hasDevices)">
-
+
+
+
+
+
{
let component: AppComponent;
@@ -94,6 +95,7 @@ describe('AppComponent', () => {
'fetchDevices',
'getTestModules',
'testrunInProgress',
+ 'fetchCertificates',
]);
mockFocusManagerService = jasmine.createSpyObj('mockFocusManagerService', [
@@ -113,6 +115,7 @@ describe('AppComponent', () => {
BypassComponent,
CalloutComponent,
MatIconTestingModule,
+ CertificatesComponent,
],
providers: [
{ provide: TestRunService, useValue: mockService },
@@ -648,6 +651,27 @@ describe('AppComponent', () => {
expect(spyToggle).toHaveBeenCalledTimes(0);
});
+
+ it('should render certificates button', () => {
+ const generalSettingsButton = compiled.querySelector(
+ '.app-toolbar-button-certificates'
+ );
+
+ expect(generalSettingsButton).toBeDefined();
+ });
+
+ it('should call settingsDrawer open on click settings button', () => {
+ fixture.detectChanges();
+
+ const settingsBtn = compiled.querySelector(
+ '.app-toolbar-button-certificates'
+ ) as HTMLButtonElement;
+ spyOn(component.certDrawer, 'open');
+
+ settingsBtn.click();
+
+ expect(component.certDrawer.open).toHaveBeenCalledTimes(1);
+ });
});
@Component({
diff --git a/modules/ui/src/app/app.component.ts b/modules/ui/src/app/app.component.ts
index 8b1e87cd0..4334662b3 100644
--- a/modules/ui/src/app/app.component.ts
+++ b/modules/ui/src/app/app.component.ts
@@ -54,7 +54,10 @@ export class AppComponent {
private openedSettingFromToggleBtn = true;
@ViewChild('settingsDrawer') public settingsDrawer!: MatDrawer;
+ @ViewChild('certDrawer') public certDrawer!: MatDrawer;
@ViewChild('toggleSettingsBtn') public toggleSettingsBtn!: HTMLButtonElement;
+ @ViewChild('toggleCertificatesBtn')
+ public toggleCertificatesBtn!: HTMLButtonElement;
@ViewChild('navigation') public navigation!: ElementRef;
@ViewChild('settings') public settings!: GeneralSettingsComponent;
viewModel$ = this.appStore.viewModel$;
@@ -71,6 +74,7 @@ export class AppComponent {
) {
this.appStore.getDevices();
this.appStore.getSystemStatus();
+ this.appStore.getCertificates();
this.matIconRegistry.addSvgIcon(
'devices',
this.domSanitizer.bypassSecurityTrustResourceUrl(DEVICES_LOGO_URL)
@@ -107,6 +111,10 @@ export class AppComponent {
this.appStore.setIsOpenStartTestrun();
}
+ async closeCertificates(): Promise {
+ await this.certDrawer.close();
+ }
+
async closeSetting(hasDevices: boolean): Promise {
return await this.settingsDrawer.close().then(() => {
if (hasDevices) {
@@ -146,6 +154,10 @@ export class AppComponent {
await this.settingsDrawer.open();
}
+ async openCert() {
+ await this.certDrawer.open();
+ }
+
consentShown() {
this.appStore.setContent();
}
diff --git a/modules/ui/src/app/app.module.ts b/modules/ui/src/app/app.module.ts
index 42ade0ddd..60354e593 100644
--- a/modules/ui/src/app/app.module.ts
+++ b/modules/ui/src/app/app.module.ts
@@ -44,6 +44,7 @@ import { EffectsModule } from '@ngrx/effects';
import { AppEffects } from './store/effects';
import { CdkTrapFocus } from '@angular/cdk/a11y';
import { SettingsDropdownComponent } from './pages/settings/components/settings-dropdown/settings-dropdown.component';
+import { CertificatesComponent } from './pages/certificates/certificates.component';
@NgModule({
declarations: [AppComponent, GeneralSettingsComponent],
@@ -71,6 +72,7 @@ import { SettingsDropdownComponent } from './pages/settings/components/settings-
EffectsModule.forRoot([AppEffects]),
CdkTrapFocus,
SettingsDropdownComponent,
+ CertificatesComponent,
],
providers: [
{
diff --git a/modules/ui/src/app/app.store.spec.ts b/modules/ui/src/app/app.store.spec.ts
index d53840850..a11f85943 100644
--- a/modules/ui/src/app/app.store.spec.ts
+++ b/modules/ui/src/app/app.store.spec.ts
@@ -32,6 +32,7 @@ import SpyObj = jasmine.SpyObj;
import { device } from './mocks/device.mock';
import { setDevices, setTestrunStatus } from './store/actions';
import { MOCK_PROGRESS_DATA_IN_PROGRESS } from './mocks/progress.mock';
+import { certificate } from './mocks/certificate.mock';
const mock = (() => {
let store: { [key: string]: string } = {};
@@ -57,7 +58,11 @@ describe('AppStore', () => {
let mockService: SpyObj;
beforeEach(() => {
- mockService = jasmine.createSpyObj(['fetchDevices', 'fetchSystemStatus']);
+ mockService = jasmine.createSpyObj([
+ 'fetchDevices',
+ 'fetchSystemStatus',
+ 'fetchCertificates',
+ ]);
TestBed.configureTestingModule({
providers: [
@@ -107,6 +112,15 @@ describe('AppStore', () => {
appStore.updateIsStatusLoaded(true);
});
+
+ it('should update certificates', (done: DoneFn) => {
+ appStore.viewModel$.pipe(skip(1), take(1)).subscribe(store => {
+ expect(store.certificates).toEqual([certificate]);
+ done();
+ });
+
+ appStore.updateCertificates([certificate]);
+ });
});
describe('selectors', () => {
@@ -122,6 +136,7 @@ describe('AppStore', () => {
isMenuOpen: true,
interfaces: {},
settingMissedError: null,
+ certificates: [],
});
done();
});
@@ -184,5 +199,22 @@ describe('AppStore', () => {
appStore.getSystemStatus();
});
});
+
+ describe('fetchCertificates', () => {
+ const certificates = [certificate];
+
+ beforeEach(() => {
+ mockService.fetchCertificates.and.returnValue(of(certificates));
+ });
+
+ it('should update certificates', done => {
+ appStore.viewModel$.pipe(skip(1), take(1)).subscribe(store => {
+ expect(store.certificates).toEqual(certificates);
+ done();
+ });
+
+ appStore.getCertificates();
+ });
+ });
});
});
diff --git a/modules/ui/src/app/app.store.ts b/modules/ui/src/app/app.store.ts
index d214d4848..ee5c952fe 100644
--- a/modules/ui/src/app/app.store.ts
+++ b/modules/ui/src/app/app.store.ts
@@ -38,6 +38,7 @@ import {
} from './store/actions';
import { TestrunStatus } from './model/testrun-status';
import { SettingMissedError, SystemInterfaces } from './model/setting';
+import { Certificate } from './model/certificate';
export const CONSENT_SHOWN_KEY = 'CONSENT_SHOWN';
export interface AppComponentState {
@@ -45,11 +46,13 @@ export interface AppComponentState {
isStatusLoaded: boolean;
isTestrunStarted: boolean;
systemStatus: TestrunStatus | null;
+ certificates: Certificate[];
}
@Injectable()
export class AppStore extends ComponentStore {
private consentShown$ = this.select(state => state.consentShown);
private isStatusLoaded$ = this.select(state => state.isStatusLoaded);
+ private certificates$ = this.select(state => state.certificates);
private hasDevices$ = this.store.select(selectHasDevices);
private hasConnectionSetting$ = this.store.select(
selectHasConnectionSettings
@@ -72,6 +75,7 @@ export class AppStore extends ComponentStore {
isMenuOpen: this.isMenuOpen$,
interfaces: this.interfaces$,
settingMissedError: this.settingMissedError$,
+ certificates: this.certificates$,
});
updateConsent = this.updater((state, consentShown: boolean) => ({
@@ -84,6 +88,11 @@ export class AppStore extends ComponentStore {
isStatusLoaded,
}));
+ updateCertificates = this.updater((state, certificates: Certificate[]) => ({
+ ...state,
+ certificates,
+ }));
+
setContent = this.effect(trigger$ => {
return trigger$.pipe(
tap(() => {
@@ -128,6 +137,17 @@ export class AppStore extends ComponentStore {
);
});
+ getCertificates = this.effect(trigger$ => {
+ return trigger$.pipe(
+ exhaustMap(() => {
+ return this.testRunService.fetchCertificates().pipe(
+ tap((certificates: Certificate[]) => {
+ this.updateCertificates(certificates);
+ })
+ );
+ })
+ );
+ });
constructor(
private store: Store,
private testRunService: TestRunService
@@ -137,6 +157,7 @@ export class AppStore extends ComponentStore {
isStatusLoaded: false,
isTestrunStarted: false,
systemStatus: null,
+ certificates: [],
});
}
}
diff --git a/modules/ui/src/app/mocks/certificate.mock.ts b/modules/ui/src/app/mocks/certificate.mock.ts
new file mode 100644
index 000000000..b2a38847b
--- /dev/null
+++ b/modules/ui/src/app/mocks/certificate.mock.ts
@@ -0,0 +1,22 @@
+/**
+ * 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 { Certificate } from '../model/certificate';
+
+export const certificate = {
+ name: 'iot.bms.google.com',
+ organisation: 'Google, Inc.',
+ expires: '2024-09-01T09:00:12Z',
+} as Certificate;
diff --git a/modules/ui/src/app/model/certificate.ts b/modules/ui/src/app/model/certificate.ts
new file mode 100644
index 000000000..b3abd24d6
--- /dev/null
+++ b/modules/ui/src/app/model/certificate.ts
@@ -0,0 +1,20 @@
+/**
+ * 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.
+ */
+export interface Certificate {
+ name: string;
+ organisation: string;
+ expires: string;
+}
diff --git a/modules/ui/src/app/pages/certificates/certificate-item/certificate-item.component.html b/modules/ui/src/app/pages/certificates/certificate-item/certificate-item.component.html
new file mode 100644
index 000000000..2f1a4c778
--- /dev/null
+++ b/modules/ui/src/app/pages/certificates/certificate-item/certificate-item.component.html
@@ -0,0 +1,19 @@
+