commit
550fb542d4
@ -1,17 +1,18 @@
|
||||
# Officially Supported NAS OS's
|
||||
# Officially Supported NAS/OS's
|
||||
|
||||
These are the officially supported NAS OS's (with documentation and setup guides).
|
||||
Once a guide is created (in `docs/guides/`) it will be linked here.
|
||||
These are the officially supported NAS OS's (with documentation and setup guides). Once a guide is created (
|
||||
in `docs/guides/` or elsewhere) it will be linked here.
|
||||
|
||||
- [ ] freenas/truenas
|
||||
- [x] [freenas/truenas](https://blog.stefandroid.com/2022/01/14/smart-scrutiny.html)
|
||||
- [x] [unraid](./INSTALL_UNRAID.md)
|
||||
- [ ] ESXI
|
||||
- [ ] Proxmox
|
||||
- [x] Synology(./INSTALL_SYNOLOGY_COLLECTOR.md)
|
||||
- [x] [Synology](./INSTALL_SYNOLOGY_COLLECTOR.md)
|
||||
- [ ] OMV
|
||||
- [ ] Amahi
|
||||
- [ ] Running in a LXC container
|
||||
- [x] [PFSense](./INSTALL_UNRAID.md)
|
||||
- [ ] QNAP
|
||||
- [ ] RockStor
|
||||
|
||||
- [x] QNAP
|
||||
- [x] [RockStor](https://rockstor.com/docs/interface/docker-based-rock-ons/scrutiny.html)
|
||||
- [ ] Solaris/OmniOS CE Support
|
||||
- [ ] Kubernetes
|
||||
|
@ -0,0 +1,164 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
mock_config "github.com/analogj/scrutiny/webapp/backend/pkg/config/mock"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_DownsampleScript_Weekly(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
//setup
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes()
|
||||
|
||||
deviceRepo := scrutinyRepository{
|
||||
appConfig: fakeConfig,
|
||||
}
|
||||
|
||||
aggregationType := "weekly"
|
||||
|
||||
//test
|
||||
influxDbScript := deviceRepo.DownsampleScript(aggregationType, "tsk-weekly-aggr", "0 1 * * 0")
|
||||
|
||||
//assert
|
||||
require.Equal(t, `
|
||||
option task = {
|
||||
name: "tsk-weekly-aggr",
|
||||
cron: "0 1 * * 0",
|
||||
}
|
||||
|
||||
sourceBucket = "metrics"
|
||||
rangeStart = -2w
|
||||
rangeEnd = -1w
|
||||
aggWindow = 1w
|
||||
destBucket = "metrics_weekly"
|
||||
destOrg = "scrutiny"
|
||||
|
||||
from(bucket: sourceBucket)
|
||||
|> range(start: rangeStart, stop: rangeEnd)
|
||||
|> filter(fn: (r) => r["_measurement"] == "smart" )
|
||||
|> group(columns: ["device_wwn", "_field"])
|
||||
|> aggregateWindow(every: aggWindow, fn: last, createEmpty: false)
|
||||
|> to(bucket: destBucket, org: destOrg)
|
||||
|
||||
from(bucket: sourceBucket)
|
||||
|> range(start: rangeStart, stop: rangeEnd)
|
||||
|> filter(fn: (r) => r["_measurement"] == "temp")
|
||||
|> group(columns: ["device_wwn"])
|
||||
|> toInt()
|
||||
|> aggregateWindow(fn: mean, every: aggWindow, createEmpty: false)
|
||||
|> set(key: "_measurement", value: "temp")
|
||||
|> set(key: "_field", value: "temp")
|
||||
|> to(bucket: destBucket, org: destOrg)
|
||||
`, influxDbScript)
|
||||
}
|
||||
|
||||
func Test_DownsampleScript_Monthly(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
//setup
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes()
|
||||
|
||||
deviceRepo := scrutinyRepository{
|
||||
appConfig: fakeConfig,
|
||||
}
|
||||
|
||||
aggregationType := "monthly"
|
||||
|
||||
//test
|
||||
influxDbScript := deviceRepo.DownsampleScript(aggregationType, "tsk-monthly-aggr", "30 1 1 * *")
|
||||
|
||||
//assert
|
||||
require.Equal(t, `
|
||||
option task = {
|
||||
name: "tsk-monthly-aggr",
|
||||
cron: "30 1 1 * *",
|
||||
}
|
||||
|
||||
sourceBucket = "metrics_weekly"
|
||||
rangeStart = -2mo
|
||||
rangeEnd = -1mo
|
||||
aggWindow = 1mo
|
||||
destBucket = "metrics_monthly"
|
||||
destOrg = "scrutiny"
|
||||
|
||||
from(bucket: sourceBucket)
|
||||
|> range(start: rangeStart, stop: rangeEnd)
|
||||
|> filter(fn: (r) => r["_measurement"] == "smart" )
|
||||
|> group(columns: ["device_wwn", "_field"])
|
||||
|> aggregateWindow(every: aggWindow, fn: last, createEmpty: false)
|
||||
|> to(bucket: destBucket, org: destOrg)
|
||||
|
||||
from(bucket: sourceBucket)
|
||||
|> range(start: rangeStart, stop: rangeEnd)
|
||||
|> filter(fn: (r) => r["_measurement"] == "temp")
|
||||
|> group(columns: ["device_wwn"])
|
||||
|> toInt()
|
||||
|> aggregateWindow(fn: mean, every: aggWindow, createEmpty: false)
|
||||
|> set(key: "_measurement", value: "temp")
|
||||
|> set(key: "_field", value: "temp")
|
||||
|> to(bucket: destBucket, org: destOrg)
|
||||
`, influxDbScript)
|
||||
}
|
||||
|
||||
func Test_DownsampleScript_Yearly(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
//setup
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes()
|
||||
|
||||
deviceRepo := scrutinyRepository{
|
||||
appConfig: fakeConfig,
|
||||
}
|
||||
|
||||
aggregationType := "yearly"
|
||||
|
||||
//test
|
||||
influxDbScript := deviceRepo.DownsampleScript(aggregationType, "tsk-yearly-aggr", "0 2 1 1 *")
|
||||
|
||||
//assert
|
||||
require.Equal(t, `
|
||||
option task = {
|
||||
name: "tsk-yearly-aggr",
|
||||
cron: "0 2 1 1 *",
|
||||
}
|
||||
|
||||
sourceBucket = "metrics_monthly"
|
||||
rangeStart = -2y
|
||||
rangeEnd = -1y
|
||||
aggWindow = 1y
|
||||
destBucket = "metrics_yearly"
|
||||
destOrg = "scrutiny"
|
||||
|
||||
from(bucket: sourceBucket)
|
||||
|> range(start: rangeStart, stop: rangeEnd)
|
||||
|> filter(fn: (r) => r["_measurement"] == "smart" )
|
||||
|> group(columns: ["device_wwn", "_field"])
|
||||
|> aggregateWindow(every: aggWindow, fn: last, createEmpty: false)
|
||||
|> to(bucket: destBucket, org: destOrg)
|
||||
|
||||
from(bucket: sourceBucket)
|
||||
|> range(start: rangeStart, stop: rangeEnd)
|
||||
|> filter(fn: (r) => r["_measurement"] == "temp")
|
||||
|> group(columns: ["device_wwn"])
|
||||
|> toInt()
|
||||
|> aggregateWindow(fn: mean, every: aggWindow, createEmpty: false)
|
||||
|> set(key: "_measurement", value: "temp")
|
||||
|> set(key: "_field", value: "temp")
|
||||
|> to(bucket: destBucket, org: destOrg)
|
||||
`, influxDbScript)
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
mock_config "github.com/analogj/scrutiny/webapp/backend/pkg/config/mock"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_aggregateTempQuery_Week(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
//setup
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes()
|
||||
|
||||
deviceRepo := scrutinyRepository{
|
||||
appConfig: fakeConfig,
|
||||
}
|
||||
|
||||
aggregationType := DURATION_KEY_WEEK
|
||||
|
||||
//test
|
||||
influxDbScript := deviceRepo.aggregateTempQuery(aggregationType)
|
||||
|
||||
//assert
|
||||
require.Equal(t, `import "influxdata/influxdb/schema"
|
||||
weekData = from(bucket: "metrics")
|
||||
|> range(start: -1w, stop: now())
|
||||
|> filter(fn: (r) => r["_measurement"] == "temp" )
|
||||
|> aggregateWindow(every: 1h, fn: mean, createEmpty: false)
|
||||
|> group(columns: ["device_wwn"])
|
||||
|> toInt()
|
||||
|
||||
weekData
|
||||
|> schema.fieldsAsCols()
|
||||
|> yield()`, influxDbScript)
|
||||
}
|
||||
|
||||
func Test_aggregateTempQuery_Month(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
//setup
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes()
|
||||
|
||||
deviceRepo := scrutinyRepository{
|
||||
appConfig: fakeConfig,
|
||||
}
|
||||
|
||||
aggregationType := DURATION_KEY_MONTH
|
||||
|
||||
//test
|
||||
influxDbScript := deviceRepo.aggregateTempQuery(aggregationType)
|
||||
|
||||
//assert
|
||||
require.Equal(t, `import "influxdata/influxdb/schema"
|
||||
weekData = from(bucket: "metrics")
|
||||
|> range(start: -1w, stop: now())
|
||||
|> filter(fn: (r) => r["_measurement"] == "temp" )
|
||||
|> aggregateWindow(every: 1h, fn: mean, createEmpty: false)
|
||||
|> group(columns: ["device_wwn"])
|
||||
|> toInt()
|
||||
|
||||
monthData = from(bucket: "metrics_weekly")
|
||||
|> range(start: -1mo, stop: -1w)
|
||||
|> filter(fn: (r) => r["_measurement"] == "temp" )
|
||||
|> aggregateWindow(every: 1h, fn: mean, createEmpty: false)
|
||||
|> group(columns: ["device_wwn"])
|
||||
|> toInt()
|
||||
|
||||
union(tables: [weekData, monthData])
|
||||
|> group(columns: ["device_wwn"])
|
||||
|> sort(columns: ["_time"], desc: false)
|
||||
|> schema.fieldsAsCols()`, influxDbScript)
|
||||
}
|
||||
|
||||
func Test_aggregateTempQuery_Year(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
//setup
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes()
|
||||
|
||||
deviceRepo := scrutinyRepository{
|
||||
appConfig: fakeConfig,
|
||||
}
|
||||
|
||||
aggregationType := DURATION_KEY_YEAR
|
||||
|
||||
//test
|
||||
influxDbScript := deviceRepo.aggregateTempQuery(aggregationType)
|
||||
|
||||
//assert
|
||||
require.Equal(t, `import "influxdata/influxdb/schema"
|
||||
weekData = from(bucket: "metrics")
|
||||
|> range(start: -1w, stop: now())
|
||||
|> filter(fn: (r) => r["_measurement"] == "temp" )
|
||||
|> aggregateWindow(every: 1h, fn: mean, createEmpty: false)
|
||||
|> group(columns: ["device_wwn"])
|
||||
|> toInt()
|
||||
|
||||
monthData = from(bucket: "metrics_weekly")
|
||||
|> range(start: -1mo, stop: -1w)
|
||||
|> filter(fn: (r) => r["_measurement"] == "temp" )
|
||||
|> aggregateWindow(every: 1h, fn: mean, createEmpty: false)
|
||||
|> group(columns: ["device_wwn"])
|
||||
|> toInt()
|
||||
|
||||
yearData = from(bucket: "metrics_monthly")
|
||||
|> range(start: -1y, stop: -1mo)
|
||||
|> filter(fn: (r) => r["_measurement"] == "temp" )
|
||||
|> aggregateWindow(every: 1h, fn: mean, createEmpty: false)
|
||||
|> group(columns: ["device_wwn"])
|
||||
|> toInt()
|
||||
|
||||
union(tables: [weekData, monthData, yearData])
|
||||
|> group(columns: ["device_wwn"])
|
||||
|> sort(columns: ["_time"], desc: false)
|
||||
|> schema.fieldsAsCols()`, influxDbScript)
|
||||
}
|
||||
|
||||
func Test_aggregateTempQuery_Forever(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
//setup
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes()
|
||||
|
||||
deviceRepo := scrutinyRepository{
|
||||
appConfig: fakeConfig,
|
||||
}
|
||||
|
||||
aggregationType := DURATION_KEY_FOREVER
|
||||
|
||||
//test
|
||||
influxDbScript := deviceRepo.aggregateTempQuery(aggregationType)
|
||||
|
||||
//assert
|
||||
require.Equal(t, `import "influxdata/influxdb/schema"
|
||||
weekData = from(bucket: "metrics")
|
||||
|> range(start: -1w, stop: now())
|
||||
|> filter(fn: (r) => r["_measurement"] == "temp" )
|
||||
|> aggregateWindow(every: 1h, fn: mean, createEmpty: false)
|
||||
|> group(columns: ["device_wwn"])
|
||||
|> toInt()
|
||||
|
||||
monthData = from(bucket: "metrics_weekly")
|
||||
|> range(start: -1mo, stop: -1w)
|
||||
|> filter(fn: (r) => r["_measurement"] == "temp" )
|
||||
|> aggregateWindow(every: 1h, fn: mean, createEmpty: false)
|
||||
|> group(columns: ["device_wwn"])
|
||||
|> toInt()
|
||||
|
||||
yearData = from(bucket: "metrics_monthly")
|
||||
|> range(start: -1y, stop: -1mo)
|
||||
|> filter(fn: (r) => r["_measurement"] == "temp" )
|
||||
|> aggregateWindow(every: 1h, fn: mean, createEmpty: false)
|
||||
|> group(columns: ["device_wwn"])
|
||||
|> toInt()
|
||||
|
||||
foreverData = from(bucket: "metrics_yearly")
|
||||
|> range(start: -10y, stop: -1y)
|
||||
|> filter(fn: (r) => r["_measurement"] == "temp" )
|
||||
|> aggregateWindow(every: 1h, fn: mean, createEmpty: false)
|
||||
|> group(columns: ["device_wwn"])
|
||||
|> toInt()
|
||||
|
||||
union(tables: [weekData, monthData, yearData, foreverData])
|
||||
|> group(columns: ["device_wwn"])
|
||||
|> sort(columns: ["_time"], desc: false)
|
||||
|> schema.fieldsAsCols()`, influxDbScript)
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
import {DeviceModel} from 'app/core/models/device-model';
|
||||
import {SmartModel} from 'app/core/models/measurements/smart-model';
|
||||
import {AttributeMetadataModel} from 'app/core/models/thresholds/attribute-metadata-model';
|
||||
|
||||
// maps to webapp/backend/pkg/models/device_summary.go
|
||||
export interface DeviceDetailsResponseWrapper {
|
||||
success: boolean;
|
||||
errors?: any[];
|
||||
data: {
|
||||
device: DeviceModel;
|
||||
smart_results: SmartModel[];
|
||||
},
|
||||
metadata: { [key: string]: AttributeMetadataModel } | { [key: number]: AttributeMetadataModel };
|
||||
}
|
@ -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;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import {DeviceModel} from 'app/core/models/device-model';
|
||||
import {SmartTemperatureModel} from 'app/core/models/measurements/smart-temperature-model';
|
||||
|
||||
// maps to webapp/backend/pkg/models/device_summary.go
|
||||
export interface DeviceSummaryModel {
|
||||
device: DeviceModel;
|
||||
smart?: SmartSummary;
|
||||
temp_history?: SmartTemperatureModel[];
|
||||
}
|
||||
|
||||
export interface SmartSummary {
|
||||
collector_date?: string,
|
||||
temp?: number
|
||||
power_on_hours?: number
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
import {DeviceSummaryModel} from 'app/core/models/device-summary-model';
|
||||
|
||||
// maps to webapp/backend/pkg/models/device_summary.go
|
||||
export interface DeviceSummaryResponseWrapper {
|
||||
success: boolean;
|
||||
errors: any[];
|
||||
data: {
|
||||
summary: { [key: string]: DeviceSummaryModel }
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
import {SmartTemperatureModel} from './measurements/smart-temperature-model';
|
||||
|
||||
export interface DeviceSummaryTempResponseWrapper {
|
||||
success: boolean;
|
||||
errors: any[];
|
||||
data: {
|
||||
temp_history: { [key: string]: SmartTemperatureModel[]; }
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
// maps to webapp/backend/pkg/models/measurements/smart_ata_attribute.go
|
||||
// maps to webapp/backend/pkg/models/measurements/smart_nvme_attribute.go
|
||||
// maps to webapp/backend/pkg/models/measurements/smart_scsi_attribute.go
|
||||
export interface SmartAttributeModel {
|
||||
attribute_id: number | string
|
||||
value: number
|
||||
thresh: number
|
||||
worst?: number
|
||||
raw_value?: number
|
||||
raw_string?: string
|
||||
when_failed?: string
|
||||
|
||||
transformed_value: number
|
||||
status: number
|
||||
status_reason?: string
|
||||
failure_rate?: number
|
||||
|
||||
chartData?: any[]
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
// maps to webapp/backend/pkg/models/measurements/smart.go
|
||||
import {SmartAttributeModel} from './smart-attribute-model';
|
||||
|
||||
export interface SmartModel {
|
||||
date: string;
|
||||
device_wwn: string;
|
||||
device_protocol: string;
|
||||
|
||||
temp: number;
|
||||
power_on_hours: number;
|
||||
power_cycle_count: number
|
||||
attrs: { [key: string]: SmartAttributeModel }
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
// maps to webapp/backend/pkg/models/measurements/smart_temperature.go
|
||||
export interface SmartTemperatureModel {
|
||||
date: string;
|
||||
temp: number;
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
// map to webapp/backend/pkg/thresholds/ata_attribute_metadata.go
|
||||
// map to webapp/backend/pkg/thresholds/nvme_attribute_metadata.go
|
||||
// map to webapp/backend/pkg/thresholds/scsi_attribute_metadata.go
|
||||
export interface AttributeMetadataModel {
|
||||
display_name: string
|
||||
ideal: string
|
||||
critical: boolean
|
||||
description: string
|
||||
|
||||
transform_value_unit?: string
|
||||
observed_thresholds?: any[]
|
||||
display_type: string
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,25 +1,64 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {DashboardDeviceDeleteDialogComponent} from './dashboard-device-delete-dialog.component';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {MAT_DIALOG_DATA, MatDialogModule, MatDialogRef} from '@angular/material/dialog';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {SharedModule} from '../../../shared/shared.module';
|
||||
import {DashboardDeviceDeleteDialogService} from './dashboard-device-delete-dialog.service';
|
||||
import {of} from 'rxjs';
|
||||
|
||||
import { DashboardDeviceDeleteDialogComponent } from './dashboard-device-delete-dialog.component';
|
||||
|
||||
describe('DashboardDeviceDeleteDialogComponent', () => {
|
||||
let component: DashboardDeviceDeleteDialogComponent;
|
||||
let fixture: ComponentFixture<DashboardDeviceDeleteDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ DashboardDeviceDeleteDialogComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DashboardDeviceDeleteDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
let component: DashboardDeviceDeleteDialogComponent;
|
||||
let fixture: ComponentFixture<DashboardDeviceDeleteDialogComponent>;
|
||||
|
||||
const matDialogRefSpy = jasmine.createSpyObj('MatDialogRef', ['closeDialog', 'close']);
|
||||
const dashboardDeviceDeleteDialogServiceSpy = jasmine.createSpyObj('DashboardDeviceDeleteDialogService', ['deleteDevice']);
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
HttpClientModule,
|
||||
MatDialogModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
SharedModule,
|
||||
],
|
||||
providers: [
|
||||
{provide: MatDialogRef, useValue: matDialogRefSpy},
|
||||
{provide: MAT_DIALOG_DATA, useValue: {wwn: 'test-wwn', title: 'my-test-device-title'}},
|
||||
{provide: DashboardDeviceDeleteDialogService, useValue: dashboardDeviceDeleteDialogServiceSpy}
|
||||
],
|
||||
declarations: [DashboardDeviceDeleteDialogComponent]
|
||||
})
|
||||
.compileComponents()
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DashboardDeviceDeleteDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should close the component if cancel is clicked', () => {
|
||||
matDialogRefSpy.closeDialog.calls.reset();
|
||||
matDialogRefSpy.closeDialog()
|
||||
expect(matDialogRefSpy.closeDialog).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should attempt to delete device if delete is clicked', () => {
|
||||
dashboardDeviceDeleteDialogServiceSpy.deleteDevice.and.returnValue(of({'success': true}));
|
||||
|
||||
component.onDeleteClick()
|
||||
expect(dashboardDeviceDeleteDialogServiceSpy.deleteDevice).toHaveBeenCalledWith('test-wwn');
|
||||
expect(dashboardDeviceDeleteDialogServiceSpy.deleteDevice.calls.count())
|
||||
.withContext('one call')
|
||||
.toBe(1);
|
||||
});
|
||||
});
|
||||
|
@ -1,25 +1,105 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import { DashboardDeviceComponent } from './dashboard-device.component';
|
||||
import {DashboardDeviceComponent} from './dashboard-device.component';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {SharedModule} from 'app/shared/shared.module';
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
import {TREO_APP_CONFIG} from '@treo/services/config/config.constants';
|
||||
import {DeviceSummaryModel} from 'app/core/models/device-summary-model';
|
||||
import * as moment from 'moment';
|
||||
|
||||
describe('DashboardDeviceComponent', () => {
|
||||
let component: DashboardDeviceComponent;
|
||||
let fixture: ComponentFixture<DashboardDeviceComponent>;
|
||||
let component: DashboardDeviceComponent;
|
||||
let fixture: ComponentFixture<DashboardDeviceComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ DashboardDeviceComponent ]
|
||||
const matDialogSpy = jasmine.createSpyObj('MatDialog', ['open']);
|
||||
// const configServiceSpy = jasmine.createSpyObj('TreoConfigService', ['config$']);
|
||||
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatMenuModule,
|
||||
SharedModule,
|
||||
],
|
||||
providers: [
|
||||
{provide: MatDialog, useValue: matDialogSpy},
|
||||
{provide: TREO_APP_CONFIG, useValue: {dashboardDisplay: 'name'}}
|
||||
],
|
||||
declarations: [DashboardDeviceComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
// configServiceSpy.config$.and.returnValue(of({'success': true}));
|
||||
fixture = TestBed.createComponent(DashboardDeviceComponent);
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('#classDeviceLastUpdatedOn()', () => {
|
||||
|
||||
it('if non-zero device status, should be red', () => {
|
||||
// component.deviceSummary = summary.data.summary['0x5000c500673e6b5f'] as DeviceSummaryModel
|
||||
expect(component.classDeviceLastUpdatedOn({
|
||||
device: {
|
||||
device_status: 2
|
||||
}
|
||||
} as DeviceSummaryModel)).toBe('text-red')
|
||||
});
|
||||
|
||||
it('if non-zero device status, should be red', () => {
|
||||
// component.deviceSummary = summary.data.summary['0x5000c500673e6b5f'] as DeviceSummaryModel
|
||||
expect(component.classDeviceLastUpdatedOn({
|
||||
device: {
|
||||
device_status: 2
|
||||
}
|
||||
} as DeviceSummaryModel)).toBe('text-red')
|
||||
});
|
||||
|
||||
it('if healthy device status and updated in the last two weeks, should be green', () => {
|
||||
// component.deviceSummary = summary.data.summary['0x5000c500673e6b5f'] as DeviceSummaryModel
|
||||
expect(component.classDeviceLastUpdatedOn({
|
||||
device: {
|
||||
device_status: 0
|
||||
},
|
||||
smart: {
|
||||
collector_date: moment().subtract(13, 'days').toISOString()
|
||||
}
|
||||
} as DeviceSummaryModel)).toBe('text-green')
|
||||
});
|
||||
|
||||
it('if healthy device status and updated more than two weeks ago, but less than 1 month, should be yellow', () => {
|
||||
// component.deviceSummary = summary.data.summary['0x5000c500673e6b5f'] as DeviceSummaryModel
|
||||
expect(component.classDeviceLastUpdatedOn({
|
||||
device: {
|
||||
device_status: 0
|
||||
},
|
||||
smart: {
|
||||
collector_date: moment().subtract(3, 'weeks').toISOString()
|
||||
}
|
||||
} as DeviceSummaryModel)).toBe('text-yellow')
|
||||
});
|
||||
|
||||
it('if healthy device status and updated more 1 month ago, should be red', () => {
|
||||
// component.deviceSummary = summary.data.summary['0x5000c500673e6b5f'] as DeviceSummaryModel
|
||||
expect(component.classDeviceLastUpdatedOn({
|
||||
device: {
|
||||
device_status: 0
|
||||
},
|
||||
smart: {
|
||||
collector_date: moment().subtract(5, 'weeks').toISOString()
|
||||
}
|
||||
} as DeviceSummaryModel)).toBe('text-red')
|
||||
});
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DashboardDeviceComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1,53 +1,30 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { Overlay } from '@angular/cdk/overlay';
|
||||
import { MAT_AUTOCOMPLETE_SCROLL_STRATEGY, MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { SharedModule } from 'app/shared/shared.module';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {SharedModule} from 'app/shared/shared.module';
|
||||
import {DashboardDeviceComponent} from 'app/layout/common/dashboard-device/dashboard-device.component'
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatButtonToggleModule} from '@angular/material/button-toggle';
|
||||
import {MatTabsModule} from '@angular/material/tabs';
|
||||
import {MatSliderModule} from '@angular/material/slider';
|
||||
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
|
||||
import {MatTooltipModule} from '@angular/material/tooltip';
|
||||
import {dashboardRoutes} from '../../../modules/dashboard/dashboard.routing';
|
||||
import {MatDividerModule} from '@angular/material/divider';
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
import {MatProgressBarModule} from '@angular/material/progress-bar';
|
||||
import {MatSortModule} from '@angular/material/sort';
|
||||
import {MatTableModule} from '@angular/material/table';
|
||||
import {NgApexchartsModule} from 'ng-apexcharts';
|
||||
import {DashboardDeviceDeleteDialogModule} from 'app/layout/common/dashboard-device-delete-dialog/dashboard-device-delete-dialog.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
DashboardDeviceComponent
|
||||
],
|
||||
imports : [
|
||||
imports: [
|
||||
RouterModule.forChild([]),
|
||||
RouterModule.forChild(dashboardRoutes),
|
||||
MatButtonModule,
|
||||
MatDividerModule,
|
||||
MatTooltipModule,
|
||||
MatIconModule,
|
||||
MatMenuModule,
|
||||
MatProgressBarModule,
|
||||
MatSortModule,
|
||||
MatTableModule,
|
||||
NgApexchartsModule,
|
||||
SharedModule,
|
||||
DashboardDeviceDeleteDialogModule
|
||||
],
|
||||
exports : [
|
||||
exports: [
|
||||
DashboardDeviceComponent,
|
||||
],
|
||||
providers : []
|
||||
providers: []
|
||||
})
|
||||
export class DashboardDeviceModule
|
||||
{
|
||||
export class DashboardDeviceModule {
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DashboardSettingsComponent } from './dashboard-settings.component';
|
||||
|
||||
describe('DashboardSettingsComponent', () => {
|
||||
let component: DashboardSettingsComponent;
|
||||
let fixture: ComponentFixture<DashboardSettingsComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ DashboardSettingsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DashboardSettingsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,44 @@
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {DashboardService} from './dashboard.service';
|
||||
import {of} from 'rxjs';
|
||||
import {summary} from 'app/data/mock/summary/data'
|
||||
import {temp_history} from 'app/data/mock/summary/temp_history'
|
||||
import {DeviceSummaryModel} from 'app/core/models/device-summary-model';
|
||||
import {SmartTemperatureModel} from 'app/core/models/measurements/smart-temperature-model';
|
||||
|
||||
describe('DashboardService', () => {
|
||||
let service: DashboardService;
|
||||
let httpClientSpy: jasmine.SpyObj<HttpClient>;
|
||||
|
||||
beforeEach(() => {
|
||||
httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']);
|
||||
service = new DashboardService(httpClientSpy);
|
||||
});
|
||||
|
||||
it('should unwrap and return getSummaryData() (HttpClient called once)', (done: DoneFn) => {
|
||||
httpClientSpy.get.and.returnValue(of(summary));
|
||||
|
||||
service.getSummaryData().subscribe(value => {
|
||||
expect(value).toBe(summary.data.summary as { [key: string]: DeviceSummaryModel });
|
||||
done();
|
||||
});
|
||||
expect(httpClientSpy.get.calls.count())
|
||||
.withContext('one call')
|
||||
.toBe(1);
|
||||
});
|
||||
|
||||
it('should unwrap and return getSummaryTempData() (HttpClient called once)', (done: DoneFn) => {
|
||||
// const expectedHeroes: any[] =
|
||||
// [{ id: 1, name: 'A' }, { id: 2, name: 'B' }];
|
||||
|
||||
httpClientSpy.get.and.returnValue(of(temp_history));
|
||||
|
||||
service.getSummaryTempData('weekly').subscribe(value => {
|
||||
expect(value).toBe(temp_history.data.temp_history as { [key: string]: SmartTemperatureModel[] });
|
||||
done();
|
||||
});
|
||||
expect(httpClientSpy.get.calls.count())
|
||||
.withContext('one call')
|
||||
.toBe(1);
|
||||
});
|
||||
});
|
@ -1,25 +0,0 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DetailComponent } from './detail.component';
|
||||
|
||||
describe('DetailComponent', () => {
|
||||
let component: DetailComponent;
|
||||
let fixture: ComponentFixture<DetailComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ DetailComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DetailComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,28 @@
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {DetailService} from './detail.service';
|
||||
import {of} from 'rxjs';
|
||||
import {sda} from 'app/data/mock/device/details/sda'
|
||||
import {DeviceDetailsResponseWrapper} from 'app/core/models/device-details-response-wrapper';
|
||||
|
||||
describe('DetailService', () => {
|
||||
describe('#getData', () => {
|
||||
let service: DetailService;
|
||||
let httpClientSpy: jasmine.SpyObj<HttpClient>;
|
||||
|
||||
beforeEach(() => {
|
||||
httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']);
|
||||
service = new DetailService(httpClientSpy);
|
||||
});
|
||||
it('should return getData() (HttpClient called once)', (done: DoneFn) => {
|
||||
httpClientSpy.get.and.returnValue(of(sda));
|
||||
|
||||
service.getData('test').subscribe(value => {
|
||||
expect(value).toBe(sda as DeviceDetailsResponseWrapper);
|
||||
done();
|
||||
});
|
||||
expect(httpClientSpy.get.calls.count())
|
||||
.withContext('one call')
|
||||
.toBe(1);
|
||||
});
|
||||
})
|
||||
});
|
@ -1,8 +1,151 @@
|
||||
import { DeviceTitlePipe } from './device-title.pipe';
|
||||
import {DeviceTitlePipe} from './device-title.pipe';
|
||||
import {DeviceModel} from 'app/core/models/device-model';
|
||||
|
||||
describe('DeviceTitlePipe', () => {
|
||||
it('create an instance', () => {
|
||||
const pipe = new DeviceTitlePipe();
|
||||
expect(pipe).toBeTruthy();
|
||||
});
|
||||
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);
|
||||
});
|
||||
})
|
||||
})
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
});
|
@ -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);
|
||||
});
|
||||
})
|
||||
})
|
||||
});
|
||||
|
Loading…
Reference in new issue