Skip to content

Commit 5f2407e

Browse files
authored
feat(material/chips): add test harnesses for selectable chips (#20875)
Adds alternate test harnesses specifically for selectable chips and chips lists. Note that while these harnesses currently match the same elements as their non-selectable counterparts, they'll match different elements in the MDC chips module. This is a step towards consolidating the MDC and non-MDC harnesses for #20826.
1 parent dfdd422 commit 5f2407e

File tree

9 files changed

+186
-40
lines changed

9 files changed

+186
-40
lines changed

src/material/chips/testing/chip-harness-filters.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,30 @@
77
*/
88
import {BaseHarnessFilters} from '@angular/cdk/testing';
99

10-
/** A set of criteria that can be used to filter a list of `MatChipHarness` instances. */
10+
/** A set of criteria that can be used to filter a list of chip instances. */
1111
export interface ChipHarnessFilters extends BaseHarnessFilters {
1212
/** Only find instances whose text matches the given value. */
1313
text?: string | RegExp;
1414
/**
1515
* Only find chip instances whose selected state matches the given value.
16-
* @deprecated Will be moved into separate selection-specific harness.
16+
* @deprecated Use `MatChipOptionHarness` together with `ChipOptionHarnessFilters`.
1717
* @breaking-change 12.0.0
1818
*/
1919
selected?: boolean;
2020
}
2121

22-
/** A set of criteria that can be used to filter a list of `MatChipListHarness` instances. */
22+
/** A set of criteria that can be used to filter a list of selectable chip instances. */
23+
export interface ChipOptionHarnessFilters extends ChipHarnessFilters {
24+
/** Only find chip instances whose selected state matches the given value. */
25+
selected?: boolean;
26+
}
27+
28+
/** A set of criteria that can be used to filter chip list instances. */
2329
export interface ChipListHarnessFilters extends BaseHarnessFilters {}
2430

31+
/** A set of criteria that can be used to filter selectable chip list instances. */
32+
export interface ChipListboxHarnessFilters extends BaseHarnessFilters {}
33+
2534
/** A set of criteria that can be used to filter a list of `MatChipListInputHarness` instances. */
2635
export interface ChipInputHarnessFilters extends BaseHarnessFilters {
2736
/** Filters based on the value of the input. */

src/material/chips/testing/chip-harness.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {ComponentHarness, HarnessPredicate, TestKey} from '@angular/cdk/testing'
1010
import {ChipHarnessFilters, ChipRemoveHarnessFilters} from './chip-harness-filters';
1111
import {MatChipRemoveHarness} from './chip-remove-harness';
1212

13-
/** Harness for interacting with a standard Angular Material chip in tests. */
13+
/** Harness for interacting with a standard selectable Angular Material chip in tests. */
1414
export class MatChipHarness extends ComponentHarness {
1515
/** The selector for the host element of a `MatChip` instance. */
1616
static hostSelector = '.mat-chip';
@@ -38,7 +38,7 @@ export class MatChipHarness extends ComponentHarness {
3838

3939
/**
4040
* Whether the chip is selected.
41-
* @deprecated Will be moved into separate selection-specific harness.
41+
* @deprecated Use `MatChipOptionHarness.isSelected` instead.
4242
* @breaking-change 12.0.0
4343
*/
4444
async isSelected(): Promise<boolean> {
@@ -52,7 +52,7 @@ export class MatChipHarness extends ComponentHarness {
5252

5353
/**
5454
* Selects the given chip. Only applies if it's selectable.
55-
* @deprecated Will be moved into separate selection-specific harness.
55+
* @deprecated Use `MatChipOptionHarness.select` instead.
5656
* @breaking-change 12.0.0
5757
*/
5858
async select(): Promise<void> {
@@ -63,7 +63,7 @@ export class MatChipHarness extends ComponentHarness {
6363

6464
/**
6565
* Deselects the given chip. Only applies if it's selectable.
66-
* @deprecated Will be moved into separate selection-specific harness.
66+
* @deprecated Use `MatChipOptionHarness.deselect` instead.
6767
* @breaking-change 12.0.0
6868
*/
6969
async deselect(): Promise<void> {
@@ -74,7 +74,7 @@ export class MatChipHarness extends ComponentHarness {
7474

7575
/**
7676
* Toggles the selected state of the given chip. Only applies if it's selectable.
77-
* @deprecated Will be moved into separate selection-specific harness.
77+
* @deprecated Use `MatChipOptionHarness.toggle` instead.
7878
* @breaking-change 12.0.0
7979
*/
8080
async toggle(): Promise<void> {

src/material/chips/testing/chip-list-harness.spec.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import {MatChipListHarness} from './chip-list-harness';
44
import {MatChipHarness} from './chip-harness';
55
import {MatChipInputHarness} from './chip-input-harness';
66
import {MatChipRemoveHarness} from './chip-remove-harness';
7+
import {MatChipListboxHarness} from './chip-listbox-harness';
8+
import {MatChipOptionHarness} from './chip-option-harness';
79

810
describe('Non-MDC-based MatChipListHarness', () => {
9-
runHarnessTests(MatChipsModule, MatChipListHarness, MatChipHarness, MatChipInputHarness,
10-
MatChipRemoveHarness);
11+
runHarnessTests(MatChipsModule, MatChipListHarness, MatChipListboxHarness, MatChipHarness,
12+
MatChipOptionHarness, MatChipInputHarness, MatChipRemoveHarness);
1113
});

src/material/chips/testing/chip-list-harness.ts

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,8 @@ import {
1515
ChipInputHarnessFilters,
1616
} from './chip-harness-filters';
1717

18-
/** Harness for interacting with a standard chip list in tests. */
19-
export class MatChipListHarness extends ComponentHarness {
20-
/** The selector for the host element of a `MatChipList` instance. */
21-
static hostSelector = '.mat-chip-list';
22-
23-
/**
24-
* Gets a `HarnessPredicate` that can be used to search for a `MatChipListHarness` that meets
25-
* certain criteria.
26-
* @param options Options for filtering which chip list instances are considered a match.
27-
* @return a `HarnessPredicate` configured with the given options.
28-
*/
29-
static with(options: ChipListHarnessFilters = {}): HarnessPredicate<MatChipListHarness> {
30-
return new HarnessPredicate(MatChipListHarness, options);
31-
}
32-
18+
/** Base class for chip list harnesses. */
19+
export abstract class _MatChipListHarnessBase extends ComponentHarness {
3320
/** Gets whether the chip list is disabled. */
3421
async isDisabled(): Promise<boolean> {
3522
return await (await this.host()).getAttribute('aria-disabled') === 'true';
@@ -55,6 +42,22 @@ export class MatChipListHarness extends ComponentHarness {
5542
const orientation = await (await this.host()).getAttribute('aria-orientation');
5643
return orientation === 'vertical' ? 'vertical' : 'horizontal';
5744
}
45+
}
46+
47+
/** Harness for interacting with a standard chip list in tests. */
48+
export class MatChipListHarness extends _MatChipListHarnessBase {
49+
/** The selector for the host element of a `MatChipList` instance. */
50+
static hostSelector = '.mat-chip-list';
51+
52+
/**
53+
* Gets a `HarnessPredicate` that can be used to search for a `MatChipListHarness` that meets
54+
* certain criteria.
55+
* @param options Options for filtering which chip list instances are considered a match.
56+
* @return a `HarnessPredicate` configured with the given options.
57+
*/
58+
static with(options: ChipListHarnessFilters = {}): HarnessPredicate<MatChipListHarness> {
59+
return new HarnessPredicate(MatChipListHarness, options);
60+
}
5861

5962
/**
6063
* Gets the list of chips inside the chip list.
@@ -68,13 +71,13 @@ export class MatChipListHarness extends ComponentHarness {
6871
* Selects a chip inside the chip list.
6972
* @param filter An optional filter to apply to the child chips.
7073
* All the chips matching the filter will be selected.
71-
* @deprecated Will be moved into separate selection-specific harness.
74+
* @deprecated Use `MatChipListboxHarness.selectChips` instead.
7275
* @breaking-change 12.0.0
7376
*/
7477
async selectChips(filter: ChipHarnessFilters = {}): Promise<void> {
7578
const chips = await this.getChips(filter);
7679
if (!chips.length) {
77-
throw Error(`Cannot find mat-chip matching filter ${JSON.stringify(filter)}`);
80+
throw Error(`Cannot find chip matching filter ${JSON.stringify(filter)}`);
7881
}
7982
await Promise.all(chips.map(chip => chip.select()));
8083
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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} from '@angular/cdk/testing';
10+
import {MatChipOptionHarness} from './chip-option-harness';
11+
import {
12+
ChipListboxHarnessFilters,
13+
ChipOptionHarnessFilters,
14+
} from './chip-harness-filters';
15+
import {_MatChipListHarnessBase} from './chip-list-harness';
16+
17+
/** Harness for interacting with a standard selectable chip list in tests. */
18+
export class MatChipListboxHarness extends _MatChipListHarnessBase {
19+
/** The selector for the host element of a `MatChipList` instance. */
20+
static hostSelector = '.mat-chip-list';
21+
22+
/**
23+
* Gets a `HarnessPredicate` that can be used to search for a `MatChipListHarness` that meets
24+
* certain criteria.
25+
* @param options Options for filtering which chip list instances are considered a match.
26+
* @return a `HarnessPredicate` configured with the given options.
27+
*/
28+
static with(options: ChipListboxHarnessFilters = {}):
29+
HarnessPredicate<MatChipListboxHarness> {
30+
return new HarnessPredicate(MatChipListboxHarness, options);
31+
}
32+
33+
/**
34+
* Gets the list of chips inside the chip list.
35+
* @param filter Optionally filters which chips are included.
36+
*/
37+
async getChips(filter: ChipOptionHarnessFilters = {}): Promise<MatChipOptionHarness[]> {
38+
return this.locatorForAll(MatChipOptionHarness.with(filter))();
39+
}
40+
41+
/**
42+
* Selects a chip inside the chip list.
43+
* @param filter An optional filter to apply to the child chips.
44+
* All the chips matching the filter will be selected.
45+
*/
46+
async selectChips(filter: ChipOptionHarnessFilters = {}): Promise<void> {
47+
const chips = await this.getChips(filter);
48+
if (!chips.length) {
49+
throw Error(`Cannot find chip matching filter ${JSON.stringify(filter)}`);
50+
}
51+
await Promise.all(chips.map(chip => chip.select()));
52+
}
53+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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} from '@angular/cdk/testing';
10+
import {MatChipHarness} from './chip-harness';
11+
import {ChipOptionHarnessFilters} from './chip-harness-filters';
12+
13+
export class MatChipOptionHarness extends MatChipHarness {
14+
/** The selector for the host element of a selectable chip instance. */
15+
static hostSelector = '.mat-chip';
16+
17+
/**
18+
* Gets a `HarnessPredicate` that can be used to search for a `MatChipOptionHarness`
19+
* that meets certain criteria.
20+
* @param options Options for filtering which chip instances are considered a match.
21+
* @return a `HarnessPredicate` configured with the given options.
22+
*/
23+
static with(options: ChipOptionHarnessFilters = {}):
24+
HarnessPredicate<MatChipOptionHarness> {
25+
return new HarnessPredicate(MatChipOptionHarness, options)
26+
.addOption('text', options.text,
27+
(harness, label) => HarnessPredicate.stringMatches(harness.getText(), label))
28+
.addOption('selected', options.selected,
29+
async (harness, selected) => (await harness.isSelected()) === selected);
30+
}
31+
32+
/** Whether the chip is selected. */
33+
async isSelected(): Promise<boolean> {
34+
return (await this.host()).hasClass('mat-chip-selected');
35+
}
36+
37+
/** Selects the given chip. Only applies if it's selectable. */
38+
async select(): Promise<void> {
39+
if (!(await this.isSelected())) {
40+
await this.toggle();
41+
}
42+
}
43+
44+
/** Deselects the given chip. Only applies if it's selectable. */
45+
async deselect(): Promise<void> {
46+
if (await this.isSelected()) {
47+
await this.toggle();
48+
}
49+
}
50+
51+
/** Toggles the selected state of the given chip. */
52+
async toggle(): Promise<void> {
53+
return (await this.host()).sendKeys(' ');
54+
}
55+
}

src/material/chips/testing/public-api.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
export * from './chip-harness';
1010
export * from './chip-harness-filters';
11-
export * from './chip-list-harness';
11+
export {MatChipListHarness} from './chip-list-harness';
1212
export * from './chip-input-harness';
1313
export * from './chip-remove-harness';
14+
export * from './chip-option-harness';
15+
export * from './chip-listbox-harness';

src/material/chips/testing/shared.spec.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,16 @@ import {MatChipListHarness} from './chip-list-harness';
99
import {MatChipHarness} from './chip-harness';
1010
import {MatChipInputHarness} from './chip-input-harness';
1111
import {MatChipRemoveHarness} from './chip-remove-harness';
12+
import {MatChipOptionHarness} from './chip-option-harness';
13+
import {MatChipListboxHarness} from './chip-listbox-harness';
1214

1315
/** Shared tests to run on both the original and MDC-based chips. */
1416
export function runHarnessTests(
1517
chipsModule: typeof MatChipsModule,
1618
chipListHarness: typeof MatChipListHarness,
19+
listboxHarness: typeof MatChipListboxHarness,
1720
chipHarness: typeof MatChipHarness,
21+
chipOptionHarness: typeof MatChipOptionHarness,
1822
chipInputHarness: typeof MatChipInputHarness,
1923
chipRemoveHarness: typeof MatChipRemoveHarness) {
2024
let fixture: ComponentFixture<ChipsHarnessTest>;
@@ -124,7 +128,7 @@ export function runHarnessTests(
124128
});
125129

126130
it('should be able to get the selected chips in a list', async () => {
127-
const chipList = await loader.getHarness(chipListHarness);
131+
const chipList = await loader.getHarness(listboxHarness);
128132
const chips = await chipList.getChips();
129133

130134
expect((await chipList.getChips({selected: true})).length).toBe(0);
@@ -135,7 +139,7 @@ export function runHarnessTests(
135139
});
136140

137141
it('should be able to select chips based on a filter', async () => {
138-
const chipList = await loader.getHarness(chipListHarness);
142+
const chipList = await loader.getHarness(listboxHarness);
139143
fixture.componentInstance.isMultiple = true;
140144

141145
expect((await chipList.getChips({selected: true})).length).toBe(0);
@@ -166,22 +170,22 @@ export function runHarnessTests(
166170
});
167171

168172
it('should be able to select a chip', async () => {
169-
const chip = await loader.getHarness(chipHarness);
173+
const chip = await loader.getHarness(chipOptionHarness);
170174
expect(await chip.isSelected()).toBe(false);
171175
await chip.select();
172176
expect(await chip.isSelected()).toBe(true);
173177
});
174178

175179
it('should be able to deselect a chip', async () => {
176-
const chip = await loader.getHarness(chipHarness);
180+
const chip = await loader.getHarness(chipOptionHarness);
177181
await chip.select();
178182
expect(await chip.isSelected()).toBe(true);
179183
await chip.deselect();
180184
expect(await chip.isSelected()).toBe(false);
181185
});
182186

183187
it('should be able to toggle the selected state of a chip', async () => {
184-
const chip = await loader.getHarness(chipHarness);
188+
const chip = await loader.getHarness(chipOptionHarness);
185189
expect(await chip.isSelected()).toBe(false);
186190
await chip.toggle();
187191
expect(await chip.isSelected()).toBe(true);
@@ -202,7 +206,7 @@ export function runHarnessTests(
202206
});
203207

204208
it('should get the selected text of a chip', async () => {
205-
const chips = await loader.getAllHarnesses(chipHarness);
209+
const chips = await loader.getAllHarnesses(chipOptionHarness);
206210
expect(await Promise.all(chips.map(chip => chip.isSelected()))).toEqual([
207211
false,
208212
false,

tools/public_api_guard/material/chips/testing.d.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,16 @@ export interface ChipInputHarnessFilters extends BaseHarnessFilters {
88
value?: string | RegExp;
99
}
1010

11+
export interface ChipListboxHarnessFilters extends BaseHarnessFilters {
12+
}
13+
1114
export interface ChipListHarnessFilters extends BaseHarnessFilters {
1215
}
1316

17+
export interface ChipOptionHarnessFilters extends ChipHarnessFilters {
18+
selected?: boolean;
19+
}
20+
1421
export interface ChipRemoveHarnessFilters extends BaseHarnessFilters {
1522
}
1623

@@ -41,19 +48,30 @@ export declare class MatChipInputHarness extends ComponentHarness {
4148
static with(options?: ChipInputHarnessFilters): HarnessPredicate<MatChipInputHarness>;
4249
}
4350

44-
export declare class MatChipListHarness extends ComponentHarness {
51+
export declare class MatChipListboxHarness extends _MatChipListHarnessBase {
52+
getChips(filter?: ChipOptionHarnessFilters): Promise<MatChipOptionHarness[]>;
53+
selectChips(filter?: ChipOptionHarnessFilters): Promise<void>;
54+
static hostSelector: string;
55+
static with(options?: ChipListboxHarnessFilters): HarnessPredicate<MatChipListboxHarness>;
56+
}
57+
58+
export declare class MatChipListHarness extends _MatChipListHarnessBase {
4559
getChips(filter?: ChipHarnessFilters): Promise<MatChipHarness[]>;
4660
getInput(filter?: ChipInputHarnessFilters): Promise<MatChipInputHarness>;
47-
getOrientation(): Promise<'horizontal' | 'vertical'>;
48-
isDisabled(): Promise<boolean>;
49-
isInvalid(): Promise<boolean>;
50-
isMultiple(): Promise<boolean>;
51-
isRequired(): Promise<boolean>;
5261
selectChips(filter?: ChipHarnessFilters): Promise<void>;
5362
static hostSelector: string;
5463
static with(options?: ChipListHarnessFilters): HarnessPredicate<MatChipListHarness>;
5564
}
5665

66+
export declare class MatChipOptionHarness extends MatChipHarness {
67+
deselect(): Promise<void>;
68+
isSelected(): Promise<boolean>;
69+
select(): Promise<void>;
70+
toggle(): Promise<void>;
71+
static hostSelector: string;
72+
static with(options?: ChipOptionHarnessFilters): HarnessPredicate<MatChipOptionHarness>;
73+
}
74+
5775
export declare class MatChipRemoveHarness extends ComponentHarness {
5876
click(): Promise<void>;
5977
static hostSelector: string;

0 commit comments

Comments
 (0)