diff --git a/webapp/frontend/angular.json b/webapp/frontend/angular.json index 004e2cd..6ea760f 100644 --- a/webapp/frontend/angular.json +++ b/webapp/frontend/angular.json @@ -91,6 +91,7 @@ }, "test": { "builder": "@angular-devkit/build-angular:karma", + "defaultConfiguration": "production", "options": { "main": "src/test.ts", "polyfills": "src/polyfills.ts", @@ -101,10 +102,22 @@ "src/favicon-32x32.png", "src/assets" ], + "stylePreprocessorOptions": { + "includePaths": [ + "src/@treo/styles" + ] + }, "styles": [ - "src/styles.scss" + "src/styles/vendors.scss", + "src/@treo/styles/main.scss", + "src/styles/styles.scss", + "src/styles/tailwind.scss" ], - "scripts": [] + "scripts": [], + "fileReplacements": [{ + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + }] } }, "lint": { diff --git a/webapp/frontend/src/app/core/models/device-model.ts b/webapp/frontend/src/app/core/models/device-model.ts new file mode 100644 index 0000000..ae7e5b0 --- /dev/null +++ b/webapp/frontend/src/app/core/models/device-model.ts @@ -0,0 +1,26 @@ +// maps to webapp/backend/pkg/models/device.go +export interface DeviceModel { + wwn: string; + device_name: string; + device_uuid: string; + device_serial_id: string; + device_label: string; + + manufacturer: string; + model_name: string; + interface_type: string; + interface_speed: string; + serial_number: string; + firmware: string; + rotational_speed: number; + capacity: number; + form_factor: string; + smart_support: boolean; + device_protocol: string; + device_type: string; + + label: string; + host_id: string; + + device_status: number; +} diff --git a/webapp/frontend/src/app/modules/detail/detail.component.spec.ts b/webapp/frontend/src/app/modules/detail/detail.component.spec.ts index 149b9be..6f956ee 100644 --- a/webapp/frontend/src/app/modules/detail/detail.component.spec.ts +++ b/webapp/frontend/src/app/modules/detail/detail.component.spec.ts @@ -1,14 +1,27 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - +import { HttpClientModule } from '@angular/common/http'; import { DetailComponent } from './detail.component'; +import {TreoConfigService} from '@treo/services/config'; +import { TREO_APP_CONFIG } from '@treo/services/config/config.constants'; +const TREO_APP_CONFIG_PROVIDER = [ { provide: TREO_APP_CONFIG, useValue: TreoConfigService } ]; +import { MatDialogModule } from '@angular/material/dialog'; +import {DeviceTitlePipe} from 'app/shared/device-title.pipe'; + + describe('DetailComponent', () => { let component: DetailComponent; let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ DetailComponent ] + imports: [ + HttpClientModule, + MatDialogModule + + ], + declarations: [ DetailComponent, DeviceTitlePipe ], + providers: [ TREO_APP_CONFIG_PROVIDER ] }) .compileComponents(); })); diff --git a/webapp/frontend/src/app/shared/device-title.pipe.spec.ts b/webapp/frontend/src/app/shared/device-title.pipe.spec.ts index 1d64103..0661e84 100644 --- a/webapp/frontend/src/app/shared/device-title.pipe.spec.ts +++ b/webapp/frontend/src/app/shared/device-title.pipe.spec.ts @@ -1,8 +1,152 @@ import { DeviceTitlePipe } from './device-title.pipe'; +import {FileSizePipe} from "./file-size.pipe"; +import {DeviceModel} from "../core/models/device-model"; describe('DeviceTitlePipe', () => { it('create an instance', () => { const pipe = new DeviceTitlePipe(); expect(pipe).toBeTruthy(); }); + + describe('#deviceTitleForType',() => { + const testCases = [ + { + 'device': { + 'device_name': 'sda', + 'device_type': 'ata', + 'model_name': 'Samsung', + }, + 'titleType': 'name', + 'result': '/dev/sda - Samsung' + },{ + 'device': { + 'device_name': 'nvme0', + 'device_type': 'nvme', + 'model_name': 'Samsung', + }, + 'titleType': 'name', + 'result': '/dev/nvme0 - nvme - Samsung' + },{ + 'device': {}, + 'titleType': 'serial_id', + 'result': '' + },{ + 'device': { + 'device_serial_id': 'ata-WDC_WD140EDFZ-11AXXXXX_9RXXXXXX', + }, + 'titleType': 'serial_id', + 'result': '/by-id/ata-WDC_WD140EDFZ-11AXXXXX_9RXXXXXX' + },{ + 'device': {}, + 'titleType': 'uuid', + 'result': '' + },{ + 'device': { + 'device_uuid': 'abcdef-1234-4567-8901' + }, + 'titleType': 'uuid', + 'result': '/by-uuid/abcdef-1234-4567-8901' + },{ + 'device': {}, + 'titleType': 'label', + 'result': '' + },{ + 'device': { + 'label': 'custom-device-label' + }, + 'titleType': 'label', + 'result': 'custom-device-label' + },{ + 'device': { + 'device_label': 'drive-volume-label' + }, + 'titleType': 'label', + 'result': '/by-label/drive-volume-label' + }, + ] + testCases.forEach((test, index) => { + it(`should correctly format device title ${JSON.stringify(test.device)}. (testcase: ${index + 1})`, () => { + // test + const formatted = DeviceTitlePipe.deviceTitleForType(test.device as DeviceModel, test.titleType) + expect(formatted).toEqual(test.result); + }); + }) + }) + + describe('#deviceTitleWithFallback',() => { + const testCases = [ + { + 'device': { + 'device_name': 'sda', + 'device_type': 'ata', + 'model_name': 'Samsung', + }, + 'titleType': 'name', + 'result': '/dev/sda - Samsung' + },{ + 'device': { + 'device_name': 'nvme0', + 'device_type': 'nvme', + 'model_name': 'Samsung', + }, + 'titleType': 'name', + 'result': '/dev/nvme0 - nvme - Samsung' + },{ + 'device': { + 'device_name': 'fallback', + 'device_type': 'ata', + 'model_name': 'fallback', + }, + 'titleType': 'serial_id', + 'result': '/dev/fallback - fallback' + },{ + 'device': { + 'device_serial_id': 'ata-WDC_WD140EDFZ-11AXXXXX_9RXXXXXX', + }, + 'titleType': 'serial_id', + 'result': '/by-id/ata-WDC_WD140EDFZ-11AXXXXX_9RXXXXXX' + },{ + 'device': { + 'device_name': 'fallback', + 'device_type': 'ata', + 'model_name': 'fallback', + }, + 'titleType': 'uuid', + 'result': '/dev/fallback - fallback' + },{ + 'device': { + 'device_uuid': 'abcdef-1234-4567-8901' + }, + 'titleType': 'uuid', + 'result': '/by-uuid/abcdef-1234-4567-8901' + },{ + 'device': { + 'device_name': 'fallback', + 'device_type': 'ata', + 'model_name': 'fallback', + }, + 'titleType': 'label', + 'result': '/dev/fallback - fallback' + },{ + 'device': { + 'label': 'custom-device-label' + }, + 'titleType': 'label', + 'result': 'custom-device-label' + },{ + 'device': { + 'device_label': 'drive-volume-label' + }, + 'titleType': 'label', + 'result': '/by-label/drive-volume-label' + }, + ] + testCases.forEach((test, index) => { + it(`should correctly format device title ${JSON.stringify(test.device)}. (testcase: ${index + 1})`, () => { + // test + const formatted = DeviceTitlePipe.deviceTitleWithFallback(test.device as DeviceModel, test.titleType) + expect(formatted).toEqual(test.result); + }); + }) + }) }); diff --git a/webapp/frontend/src/app/shared/device-title.pipe.ts b/webapp/frontend/src/app/shared/device-title.pipe.ts index 1196fb8..0c873f8 100644 --- a/webapp/frontend/src/app/shared/device-title.pipe.ts +++ b/webapp/frontend/src/app/shared/device-title.pipe.ts @@ -1,11 +1,12 @@ import { Pipe, PipeTransform } from '@angular/core'; +import {DeviceModel} from 'app/core/models/device-model'; @Pipe({ name: 'deviceTitle' }) export class DeviceTitlePipe implements PipeTransform { - static deviceTitleForType(device: any, titleType: string): string { + static deviceTitleForType(device: DeviceModel, titleType: string): string { const titleParts = [] switch(titleType){ case 'name': diff --git a/webapp/frontend/src/app/shared/file-size.pipe.spec.ts b/webapp/frontend/src/app/shared/file-size.pipe.spec.ts new file mode 100644 index 0000000..14973cf --- /dev/null +++ b/webapp/frontend/src/app/shared/file-size.pipe.spec.ts @@ -0,0 +1,35 @@ +import { FileSizePipe } from './file-size.pipe'; + +describe('FileSizePipe', () => { + it('create an instance', () => { + const pipe = new FileSizePipe(); + expect(pipe).toBeTruthy(); + }); + + describe('#transform',() => { + const testCases = [ + { + 'bytes': 1500, + 'precision': undefined, + 'result': '1 KB' + },{ + 'bytes': 2_100_000_000, + 'precision': undefined, + 'result': '2.0 GB', + },{ + 'bytes': 1500, + 'precision': 2, + 'result': '1.46 KB', + } + ] + testCases.forEach((test, index) => { + it(`should correctly format bytes ${test.bytes}. (testcase: ${index + 1})`, () => { + // test + const pipe = new FileSizePipe(); + const formatted = pipe.transform(test.bytes, test.precision) + expect(formatted).toEqual(test.result); + }); + }) + }) + +}); diff --git a/webapp/frontend/src/app/shared/temperature.pipe.spec.ts b/webapp/frontend/src/app/shared/temperature.pipe.spec.ts index fc30978..70a4908 100644 --- a/webapp/frontend/src/app/shared/temperature.pipe.spec.ts +++ b/webapp/frontend/src/app/shared/temperature.pipe.spec.ts @@ -1,8 +1,83 @@ import { TemperaturePipe } from './temperature.pipe'; describe('TemperaturePipe', () => { - it('create an instance', () => { - const pipe = new TemperaturePipe(); - expect(pipe).toBeTruthy(); - }); + it('create an instance', () => { + const pipe = new TemperaturePipe(); + expect(pipe).toBeTruthy(); + }); + + + describe('#celsiusToFahrenheit', () => { + const testCases = [ + { + 'c': -273.15, + 'f': -460, + },{ + 'c': -34.44, + 'f': -30, + },{ + 'c': -23.33, + 'f': -10, + },{ + 'c': -17.78, + 'f': -0, + },{ + 'c': 0, + 'f': 32, + },{ + 'c': 10, + 'f': 50, + },{ + 'c': 26.67, + 'f': 80, + },{ + 'c': 37, + 'f': 99, + },{ + 'c': 60, + 'f': 140, + } + ] + testCases.forEach((test, index) => { + it(`should correctly convert ${test.c}, Celsius to Fahrenheit (testcase: ${index + 1})`, () => { + // test + const numb = TemperaturePipe.celsiusToFahrenheit(test.c) + const roundNumb = Math.round(numb); + expect(roundNumb).toEqual(test.f); + }); + }) + }); + + describe('#formatTemperature',() => { + const testCases = [ + { + 'c': 26.67, + 'unit': 'celsius', + 'includeUnits': true, + 'result': '26.67°C' + },{ + 'c': 26.67, + 'unit': 'celsius', + 'includeUnits': false, + 'result': '26.67', + },{ + 'c': 26.67, + 'unit': 'fahrenheit', + 'includeUnits': true, + 'result': '80.006°F', + },{ + 'c': 26.67, + 'unit': 'fahrenheit', + 'includeUnits': false, + 'result': '80.006', + } + ] + testCases.forEach((test, index) => { + it(`should correctly format temperature ${test.c} to ${test.unit} ${test.includeUnits ? 'with' : 'without'} unit. (testcase: ${index + 1})`, () => { + // test + const formatted = TemperaturePipe.formatTemperature(test.c, test.unit, test.includeUnits) + expect(formatted).toEqual(test.result); + }); + }) + }) });