diff --git a/framework/python/src/common/risk_profile.py b/framework/python/src/common/risk_profile.py
index 559117aec..7a7c179af 100644
--- a/framework/python/src/common/risk_profile.py
+++ b/framework/python/src/common/risk_profile.py
@@ -69,8 +69,7 @@ def __init__(self, profile_json=None, profile_format=None):
'r',
encoding='utf-8') as device_format_file:
device_format_json = json.load(device_format_file)
- for step in device_format_json:
- self._device_format.extend(step['questions'])
+ self._device_format = device_format_json
except (IOError, ValueError) as e:
LOGGER.error(
'An error occurred whilst loading the device profile format')
diff --git a/modules/ui/src/app/components/device-tests/device-tests.component.scss b/modules/ui/src/app/components/device-tests/device-tests.component.scss
index a70c57d1b..d0ea57461 100644
--- a/modules/ui/src/app/components/device-tests/device-tests.component.scss
+++ b/modules/ui/src/app/components/device-tests/device-tests.component.scss
@@ -35,16 +35,16 @@
margin: 20px 0 8px;
font-size: 18px;
line-height: 24px;
- color: colors.$grey-800;
+ color: colors.$on-surface-variant;
}
.device-tests-description {
margin: 0;
- font-family: variables.$font-secondary;
+ font-family: variables.$font-text;
font-size: 14px;
line-height: 20px;
letter-spacing: 0.2px;
- color: colors.$grey-800;
+ color: colors.$on-surface-variant;
}
.device-form-test-modules {
diff --git a/modules/ui/src/app/components/dynamic-form/dynamic-form.component.scss b/modules/ui/src/app/components/dynamic-form/dynamic-form.component.scss
index 96c6bfc1b..3ef463e0d 100644
--- a/modules/ui/src/app/components/dynamic-form/dynamic-form.component.scss
+++ b/modules/ui/src/app/components/dynamic-form/dynamic-form.component.scss
@@ -19,12 +19,14 @@
@use 'variables';
.field-label {
- margin: 0;
- color: colors.$grey-800;
- font-size: 18px;
+ font-family: variables.$font-text;
+ font-style: normal;
+ font-weight: 500;
+ font-size: 16px;
line-height: 24px;
- padding-top: 24px;
- padding-bottom: 16px;
+ letter-spacing: 0.1px;
+ color: colors.$on-surface-variant;
+ padding: 20px 20px 8px 16px;
display: inline-block;
&:has(+ .field-select-multiple.ng-invalid.ng-dirty) {
color: mat.get-theme-color($light-theme, error, 40);
diff --git a/modules/ui/src/app/mocks/device.mock.ts b/modules/ui/src/app/mocks/device.mock.ts
index 86ef4ffd7..19732c4c3 100644
--- a/modules/ui/src/app/mocks/device.mock.ts
+++ b/modules/ui/src/app/mocks/device.mock.ts
@@ -13,13 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import {
- Device,
- DeviceStatus,
- DeviceQuestionnaireSection,
-} from '../model/device';
+import { Device, DeviceStatus } from '../model/device';
import { ProfileRisk } from '../model/profile';
-import { FormControlType } from '../model/question';
+import { FormControlType, QuestionFormat } from '../model/question';
export const device = {
status: DeviceStatus.VALID,
@@ -71,55 +67,45 @@ export const MOCK_TEST_MODULES = [
export const MOCK_MODULES = ['Connection', 'Udmi'];
-export const DEVICES_FORM: DeviceQuestionnaireSection[] = [
+export const DEVICES_FORM: QuestionFormat[] = [
{
- step: 1,
- title: 'Step 1 title',
- description: 'Step 1 description',
- questions: [
+ question: 'What type of device is this?',
+ type: FormControlType.SELECT,
+ options: [
{
+ text: 'Building Automation Gateway',
+ risk: ProfileRisk.HIGH,
id: 1,
- question: 'What type of device is this?',
- type: FormControlType.SELECT,
- options: [
- {
- text: 'Building Automation Gateway',
- risk: ProfileRisk.HIGH,
- id: 1,
- },
- {
- text: 'IoT Gateway',
- risk: ProfileRisk.LIMITED,
- id: 2,
- },
- ],
},
{
+ text: 'IoT Gateway',
+ risk: ProfileRisk.LIMITED,
id: 2,
- question: 'Does your device process any sensitive information? ',
- type: FormControlType.SELECT,
- options: [
- {
- id: 1,
- text: 'Yes',
- risk: ProfileRisk.LIMITED,
- },
- {
- id: 2,
- text: 'No',
- risk: ProfileRisk.HIGH,
- },
- ],
+ },
+ ],
+ },
+ {
+ question: 'Does your device process any sensitive information? ',
+ type: FormControlType.SELECT,
+ options: [
+ {
+ id: 1,
+ text: 'Yes',
+ risk: ProfileRisk.LIMITED,
},
{
- id: 3,
- question: 'Please select the technology this device falls into',
- type: FormControlType.SELECT,
- options: [
- { text: 'Hardware - Access Control' },
- { text: 'Hardware - Air quality' },
- ],
+ id: 2,
+ text: 'No',
+ risk: ProfileRisk.HIGH,
},
],
},
+ {
+ question: 'Please select the technology this device falls into',
+ type: FormControlType.SELECT,
+ options: [
+ { text: 'Hardware - Access Control' },
+ { text: 'Hardware - Air quality' },
+ ],
+ },
];
diff --git a/modules/ui/src/app/model/device.ts b/modules/ui/src/app/model/device.ts
index 5f73d3c28..62100f170 100644
--- a/modules/ui/src/app/model/device.ts
+++ b/modules/ui/src/app/model/device.ts
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { QuestionFormat } from './question';
import { Question } from './profile';
export interface Device {
@@ -57,17 +56,6 @@ export enum DeviceView {
WithActions = 'with actions',
}
-export interface DeviceQuestionnaireSection {
- step: number;
- title?: string;
- description?: string;
- questions: QuestionnaireFormat[];
-}
-
-export interface QuestionnaireFormat extends QuestionFormat {
- id: number;
-}
-
export enum TestingType {
Pilot = 'Pilot Assessment',
Qualification = 'Device Qualification',
diff --git a/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.html b/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.html
index 087a3e74e..5d93022da 100644
--- a/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.html
+++ b/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.html
@@ -14,333 +14,138 @@
limitations under the License.
-->
diff --git a/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.scss b/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.scss
index 1790bf6b5..be86387a8 100644
--- a/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.scss
+++ b/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.scss
@@ -31,25 +31,27 @@ $form-min-width: 285px;
container-name: qualification-form;
display: grid;
- grid-template-rows: 1fr;
- overflow: auto;
- grid-template-columns: minmax($form-min-width, $form-max-width);
- height: 100vh;
- max-height: 978px;
+ height: 100%;
+ background: colors.$surface;
+ border-radius: 8px;
+ box-shadow:
+ 0px 4px 8px 3px rgba(60, 64, 67, 0.15),
+ 0px 1px 3px 0px rgba(60, 64, 67, 0.3);
}
.device-qualification-form {
- overflow: hidden;
+ overflow: scroll;
}
::ng-deep .device-form-test-modules {
overflow: auto;
min-height: 78px;
display: grid;
- grid-template-columns: repeat(2, 1fr);
- grid-template-rows: repeat(4, 1fr);
+ grid-template-columns: repeat(3, 1fr);
+ grid-template-rows: repeat(2, 1fr);
grid-auto-flow: column;
padding-top: 16px;
+ padding-left: 10px;
p {
margin: 8px 0;
}
@@ -99,14 +101,14 @@ $form-min-width: 285px;
}
.device-qualification-form-journey-label {
- font-family: variables.$font-secondary;
+ font-family: variables.$font-text;
font-style: normal;
- font-weight: 400;
+ font-weight: 500;
font-size: 16px;
line-height: 24px;
letter-spacing: 0.1px;
- color: colors.$grey-800;
- margin: 24px 16px 0 16px;
+ color: colors.$on-surface-variant;
+ padding: 20px 20px 8px 16px;
}
.device-qualification-form-journey-button {
@@ -118,17 +120,12 @@ $form-min-width: 285px;
}
.device-qualification-form-journey-button-label {
- font-family: variables.$font-secondary;
+ font-family: variables.$font-text;
font-style: normal;
- font-weight: 500;
- font-size: 14px;
- line-height: 20px;
+ font-weight: 400;
+ font-size: 16px;
+ line-height: 24px;
letter-spacing: 0.2px;
- color: colors.$grey-800;
-}
-
-.device-qualification-form-test-modules-container {
- padding: 0 24px;
}
.device-qualification-form-step-title {
@@ -158,22 +155,10 @@ $form-min-width: 285px;
cursor: pointer;
}
-.device-qualification-form-step-content {
- padding: 0 16px;
- overflow: scroll;
-}
-
.device-qualification-form-page {
- padding-top: 10px;
- margin-top: -10px;
display: grid;
- gap: 8px;
- height: 100%;
- overflow: hidden;
align-content: start;
- &:has(.device-qualification-form-summary-container) {
- grid-template-rows: min-content min-content 1fr min-content;
- }
+ padding: 24px 16px;
}
.device-qualification-form-summary-container {
@@ -310,6 +295,20 @@ $form-min-width: 285px;
}
}
+::ng-deep .device-tests-description {
+ padding: 0 20px;
+}
+
+::ng-deep .device-tests-title {
+ font-family: variables.$font-text;
+ font-style: normal;
+ font-weight: 500;
+ font-size: 16px !important;
+ line-height: 24px !important;
+ letter-spacing: 0.1px;
+ padding: 20px 20px 8px 16px;
+}
+
.device-qualification-form-test-modules-container-error
::ng-deep
.device-tests-title {
diff --git a/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.spec.ts b/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.spec.ts
index 2087a64ea..6e9f83148 100644
--- a/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.spec.ts
+++ b/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.spec.ts
@@ -17,7 +17,6 @@ import {
ComponentFixture,
discardPeriodicTasks,
fakeAsync,
- flush,
TestBed,
tick,
} from '@angular/core/testing';
@@ -31,7 +30,7 @@ import {
import { of } from 'rxjs';
import { NgxMaskDirective, NgxMaskPipe, provideNgxMask } from 'ngx-mask';
import { MatButtonModule } from '@angular/material/button';
-import { FormArray, ReactiveFormsModule } from '@angular/forms';
+import { ReactiveFormsModule } from '@angular/forms';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatInputModule } from '@angular/material/input';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@@ -46,7 +45,6 @@ import { MatIconTestingModule } from '@angular/material/icon/testing';
import { TestRunService } from '../../../../services/test-run.service';
import { DevicesStore } from '../../devices.store';
import { provideMockStore } from '@ngrx/store/testing';
-import { FormAction } from '../../devices.component';
import { DeviceStatus, TestingType } from '../../../../model/device';
import { Component, Input } from '@angular/core';
import { QuestionFormat } from '../../../../model/question';
@@ -68,9 +66,9 @@ describe('DeviceQualificationFromComponent', () => {
const MOCK_DEVICE = {
status: DeviceStatus.VALID,
- manufacturer: '',
- model: '',
- mac_addr: '',
+ manufacturer: 'manufacturer',
+ model: 'model',
+ mac_addr: '01:01:01:01:01:01',
test_pack: TestingType.Qualification,
type: '',
technology: '',
@@ -134,12 +132,10 @@ describe('DeviceQualificationFromComponent', () => {
component = fixture.componentInstance;
compiled = fixture.nativeElement as HTMLElement;
- component.data = {
- testModules: MOCK_TEST_MODULES,
- devices: [],
- index: 0,
- isCreate: true,
- };
+ fixture.componentRef.setInput('testModules', MOCK_TEST_MODULES);
+ fixture.componentRef.setInput('devices', []);
+ fixture.componentRef.setInput('isCreate', true);
+
testrunServiceMock.fetchQuestionnaireFormat.and.returnValue(
of(DEVICES_FORM)
);
@@ -160,180 +156,15 @@ describe('DeviceQualificationFromComponent', () => {
});
it('should fetch devices format', () => {
- fixture.detectChanges();
const getQuestionnaireFormatSpy = spyOn(
component.devicesStore,
'getQuestionnaireFormat'
);
- component.ngOnInit();
fixture.detectChanges();
expect(getQuestionnaireFormatSpy).toHaveBeenCalled();
});
- it('should close dialog on "cancel" click with do data if form has no changes', () => {
- fixture.detectChanges();
- const closeSpy = spyOn(component.dialogRef, 'close');
- const closeButton = compiled.querySelector(
- '.device-qualification-form-header-close-button'
- ) as HTMLButtonElement;
-
- closeButton?.click();
-
- expect(closeSpy).toHaveBeenCalledWith();
-
- closeSpy.calls.reset();
- });
-
- it('should close dialog on escape', fakeAsync(() => {
- const closeSpy = spyOn(component.dialogRef, 'close');
- fixture.detectChanges();
-
- keyboardEvent.next(new KeyboardEvent('keydown', { code: 'Escape' }));
-
- tick();
-
- expect(closeSpy).toHaveBeenCalledWith();
-
- closeSpy.calls.reset();
- }));
-
- it('should close dialog on submit with "Save" action', fakeAsync(() => {
- component.device = MOCK_DEVICE;
- const closeSpy = spyOn(component.dialogRef, 'close');
- fixture.detectChanges();
-
- component.submit();
- tick();
- flush();
-
- expect(closeSpy).toHaveBeenCalledWith({
- action: 'Save',
- device: MOCK_DEVICE,
- });
-
- closeSpy.calls.reset();
- }));
-
- it('should close dialog on delete with "Delete" action', fakeAsync(() => {
- const closeSpy = spyOn(component.dialogRef, 'close');
- fixture.detectChanges();
-
- component.delete();
- tick();
-
- expect(closeSpy).toHaveBeenCalledWith({
- action: 'Delete',
- device: MOCK_DEVICE,
- index: 0,
- });
-
- closeSpy.calls.reset();
- }));
-
- describe('#deviceHasNoChanges', () => {
- const deviceProps = [
- { manufacturer: 'test' },
- { model: 'test' },
- { mac_addr: 'test' },
- { test_pack: TestingType.Pilot },
- { type: 'test' },
- { technology: 'test' },
- {
- test_modules: {
- udmi: {
- enabled: false,
- },
- },
- },
- { additional_info: undefined },
- {
- additional_info: [
- { question: 'What type of device is this?', answer: 'test' },
- ],
- },
- ];
- it('should return true if devices the same', () => {
- const result = component.deviceHasNoChanges(MOCK_DEVICE, MOCK_DEVICE);
-
- expect(result).toBeTrue();
- });
-
- deviceProps.forEach(item => {
- it(`should return false if devices have different props`, () => {
- const MOCK_DEVICE_1 = { ...MOCK_DEVICE, ...item };
- const result = component.deviceHasNoChanges(MOCK_DEVICE_1, MOCK_DEVICE);
-
- expect(result).toBeFalse();
- });
- });
- });
-
- it('should trigger onResize method when window is resized ', () => {
- fixture.detectChanges();
- const spyOnResize = spyOn(component, 'onResize');
- window.dispatchEvent(new Event('resize'));
- fixture.detectChanges();
- expect(spyOnResize).toHaveBeenCalled();
- });
-
- it('#goToStep should set selected index', () => {
- fixture.detectChanges();
- component.goToStep(0);
-
- expect(component.stepper().selectedIndex).toBe(0);
- });
-
- it('should close dialog on "cancel" click', () => {
- fixture.detectChanges();
- component.manufacturer.setValue('test');
- (
- component.deviceQualificationForm.get('steps') as FormArray
- ).controls.forEach(control => control.markAsDirty());
- fixture.detectChanges();
- const closeSpy = spyOn(component.dialogRef, 'close');
- const closeButton = compiled.querySelector(
- '.device-qualification-form-header-close-button'
- ) as HTMLButtonElement;
-
- closeButton?.click();
-
- expect(closeSpy).toHaveBeenCalledWith({
- action: FormAction.Close,
- index: 0,
- device: {
- status: DeviceStatus.VALID,
- manufacturer: 'test',
- model: '',
- mac_addr: '',
- test_pack: 'Device Qualification',
- type: '',
- technology: '',
- test_modules: {
- udmi: {
- enabled: true,
- },
- connection: {
- enabled: true,
- },
- },
- additional_info: [
- { question: 'What type of device is this?', answer: '' },
- {
- question: 'Does your device process any sensitive information? ',
- answer: '',
- },
- {
- question: 'Please select the technology this device falls into',
- answer: '',
- },
- ],
- },
- });
-
- closeSpy.calls.reset();
- });
-
describe('test modules', () => {
beforeEach(() => {
fixture.detectChanges();
@@ -463,15 +294,13 @@ describe('DeviceQualificationFromComponent', () => {
});
describe('mac address', () => {
- beforeEach(() => {
- fixture.detectChanges();
- });
-
it('should not be disabled', () => {
+ fixture.detectChanges();
expect(component.mac_addr.disabled).toBeFalse();
});
it('should not contain errors when input is correct', () => {
+ fixture.detectChanges();
const macAddress: HTMLInputElement = compiled.querySelector(
'.device-qualification-form-mac-address'
) as HTMLInputElement;
@@ -488,6 +317,7 @@ describe('DeviceQualificationFromComponent', () => {
});
it('should have "pattern" error when field does not satisfy pattern', () => {
+ fixture.detectChanges();
['value', 'q01e423573c4'].forEach(value => {
const macAddress: HTMLInputElement = compiled.querySelector(
'.device-qualification-form-mac-address'
@@ -508,13 +338,10 @@ describe('DeviceQualificationFromComponent', () => {
});
it('should have "has_same_mac_address" error when MAC address is already used', () => {
- component.data = {
- testModules: MOCK_TEST_MODULES,
- devices: [device],
- index: 0,
- isCreate: true,
- };
- component.ngOnInit();
+ fixture.componentRef.setInput('testModules', MOCK_TEST_MODULES);
+ fixture.componentRef.setInput('devices', [device]);
+ fixture.componentRef.setInput('isCreate', true);
+
fixture.detectChanges();
const macAddress: HTMLInputElement = compiled.querySelector(
@@ -537,23 +364,20 @@ describe('DeviceQualificationFromComponent', () => {
describe('when device is present', () => {
beforeEach(() => {
- component.data = {
- devices: [device],
- testModules: MOCK_TEST_MODULES,
- device: {
- status: DeviceStatus.VALID,
- manufacturer: 'Delta',
- model: 'O3-DIN-CPU',
- mac_addr: '00:1e:42:35:73:c4',
- test_modules: {
- udmi: {
- enabled: true,
- },
+ fixture.componentRef.setInput('testModules', MOCK_TEST_MODULES);
+ fixture.componentRef.setInput('devices', [device]);
+ fixture.componentRef.setInput('isCreate', false);
+ fixture.componentRef.setInput('initialDevice', {
+ status: DeviceStatus.VALID,
+ manufacturer: 'Delta',
+ model: 'O3-DIN-CPU',
+ mac_addr: '00:1e:42:35:73:c4',
+ test_modules: {
+ udmi: {
+ enabled: true,
},
},
- isCreate: false,
- index: 0,
- };
+ });
});
it('should fill form values with device values', fakeAsync(() => {
@@ -579,297 +403,17 @@ describe('DeviceQualificationFromComponent', () => {
}));
});
- describe('steps', () => {
- beforeEach(() => {
+ describe('onSaveClicked', () => {
+ it('should emit device', () => {
fixture.detectChanges();
- });
-
- describe('with questionnaire', () => {
- it('should have steps', () => {
- expect(
- (component.deviceQualificationForm.get('steps') as FormArray).controls
- .length
- ).toEqual(3);
- });
- });
-
- it('should not save data when fields are empty', () => {
- const forwardButton = compiled.querySelector(
- '.form-button-forward'
- ) as HTMLButtonElement;
- const model: HTMLInputElement = compiled.querySelector(
- '.device-qualification-form-model'
- ) as HTMLInputElement;
- const manufacturer: HTMLInputElement = compiled.querySelector(
- '.device-qualification-form-manufacturer'
- ) as HTMLInputElement;
- const macAddress: HTMLInputElement = compiled.querySelector(
- '.device-qualification-form-mac-address'
- ) as HTMLInputElement;
-
- ['', ' '].forEach(value => {
- model.value = value;
- model.dispatchEvent(new Event('input'));
- manufacturer.value = value;
- manufacturer.dispatchEvent(new Event('input'));
- macAddress.value = value;
- macAddress.dispatchEvent(new Event('input'));
- forwardButton?.click();
- fixture.detectChanges();
-
- const requiredErrors = compiled.querySelectorAll('mat-error');
- expect(requiredErrors?.length).toEqual(3);
-
- requiredErrors.forEach(error => {
- expect(error?.innerHTML).toContain('required');
- });
- });
- });
-
- describe('happy flow', () => {
- beforeEach(() => {
- component.model.setValue('model');
- component.manufacturer.setValue('manufacturer');
- component.mac_addr.setValue('07:07:07:07:07:07');
- component.test_modules.setValue([true, true]);
- });
-
- it('should save device when step is changed', () => {
- const forwardButton = compiled.querySelector(
- '.form-button-forward'
- ) as HTMLButtonElement;
- forwardButton.click();
-
- expect(component.device).toEqual({
- status: DeviceStatus.VALID,
- manufacturer: 'manufacturer',
- model: 'model',
- mac_addr: '07:07:07:07:07:07',
- test_pack: TestingType.Qualification,
- type: '',
- technology: '',
- test_modules: {
- udmi: {
- enabled: true,
- },
- connection: {
- enabled: true,
- },
- },
- additional_info: [
- { question: 'What type of device is this?', answer: '' },
- {
- question: 'Does your device process any sensitive information? ',
- answer: '',
- },
- {
- question: 'Please select the technology this device falls into',
- answer: '',
- },
- ],
- });
- });
-
- describe('summary', () => {
- beforeEach(() => {
- const forwardButton = compiled.querySelector(
- '.form-button-forward'
- ) as HTMLButtonElement;
- forwardButton.click(); // will redirect to 2 step
- fixture.detectChanges();
-
- const nextForwardButton = compiled.querySelector(
- '.form-button-forward'
- ) as HTMLButtonElement;
- nextForwardButton.click(); //will redirect to summary
-
- fixture.detectChanges();
- });
-
- it('should have device item', () => {
- const item = compiled.querySelector('app-device-item');
- expect(item).toBeTruthy();
- });
-
- it('should have instructions', () => {
- const instructions = compiled.querySelector(
- '.device-qualification-form-instructions'
- );
- expect(instructions).toBeTruthy();
- });
-
- it('should not have instructions when device is editing', () => {
- component.data = {
- devices: [device],
- testModules: MOCK_TEST_MODULES,
- device: {
- status: DeviceStatus.VALID,
- manufacturer: 'Delta',
- model: 'O3-DIN-CPU',
- mac_addr: '00:1e:42:35:73:c4',
- test_modules: {
- udmi: {
- enabled: true,
- },
- },
- },
- isCreate: false,
- index: 0,
- };
- fixture.detectChanges();
-
- const instructions = compiled.querySelector(
- '.device-qualification-form-instructions'
- );
- expect(instructions).toBeNull();
- });
-
- it('should save device', () => {
- const saveSpy = spyOn(component.devicesStore, 'saveDevice');
-
- component.submit();
-
- const args = saveSpy.calls.argsFor(0);
- // @ts-expect-error config is in object
- expect(args[0].device).toEqual({
- status: DeviceStatus.VALID,
- manufacturer: 'manufacturer',
- model: 'model',
- mac_addr: '07:07:07:07:07:07',
- test_pack: 'Device Qualification',
- type: '',
- technology: '',
- test_modules: {
- connection: {
- enabled: true,
- },
- udmi: {
- enabled: true,
- },
- },
- additional_info: [
- { question: 'What type of device is this?', answer: '' },
- {
- question:
- 'Does your device process any sensitive information? ',
- answer: '',
- },
- {
- question: 'Please select the technology this device falls into',
- answer: '',
- },
- ],
- });
- expect(saveSpy).toHaveBeenCalled();
- });
-
- it('should edit device', () => {
- component.data = {
- devices: [device],
- testModules: MOCK_TEST_MODULES,
- device: {
- status: DeviceStatus.VALID,
- manufacturer: 'Delta',
- model: 'O3-DIN-CPU',
- mac_addr: '00:1e:42:35:73:c4',
- test_modules: {
- udmi: {
- enabled: true,
- },
- },
- },
- isCreate: false,
- index: 0,
- };
- fixture.detectChanges();
- const editSpy = spyOn(component.devicesStore, 'editDevice');
-
- component.submit();
-
- const args = editSpy.calls.argsFor(0);
- // @ts-expect-error config is in object
- expect(args[0].device).toEqual({
- status: DeviceStatus.VALID,
- manufacturer: 'manufacturer',
- model: 'model',
- mac_addr: '07:07:07:07:07:07',
- test_pack: 'Device Qualification',
- type: '',
- technology: '',
- test_modules: {
- connection: {
- enabled: true,
- },
- udmi: {
- enabled: true,
- },
- },
- additional_info: [
- { question: 'What type of device is this?', answer: '' },
- {
- question:
- 'Does your device process any sensitive information? ',
- answer: '',
- },
- {
- question: 'Please select the technology this device falls into',
- answer: '',
- },
- ],
- });
- expect(editSpy).toHaveBeenCalled();
- });
- });
- });
-
- describe('with errors', () => {
- beforeEach(() => {
- component.data = {
- devices: [device],
- testModules: MOCK_TEST_MODULES,
- device: {
- status: DeviceStatus.VALID,
- manufacturer: 'Delta',
- model: 'O3-DIN-CPU',
- mac_addr: '00:1e:42:35:73:c4',
- test_modules: {
- udmi: {
- enabled: true,
- },
- },
- },
- isCreate: false,
- index: 0,
- };
- component.model.setValue('');
-
- fixture.detectChanges();
- });
-
- describe('summary', () => {
- beforeEach(() => {
- const forwardButton = compiled.querySelector(
- '.form-button-forward'
- ) as HTMLButtonElement;
- forwardButton.click(); // will redirect to 2 step
- fixture.detectChanges();
-
- const nextForwardButton = compiled.querySelector(
- '.form-button-forward'
- ) as HTMLButtonElement;
- nextForwardButton.click(); //will redirect to summary
- fixture.detectChanges();
- });
-
- it('should have error message', () => {
- const error = compiled.querySelector(
- '.device-qualification-form-summary-info-description'
- );
- expect(error?.textContent?.trim()).toEqual(
- 'Please go back and correct the errors on Step 1.'
- );
- });
- });
+ const saveSpy = spyOn(component.save, 'emit');
+ component.manufacturer.setValue('manufacturer');
+ component.model.setValue('model');
+ component.mac_addr.setValue('01:01:01:01:01:01');
+ component.deviceQualificationForm.markAsDirty();
+
+ component.onSaveClicked();
+ expect(saveSpy).toHaveBeenCalledWith(MOCK_DEVICE);
});
});
});
diff --git a/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.ts b/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.ts
index 409bd982d..4cb4e15f7 100644
--- a/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.ts
+++ b/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.ts
@@ -14,14 +14,12 @@
* limitations under the License.
*/
import {
- AfterViewInit,
Component,
- ElementRef,
- HostListener,
OnDestroy,
OnInit,
- viewChild,
inject,
+ input,
+ output,
} from '@angular/core';
import {
AbstractControl,
@@ -34,16 +32,12 @@ import {
import { DeviceValidators } from '../device-form/device.validators';
import {
Device,
- DeviceQuestionnaireSection,
DeviceStatus,
DeviceView,
TestingType,
TestModule,
} from '../../../../model/device';
-import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { CommonModule } from '@angular/common';
-import { CdkStep, StepperSelectionEvent } from '@angular/cdk/stepper';
-import { StepperComponent } from '../../../../components/stepper/stepper.component';
import {
MatError,
MatFormField,
@@ -56,39 +50,21 @@ import { MatSelectModule } from '@angular/material/select';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { TextFieldModule } from '@angular/cdk/text-field';
import { NgxMaskDirective, provideNgxMask } from 'ngx-mask';
-import { MatIcon } from '@angular/material/icon';
import { MatRadioButton, MatRadioGroup } from '@angular/material/radio';
import { ProfileValidators } from '../../../risk-assessment/profile-form/profile.validators';
import { DevicesStore } from '../../devices.store';
import { DynamicFormComponent } from '../../../../components/dynamic-form/dynamic-form.component';
-import { filter, skip, Subject, takeUntil, timer } from 'rxjs';
-import { FormAction, FormResponse } from '../../devices.component';
-import { DeviceItemComponent } from '../../../../components/device-item/device-item.component';
-import { ProgramTypeIconComponent } from '../../../../components/program-type-icon/program-type-icon.component';
+import { skip, Subject, takeUntil, timer } from 'rxjs';
import { Question } from '../../../../model/profile';
-import { FormControlType } from '../../../../model/question';
-import { ProgramType } from '../../../../model/program-type';
-import { FocusManagerService } from '../../../../services/focus-manager.service';
+import { FormControlType, QuestionFormat } from '../../../../model/question';
const MAC_ADDRESS_PATTERN =
'^[\\s]*[a-fA-F0-9]{2}(?:[:][a-fA-F0-9]{2}){5}[\\s]*$';
-interface DialogData {
- title?: string;
- device?: Device;
- initialDevice?: Device;
- devices: Device[];
- testModules: TestModule[];
- index: number;
- isCreate: boolean;
-}
-
@Component({
selector: 'app-device-qualification-from',
imports: [
- CdkStep,
- StepperComponent,
MatFormField,
DeviceTestsComponent,
MatButtonModule,
@@ -101,251 +77,143 @@ interface DialogData {
MatCheckboxModule,
TextFieldModule,
NgxMaskDirective,
- MatIcon,
MatRadioGroup,
MatRadioButton,
DynamicFormComponent,
- DeviceItemComponent,
- ProgramTypeIconComponent,
],
providers: [provideNgxMask(), DevicesStore],
templateUrl: './device-qualification-from.component.html',
styleUrl: './device-qualification-from.component.scss',
})
-export class DeviceQualificationFromComponent
- implements OnInit, AfterViewInit, OnDestroy
-{
+export class DeviceQualificationFromComponent implements OnInit, OnDestroy {
+ readonly TestingType = TestingType;
+ readonly DeviceView = DeviceView;
+
private fb = inject(FormBuilder);
private deviceValidators = inject(DeviceValidators);
private profileValidators = inject(ProfileValidators);
- dialogRef =
- inject>(MatDialogRef);
- data = inject(MAT_DIALOG_DATA);
+ private destroy$: Subject = new Subject();
devicesStore = inject(DevicesStore);
- private element = inject(ElementRef);
- private focusService = inject(FocusManagerService);
- readonly FORM_HEIGHT = 993;
- readonly TestingType = TestingType;
- readonly DeviceView = DeviceView;
- readonly ProgramType = ProgramType;
- readonly stepper = viewChild.required('stepper');
- testModules: TestModule[] = [];
deviceQualificationForm: FormGroup = this.fb.group({});
- device: Device | undefined;
- format: DeviceQuestionnaireSection[] = [];
- selectedIndex: number = 0;
- typeStep = 1;
+ format: QuestionFormat[] = [];
typeQuestion = 0;
- technologyStep = 1;
technologyQuestion = 2;
- private destroy$: Subject = new Subject();
+ initialDevice = input();
+ devices = input([]);
+ testModules = input([]);
+ isCreate = input(true);
+
+ save = output();
+ delete = output();
+ cancel = output();
get model() {
- return this.getStep(0).get('model') as AbstractControl;
+ return this.deviceQualificationForm.get('model') as AbstractControl;
}
get manufacturer() {
- return this.getStep(0).get('manufacturer') as AbstractControl;
+ return this.deviceQualificationForm.get('manufacturer') as AbstractControl;
}
get mac_addr() {
- return this.getStep(0).get('mac_addr') as AbstractControl;
+ return this.deviceQualificationForm.get('mac_addr') as AbstractControl;
}
get test_pack() {
- return this.getStep(0).get('test_pack') as AbstractControl;
+ return this.deviceQualificationForm.get('test_pack') as AbstractControl;
}
get type() {
- return this.getStep(this.typeStep)?.get(
+ return this.deviceQualificationForm.get(
this.typeQuestion.toString()
) as AbstractControl;
}
get technology() {
- return this.getStep(this.technologyStep)?.get(
+ return this.deviceQualificationForm.get(
this.technologyQuestion.toString()
) as AbstractControl;
}
get test_modules() {
- return this.getStep(0).controls['test_modules'] as FormArray;
- }
-
- get formValid() {
- return (
- this.deviceQualificationForm.get('steps') as FormArray
- ).controls.every(control => (control as FormGroup).valid);
- }
-
- deviceHasNoChanges(device1: Device | undefined, device2: Device | undefined) {
- return device1 && device2 && this.compareDevices(device1, device2);
- }
-
- @HostListener('window:resize', ['$event'])
- onResize() {
- this.setDialogHeight();
- }
-
- constructor() {
- const data = this.data;
-
- this.device = data.device;
+ return this.deviceQualificationForm.controls['test_modules'] as FormArray;
}
ngOnInit(): void {
- this.createBasicStep();
- this.testModules = this.data.testModules;
+ this.createDeviceForm();
this.devicesStore.questionnaireFormat$.pipe(skip(1)).subscribe(format => {
- this.createDeviceForm(format);
this.format = format;
- format.forEach(step => {
- step.questions.forEach((question, index) => {
- // need to define the step and index of type and technology
- if (question.question.toLowerCase().includes('type')) {
- this.typeStep = step.step;
- this.typeQuestion = index;
- } else if (question.question.toLowerCase().includes('technology')) {
- this.technologyStep = step.step;
- this.technologyQuestion = index;
- }
- });
+ format.forEach((question, index) => {
+ // need to define the step and index of type and technology
+ if (question.question.toLowerCase().includes('type')) {
+ this.typeQuestion = index;
+ } else if (question.question.toLowerCase().includes('technology')) {
+ this.technologyQuestion = index;
+ }
});
timer(0)
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
- if (this.data.device) {
- this.fillDeviceForm(this.format, this.data.device!);
- }
- if (this.data.index) {
- // previous steps should be marked as interacted
- for (let i = 0; i <= this.data.index; i++) {
- this.goToStep(i);
- }
+ if (this.initialDevice()) {
+ this.fillDeviceForm(this.format, this.initialDevice()!);
}
- this.dialogRef
- .keydownEvents()
- .pipe(filter((e: KeyboardEvent) => e.code === 'Escape'))
- .subscribe(() => {
- this.closeForm();
- });
});
});
this.devicesStore.getQuestionnaireFormat();
}
-
- ngAfterViewInit() {
- //set static height for better UX
- this.element.nativeElement.style.height =
- this.element.nativeElement.offsetHeight + 'px';
- }
-
ngOnDestroy() {
this.destroy$.next(true);
this.destroy$.unsubscribe();
}
- submit(): void {
- this.updateDevice(this.device!, () => {
- this.dialogRef.close({
- action: FormAction.Save,
- device: this.device,
- } as FormResponse);
- });
+ onSaveClicked(): void {
+ this.save.emit(this.createDeviceFromForm());
}
- delete(): void {
- this.dialogRef.close({
- action: FormAction.Delete,
- device: this.createDeviceFromForm(),
- index: this.stepper().selectedIndex,
- } as FormResponse);
+ onCancelClicked(): void {
+ this.cancel.emit();
}
- closeForm(): void {
- const device1 = this.data.initialDevice;
- const device2 = this.createDeviceFromForm();
- if (
- (device1 && device2 && this.compareDevices(device1, device2)) ||
- (!device1 && this.deviceIsEmpty(device2))
- ) {
- this.dialogRef.close();
- } else {
- this.dialogRef.close({
- action: FormAction.Close,
- device: this.createDeviceFromForm(),
- index: this.stepper().selectedIndex,
- } as FormResponse);
- }
- }
-
- getStep(step: number) {
- return (this.deviceQualificationForm.get('steps') as FormArray).controls[
- step
- ] as FormGroup;
- }
-
- onStepChange(event: StepperSelectionEvent) {
- this.focusService.focusFirstElementInContainer();
- if (event.previouslySelectedStep.completed) {
- this.device = this.createDeviceFromForm();
- }
+ onDeleteClick(): void {
+ this.delete.emit(this.initialDevice()!);
}
- getErrorSteps(): number[] {
- const steps: number[] = [];
- (this.deviceQualificationForm.get('steps') as FormArray).controls.forEach(
- (control, index) => {
- if (!control.valid) steps.push(index);
- }
- );
- return steps;
- }
-
- goToStep(index: number, event?: Event) {
- event?.preventDefault();
- this.stepper().selectedIndex = index;
- }
-
- private fillDeviceForm(
- format: DeviceQuestionnaireSection[],
- device: Device
- ): void {
- format.forEach(step => {
- step.questions.forEach((question, index) => {
- const answer = device.additional_info?.find(
- answers => answers.question === question.question
- )?.answer;
- if (answer !== undefined && answer !== null && answer !== '') {
- if (question.type === FormControlType.SELECT_MULTIPLE) {
- question.options?.forEach((item, idx) => {
- if ((answer as number[])?.includes(idx)) {
- (
- this.getStep(step.step).get(index.toString()) as FormGroup
- ).controls[idx].setValue(true);
- } else {
- (
- this.getStep(step.step).get(index.toString()) as FormGroup
- ).controls[idx].setValue(false);
- }
- });
- } else {
- (
- this.getStep(step.step).get(index.toString()) as AbstractControl
- ).setValue(answer || '');
- }
+ private fillDeviceForm(format: QuestionFormat[], device: Device): void {
+ format.forEach((question, index) => {
+ const answer = device.additional_info?.find(
+ answers => answers.question === question.question
+ )?.answer;
+ if (answer !== undefined && answer !== null && answer !== '') {
+ if (question.type === FormControlType.SELECT_MULTIPLE) {
+ question.options?.forEach((item, idx) => {
+ if ((answer as number[])?.includes(idx)) {
+ (
+ this.deviceQualificationForm.get(index.toString()) as FormGroup
+ ).controls[idx].setValue(true);
+ } else {
+ (
+ this.deviceQualificationForm.get(index.toString()) as FormGroup
+ ).controls[idx].setValue(false);
+ }
+ });
} else {
(
- this.getStep(step.step)?.get(index.toString()) as AbstractControl
- )?.markAsTouched();
+ this.deviceQualificationForm.get(
+ index.toString()
+ ) as AbstractControl
+ ).setValue(answer || '');
}
- });
+ } else {
+ (
+ this.deviceQualificationForm.get(index.toString()) as AbstractControl
+ )?.markAsTouched();
+ }
});
this.model.setValue(device.model);
this.manufacturer.setValue(device.manufacturer);
@@ -365,23 +233,11 @@ export class DeviceQualificationFromComponent
this.technology?.setValue(device.technology);
}
- private updateDevice(device: Device, callback: () => void) {
- if (!this.data.isCreate && this.data.device) {
- this.devicesStore.editDevice({
- device,
- mac_addr: this.data.device.mac_addr,
- onSuccess: callback,
- });
- } else {
- this.devicesStore.saveDevice({ device, onSuccess: callback });
- }
- }
-
private createDeviceFromForm(): Device {
const testModules: { [key: string]: { enabled: boolean } } = {};
- this.getStep(0).value.test_modules.forEach(
+ this.deviceQualificationForm.value.test_modules.forEach(
(enabled: boolean, i: number) => {
- testModules[this.testModules[i]?.name] = {
+ testModules[this.testModules()[i]?.name] = {
enabled: enabled,
};
}
@@ -389,25 +245,23 @@ export class DeviceQualificationFromComponent
const additionalInfo: Question[] = [];
- this.format.forEach(step => {
- step.questions.forEach((question, index) => {
- const response: Question = {};
- response.question = question.question;
+ this.format.forEach((question, index) => {
+ const response: Question = {};
+ response.question = question.question;
- if (question.type === FormControlType.SELECT_MULTIPLE) {
- const answer: number[] = [];
- question.options?.forEach((_, idx) => {
- const value = this.getStep(step.step).value[index][idx];
- if (value) {
- answer.push(idx);
- }
- });
- response.answer = answer;
- } else {
- response.answer = this.getStep(step.step).value[index]?.trim();
- }
- additionalInfo.push(response);
- });
+ if (question.type === FormControlType.SELECT_MULTIPLE) {
+ const answer: number[] = [];
+ question.options?.forEach((_, idx) => {
+ const value = this.deviceQualificationForm.value[index][idx];
+ if (value) {
+ answer.push(idx);
+ }
+ });
+ response.answer = answer;
+ } else {
+ response.answer = this.deviceQualificationForm.value[index]?.trim();
+ }
+ additionalInfo.push(response);
});
return {
@@ -423,8 +277,8 @@ export class DeviceQualificationFromComponent
} as Device;
}
- private createBasicStep() {
- const firstStep = this.fb.group({
+ private createDeviceForm() {
+ this.deviceQualificationForm = this.fb.group({
model: [
'',
[
@@ -445,8 +299,8 @@ export class DeviceQualificationFromComponent
this.profileValidators.textRequired(),
Validators.pattern(MAC_ADDRESS_PATTERN),
this.deviceValidators.differentMACAddress(
- this.data.devices,
- this.data.device
+ this.devices(),
+ this.initialDevice()
),
],
],
@@ -456,121 +310,5 @@ export class DeviceQualificationFromComponent
),
test_pack: [TestingType.Qualification],
});
-
- this.deviceQualificationForm = this.fb.group({
- steps: this.fb.array([firstStep]),
- });
- }
-
- private createDeviceForm(format: DeviceQuestionnaireSection[]) {
- format.forEach(() => {
- (this.deviceQualificationForm.get('steps') as FormArray).controls.push(
- this.createStep()
- );
- });
-
- // summary step
- (this.deviceQualificationForm.get('steps') as FormArray).controls.push(
- this.fb.group({})
- );
- }
-
- private createStep() {
- return new FormGroup({});
- }
-
- private compareDevices(device1: Device, device2: Device) {
- if (device1.manufacturer !== device2.manufacturer) {
- return false;
- }
- if (device1.model !== device2.model) {
- return false;
- }
- if (device1.mac_addr !== device2.mac_addr) {
- return false;
- }
- if (device1.type !== device2.type) {
- return false;
- }
- if (device1.technology !== device2.technology) {
- return false;
- }
- if (device1.test_pack !== device2.test_pack) {
- return false;
- }
- const keys1 = Object.keys(device1.test_modules!);
-
- for (const key of keys1) {
- const val1 = device1.test_modules![key];
- const val2 = device2.test_modules![key];
- if (val1?.enabled !== val2?.enabled) {
- return false;
- }
- }
-
- if (device1.additional_info) {
- for (const question of device1.additional_info) {
- if (
- question.answer !==
- device2.additional_info?.find(
- question2 => question2.question === question.question
- )?.answer
- ) {
- return false;
- }
- }
- } else {
- return false;
- }
- return true;
- }
-
- private deviceIsEmpty(device: Device) {
- if (device.manufacturer !== '') {
- return false;
- }
- if (device.model !== '') {
- return false;
- }
- if (device.mac_addr !== '') {
- return false;
- }
- if (device.type !== '') {
- return false;
- }
- if (device.technology !== '') {
- return false;
- }
- if (device.test_pack !== TestingType.Qualification) {
- return false;
- }
- const keys1 = Object.keys(device.test_modules!);
-
- for (const key of keys1) {
- const val1 = device.test_modules![key];
- if (!val1.enabled) {
- return false;
- }
- }
-
- if (device.additional_info) {
- for (const question of device.additional_info) {
- if (question.answer !== '') {
- return false;
- }
- }
- } else {
- return false;
- }
- return true;
- }
-
- private setDialogHeight(): void {
- const windowHeight = window.innerHeight;
- if (windowHeight < this.FORM_HEIGHT) {
- this.element.nativeElement.style.height = '100vh';
- } else {
- this.element.nativeElement.style.height = this.FORM_HEIGHT + 'px';
- }
}
}
diff --git a/modules/ui/src/app/pages/devices/devices.component.html b/modules/ui/src/app/pages/devices/devices.component.html
index 28fb015db..73c56c628 100644
--- a/modules/ui/src/app/pages/devices/devices.component.html
+++ b/modules/ui/src/app/pages/devices/devices.component.html
@@ -22,15 +22,21 @@
[itemTemplate]="itemTemplate"
[actions]="vm.actions"
[entities]="vm.devices"
- (addEntity)="openDialog(vm.devices, vm.testModules)"
+ (addEntity)="openForm()"
(menuItemClicked)="menuItemClicked($event, vm.devices, vm.testModules)">
+
@@ -44,7 +50,7 @@
diff --git a/modules/ui/src/app/pages/devices/devices.component.spec.ts b/modules/ui/src/app/pages/devices/devices.component.spec.ts
index 9c46d8c81..59abc278d 100644
--- a/modules/ui/src/app/pages/devices/devices.component.spec.ts
+++ b/modules/ui/src/app/pages/devices/devices.component.spec.ts
@@ -22,7 +22,7 @@ import {
import { of } from 'rxjs';
import { Device, DeviceAction } from '../../model/device';
-import { DevicesComponent, FormAction } from './devices.component';
+import { DevicesComponent } from './devices.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatDialogRef } from '@angular/material/dialog';
import { device, MOCK_TEST_MODULES } from '../../mocks/device.mock';
@@ -37,7 +37,6 @@ import { Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { Component } from '@angular/core';
import { MOCK_PROGRESS_DATA_IN_PROGRESS } from '../../mocks/testrun.mock';
-import { DeviceQualificationFromComponent } from './components/device-qualification-from/device-qualification-from.component';
describe('DevicesComponent', () => {
let component: DevicesComponent;
@@ -115,12 +114,10 @@ describe('DevicesComponent', () => {
expect(button).toBeTruthy();
});
- it('should open the modal if isOpenAddDevice$ as true', () => {
- const openDialogSpy = spyOn(component, 'openDialog');
-
+ it('should open form if isOpenAddDevice$ as true', () => {
component.ngOnInit();
- expect(openDialogSpy).toHaveBeenCalled();
+ expect(component.isOpenDeviceForm).toBeTrue();
});
});
@@ -148,11 +145,7 @@ describe('DevicesComponent', () => {
expect(item.length).toEqual(3);
}));
- it('should open device dialog on "add device button" click', () => {
- const openSpy = spyOn(component.dialog, 'open').and.returnValue({
- afterClosed: () => of(true),
- beforeClosed: () => of(true),
- } as MatDialogRef);
+ it('should open form on "add device button" click', () => {
fixture.detectChanges();
const button = compiled.querySelector(
'.add-entity-button'
@@ -160,56 +153,7 @@ describe('DevicesComponent', () => {
button?.click();
expect(button).toBeTruthy();
- expect(openSpy).toHaveBeenCalled();
- expect(openSpy).toHaveBeenCalledWith(DeviceQualificationFromComponent, {
- ariaLabel: 'Create Device',
- data: {
- device: null,
- initialDevice: undefined,
- title: 'Create Device',
- testModules: [],
- devices: [device, device, device],
- index: 0,
- isCreate: true,
- },
- autoFocus: 'first-tabbable',
- hasBackdrop: true,
- disableClose: true,
- panelClass: 'device-form-dialog',
- });
-
- openSpy.calls.reset();
- });
-
- describe('#openDialog', () => {
- it('should open device dialog on item click', () => {
- const openSpy = spyOn(component.dialog, 'open').and.returnValue({
- beforeClosed: () => of(true),
- } as MatDialogRef);
- fixture.detectChanges();
-
- component.openDialog([device], MOCK_TEST_MODULES, device, device, true);
-
- expect(openSpy).toHaveBeenCalled();
- expect(openSpy).toHaveBeenCalledWith(DeviceQualificationFromComponent, {
- ariaLabel: 'Edit device',
- data: {
- device: device,
- initialDevice: device,
- title: 'Edit device',
- devices: [device],
- testModules: MOCK_TEST_MODULES,
- index: 0,
- isCreate: false,
- },
- autoFocus: 'first-tabbable',
- hasBackdrop: true,
- disableClose: true,
- panelClass: 'device-form-dialog',
- });
-
- openSpy.calls.reset();
- });
+ expect(component.isOpenDeviceForm).toBeTrue();
});
it('should disable device if deviceInProgress is exist', () => {
@@ -219,16 +163,6 @@ describe('DevicesComponent', () => {
});
});
- it('should call setIsOpenAddDevice if dialog closes with null', () => {
- spyOn(component.dialog, 'open').and.returnValue({
- beforeClosed: () => of(null),
- } as MatDialogRef);
-
- component.openDialog([], MOCK_TEST_MODULES);
-
- expect(mockDevicesStore.setIsOpenAddDevice).toHaveBeenCalled();
- });
-
describe('close dialog', () => {
beforeEach(() => {
component.viewModel$ = of({
@@ -252,47 +186,6 @@ describe('DevicesComponent', () => {
expect(item.length).toEqual(3);
}));
-
- it('should open device dialog when dialog return null', () => {
- const openDeviceDialogSpy = spyOn(component, 'openDialog');
- spyOn(component.dialog, 'open').and.returnValue({
- beforeClosed: () => of(null),
- } as MatDialogRef);
-
- component.openCloseDialog(
- [device],
- MOCK_TEST_MODULES,
- device,
- undefined,
- false,
- 0,
- 0
- );
-
- expect(openDeviceDialogSpy).toHaveBeenCalledWith(
- [device],
- MOCK_TEST_MODULES,
- device,
- undefined,
- false,
- 0,
- 0
- );
- });
- });
-
- it('should delete device if dialog closes with object, action delete and selected device', () => {
- spyOn(component.dialog, 'open').and.returnValue({
- beforeClosed: () =>
- of({
- device,
- action: FormAction.Delete,
- }),
- } as MatDialogRef);
-
- component.openDialog([device], MOCK_TEST_MODULES, device);
-
- expect(mockDevicesStore.deleteDevice).toHaveBeenCalled();
});
describe('delete device dialog', () => {
@@ -318,7 +211,7 @@ describe('DevicesComponent', () => {
beforeClosed: () => of(true),
} as MatDialogRef);
- component.openDeleteDialog([device], MOCK_TEST_MODULES, device, 0);
+ component.openDeleteDialog(device, 0);
const args = mockDevicesStore.deleteDevice.calls.argsFor(0);
// @ts-expect-error config is in object
diff --git a/modules/ui/src/app/pages/devices/devices.component.ts b/modules/ui/src/app/pages/devices/devices.component.ts
index c71e571db..29f24e0f4 100644
--- a/modules/ui/src/app/pages/devices/devices.component.ts
+++ b/modules/ui/src/app/pages/devices/devices.component.ts
@@ -21,11 +21,7 @@ import {
ChangeDetectorRef,
inject,
} from '@angular/core';
-import {
- MatDialog,
- MatDialogModule,
- MatDialogRef,
-} from '@angular/material/dialog';
+import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import {
Device,
DeviceAction,
@@ -33,7 +29,7 @@ import {
DeviceView,
TestModule,
} from '../../model/device';
-import { map, Subject, takeUntil, timer } from 'rxjs';
+import { Subject, takeUntil, timer } from 'rxjs';
import { SimpleDialogComponent } from '../../components/simple-dialog/simple-dialog.component';
import { combineLatest } from 'rxjs/internal/observable/combineLatest';
import { FocusManagerService } from '../../services/focus-manager.service';
@@ -42,8 +38,6 @@ import { Router } from '@angular/router';
import { TestrunInitiateFormComponent } from '../testrun/components/testrun-initiate-form/testrun-initiate-form.component';
import { DevicesStore } from './devices.store';
import { DeviceQualificationFromComponent } from './components/device-qualification-from/device-qualification-from.component';
-import { Observable } from 'rxjs/internal/Observable';
-import { CanComponentDeactivate } from '../../guards/can-deactivate.guard';
import { CommonModule } from '@angular/common';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatButtonModule } from '@angular/material/button';
@@ -57,18 +51,7 @@ import { EmptyPageComponent } from '../../components/empty-page/empty-page.compo
import { ListLayoutComponent } from '../../components/list-layout/list-layout.component';
import { EntityActionResult } from '../../model/entity-action';
import { NoEntitySelectedComponent } from '../../components/no-entity-selected/no-entity-selected.component';
-
-export enum FormAction {
- Delete = 'Delete',
- Close = 'Close',
- Save = 'Save',
-}
-
-export interface FormResponse {
- device?: Device;
- action: FormAction;
- index: number;
-}
+import { LiveAnnouncer } from '@angular/cdk/a11y';
@Component({
selector: 'app-device-repository',
@@ -88,38 +71,36 @@ export interface FormResponse {
EmptyPageComponent,
ListLayoutComponent,
NoEntitySelectedComponent,
+ DeviceQualificationFromComponent,
],
providers: [DevicesStore],
})
-export class DevicesComponent
- implements OnInit, OnDestroy, CanComponentDeactivate
-{
+export class DevicesComponent implements OnInit, OnDestroy {
+ readonly DeviceView = DeviceView;
private readonly focusManagerService = inject(FocusManagerService);
+ private readonly changeDetectorRef = inject(ChangeDetectorRef);
+ private readonly liveAnnouncer = inject(LiveAnnouncer);
+ private readonly route = inject(Router);
+ private readonly devicesStore = inject(DevicesStore);
dialog = inject(MatDialog);
private element = inject(ElementRef);
- private readonly changeDetectorRef = inject(ChangeDetectorRef);
- private route = inject(Router);
- private devicesStore = inject(DevicesStore);
-
- readonly DeviceView = DeviceView;
private destroy$: Subject = new Subject();
viewModel$ = this.devicesStore.viewModel$;
- deviceDialog: MatDialogRef | undefined;
+ isOpenDeviceForm = false;
ngOnInit(): void {
combineLatest([
this.devicesStore.devices$,
this.devicesStore.isOpenAddDevice$,
- this.devicesStore.testModules$,
])
.pipe(takeUntil(this.destroy$))
- .subscribe(([devices, isOpenAddDevice, testModules]) => {
+ .subscribe(([devices, isOpenAddDevice]) => {
if (
!devices?.filter(device => device.status === DeviceStatus.VALID)
.length &&
isOpenAddDevice
) {
- this.openDialog(devices, testModules);
+ this.openForm();
}
});
}
@@ -129,15 +110,6 @@ export class DevicesComponent
this.destroy$.unsubscribe();
}
- canDeactivate(): Observable {
- this.deviceDialog?.componentInstance?.closeForm();
- return this.dialog.afterAllClosed.pipe(
- map(() => {
- return true;
- })
- );
- }
-
menuItemClicked(
{ action, entity, index }: EntityActionResult,
devices: Device[],
@@ -148,7 +120,7 @@ export class DevicesComponent
this.openStartTestrun(entity, devices, testModules);
break;
case DeviceAction.Delete:
- this.openDeleteDialog(devices, testModules, entity, index);
+ this.openDeleteDialog(entity, index);
break;
}
}
@@ -190,18 +162,26 @@ export class DevicesComponent
});
}
- openDeleteDialog(
- devices: Device[],
- testModules: TestModule[],
- initialDevice: Device,
- deviceIndex: number
- ) {
+ async openForm() {
+ this.isOpenDeviceForm = true;
+ await this.liveAnnouncer.announce('Device qualification form');
+ this.focusManagerService.focusFirstElementInContainer(
+ window.document.querySelector('app-device-qualification-from')
+ );
+ }
+
+ discard() {
+ this.isOpenDeviceForm = false;
+ this.openCloseDialog();
+ }
+
+ openDeleteDialog(device: Device, deviceIndex: number) {
const dialogRef = this.dialog.open(SimpleDialogComponent, {
ariaLabel: 'Delete device',
data: {
title: 'Delete device?',
content: `You are about to delete ${
- initialDevice.manufacturer + ' ' + initialDevice.model
+ device.manufacturer + ' ' + device.model
}. Are you sure?`,
},
autoFocus: true,
@@ -212,7 +192,7 @@ export class DevicesComponent
dialogRef?.beforeClosed().subscribe(deleteDevice => {
if (deleteDevice) {
this.devicesStore.deleteDevice({
- device: initialDevice,
+ device: device,
onDelete: () => {
this.focusNextButton(deviceIndex);
},
@@ -221,79 +201,7 @@ export class DevicesComponent
});
}
- openDialog(
- devices: Device[] = [],
- testModules: TestModule[],
- initialDevice?: Device,
- selectedDevice?: Device,
- isEditDevice = false,
- index = 0,
- deviceIndex?: number
- ): void {
- this.deviceDialog = this.dialog.open(DeviceQualificationFromComponent, {
- ariaLabel: isEditDevice ? 'Edit device' : 'Create Device',
- data: {
- device: selectedDevice || null,
- initialDevice,
- title: isEditDevice ? 'Edit device' : 'Create Device',
- testModules: testModules,
- devices,
- index,
- isCreate: !isEditDevice,
- },
- autoFocus: 'first-tabbable',
- hasBackdrop: true,
- disableClose: true,
- panelClass: 'device-form-dialog',
- });
- this.deviceDialog?.beforeClosed().subscribe((response: FormResponse) => {
- if (!response) {
- this.devicesStore.setIsOpenAddDevice(false);
- return;
- }
- if (response.action === FormAction.Close) {
- this.openCloseDialog(
- devices,
- testModules,
- initialDevice,
- response.device,
- isEditDevice,
- response.index,
- deviceIndex
- );
- } else if (response.action === FormAction.Save && response.device) {
- timer(10)
- .pipe(takeUntil(this.destroy$))
- .subscribe(() => {
- if (!initialDevice) {
- this.focusManagerService.focusFirstElementInContainer();
- } else if (deviceIndex !== undefined) {
- this.focusSelectedButton(deviceIndex);
- }
- });
- }
- if (response.action === FormAction.Delete && initialDevice) {
- if (response.device) {
- this.openDeleteDialog(
- devices,
- testModules,
- initialDevice,
- deviceIndex!
- );
- }
- }
- });
- }
-
- openCloseDialog(
- devices: Device[],
- testModules: TestModule[],
- initialDevice?: Device,
- device?: Device,
- isEditDevice = false,
- index = 0,
- deviceIndex?: number
- ) {
+ private openCloseDialog(deviceIndex?: number) {
const dialogRef = this.dialog.open(SimpleDialogComponent, {
ariaLabel: 'Close the Device menu',
data: {
@@ -307,20 +215,12 @@ export class DevicesComponent
});
dialogRef?.beforeClosed().subscribe(close => {
- if (!close) {
- this.openDialog(
- devices,
- testModules,
- initialDevice,
- device,
- isEditDevice,
- index,
- deviceIndex
- );
- } else if (deviceIndex !== undefined) {
- this.focusSelectedButton(deviceIndex);
- } else {
- this.focusManagerService.focusFirstElementInContainer();
+ if (close) {
+ if (deviceIndex !== undefined) {
+ this.focusSelectedButton(deviceIndex);
+ } else {
+ this.focusManagerService.focusFirstElementInContainer();
+ }
}
});
}
diff --git a/modules/ui/src/app/pages/devices/devices.store.ts b/modules/ui/src/app/pages/devices/devices.store.ts
index f164d643d..a17e2c7b0 100644
--- a/modules/ui/src/app/pages/devices/devices.store.ts
+++ b/modules/ui/src/app/pages/devices/devices.store.ts
@@ -34,14 +34,14 @@ import {
setIsOpenAddDevice,
} from '../../store/actions';
import { TestrunStatus } from '../../model/testrun-status';
-import { DeviceQuestionnaireSection } from '../../model/device';
import { EntityAction } from '../../model/entity-action';
+import { QuestionFormat } from '../../model/question';
export interface DevicesComponentState {
devices: Device[];
selectedDevice: Device | null;
testModules: TestModule[];
- questionnaireFormat: DeviceQuestionnaireSection[];
+ questionnaireFormat: QuestionFormat[];
actions: EntityAction[];
}
@@ -72,7 +72,7 @@ export class DevicesStore extends ComponentStore {
}));
updateQuestionnaireFormat = this.updater(
- (state, questionnaireFormat: DeviceQuestionnaireSection[]) => ({
+ (state, questionnaireFormat: QuestionFormat[]) => ({
...state,
questionnaireFormat,
})
@@ -158,7 +158,7 @@ export class DevicesStore extends ComponentStore {
return trigger$.pipe(
exhaustMap(() => {
return this.testRunService.fetchQuestionnaireFormat().pipe(
- tap((questionnaireFormat: DeviceQuestionnaireSection[]) => {
+ tap((questionnaireFormat: QuestionFormat[]) => {
this.updateQuestionnaireFormat(questionnaireFormat);
})
);
diff --git a/modules/ui/src/app/services/test-run.service.ts b/modules/ui/src/app/services/test-run.service.ts
index a9ed6bdfe..e3ea7004d 100644
--- a/modules/ui/src/app/services/test-run.service.ts
+++ b/modules/ui/src/app/services/test-run.service.ts
@@ -17,7 +17,7 @@ import { HttpClient } from '@angular/common/http';
import { Injectable, inject } from '@angular/core';
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
import { Observable } from 'rxjs/internal/Observable';
-import { Device, DeviceQuestionnaireSection } from '../model/device';
+import { Device } from '../model/device';
import { catchError, map, of, retry } from 'rxjs';
import { SystemConfig, SystemInterfaces } from '../model/setting';
import {
@@ -34,6 +34,7 @@ import {
ProfileRisk,
RiskResultClassName,
} from '../model/profile';
+import { QuestionFormat } from '../model/question';
const API_URL = `http://${window.location.hostname}:8000`;
export const SYSTEM_STOP = '/system/stop';
@@ -300,10 +301,8 @@ export class TestRunService {
return this.http.get(`${API_URL}/profiles/format`);
}
- fetchQuestionnaireFormat(): Observable {
- return this.http.get(
- `${API_URL}/devices/format`
- );
+ fetchQuestionnaireFormat(): Observable {
+ return this.http.get(`${API_URL}/devices/format`);
}
saveProfile(profile: Profile): Observable {
diff --git a/modules/ui/src/styles.scss b/modules/ui/src/styles.scss
index 6dccfb0c6..fa96dc4b2 100644
--- a/modules/ui/src/styles.scss
+++ b/modules/ui/src/styles.scss
@@ -142,10 +142,6 @@ body {
border-radius: 12px;
}
-mat-hint {
- color: colors.$grey-700;
-}
-
.mdc-button:focus-visible,
.mdc-icon-button:focus-visible,
.mdc-radio__native-control:focus-visible,
diff --git a/modules/ui/src/theming/m3-theme.scss b/modules/ui/src/theming/m3-theme.scss
index a47974828..6aea20013 100644
--- a/modules/ui/src/theming/m3-theme.scss
+++ b/modules/ui/src/theming/m3-theme.scss
@@ -152,7 +152,7 @@ $light-theme: mat.define-theme(
),
typography: (
brand-family: variables.$font-primary,
- plain-family: variables.$font-secondary,
+ plain-family: variables.$font-text,
),
density: (
scale: 0,
diff --git a/resources/devices/device_profile.json b/resources/devices/device_profile.json
index df96ca759..5158dd5ff 100644
--- a/resources/devices/device_profile.json
+++ b/resources/devices/device_profile.json
@@ -1,419 +1,405 @@
[
{
- "step": 1,
- "title": "Select device type & technology",
- "description": "Before your device can go through testing, tell us more about your device and its functionality. It is important that we fully understand your device before a thorough assessment can be made.",
- "questions": [
+ "id": 1,
+ "question": "What type of device is this?",
+ "validation": {
+ "required": true
+ },
+ "type": "select",
+ "options": [
+ {
+ "text": "Building Automation Gateway",
+ "risk": "High",
+ "id": 1
+ },
+ {
+ "text": "IoT Gateway",
+ "risk": "High",
+ "id": 2
+ },
+ {
+ "text": "Controller - AHU",
+ "risk": "High",
+ "id": 3
+ },
+ {
+ "text": "Controller - Boiler",
+ "risk": "High",
+ "id": 4
+ },
+ {
+ "text": "Controller - Chiller",
+ "risk": "High",
+ "id": 5
+ },
+ {
+ "text": "Controller - FCU",
+ "risk": "Limited",
+ "id": 6
+ },
+ {
+ "text": "Controller - Pump",
+ "risk": "Limited",
+ "id": 7
+ },
+ {
+ "text": "Controller - CRAC",
+ "risk": "High",
+ "id": 8
+ },
+ {
+ "text": "Controller - VAV",
+ "risk": "Limited",
+ "id": 9
+ },
+ {
+ "text": "Controller - VRF",
+ "risk": "Limited",
+ "id": 10
+ },
+ {
+ "text": "Controller - Multiple",
+ "risk": "High",
+ "id": 11
+ },
+ {
+ "text": "Controller - Other",
+ "risk": "High",
+ "id": 12
+ },
+ {
+ "text": "Controller - Lighting",
+ "risk": "Limited",
+ "id": 13
+ },
+ {
+ "text": "Controller - Blinds/Facades",
+ "risk": "High",
+ "id": 14
+ },
+ {
+ "text": "Controller - Lifts/Elevators",
+ "risk": "High",
+ "id": 15
+ },
+ {
+ "text": "Controller - UPS",
+ "risk": "High",
+ "id": 16
+ },
+ {
+ "text": "Sensor - Air Quality",
+ "risk": "Limited",
+ "id": 17
+ },
+ {
+ "text": "Sensor - Vibration",
+ "risk": "Limited",
+ "id": 18
+ },
+ {
+ "text": "Sensor - Humidity",
+ "risk": "Limited",
+ "id": 19
+ },
+ {
+ "text": "Sensor - Water",
+ "risk": "Limited",
+ "id": 20
+ },
+ {
+ "text": "Sensor - Occupancy",
+ "risk": "High",
+ "id": 21
+ },
+ {
+ "text": "Sensor - Volume",
+ "risk": "Limited",
+ "id": 22
+ },
+ {
+ "text": "Sensor - Weight",
+ "risk": "Limited",
+ "id": 23
+ },
+ {
+ "text": "Sensor - Weather",
+ "risk": "Limited",
+ "id": 24
+ },
+ {
+ "text": "Sensor - Steam",
+ "risk": "High",
+ "id": 25
+ },
+ {
+ "text": "Sensor - Air Flow",
+ "risk": "Limited",
+ "id": 26
+ },
+ {
+ "text": "Sensor - Lighting",
+ "risk": "Limited",
+ "id": 27
+ },
+ {
+ "text": "Sensor - Other",
+ "risk": "High",
+ "id": 28
+ },
+ {
+ "text": "Sensor - Air Quality",
+ "risk": "Limited",
+ "id": 29
+ },
+ {
+ "text": "Monitoring - Fire System",
+ "risk": "Limited",
+ "id": 30
+ },
+ {
+ "text": "Monitoring - Emergency Lighting",
+ "risk": "Limited",
+ "id": 31
+ },
+ {
+ "text": "Monitoring - Other",
+ "risk": "High",
+ "id": 32
+ },
+ {
+ "text": "Monitoring - UPS",
+ "risk": "Limited",
+ "id": 33
+ },
+ {
+ "text": "Meter - Water",
+ "risk": "Limited",
+ "id": 34
+ },
+ {
+ "text": "Meter - Gas",
+ "risk": "Limited",
+ "id": 35
+ },
+ {
+ "text": "Meter - Electricity",
+ "risk": "Limited",
+ "id": 36
+ },
+ {
+ "text": "Meter - Other",
+ "risk": "High",
+ "id": 37
+ },
+ {
+ "text": "Other",
+ "risk": "High",
+ "id": 38
+ },
+ {
+ "text": "Data - Storage",
+ "risk": "High",
+ "id": 39
+ },
+ {
+ "text": "Data - Processing",
+ "risk": "High",
+ "id": 40
+ },
+ {
+ "text": "Tablet",
+ "risk": "High",
+ "id": 41
+ }
+ ]
+ },
+ {
+ "id": 2,
+ "question": "Please select the technology this device falls into",
+ "validation": {
+ "required": true
+ },
+ "type": "select",
+ "options": [
{
"id": 1,
- "question": "What type of device is this?",
- "validation": {
- "required": true
- },
- "type": "select",
- "options": [
- {
- "text": "Building Automation Gateway",
- "risk": "High",
- "id": 1
- },
- {
- "text": "IoT Gateway",
- "risk": "High",
- "id": 2
- },
- {
- "text": "Controller - AHU",
- "risk": "High",
- "id": 3
- },
- {
- "text": "Controller - Boiler",
- "risk": "High",
- "id": 4
- },
- {
- "text": "Controller - Chiller",
- "risk": "High",
- "id": 5
- },
- {
- "text": "Controller - FCU",
- "risk": "Limited",
- "id": 6
- },
- {
- "text": "Controller - Pump",
- "risk": "Limited",
- "id": 7
- },
- {
- "text": "Controller - CRAC",
- "risk": "High",
- "id": 8
- },
- {
- "text": "Controller - VAV",
- "risk": "Limited",
- "id": 9
- },
- {
- "text": "Controller - VRF",
- "risk": "Limited",
- "id": 10
- },
- {
- "text": "Controller - Multiple",
- "risk": "High",
- "id": 11
- },
- {
- "text": "Controller - Other",
- "risk": "High",
- "id": 12
- },
- {
- "text": "Controller - Lighting",
- "risk": "Limited",
- "id": 13
- },
- {
- "text": "Controller - Blinds/Facades",
- "risk": "High",
- "id": 14
- },
- {
- "text": "Controller - Lifts/Elevators",
- "risk": "High",
- "id": 15
- },
- {
- "text": "Controller - UPS",
- "risk": "High",
- "id": 16
- },
- {
- "text": "Sensor - Air Quality",
- "risk": "Limited",
- "id": 17
- },
- {
- "text": "Sensor - Vibration",
- "risk": "Limited",
- "id": 18
- },
- {
- "text": "Sensor - Humidity",
- "risk": "Limited",
- "id": 19
- },
- {
- "text": "Sensor - Water",
- "risk": "Limited",
- "id": 20
- },
- {
- "text": "Sensor - Occupancy",
- "risk": "High",
- "id": 21
- },
- {
- "text": "Sensor - Volume",
- "risk": "Limited",
- "id": 22
- },
- {
- "text": "Sensor - Weight",
- "risk": "Limited",
- "id": 23
- },
- {
- "text": "Sensor - Weather",
- "risk": "Limited",
- "id": 24
- },
- {
- "text": "Sensor - Steam",
- "risk": "High",
- "id": 25
- },
- {
- "text": "Sensor - Air Flow",
- "risk": "Limited",
- "id": 26
- },
- {
- "text": "Sensor - Lighting",
- "risk": "Limited",
- "id": 27
- },
- {
- "text": "Sensor - Other",
- "risk": "High",
- "id": 28
- },
- {
- "text": "Sensor - Air Quality",
- "risk": "Limited",
- "id": 29
- },
- {
- "text": "Monitoring - Fire System",
- "risk": "Limited",
- "id": 30
- },
- {
- "text": "Monitoring - Emergency Lighting",
- "risk": "Limited",
- "id": 31
- },
- {
- "text": "Monitoring - Other",
- "risk": "High",
- "id": 32
- },
- {
- "text": "Monitoring - UPS",
- "risk": "Limited",
- "id": 33
- },
- {
- "text": "Meter - Water",
- "risk": "Limited",
- "id": 34
- },
- {
- "text": "Meter - Gas",
- "risk": "Limited",
- "id": 35
- },
- {
- "text": "Meter - Electricity",
- "risk": "Limited",
- "id": 36
- },
- {
- "text": "Meter - Other",
- "risk": "High",
- "id": 37
- },
- {
- "text": "Other",
- "risk": "High",
- "id": 38
- },
- {
- "text": "Data - Storage",
- "risk": "High",
- "id": 39
- },
- {
- "text": "Data - Processing",
- "risk": "High",
- "id": 40
- },
- {
- "text": "Tablet",
- "risk": "High",
- "id": 41
- }
- ]
+ "text": "Hardware - Access Control"
},
{
"id": 2,
- "question": "Please select the technology this device falls into",
- "validation": {
- "required": true
- },
- "type": "select",
- "options": [
- {
- "id": 1,
- "text": "Hardware - Access Control"
- },
- {
- "id": 2,
- "text": "Hardware - Air quality"
- },
- {
- "id": 3,
- "text": "Hardware - Asset location tracking"
- },
- {
- "id": 4,
- "text": "Hardware - Audio Visual"
- },
- {
- "id": 5,
- "text": "Hardware - Blinds/Facade"
- },
- {
- "id": 6,
- "text": "Hardware - Cameras"
- },
- {
- "id": 7,
- "text": "Hardware - Catering"
- },
- {
- "id": 8,
- "text": "Hardware - Data Ingestion/Managment"
- },
- {
- "id": 9,
- "text": "Hardware - EV Charging"
- },
- {
- "id": 10,
- "text": "Hardware - Fitness"
- },
- {
- "id": 11,
- "text": "Hardware - HVAC"
- },
- {
- "id": 12,
- "text": "Hardware - Irrigation"
- },
- {
- "id": 13,
- "text": "Hardware - Leak Detection"
- },
- {
- "id": 14,
- "text": "Hardware - Lifts/Elevators"
- },
- {
- "id": 15,
- "text": "Hardware - Lighting"
- },
- {
- "id": 16,
- "text": "Hardware - Metering"
- },
- {
- "id": 17,
- "text": "Hardware - Monitoring"
- },
- {
- "id": 18,
- "text": "Hardware - Occupancy"
- },
- {
- "id": 19,
- "text": "Hardware - System Integration"
- },
- {
- "id": 20,
- "text": "Hardware - Time Management"
- },
- {
- "id": 21,
- "text": "Hardware - UPS"
- },
- {
- "id": 22,
- "text": "Hardware - Waste Management"
- },
- {
- "id": 23,
- "text": "Building Automation"
- },
- {
- "id": 24,
- "text": "Other"
- }
- ]
+ "text": "Hardware - Air quality"
},
{
"id": 3,
- "question": "Does your device process any sensitive information? ",
- "validation": {
- "required": true
- },
- "type": "select",
- "options": [
- {
- "id": 1,
- "text": "Yes",
- "risk": "High"
- },
- {
- "id": 2,
- "text": "No",
- "risk": "Limited"
- },
- {
- "id": 3,
- "text": "I don't know",
- "risk": "High"
- }
- ]
+ "text": "Hardware - Asset location tracking"
+ },
+ {
+ "id": 4,
+ "text": "Hardware - Audio Visual"
+ },
+ {
+ "id": 5,
+ "text": "Hardware - Blinds/Facade"
+ },
+ {
+ "id": 6,
+ "text": "Hardware - Cameras"
+ },
+ {
+ "id": 7,
+ "text": "Hardware - Catering"
+ },
+ {
+ "id": 8,
+ "text": "Hardware - Data Ingestion/Managment"
+ },
+ {
+ "id": 9,
+ "text": "Hardware - EV Charging"
+ },
+ {
+ "id": 10,
+ "text": "Hardware - Fitness"
+ },
+ {
+ "id": 11,
+ "text": "Hardware - HVAC"
+ },
+ {
+ "id": 12,
+ "text": "Hardware - Irrigation"
+ },
+ {
+ "id": 13,
+ "text": "Hardware - Leak Detection"
+ },
+ {
+ "id": 14,
+ "text": "Hardware - Lifts/Elevators"
+ },
+ {
+ "id": 15,
+ "text": "Hardware - Lighting"
+ },
+ {
+ "id": 16,
+ "text": "Hardware - Metering"
+ },
+ {
+ "id": 17,
+ "text": "Hardware - Monitoring"
+ },
+ {
+ "id": 18,
+ "text": "Hardware - Occupancy"
+ },
+ {
+ "id": 19,
+ "text": "Hardware - System Integration"
+ },
+ {
+ "id": 20,
+ "text": "Hardware - Time Management"
+ },
+ {
+ "id": 21,
+ "text": "Hardware - UPS"
+ },
+ {
+ "id": 22,
+ "text": "Hardware - Waste Management"
+ },
+ {
+ "id": 23,
+ "text": "Building Automation"
+ },
+ {
+ "id": 24,
+ "text": "Other"
}
]
},
{
- "step": 2,
- "title": "Tell us more about your device",
- "description": "Before your device can go through testing, tell us more about your device and its functionality. It is important that we fully understand your device before a thorough assessment can be made.",
- "questions": [
+ "id": 3,
+ "question": "Does your device process any sensitive information? ",
+ "validation": {
+ "required": true
+ },
+ "type": "select",
+ "options": [
{
"id": 1,
- "question": "Can all non-essential services be disabled on your device?",
- "validation": {
- "required": true
- },
- "type": "select",
- "options": [
- {
- "text": "Yes",
- "id": 1
- },
- {
- "text": "No",
- "id": 2
- }
- ]
+ "text": "Yes",
+ "risk": "High"
},
{
"id": 2,
- "question": "Is there a second IP port on the device?",
- "validation": {
- "required": true
- },
- "type": "select",
- "options": [
- {
- "text": "Yes",
- "id": 1
- },
- {
- "text": "No",
- "id": 2
- }
- ]
+ "text": "No",
+ "risk": "Limited"
},
{
"id": 3,
- "question": "Can the second IP port on your device be disabled?",
- "validation": {
- "required": true
- },
- "type": "select",
- "options": [
- {
- "text": "Yes",
- "id": 1
- },
- {
- "text": "No",
- "id": 2
- },
- {
- "text": "N/A",
- "id": 3
- }
- ]
+ "text": "I don't know",
+ "risk": "High"
+ }
+ ]
+ },
+ {
+ "id": 4,
+ "question": "Can all non-essential services be disabled on your device?",
+ "validation": {
+ "required": true
+ },
+ "type": "select",
+ "options": [
+ {
+ "text": "Yes",
+ "id": 1
+ },
+ {
+ "text": "No",
+ "id": 2
+ }
+ ]
+ },
+ {
+ "id": 5,
+ "question": "Is there a second IP port on the device?",
+ "validation": {
+ "required": true
+ },
+ "type": "select",
+ "options": [
+ {
+ "text": "Yes",
+ "id": 1
+ },
+ {
+ "text": "No",
+ "id": 2
+ }
+ ]
+ },
+ {
+ "id": 6,
+ "question": "Can the second IP port on your device be disabled?",
+ "validation": {
+ "required": true
+ },
+ "type": "select",
+ "options": [
+ {
+ "text": "Yes",
+ "id": 1
+ },
+ {
+ "text": "No",
+ "id": 2
+ },
+ {
+ "text": "N/A",
+ "id": 3
}
]
}
diff --git a/testing/api/test_api.py b/testing/api/test_api.py
index e67506a71..a7e77f2e9 100644
--- a/testing/api/test_api.py
+++ b/testing/api/test_api.py
@@ -2146,15 +2146,8 @@ def test_get_devices_format(testrun): # pylint: disable=W0613
# Check if the response is a list
assert isinstance(response, list)
- # Store the expected main keys and types
+ # Store the item expected keys and types
response_keys = {
- "step": int,
- "title": str,
- "questions": list
- }
-
- # Store the 'questions' field expected keys and types
- questions_keys = {
"id": int,
"question": str,
"type": str,
@@ -2173,18 +2166,6 @@ def test_get_devices_format(testrun): # pylint: disable=W0613
# Check if the key has the expected data type
assert isinstance(item[key], key_type)
- # Iterate over the 'questions' field
- for questions in item["questions"]:
-
- # Iterate over the 'questions_keys' dict keys and values
- for key, key_type in questions_keys.items():
-
- # Check if the key is in 'questions' field
- assert key in questions
-
- # Check if the key has the expected data type
- assert isinstance(questions[key], key_type)
-
def test_sys_testpacks(testrun): # pylint: disable=W0613
""" Test for system testpack endpoint (200) """