Skip to content

Commit f2ae6a8

Browse files
committed
feat(chips): add test harness
Adds test harnesses for the Material chips and the related components.
1 parent 0d43581 commit f2ae6a8

File tree

17 files changed

+844
-3
lines changed

17 files changed

+844
-3
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@
290290
/tools/public_api_guard/material/card.d.ts @jelbourn
291291
/tools/public_api_guard/material/checkbox.d.ts @jelbourn @devversion
292292
/tools/public_api_guard/material/chips.d.ts @jelbourn
293+
/tools/public_api_guard/material/chips/testing.d.ts @jelbourn
293294
/tools/public_api_guard/material/core.d.ts @jelbourn
294295
/tools/public_api_guard/material/datepicker.d.ts @mmalerba
295296
/tools/public_api_guard/material/dialog.d.ts @jelbourn @crisbeto

src/cdk/testing/protractor/protractor-element.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ const keyMap = {
4040
[TestKey.F10]: Key.F10,
4141
[TestKey.F11]: Key.F11,
4242
[TestKey.F12]: Key.F12,
43-
[TestKey.META]: Key.META
43+
[TestKey.META]: Key.META,
44+
[TestKey.SPACE]: Key.SPACE
4445
};
4546

4647
/** Converts a `ModifierKeys` object to a list of Protractor `Key`s. */

src/cdk/testing/test-element.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ export enum TestKey {
5353
F10,
5454
F11,
5555
F12,
56-
META
56+
META,
57+
SPACE,
5758
}
5859

5960
/**

src/cdk/testing/testbed/unit-test-element.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ const keyMap = {
4949
[TestKey.F10]: {keyCode: keyCodes.F10, key: 'F10'},
5050
[TestKey.F11]: {keyCode: keyCodes.F11, key: 'F11'},
5151
[TestKey.F12]: {keyCode: keyCodes.F12, key: 'F12'},
52-
[TestKey.META]: {keyCode: keyCodes.META, key: 'Meta'}
52+
[TestKey.META]: {keyCode: keyCodes.META, key: 'Meta'},
53+
[TestKey.SPACE]: {keyCode: keyCodes.SPACE, key: 'Space'},
5354
};
5455

5556
/** A `TestElement` implementation for unit tests. */

src/material/chips/chip-list.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,10 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
418418
/** Associates an HTML input element with this chip list. */
419419
registerInput(inputElement: MatChipTextControl): void {
420420
this._chipInput = inputElement;
421+
422+
// We use this attribute to match the chip list to its input in test harnesses.
423+
// Set the attribute directly here to avoid "changed after checked" errors.
424+
this._elementRef.nativeElement.setAttribute('data-mat-chip-input', inputElement.id);
421425
}
422426

423427
/**
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
load("//tools:defaults.bzl", "ng_test_library", "ng_web_test_suite", "ts_library")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
ts_library(
6+
name = "testing",
7+
srcs = glob(
8+
["**/*.ts"],
9+
exclude = ["**/*.spec.ts"],
10+
),
11+
module_name = "@angular/material/chips/testing",
12+
deps = [
13+
"//src/cdk/testing",
14+
],
15+
)
16+
17+
filegroup(
18+
name = "source-files",
19+
srcs = glob(["**/*.ts"]),
20+
)
21+
22+
ng_test_library(
23+
name = "harness_tests_lib",
24+
srcs = ["shared.spec.ts"],
25+
deps = [
26+
":testing",
27+
"//src/cdk/testing",
28+
"//src/cdk/testing/private",
29+
"//src/cdk/testing/testbed",
30+
"//src/material/chips",
31+
"//src/material/form-field",
32+
"@npm//@angular/platform-browser",
33+
],
34+
)
35+
36+
ng_test_library(
37+
name = "unit_tests_lib",
38+
srcs = glob(
39+
["**/*.spec.ts"],
40+
exclude = ["shared.spec.ts"],
41+
),
42+
deps = [
43+
":harness_tests_lib",
44+
":testing",
45+
"//src/material/chips",
46+
],
47+
)
48+
49+
ng_web_test_suite(
50+
name = "unit_tests",
51+
deps = [":unit_tests_lib"],
52+
)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import {BaseHarnessFilters} from '@angular/cdk/testing';
9+
10+
/** A set of criteria that can be used to filter a list of `MatChipHarness` instances. */
11+
export interface ChipHarnessFilters extends BaseHarnessFilters {
12+
/** Only find instances whose text matches the given value. */
13+
text?: string | RegExp;
14+
}
15+
16+
/** A set of criteria that can be used to filter a list of `MatChipListHarness` instances. */
17+
export interface ChipListHarnessFilters extends BaseHarnessFilters {}
18+
19+
/** A set of criteria that can be used to filter a list of `MatChipListInputHarness` instances. */
20+
export interface ChipInputHarnessFilters extends BaseHarnessFilters {
21+
/** Filters based on the value of the input. */
22+
value?: string | RegExp;
23+
/** Filters based on the placeholder text of the input. */
24+
placeholder?: string | RegExp;
25+
}
26+
27+
/** A set of criteria that can be used to filter a list of `MatChipRemoveHarness` instances. */
28+
export interface ChipRemoveHarnessFilters extends BaseHarnessFilters {}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {ComponentHarness, HarnessPredicate, TestKey} from '@angular/cdk/testing';
10+
import {ChipHarnessFilters, ChipRemoveHarnessFilters} from './chip-harness-filters';
11+
import {MatChipRemoveHarness} from './chip-remove-harness';
12+
13+
/** Harness for interacting with a standard Angular Material chip in tests. */
14+
export class MatChipHarness extends ComponentHarness {
15+
/** The selector for the host element of a `MatChip` instance. */
16+
static hostSelector = '.mat-chip';
17+
18+
/**
19+
* Gets a `HarnessPredicate` that can be used to search for a `MatChipHarness` that meets
20+
* certain criteria.
21+
* @param options Options for filtering which chip instances are considered a match.
22+
* @return a `HarnessPredicate` configured with the given options.
23+
*/
24+
static with(options: ChipHarnessFilters = {}): HarnessPredicate<MatChipHarness> {
25+
return new HarnessPredicate(MatChipHarness, options)
26+
.addOption('text', options.text,
27+
(harness, label) => HarnessPredicate.stringMatches(harness.getText(), label));
28+
}
29+
30+
/** Gets the text of the chip. */
31+
async getText(): Promise<string> {
32+
return (await this.host()).text();
33+
}
34+
35+
/** Whether the chip is selected. */
36+
async isSelected(): Promise<boolean> {
37+
return (await this.host()).hasClass('mat-chip-selected');
38+
}
39+
40+
/** Whether the chip is disabled. */
41+
async isDisabled(): Promise<boolean> {
42+
return (await this.host()).hasClass('mat-chip-disabled');
43+
}
44+
45+
/** Selects the given chip. Only applies if it's selectable. */
46+
async select(): Promise<void> {
47+
if (!(await this.isSelected())) {
48+
await this.toggle();
49+
}
50+
}
51+
52+
/** Deselects the given chip. Only applies if it's selectable. */
53+
async deselect(): Promise<void> {
54+
if (await this.isSelected()) {
55+
await this.toggle();
56+
}
57+
}
58+
59+
/** Toggles the selected state of the given chip. Only applies if it's selectable. */
60+
async toggle(): Promise<void> {
61+
return (await this.host()).sendKeys(TestKey.SPACE);
62+
}
63+
64+
/** Removes the given chip. Only applies if it's removable. */
65+
async remove(): Promise<void> {
66+
await (await this.host()).sendKeys(TestKey.DELETE);
67+
}
68+
69+
/**
70+
* Gets the remove button inside of a chip.
71+
* @param filter Optionally filters which chips are included.
72+
*/
73+
async getRemoveButton(filter: ChipRemoveHarnessFilters = {}): Promise<MatChipRemoveHarness> {
74+
return this.locatorFor(MatChipRemoveHarness.with(filter))();
75+
}
76+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {HarnessPredicate, ComponentHarness, TestKey} from '@angular/cdk/testing';
10+
import {ChipInputHarnessFilters} from './chip-harness-filters';
11+
12+
/** Harness for interacting with a standard Material chip inputs in tests. */
13+
export class MatChipInputHarness extends ComponentHarness {
14+
static hostSelector = '.mat-chip-input';
15+
16+
/**
17+
* Gets a `HarnessPredicate` that can be used to search for a `MatChipInputHarness` that meets
18+
* certain criteria.
19+
* @param options Options for filtering which input instances are considered a match.
20+
* @return a `HarnessPredicate` configured with the given options.
21+
*/
22+
static with(options: ChipInputHarnessFilters = {}): HarnessPredicate<MatChipInputHarness> {
23+
return new HarnessPredicate(MatChipInputHarness, options)
24+
.addOption('value', options.value, async (harness, value) => {
25+
return (await harness.getValue()) === value;
26+
})
27+
.addOption('placeholder', options.placeholder, async (harness, placeholder) => {
28+
return (await harness.getPlaceholder()) === placeholder;
29+
});
30+
}
31+
32+
/** Whether the input is disabled. */
33+
async isDisabled(): Promise<boolean> {
34+
return (await this.host()).getProperty('disabled')!;
35+
}
36+
37+
/** Whether the input is required. */
38+
async isRequired(): Promise<boolean> {
39+
return (await this.host()).getProperty('required')!;
40+
}
41+
42+
/** Gets the value of the input. */
43+
async getValue(): Promise<string> {
44+
// The "value" property of the native input is never undefined.
45+
return (await (await this.host()).getProperty('value'))!;
46+
}
47+
48+
/** Gets the placeholder of the input. */
49+
async getPlaceholder(): Promise<string> {
50+
return (await (await this.host()).getProperty('placeholder'));
51+
}
52+
53+
/**
54+
* Focuses the input and returns a promise that indicates when the
55+
* action is complete.
56+
*/
57+
async focus(): Promise<void> {
58+
return (await this.host()).focus();
59+
}
60+
61+
/**
62+
* Blurs the input and returns a promise that indicates when the
63+
* action is complete.
64+
*/
65+
async blur(): Promise<void> {
66+
return (await this.host()).blur();
67+
}
68+
69+
/** Whether the input is focused. */
70+
async isFocused(): Promise<boolean> {
71+
return (await this.host()).isFocused();
72+
}
73+
74+
/**
75+
* Sets the value of the input. The value will be set by simulating
76+
* keypresses that correspond to the given value.
77+
*/
78+
async setValue(newValue: string): Promise<void> {
79+
const inputEl = await this.host();
80+
await inputEl.clear();
81+
82+
// We don't want to send keys for the value if the value is an empty
83+
// string in order to clear the value. Sending keys with an empty string
84+
// still results in unnecessary focus events.
85+
if (newValue) {
86+
await inputEl.sendKeys(newValue);
87+
}
88+
}
89+
90+
/** Sends a chip separator key to the input element. */
91+
async sendSeparatorKey(key: TestKey | string): Promise<void> {
92+
const inputEl = await this.host();
93+
return inputEl.sendKeys(key);
94+
}
95+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {MatChipsModule} from '@angular/material/chips';
2+
import {runHarnessTests} from '@angular/material/chips/testing/shared.spec';
3+
import {MatChipListHarness} from './chip-list-harness';
4+
import {MatChipHarness} from './chip-harness';
5+
import {MatChipInputHarness} from './chip-input-harness';
6+
import {MatChipRemoveHarness} from './chip-remove-harness';
7+
8+
describe('Non-MDC-based MatChipListHarness', () => {
9+
runHarnessTests(MatChipsModule, MatChipListHarness, MatChipHarness, MatChipInputHarness,
10+
MatChipRemoveHarness);
11+
});

0 commit comments

Comments
 (0)