diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector.component.spec.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector.component.spec.ts index 68e889cf8e..57a36bc9cc 100644 --- a/lib/content-services/src/lib/content-node-selector/content-node-selector.component.spec.ts +++ b/lib/content-services/src/lib/content-node-selector/content-node-selector.component.spec.ts @@ -35,6 +35,9 @@ import { NodeAction } from '../document-list/models/node-action.enum'; import { SitesService } from '../common/services/sites.service'; import { NodesApiService } from '../common/services/nodes-api.service'; import { ContentService } from '../common/services/content.service'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { MatTabGroupHarness } from '@angular/material/tabs/testing'; describe('ContentNodeSelectorComponent', () => { let component: ContentNodeSelectorComponent; @@ -42,6 +45,7 @@ describe('ContentNodeSelectorComponent', () => { let data: any; let uploadService: UploadService; let dialog: MatDialogRef; + let loader: HarnessLoader; beforeEach(() => { data = { @@ -86,6 +90,7 @@ describe('ContentNodeSelectorComponent', () => { fixture = TestBed.createComponent(ContentNodeSelectorComponent); component = fixture.componentInstance; + loader = TestbedHarnessEnvironment.loader(fixture); const contentService = TestBed.inject(ContentService); spyOn(contentService, 'hasAllowableOperations').and.returnValue(true); @@ -121,13 +126,11 @@ describe('ContentNodeSelectorComponent', () => { component.isLoading = false; }; - const getTabLabel = (idx: number) => fixture.debugElement.queryAll(By.css('.mat-tab-label'))[idx]; + const selectTabByIndex = async (tabIndex: number): Promise => { + const tabGroup = await loader.getHarness(MatTabGroupHarness.with({ selector: '.adf-content-node-selector-dialog-content' })); + const tabToSelect = (await tabGroup.getTabs())[tabIndex]; - const selectTabByIndex = (tabIndex: number) => { - const uploadFromLocalTab = getTabLabel(tabIndex); - const attributes = uploadFromLocalTab.nativeNode.attributes as NamedNodeMap; - const tabPositionInSet = Number(attributes.getNamedItem('aria-posinset').value) - 1; - component.onTabSelectionChange(tabPositionInSet); + return tabToSelect.select(); }; describe('Data injecting with the "Material dialog way"', () => { @@ -260,39 +263,38 @@ describe('ContentNodeSelectorComponent', () => { describe('Upload button', () => { const getUploadButton = () => fixture.debugElement.query(By.css('adf-upload-button button'))?.nativeElement as HTMLButtonElement; - it('Should not be able to upload a file whilst a search is still running', () => { + it('Should not be able to upload a file whilst a search is still running', async () => { enableLocalUpload(); fixture.detectChanges(); + const tabGroup = await loader.getHarness(MatTabGroupHarness.with({ selector: '.adf-content-node-selector-dialog-content' })); + const uploadFromLocalTab = (await tabGroup.getTabs())[1]; let infoMatIcon = getTabInfoButton(); - let uploadFromLocalTab = getTabLabel(1); - expect(uploadFromLocalTab.nativeElement.getAttribute('aria-disabled')).toBe('false'); + expect(await uploadFromLocalTab.isDisabled()).toBeFalse(); expect(infoMatIcon).toBeFalsy(); component.showingSearch = true; fixture.detectChanges(); - uploadFromLocalTab = getTabLabel(1); infoMatIcon = getTabInfoButton(); - expect(uploadFromLocalTab.nativeElement.getAttribute('aria-disabled')).toBe('true'); + expect(await uploadFromLocalTab.isDisabled()).toBeTrue(); expect(infoMatIcon).toBeTruthy(); expect(component.getWarningMessage()).toEqual('NODE_SELECTOR.UPLOAD_BUTTON_SEARCH_WARNING_MESSAGE'); component.showingSearch = false; fixture.detectChanges(); - uploadFromLocalTab = getTabLabel(1); infoMatIcon = getTabInfoButton(); - expect(uploadFromLocalTab.nativeElement.getAttribute('aria-disabled')).toBe('false'); + expect(await uploadFromLocalTab.isDisabled()).toBeFalse(); expect(infoMatIcon).toBeFalsy(); }); it('should be able to show upload button if showLocalUploadButton set to true', async () => { enableLocalUpload(); - selectTabByIndex(1); + await selectTabByIndex(1); fixture.detectChanges(); const adfUploadButton = fixture.debugElement.query(By.css('adf-upload-button')); @@ -301,8 +303,8 @@ describe('ContentNodeSelectorComponent', () => { expect(adfUploadButton.nativeElement.textContent).toEqual('file_uploadFORM.FIELD.UPLOAD'); }); - it('should be able to disable UploadButton if showingSearch set to true', () => { - selectTabByIndex(1); + it('should be able to disable UploadButton if showingSearch set to true', async () => { + await selectTabByIndex(1); component.showingSearch = true; component.hasAllowableOperations = true; @@ -313,8 +315,8 @@ describe('ContentNodeSelectorComponent', () => { expect(adfUploadButton.disabled).toBe(true); }); - it('should be able to enable UploadButton if showingSearch set to false', () => { - selectTabByIndex(1); + it('should be able to enable UploadButton if showingSearch set to false', async () => { + await selectTabByIndex(1); component.showingSearch = false; component.hasAllowableOperations = true; @@ -325,11 +327,11 @@ describe('ContentNodeSelectorComponent', () => { expect(adfUploadButton.disabled).toBe(false); }); - it('should be able to show warning message while searching', () => { + it('should be able to show warning message while searching', async () => { component.data.showLocalUploadButton = true; component.showingSearch = true; component.hasAllowableOperations = false; - selectTabByIndex(1); + await selectTabByIndex(1); fixture.detectChanges(); const infoMatIcon = getTabInfoButton(); @@ -351,9 +353,11 @@ describe('ContentNodeSelectorComponent', () => { expect(warningMessage).toBeNull(); }); - it('should be able to disable UploadButton if user does not have allowable operations', () => { + it('should be able to disable UploadButton if user does not have allowable operations', async () => { component.hasAllowableOperations = false; - selectTabByIndex(1); + + await selectTabByIndex(1); + component.onTabSelectionChange(1); fixture.detectChanges(); const adfUploadButton = getUploadButton(); @@ -362,8 +366,8 @@ describe('ContentNodeSelectorComponent', () => { expect(adfUploadButton.disabled).toBe(true); }); - it('should be able to enable UploadButton if user has allowable operations', () => { - selectTabByIndex(1); + it('should be able to enable UploadButton if user has allowable operations', async () => { + await selectTabByIndex(1); component.hasAllowableOperations = true; fixture.detectChanges(); @@ -373,21 +377,21 @@ describe('ContentNodeSelectorComponent', () => { expect(adfUploadButton.disabled).toBe(false); }); - it('should not be able to show warning message if user has allowable operations', () => { + it('should not be able to show warning message if user has allowable operations', async () => { enableLocalUpload(); - selectTabByIndex(1); + await selectTabByIndex(1); fixture.detectChanges(); const warningMessage = fixture.debugElement.query(By.css('.adf-content-node-upload-button-warning-message span')); expect(warningMessage).toBeNull(); }); - it('should be able to show warning message if user does not have allowable operations', () => { + it('should be able to show warning message if user does not have allowable operations', async () => { component.data.showLocalUploadButton = true; component.hasAllowableOperations = false; component.showingSearch = false; component.isLoading = false; - selectTabByIndex(1); + await selectTabByIndex(1); fixture.detectChanges(); const infoMatIcon = getTabInfoButton(); @@ -412,15 +416,15 @@ describe('ContentNodeSelectorComponent', () => { }); describe('Tabs', () => { - it('should isFileServerTabSelected return true when tabIndex 0 is selected', () => { - selectTabByIndex(0); + it('should isFileServerTabSelected return true when tabIndex 0 is selected', async () => { + await selectTabByIndex(0); expect(component.isFileServerTabSelected()).toEqual(true); }); - it('should isLocalUploadTabSelected return true when tabIndex 1 is selected', () => { + it('should isLocalUploadTabSelected return true when tabIndex 1 is selected', async () => { enableLocalUpload(); - selectTabByIndex(1); + await selectTabByIndex(1); expect(component.isLocalUploadTabSelected()).toEqual(true); }); @@ -460,8 +464,7 @@ describe('ContentNodeSelectorComponent', () => { it('should show drag and drop area with the empty list template when no upload has started', async () => { enableLocalUpload(); - const uploadFromLocalTab = getTabLabel(1); - uploadFromLocalTab.nativeElement.click(); + await selectTabByIndex(1); fixture.detectChanges(); await fixture.whenRenderingDone(); @@ -475,8 +478,7 @@ describe('ContentNodeSelectorComponent', () => { it('should not show the empty list template when an upload has started', async () => { enableLocalUpload(); - const uploadFromLocalTab = getTabLabel(1); - uploadFromLocalTab.nativeElement.click(); + await selectTabByIndex(1); component.uploadStarted = true; fixture.detectChanges(); diff --git a/lib/content-services/src/lib/content-type/content-type-dialog.component.spec.ts b/lib/content-services/src/lib/content-type/content-type-dialog.component.spec.ts index 46d01e720d..72cd8218a7 100644 --- a/lib/content-services/src/lib/content-type/content-type-dialog.component.spec.ts +++ b/lib/content-services/src/lib/content-type/content-type-dialog.component.spec.ts @@ -24,6 +24,10 @@ import { ContentTypeDialogComponent } from './content-type-dialog.component'; import { ContentTypeService } from './content-type.service'; import { ContentTypeDialogComponentData } from './content-type-metadata.interface'; import { TypeEntry } from '@alfresco/js-api'; +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'; const elementCustom: TypeEntry = { entry: { @@ -82,6 +86,7 @@ describe('Content Type Dialog Component', () => { let fixture: ComponentFixture; let contentTypeService: ContentTypeService; let data: ContentTypeDialogComponentData; + let loader: HarnessLoader; beforeEach(async () => { data = { @@ -116,6 +121,7 @@ describe('Content Type Dialog Component', () => { contentTypeService = TestBed.inject(ContentTypeService); spyOn(contentTypeService, 'getContentTypeByPrefix').and.returnValue(of(elementCustom)); fixture = TestBed.createComponent(ContentTypeDialogComponent); + loader = TestbedHarnessEnvironment.loader(fixture); fixture.detectChanges(); }); @@ -147,13 +153,11 @@ describe('Content Type Dialog Component', () => { }); it('should show the property with the aspect prefix not the inherited ones', async () => { - const showPropertyAccordion: HTMLButtonElement = fixture.nativeElement.querySelector('.adf-content-type-accordion .mat-expansion-panel-header'); - expect(showPropertyAccordion).toBeDefined(); - showPropertyAccordion.click(); - fixture.detectChanges(); - await fixture.whenStable(); - const propertyShowed: NodeList = fixture.nativeElement.querySelectorAll('.adf-content-type-table .mat-row'); - expect(propertyShowed.length).toBe(3); + await (await loader.getHarness(MatExpansionPanelHarness)).toggle(); + + const table = await loader.getHarness(MatTableHarness.with({ selector: '.adf-content-type-table' })); + + expect((await table.getRows()).length).toBe(3); }); it('should emit true when apply is clicked', (done) => { diff --git a/lib/content-services/src/lib/permission-manager/components/add-permission/add-permission-panel.component.spec.ts b/lib/content-services/src/lib/permission-manager/components/add-permission/add-permission-panel.component.spec.ts index 30dd64e4ef..74ee9d0c7d 100644 --- a/lib/content-services/src/lib/permission-manager/components/add-permission/add-permission-panel.component.spec.ts +++ b/lib/content-services/src/lib/permission-manager/components/add-permission/add-permission-panel.component.spec.ts @@ -25,13 +25,17 @@ import { SearchService } from '../../../search/services/search.service'; import { DebugElement } from '@angular/core'; import { TranslateModule } from '@ngx-translate/core'; import { MatIconTestingModule } from '@angular/material/icon/testing'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { MatSelectionListHarness } from '@angular/material/list/testing'; +import { MatInputHarness } from '@angular/material/input/testing'; describe('AddPermissionPanelComponent', () => { let fixture: ComponentFixture; let component: AddPermissionPanelComponent; let element: HTMLElement; let searchApiService: SearchService; - let debugElement: DebugElement; + let loader: HarnessLoader; beforeEach(() => { TestBed.configureTestingModule({ @@ -42,9 +46,9 @@ describe('AddPermissionPanelComponent', () => { ] }); fixture = TestBed.createComponent(AddPermissionPanelComponent); + loader = TestbedHarnessEnvironment.loader(fixture); searchApiService = fixture.componentRef.injector.get(SearchService); - debugElement = fixture.debugElement; element = fixture.nativeElement; component = fixture.componentInstance; @@ -55,12 +59,10 @@ describe('AddPermissionPanelComponent', () => { fixture.destroy(); }); - const typeWordIntoSearchInput = (word: string): void => { - const inputDebugElement = debugElement.query(By.css('#searchInput')); - inputDebugElement.nativeElement.value = word; - inputDebugElement.nativeElement.focus(); - inputDebugElement.nativeElement.dispatchEvent(new Event('input')); - fixture.detectChanges(); + const typeWordIntoSearchInput = async (word: string): Promise => { + const input = await loader.getHarness(MatInputHarness.with({ selector: '#searchInput' })); + + return input.setValue(word); }; it('should be able to render the component', () => { @@ -73,9 +75,7 @@ describe('AddPermissionPanelComponent', () => { expect(element.querySelector('#adf-add-permission-type-search')).not.toBeNull(); expect(element.querySelector('#searchInput')).not.toBeNull(); - typeWordIntoSearchInput('a'); - await fixture.whenStable(); - fixture.detectChanges(); + await typeWordIntoSearchInput('a'); expect(element.querySelector('#adf-add-permission-authority-results')).not.toBeNull(); expect(element.querySelector('#result_option_0')).not.toBeNull(); @@ -90,9 +90,7 @@ describe('AddPermissionPanelComponent', () => { expect(items[0].entry.id).toBe(fakeAuthorityListResult.list.entries[0].entry.id); }); - typeWordIntoSearchInput('a'); - await fixture.whenStable(); - fixture.detectChanges(); + await typeWordIntoSearchInput('a'); const listElement: DebugElement = fixture.debugElement.query(By.css('#result_option_0')); expect(listElement).toBeTruthy(); @@ -104,9 +102,7 @@ describe('AddPermissionPanelComponent', () => { expect(element.querySelector('#adf-add-permission-type-search')).not.toBeNull(); expect(element.querySelector('#searchInput')).not.toBeNull(); - typeWordIntoSearchInput('a'); - await fixture.whenStable(); - fixture.detectChanges(); + await typeWordIntoSearchInput('a'); expect(element.querySelector('#adf-add-permission-authority-results')).toBeTruthy(); expect(element.querySelector('#result_option_0')).toBeTruthy(); @@ -118,10 +114,8 @@ describe('AddPermissionPanelComponent', () => { spyOn(searchApiService, 'search').and.returnValue(of(fakeAuthorityListResult)); expect(element.querySelector('#adf-add-permission-type-search')).not.toBeNull(); expect(element.querySelector('#searchInput')).not.toBeNull(); - typeWordIntoSearchInput('a'); + await typeWordIntoSearchInput('a'); - await fixture.whenStable(); - fixture.detectChanges(); expect(element.querySelector('#result_option_0')).toBeTruthy(); const clearButton = fixture.debugElement.query(By.css('#adf-permission-clear-input')); @@ -136,9 +130,7 @@ describe('AddPermissionPanelComponent', () => { it('should remove element from selection on click when is already selected', async () => { spyOn(searchApiService, 'search').and.returnValue(of(fakeAuthorityListResult)); - typeWordIntoSearchInput('a'); - await fixture.whenStable(); - fixture.detectChanges(); + await typeWordIntoSearchInput('a'); let selectedUserIcon = fixture.debugElement.query(By.css('.adf-people-select-icon')); const listElement: DebugElement = fixture.debugElement.query(By.css('#result_option_0')); @@ -162,9 +154,7 @@ describe('AddPermissionPanelComponent', () => { spyOn(searchApiService, 'search').and.returnValue(of(fakeAuthorityListResult)); component.selectedItems.push(fakeAuthorityListResult.list.entries[0]); - typeWordIntoSearchInput('a'); - await fixture.whenStable(); - fixture.detectChanges(); + await typeWordIntoSearchInput('a'); expect(element.querySelector('#adf-add-permission-authority-results')).toBeTruthy(); expect(element.querySelector('#adf-add-permission-group-everyone')).toBeTruthy(); @@ -177,9 +167,7 @@ describe('AddPermissionPanelComponent', () => { spyOn(searchApiService, 'search').and.returnValue(of({ list: { entries: [] } })); component.selectedItems.push(fakeAuthorityListResult.list.entries[0]); - typeWordIntoSearchInput('a'); - await fixture.whenStable(); - fixture.detectChanges(); + await typeWordIntoSearchInput('a'); expect(element.querySelector('#adf-add-permission-authority-results')).not.toBeNull(); expect(element.querySelector('#adf-add-permission-group-everyone')).toBeDefined(); @@ -191,13 +179,12 @@ describe('AddPermissionPanelComponent', () => { component.selectedItems.push(fakeNameListResult.list.entries[0]); component.selectedItems.push(fakeNameListResult.list.entries[1]); - typeWordIntoSearchInput('a'); - await fixture.whenStable(); - fixture.detectChanges(); + await typeWordIntoSearchInput('a'); - expect(element.querySelector('#result_option_0 .mat-list-text')).toBeTruthy(); - expect(element.querySelector('#result_option_1 .mat-list-text')).toBeTruthy(); - expect(element.querySelector('#result_option_0 .mat-list-text').innerHTML).not.toEqual(element.querySelector('#result_option_1 .mat-list-text').innerHTML); + const list = await loader.getHarness(MatSelectionListHarness); + const items = await list.getItems(); + expect(items.length).toBe(3); + expect(await items[0].getText()).not.toEqual(await items[1].getText()); }); it('should emit unique element in between multiple search', async () => { @@ -212,9 +199,7 @@ describe('AddPermissionPanelComponent', () => { expect(items[0].entry.id).toBe(fakeAuthorityListResult.list.entries[0].entry.id); }); - typeWordIntoSearchInput('a'); - await fixture.whenStable(); - fixture.detectChanges(); + await typeWordIntoSearchInput('a'); let listElement: DebugElement = fixture.debugElement.query(By.css('#result_option_0')); expect(listElement).not.toBeNull(); @@ -226,9 +211,7 @@ describe('AddPermissionPanelComponent', () => { clearButton.triggerEventHandler('click', {}); fixture.detectChanges(); - typeWordIntoSearchInput('abc'); - await fixture.whenStable(); - fixture.detectChanges(); + await typeWordIntoSearchInput('abc'); listElement = fixture.debugElement.query(By.css('#result_option_0')); expect(listElement).not.toBeNull(); diff --git a/lib/content-services/src/lib/search/components/search-chip-autocomplete-input/search-chip-autocomplete-input.component.spec.ts b/lib/content-services/src/lib/search/components/search-chip-autocomplete-input/search-chip-autocomplete-input.component.spec.ts index 45056d2f60..57778b1777 100644 --- a/lib/content-services/src/lib/search/components/search-chip-autocomplete-input/search-chip-autocomplete-input.component.spec.ts +++ b/lib/content-services/src/lib/search/components/search-chip-autocomplete-input/search-chip-autocomplete-input.component.spec.ts @@ -16,17 +16,23 @@ */ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MatChip, MatChipRemove } from '@angular/material/chips'; +import { MatChipRemove } from '@angular/material/chips'; import { By } from '@angular/platform-browser'; import { TranslateModule } from '@ngx-translate/core'; import { Subject } from 'rxjs'; import { ContentTestingModule } from '../../../testing/content.testing.module'; import { SearchChipAutocompleteInputComponent } from './search-chip-autocomplete-input.component'; import { DebugElement } from '@angular/core'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { MatChipHarness, MatChipListHarness } from '@angular/material/chips/testing'; +import { MatAutocompleteHarness } from '@angular/material/autocomplete/testing'; +import { MatOptionHarness } from '@angular/material/core/testing'; describe('SearchChipAutocompleteInputComponent', () => { let component: SearchChipAutocompleteInputComponent; let fixture: ComponentFixture; + let loader: HarnessLoader; const onResetSubject = new Subject(); beforeEach(() => { @@ -39,6 +45,7 @@ describe('SearchChipAutocompleteInputComponent', () => { }); fixture = TestBed.createComponent(SearchChipAutocompleteInputComponent); + loader = TestbedHarnessEnvironment.loader(fixture); component = fixture.componentInstance; component.onReset$ = onResetSubject.asObservable(); component.autocompleteOptions = [{value: 'option1'}, {value: 'option2'}]; @@ -85,8 +92,9 @@ describe('SearchChipAutocompleteInputComponent', () => { * * @returns list of chips */ - function getChipList(): MatChip[] { - return fixture.debugElement.queryAll(By.css('mat-chip')).map((chip) => chip.nativeElement); + async function getChipList(): Promise { + const harness = await loader.getHarness(MatChipListHarness); + return harness.getChips(); } /** @@ -95,8 +103,9 @@ describe('SearchChipAutocompleteInputComponent', () => { * @param index index of the chip * @returns chip value */ - function getChipValue(index: number): string { - return fixture.debugElement.queryAll(By.css('mat-chip span')).map((chip) => chip.nativeElement)[index].innerText; + async function getChipValue(index: number): Promise { + const chipList = await getChipList(); + return chipList[index].getText(); } /** @@ -104,8 +113,9 @@ describe('SearchChipAutocompleteInputComponent', () => { * * @returns list of debug elements */ - function getOptionElements(): DebugElement[] { - return fixture.debugElement.queryAll(By.css('mat-option')); + async function getOptionElements(): Promise { + const autocomplete = await loader.getHarness(MatAutocompleteHarness); + return autocomplete.getOptions(); } /** @@ -117,29 +127,29 @@ describe('SearchChipAutocompleteInputComponent', () => { return fixture.debugElement.queryAll(By.css('.adf-autocomplete-added-option')); } - it('should add new option only if value is predefined when allowOnlyPredefinedValues = true', () => { + it('should add new option only if value is predefined when allowOnlyPredefinedValues = true', async () => { addNewOption('test'); addNewOption('option1'); - expect(getChipList().length).toBe(1); - expect(getChipValue(0)).toBe('option1'); + expect((await getChipList()).length).toBe(1); + expect(await getChipValue(0)).toBe('option1'); }); - it('should add new option even if value is not predefined when allowOnlyPredefinedValues = false', () => { + it('should add new option even if value is not predefined when allowOnlyPredefinedValues = false', async () => { component.allowOnlyPredefinedValues = false; addNewOption('test'); addNewOption('option1'); - expect(getChipList().length).toBe(2); - expect(getChipValue(0)).toBe('test'); + expect((await getChipList()).length).toBe(2); + expect(await getChipValue(0)).toBe('test'); }); - it('should add new formatted option based on formatChipValue', () => { + it('should add new formatted option based on formatChipValue', async () => { component.allowOnlyPredefinedValues = false; const option = 'abc'; component.formatChipValue = (value) => value.replace('.', ''); addNewOption(`.${option}`); - expect(getChipList().length).toBe(1); - expect(getChipValue(0)).toBe(option); + expect((await getChipList()).length).toBe(1); + expect(await getChipValue(0)).toBe(option); }); it('should add new option upon clicking on option from autocomplete', async () => { @@ -148,15 +158,15 @@ describe('SearchChipAutocompleteInputComponent', () => { await fixture.whenStable(); fixture.detectChanges(); - const matOptions = getOptionElements(); + const matOptions = await getOptionElements(); expect(matOptions.length).toBe(2); - const optionToClick = matOptions[0].nativeElement as HTMLElement; - optionToClick.click(); + const optionToClick = matOptions[0]; + await optionToClick.click(); expect(optionsChangedSpy).toHaveBeenCalledOnceWith([{value: 'option1'}]); expect(component.selectedOptions).toEqual([{value: 'option1'}]); - expect(getChipList().length).toBe(1); + expect((await getChipList()).length).toBe(1); }); it('should apply class to already selected options', async () => { @@ -192,20 +202,20 @@ describe('SearchChipAutocompleteInputComponent', () => { await fixture.whenStable(); fixture.detectChanges(); - expect(getOptionElements().length).toBe(15); + expect((await getOptionElements()).length).toBe(15); }); - it('should not add a value if same value has already been added', () => { + it('should not add a value if same value has already been added', async () => { addNewOption('option1'); addNewOption('option1'); - expect(getChipList().length).toBe(1); + expect((await getChipList()).length).toBe(1); }); it('should show autocomplete list if similar predefined values exists', async () => { enterNewInputValue('op'); await fixture.whenStable(); fixture.detectChanges(); - expect(getOptionElements().length).toBe(2); + expect((await getOptionElements()).length).toBe(2); }); it('should show autocomplete list based on custom filtering', async () => { @@ -214,22 +224,23 @@ describe('SearchChipAutocompleteInputComponent', () => { enterNewInputValue('test1'); await fixture.whenStable(); fixture.detectChanges(); - expect(getOptionElements().length).toBe(1); + expect((await getOptionElements()).length).toBe(1); }); it('should not show autocomplete list if there are no similar predefined values', async () => { enterNewInputValue('test'); - await fixture.whenStable(); - fixture.detectChanges(); - expect(getOptionElements().length).toBe(0); + + const autocomplete = await loader.getHarness(MatAutocompleteHarness); + + expect(await autocomplete.isOpen()).toBeFalse(); }); - it('should emit new value when selected options changed', () => { + it('should emit new value when selected options changed', async () => { const optionsChangedSpy = spyOn(component.optionsChanged, 'emit'); addNewOption('option1'); expect(optionsChangedSpy).toHaveBeenCalledOnceWith([{value: 'option1'}]); - expect(getChipList().length).toBe(1); - expect(getChipValue(0)).toBe('option1'); + expect((await getChipList()).length).toBe(1); + expect(await getChipValue(0)).toBe('option1'); }); it('should clear the input after a new value is added', () => { @@ -248,7 +259,7 @@ describe('SearchChipAutocompleteInputComponent', () => { expect(getInput().placeholder).toBe(component.placeholder); }); - it('should reset all options when onReset$ event is emitted', () => { + it('should reset all options when onReset$ event is emitted', async () => { addNewOption('option1'); addNewOption('option2'); const optionsChangedSpy = spyOn(component.optionsChanged, 'emit'); @@ -256,11 +267,11 @@ describe('SearchChipAutocompleteInputComponent', () => { fixture.detectChanges(); expect(optionsChangedSpy).toHaveBeenCalledOnceWith([]); - expect(getChipList()).toEqual([]); + expect((await getChipList())).toEqual([]); expect(component.selectedOptions).toEqual([]); }); - it('should remove option upon clicking remove button', () => { + it('should remove option upon clicking remove button', async () => { addNewOption('option1'); addNewOption('option2'); const optionsChangedSpy = spyOn(component.optionsChanged, 'emit'); @@ -269,16 +280,16 @@ describe('SearchChipAutocompleteInputComponent', () => { fixture.detectChanges(); expect(optionsChangedSpy).toHaveBeenCalledOnceWith([{value: 'option2'}]); - expect(getChipList().length).toEqual(1); + expect((await getChipList()).length).toEqual(1); }); - it('should show full category path when fullPath provided', () => { + it('should show full category path when fullPath provided', async () => { component.filteredOptions = [{id: 'test-id', value: 'test-value', fullPath: 'test-full-path'}]; - enterNewInputValue('test-value'); - const matOption = fixture.debugElement.query(By.css('.mat-option span')).nativeElement; - fixture.detectChanges(); - expect(matOption.innerHTML).toEqual(' test-full-path '); + enterNewInputValue('test-value'); + + const matOption = fixture.debugElement.query(By.css('.adf-search-chip-autocomplete-added-option')).nativeElement; + expect(matOption.textContent).toEqual(' test-full-path '); }); it('should emit input value when input changed', async () => { diff --git a/lib/content-services/src/lib/search/components/search-date-range-tabbed/search-date-range/search-date-range.component.spec.ts b/lib/content-services/src/lib/search/components/search-date-range-tabbed/search-date-range/search-date-range.component.spec.ts index a29356ef9b..efb80376aa 100644 --- a/lib/content-services/src/lib/search/components/search-date-range-tabbed/search-date-range/search-date-range.component.spec.ts +++ b/lib/content-services/src/lib/search/components/search-date-range-tabbed/search-date-range/search-date-range.component.spec.ts @@ -22,10 +22,15 @@ import { ContentTestingModule } from '../../../../testing/content.testing.module import { SearchDateRangeComponent } from './search-date-range.component'; import { addDays, endOfToday, format, parse, startOfYesterday, subDays } from 'date-fns'; import { Validators } from '@angular/forms'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { MatCalendarHarness, MatDatepickerToggleHarness } from '@angular/material/datepicker/testing'; +import { MatRadioButtonHarness } from '@angular/material/radio/testing'; describe('SearchDateRangeComponent', () => { let component: SearchDateRangeComponent; let fixture: ComponentFixture; + let loader: HarnessLoader; const startDateSampleValue = parse('05-Jun-23', 'dd-MMM-yy', new Date()); const endDateSampleValue = parse('07-Jun-23', 'dd-MMM-yy', new Date()); @@ -51,6 +56,7 @@ describe('SearchDateRangeComponent', () => { betweenStartDate: null, betweenEndDate: null }); + loader = TestbedHarnessEnvironment.documentRootLoader(fixture); fixture.detectChanges(); }); @@ -179,12 +185,15 @@ describe('SearchDateRangeComponent', () => { component.form.controls.dateRangeType.setValue(component.DateRangeType.BETWEEN); component.maxDate = 'today'; fixture.detectChanges(); - getElementBySelector('[data-automation-id="date-range-between-datepicker-toggle"]').click(); - fixture.detectChanges(); + + const datePicker = await loader.getHarness(MatDatepickerToggleHarness); + await datePicker.openCalendar(); + + const calendar = await loader.getHarness(MatCalendarHarness); const afterDate = format(addDays(new Date(), 1), 'MMM d, yyyy'); - const afterDateItem = document.querySelector(`.mat-calendar-body-cell[aria-label="${afterDate}"]`); - expect(afterDateItem.getAttribute('aria-disabled')).toBeTruthy(); + const cell = await calendar.getCells({ disabled: true }); + expect(await cell[0].getAriaLabel()).toEqual(afterDate); }); it('should emit valid as false when form is invalid', () => { @@ -216,7 +225,7 @@ describe('SearchDateRangeComponent', () => { expect(component.valid.emit).toHaveBeenCalledWith(true); }); - it('should not emit values when form is invalid', () => { + it('should not emit values when form is invalid', async () => { spyOn(component.changed, 'emit'); let value = { dateRangeType: component.DateRangeType.IN_LAST, @@ -225,8 +234,8 @@ describe('SearchDateRangeComponent', () => { betweenStartDate: undefined, betweenEndDate: undefined }; - let dateRangeTypeRadioButton = getElementBySelector('[data-automation-id="date-range-in-last"] .mat-radio-input'); - dateRangeTypeRadioButton.click(); + let dateRangeTypeRadioButton = await loader.getHarness(MatRadioButtonHarness.with({ selector: '[data-automation-id="date-range-in-last"]' })); + await dateRangeTypeRadioButton.check(); selectDropdownOption('date-range-in-last-option-weeks'); enterValueInInputFieldAndTriggerEvent('date-range-in-last-input', ''); expect(component.changed.emit).not.toHaveBeenCalledWith(value); @@ -246,13 +255,13 @@ describe('SearchDateRangeComponent', () => { betweenStartDate: '', betweenEndDate: '' }; - dateRangeTypeRadioButton = getElementBySelector('[data-automation-id="date-range-between"] .mat-radio-input'); - dateRangeTypeRadioButton.click(); + dateRangeTypeRadioButton = await loader.getHarness(MatRadioButtonHarness.with({ selector: '[data-automation-id="date-range-between"]' })); + await dateRangeTypeRadioButton.check(); fixture.detectChanges(); expect(component.changed.emit).not.toHaveBeenCalledWith(value); }); - it('should emit values when form is valid', () => { + it('should emit values when form is valid', async () => { spyOn(component.changed, 'emit'); let value = { dateRangeType: component.DateRangeType.IN_LAST, @@ -261,8 +270,8 @@ describe('SearchDateRangeComponent', () => { betweenStartDate: null, betweenEndDate: null }; - let dateRangeTypeRadioButton = getElementBySelector('[data-automation-id="date-range-in-last"] .mat-radio-input'); - dateRangeTypeRadioButton.click(); + let dateRangeTypeRadioButton = await loader.getHarness(MatRadioButtonHarness.with({ selector: '[data-automation-id="date-range-in-last"]' })); + await dateRangeTypeRadioButton.check(); selectDropdownOption('date-range-in-last-option-weeks'); enterValueInInputFieldAndTriggerEvent('date-range-in-last-input', '5'); fixture.detectChanges(); @@ -283,8 +292,8 @@ describe('SearchDateRangeComponent', () => { betweenStartDate: startDateSampleValue, betweenEndDate: endDateSampleValue }; - dateRangeTypeRadioButton = getElementBySelector('[data-automation-id="date-range-between"] .mat-radio-input'); - dateRangeTypeRadioButton.click(); + dateRangeTypeRadioButton = await loader.getHarness(MatRadioButtonHarness.with({ selector: '[data-automation-id="date-range-between"]' })); + await dateRangeTypeRadioButton.check(); component.betweenStartDateFormControl.setValue(startDateSampleValue); component.betweenEndDateFormControl.setValue(endDateSampleValue); fixture.detectChanges(); diff --git a/lib/content-services/src/lib/search/components/search-filter-chips/search-facet-chip-tabbed/search-facet-tabbed-content.component.spec.ts b/lib/content-services/src/lib/search/components/search-filter-chips/search-facet-chip-tabbed/search-facet-tabbed-content.component.spec.ts index 50ac129f64..f7322b425e 100644 --- a/lib/content-services/src/lib/search/components/search-filter-chips/search-facet-chip-tabbed/search-facet-tabbed-content.component.spec.ts +++ b/lib/content-services/src/lib/search/components/search-filter-chips/search-facet-chip-tabbed/search-facet-tabbed-content.component.spec.ts @@ -18,7 +18,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ContentTestingModule } from '../../../../testing/content.testing.module'; import { TranslateModule } from '@ngx-translate/core'; -import { By } from '@angular/platform-browser'; import { SearchQueryBuilderService } from '../../../services/search-query-builder.service'; import { SearchFilterList } from '../../../models/search-filter-list.model'; import { FacetField } from '../../../models/facet-field.interface'; @@ -26,12 +25,16 @@ import { SearchFacetFiltersService } from '../../../services/search-facet-filter import { NO_ERRORS_SCHEMA, SimpleChange } from '@angular/core'; import { SearchFacetTabbedContentComponent } from './search-facet-tabbed-content.component'; import { of } from 'rxjs'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { MatTabGroupHarness, MatTabHarness } from '@angular/material/tabs/testing'; describe('SearchFacetTabbedContentComponent', () => { let component: SearchFacetTabbedContentComponent; let fixture: ComponentFixture; let queryBuilder: SearchQueryBuilderService; let searchFacetService: SearchFacetFiltersService; + let loader: HarnessLoader; beforeEach(() => { TestBed.configureTestingModule({ @@ -39,6 +42,7 @@ describe('SearchFacetTabbedContentComponent', () => { schemas: [NO_ERRORS_SCHEMA] }); fixture = TestBed.createComponent(SearchFacetTabbedContentComponent); + loader = TestbedHarnessEnvironment.loader(fixture); component = fixture.componentInstance; queryBuilder = TestBed.inject(SearchQueryBuilderService); searchFacetService = TestBed.inject(SearchFacetFiltersService); @@ -67,18 +71,10 @@ describe('SearchFacetTabbedContentComponent', () => { * * @returns list of native elements */ - function getTabs(): HTMLDivElement[] { - return fixture.debugElement.queryAll(By.css('.mat-tab-label-content')).map((element) => element.nativeElement); - } + async function getTabs(): Promise { + const tabGroup = await loader.getHarness(MatTabGroupHarness); - /** - * Set selected tab - * - * @param tabIndex index of the tab - */ - function changeTab(tabIndex: number) { - getTabs()[tabIndex].click(); - fixture.detectChanges(); + return tabGroup.getTabs(); } /** @@ -107,20 +103,22 @@ describe('SearchFacetTabbedContentComponent', () => { triggerComponentChanges(); } - it('should display 2 tabs with specific labels', () => { - const tabLabels = getTabs(); + it('should display 2 tabs with specific labels', async () => { + const tabLabels = await getTabs(); expect(tabLabels.length).toBe(2); - expect(tabLabels[0].innerText).toBe(component.tabbedFacet.facets['field'].label); - expect(tabLabels[1].innerText).toBe(component.tabbedFacet.facets['field2'].label); + expect(await tabLabels[0].getLabel()).toBe(component.tabbedFacet.facets['field'].label); + expect(await tabLabels[1].getLabel()).toBe(component.tabbedFacet.facets['field2'].label); }); - it('should display creator tab as active initially and allow navigation', () => { - let activeTabLabel = fixture.debugElement.query(By.css('.mat-tab-label-active .mat-tab-label-content')).nativeElement.innerText; - expect(activeTabLabel).toBe(component.tabbedFacet.facets['field'].label); + it('should display creator tab as active initially and allow navigation', async () => { + let tabs = await getTabs(); + expect(await tabs[0].isSelected()).toBeTrue(); + expect(await tabs[1].isSelected()).toBeFalse(); - changeTab(1); - activeTabLabel = fixture.debugElement.query(By.css('.mat-tab-label-active .mat-tab-label-content')).nativeElement.innerText; - expect(activeTabLabel).toBe(component.tabbedFacet.facets['field2'].label); + await tabs[1].select(); + + expect(await tabs[0].isSelected()).toBeFalse(); + expect(await tabs[1].isSelected()).toBeTrue(); }); it('should create empty selected options for each tab initially', () => { diff --git a/lib/content-services/src/lib/search/components/search-filter-chips/search-filter-chips.component.spec.ts b/lib/content-services/src/lib/search/components/search-filter-chips/search-filter-chips.component.spec.ts index d80a78462a..9789f1b89b 100644 --- a/lib/content-services/src/lib/search/components/search-filter-chips/search-filter-chips.component.spec.ts +++ b/lib/content-services/src/lib/search/components/search-filter-chips/search-filter-chips.component.spec.ts @@ -34,14 +34,19 @@ import { stepThree, stepTwo } from '../../../mock'; -import { getAllMenus } from '../search-filter/search-filter.component.spec'; import { AppConfigService } from '@alfresco/adf-core'; +import { MatButtonHarness } from '@angular/material/button/testing'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { MatCheckboxHarness } from '@angular/material/checkbox/testing'; +import { MatChipHarness } from '@angular/material/chips/testing'; describe('SearchFilterChipsComponent', () => { let fixture: ComponentFixture; let searchFacetFiltersService: SearchFacetFiltersService; let queryBuilder: SearchQueryBuilderService; let appConfigService: AppConfigService; + let loader: HarnessLoader; beforeEach(() => { TestBed.configureTestingModule({ @@ -54,9 +59,10 @@ describe('SearchFilterChipsComponent', () => { appConfigService = TestBed.inject(AppConfigService); searchFacetFiltersService = TestBed.inject(SearchFacetFiltersService); fixture = TestBed.createComponent(SearchFilterChipsComponent); + loader = TestbedHarnessEnvironment.documentRootLoader(fixture); }); - it('should fetch facet fields from response payload and show the already checked items', () => { + it('should fetch facet fields from response payload and show the already checked items', async () => { spyOn(queryBuilder, 'execute').and.stub(); queryBuilder.config = { categories: [], @@ -93,10 +99,8 @@ describe('SearchFilterChipsComponent', () => { fixture.detectChanges(); - const facetChip = fixture.debugElement.query(By.css('[data-automation-id="search-fact-chip-f1"] mat-chip')); - facetChip.triggerEventHandler('click', { stopPropagation: () => null }); - - fixture.detectChanges(); + const facetChip = await loader.getHarness(MatChipHarness.with({ selector: '[data-automation-id="search-filter-chip-f1"]' })); + await (await facetChip.host()).click(); const facetField: SearchFacetFieldComponent = fixture.debugElement.query(By.css('adf-search-facet-field')).componentInstance; facetField.selectFacetBucket({ field: 'f1', label: 'f1' }, searchFacetFiltersService.responseFacets[0].buckets.items[1]); @@ -106,7 +110,7 @@ describe('SearchFilterChipsComponent', () => { expect(searchFacetFiltersService.responseFacets[0].buckets.items[0].checked).toEqual(true, 'should show the already checked item'); }); - it('should fetch facet fields from response payload and show the newly checked items', () => { + it('should fetch facet fields from response payload and show the newly checked items', async () => { spyOn(queryBuilder, 'execute').and.stub(); queryBuilder.config = { categories: [], @@ -142,10 +146,8 @@ describe('SearchFilterChipsComponent', () => { }; fixture.detectChanges(); - const facetChip = fixture.debugElement.query(By.css('[data-automation-id="search-fact-chip-f1"] mat-chip')); - facetChip.triggerEventHandler('click', { stopPropagation: () => null }); - - fixture.detectChanges(); + const facetChip = await loader.getHarness(MatChipHarness.with({ selector: '[data-automation-id="search-filter-chip-f1"]' })); + await (await facetChip.host()).click(); const facetField: SearchFacetFieldComponent = fixture.debugElement.query(By.css('adf-search-facet-field')).componentInstance; facetField.selectFacetBucket({ field: 'f1', label: 'f1' }, searchFacetFiltersService.responseFacets[0].buckets.items[1]); @@ -155,7 +157,7 @@ describe('SearchFilterChipsComponent', () => { expect(searchFacetFiltersService.responseFacets[0].buckets.items[1].checked).toEqual(true, 'should show the newly checked item'); }); - it('should show buckets with 0 values when there are no facet fields on the response payload', () => { + it('should show buckets with 0 values when there are no facet fields on the response payload', async () => { spyOn(queryBuilder, 'execute').and.stub(); queryBuilder.config = { categories: [], @@ -182,10 +184,8 @@ describe('SearchFilterChipsComponent', () => { }; fixture.detectChanges(); - const facetChip = fixture.debugElement.query(By.css('[data-automation-id="search-fact-chip-f1"] mat-chip')); - facetChip.triggerEventHandler('click', { stopPropagation: () => null }); - - fixture.detectChanges(); + const facetChip = await loader.getHarness(MatChipHarness.with({ selector: '[data-automation-id="search-filter-chip-f1"]' })); + await (await facetChip.host()).click(); const facetField: SearchFacetFieldComponent = fixture.debugElement.query(By.css('adf-search-facet-field')).componentInstance; facetField.selectFacetBucket({ field: 'f1', label: 'f1' }, searchFacetFiltersService.responseFacets[0].buckets.items[1]); @@ -195,7 +195,7 @@ describe('SearchFilterChipsComponent', () => { expect(searchFacetFiltersService.responseFacets[0].buckets.items[1].count).toEqual(0); }); - it('should update query builder upon resetting selected queries', () => { + it('should update query builder upon resetting selected queries', async () => { spyOn(queryBuilder, 'update').and.stub(); spyOn(queryBuilder, 'removeUserFacetBucket').and.callThrough(); @@ -211,9 +211,9 @@ describe('SearchFilterChipsComponent', () => { fixture.detectChanges(); - const facetChip = fixture.debugElement.query(By.css(`[data-automation-id="search-fact-chip-query-response"] mat-chip`)); - facetChip.triggerEventHandler('click', { stopPropagation: () => null }); - fixture.detectChanges(); + const facetChip = await loader.getHarness(MatChipHarness.with({ selector: '[data-automation-id="search-filter-chip-query response"]' })); + await (await facetChip.host()).click(); + const facetField: SearchFacetFieldComponent = fixture.debugElement.query(By.css('adf-search-facet-field')).componentInstance; facetField.resetSelectedBuckets(queryResponse); @@ -232,9 +232,8 @@ describe('SearchFilterChipsComponent', () => { appConfigService.config.search = { categories: disabledCategories }; queryBuilder.resetToDefaults(); - fixture.detectChanges(); - await fixture.whenStable(); - const chips = fixture.debugElement.queryAll(By.css('mat-chip')); + const chips = await loader.getAllHarnesses(MatChipHarness); + expect(chips.length).toBe(0); }); @@ -242,10 +241,7 @@ describe('SearchFilterChipsComponent', () => { appConfigService.config.search = { categories: simpleCategories }; queryBuilder.resetToDefaults(); - fixture.detectChanges(); - await fixture.whenStable(); - - const chips = fixture.debugElement.queryAll(By.css('mat-chip')); + const chips = await loader.getAllHarnesses(MatChipHarness); expect(chips.length).toBe(2); const titleElements = fixture.debugElement.queryAll(By.css('.adf-search-filter-placeholder')); @@ -257,9 +253,7 @@ describe('SearchFilterChipsComponent', () => { appConfigService.config.search = searchFilter; queryBuilder.resetToDefaults(); - fixture.detectChanges(); - await fixture.whenStable(); - let chips = fixture.debugElement.queryAll(By.css('mat-chip')); + let chips = await loader.getAllHarnesses(MatChipHarness); expect(chips.length).toBe(6); fixture.detectChanges(); @@ -272,14 +266,12 @@ describe('SearchFilterChipsComponent', () => { expect(queryBuilder.update).toHaveBeenCalled(); queryBuilder.executed.next(mockSearchResult); - await fixture.whenStable(); - fixture.detectChanges(); - chips = fixture.debugElement.queryAll(By.css('mat-chip')); + chips = await loader.getAllHarnesses(MatChipHarness); expect(chips.length).toBe(8); }); - it('should show the long facet options list with pagination', () => { + it('should show the long facet options list with pagination', async () => { const field = `[data-automation-id="search-field-Size facet queries"]`; appConfigService.config.search = searchFilter; queryBuilder.resetToDefaults(); @@ -293,58 +285,67 @@ describe('SearchFilterChipsComponent', () => { searchChip.triggerEventHandler('click', { stopPropagation: () => null }); fixture.detectChanges(); - let sizes = getAllMenus(`${field} mat-checkbox`, fixture); - expect(sizes).toEqual(stepOne); + let sizes = await loader.getAllHarnesses(MatCheckboxHarness.with({ selector: '.adf-search-filter-facet-checkbox' })); + stepOne.forEach(async (item, index) => { + expect(await sizes[index].getLabelText()).toEqual(item); + }); let moreButton = fixture.debugElement.query(By.css(`${field} button[title="SEARCH.FILTER.ACTIONS.SHOW-MORE"]`)); let lessButton = fixture.debugElement.query(By.css(`${field} button[title="SEARCH.FILTER.ACTIONS.SHOW-LESS"]`)); expect(lessButton).toEqual(null); - expect(moreButton).toBeDefined(); + expect(moreButton).not.toEqual(null); moreButton.triggerEventHandler('click', {}); fixture.detectChanges(); - sizes = getAllMenus(`${field} mat-checkbox`, fixture); - expect(sizes).toEqual(stepTwo); + sizes = await loader.getAllHarnesses(MatCheckboxHarness.with({ selector: '.adf-search-filter-facet-checkbox' })); + stepTwo.forEach(async (item, index) => { + expect(await sizes[index].getLabelText()).toEqual(item); + }); moreButton = fixture.debugElement.query(By.css(`${field} button[title="SEARCH.FILTER.ACTIONS.SHOW-MORE"]`)); lessButton = fixture.debugElement.query(By.css(`${field} button[title="SEARCH.FILTER.ACTIONS.SHOW-LESS"]`)); expect(lessButton).toBeDefined(); - expect(moreButton).toBeDefined(); + expect(moreButton).not.toEqual(null); moreButton.triggerEventHandler('click', {}); fixture.detectChanges(); - sizes = getAllMenus(`${field} mat-checkbox`, fixture); - - expect(sizes).toEqual(stepThree); + sizes = await loader.getAllHarnesses(MatCheckboxHarness.with({ selector: '.adf-search-filter-facet-checkbox' })); + stepThree.forEach(async (item, index) => { + expect(await sizes[index].getLabelText()).toEqual(item); + }); moreButton = fixture.debugElement.query(By.css(`${field} button[title="SEARCH.FILTER.ACTIONS.SHOW-MORE"]`)); lessButton = fixture.debugElement.query(By.css(`${field} button[title="SEARCH.FILTER.ACTIONS.SHOW-LESS"]`)); - expect(lessButton).toBeDefined(); + expect(lessButton).not.toEqual(null); expect(moreButton).toEqual(null); lessButton.triggerEventHandler('click', {}); fixture.detectChanges(); - sizes = getAllMenus(`${field} mat-checkbox`, fixture); - expect(sizes).toEqual(stepTwo); + sizes = await loader.getAllHarnesses(MatCheckboxHarness.with({ selector: '.adf-search-filter-facet-checkbox' })); + stepTwo.forEach(async (item, index) => { + expect(await sizes[index].getLabelText()).toEqual(item); + }); moreButton = fixture.debugElement.query(By.css(`${field} button[title="SEARCH.FILTER.ACTIONS.SHOW-MORE"]`)); lessButton = fixture.debugElement.query(By.css(`${field} button[title="SEARCH.FILTER.ACTIONS.SHOW-LESS"]`)); - expect(lessButton).toBeDefined(); - expect(moreButton).toBeDefined(); + expect(lessButton).not.toEqual(null); + expect(moreButton).not.toEqual(null); lessButton.triggerEventHandler('click', {}); fixture.detectChanges(); - sizes = getAllMenus(`${field} mat-checkbox`, fixture); - expect(sizes).toEqual(stepOne); + sizes = await loader.getAllHarnesses(MatCheckboxHarness.with({ selector: '.adf-search-filter-facet-checkbox' })); + stepOne.forEach(async (item, index) => { + expect(await sizes[index].getLabelText()).toEqual(item); + }); moreButton = fixture.debugElement.query(By.css(`${field} button[title="SEARCH.FILTER.ACTIONS.SHOW-MORE"]`)); lessButton = fixture.debugElement.query(By.css(`${field} button[title="SEARCH.FILTER.ACTIONS.SHOW-LESS"]`)); expect(lessButton).toEqual(null); - expect(moreButton).toBeDefined(); + expect(moreButton).not.toEqual(null); }); it('should not show facets if filter is not available', () => { @@ -363,7 +364,7 @@ describe('SearchFilterChipsComponent', () => { expect(facetElement).toEqual(null); }); - it('should search the facets options and select it', () => { + it('should search the facets options and select it', async () => { const field = `[data-automation-id="search-field-Size facet queries"]`; appConfigService.config.search = searchFilter; queryBuilder.resetToDefaults(); @@ -382,30 +383,30 @@ describe('SearchFilterChipsComponent', () => { inputElement.nativeElement.dispatchEvent(new Event('input')); fixture.detectChanges(); - let filteredMenu = getAllMenus(`${field} mat-checkbox`, fixture); - expect(filteredMenu).toEqual(['Extra Small (10239)']); + let filteredMenu = await loader.getAllHarnesses(MatCheckboxHarness.with({ selector: '.adf-search-filter-facet-checkbox' })); + expect(filteredMenu.length).toBe(1); + expect(await filteredMenu[0].getLabelText()).toEqual('Extra Small (10239)'); inputElement.nativeElement.value = 'my'; inputElement.nativeElement.dispatchEvent(new Event('input')); fixture.detectChanges(); - filteredMenu = getAllMenus(`${field} mat-checkbox`, fixture); - expect(filteredMenu).toEqual(filteredResult); + filteredMenu = await loader.getAllHarnesses(MatCheckboxHarness.with({ selector: '.adf-search-filter-facet-checkbox' })); + filteredResult.forEach(async (item, index) => { + expect(await filteredMenu[index].getLabelText()).toEqual(item); + }); - const clearButton = fixture.debugElement.query(By.css(`${field} mat-form-field button`)); - clearButton.triggerEventHandler('click', {}); - fixture.detectChanges(); + const clearButton = await loader.getHarness(MatButtonHarness.with({selector: '[title="SEARCH.FILTER.BUTTONS.CLEAR"]' })); + await clearButton.click(); - filteredMenu = getAllMenus(`${field} mat-checkbox`, fixture); - expect(filteredMenu).toEqual(stepOne); + filteredMenu = await loader.getAllHarnesses(MatCheckboxHarness.with({ selector: '.adf-search-filter-facet-checkbox' })); + stepOne.forEach(async (item, index) => { + expect(await filteredMenu[index].getLabelText()).toEqual(item); + }); - const firstOption = fixture.debugElement.query(By.css(`${field} mat-checkbox`)); - firstOption.triggerEventHandler('change', { checked: true }); - fixture.detectChanges(); - - const checkedOption = fixture.debugElement.query(By.css(`${field} mat-checkbox.mat-checkbox-checked`)); - expect(checkedOption.nativeElement.innerText).toEqual('Extra Small (10239)'); + await filteredMenu[0].check(); + expect(await filteredMenu[0].getLabelText()).toEqual('Extra Small (10239)'); expect(queryBuilder.update).toHaveBeenCalledTimes(1); }); diff --git a/lib/content-services/src/lib/tag/tags-creator/tags-creator.component.spec.ts b/lib/content-services/src/lib/tag/tags-creator/tags-creator.component.spec.ts index 4c524542df..77a58673f0 100644 --- a/lib/content-services/src/lib/tag/tags-creator/tags-creator.component.spec.ts +++ b/lib/content-services/src/lib/tag/tags-creator/tags-creator.component.spec.ts @@ -31,12 +31,16 @@ import { EMPTY, of, throwError } from 'rxjs'; import { DebugElement } from '@angular/core'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { MatListModule } from '@angular/material/list'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing'; describe('TagsCreatorComponent', () => { let fixture: ComponentFixture; let component: TagsCreatorComponent; let tagService: TagService; let notificationService: NotificationService; + let loader: HarnessLoader; beforeEach(() => { TestBed.configureTestingModule({ @@ -76,6 +80,7 @@ describe('TagsCreatorComponent', () => { }); fixture = TestBed.createComponent(TagsCreatorComponent); + loader = TestbedHarnessEnvironment.loader(fixture); component = fixture.componentInstance; tagService = TestBed.inject(TagService); notificationService = TestBed.inject(NotificationService); @@ -725,34 +730,37 @@ describe('TagsCreatorComponent', () => { * * @returns debug element */ - function getSpinner(): DebugElement { - return fixture.debugElement.query(By.css(`.mat-progress-spinner`)); + async function getSpinner(): Promise { + const progressSpinner = await loader.getHarnessOrNull(MatProgressSpinnerHarness); + + return progressSpinner; } - it('should be displayed when existing tags are loading', fakeAsync(() => { + it('should be displayed when existing tags are loading', fakeAsync(async () => { typeTag('tag', 0); component.tagNameControl.markAsTouched(); fixture.detectChanges(); - const spinner = getSpinner(); + const spinner = await getSpinner(); expect(spinner).toBeTruthy(); discardPeriodicTasks(); flush(); })); - it('should not be displayed when existing tags stopped loading', fakeAsync(() => { + it('should not be displayed when existing tags stopped loading', fakeAsync(async () => { typeTag('tag'); - const spinner = getSpinner(); + const spinner = await getSpinner(); expect(spinner).toBeFalsy(); })); - it('should have correct diameter', fakeAsync(() => { + it('should have correct diameter', fakeAsync(async () => { typeTag('tag', 0); - const spinner = getSpinner(); - expect(spinner.componentInstance.diameter).toBe(50); + const spinner = await getSpinner(); + expect((await (await spinner.host()).getDimensions()).width).toBe(50); + expect((await (await spinner.host()).getDimensions()).height).toBe(50); discardPeriodicTasks(); flush(); diff --git a/lib/content-services/src/lib/tree-view/components/tree-view.component.spec.ts b/lib/content-services/src/lib/tree-view/components/tree-view.component.spec.ts index 43bf8235c1..511468d645 100644 --- a/lib/content-services/src/lib/tree-view/components/tree-view.component.spec.ts +++ b/lib/content-services/src/lib/tree-view/components/tree-view.component.spec.ts @@ -134,7 +134,7 @@ describe('TreeViewComponent', () => { expect(element.querySelector('#fake-child-name-tree-child-node')).not.toBeNull(); expect(element.querySelector('#fake-second-name-tree-child-node')).not.toBeNull(); - expect(element.querySelectorAll('mat-tree-node').length).toBe(4); + expect(element.querySelectorAll('.adf-tree-view-node').length).toBe(4); }); it('should throw a nodeClicked event when a node is clicked', (done) => { @@ -153,13 +153,13 @@ describe('TreeViewComponent', () => { it('should change the icon of the opened folders', async () => { const rootFolderButton = element.querySelector('#button-fake-node-name'); expect(rootFolderButton).not.toBeNull(); - expect(element.querySelector('#button-fake-node-name .mat-icon').textContent.trim()).toBe('folder'); + expect(element.querySelector('#button-fake-node-name .adf-tree-view-icon').textContent.trim()).toBe('folder'); rootFolderButton.click(); fixture.detectChanges(); await fixture.whenStable(); - expect(element.querySelector('#button-fake-node-name .mat-icon').textContent.trim()).toBe('folder_open'); + expect(element.querySelector('#button-fake-node-name .adf-tree-view-icon').textContent.trim()).toBe('folder_open'); }); it('should show the subfolders of a subfolder if there are any', async () => { @@ -197,7 +197,7 @@ describe('TreeViewComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - expect(element.querySelector('#button-fake-node-name .mat-icon').textContent.trim()).toBe('folder'); + expect(element.querySelector('#button-fake-node-name .adf-tree-view-icon').textContent.trim()).toBe('folder'); expect(element.querySelector('#fake-child-name-tree-child-node')).toBeNull(); expect(element.querySelector('#fake-second-name-tree-child-node')).toBeNull(); }); diff --git a/lib/content-services/src/lib/tree/components/tree.component.html b/lib/content-services/src/lib/tree/components/tree.component.html index e67d6bf254..7fb5f3c178 100644 --- a/lib/content-services/src/lib/tree/components/tree.component.html +++ b/lib/content-services/src/lib/tree/components/tree.component.html @@ -72,14 +72,16 @@ [id]="node.id" [checked]="descendantsAllSelected(node)" [indeterminate]="descendantsPartiallySelected(node)" - (change)="onNodeSelected(node)"> + (change)="onNodeSelected(node)" + data-automation-id="has-children-node-checkbox"> + (change)="onNodeSelected(node)" + data-automation-id="no-children-node-checkbox"> diff --git a/lib/content-services/src/lib/tree/components/tree.component.spec.ts b/lib/content-services/src/lib/tree/components/tree.component.spec.ts index 046c62a318..546c47f01e 100644 --- a/lib/content-services/src/lib/tree/components/tree.component.spec.ts +++ b/lib/content-services/src/lib/tree/components/tree.component.spec.ts @@ -33,13 +33,18 @@ import { TreeServiceMock } from '../mock/tree-service.service.mock'; import { By } from '@angular/platform-browser'; import { SelectionChange } from '@angular/cdk/collections'; import { DebugElement } from '@angular/core'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing'; +import { MatCheckboxHarness } from '@angular/material/checkbox/testing'; describe('TreeComponent', () => { let fixture: ComponentFixture>; let component: TreeComponent; let userPreferenceService: UserPreferencesService; + let loader: HarnessLoader; - const composeNodeSelector = (nodeId: string) => `.mat-tree-node[data-automation-id="node_${nodeId}"]`; + const composeNodeSelector = (nodeId: string) => `[data-automation-id="node_${nodeId}"]`; const getNode = (nodeId: string) => fixture.debugElement.query(By.css(composeNodeSelector(nodeId))); @@ -49,12 +54,13 @@ describe('TreeComponent', () => { const getNodePadding = (nodeId: string) => parseInt(getComputedStyle(getNode(nodeId).nativeElement).paddingLeft, 10); - const getNodeSpinner = (nodeId: string) => fixture.nativeElement.querySelector(`${composeNodeSelector(nodeId)} .mat-progress-spinner`); + const getNodeSpinner = async (nodeId: string) => loader.getHarnessOrNull(MatProgressSpinnerHarness.with({ ancestor: composeNodeSelector(nodeId) })); const getExpandCollapseBtn = (nodeId: string) => fixture.nativeElement.querySelector(`${composeNodeSelector(nodeId)} .adf-icon`); const tickCheckbox = (index: number) => { - const nodeCheckboxes = fixture.debugElement.queryAll(By.css('mat-checkbox')); + const selector = `[data-automation-id="${index === 0 ? 'has-children-node-checkbox' : 'no-children-node-checkbox'}"]`; + const nodeCheckboxes = fixture.debugElement.queryAll(By.css(selector)); nodeCheckboxes[index].nativeElement.dispatchEvent(new Event('change')); }; @@ -73,6 +79,7 @@ describe('TreeComponent', () => { }); fixture = TestBed.createComponent(TreeComponent); + loader = TestbedHarnessEnvironment.loader(fixture); component = fixture.componentInstance; userPreferenceService = TestBed.inject(UserPreferencesService); }); @@ -126,20 +133,21 @@ describe('TreeComponent', () => { expect(nodeLevel1Padding).toBeGreaterThan(nodeLevel0Padding); }); - it('should show a spinner for nodes that are loading subnodes', () => { + it('should show a spinner for nodes that are loading subnodes', async () => { component.treeService.treeNodes = Array.from(treeNodesChildrenMockExpanded); fixture.detectChanges(); component.treeService.treeControl.dataNodes[0].isLoading = true; fixture.detectChanges(); - const nodeSpinner = getNodeSpinner('testId1'); + const nodeSpinner = await getNodeSpinner('testId1'); expect(nodeSpinner).not.toBeNull(); }); - it('should show a spinner while the tree is loading', () => { + it('should show a spinner while the tree is loading', async () => { fixture.detectChanges(); component.loadingRoot$ = of(true); fixture.detectChanges(); - const matSpinnerElement = fixture.nativeElement.querySelector('.adf-tree-loading-spinner-container .mat-progress-spinner'); + + const matSpinnerElement = await loader.getHarnessOrNull(MatProgressSpinnerHarness.with({ ancestor: '.adf-tree-loading-spinner-container' })); expect(matSpinnerElement).not.toBeNull(); }); @@ -172,13 +180,13 @@ describe('TreeComponent', () => { expect(getNodesSpy).toHaveBeenCalledWith('-root-', 0, 25, 'some term'); }); - it('should not collapse node when loading', () => { + it('should not collapse node when loading', async () => { component.refreshTree(); component.treeService.treeNodes[0].isLoading = true; fixture.detectChanges(); const collapseSpy = spyOn(component.treeService, 'collapseNode'); spyOn(component.treeService.treeControl, 'isExpanded').and.returnValue(true); - getNodeSpinner(component.treeService.treeNodes[0].id).dispatchEvent(new Event('click')); + await (await ((await getNodeSpinner(component.treeService.treeNodes[0].id)).host())).click(); expect(collapseSpy).not.toHaveBeenCalled(); }); @@ -192,13 +200,13 @@ describe('TreeComponent', () => { expect(collapseSpy).toHaveBeenCalledWith(component.treeService.treeNodes[0]); }); - it('should not expand node when loading', () => { + it('should not expand node when loading', async () => { component.refreshTree(); component.treeService.treeNodes[0].isLoading = true; fixture.detectChanges(); const expandSpy = spyOn(component.treeService, 'expandNode'); spyOn(component.treeService.treeControl, 'isExpanded').and.returnValue(false); - getNodeSpinner(component.treeService.treeNodes[0].id).dispatchEvent(new Event('click')); + await (await ((await getNodeSpinner(component.treeService.treeNodes[0].id)).host())).click(); expect(expandSpy).not.toHaveBeenCalled(); }); @@ -292,10 +300,11 @@ describe('TreeComponent', () => { .toBeUndefined(); }); - it('selection should be disabled by default, no checkboxes should be displayed', () => { + it('selection should be disabled by default, no checkboxes should be displayed', async () => { component.refreshTree(); fixture.detectChanges(); - const nodeCheckboxes = fixture.debugElement.queryAll(By.css('mat-checkbox')); + const nodeCheckboxes = await loader.getAllHarnesses(MatCheckboxHarness); + expect(nodeCheckboxes.length).toEqual(0); expect(component.selectableNodes).toEqual(false); }); @@ -305,10 +314,10 @@ describe('TreeComponent', () => { component.selectableNodes = true; }); - it('should display checkboxes when selection is enabled', () => { + it('should display checkboxes when selection is enabled', async () => { component.refreshTree(); fixture.detectChanges(); - const nodeCheckboxes = fixture.debugElement.queryAll(By.css('mat-checkbox')); + const nodeCheckboxes = await loader.getAllHarnesses(MatCheckboxHarness); const selectableNodes = component.treeService.treeNodes.filter((node: TreeNode) => node.nodeType !== TreeNodeType.LoadMoreNode); expect(nodeCheckboxes.length).toEqual(selectableNodes.length); }); @@ -336,7 +345,7 @@ describe('TreeComponent', () => { spyOn(component.treeService, 'getSubNodes').and.returnValue(of({ pagination: {}, entries: Array.from(treeNodesChildrenMockExpanded) })); fixture.detectChanges(); tickCheckbox(0); - tickCheckbox(2); + tickCheckbox(1); expect(component.descendantsPartiallySelected(component.treeService.treeNodes[0])).toBeTrue(); expect(component.descendantsPartiallySelected(component.treeService.treeNodes[1])).toBeTrue(); });