From 4408eb4489e7d7d91bb374ef2e2bb3e103896dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Ja=C5=9Bkowski?= <138671284+g-jaskowski@users.noreply.github.com> Date: Thu, 4 Apr 2024 16:45:06 +0200 Subject: [PATCH] [ACS-7311] add no-angular-material-selectors eslint rule (#3732) * [ACS-7311] add no-angular-material-selectors rule, fix errors * [ACS-7311] fix faulty unit tests --- .eslintrc.json | 4 +- .../src/tests/create-library.e2e.ts | 2 +- .../manage-rules.smart-component.spec.ts | 16 +- .../actions/rule-action.ui-component.spec.ts | 67 ++--- ...rule-simple-condition.ui-component.spec.ts | 277 +++++++++--------- .../options/rule-options.ui-component.spec.ts | 18 +- .../services/folder-rule-sets.service.spec.ts | 11 +- .../view-node/view-node.component.spec.ts | 18 +- 8 files changed, 219 insertions(+), 194 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 31c842873..ef1883962 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -320,10 +320,12 @@ }, { "files": ["*.spec.ts", "*.test.ts", "*.e2e.ts"], + "plugins": ["@alfresco/eslint-angular"], "rules": { "@typescript-eslint/no-floating-promises": "warn", "@typescript-eslint/no-misused-promises": "warn", - "max-lines": "off" + "max-lines": "off", + "@alfresco/eslint-angular/no-angular-material-selectors": "error" } }, { diff --git a/e2e/playwright/create-actions/src/tests/create-library.e2e.ts b/e2e/playwright/create-actions/src/tests/create-library.e2e.ts index c535125d5..f0d11e7df 100644 --- a/e2e/playwright/create-actions/src/tests/create-library.e2e.ts +++ b/e2e/playwright/create-actions/src/tests/create-library.e2e.ts @@ -159,7 +159,7 @@ test.describe('Create Libraries ', () => { await libraryViewDetails.click(); await expect(libraryDetails.getNameField('Name').locator('input')).toHaveValue(randomLibraryName); await expect(libraryDetails.getIdField('Library ID').locator('input')).toHaveValue(randomLibraryId); - await expect(libraryDetails.getVisibilityField('Visibility').locator('.mat-select-value').getByText(publicVisibility)).toBeVisible(); + await expect(libraryDetails.getVisibilityField('Visibility').getByText(publicVisibility)).toBeVisible(); await expect(libraryDetails.getDescriptionField).toHaveValue(randomLibraryDescription); createdLibrariesIds.push(randomLibraryId); diff --git a/projects/aca-content/folder-rules/src/manage-rules/manage-rules.smart-component.spec.ts b/projects/aca-content/folder-rules/src/manage-rules/manage-rules.smart-component.spec.ts index 769c0d7b8..11515944a 100644 --- a/projects/aca-content/folder-rules/src/manage-rules/manage-rules.smart-component.spec.ts +++ b/projects/aca-content/folder-rules/src/manage-rules/manage-rules.smart-component.spec.ts @@ -44,11 +44,16 @@ import { FolderRuleSetsService } from '../services/folder-rule-sets.service'; import { ruleMock, ruleSettingsMock } from '../mock/rules.mock'; import { Store } from '@ngrx/store'; import { AppService } from '@alfresco/aca-shared'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { MatProgressBarHarness } from '@angular/material/progress-bar/testing'; +import { MatSlideToggleHarness } from '@angular/material/slide-toggle/testing'; describe('ManageRulesSmartComponent', () => { let fixture: ComponentFixture; let component: ManageRulesSmartComponent; let debugElement: DebugElement; + let loader: HarnessLoader; let folderRuleSetsService: FolderRuleSetsService; let folderRulesService: FolderRulesService; @@ -74,6 +79,7 @@ describe('ManageRulesSmartComponent', () => { fixture = TestBed.createComponent(ManageRulesSmartComponent); component = fixture.componentInstance; debugElement = fixture.debugElement; + loader = TestbedHarnessEnvironment.loader(fixture); folderRuleSetsService = TestBed.inject(FolderRuleSetsService); folderRulesService = TestBed.inject(FolderRulesService); @@ -193,7 +199,7 @@ describe('ManageRulesSmartComponent', () => { expect(component).toBeTruthy(); - const matProgressBar = debugElement.query(By.css('mat-progress-bar')); + const matProgressBar = loader.getHarness(MatProgressBarHarness); const rules = debugElement.query(By.css('.aca-rule-list-item')); const ruleDetails = debugElement.query(By.css('aca-rule-details')); @@ -307,16 +313,18 @@ describe('ManageRulesSmartComponent', () => { actionsService.loading$ = of(false); }); - it('should show inherit rules toggle button, and disable it when isInheritanceToggleDisabled = true', () => { + it('should show inherit rules toggle button, and disable it when isInheritanceToggleDisabled = true', async () => { fixture.detectChanges(); - const createButton = debugElement.query(By.css(`[data-automation-id="manage-rules-inheritance-toggle-button"]`)); + const createButton = await loader.getHarness( + MatSlideToggleHarness.with({ selector: `[data-automation-id="manage-rules-inheritance-toggle-button"]` }) + ); expect(createButton).toBeTruthy(); component.isInheritanceToggleDisabled = true; fixture.detectChanges(); - expect(createButton.nativeNode.classList).toContain('mat-disabled'); + expect(await createButton.isDisabled()).toBeTrue(); }); it('should call onInheritanceToggleChange() on change', () => { diff --git a/projects/aca-content/folder-rules/src/rule-details/actions/rule-action.ui-component.spec.ts b/projects/aca-content/folder-rules/src/rule-details/actions/rule-action.ui-component.spec.ts index 0d55badc9..68e99b0a6 100644 --- a/projects/aca-content/folder-rules/src/rule-details/actions/rule-action.ui-component.spec.ts +++ b/projects/aca-content/folder-rules/src/rule-details/actions/rule-action.ui-component.spec.ts @@ -29,20 +29,19 @@ import { actionLinkToCategoryTransformedMock, actionsTransformedListMock } from import { By } from '@angular/platform-browser'; import { dummyCategoriesConstraints, dummyConstraints, dummyTagsConstraints } from '../../mock/action-parameter-constraints.mock'; import { CategoryService, TagService } from '@alfresco/adf-content-services'; -import { MatSelect } from '@angular/material/select'; import { MatDialog } from '@angular/material/dialog'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { MatSelectHarness } from '@angular/material/select/testing'; describe('RuleActionUiComponent', () => { let fixture: ComponentFixture; let component: RuleActionUiComponent; + let loader: HarnessLoader; - const getSelectElement = (): HTMLElement => fixture.debugElement.query(By.directive(MatSelect)).nativeElement; - - const changeMatSelectValue = (value: string) => { - getSelectElement().click(); - fixture.detectChanges(); - const matOption = fixture.debugElement.query(By.css(`.mat-option[ng-reflect-value="${value}"]`)).nativeElement; - matOption.click(); + const changeMatSelectValue = async (value: string) => { + const matSelect = await loader.getHarness(MatSelectHarness); + await matSelect.clickOptions({ selector: `[ng-reflect-value="${value}"]` }); fixture.detectChanges(); }; @@ -62,6 +61,7 @@ describe('RuleActionUiComponent', () => { fixture = TestBed.createComponent(RuleActionUiComponent); component = fixture.componentInstance; + loader = TestbedHarnessEnvironment.loader(fixture); }); it('should clear empty parameters', async () => { @@ -69,7 +69,7 @@ describe('RuleActionUiComponent', () => { component.parameterConstraints = dummyConstraints; fixture.detectChanges(); - changeMatSelectValue('mock-action-1-definition'); + await changeMatSelectValue('mock-action-1-definition'); setInputValue('test'); await fixture.whenStable(); @@ -82,25 +82,24 @@ describe('RuleActionUiComponent', () => { expect(component.parameters).toEqual({ 'mock-action-parameter-boolean': false }); }); - it('should populate the dropdown selector with the action definitions', () => { + it('should populate the dropdown selector with the action definitions', async () => { component.actionDefinitions = actionsTransformedListMock; fixture.detectChanges(); - getSelectElement().click(); - fixture.detectChanges(); - - const matOptions = fixture.debugElement.queryAll(By.css(`mat-option`)); - expect(matOptions.length).toBe(2); - expect(matOptions[0].nativeElement.innerText).toBe('Action 1 title'); - expect(matOptions[1].nativeElement.innerText).toBe('mock-action-2-definition'); + const select = await loader.getHarness(MatSelectHarness); + await select.open(); + const options = await select.getOptions(); + expect(options.length).toBe(2); + expect(await options[0].getText()).toBe('Action 1 title'); + expect(await options[1].getText()).toBe('mock-action-2-definition'); }); - it('should populate the card view with parameters when an action is selected', () => { + it('should populate the card view with parameters when an action is selected', async () => { component.actionDefinitions = actionsTransformedListMock; component.parameterConstraints = dummyConstraints; fixture.detectChanges(); - changeMatSelectValue('mock-action-1-definition'); + await changeMatSelectValue('mock-action-1-definition'); const cardView = getPropertiesCardView(); @@ -111,16 +110,16 @@ describe('RuleActionUiComponent', () => { expect(cardView.properties[3]).toBeInstanceOf(CardViewTextItemModel); expect(cardView.properties[4]).toBeInstanceOf(CardViewSelectItemModel); - changeMatSelectValue('mock-action-2-definition'); + await changeMatSelectValue('mock-action-2-definition'); expect(fixture.debugElement.query(By.directive(CardViewComponent))).toBeNull(); }); - it('should create category-value action parameter as a text box rather than node picker', () => { + it('should create category-value action parameter as a text box rather than node picker', async () => { component.actionDefinitions = [actionLinkToCategoryTransformedMock]; component.parameterConstraints = dummyConstraints; fixture.detectChanges(); - changeMatSelectValue('mock-action-3-definition'); + await changeMatSelectValue('mock-action-3-definition'); const cardView = getPropertiesCardView(); @@ -130,14 +129,14 @@ describe('RuleActionUiComponent', () => { expect(cardView.properties[0]).toBeInstanceOf(CardViewTextItemModel); }); - it('should open category selector dialog on category-value action parameter clicked', () => { + it('should open category selector dialog on category-value action parameter clicked', async () => { const dialog = fixture.debugElement.injector.get(MatDialog); component.actionDefinitions = [actionLinkToCategoryTransformedMock]; component.parameterConstraints = dummyConstraints; spyOn(dialog, 'open'); fixture.detectChanges(); - changeMatSelectValue('mock-action-3-definition'); + await changeMatSelectValue('mock-action-3-definition'); fixture.debugElement.query(By.css('.adf-textitem-action')).nativeElement.click(); expect(dialog.open).toHaveBeenCalledTimes(1); @@ -149,13 +148,13 @@ describe('RuleActionUiComponent', () => { component.actionDefinitions = actionsTransformedListMock; }); - it('should not filter out tags related options if tagService.areTagsEnabled returns true', (done) => { + it('should not filter out tags related options if tagService.areTagsEnabled returns true', async () => { component.parameterConstraints = dummyTagsConstraints; const tagService = TestBed.inject(TagService); spyOn(tagService, 'areTagsEnabled').and.returnValue(true); fixture.detectChanges(); - changeMatSelectValue('mock-action-1-definition'); + await changeMatSelectValue('mock-action-1-definition'); expect(tagService.areTagsEnabled).toHaveBeenCalled(); (getPropertiesCardView().properties[2] as CardViewSelectItemModel).options$.subscribe((options) => { expect(options).toEqual( @@ -164,17 +163,16 @@ describe('RuleActionUiComponent', () => { label: `${constraint.label} [${constraint.value}]` })) ); - done(); }); }); - it('should filter out tags related options if tagService.areTagsEnabled returns false', (done) => { + it('should filter out tags related options if tagService.areTagsEnabled returns false', async () => { component.parameterConstraints = dummyTagsConstraints; const tagService = TestBed.inject(TagService); spyOn(tagService, 'areTagsEnabled').and.returnValue(false); fixture.detectChanges(); - changeMatSelectValue('mock-action-1-definition'); + await changeMatSelectValue('mock-action-1-definition'); expect(tagService.areTagsEnabled).toHaveBeenCalled(); (getPropertiesCardView().properties[2] as CardViewSelectItemModel).options$.subscribe((options) => { expect(options).toEqual([ @@ -183,17 +181,16 @@ describe('RuleActionUiComponent', () => { label: 'Label 3 [cm:notTagRelated]' } ]); - done(); }); }); - it('should not filter out categories related options if categoryService.areCategoriesEnabled returns true', (done) => { + it('should not filter out categories related options if categoryService.areCategoriesEnabled returns true', async () => { component.parameterConstraints = dummyCategoriesConstraints; const categoriesService = TestBed.inject(CategoryService); spyOn(categoriesService, 'areCategoriesEnabled').and.returnValue(true); fixture.detectChanges(); - changeMatSelectValue('mock-action-1-definition'); + await changeMatSelectValue('mock-action-1-definition'); expect(categoriesService.areCategoriesEnabled).toHaveBeenCalled(); (getPropertiesCardView().properties[2] as CardViewSelectItemModel).options$.subscribe((options) => { expect(options).toEqual( @@ -202,17 +199,16 @@ describe('RuleActionUiComponent', () => { label: `${constraint.label} [${constraint.value}]` })) ); - done(); }); }); - it('should filter out categories related options if categoryService.areCategoriesEnabled returns false', (done) => { + it('should filter out categories related options if categoryService.areCategoriesEnabled returns false', async () => { component.parameterConstraints = dummyCategoriesConstraints; const categoryService = TestBed.inject(CategoryService); spyOn(categoryService, 'areCategoriesEnabled').and.returnValue(false); fixture.detectChanges(); - changeMatSelectValue('mock-action-1-definition'); + await changeMatSelectValue('mock-action-1-definition'); expect(categoryService.areCategoriesEnabled).toHaveBeenCalled(); (getPropertiesCardView().properties[2] as CardViewSelectItemModel).options$.subscribe((options) => { expect(options).toEqual([ @@ -221,7 +217,6 @@ describe('RuleActionUiComponent', () => { label: 'Label 2 [cm:notCategoryRelated]' } ]); - done(); }); }); }); diff --git a/projects/aca-content/folder-rules/src/rule-details/conditions/rule-simple-condition.ui-component.spec.ts b/projects/aca-content/folder-rules/src/rule-details/conditions/rule-simple-condition.ui-component.spec.ts index b21859a89..6e82b6cae 100644 --- a/projects/aca-content/folder-rules/src/rule-details/conditions/rule-simple-condition.ui-component.spec.ts +++ b/projects/aca-content/folder-rules/src/rule-details/conditions/rule-simple-condition.ui-component.spec.ts @@ -33,24 +33,32 @@ import { CategoryService, TagService } from '@alfresco/adf-content-services'; import { of } from 'rxjs'; import { RuleSimpleCondition } from '../../model/rule-simple-condition.model'; import { delay } from 'rxjs/operators'; -import { MatOption } from '@angular/material/core'; import { RuleConditionField, ruleConditionFields } from './rule-condition-fields'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { MatSelectHarness } from '@angular/material/select/testing'; +import { MatAutocompleteHarness } from '@angular/material/autocomplete/testing'; describe('RuleSimpleConditionUiComponent', () => { let fixture: ComponentFixture; let categoryService: CategoryService; + let loader: HarnessLoader; const fieldSelectAutomationId = 'field-select'; + const folderRulesBaseLabel = 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS'; const getByDataAutomationId = (dataAutomationId: string): DebugElement => fixture.debugElement.query(By.css(`[data-automation-id="${dataAutomationId}"]`)); - const changeMatSelectValue = (dataAutomationId: string, value: string) => { - const matSelect = getByDataAutomationId(dataAutomationId).nativeElement; - matSelect.click(); + const changeMatSelectValue = async (dataAutomationId: string, value: string) => { + const matSelect = await loader.getHarness(MatSelectHarness.with({ selector: `[data-automation-id="${dataAutomationId}"]` })); + await matSelect.clickOptions({ selector: `[ng-reflect-value="${value}"]` }); fixture.detectChanges(); - const matOption = fixture.debugElement.query(By.css(`.mat-option[ng-reflect-value="${value}"]`)).nativeElement; - matOption.click(); + }; + + const changeMatAutocompleteValue = async (value: string) => { + const matAutocomplete = await loader.getHarness(MatAutocompleteHarness); + await matAutocomplete.selectOption({ selector: `[ng-reflect-value="${value}"]` }); fixture.detectChanges(); }; @@ -61,16 +69,16 @@ describe('RuleSimpleConditionUiComponent', () => { fixture.detectChanges(); }; - const expectConditionFieldsDisplayedAsOptions = (conditionFields: RuleConditionField[]): void => { - fixture.detectChanges(); - getByDataAutomationId(fieldSelectAutomationId).nativeElement.click(); - fixture.detectChanges(); - const options = fixture.debugElement.queryAll(By.directive(MatOption)); - conditionFields.forEach((field, i) => { - const option = options[i]; - expect(field.name).toBe(option.componentInstance.value); - expect(field.label).toBe(option.nativeElement.textContent.trim()); - }); + const expectConditionFieldsDisplayedAsOptions = async (conditionFields: RuleConditionField[]): Promise => { + loader = TestbedHarnessEnvironment.loader(fixture); + const select = await loader.getHarness(MatSelectHarness); + await select.open(); + const options = await select.getOptions(); + await Promise.all( + conditionFields.map(async (field, i) => { + expect(field.label).toEqual(await options[i].getText()); + }) + ); }; beforeEach(() => { @@ -80,6 +88,7 @@ describe('RuleSimpleConditionUiComponent', () => { fixture = TestBed.createComponent(RuleSimpleConditionUiComponent); categoryService = TestBed.inject(CategoryService); + loader = TestbedHarnessEnvironment.loader(fixture); }); it('should default the field to the name, the comparator to equals and the value empty', () => { @@ -90,60 +99,30 @@ describe('RuleSimpleConditionUiComponent', () => { expect(getByDataAutomationId('value-input').nativeElement.value).toBe(''); }); - it('should hide the comparator select box if the type of the field is special', () => { + it('should hide the comparator select box if the type of the field is mimeType', async () => { + fixture.componentInstance.mimeTypes = [{ value: '', label: '' } as MimeType]; fixture.detectChanges(); const comparatorFormField = getByDataAutomationId('comparator-form-field').nativeElement; expect(fixture.componentInstance.isComparatorHidden).toBeFalsy(); expect(getComputedStyle(comparatorFormField).display).not.toBe('none'); - changeMatSelectValue(fieldSelectAutomationId, 'category'); + await changeMatSelectValue(fieldSelectAutomationId, 'mimetype'); expect(fixture.componentInstance.isComparatorHidden).toBeTruthy(); expect(getComputedStyle(comparatorFormField).display).toBe('none'); }); - it('should hide the comparator select box if the type of the field is mimeType', () => { - fixture.detectChanges(); - const comparatorFormField = getByDataAutomationId('comparator-form-field').nativeElement; + it('should set the comparator to equals if the field is set to a type with different comparators', async () => { + spyOn(fixture.componentInstance, 'onChangeField').and.callThrough(); + const comparatorSelect = await loader.getHarness(MatSelectHarness.with({ selector: '[data-automation-id="comparator-select"]' })); - expect(fixture.componentInstance.isComparatorHidden).toBeFalsy(); - expect(getComputedStyle(comparatorFormField).display).not.toBe('none'); + await changeMatSelectValue('comparator-select', 'contains'); + expect(await comparatorSelect.getValueText()).toBe(folderRulesBaseLabel + '.CONTAINS'); - changeMatSelectValue(fieldSelectAutomationId, 'mimetype'); - - expect(fixture.componentInstance.isComparatorHidden).toBeTruthy(); - expect(getComputedStyle(comparatorFormField).display).toBe('none'); - }); - - it('should hide the comparator select box if the type of the field is autoComplete', () => { - const autoCompleteField = 'category'; - fixture.detectChanges(); - const comparatorFormField = getByDataAutomationId('comparator-form-field').nativeElement; - - expect(fixture.componentInstance.isComparatorHidden).toBeFalsy(); - expect(getComputedStyle(comparatorFormField).display).not.toBe('none'); - - changeMatSelectValue(fieldSelectAutomationId, autoCompleteField); - - expect(fixture.componentInstance.isComparatorHidden).toBeTruthy(); - expect(getComputedStyle(comparatorFormField).display).toBe('none'); - }); - - it('should set the comparator to equals if the field is set to a type with different comparators', () => { - const onChangeFieldSpy = spyOn(fixture.componentInstance, 'onChangeField').and.callThrough(); - fixture.detectChanges(); - changeMatSelectValue('comparator-select', 'contains'); - - expect(getByDataAutomationId('comparator-select').componentInstance.value).toBe('contains'); - changeMatSelectValue(fieldSelectAutomationId, 'mimetype'); - - expect(onChangeFieldSpy).toHaveBeenCalledTimes(1); - expect(getByDataAutomationId('comparator-select').componentInstance.value).toBe('equals'); - changeMatSelectValue(fieldSelectAutomationId, 'size'); - - expect(onChangeFieldSpy).toHaveBeenCalledTimes(2); - expect(getByDataAutomationId('comparator-select').componentInstance.value).toBe('equals'); + await changeMatSelectValue(fieldSelectAutomationId, 'size'); + expect(await comparatorSelect.getValueText()).toBe(folderRulesBaseLabel + '.EQUALS'); + expect(fixture.componentInstance.onChangeField).toHaveBeenCalled(); }); it('should display an additional option for a currently selected unknown field', () => { @@ -160,10 +139,10 @@ describe('RuleSimpleConditionUiComponent', () => { expect((unknownOptionMatOption.nativeElement as HTMLElement).innerText.trim()).toBe(simpleConditionUnknownFieldMock.field); }); - it('should remove the option for the unknown field as soon as another option is selected', () => { + it('should remove the option for the unknown field as soon as another option is selected', async () => { fixture.componentInstance.writeValue(simpleConditionUnknownFieldMock); fixture.detectChanges(); - changeMatSelectValue(fieldSelectAutomationId, 'cm:name'); + await changeMatSelectValue(fieldSelectAutomationId, 'cm:name'); const matSelect = getByDataAutomationId(fieldSelectAutomationId).nativeElement; matSelect.click(); fixture.detectChanges(); @@ -214,38 +193,106 @@ describe('RuleSimpleConditionUiComponent', () => { expect(getByDataAutomationId('value-input').nativeElement.value).toBe(''); }); - it('should provide auto-complete option when category is selected', () => { + it('should show loading spinner while auto-complete options are fetched, and then remove it once it is received', fakeAsync(async () => { + spyOn(categoryService, 'searchCategories').and.returnValue(of(categoriesListMock).pipe(delay(1000))); fixture.detectChanges(); - changeMatSelectValue(fieldSelectAutomationId, 'category'); + await changeMatSelectValue(fieldSelectAutomationId, 'category'); + tick(500); + getByDataAutomationId('auto-complete-input-field')?.nativeElement?.click(); + let loadingSpinner = getByDataAutomationId('auto-complete-loading-spinner'); + expect(loadingSpinner).not.toBeNull(); + tick(1000); + fixture.detectChanges(); + loadingSpinner = getByDataAutomationId('auto-complete-loading-spinner'); + expect(loadingSpinner).toBeNull(); + discardPeriodicTasks(); + })); - expect(getByDataAutomationId('auto-complete-input-field')).toBeTruthy(); - expect(fixture.componentInstance.form.get('parameter').value).toEqual(''); + describe('With categories option selected', () => { + beforeEach(() => { + spyOn(categoryService, 'searchCategories').and.returnValue(of(categoriesListMock)); + }); + + it('should hide the comparator select box if the type of the field is autoComplete', async () => { + const autoCompleteField = 'category'; + fixture.detectChanges(); + const comparatorFormField = getByDataAutomationId('comparator-form-field').nativeElement; + + expect(fixture.componentInstance.isComparatorHidden).toBeFalsy(); + expect(getComputedStyle(comparatorFormField).display).not.toBe('none'); + + await changeMatSelectValue(fieldSelectAutomationId, autoCompleteField); + + expect(fixture.componentInstance.isComparatorHidden).toBeTruthy(); + expect(getComputedStyle(comparatorFormField).display).toBe('none'); + }); + + it('should hide the comparator select box if the type of the field is special', async () => { + fixture.detectChanges(); + const comparatorFormField = getByDataAutomationId('comparator-form-field').nativeElement; + + expect(fixture.componentInstance.isComparatorHidden).toBeFalsy(); + expect(getComputedStyle(comparatorFormField).display).not.toBe('none'); + + await changeMatSelectValue(fieldSelectAutomationId, 'category'); + + expect(fixture.componentInstance.isComparatorHidden).toBeTruthy(); + expect(getComputedStyle(comparatorFormField).display).toBe('none'); + }); + + it('should provide auto-complete option when category is selected', async () => { + fixture.detectChanges(); + await changeMatSelectValue(fieldSelectAutomationId, 'category'); + + expect(getByDataAutomationId('auto-complete-input-field')).toBeTruthy(); + expect(fixture.componentInstance.form.get('parameter').value).toEqual(''); + }); + + it('should fetch category list when category option is selected', fakeAsync(async () => { + fixture.detectChanges(); + await changeMatSelectValue(fieldSelectAutomationId, 'category'); + tick(500); + + expect(categoryService.searchCategories).toHaveBeenCalledWith(''); + })); + + it('should fetch new category list with user input when user types into parameter field after category option is select', fakeAsync(async () => { + const categoryValue = 'a new category'; + + fixture.detectChanges(); + await changeMatSelectValue(fieldSelectAutomationId, 'category'); + tick(500); + expect(categoryService.searchCategories).toHaveBeenCalledWith(''); + + setValueInInputField('auto-complete-input-field', categoryValue); + tick(500); + expect(categoryService.searchCategories).toHaveBeenCalledWith(categoryValue); + })); + + it('should display correct label for category when user selects a category from auto-complete dropdown', fakeAsync(async () => { + fixture.detectChanges(); + await changeMatSelectValue(fieldSelectAutomationId, 'category'); + tick(500); + getByDataAutomationId('auto-complete-input-field')?.nativeElement?.click(); + await changeMatAutocompleteValue(categoriesListMock.list.entries[0].entry.id); + const displayValue = getByDataAutomationId('auto-complete-input-field')?.nativeElement?.value; + expect(displayValue).toBe('category/path/1/FakeCategory1'); + discardPeriodicTasks(); + })); + + it('should automatically select first category when user focuses out of parameter form field with category option selected', fakeAsync(async () => { + fixture.detectChanges(); + await changeMatSelectValue(fieldSelectAutomationId, 'category'); + tick(500); + const autoCompleteInputField = getByDataAutomationId('auto-complete-input-field')?.nativeElement; + autoCompleteInputField.value = 'FakeCat'; + autoCompleteInputField.dispatchEvent(new Event('focusout')); + const parameterValue = fixture.componentInstance.form.get('parameter').value; + expect(parameterValue).toEqual(categoriesListMock.list.entries[0].entry.id); + discardPeriodicTasks(); + })); }); - it('should fetch category list when category option is selected', fakeAsync(() => { - spyOn(categoryService, 'searchCategories').and.returnValue(of(categoriesListMock)); - - fixture.detectChanges(); - changeMatSelectValue(fieldSelectAutomationId, 'category'); - tick(500); - - expect(categoryService.searchCategories).toHaveBeenCalledWith(''); - })); - - it('should fetch new category list with user input when user types into parameter field after category option is select', fakeAsync(() => { - const categoryValue = 'a new category'; - spyOn(categoryService, 'searchCategories').and.returnValue(of(categoriesListMock)); - - fixture.detectChanges(); - changeMatSelectValue(fieldSelectAutomationId, 'category'); - tick(500); - expect(categoryService.searchCategories).toHaveBeenCalledWith(''); - - setValueInInputField('auto-complete-input-field', categoryValue); - tick(500); - expect(categoryService.searchCategories).toHaveBeenCalledWith(categoryValue); - })); - it('should fetch category details when a saved rule with category condition is edited', () => { const savedCategoryMock: RuleSimpleCondition = { field: 'category', @@ -269,77 +316,37 @@ describe('RuleSimpleConditionUiComponent', () => { expect(categoryService.getCategory).toHaveBeenCalledWith(savedCategoryMock.parameter, { include: ['path'] }); }); - it('should show loading spinner while auto-complete options are fetched, and then remove it once it is received', fakeAsync(() => { - spyOn(categoryService, 'searchCategories').and.returnValue(of(categoriesListMock).pipe(delay(1000))); - fixture.detectChanges(); - changeMatSelectValue(fieldSelectAutomationId, 'category'); - tick(500); - getByDataAutomationId('auto-complete-input-field')?.nativeElement?.click(); - let loadingSpinner = getByDataAutomationId('auto-complete-loading-spinner'); - expect(loadingSpinner).not.toBeNull(); - tick(1000); - fixture.detectChanges(); - loadingSpinner = getByDataAutomationId('auto-complete-loading-spinner'); - expect(loadingSpinner).toBeNull(); - discardPeriodicTasks(); - })); - - it('should display correct label for category when user selects a category from auto-complete dropdown', fakeAsync(() => { - spyOn(categoryService, 'searchCategories').and.returnValue(of(categoriesListMock)); - fixture.detectChanges(); - changeMatSelectValue(fieldSelectAutomationId, 'category'); - tick(500); - getByDataAutomationId('auto-complete-input-field')?.nativeElement?.click(); - changeMatSelectValue('folder-rule-auto-complete', categoriesListMock.list.entries[0].entry.id); - const displayValue = getByDataAutomationId('auto-complete-input-field')?.nativeElement?.value; - expect(displayValue).toBe('category/path/1/FakeCategory1'); - discardPeriodicTasks(); - })); - - it('should automatically select first category when user focuses out of parameter form field with category option selected', fakeAsync(() => { - spyOn(categoryService, 'searchCategories').and.returnValue(of(categoriesListMock)); - fixture.detectChanges(); - changeMatSelectValue(fieldSelectAutomationId, 'category'); - tick(500); - const autoCompleteInputField = getByDataAutomationId('auto-complete-input-field')?.nativeElement; - autoCompleteInputField.value = 'FakeCat'; - autoCompleteInputField.dispatchEvent(new Event('focusout')); - const parameterValue = fixture.componentInstance.form.get('parameter').value; - expect(parameterValue).toEqual(categoriesListMock.list.entries[0].entry.id); - discardPeriodicTasks(); - })); - - it('should display correct condition field options when tagService.areTagsEnabled returns true', () => { + it('should display correct condition field options when tagService.areTagsEnabled returns true', async () => { const tagService = TestBed.inject(TagService); spyOn(tagService, 'areTagsEnabled').and.returnValue(true); fixture = TestBed.createComponent(RuleSimpleConditionUiComponent); expect(tagService.areTagsEnabled).toHaveBeenCalled(); - expectConditionFieldsDisplayedAsOptions(ruleConditionFields); + await expectConditionFieldsDisplayedAsOptions(ruleConditionFields); }); - it('should display correct condition field options when tagService.areTagsEnabled returns false', () => { + it('should display correct condition field options when tagService.areTagsEnabled returns false', async () => { const tagService = TestBed.inject(TagService); spyOn(tagService, 'areTagsEnabled').and.returnValue(false); fixture = TestBed.createComponent(RuleSimpleConditionUiComponent); expect(tagService.areTagsEnabled).toHaveBeenCalled(); - expectConditionFieldsDisplayedAsOptions(ruleConditionFields.filter((field) => field.name !== 'tag')); + await expectConditionFieldsDisplayedAsOptions(ruleConditionFields.filter((field) => field.name !== 'tag')); }); - it('should display correct condition field options when categoryService.areCategoriesEnabled returns true', () => { + it('should display correct condition field options when categoryService.areCategoriesEnabled returns true', async () => { spyOn(categoryService, 'areCategoriesEnabled').and.returnValue(true); fixture = TestBed.createComponent(RuleSimpleConditionUiComponent); expect(categoryService.areCategoriesEnabled).toHaveBeenCalled(); - expectConditionFieldsDisplayedAsOptions(ruleConditionFields); + await expectConditionFieldsDisplayedAsOptions(ruleConditionFields); }); - it('should display correct condition field options when categoryService.areCategoriesEnabled returns false', () => { + it('should display correct condition field options when categoryService.areCategoriesEnabled returns false', async () => { spyOn(categoryService, 'areCategoriesEnabled').and.returnValue(false); fixture = TestBed.createComponent(RuleSimpleConditionUiComponent); expect(categoryService.areCategoriesEnabled).toHaveBeenCalled(); - expectConditionFieldsDisplayedAsOptions(ruleConditionFields.filter((field) => field.name !== 'category')); + await expectConditionFieldsDisplayedAsOptions(ruleConditionFields.filter((field) => field.name !== 'category')); }); }); diff --git a/projects/aca-content/folder-rules/src/rule-details/options/rule-options.ui-component.spec.ts b/projects/aca-content/folder-rules/src/rule-details/options/rule-options.ui-component.spec.ts index 42e3eec34..2392036b1 100644 --- a/projects/aca-content/folder-rules/src/rule-details/options/rule-options.ui-component.spec.ts +++ b/projects/aca-content/folder-rules/src/rule-details/options/rule-options.ui-component.spec.ts @@ -30,10 +30,14 @@ import { CoreTestingModule } from '@alfresco/adf-core'; import { By } from '@angular/platform-browser'; import { errorScriptConstraintMock } from '../../mock/actions.mock'; import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { MatSelectHarness } from '@angular/material/select/testing'; describe('RuleOptionsUiComponent', () => { let fixture: ComponentFixture; let component: RuleOptionsUiComponent; + let loader: HarnessLoader; const getByDataAutomationId = (dataAutomationId: string): DebugElement => fixture.debugElement.query(By.css(`[data-automation-id="${dataAutomationId}"]`)); @@ -63,6 +67,7 @@ describe('RuleOptionsUiComponent', () => { fixture = TestBed.createComponent(RuleOptionsUiComponent); component = fixture.componentInstance; + loader = TestbedHarnessEnvironment.loader(fixture); component.writeValue({ isEnabled: true, @@ -126,7 +131,7 @@ describe('RuleOptionsUiComponent', () => { expect(getByDataAutomationId('rule-option-select-errorScript')).toBeTruthy(); }); - it('should populate the error script dropdown with scripts', () => { + it('should populate the error script dropdown with scripts', async () => { component.writeValue({ isEnabled: true, isInheritable: false, @@ -140,11 +145,12 @@ describe('RuleOptionsUiComponent', () => { (getByDataAutomationId('rule-option-select-errorScript').nativeElement as HTMLElement).click(); fixture.detectChanges(); - const matOptions = fixture.debugElement.queryAll(By.css(`.mat-option`)); + const selection = await loader.getHarness(MatSelectHarness); + const matOptions = await selection.getOptions(); expect(matOptions.length).toBe(3); - expect((matOptions[0].nativeElement as HTMLElement).innerText.trim()).toBe('ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.NO_SCRIPT'); - expect((matOptions[1].nativeElement as HTMLElement).innerText.trim()).toBe('Script 1'); - expect((matOptions[2].nativeElement as HTMLElement).innerText.trim()).toBe('Script 2'); + expect((await matOptions[0].getText()).trim()).toBe('ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.NO_SCRIPT'); + expect((await matOptions[1].getText()).trim()).toBe('Script 1'); + expect((await matOptions[2].getText()).trim()).toBe('Script 2'); }); it('should always show a label for the error script dropdown even when MAT_FORM_FIELD_DEFAULT_OPTIONS sets floatLabel to never', () => { @@ -157,7 +163,7 @@ describe('RuleOptionsUiComponent', () => { component.errorScriptConstraint = errorScriptConstraintMock; fixture.detectChanges(); - const matFormField = fixture.debugElement.query(By.css(`[data-automation-id="rule-option-form-field-errorScript"] .mat-form-field-label`)); + const matFormField = fixture.debugElement.query(By.css('[data-automation-id="rule-option-form-field-errorScript"')); fixture.detectChanges(); expect(matFormField).not.toBeNull(); expect(matFormField.componentInstance['floatLabel']).toBe('always'); diff --git a/projects/aca-content/folder-rules/src/services/folder-rule-sets.service.spec.ts b/projects/aca-content/folder-rules/src/services/folder-rule-sets.service.spec.ts index 198cd976c..67411f36f 100644 --- a/projects/aca-content/folder-rules/src/services/folder-rule-sets.service.spec.ts +++ b/projects/aca-content/folder-rules/src/services/folder-rule-sets.service.spec.ts @@ -56,7 +56,8 @@ describe('FolderRuleSetsService', () => { .withArgs(`/nodes/${owningFolderIdMock}/rule-sets/-default-?include=isLinkedTo,owningFolder,linkedToBy`, 'GET') .and.returnValue(of(getDefaultRuleSetResponseMock)) .withArgs(`/nodes/${owningFolderIdMock}/rule-sets?include=isLinkedTo,owningFolder,linkedToBy&skipCount=0&maxItems=100`, 'GET') - .and.returnValue(of(getRuleSetsResponseMock)); + .and.returnValue(of(getRuleSetsResponseMock)) + .and.stub(); getRulesSpy = spyOn(folderRulesService, 'getRules') .withArgs(jasmine.anything(), 'rule-set-no-links') .and.returnValue(of({ rules: ownedRulesMock, hasMoreRules: false })) @@ -138,15 +139,15 @@ describe('FolderRuleSetsService', () => { expect(selectRuleSpy).toHaveBeenCalledWith(ruleMock('inherited-rule-1')); }); - it('should send a POST request to create a new link between two folders', () => { - folderRuleSetsService.createRuleSetLink('folder-1-id', 'folder-2-id'); + it('should send a POST request to create a new link between two folders', async () => { + await folderRuleSetsService.createRuleSetLink('folder-1-id', 'folder-2-id'); expect(callApiSpy).toHaveBeenCalledWith('/nodes/folder-1-id/rule-set-links', 'POST', { id: 'folder-2-id' }); }); - it('should send a DELETE request to delete a link between two folders', () => { - folderRuleSetsService.deleteRuleSetLink('folder-1-id', 'rule-set-1-id'); + it('should send a DELETE request to delete a link between two folders', async () => { + await folderRuleSetsService.deleteRuleSetLink('folder-1-id', 'rule-set-1-id'); expect(callApiSpy).toHaveBeenCalledWith('/nodes/folder-1-id/rule-set-links/rule-set-1-id', 'DELETE'); }); }); diff --git a/projects/aca-content/src/lib/components/toolbar/view-node/view-node.component.spec.ts b/projects/aca-content/src/lib/components/toolbar/view-node/view-node.component.spec.ts index 9c364f46b..2aa81ec53 100644 --- a/projects/aca-content/src/lib/components/toolbar/view-node/view-node.component.spec.ts +++ b/projects/aca-content/src/lib/components/toolbar/view-node/view-node.component.spec.ts @@ -29,10 +29,15 @@ import { ActivatedRoute, Router } from '@angular/router'; import { of } from 'rxjs'; import { ViewNodeAction } from '@alfresco/aca-shared/store'; import { AppTestingModule } from '../../../testing/app-testing.module'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { MatMenuItemHarness } from '@angular/material/menu/testing'; +import { MatButtonHarness } from '@angular/material/button/testing'; describe('ViewNodeComponent', () => { let component: ViewNodeComponent; let fixture; + let loader: HarnessLoader; const mockRouter = { url: 'some-url' }; @@ -63,30 +68,31 @@ describe('ViewNodeComponent', () => { fixture = TestBed.createComponent(ViewNodeComponent); component = fixture.componentInstance; + loader = TestbedHarnessEnvironment.loader(fixture); }); afterEach(() => { mockStore.dispatch.calls.reset(); }); - it('should render as a menu button', () => { + it('should render as a menu button', async () => { component.data = { menuButton: true }; - fixture.detectChanges(); + const menuItem = await loader.getHarness(MatMenuItemHarness); - expect(fixture.nativeElement.querySelector('.mat-menu-item')).not.toBe(null); + expect(menuItem).toBeDefined(); }); - it('should render as a icon button', () => { + it('should render as a icon button', async () => { component.data = { iconButton: true }; - fixture.detectChanges(); + const icon = await loader.getHarness(MatButtonHarness); - expect(fixture.nativeElement.querySelector('.mat-icon-button')).not.toBe(null); + expect(icon).toBeDefined(); }); it('should call ViewNodeAction onClick event', () => {