From c804f4f02ab5a4fa81f8f5d5c2c8ea87fe848eb8 Mon Sep 17 00:00:00 2001 From: Alex Karpov Date: Wed, 15 May 2024 18:12:31 +0300 Subject: [PATCH] NAS-128968: TrueCloud Backup shows non-Storj credentials --- package.json | 2 +- ...cloud-credentials-select.component.spec.ts | 121 ++++++++++++++++++ .../cloud-credentials-select.spec.ts | 69 ---------- .../ix-explorer/ix-explorer.component.html | 2 +- src/app/modules/ix-icon/ix-icon.constants.ts | 1 + .../dataset-icon.component.spec.ts | 4 +- .../dataset-icon/dataset-icon.component.ts | 4 +- .../services/navigation/navigation.service.ts | 2 +- src/assets/customicons/dataset.svg | 4 +- src/assets/customicons/dataset_root.svg | 3 + src/setup-jest.ts | 2 + 11 files changed, 136 insertions(+), 78 deletions(-) create mode 100644 src/app/modules/custom-selects/cloud-credentials-select/cloud-credentials-select.component.spec.ts delete mode 100644 src/app/modules/custom-selects/cloud-credentials-select/cloud-credentials-select.spec.ts create mode 100644 src/assets/customicons/dataset_root.svg diff --git a/package.json b/package.json index ea87d1b41a1..f96ef0fd117 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "test": "jest", "test:watch": "jest --watch", "test:ci": "jest --runInBand", - "test:pr": "yarn run check-env -s && echo 'Setting up temporary environment file...\\n' && yarn run ui remote -i 'headless.local' && jest --coverage --maxWorkers=2 src/app/pages/dashboard/components", + "test:pr": "yarn run check-env -s && echo 'Setting up temporary environment file...\\n' && yarn run ui remote -i 'headless.local' && jest --coverage --maxWorkers=2", "test:changed": "node scripts/test_changed.js", "lint": "ng lint && stylelint 'src/**/*.scss' && markuplint 'src/**/*.html'", "lint:fix": "ng lint --fix && stylelint --fix 'src/**/*.scss' && markuplint --fix 'src/**/*.html'", diff --git a/src/app/modules/custom-selects/cloud-credentials-select/cloud-credentials-select.component.spec.ts b/src/app/modules/custom-selects/cloud-credentials-select/cloud-credentials-select.component.spec.ts new file mode 100644 index 00000000000..8d9744fe2ae --- /dev/null +++ b/src/app/modules/custom-selects/cloud-credentials-select/cloud-credentials-select.component.spec.ts @@ -0,0 +1,121 @@ +import { HarnessLoader, parallel } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { + FormGroup, FormControl, ReactiveFormsModule, FormsModule, +} from '@angular/forms'; +import { TooltipComponent } from '@angular/material/tooltip'; +import { SpectatorHost } from '@ngneat/spectator'; +import { createHostFactory, mockProvider } from '@ngneat/spectator/jest'; +import { TranslateModule } from '@ngx-translate/core'; +import { MockComponent } from 'ng-mocks'; +import { of } from 'rxjs'; +import { CloudSyncProviderName } from 'app/enums/cloudsync-provider.enum'; +import { CloudCredentialsSelectComponent } from 'app/modules/custom-selects/cloud-credentials-select/cloud-credentials-select.component'; +import { IxErrorsComponent } from 'app/modules/ix-forms/components/ix-errors/ix-errors.component'; +import { IxLabelComponent } from 'app/modules/ix-forms/components/ix-label/ix-label.component'; +import { IxSelectComponent } from 'app/modules/ix-forms/components/ix-select/ix-select.component'; +import { IxSelectHarness } from 'app/modules/ix-forms/components/ix-select/ix-select.harness'; +import { IxFormsModule } from 'app/modules/ix-forms/ix-forms.module'; +import { CloudCredentialService } from 'app/services/cloud-credential.service'; + +describe('CloudCredentialsSelectComponent', () => { + let spectator: SpectatorHost; + let loader: HarnessLoader; + + const host = ` +
+ +
+ `; + + const defaultHostProps = { + form: new FormGroup({ + credentials: new FormControl(), + }), + label: '', + required: false, + tooltip: '', + }; + + const mockCloudCredentialService = { + getCloudSyncCredentials: jest.fn(() => of([ + { id: '1', name: 'AWS S3', provider: CloudSyncProviderName.AmazonS3 }, + { id: '2', name: 'Dropbox', provider: CloudSyncProviderName.Dropbox }, + { id: '2', name: 'Drive', provider: CloudSyncProviderName.GoogleDrive }, + ])), + }; + + const createHost = createHostFactory({ + component: CloudCredentialsSelectComponent, + imports: [ + TranslateModule.forRoot(), + IxFormsModule, + ReactiveFormsModule, + FormsModule, + ], + declarations: [ + IxSelectComponent, + MockComponent(IxErrorsComponent), + MockComponent(IxLabelComponent), + MockComponent(TooltipComponent), + ], + providers: [ + mockProvider(CloudCredentialService, mockCloudCredentialService), + ], + }); + + describe('no filter by providers set', () => { + beforeEach(() => { + spectator = createHost(host, { + hostProps: defaultHostProps, + }); + + loader = TestbedHarnessEnvironment.loader(spectator.fixture); + }); + + it('should populate ix-select with credentials when providers are set', async () => { + const select = await (await loader.getHarness(IxSelectHarness)).getSelectHarness(); + await select.open(); + const options = await select.getOptions(); + const optionLabels = await parallel(() => options.map((option) => option.getText())); + expect(optionLabels).toEqual([ + '--', + 'Add New', + 'AWS S3 (Amazon S3)', + 'Dropbox (Dropbox)', + 'Drive (Google Drive)', + ]); + }); + }); + + describe('filter by providers is set', () => { + beforeEach(() => { + spectator = createHost(host, { + hostProps: { + ...defaultHostProps, + filterByProviders: [CloudSyncProviderName.AmazonS3], + }, + }); + + loader = TestbedHarnessEnvironment.loader(spectator.fixture); + }); + + it('should populate ix-select with credentials when providers are set', async () => { + const select = await (await loader.getHarness(IxSelectHarness)).getSelectHarness(); + await select.open(); + const options = await select.getOptions(); + const optionLabels = await parallel(() => options.map((option) => option.getText())); + expect(optionLabels).toEqual([ + '--', + 'Add New', + 'AWS S3 (Amazon S3)', + ]); + }); + }); +}); diff --git a/src/app/modules/custom-selects/cloud-credentials-select/cloud-credentials-select.spec.ts b/src/app/modules/custom-selects/cloud-credentials-select/cloud-credentials-select.spec.ts deleted file mode 100644 index 5f45479efe0..00000000000 --- a/src/app/modules/custom-selects/cloud-credentials-select/cloud-credentials-select.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { TranslateService } from '@ngx-translate/core'; -import { Observable, of } from 'rxjs'; -import { CloudSyncProviderName } from 'app/enums/cloudsync-provider.enum'; -import { CloudCredentialsFormComponent } from 'app/pages/credentials/backup-credentials/cloud-credentials-form/cloud-credentials-form.component'; -import { CloudCredentialService } from 'app/services/cloud-credential.service'; -import { CloudCredentialsSelectComponent } from './cloud-credentials-select.component'; - -class MockTranslateService { - translate(key: string): Observable { - return of(key); - } -} - -const cloudCredentialServiceMock = { - getCloudSyncCredentials: jest.fn(() => of([ - { id: '1', name: 'AWS S3', provider: CloudSyncProviderName.AmazonS3 }, - { id: '2', name: 'Dropbox', provider: CloudSyncProviderName.Dropbox }, - ])), -}; - -describe('CloudCredentialsSelectComponent', () => { - let component: CloudCredentialsSelectComponent; - let fixture: ComponentFixture; - let cloudCredentialService: jest.Mocked; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [CloudCredentialsSelectComponent], - providers: [ - { - provide: CloudCredentialService, - useValue: cloudCredentialServiceMock, - }, - { - provide: TranslateService, - useClass: MockTranslateService, - }, - ], - schemas: [NO_ERRORS_SCHEMA], - }).compileComponents(); - - fixture = TestBed.createComponent(CloudCredentialsSelectComponent); - component = fixture.componentInstance; - cloudCredentialService = TestBed.inject(CloudCredentialService) as jest.Mocked; - }); - - it('should fetch options correctly', async () => { - const options = await fixture.componentInstance.fetchOptions().toPromise(); - expect(options).toEqual([ - { label: 'AWS S3 (Amazon S3)', value: '1' }, - { label: 'Dropbox (Dropbox)', value: '2' }, - ]); - expect(cloudCredentialService.getCloudSyncCredentials).toHaveBeenCalled(); - }); - - it('should fetch and filter options correctly', async () => { - component.filterByProviders = [CloudSyncProviderName.AmazonS3]; - - const options = await fixture.componentInstance.fetchOptions().toPromise(); - expect(options).toEqual([{ label: 'AWS S3 (Amazon S3)', value: '1' }]); - expect(cloudCredentialService.getCloudSyncCredentials).toHaveBeenCalled(); - }); - - it('should return the correct form component type', () => { - expect(component.getFormComponentType()).toBe(CloudCredentialsFormComponent); - }); -}); diff --git a/src/app/modules/ix-forms/components/ix-explorer/ix-explorer.component.html b/src/app/modules/ix-forms/components/ix-explorer/ix-explorer.component.html index 3e2a93a6ac3..910d45ea87b 100644 --- a/src/app/modules/ix-forms/components/ix-explorer/ix-explorer.component.html +++ b/src/app/modules/ix-forms/components/ix-explorer/ix-explorer.component.html @@ -60,7 +60,7 @@ - + diff --git a/src/app/modules/ix-icon/ix-icon.constants.ts b/src/app/modules/ix-icon/ix-icon.constants.ts index 7955771bd6d..644ddb0310b 100644 --- a/src/app/modules/ix-icon/ix-icon.constants.ts +++ b/src/app/modules/ix-icon/ix-icon.constants.ts @@ -9,6 +9,7 @@ export const ixSvgIcons = { anti_lock: 'assets/customicons/anti-lock.svg', two_factor_auth: 'assets/customicons/2fa.svg', dataset: 'assets/customicons/dataset.svg', + dataset_root: 'assets/customicons/dataset_root.svg', ha_disabled: 'assets/customicons/ha_disabled.svg', ha_enabled: 'assets/customicons/ha_enabled.svg', ha_reconnecting: 'assets/customicons/ha_reconnecting.svg', diff --git a/src/app/pages/datasets/components/dataset-icon/dataset-icon.component.spec.ts b/src/app/pages/datasets/components/dataset-icon/dataset-icon.component.spec.ts index b7b7452e7ba..f1c3e141ad0 100644 --- a/src/app/pages/datasets/components/dataset-icon/dataset-icon.component.spec.ts +++ b/src/app/pages/datasets/components/dataset-icon/dataset-icon.component.spec.ts @@ -33,7 +33,7 @@ describe('DatasetIconComponent', () => { } as DatasetDetails); expect(await ixIcon.getNamespace()).toBe('ix'); - expect(await ixIcon.getName()).toBe('dataset'); + expect(await ixIcon.getName()).toBe('dataset_root'); }); it('shows an icon for an ordinary datasets', async () => { @@ -42,7 +42,7 @@ describe('DatasetIconComponent', () => { type: DatasetType.Filesystem, } as DatasetDetails); - expect(await ixIcon.getName()).toBe('folder'); + expect(await ixIcon.getName()).toBe('dataset'); }); it('shows an icon for a zvol', async () => { diff --git a/src/app/pages/datasets/components/dataset-icon/dataset-icon.component.ts b/src/app/pages/datasets/components/dataset-icon/dataset-icon.component.ts index 61ebffe5ed8..9d236c9f319 100644 --- a/src/app/pages/datasets/components/dataset-icon/dataset-icon.component.ts +++ b/src/app/pages/datasets/components/dataset-icon/dataset-icon.component.ts @@ -24,12 +24,12 @@ export class DatasetIconComponent { get name(): string { if (this.isRoot) { - return 'ix:dataset'; + return 'ix:dataset_root'; } if (this.isZvol) { return 'mdi-database'; } - return 'folder'; + return 'ix:dataset'; } } diff --git a/src/app/services/navigation/navigation.service.ts b/src/app/services/navigation/navigation.service.ts index 5d7be5113ae..ac1aaae7afd 100644 --- a/src/app/services/navigation/navigation.service.ts +++ b/src/app/services/navigation/navigation.service.ts @@ -48,7 +48,7 @@ export class NavigationService { name: T('Datasets'), type: MenuItemType.Link, tooltip: T('Datasets'), - icon: 'ix:dataset', + icon: 'ix:dataset_root', state: 'datasets', }, { diff --git a/src/assets/customicons/dataset.svg b/src/assets/customicons/dataset.svg index 8b99e3cc038..783b002c9a4 100644 --- a/src/assets/customicons/dataset.svg +++ b/src/assets/customicons/dataset.svg @@ -1,3 +1,3 @@ - - + + diff --git a/src/assets/customicons/dataset_root.svg b/src/assets/customicons/dataset_root.svg new file mode 100644 index 00000000000..8b99e3cc038 --- /dev/null +++ b/src/assets/customicons/dataset_root.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/setup-jest.ts b/src/setup-jest.ts index d675080f17c..0be9957f71d 100644 --- a/src/setup-jest.ts +++ b/src/setup-jest.ts @@ -10,6 +10,7 @@ import { MATERIAL_SANITY_CHECKS, MatNativeDateModule } from '@angular/material/c import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDialogModule } from '@angular/material/dialog'; import { MatExpansionModule } from '@angular/material/expansion'; +import { MatIconTestingModule } from '@angular/material/icon/testing'; import { MatListModule } from '@angular/material/list'; import { MatMenuModule } from '@angular/material/menu'; import { MatProgressBarModule } from '@angular/material/progress-bar'; @@ -54,6 +55,7 @@ defineGlobalsInjections({ MatSlideToggleModule, MatMenuModule, IxIconModule, + MatIconTestingModule, MatDatepickerModule, MatNativeDateModule, MatSelectModule,