Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(chips): add test harness (#20028)
Adds test harnesses for the Material chips and the related components.
- Loading branch information
Showing
14 changed files
with
827 additions
and
0 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
load("//tools:defaults.bzl", "ng_test_library", "ng_web_test_suite", "ts_library") | ||
|
||
package(default_visibility = ["//visibility:public"]) | ||
|
||
ts_library( | ||
name = "testing", | ||
srcs = glob( | ||
["**/*.ts"], | ||
exclude = ["**/*.spec.ts"], | ||
), | ||
module_name = "@angular/material/chips/testing", | ||
deps = [ | ||
"//src/cdk/testing", | ||
], | ||
) | ||
|
||
filegroup( | ||
name = "source-files", | ||
srcs = glob(["**/*.ts"]), | ||
) | ||
|
||
ng_test_library( | ||
name = "harness_tests_lib", | ||
srcs = ["shared.spec.ts"], | ||
deps = [ | ||
":testing", | ||
"//src/cdk/testing", | ||
"//src/cdk/testing/private", | ||
"//src/cdk/testing/testbed", | ||
"//src/material/chips", | ||
"//src/material/form-field", | ||
"@npm//@angular/platform-browser", | ||
], | ||
) | ||
|
||
ng_test_library( | ||
name = "unit_tests_lib", | ||
srcs = glob( | ||
["**/*.spec.ts"], | ||
exclude = ["shared.spec.ts"], | ||
), | ||
deps = [ | ||
":harness_tests_lib", | ||
":testing", | ||
"//src/material/chips", | ||
], | ||
) | ||
|
||
ng_web_test_suite( | ||
name = "unit_tests", | ||
deps = [":unit_tests_lib"], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
import {BaseHarnessFilters} from '@angular/cdk/testing'; | ||
|
||
/** A set of criteria that can be used to filter a list of `MatChipHarness` instances. */ | ||
export interface ChipHarnessFilters extends BaseHarnessFilters { | ||
/** Only find instances whose text matches the given value. */ | ||
text?: string | RegExp; | ||
/** Only find chip instances whose selected state matches the given value. */ | ||
selected?: boolean; | ||
} | ||
|
||
/** A set of criteria that can be used to filter a list of `MatChipListHarness` instances. */ | ||
export interface ChipListHarnessFilters extends BaseHarnessFilters {} | ||
|
||
/** A set of criteria that can be used to filter a list of `MatChipListInputHarness` instances. */ | ||
export interface ChipInputHarnessFilters extends BaseHarnessFilters { | ||
/** Filters based on the value of the input. */ | ||
value?: string | RegExp; | ||
/** Filters based on the placeholder text of the input. */ | ||
placeholder?: string | RegExp; | ||
} | ||
|
||
/** A set of criteria that can be used to filter a list of `MatChipRemoveHarness` instances. */ | ||
export interface ChipRemoveHarnessFilters extends BaseHarnessFilters {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {ComponentHarness, HarnessPredicate, TestKey} from '@angular/cdk/testing'; | ||
import {ChipHarnessFilters, ChipRemoveHarnessFilters} from './chip-harness-filters'; | ||
import {MatChipRemoveHarness} from './chip-remove-harness'; | ||
|
||
/** Harness for interacting with a standard Angular Material chip in tests. */ | ||
export class MatChipHarness extends ComponentHarness { | ||
/** The selector for the host element of a `MatChip` instance. */ | ||
static hostSelector = '.mat-chip'; | ||
|
||
/** | ||
* Gets a `HarnessPredicate` that can be used to search for a `MatChipHarness` that meets | ||
* certain criteria. | ||
* @param options Options for filtering which chip instances are considered a match. | ||
* @return a `HarnessPredicate` configured with the given options. | ||
*/ | ||
static with(options: ChipHarnessFilters = {}): HarnessPredicate<MatChipHarness> { | ||
return new HarnessPredicate(MatChipHarness, options) | ||
.addOption('text', options.text, | ||
(harness, label) => HarnessPredicate.stringMatches(harness.getText(), label)) | ||
.addOption('selected', options.selected, | ||
async (harness, selected) => (await harness.isSelected()) === selected); | ||
} | ||
|
||
/** Gets the text of the chip. */ | ||
async getText(): Promise<string> { | ||
return (await this.host()).text(); | ||
} | ||
|
||
/** Whether the chip is selected. */ | ||
async isSelected(): Promise<boolean> { | ||
return (await this.host()).hasClass('mat-chip-selected'); | ||
} | ||
|
||
/** Whether the chip is disabled. */ | ||
async isDisabled(): Promise<boolean> { | ||
return (await this.host()).hasClass('mat-chip-disabled'); | ||
} | ||
|
||
/** Selects the given chip. Only applies if it's selectable. */ | ||
async select(): Promise<void> { | ||
if (!(await this.isSelected())) { | ||
await this.toggle(); | ||
} | ||
} | ||
|
||
/** Deselects the given chip. Only applies if it's selectable. */ | ||
async deselect(): Promise<void> { | ||
if (await this.isSelected()) { | ||
await this.toggle(); | ||
} | ||
} | ||
|
||
/** Toggles the selected state of the given chip. Only applies if it's selectable. */ | ||
async toggle(): Promise<void> { | ||
return (await this.host()).sendKeys(' '); | ||
} | ||
|
||
/** Removes the given chip. Only applies if it's removable. */ | ||
async remove(): Promise<void> { | ||
await (await this.host()).sendKeys(TestKey.DELETE); | ||
} | ||
|
||
/** | ||
* Gets the remove button inside of a chip. | ||
* @param filter Optionally filters which chips are included. | ||
*/ | ||
async getRemoveButton(filter: ChipRemoveHarnessFilters = {}): Promise<MatChipRemoveHarness> { | ||
return this.locatorFor(MatChipRemoveHarness.with(filter))(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {HarnessPredicate, ComponentHarness, TestKey} from '@angular/cdk/testing'; | ||
import {ChipInputHarnessFilters} from './chip-harness-filters'; | ||
|
||
/** Harness for interacting with a standard Material chip inputs in tests. */ | ||
export class MatChipInputHarness extends ComponentHarness { | ||
static hostSelector = '.mat-chip-input'; | ||
|
||
/** | ||
* Gets a `HarnessPredicate` that can be used to search for a `MatChipInputHarness` that meets | ||
* certain criteria. | ||
* @param options Options for filtering which input instances are considered a match. | ||
* @return a `HarnessPredicate` configured with the given options. | ||
*/ | ||
static with(options: ChipInputHarnessFilters = {}): HarnessPredicate<MatChipInputHarness> { | ||
return new HarnessPredicate(MatChipInputHarness, options) | ||
.addOption('value', options.value, async (harness, value) => { | ||
return (await harness.getValue()) === value; | ||
}) | ||
.addOption('placeholder', options.placeholder, async (harness, placeholder) => { | ||
return (await harness.getPlaceholder()) === placeholder; | ||
}); | ||
} | ||
|
||
/** Whether the input is disabled. */ | ||
async isDisabled(): Promise<boolean> { | ||
return (await this.host()).getProperty('disabled')!; | ||
} | ||
|
||
/** Whether the input is required. */ | ||
async isRequired(): Promise<boolean> { | ||
return (await this.host()).getProperty('required')!; | ||
} | ||
|
||
/** Gets the value of the input. */ | ||
async getValue(): Promise<string> { | ||
// The "value" property of the native input is never undefined. | ||
return (await (await this.host()).getProperty('value'))!; | ||
} | ||
|
||
/** Gets the placeholder of the input. */ | ||
async getPlaceholder(): Promise<string> { | ||
return (await (await this.host()).getProperty('placeholder')); | ||
} | ||
|
||
/** | ||
* Focuses the input and returns a promise that indicates when the | ||
* action is complete. | ||
*/ | ||
async focus(): Promise<void> { | ||
return (await this.host()).focus(); | ||
} | ||
|
||
/** | ||
* Blurs the input and returns a promise that indicates when the | ||
* action is complete. | ||
*/ | ||
async blur(): Promise<void> { | ||
return (await this.host()).blur(); | ||
} | ||
|
||
/** Whether the input is focused. */ | ||
async isFocused(): Promise<boolean> { | ||
return (await this.host()).isFocused(); | ||
} | ||
|
||
/** | ||
* Sets the value of the input. The value will be set by simulating | ||
* keypresses that correspond to the given value. | ||
*/ | ||
async setValue(newValue: string): Promise<void> { | ||
const inputEl = await this.host(); | ||
await inputEl.clear(); | ||
|
||
// We don't want to send keys for the value if the value is an empty | ||
// string in order to clear the value. Sending keys with an empty string | ||
// still results in unnecessary focus events. | ||
if (newValue) { | ||
await inputEl.sendKeys(newValue); | ||
} | ||
} | ||
|
||
/** Sends a chip separator key to the input element. */ | ||
async sendSeparatorKey(key: TestKey | string): Promise<void> { | ||
const inputEl = await this.host(); | ||
return inputEl.sendKeys(key); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import {MatChipsModule} from '@angular/material/chips'; | ||
import {runHarnessTests} from '@angular/material/chips/testing/shared.spec'; | ||
import {MatChipListHarness} from './chip-list-harness'; | ||
import {MatChipHarness} from './chip-harness'; | ||
import {MatChipInputHarness} from './chip-input-harness'; | ||
import {MatChipRemoveHarness} from './chip-remove-harness'; | ||
|
||
describe('Non-MDC-based MatChipListHarness', () => { | ||
runHarnessTests(MatChipsModule, MatChipListHarness, MatChipHarness, MatChipInputHarness, | ||
MatChipRemoveHarness); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {ComponentHarness, HarnessPredicate} from '@angular/cdk/testing'; | ||
import {MatChipHarness} from './chip-harness'; | ||
import {MatChipInputHarness} from './chip-input-harness'; | ||
import { | ||
ChipListHarnessFilters, | ||
ChipHarnessFilters, | ||
ChipInputHarnessFilters, | ||
} from './chip-harness-filters'; | ||
|
||
/** Harness for interacting with a standard chip list in tests. */ | ||
export class MatChipListHarness extends ComponentHarness { | ||
/** The selector for the host element of a `MatChipList` instance. */ | ||
static hostSelector = '.mat-chip-list'; | ||
|
||
/** | ||
* Gets a `HarnessPredicate` that can be used to search for a `MatChipListHarness` that meets | ||
* certain criteria. | ||
* @param options Options for filtering which chip list instances are considered a match. | ||
* @return a `HarnessPredicate` configured with the given options. | ||
*/ | ||
static with(options: ChipListHarnessFilters = {}): HarnessPredicate<MatChipListHarness> { | ||
return new HarnessPredicate(MatChipListHarness, options); | ||
} | ||
|
||
/** Gets whether the chip list is disabled. */ | ||
async isDisabled(): Promise<boolean> { | ||
return await (await this.host()).getAttribute('aria-disabled') === 'true'; | ||
} | ||
|
||
/** Gets whether the chip list is required. */ | ||
async isRequired(): Promise<boolean> { | ||
return await (await this.host()).getAttribute('aria-required') === 'true'; | ||
} | ||
|
||
/** Gets whether the chip list is invalid. */ | ||
async isInvalid(): Promise<boolean> { | ||
return await (await this.host()).getAttribute('aria-invalid') === 'true'; | ||
} | ||
|
||
/** Gets whether the chip list is in multi selection mode. */ | ||
async isMultiple(): Promise<boolean> { | ||
return await (await this.host()).getAttribute('aria-multiselectable') === 'true'; | ||
} | ||
|
||
/** Gets whether the orientation of the chip list. */ | ||
async getOrientation(): Promise<'horizontal' | 'vertical'> { | ||
const orientation = await (await this.host()).getAttribute('aria-orientation'); | ||
return orientation === 'vertical' ? 'vertical' : 'horizontal'; | ||
} | ||
|
||
/** | ||
* Gets the list of chips inside the chip list. | ||
* @param filter Optionally filters which chips are included. | ||
*/ | ||
async getChips(filter: ChipHarnessFilters = {}): Promise<MatChipHarness[]> { | ||
return this.locatorForAll(MatChipHarness.with(filter))(); | ||
} | ||
|
||
/** | ||
* Selects a chip inside the chip list. | ||
* @param filter An optional filter to apply to the child chips. | ||
* All the chips matching the filter will be selected. | ||
*/ | ||
async selectChips(filter: ChipHarnessFilters = {}): Promise<void> { | ||
const chips = await this.getChips(filter); | ||
if (!chips.length) { | ||
throw Error(`Cannot find mat-chip matching filter ${JSON.stringify(filter)}`); | ||
} | ||
await Promise.all(chips.map(chip => chip.select())); | ||
} | ||
|
||
/** | ||
* Gets the `MatChipInput` inside the chip list. | ||
* @param filter Optionally filters which chip input is included. | ||
*/ | ||
async getInput(filter: ChipInputHarnessFilters = {}): Promise<MatChipInputHarness> { | ||
// The input isn't required to be a descendant of the chip list so we have to look it up by id. | ||
const inputId = await (await this.host()).getAttribute('data-mat-chip-input'); | ||
|
||
if (!inputId) { | ||
throw Error(`Chip list is not associated with an input`); | ||
} | ||
|
||
return this.documentRootLocatorFactory().locatorFor( | ||
MatChipInputHarness.with({...filter, selector: `#${inputId}`}))(); | ||
} | ||
} |
Oops, something went wrong.