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>
- <ng-container *ngFor="let device of data | keyvalue">
+ <ng-container *ngIf="!isEmpty(data)">
<ul ngbNav
#nav="ngbNav"
class="nav-tabs">
- <li ngbNavItem>
- <a ngbNavLink
- i18n>{{ device.value.device }} ({{ device.value.identifier }})</a>
+ <li ngbNavItem
+ *ngFor="let device of data | keyvalue">
+ <a ngbNavLink>{{ device.value.device }} ({{ device.value.identifier }})</a>
<ng-template ngbNavContent>
-
<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>
- <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>
+ <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>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>
- <ul ngbNav
- #innerNav="ngbNav"
- class="nav-tabs">
- <li ngbNavItem>
- <a ngbNavLink
- i18n>Device Information</a>
- <ng-template ngbNavContent>
- <cd-table-key-value [renderObjects]="true"
- [data]="device.value.info"></cd-table-key-value>
- </ng-template>
- </li>
- <li ngbNavItem>
- <a ngbNavLink
- i18n>SMART</a>
- <ng-template ngbNavContent>
- <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"
- [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"
- type="info"
- i18n>No SMART data available for this device.</cd-alert-panel>
- </ng-template>
- </li>
- </ul>
+ <ng-container *ngIf="!isEmpty(device.value.info) || !isEmpty(device.value.smart)">
+ <ul ngbNav
+ #innerNav="ngbNav"
+ class="nav-tabs">
+ <li [ngbNavItem]="1">
+ <a ngbNavLink
+ i18n>Device Information</a>
+ <ng-template ngbNavContent>
+ <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>
+ </ng-template>
+ </li>
+ <li [ngbNavItem]="2">
+ <a ngbNavLink
+ i18n>SMART</a>
+ <ng-template ngbNavContent>
+ <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"
+ [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"
+ id="alert-device-smart-data-unavailable"
+ type="info"
+ i18n>No SMART data available for this device.</cd-alert-panel>
+ </ng-template>
+ </li>
+ </ul>
- <div [ngbNavOutlet]="innerNav"></div>
+ <div [ngbNavOutlet]="innerNav"></div>
+ </ng-container>
</ng-template>
</li>
</ul>
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, SharedModule, HttpClientTestingModule, NgbNavModule]
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.nav.select(1);
+ fixture.detectChanges();
+ verifyAlertPanel(
+ '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.nav.select(2);
+ fixture.detectChanges();
+ verifyAlertPanel(
+ '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 { NgbNav } from '@ng-bootstrap/ng-bootstrap';
import _ from 'lodash';
import { HostService } from '../../../shared/api/host.service';
styleUrls: ['./smart-list.component.scss']
})
export class SmartListComponent implements OnInit, OnChanges {
+ @ViewChild('innerNav')
+ nav: NgbNav;
+
@Input()
osdId: number = null;
@Input()
}
}
+ isEmpty(value: any) {
+ return _.isEmpty(value);
+ }
+
ngOnInit() {
this.smartDataColumns = [
{ prop: 'id', name: $localize`ID` },