i18n>The data received has the JSON format version 2.x and is currently incompatible with the dashboard.</cd-alert-panel>
<ng-container *ngIf="!error && !incompatible">
- <cd-alert-panel *ngIf="!(data | keyvalue).length"
+ <cd-alert-panel *ngIf="isEmpty(data)"
type="info"
i18n>No SMART data available.</cd-alert-panel>
- <tabset *ngFor="let device of data | keyvalue">
- <tab [heading]="device.value.device + ' (' + device.value.identifier + ')'">
- <ng-container *ngIf="device.value.error; else noError">
- <cd-alert-panel id="alert-error"
- type="warning">{{ device.value.userMessage }}</cd-alert-panel>
- </ng-container>
- <ng-template #noError>
- <!-- HDD/NVMe self test -->
- <ng-container *ngIf="device.value.info.smart_status.passed; else selfTestFailed">
- <cd-alert-panel id="alert-self-test-passed"
- size="slim"
- type="info"
- i18n-title
- title="SMART overall-health self-assessment test result"
- i18n>passed</cd-alert-panel>
+ <ng-container *ngIf="!isEmpty(data)">
+ <tabset>
+ <tab *ngFor="let device of data | keyvalue"
+ [heading]="device.value.device + ' (' + device.value.identifier + ')'">
+ <ng-container *ngIf="device.value.error; else noError">
+ <cd-alert-panel id="alert-error"
+ type="warning">{{ device.value.userMessage }}</cd-alert-panel>
</ng-container>
- <ng-template #selfTestFailed>
- <cd-alert-panel id="alert-self-test-failed"
+
+ <ng-template #noError>
+ <cd-alert-panel *ngIf="isEmpty(device.value.info?.smart_status); else hasSmartStatus"
+ id="alert-self-test-unknown"
size="slim"
type="warning"
i18n-title
title="SMART overall-health self-assessment test result"
- i18n>failed</cd-alert-panel>
+ i18n>unknown</cd-alert-panel>
+ <ng-template #hasSmartStatus>
+ <!-- HDD/NVMe self test -->
+ <ng-container *ngIf="device.value.info.smart_status.passed; else selfTestFailed">
+ <cd-alert-panel id="alert-self-test-passed"
+ size="slim"
+ type="info"
+ i18n-title
+ title="SMART overall-health self-assessment test result"
+ i18n>passed</cd-alert-panel>
+ </ng-container>
+ <ng-template #selfTestFailed>
+ <cd-alert-panel id="alert-self-test-failed"
+ size="slim"
+ type="warning"
+ i18n-title
+ title="SMART overall-health self-assessment test result"
+ i18n>failed</cd-alert-panel>
+ </ng-template>
+ </ng-template>
</ng-template>
- <tabset>
+ <tabset #innerTabset
+ *ngIf="!isEmpty(device.value.info) || !isEmpty(device.value.smart)">
<tab i18n-heading
heading="Device Information">
- <cd-table-key-value [renderObjects]="true"
+ <cd-table-key-value *ngIf="!isEmpty(device.value.info)"
+ [renderObjects]="true"
[data]="device.value.info"></cd-table-key-value>
+ <cd-alert-panel *ngIf="isEmpty(device.value.info)"
+ id="alert-device-info-unavailable"
+ type="info"
+ i18n>No device information available for this device.</cd-alert-panel>
</tab>
-
<tab i18n-heading
heading="SMART">
- <cd-table *ngIf="device.value.smart.attributes"
+ <cd-table *ngIf="device.value.smart?.attributes"
[data]="device.value.smart.attributes.table"
updateSelectionOnRefresh="never"
[columns]="smartDataColumns"></cd-table>
- <cd-table-key-value *ngIf="device.value.smart.nvmeData"
+ <cd-table-key-value *ngIf="device.value.smart?.nvmeData"
[renderObjects]="true"
[data]="device.value.smart.nvmeData"
updateSelectionOnRefresh="never"></cd-table-key-value>
- <cd-alert-panel *ngIf="!device.value.smart.attributes && !device.value.smart.nvmeData"
+ <cd-alert-panel *ngIf="!device.value.smart?.attributes && !device.value.smart?.nvmeData"
+ id="alert-device-smart-data-unavailable"
type="info"
i18n>No SMART data available for this device.</cd-alert-panel>
</tab>
</tabset>
- </ng-template>
- </tab>
- </tabset>
+ </tab>
+ </tabset>
+ </ng-container>
</ng-container>
</ng-container>
<ng-template #isLoading>
component.ngOnChanges(changes);
};
+ /**
+ * Verify an alert panel and its attributes.
+ *
+ * @param selector The CSS selector for the alert panel.
+ * @param panelTitle The title should be displayed.
+ * @param panelType Alert level of panel. Can be in `warning` or `info`.
+ * @param panelSize Pass `slim` for slim alert panel.
+ */
+ const verifyAlertPanel = (
+ selector: string,
+ panelTitle: string,
+ panelType: 'warning' | 'info',
+ panelSize?: 'slim'
+ ) => {
+ const alertPanel = fixture.debugElement.query(By.css(selector));
+ expect(component.incompatible).toBe(false);
+ expect(component.loading).toBe(false);
+
+ expect(alertPanel.attributes.type).toBe(panelType);
+ if (panelSize === 'slim') {
+ expect(alertPanel.attributes.title).toBe(panelTitle);
+ expect(alertPanel.attributes.size).toBe(panelSize);
+ } else {
+ const panelText = alertPanel.query(By.css('.alert-panel-text'));
+ expect(panelText.nativeElement.textContent).toBe(panelTitle);
+ }
+ };
+
configureTestBed({
declarations: [SmartListComponent],
imports: [BrowserAnimationsModule, TabsModule, SharedModule, HttpClientTestingModule],
it('should display info panel for passed self test', () => {
initializeComponentWithData('hdd_v1');
fixture.detectChanges();
- const alertPanel = fixture.debugElement.query(By.css('cd-alert-panel#alert-self-test-passed'));
- expect(component.incompatible).toBe(false);
- expect(component.loading).toBe(false);
- expect(alertPanel.attributes.size).toBe('slim');
- expect(alertPanel.attributes.title).toBe('SMART overall-health self-assessment test result');
- expect(alertPanel.attributes.type).toBe('info');
+ verifyAlertPanel(
+ 'cd-alert-panel#alert-self-test-passed',
+ 'SMART overall-health self-assessment test result',
+ 'info',
+ 'slim'
+ );
});
it('should display warning panel for failed self test', () => {
initializeComponentWithData('hdd_v1', { 'smart_status.passed': false });
fixture.detectChanges();
- const alertPanel = fixture.debugElement.query(By.css('cd-alert-panel#alert-self-test-failed'));
- expect(component.incompatible).toBe(false);
- expect(component.loading).toBe(false);
- expect(alertPanel.attributes.size).toBe('slim');
- expect(alertPanel.attributes.title).toBe('SMART overall-health self-assessment test result');
- expect(alertPanel.attributes.type).toBe('warning');
+ verifyAlertPanel(
+ 'cd-alert-panel#alert-self-test-failed',
+ 'SMART overall-health self-assessment test result',
+ 'warning',
+ 'slim'
+ );
+ });
+
+ it('should display warning panel for unknown self test', () => {
+ initializeComponentWithData('hdd_v1', { smart_status: undefined });
+ fixture.detectChanges();
+ verifyAlertPanel(
+ 'cd-alert-panel#alert-self-test-unknown',
+ 'SMART overall-health self-assessment test result',
+ 'warning',
+ 'slim'
+ );
+ });
+
+ it('should display info panel for empty device info', () => {
+ initializeComponentWithData('hdd_v1');
+ const deviceId: string = _.keys(component.data)[0];
+ component.data[deviceId]['info'] = {};
+ fixture.detectChanges();
+ component.innerTabset.tabs[0].active = true;
+ fixture.detectChanges();
+ verifyAlertPanel(
+ 'tab.active cd-alert-panel#alert-device-info-unavailable',
+ 'No device information available for this device.',
+ 'info'
+ );
+ });
+
+ it('should display info panel for empty SMART data', () => {
+ initializeComponentWithData('hdd_v1');
+ const deviceId: string = _.keys(component.data)[0];
+ component.data[deviceId]['smart'] = {};
+ fixture.detectChanges();
+ component.innerTabset.tabs[1].active = true;
+ fixture.detectChanges();
+ verifyAlertPanel(
+ 'tab.active cd-alert-panel#alert-device-smart-data-unavailable',
+ 'No SMART data available for this device.',
+ 'info'
+ );
});
});
-import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
+import { Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core';
import { I18n } from '@ngx-translate/i18n-polyfill';
import * as _ from 'lodash';
+import { TabsetComponent } from 'ngx-bootstrap/tabs';
import { HostService } from '../../../shared/api/host.service';
import { OsdService } from '../../../shared/api/osd.service';
styleUrls: ['./smart-list.component.scss']
})
export class SmartListComponent implements OnInit, OnChanges {
+ @ViewChild('innerTabset', { static: false })
+ innerTabset: TabsetComponent;
+
@Input()
osdId: number = null;
@Input()
}
}
+ isEmpty(value: any) {
+ return _.isEmpty(value);
+ }
+
ngOnInit() {
this.smartDataColumns = [
{ prop: 'id', name: this.i18n('ID') },