From 0a1d273697305a12b8ab6b3602c1fdb0820431de Mon Sep 17 00:00:00 2001 From: Boris Vasilenko <bvasilenko@ixsystems.com> Date: Wed, 11 Dec 2024 17:16:36 +0300 Subject: [PATCH] NAS-132636: Fix unit tests (Part 2) --- .../ix-dynamic-form-item.component.spec.ts | 69 +++++++------------ .../global-search-trigger.component.spec.ts | 3 +- .../global-search/global-search.component.ts | 19 +++-- .../scheduler-preview-column.component.ts | 18 +++-- .../custom-app-button.component.spec.ts | 7 +- src/app/pages/audit/audit.component.spec.ts | 2 +- .../widget-group-slot-form.component.ts | 15 ++-- .../network-chart.component.spec.ts | 26 +++++-- .../widget-interface.component.spec.ts | 12 ++-- .../manual-selection-disks.component.spec.ts | 1 + .../bootenv-status.component.spec.ts | 1 + .../qr-viewer/qr-viewer.component.spec.ts | 9 +-- .../two-factor.component.spec.ts | 5 +- 13 files changed, 95 insertions(+), 92 deletions(-) diff --git a/src/app/modules/forms/ix-dynamic-form/components/ix-dynamic-form/ix-dynamic-form-item/ix-dynamic-form-item.component.spec.ts b/src/app/modules/forms/ix-dynamic-form/components/ix-dynamic-form/ix-dynamic-form-item/ix-dynamic-form-item.component.spec.ts index b5ab839591a..707406f0c78 100644 --- a/src/app/modules/forms/ix-dynamic-form/components/ix-dynamic-form/ix-dynamic-form-item/ix-dynamic-form-item.component.spec.ts +++ b/src/app/modules/forms/ix-dynamic-form/components/ix-dynamic-form/ix-dynamic-form-item/ix-dynamic-form-item.component.spec.ts @@ -2,7 +2,6 @@ import { FormArray, FormControl, FormGroup, ReactiveFormsModule, } from '@angular/forms'; import { createComponentFactory, Spectator } from '@ngneat/spectator/jest'; -import { MockComponent } from 'ng-mocks'; import { BehaviorSubject, of } from 'rxjs'; import { CodeEditorLanguage } from 'app/enums/code-editor-language.enum'; import { @@ -20,15 +19,12 @@ import { CustomUntypedFormField } from 'app/modules/forms/ix-dynamic-form/compon import { IxDynamicFormItemComponent } from 'app/modules/forms/ix-dynamic-form/components/ix-dynamic-form/ix-dynamic-form-item/ix-dynamic-form-item.component'; import { IxCheckboxComponent } from 'app/modules/forms/ix-forms/components/ix-checkbox/ix-checkbox.component'; import { IxCodeEditorComponent } from 'app/modules/forms/ix-forms/components/ix-code-editor/ix-code-editor.component'; -import { IxErrorsComponent } from 'app/modules/forms/ix-forms/components/ix-errors/ix-errors.component'; import { IxExplorerComponent } from 'app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component'; import { IxInputComponent } from 'app/modules/forms/ix-forms/components/ix-input/ix-input.component'; import { IxIpInputWithNetmaskComponent } from 'app/modules/forms/ix-forms/components/ix-ip-input-with-netmask/ix-ip-input-with-netmask.component'; -import { IxLabelComponent } from 'app/modules/forms/ix-forms/components/ix-label/ix-label.component'; import { IxListItemComponent } from 'app/modules/forms/ix-forms/components/ix-list/ix-list-item/ix-list-item.component'; import { IxListComponent } from 'app/modules/forms/ix-forms/components/ix-list/ix-list.component'; import { IxSelectComponent } from 'app/modules/forms/ix-forms/components/ix-select/ix-select.component'; -import { CastPipe } from 'app/modules/pipes/cast/cast.pipe'; const dynamicForm = new FormGroup({ dict: new FormGroup({ @@ -138,19 +134,6 @@ describe('IxDynamicFormItemComponent', () => { imports: [ ReactiveFormsModule, ], - declarations: [ - MockComponent(IxErrorsComponent), - MockComponent(IxLabelComponent), - MockComponent(IxInputComponent), - MockComponent(IxListComponent), - MockComponent(IxCodeEditorComponent), - MockComponent(IxListItemComponent), - MockComponent(IxSelectComponent), - MockComponent(IxCheckboxComponent), - MockComponent(IxIpInputWithNetmaskComponent), - MockComponent(IxExplorerComponent), - CastPipe, - ], }); beforeEach(() => { @@ -166,12 +149,12 @@ describe('IxDynamicFormItemComponent', () => { }, }); expect(spectator.query('ix-input')).toBeVisible(); - expect(spectator.query(IxInputComponent).required).toBe(inputSchema.required); - expect(spectator.query(IxInputComponent).type).toBe(inputSchema.inputType); - expect(spectator.query(IxInputComponent).tooltip).toBe(inputSchema.tooltip); + expect(spectator.query(IxInputComponent).required()).toBe(inputSchema.required); + expect(spectator.query(IxInputComponent).type()).toBe(inputSchema.inputType); + expect(spectator.query(IxInputComponent).tooltip()).toBe(inputSchema.tooltip); expect(spectator.query('ix-input')).not.toBeHidden(); - const field = spectator.component.dynamicForm.controls.input as CustomUntypedFormField; + const field = spectator.component.dynamicForm().controls.input as CustomUntypedFormField; if (!field.hidden$) { field.hidden$ = new BehaviorSubject<boolean>(false); } @@ -188,10 +171,10 @@ describe('IxDynamicFormItemComponent', () => { }, }); expect(spectator.query('ix-code-editor')).toBeVisible(); - expect(spectator.query(IxCodeEditorComponent).required).toBe(textSchema.required); - expect(spectator.query(IxCodeEditorComponent).tooltip).toBe(textSchema.tooltip); + expect(spectator.query(IxCodeEditorComponent).required()).toBe(textSchema.required); + expect(spectator.query(IxCodeEditorComponent).tooltip()).toBe(textSchema.tooltip); expect(spectator.query('ix-code-editor')).not.toBeHidden(); - const field = spectator.component.dynamicForm.controls.text as CustomUntypedFormField; + const field = spectator.component.dynamicForm().controls.text as CustomUntypedFormField; if (!field.hidden$) { field.hidden$ = new BehaviorSubject<boolean>(false); } @@ -208,12 +191,12 @@ describe('IxDynamicFormItemComponent', () => { }, }); expect(spectator.query('ix-select')).toBeVisible(); - expect(spectator.query(IxSelectComponent).required).toBe(selectSchema.required); - expect(spectator.query(IxSelectComponent).hideEmpty).toBe(selectSchema.hideEmpty); - expect(spectator.query(IxSelectComponent).tooltip).toBe(selectSchema.tooltip); + expect(spectator.query(IxSelectComponent).required()).toBe(selectSchema.required); + expect(spectator.query(IxSelectComponent).hideEmpty()).toBe(selectSchema.hideEmpty); + expect(spectator.query(IxSelectComponent).tooltip()).toBe(selectSchema.tooltip); expect(spectator.query('ix-select')).not.toBeHidden(); - const field = spectator.component.dynamicForm.controls.select as CustomUntypedFormField; + const field = spectator.component.dynamicForm().controls.select as CustomUntypedFormField; if (!field.hidden$) { field.hidden$ = new BehaviorSubject<boolean>(false); } @@ -230,11 +213,11 @@ describe('IxDynamicFormItemComponent', () => { }, }); expect(spectator.query('ix-checkbox')).toBeVisible(); - expect(spectator.query(IxCheckboxComponent).required).toBe(checkboxSchema.required); - expect(spectator.query(IxCheckboxComponent).tooltip).toBe(checkboxSchema.tooltip); + expect(spectator.query(IxCheckboxComponent).required()).toBe(checkboxSchema.required); + expect(spectator.query(IxCheckboxComponent).tooltip()).toBe(checkboxSchema.tooltip); expect(spectator.query('ix-checkbox')).not.toBeHidden(); - const field = spectator.component.dynamicForm.controls.checkbox as CustomUntypedFormField; + const field = spectator.component.dynamicForm().controls.checkbox as CustomUntypedFormField; if (!field.hidden$) { field.hidden$ = new BehaviorSubject<boolean>(false); } @@ -251,11 +234,11 @@ describe('IxDynamicFormItemComponent', () => { }, }); expect(spectator.query('ix-ip-input-with-netmask')).toBeVisible(); - expect(spectator.query(IxIpInputWithNetmaskComponent).required).toBe(ipaddrSchema.required); - expect(spectator.query(IxIpInputWithNetmaskComponent).tooltip).toBe(ipaddrSchema.tooltip); + expect(spectator.query(IxIpInputWithNetmaskComponent).required()).toBe(ipaddrSchema.required); + expect(spectator.query(IxIpInputWithNetmaskComponent).tooltip()).toBe(ipaddrSchema.tooltip); expect(spectator.query('ix-ip-input-with-netmask')).not.toBeHidden(); - const field = spectator.component.dynamicForm.controls.ipaddr as CustomUntypedFormField; + const field = spectator.component.dynamicForm().controls.ipaddr as CustomUntypedFormField; if (!field.hidden$) { field.hidden$ = new BehaviorSubject<boolean>(false); } @@ -272,11 +255,11 @@ describe('IxDynamicFormItemComponent', () => { }, }); expect(spectator.query('ix-explorer')).toBeVisible(); - expect(spectator.query(IxExplorerComponent).required).toBe(explorerSchema.required); - expect(spectator.query(IxExplorerComponent).tooltip).toBe(explorerSchema.tooltip); + expect(spectator.query(IxExplorerComponent).required()).toBe(explorerSchema.required); + expect(spectator.query(IxExplorerComponent).tooltip()).toBe(explorerSchema.tooltip); expect(spectator.query('ix-explorer')).not.toBeHidden(); - const field = spectator.component.dynamicForm.controls.explorer as CustomUntypedFormField; + const field = spectator.component.dynamicForm().controls.explorer as CustomUntypedFormField; if (!field.hidden$) { field.hidden$ = new BehaviorSubject<boolean>(false); } @@ -295,11 +278,11 @@ describe('IxDynamicFormItemComponent', () => { expect(spectator.query('ix-list')).toBeVisible(); expect(spectator.queryAll('ix-list-item')).toHaveLength(1); expect(spectator.queryAll('ix-dynamic-form-item')).toHaveLength(listSchema.items.length); - expect(spectator.query(IxListComponent).empty).toBe(false); - expect(spectator.query(IxListComponent).label).toBe(listSchema.title); + expect(spectator.query(IxListComponent).empty()).toBe(false); + expect(spectator.query(IxListComponent).label()).toBe(listSchema.title); expect(spectator.query('ix-list')).not.toBeHidden(); - const field = spectator.component.dynamicForm.controls.list as CustomUntypedFormField; + const field = spectator.component.dynamicForm().controls.list as CustomUntypedFormField; if (!field.hidden$) { field.hidden$ = new BehaviorSubject<boolean>(false); } @@ -322,7 +305,7 @@ describe('IxDynamicFormItemComponent', () => { expect(item).not.toBeHidden(); }); - const field = spectator.component.dynamicForm.controls.dict as CustomUntypedFormField; + const field = spectator.component.dynamicForm().controls.dict as CustomUntypedFormField; if (!field.hidden$) { field.hidden$ = new BehaviorSubject<boolean>(false); } @@ -344,7 +327,7 @@ describe('IxDynamicFormItemComponent', () => { }, }); - const field = spectator.component.dynamicForm.controls.list as CustomUntypedFormField; + const field = spectator.component.dynamicForm().controls.list as CustomUntypedFormField; if (!field.hidden$) { field.hidden$ = new BehaviorSubject<boolean>(false); } @@ -368,7 +351,7 @@ describe('IxDynamicFormItemComponent', () => { }, }); - const field = spectator.component.dynamicForm.controls.list as CustomUntypedFormField; + const field = spectator.component.dynamicForm().controls.list as CustomUntypedFormField; if (!field.hidden$) { field.hidden$ = new BehaviorSubject<boolean>(false); } diff --git a/src/app/modules/global-search/components/global-search-trigger/global-search-trigger.component.spec.ts b/src/app/modules/global-search/components/global-search-trigger/global-search-trigger.component.spec.ts index 9ed18f19cc9..a9a479659be 100644 --- a/src/app/modules/global-search/components/global-search-trigger/global-search-trigger.component.spec.ts +++ b/src/app/modules/global-search/components/global-search-trigger/global-search-trigger.component.spec.ts @@ -21,7 +21,6 @@ describe('GlobalSearchTriggerComponent', () => { imports: [ MockComponent(KeyboardShortcutComponent), MockComponent(GlobalSearchComponent), - MatInput, ], providers: [ mockProvider(UiSearchProvider, { @@ -43,7 +42,7 @@ describe('GlobalSearchTriggerComponent', () => { }); it('renders and input prompting for search', () => { - const input = spectator.query('input'); + const input = spectator.query(MatInput); expect(input).toExist(); expect(input).toHaveAttribute('placeholder', 'Search UI'); }); diff --git a/src/app/modules/global-search/components/global-search/global-search.component.ts b/src/app/modules/global-search/components/global-search/global-search.component.ts index 1e1d37f3a15..f827e373bd3 100644 --- a/src/app/modules/global-search/components/global-search/global-search.component.ts +++ b/src/app/modules/global-search/components/global-search/global-search.component.ts @@ -1,11 +1,10 @@ import { CdkTrapFocus } from '@angular/cdk/a11y'; import { DOCUMENT } from '@angular/common'; import { - Component, ChangeDetectionStrategy, OnInit, ElementRef, ChangeDetectorRef, + Component, ChangeDetectionStrategy, OnInit, ViewChild, ElementRef, ChangeDetectorRef, Inject, AfterViewInit, OnDestroy, - viewChild, } from '@angular/core'; import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { MatInput } from '@angular/material/input'; @@ -54,8 +53,8 @@ import { waitForSystemInfo } from 'app/store/system-info/system-info.selectors'; ], }) export class GlobalSearchComponent implements OnInit, AfterViewInit, OnDestroy { - readonly searchInput = viewChild<ElementRef<HTMLInputElement>>('searchInput'); - readonly searchBoxWrapper = viewChild<ElementRef<HTMLElement>>('searchBoxWrapper'); + @ViewChild('searchInput') searchInput: ElementRef<HTMLInputElement>; + @ViewChild('searchBoxWrapper') searchBoxWrapper: ElementRef<HTMLElement>; searchControl = new FormControl<string>(''); searchResults: UiSearchableElement[]; @@ -64,7 +63,7 @@ export class GlobalSearchComponent implements OnInit, AfterViewInit, OnDestroy { detachOverlay: () => void; // passed from global-search-trigger get isSearchInputFocused(): boolean { - return document.activeElement === this.searchInput()?.nativeElement; + return document.activeElement === this.searchInput?.nativeElement; } constructor( @@ -89,11 +88,11 @@ export class GlobalSearchComponent implements OnInit, AfterViewInit, OnDestroy { } ngAfterViewInit(): void { - this.searchBoxWrapper().nativeElement.addEventListener('focusout', this.handleFocusOut.bind(this)); + this.searchBoxWrapper.nativeElement.addEventListener('focusout', this.handleFocusOut.bind(this)); } ngOnDestroy(): void { - this.searchBoxWrapper().nativeElement.removeEventListener('focusout', this.handleFocusOut.bind(this)); + this.searchBoxWrapper.nativeElement.removeEventListener('focusout', this.handleFocusOut.bind(this)); } handleKeyDown(event: KeyboardEvent): void { @@ -178,7 +177,7 @@ export class GlobalSearchComponent implements OnInit, AfterViewInit, OnDestroy { } private focusInputElement(): void { - this.searchInput().nativeElement?.focus(); + this.searchInput.nativeElement?.focus(); } private getSystemVersion(): void { @@ -211,7 +210,7 @@ export class GlobalSearchComponent implements OnInit, AfterViewInit, OnDestroy { } private handleTabOutFromGlobalSearch(event: KeyboardEvent): void { - const focusableElements = this.focusService.getFocusableElements(this.searchBoxWrapper().nativeElement); + const focusableElements = this.focusService.getFocusableElements(this.searchBoxWrapper.nativeElement); const firstElement = focusableElements[0]; const lastElement = focusableElements[focusableElements.length - 1]; @@ -230,7 +229,7 @@ export class GlobalSearchComponent implements OnInit, AfterViewInit, OnDestroy { private handleFocusOut(event: FocusEvent): void { const relatedTarget = event.relatedTarget as HTMLElement; - if (relatedTarget && !this.searchBoxWrapper().nativeElement.contains(relatedTarget)) { + if (relatedTarget && !this.searchBoxWrapper.nativeElement.contains(relatedTarget)) { this.detachOverlay(); } } diff --git a/src/app/modules/scheduler/components/scheduler-modal/scheduler-preview-column/scheduler-preview-column.component.ts b/src/app/modules/scheduler/components/scheduler-modal/scheduler-preview-column/scheduler-preview-column.component.ts index 80688895f6d..32b9ff5026a 100644 --- a/src/app/modules/scheduler/components/scheduler-modal/scheduler-preview-column/scheduler-preview-column.component.ts +++ b/src/app/modules/scheduler/components/scheduler-modal/scheduler-preview-column/scheduler-preview-column.component.ts @@ -4,7 +4,7 @@ import { input, OnChanges, OnInit, - viewChild, + ViewChild, } from '@angular/core'; import { MatIconButton } from '@angular/material/button'; import { MatCalendar, MatCalendarCellClassFunction } from '@angular/material/datepicker'; @@ -54,19 +54,18 @@ export class SchedulerPreviewColumnComponent implements OnChanges, OnInit { cronPreview: CronSchedulePreview; - readonly calendar = viewChild<MatCalendar<Date>>('calendar'); + @ViewChild('calendar', { static: true }) calendar: MatCalendar<Date>; get startDate(): Date { - const calendar = this.calendar(); - if (!calendar.activeDate || differenceInCalendarMonths(calendar.activeDate, new Date()) < 1) { + if (!this.calendar.activeDate || differenceInCalendarMonths(this.calendar.activeDate, new Date()) < 1) { return utcToZonedTime(new Date(), this.timezone()); } - return startOfMonth(calendar.activeDate); + return startOfMonth(this.calendar.activeDate); } get isPastMonth(): boolean { - return isBefore(this.calendar().activeDate, startOfMonth(new Date())); + return isBefore(this.calendar.activeDate, startOfMonth(new Date())); } ngOnChanges(): void { @@ -75,7 +74,7 @@ export class SchedulerPreviewColumnComponent implements OnChanges, OnInit { } ngOnInit(): void { - this.calendar().stateChanges + this.calendar.stateChanges .pipe(untilDestroyed(this)) .subscribe(() => this.onCalendarUpdated()); } @@ -114,11 +113,10 @@ export class SchedulerPreviewColumnComponent implements OnChanges, OnInit { } private refreshCalendar(): void { - const calendar = this.calendar(); - if (!calendar.monthView) { + if (!this.calendar.monthView) { return; } - calendar.updateTodaysDate(); + this.calendar.updateTodaysDate(); } } diff --git a/src/app/pages/apps/components/available-apps/custom-app-button/custom-app-button.component.spec.ts b/src/app/pages/apps/components/available-apps/custom-app-button/custom-app-button.component.spec.ts index 86b80ffcad6..8420ad34dae 100644 --- a/src/app/pages/apps/components/available-apps/custom-app-button/custom-app-button.component.spec.ts +++ b/src/app/pages/apps/components/available-apps/custom-app-button/custom-app-button.component.spec.ts @@ -5,6 +5,7 @@ import { MatMenuHarness } from '@angular/material/menu/testing'; import { Router } from '@angular/router'; import { SpectatorRouting } from '@ngneat/spectator'; import { createRoutingFactory, mockProvider } from '@ngneat/spectator/jest'; +import { LazyLoadImageDirective } from 'ng-lazyload-image'; import { MockComponent } from 'ng-mocks'; import { of, Subject } from 'rxjs'; import { customAppTrain, customApp } from 'app/constants/catalog.constants'; @@ -22,8 +23,10 @@ describe('CustomAppButtonComponent', () => { const createComponent = createRoutingFactory({ component: CustomAppButtonComponent, - imports: [], - declarations: [MockComponent(AppCardComponent)], + imports: [ + LazyLoadImageDirective, + MockComponent(AppCardComponent), + ], providers: [ mockAuth(), mockProvider(DockerStore, { diff --git a/src/app/pages/audit/audit.component.spec.ts b/src/app/pages/audit/audit.component.spec.ts index e5091574eb7..5b9c5672f2e 100644 --- a/src/app/pages/audit/audit.component.spec.ts +++ b/src/app/pages/audit/audit.component.spec.ts @@ -120,7 +120,7 @@ describe('AuditComponent', () => { it('loads and shows a table with audit entries', async () => { expect(api.call).toHaveBeenCalledWith( 'audit.query', - [{ 'query-filters': [], 'query-options': { limit: 50, offset: 0, order_by: ['-message_timestamp'] } }], + [{ 'query-filters': [], 'query-options': { limit: 50, offset: 0, order_by: ['-message_timestamp'] }, remote_controller: false }], ); await spectator.fixture.whenStable(); diff --git a/src/app/pages/dashboard/components/widget-group-form/widget-group-slot-form/widget-group-slot-form.component.ts b/src/app/pages/dashboard/components/widget-group-form/widget-group-slot-form/widget-group-slot-form.component.ts index 00572362b83..3ec3040ca7c 100644 --- a/src/app/pages/dashboard/components/widget-group-form/widget-group-slot-form/widget-group-slot-form.component.ts +++ b/src/app/pages/dashboard/components/widget-group-form/widget-group-slot-form/widget-group-slot-form.component.ts @@ -9,6 +9,7 @@ import { OnInit, Signal, Type, + ViewChild, ViewContainerRef, WritableSignal, computed, @@ -17,7 +18,6 @@ import { output, runInInjectionContext, signal, - viewChild, } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; import { @@ -69,7 +69,7 @@ export class WidgetGroupSlotFormComponent implements OnInit, AfterViewInit, OnCh : false; } - readonly settingsContainer = viewChild('settingsContainer', { read: ViewContainerRef }); + @ViewChild('settingsContainer', { static: true, read: ViewContainerRef }) settingsContainer: ViewContainerRef; widgetCategoriesOptions = computed<Observable<Option[]>>(() => { const layoutSupportedWidgets = this.getLayoutSupportedWidgets(); const uniqCategories = new Set(layoutSupportedWidgets.map((widget) => widget.category)); @@ -222,7 +222,7 @@ export class WidgetGroupSlotFormComponent implements OnInit, AfterViewInit, OnCh clearUpdates(): void { this.categorySubscription?.unsubscribe(); this.typeSubscription?.unsubscribe(); - this.settingsContainer()?.clear(); + this.settingsContainer?.clear(); } private setLayoutSupportedWidgets(): void { @@ -257,12 +257,11 @@ export class WidgetGroupSlotFormComponent implements OnInit, AfterViewInit, OnCh } private refreshSettingsContainer(): void { - const settingsContainer = this.settingsContainer(); - if (!settingsContainer) { + if (!this.settingsContainer) { return; } - settingsContainer.remove(); - settingsContainer.clear(); + this.settingsContainer.remove(); + this.settingsContainer.clear(); const slotConfig = this.slot(); if (slotConfig) { this.validityChange.emit([slotConfig.slotPosition, {} as ValidationErrors]); @@ -275,7 +274,7 @@ export class WidgetGroupSlotFormComponent implements OnInit, AfterViewInit, OnCh return; } - settingsContainer.createComponent(settingsComponent, { injector: this.getInjector() }); + this.settingsContainer.createComponent(settingsComponent, { injector: this.getInjector() }); } getInjector(): Injector { diff --git a/src/app/pages/dashboard/widgets/network/common/network-chart/network-chart.component.spec.ts b/src/app/pages/dashboard/widgets/network/common/network-chart/network-chart.component.spec.ts index a9c810a71ae..d00e15adec1 100644 --- a/src/app/pages/dashboard/widgets/network/common/network-chart/network-chart.component.spec.ts +++ b/src/app/pages/dashboard/widgets/network/common/network-chart/network-chart.component.spec.ts @@ -1,6 +1,6 @@ +import { Component, ChangeDetectionStrategy, input } from '@angular/core'; import { Spectator } from '@ngneat/spectator'; import { createComponentFactory, mockProvider } from '@ngneat/spectator/jest'; -import { MockComponent } from 'ng-mocks'; import { ViewChartAreaComponent } from 'app/modules/charts/view-chart-area/view-chart-area.component'; import { NetworkChartComponent } from 'app/pages/dashboard/widgets/network/common/network-chart/network-chart.component'; import { LocaleService } from 'app/services/locale.service'; @@ -8,12 +8,28 @@ import { LocaleService } from 'app/services/locale.service'; // TODO: Update when fix is ready // See https://github.com/help-me-mom/ng-mocks/issues/8634 +@Component({ + selector: 'ix-view-chart-area-mock', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, +}) +class ViewChartAreaMockComponent { + data = input(); + options = input(); +} + describe('NetworkChartComponent', () => { let spectator: Spectator<NetworkChartComponent>; const createComponent = createComponentFactory({ component: NetworkChartComponent, - declarations: [ - MockComponent(ViewChartAreaComponent), + overrideComponents: [ + [NetworkChartComponent, { + add: { + imports: [ViewChartAreaMockComponent], + template: '<ix-view-chart-area-mock [data]="data()" [options]="options()"></ix-view-chart-area-mock>', + }, + remove: { imports: [ViewChartAreaComponent] }, + }], ], providers: [ mockProvider(LocaleService, { @@ -32,10 +48,10 @@ describe('NetworkChartComponent', () => { spectator.setInput('data', { datasets: [], labels: [] }); spectator.detectChanges(); - const chart = spectator.query(ViewChartAreaComponent); + const chart = spectator.query(ViewChartAreaMockComponent); expect(chart).toBeTruthy(); - const data = chart.data; + const data = chart.data(); expect(data).toMatchObject({ datasets: [], labels: [], diff --git a/src/app/pages/dashboard/widgets/network/widget-interface/widget-interface.component.spec.ts b/src/app/pages/dashboard/widgets/network/widget-interface/widget-interface.component.spec.ts index 78a0d668310..b147673c496 100644 --- a/src/app/pages/dashboard/widgets/network/widget-interface/widget-interface.component.spec.ts +++ b/src/app/pages/dashboard/widgets/network/widget-interface/widget-interface.component.spec.ts @@ -91,6 +91,7 @@ describe('WidgetInterfaceComponent', () => { }); describe('Full Size', () => { + global.Date.now = jest.fn(() => (new Date('2024-07-23')).getTime()); // 1721689140000 beforeEach(() => { spectator = createComponent({ props: { @@ -123,8 +124,7 @@ describe('WidgetInterfaceComponent', () => { expect(spectator.query('.info-list-item.out')).toHaveText('Out:32.77 kb/s'); })); - it('shows a chart with network traffic', fakeAsync(() => { - spectator.tick(1); + it('shows a chart with network traffic', () => { startDate = Date.now() - oneHourMillis - oneMinuteMillis; const chart = spectator.query(NetworkChartComponent); expect(chart).not.toBeNull(); @@ -162,7 +162,7 @@ describe('WidgetInterfaceComponent', () => { }, ], }); - })); + }); it('checks first entry selection when settings are null', () => { spectator.setInput('settings', null); @@ -177,6 +177,7 @@ describe('WidgetInterfaceComponent', () => { describe('Half Size', () => { beforeEach(() => { + global.Date.now = jest.fn(() => (new Date('2024-07-23')).getTime()); // 1721689140000 spectator = createComponent({ props: { size: SlotSize.Half, @@ -207,8 +208,7 @@ describe('WidgetInterfaceComponent', () => { expect(spectator.query('.info-list-item.out')).toHaveText('Out:32.77 kb/s'); })); - it('shows a chart with network traffic', fakeAsync(() => { - spectator.tick(1); + it('shows a chart with network traffic', () => { startDate = Date.now() - oneHourMillis - oneMinuteMillis; const chart = spectator.query(NetworkChartComponent); expect(chart).not.toBeNull(); @@ -246,7 +246,7 @@ describe('WidgetInterfaceComponent', () => { }, ], }); - })); + }); }); describe('Quarter Size', () => { diff --git a/src/app/pages/storage/modules/pool-manager/components/manual-disk-selection/components/manual-selection-disks/manual-selection-disks.component.spec.ts b/src/app/pages/storage/modules/pool-manager/components/manual-disk-selection/components/manual-selection-disks/manual-selection-disks.component.spec.ts index f8c33cc9221..cc3d6191846 100644 --- a/src/app/pages/storage/modules/pool-manager/components/manual-disk-selection/components/manual-selection-disks/manual-selection-disks.component.spec.ts +++ b/src/app/pages/storage/modules/pool-manager/components/manual-disk-selection/components/manual-selection-disks/manual-selection-disks.component.spec.ts @@ -67,6 +67,7 @@ describe('ManualSelectionDisksComponent', () => { }); beforeEach(() => { + jest.spyOn(console, 'warn').mockImplementation(); spectator = createComponent({ props: { enclosures: [ diff --git a/src/app/pages/system/bootenv/bootenv-status/bootenv-status.component.spec.ts b/src/app/pages/system/bootenv/bootenv-status/bootenv-status.component.spec.ts index 5ffcff705b1..fb3e69b3cdd 100644 --- a/src/app/pages/system/bootenv/bootenv-status/bootenv-status.component.spec.ts +++ b/src/app/pages/system/bootenv/bootenv-status/bootenv-status.component.spec.ts @@ -83,6 +83,7 @@ describe('BootStatusListComponent', () => { }); beforeEach(() => { + jest.spyOn(console, 'warn').mockImplementation(); spectator = createComponent(); loader = TestbedHarnessEnvironment.loader(spectator.fixture); api = spectator.inject(MockApiService); diff --git a/src/app/pages/two-factor-auth/qr-viewer/qr-viewer.component.spec.ts b/src/app/pages/two-factor-auth/qr-viewer/qr-viewer.component.spec.ts index d82d3ea0885..428c3cf4f87 100644 --- a/src/app/pages/two-factor-auth/qr-viewer/qr-viewer.component.spec.ts +++ b/src/app/pages/two-factor-auth/qr-viewer/qr-viewer.component.spec.ts @@ -1,6 +1,7 @@ +import 'jest-canvas-mock'; import { Spectator, createComponentFactory } from '@ngneat/spectator/jest'; import { MockComponent, MockModule } from 'ng-mocks'; -import { QrCodeComponent, QrCodeModule } from 'ng-qrcode'; +import { QrCodeComponent, QrCodeDirective, QrCodeModule } from 'ng-qrcode'; import { helptext2fa } from 'app/helptext/system/2fa'; import { WarningComponent } from 'app/modules/forms/ix-forms/components/warning/warning.component'; import { QrViewerComponent } from 'app/pages/two-factor-auth/qr-viewer/qr-viewer.component'; @@ -10,10 +11,10 @@ describe('QrViewerComponent', () => { const createComponent = createComponentFactory({ component: QrViewerComponent, - declarations: [ - MockComponent(WarningComponent), - ], imports: [ + MockComponent(WarningComponent), + QrCodeComponent, + QrCodeDirective, MockModule(QrCodeModule), ], }); diff --git a/src/app/pages/two-factor-auth/two-factor.component.spec.ts b/src/app/pages/two-factor-auth/two-factor.component.spec.ts index 281e4e42353..89b29a921be 100644 --- a/src/app/pages/two-factor-auth/two-factor.component.spec.ts +++ b/src/app/pages/two-factor-auth/two-factor.component.spec.ts @@ -3,6 +3,7 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { MatButtonHarness } from '@angular/material/button/testing'; import { Spectator, createComponentFactory, mockProvider } from '@ngneat/spectator/jest'; import { MockComponent } from 'ng-mocks'; +import { QrCodeComponent, QrCodeDirective } from 'ng-qrcode'; import { of } from 'rxjs'; import { mockCall, mockApi } from 'app/core/testing/utils/mock-api.utils'; import { helptext2fa } from 'app/helptext/system/2fa'; @@ -23,7 +24,9 @@ describe('TwoFactorComponent', () => { const createComponent = createComponentFactory({ component: TwoFactorComponent, - declarations: [ + imports: [ + QrCodeComponent, + QrCodeDirective, MockComponent(WarningComponent), MockComponent(QrViewerComponent), MockComponent(CopyButtonComponent),