diff --git a/lib/content-services/src/lib/aspect-list/aspect-list.component.html b/lib/content-services/src/lib/aspect-list/aspect-list.component.html index e97c642cb29..abb8cb0c511 100644 --- a/lib/content-services/src/lib/aspect-list/aspect-list.component.html +++ b/lib/content-services/src/lib/aspect-list/aspect-list.component.html @@ -8,7 +8,7 @@ (click)="onCheckBoxClick($event)" (change)="onChange($event, aspect?.entry?.id)">

{{getTitle(aspect)}}

- + @@ -39,6 +39,6 @@
- +
diff --git a/lib/content-services/src/lib/aspect-list/aspect-list.component.spec.ts b/lib/content-services/src/lib/aspect-list/aspect-list.component.spec.ts index 289aa37ea74..87b77c5a1db 100644 --- a/lib/content-services/src/lib/aspect-list/aspect-list.component.spec.ts +++ b/lib/content-services/src/lib/aspect-list/aspect-list.component.spec.ts @@ -24,86 +24,96 @@ import { AspectListService } from './services/aspect-list.service'; import { of } from 'rxjs'; import { AspectEntry } from '@alfresco/js-api'; import { delay } from 'rxjs/operators'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { MatExpansionPanelHarness } from '@angular/material/expansion/testing'; +import { MatTableHarness } from '@angular/material/table/testing'; +import { MatCheckboxHarness } from '@angular/material/checkbox/testing'; +import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing'; -const aspectListMock: AspectEntry[] = [{ - entry: { - parentId: 'frs:aspectZero', - id: 'frs:AspectOne', - description: 'First Aspect with random description', - title: 'FirstAspect', - properties: [ - { - id: 'channelPassword', - title: 'The authenticated channel password', - dataType: 'd:propA' - }, - { - id: 'channelUsername', - title: 'The authenticated channel username', - dataType: 'd:propB' - } - ] +const aspectListMock: AspectEntry[] = [ + { + entry: { + parentId: 'frs:aspectZero', + id: 'frs:AspectOne', + description: 'First Aspect with random description', + title: 'FirstAspect', + properties: [ + { + id: 'channelPassword', + title: 'The authenticated channel password', + dataType: 'd:propA' + }, + { + id: 'channelUsername', + title: 'The authenticated channel username', + dataType: 'd:propB' + } + ] + } + }, + { + entry: { + parentId: 'frs:AspectZer', + id: 'frs:SecondAspect', + description: 'Second Aspect description', + title: 'SecondAspect', + properties: [ + { + id: 'assetId', + title: 'Published Asset Id', + dataType: 'd:text' + }, + { + id: 'assetUrl', + title: 'Published Asset URL', + dataType: 'd:text' + } + ] + } } -}, -{ - entry: { - parentId: 'frs:AspectZer', - id: 'frs:SecondAspect', - description: 'Second Aspect description', - title: 'SecondAspect', - properties: [ - { - id: 'assetId', - title: 'Published Asset Id', - dataType: 'd:text' - }, - { - id: 'assetUrl', - title: 'Published Asset URL', - dataType: 'd:text' - } - ] - } -}]; - -const customAspectListMock: AspectEntry[] = [{ - entry: { - parentId: 'cst:parentAspect', - id: 'cst:customAspect', - description: 'Custom Aspect with random description', - title: 'CustomAspect', - properties: [ - { - id: 'channelPassword', - title: 'The authenticated channel password', - dataType: 'd:propA' - }, - { - id: 'channelUsername', - title: 'The authenticated channel username', - dataType: 'd:propB' - } - ] - } -}, -{ - entry: { - parentId: 'cst:commonaspect', - id: 'cst:nonamedAspect', - description: '', - title: '', - properties: [ - { - id: 'channelPassword', - title: 'The authenticated channel password', - dataType: 'd:propA' - } - ] +]; + +const customAspectListMock: AspectEntry[] = [ + { + entry: { + parentId: 'cst:parentAspect', + id: 'cst:customAspect', + description: 'Custom Aspect with random description', + title: 'CustomAspect', + properties: [ + { + id: 'channelPassword', + title: 'The authenticated channel password', + dataType: 'd:propA' + }, + { + id: 'channelUsername', + title: 'The authenticated channel username', + dataType: 'd:propB' + } + ] + } + }, + { + entry: { + parentId: 'cst:commonaspect', + id: 'cst:nonamedAspect', + description: '', + title: '', + properties: [ + { + id: 'channelPassword', + title: 'The authenticated channel password', + dataType: 'd:propA' + } + ] + } } -}]; +]; describe('AspectListComponent', () => { - + let loader: HarnessLoader; let component: AspectListComponent; let fixture: ComponentFixture; let aspectListService: AspectListService; @@ -111,36 +121,31 @@ describe('AspectListComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ - TranslateModule.forRoot(), - ContentTestingModule - ], + imports: [TranslateModule.forRoot(), ContentTestingModule], providers: [AspectListService] }); }); describe('Loading', () => { - beforeEach(() => { fixture = TestBed.createComponent(AspectListComponent); component = fixture.componentInstance; nodeService = TestBed.inject(NodesApiService); aspectListService = TestBed.inject(AspectListService); + loader = TestbedHarnessEnvironment.loader(fixture); }); - it('should show the loading spinner when result is loading', () => { + it('should show the loading spinner when result is loading', async () => { const delayResult = of(null).pipe(delay(0)); spyOn(nodeService, 'getNode').and.returnValue(delayResult); spyOn(aspectListService, 'getAspects').and.returnValue(delayResult); fixture.detectChanges(); - const spinner = fixture.nativeElement.querySelector('#adf-aspect-spinner'); - expect(spinner).toBeDefined(); - expect(spinner).not.toBeNull(); + + expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(true); }); }); describe('When passing a node id', () => { - beforeEach(() => { fixture = TestBed.createComponent(AspectListComponent); component = fixture.componentInstance; @@ -152,6 +157,7 @@ describe('AspectListComponent', () => { spyOn(nodeService, 'getNode').and.returnValue(of({ id: 'fake-node-id', aspectNames: ['frs:AspectOne'] } as any)); component.nodeId = 'fake-node-id'; fixture.detectChanges(); + loader = TestbedHarnessEnvironment.loader(fixture); }); afterEach(() => { @@ -167,106 +173,80 @@ describe('AspectListComponent', () => { expect(component.hasEqualAspect).toBe(false); }); - it('should show all the aspects', () => { - const firstElement = fixture.nativeElement.querySelector('#aspect-list-FirstAspect'); - const secondElement = fixture.nativeElement.querySelector('#aspect-list-SecondAspect'); - - expect(firstElement).not.toBeNull(); - expect(firstElement).toBeDefined(); - expect(secondElement).not.toBeNull(); - expect(secondElement).toBeDefined(); + it('should show all the aspects', async () => { + expect(await loader.hasHarness(MatExpansionPanelHarness.with({ selector: '#aspect-list-FirstAspect' }))).toBe(true); + expect(await loader.hasHarness(MatExpansionPanelHarness.with({ selector: '#aspect-list-SecondAspect' }))).toBe(true); }); it('should show aspect id when name or title is not set', () => { - const noNameAspect: HTMLElement = fixture.nativeElement.querySelector('#aspect-list-cst-nonamedAspect .adf-aspect-list-element-title'); + const noNameAspect = fixture.nativeElement.querySelector('#aspect-list-cst-nonamedAspect .adf-aspect-list-element-title'); expect(noNameAspect).toBeDefined(); expect(noNameAspect).not.toBeNull(); expect(noNameAspect.innerText).toBe('cst:nonamedAspect'); }); - it('should show the details when a row is clicked', () => { - const firstElement = fixture.nativeElement.querySelector('#aspect-list-FirstAspect'); - firstElement.click(); - fixture.detectChanges(); - const firstElementDesc = fixture.nativeElement.querySelector('#aspect-list-0-description'); - expect(firstElementDesc).not.toBeNull(); - expect(firstElementDesc).toBeDefined(); - - const firstElementPropertyTable = fixture.nativeElement.querySelector('#aspect-list-0-properties-table'); - expect(firstElementPropertyTable).not.toBeNull(); - expect(firstElementPropertyTable).toBeDefined(); - const nameProperties = fixture.nativeElement.querySelectorAll('#aspect-list-0-properties-table tbody .mat-column-name'); - expect(nameProperties[0]).not.toBeNull(); - expect(nameProperties[0]).toBeDefined(); - expect(nameProperties[0].innerText).toBe('channelPassword'); - expect(nameProperties[1]).not.toBeNull(); - expect(nameProperties[1]).toBeDefined(); - expect(nameProperties[1].innerText).toBe('channelUsername'); - - const titleProperties = fixture.nativeElement.querySelectorAll('#aspect-list-0-properties-table tbody .mat-column-title'); - expect(titleProperties[0]).not.toBeNull(); - expect(titleProperties[0]).toBeDefined(); - expect(titleProperties[0].innerText).toBe('The authenticated channel password'); - expect(titleProperties[1]).not.toBeNull(); - expect(titleProperties[1]).toBeDefined(); - expect(titleProperties[1].innerText).toBe('The authenticated channel username'); - - const dataTypeProperties = fixture.nativeElement.querySelectorAll('#aspect-list-0-properties-table tbody .mat-column-dataType'); - expect(dataTypeProperties[0]).not.toBeNull(); - expect(dataTypeProperties[0]).toBeDefined(); - expect(dataTypeProperties[0].innerText).toBe('d:propA'); - expect(dataTypeProperties[1]).not.toBeNull(); - expect(dataTypeProperties[1]).toBeDefined(); - expect(dataTypeProperties[1].innerText).toBe('d:propB'); + it('should show the details when a row is clicked', async () => { + const panel = await loader.getHarness(MatExpansionPanelHarness); + await panel.expand(); + expect(await panel.getDescription()).not.toBeNull(); + + const table = await panel.getHarness(MatTableHarness); + const [row1, row2] = await table.getRows(); + const [r1c1, r1c2, r1c3] = await row1.getCells(); + expect(await r1c1.getText()).toBe('channelPassword'); + expect(await r1c2.getText()).toBe('The authenticated channel password'); + expect(await r1c3.getText()).toBe('d:propA'); + + const [r2c1, r2c2, r2c3] = await row2.getCells(); + expect(await r2c1.getText()).toBe('channelUsername'); + expect(await r2c2.getText()).toBe('The authenticated channel username'); + expect(await r2c3.getText()).toBe('d:propB'); }); - it('should show as checked the node properties', () => { - const firstAspectCheckbox: HTMLInputElement = fixture.nativeElement.querySelector('#aspect-list-0-check-input'); - expect(firstAspectCheckbox).toBeDefined(); - expect(firstAspectCheckbox).not.toBeNull(); - expect(firstAspectCheckbox.checked).toBeTruthy(); + it('should show as checked the node properties', async () => { + const panel = await loader.getHarness(MatExpansionPanelHarness); + await panel.expand(); + + const checkbox = await panel.getHarness(MatCheckboxHarness); + expect(await checkbox.isChecked()).toBe(true); }); - it('should remove aspects unchecked', (done) => { - const secondElement = fixture.nativeElement.querySelector('#aspect-list-1-check-input'); - expect(secondElement).toBeDefined(); - expect(secondElement).not.toBeNull(); - expect(secondElement.checked).toBeFalsy(); - secondElement.click(); - fixture.detectChanges(); + it('should remove aspects unchecked', async () => { + const panel = await loader.getAllHarnesses(MatExpansionPanelHarness); + await panel[1].expand(); + + const checkbox = await panel[1].getHarness(MatCheckboxHarness); + expect(await checkbox.isChecked()).toBe(false); + + await checkbox.toggle(); + expect(component.nodeAspects.length).toBe(2); expect(component.nodeAspects[1]).toBe('frs:SecondAspect'); - component.valueChanged.subscribe((aspects) => { - expect(aspects.length).toBe(1); - expect(aspects[0]).toBe('frs:AspectOne'); - done(); - }); - secondElement.click(); - fixture.detectChanges(); + + await checkbox.toggle(); + + expect(component.nodeAspects.length).toBe(1); + expect(component.nodeAspects[0]).toBe('frs:AspectOne'); }); - it('should reset the properties on reset', (done) => { - const secondElement = fixture.nativeElement.querySelector('#aspect-list-1-check-input'); - expect(secondElement).toBeDefined(); - expect(secondElement).not.toBeNull(); - expect(secondElement.checked).toBeFalsy(); - secondElement.click(); - fixture.detectChanges(); + it('should reset the properties on reset', async () => { + const panel = await loader.getAllHarnesses(MatExpansionPanelHarness); + await panel[1].expand(); + + const checkbox = await panel[1].getHarness(MatCheckboxHarness); + expect(await checkbox.isChecked()).toBe(false); + + await checkbox.toggle(); + expect(component.nodeAspects.length).toBe(2); - component.valueChanged.subscribe((aspects) => { - expect(aspects.length).toBe(1); - done(); - }); component.reset(); + expect(component.nodeAspects.length).toBe(1); }); - it('should clear all the properties on clear', (done) => { + it('should clear all the properties on clear', async () => { expect(component.nodeAspects.length).toBe(1); - component.valueChanged.subscribe((aspects) => { - expect(aspects.length).toBe(0); - done(); - }); component.clear(); + expect(component.nodeAspects.length).toBe(0); }); }); @@ -277,21 +257,16 @@ describe('AspectListComponent', () => { aspectListService = TestBed.inject(AspectListService); spyOn(aspectListService, 'getAspects').and.returnValue(of(aspectListMock)); fixture.detectChanges(); + loader = TestbedHarnessEnvironment.loader(fixture); }); afterEach(() => { fixture.destroy(); }); - it('should show all the aspects', () => { - const firstElement = fixture.nativeElement.querySelector('#aspect-list-FirstAspect'); - const secondElement = fixture.nativeElement.querySelector('#aspect-list-SecondAspect'); - - expect(firstElement).not.toBeNull(); - expect(firstElement).toBeDefined(); - expect(secondElement).not.toBeNull(); - expect(secondElement).toBeDefined(); + it('should show all the aspects', async () => { + expect(await loader.hasHarness(MatExpansionPanelHarness.with({ selector: '#aspect-list-FirstAspect' }))).toBe(true); + expect(await loader.hasHarness(MatExpansionPanelHarness.with({ selector: '#aspect-list-SecondAspect' }))).toBe(true); }); }); - }); diff --git a/lib/content-services/src/lib/category/categories-management/categories-management.component.html b/lib/content-services/src/lib/category/categories-management/categories-management.component.html index 516f0acca93..f0ca8efe99f 100644 --- a/lib/content-services/src/lib/category/categories-management/categories-management.component.html +++ b/lib/content-services/src/lib/category/categories-management/categories-management.component.html @@ -71,7 +71,8 @@ [value]="category"> {{ category.name }} -

+

{{ 'CATEGORIES_MANAGEMENT.NO_EXISTING_CATEGORIES' | translate }}

diff --git a/lib/content-services/src/lib/category/categories-management/categories-management.component.spec.ts b/lib/content-services/src/lib/category/categories-management/categories-management.component.spec.ts index d7e3801e14f..e7e88da813a 100644 --- a/lib/content-services/src/lib/category/categories-management/categories-management.component.spec.ts +++ b/lib/content-services/src/lib/category/categories-management/categories-management.component.spec.ts @@ -16,7 +16,6 @@ */ import { Category, CategoryPaging, ResultNode, ResultSetPaging } from '@alfresco/js-api'; -import { DebugElement } from '@angular/core'; import { ComponentFixture, discardPeriodicTasks, fakeAsync, flush, TestBed, tick } from '@angular/core/testing'; import { Validators } from '@angular/forms'; import { MatError } from '@angular/material/form-field'; @@ -28,8 +27,12 @@ import { ContentTestingModule } from '../../testing/content.testing.module'; import { CategoriesManagementMode } from './categories-management-mode'; import { CategoryService } from '../services/category.service'; import { CategoriesManagementComponent } from './categories-management.component'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing'; describe('CategoriesManagementComponent', () => { + let loader: HarnessLoader; let component: CategoriesManagementComponent; let fixture: ComponentFixture; let categoryService: CategoryService; @@ -38,18 +41,15 @@ describe('CategoriesManagementComponent', () => { const category2 = new Category({ id: 'test2', name: 'testCat2' }); const category3 = new Category({ id: 'test3', name: 'testCat3' }); const category4 = new Category({ id: 'test4', name: 'testCat4' }); - const resultCat1 = new ResultNode({ id: 'test', name: 'testCat', path: { name: 'general/categories' }}); - const resultCat2 = new ResultNode({ id: 'test2', name: 'testCat2', path: { name: 'general/categories' }}); - const categoryPagingResponse: CategoryPaging = { list: { pagination: {}, entries: [ { entry: category1 }, { entry: category2 }]}}; - const categorySearchResponse: ResultSetPaging = { list: { pagination: {}, entries: [ { entry: resultCat1 }, { entry: resultCat2 }]}}; + const resultCat1 = new ResultNode({ id: 'test', name: 'testCat', path: { name: 'general/categories' } }); + const resultCat2 = new ResultNode({ id: 'test2', name: 'testCat2', path: { name: 'general/categories' } }); + const categoryPagingResponse: CategoryPaging = { list: { pagination: {}, entries: [{ entry: category1 }, { entry: category2 }] } }; + const categorySearchResponse: ResultSetPaging = { list: { pagination: {}, entries: [{ entry: resultCat1 }, { entry: resultCat2 }] } }; beforeEach(() => { TestBed.configureTestingModule({ declarations: [CategoriesManagementComponent], - imports: [ - TranslateModule.forRoot(), - ContentTestingModule - ], + imports: [TranslateModule.forRoot(), ContentTestingModule], providers: [ { provide: CategoryService, @@ -65,6 +65,7 @@ describe('CategoriesManagementComponent', () => { fixture = TestBed.createComponent(CategoriesManagementComponent); component = fixture.componentInstance; categoryService = TestBed.inject(CategoryService); + loader = TestbedHarnessEnvironment.loader(fixture); }); /** @@ -138,7 +139,9 @@ describe('CategoriesManagementComponent', () => { * @returns list of native elements */ function getRemoveCategoryButtons(): HTMLButtonElement[] { - return fixture.debugElement.queryAll(By.css(`[data-automation-id="categories-remove-category-button"]`)).map((debugElem) => debugElem.nativeElement); + return fixture.debugElement + .queryAll(By.css(`[data-automation-id="categories-remove-category-button"]`)) + .map((debugElem) => debugElem.nativeElement); } /** @@ -261,39 +264,28 @@ describe('CategoriesManagementComponent', () => { }); describe('Spinner', () => { - /** - * Get the spinner element - * - * @returns debug element - */ - function getSpinner(): DebugElement { - return fixture.debugElement.query(By.css(`.mat-progress-spinner`)); - } - - it('should be displayed with correct diameter when existing categories are loading', fakeAsync(() => { - typeCategory('Category 1', 0); - - const spinner = getSpinner(); - expect(spinner).toBeTruthy(); - expect(spinner.componentInstance.diameter).toBe(50); - - discardPeriodicTasks(); - flush(); - })); + it('should not be displayed when existing categories stopped loading', async () => { + component.categoryNameControlVisible = true; + fixture.detectChanges(); - it('should not be displayed when existing categories stopped loading', fakeAsync(() => { - typeCategory('Category 1'); + const categoryControlInput = getCategoryControlInput(); + categoryControlInput.value = 'Category 1'; + categoryControlInput.dispatchEvent(new InputEvent('input')); - const spinner = getSpinner(); - expect(spinner).toBeFalsy(); - })); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(false); + }); }); it('should display correct message when there are no existing categories', fakeAsync(() => { - spyOn(categoryService, 'getSubcategories').and.returnValue(of({list: { pagination: {}, entries: []}})); + spyOn(categoryService, 'getSubcategories').and.returnValue(of({ list: { pagination: {}, entries: [] } })); typeCategory('test'); - const noExistingCategoriesMsg = fixture.debugElement.query(By.css('mat-selection-list p'))?.nativeElement.textContent.trim(); + const noExistingCategoriesMsg = fixture.debugElement + .query(By.css(`[data-automation-id="no-categories-message"]`)) + ?.nativeElement.textContent.trim(); expect(noExistingCategoriesMsg).toBe('CATEGORIES_MANAGEMENT.NO_EXISTING_CATEGORIES'); })); }); @@ -328,7 +320,9 @@ describe('CategoriesManagementComponent', () => { it('should have correct remove category title', () => { const removeButtons = getRemoveCategoryButtons(); - const isTitleCorrect = removeButtons.every((removeBtn) => removeBtn.attributes.getNamedItem('title').textContent === 'CATEGORIES_MANAGEMENT.UNASSIGN_CATEGORY'); + const isTitleCorrect = removeButtons.every( + (removeBtn) => removeBtn.attributes.getNamedItem('title').textContent === 'CATEGORIES_MANAGEMENT.UNASSIGN_CATEGORY' + ); expect(isTitleCorrect).toBeTrue(); }); @@ -434,7 +428,9 @@ describe('CategoriesManagementComponent', () => { it('should have correct remove category title', () => { const removeButtons = getRemoveCategoryButtons(); - const isTitleCorrect = removeButtons.every((removeBtn) => removeBtn.attributes.getNamedItem('title').textContent === 'CATEGORIES_MANAGEMENT.DELETE_CATEGORY'); + const isTitleCorrect = removeButtons.every( + (removeBtn) => removeBtn.attributes.getNamedItem('title').textContent === 'CATEGORIES_MANAGEMENT.DELETE_CATEGORY' + ); expect(isTitleCorrect).toBeTrue(); }); diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector.component.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector.component.ts index e13aec2fab7..fe939ff96d6 100644 --- a/lib/content-services/src/lib/content-node-selector/content-node-selector.component.ts +++ b/lib/content-services/src/lib/content-node-selector/content-node-selector.component.ts @@ -15,9 +15,9 @@ * limitations under the License. */ -import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, Inject, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { TranslationService, NotificationService} from '@alfresco/adf-core'; +import { TranslationService, NotificationService } from '@alfresco/adf-core'; import { Node } from '@alfresco/js-api'; import { AllowableOperationsEnum } from '../common/models/allowable-operations.enum'; import { ContentService } from '../common/services/content.service'; @@ -26,6 +26,8 @@ import { ContentNodeSelectorComponentData } from './content-node-selector.compon import { NodeEntryEvent } from '../document-list/components/node.event'; import { NodeAction } from '../document-list/models/node-action.enum'; import { OverlayContainer } from '@angular/cdk/overlay'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'adf-content-node-selector', @@ -33,7 +35,9 @@ import { OverlayContainer } from '@angular/cdk/overlay'; styleUrls: ['./content-node-selector.component.scss'], encapsulation: ViewEncapsulation.None }) -export class ContentNodeSelectorComponent implements OnInit { +export class ContentNodeSelectorComponent implements OnInit, OnDestroy { + private onDestroy$ = new Subject(); + title: string; action: NodeAction; buttonActionName: string; @@ -48,13 +52,15 @@ export class ContentNodeSelectorComponent implements OnInit { emptyFolderImageUrl: string = './assets/images/empty_doc_lib.svg'; breadcrumbFolderNode: Node; - constructor(private translation: TranslationService, - private contentService: ContentService, - private notificationService: NotificationService, - private uploadService: UploadService, - private dialog: MatDialogRef, - private overlayContainer: OverlayContainer, - @Inject(MAT_DIALOG_DATA) public data: ContentNodeSelectorComponentData) { + constructor( + private translation: TranslationService, + private contentService: ContentService, + private notificationService: NotificationService, + private uploadService: UploadService, + private dialog: MatDialogRef, + private overlayContainer: OverlayContainer, + @Inject(MAT_DIALOG_DATA) public data: ContentNodeSelectorComponentData + ) { this.action = data.actionName ?? NodeAction.CHOOSE; this.buttonActionName = `NODE_SELECTOR.${this.action}`; this.title = data.title; @@ -62,28 +68,41 @@ export class ContentNodeSelectorComponent implements OnInit { } ngOnInit() { - this.dialog.keydownEvents().subscribe(event => { - // Esc - if (event.keyCode === 27) { - event.preventDefault(); - event.stopImmediatePropagation(); + this.dialog + .keydownEvents() + .pipe(takeUntil(this.onDestroy$)) + .subscribe((event) => { + if (event?.key === 'Escape') { + event.preventDefault(); + event.stopImmediatePropagation(); + this.close(); + } + }); + + this.dialog + .backdropClick() + .pipe(takeUntil(this.onDestroy$)) + .subscribe(() => { this.close(); - } - }); + }); - this.dialog.backdropClick().subscribe(() => { - this.close(); - }); - - this.dialog.afterOpened().subscribe(() => { - this.overlayContainer.getContainerElement().setAttribute('role', 'main'); - }); + this.dialog + .afterOpened() + .pipe(takeUntil(this.onDestroy$)) + .subscribe(() => { + this.overlayContainer.getContainerElement().setAttribute('role', 'main'); + }); - this.uploadService.fileUploadStarting.subscribe(() => { + this.uploadService.fileUploadStarting.pipe(takeUntil(this.onDestroy$)).subscribe(() => { this.uploadStarted = true; }); } + ngOnDestroy() { + this.onDestroy$.next(); + this.onDestroy$.complete(); + } + close() { this.dialog.close(); this.overlayContainer.getContainerElement().setAttribute('role', 'region'); @@ -179,16 +198,17 @@ export class ContentNodeSelectorComponent implements OnInit { } getWarningMessage(): string { - return this.showingSearch ? 'NODE_SELECTOR.UPLOAD_BUTTON_SEARCH_WARNING_MESSAGE' : - (this.hasNoPermissionToUpload() ? 'NODE_SELECTOR.UPLOAD_BUTTON_PERMISSION_WARNING_MESSAGE' : ''); + if (this.showingSearch) { + return 'NODE_SELECTOR.UPLOAD_BUTTON_SEARCH_WARNING_MESSAGE'; + } + return this.hasNoPermissionToUpload() ? 'NODE_SELECTOR.UPLOAD_BUTTON_PERMISSION_WARNING_MESSAGE' : ''; } hasNoPermissionToUpload(): boolean { - return (!this.hasAllowableOperations && !this.showingSearch) && !this.isLoading; + return !this.hasAllowableOperations && !this.showingSearch && !this.isLoading; } hasUploadError(): boolean { return this.showingSearch || this.hasNoPermissionToUpload(); } - } diff --git a/lib/content-services/src/lib/dialogs/folder.dialog.spec.ts b/lib/content-services/src/lib/dialogs/folder.dialog.spec.ts index 63418db8f39..7cd36094da6 100644 --- a/lib/content-services/src/lib/dialogs/folder.dialog.spec.ts +++ b/lib/content-services/src/lib/dialogs/folder.dialog.spec.ts @@ -48,6 +48,8 @@ describe('FolderDialogComponent', () => { fixture.destroy(); }); + const getTitle = () => fixture.debugElement.query(By.css('[data-automation-id="adf-folder-dialog-title"]')); + describe('Edit', () => { beforeEach(() => { component.data = { @@ -70,8 +72,8 @@ describe('FolderDialogComponent', () => { }); it('should have the proper title', () => { - const title = fixture.debugElement.query(By.css('[mat-dialog-title]')); - expect(title === null).toBe(false); + const title = getTitle(); + expect(title).not.toBeNull(); expect(title.nativeElement.innerText.trim()).toBe('CORE.FOLDER_DIALOG.EDIT_FOLDER_TITLE'); }); @@ -165,8 +167,8 @@ describe('FolderDialogComponent', () => { }); it('should have the proper title', () => { - const title = fixture.debugElement.query(By.css(`[data-automation-id="adf-folder-dialog-title"]`)); - expect(title === null).toBe(false); + const title = getTitle(); + expect(title).not.toBeNull(); expect(title.nativeElement.innerText.trim()).toBe('CORE.FOLDER_DIALOG.CREATE_FOLDER_TITLE'); }); diff --git a/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts b/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts index 00adf1b0942..cdcf8701f4a 100644 --- a/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts +++ b/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts @@ -62,12 +62,16 @@ import { domSanitizerMock } from '../../testing/dom-sanitizer-mock'; import { MatDialog } from '@angular/material/dialog'; import { FileAutoDownloadComponent } from './file-auto-download/file-auto-download.component'; import { ShareDataTableAdapter } from '../data/share-datatable-adapter'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing'; const mockDialog = { open: jasmine.createSpy('open') }; describe('DocumentList', () => { + let loader: HarnessLoader; let documentList: DocumentListComponent; let documentListService: DocumentListService; let customResourcesService: CustomResourcesService; @@ -116,6 +120,8 @@ describe('DocumentList', () => { spyFavorite = spyOn(customResourcesService.favoritesApi, 'listFavorites').and.returnValue( Promise.resolve(new FavoritePaging({ list: new FavoritePagingList({ entries: [] }) })) ); + + loader = TestbedHarnessEnvironment.loader(fixture); }); afterEach(() => { @@ -219,7 +225,7 @@ describe('DocumentList', () => { it('should show the header when there are no records in the table but filter is active', () => { documentList.data = new ShareDataTableAdapter(thumbnailService, contentService, []); - documentList.filterValue = { $thumbnail: 'TYPE:"cm:folder"' }; + documentList.filterValue = { $thumbnail: 'TYPE:"cm:folder"' }; fixture.detectChanges(); @@ -1075,10 +1081,11 @@ describe('DocumentList', () => { expect(fixture.debugElement.query(By.css('.adf-no-permission__template'))).not.toBeNull(); }); - it('should display loading template when data is loading', () => { + it('should display loading template when data is loading', async () => { documentList.loading = true; fixture.detectChanges(); - expect(fixture.debugElement.query(By.css('mat-progress-spinner'))).not.toBeNull(); + + expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(true); }); it('should empty folder NOT show the pagination', () => { @@ -1406,14 +1413,16 @@ describe('DocumentList', () => { expect(documentList.reload).toHaveBeenCalled(); }); - it('should not show loading state if pagination is updated with merge setting as true', fakeAsync (() => { + it('should not show loading state if pagination is updated with merge setting as true', fakeAsync(() => { spyFolderNode = spyOn(documentListService, 'loadFolderByNodeId').and.callFake(() => - of(new DocumentLoaderNode(null, { - list: { - pagination: {}, - entries: mockPreselectedNodes - } - })) + of( + new DocumentLoaderNode(null, { + list: { + pagination: {}, + entries: mockPreselectedNodes + } + }) + ) ); fixture.detectChanges(); const fakeDatatableRows = [ diff --git a/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.html b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.html index 871bc59ace4..a47f6d297c1 100644 --- a/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.html +++ b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.html @@ -1,4 +1,4 @@ -

{{ title | translate }}

+

{{ title | translate }}

{ let component: NewVersionUploaderDialogComponent; let fixture: ComponentFixture; - let nativeElement; + let nativeElement: HTMLElement; const cssSelectors = { adfVersionUploadButton: '#adf-version-upload-button', adfVersionComparison: '#adf-version-comparison', adfVersionList: '.adf-version-list', - matDialogTitle: '.mat-dialog-title' + title: '[data-automation-id="new-version-uploader-dialog-title"]' }; const mockDialogRef = { @@ -45,10 +45,7 @@ describe('NewVersionUploaderDialog', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ - TranslateModule.forRoot(), - ContentTestingModule - ], + imports: [TranslateModule.forRoot(), ContentTestingModule], declarations: [ NewVersionUploaderDialogComponent, VersionListComponent, @@ -59,7 +56,8 @@ describe('NewVersionUploaderDialog', () => { providers: [ { provide: MAT_DIALOG_DATA, useValue: { node: mockNode, showVersionsOnly, file: mockFile } }, { - provide: MatDialogRef, useValue: mockDialogRef + provide: MatDialogRef, + useValue: mockDialogRef } ] }); @@ -72,12 +70,7 @@ describe('NewVersionUploaderDialog', () => { fixture.destroy(); }); - it('should create', () => { - expect(component).toBeTruthy(); - }); - describe('Upload New Version', () => { - const expectedUploadNewVersionTitle = 'ADF-NEW-VERSION-UPLOADER.DIALOG_UPLOAD.TITLE'; it('should display adf version upload button if showVersionsOnly is passed as false from parent component', () => { @@ -104,7 +97,7 @@ describe('NewVersionUploaderDialog', () => { it('should show default title if title is not provided from parent component', () => { component.data.showVersionsOnly = false; fixture.detectChanges(); - const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle); + const matDialogTitle = nativeElement.querySelector(cssSelectors.title); expect(matDialogTitle.innerHTML).toEqual(expectedUploadNewVersionTitle); }); @@ -112,7 +105,7 @@ describe('NewVersionUploaderDialog', () => { component.data.showVersionsOnly = false; component.data.title = ''; fixture.detectChanges(); - const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle); + const matDialogTitle = nativeElement.querySelector(cssSelectors.title); expect(matDialogTitle.innerHTML).toEqual(expectedUploadNewVersionTitle); }); @@ -120,7 +113,7 @@ describe('NewVersionUploaderDialog', () => { component.data.showVersionsOnly = false; component.data.title = 'TEST_TITLE'; fixture.detectChanges(); - const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle); + const matDialogTitle = nativeElement.querySelector(cssSelectors.title); expect(matDialogTitle.innerHTML).toEqual('TEST_TITLE'); }); @@ -150,11 +143,9 @@ describe('NewVersionUploaderDialog', () => { component.handleCancel(); expect(mockDialogRef.close).toHaveBeenCalled(); }); - }); describe('Manage Versions', () => { - const expectedManageVersionsTitle = 'ADF-NEW-VERSION-UPLOADER.DIALOG_LIST.TITLE'; it('should display adf version list if showVersionsOnly is passed as true from parent component', () => { @@ -182,7 +173,7 @@ describe('NewVersionUploaderDialog', () => { component.data.showVersionsOnly = true; component.data.title = undefined; fixture.detectChanges(); - const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle); + const matDialogTitle = nativeElement.querySelector(cssSelectors.title); expect(matDialogTitle.innerHTML).toEqual(expectedManageVersionsTitle); }); @@ -190,7 +181,7 @@ describe('NewVersionUploaderDialog', () => { component.data.showVersionsOnly = true; component.data.title = ''; fixture.detectChanges(); - const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle); + const matDialogTitle = nativeElement.querySelector(cssSelectors.title); expect(matDialogTitle.innerHTML).toEqual(expectedManageVersionsTitle); }); @@ -198,10 +189,8 @@ describe('NewVersionUploaderDialog', () => { component.data.showVersionsOnly = true; component.data.title = 'TEST_TITLE'; fixture.detectChanges(); - const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle); + const matDialogTitle = nativeElement.querySelector(cssSelectors.title); expect(matDialogTitle.innerHTML).toEqual('TEST_TITLE'); }); - }); - }); diff --git a/lib/core/src/lib/card-view/components/card-view-selectitem/select-filter-input/select-filter-input.component.html b/lib/core/src/lib/card-view/components/card-view-selectitem/select-filter-input/select-filter-input.component.html index be1de8e54a7..a5afdbdc4a9 100644 --- a/lib/core/src/lib/card-view/components/card-view-selectitem/select-filter-input/select-filter-input.component.html +++ b/lib/core/src/lib/card-view/components/card-view-selectitem/select-filter-input/select-filter-input.component.html @@ -12,7 +12,7 @@ (change)="$event.stopPropagation()" /> -