Skip to content

Commit d6dac1d

Browse files
committed
feat(material/testing): Modify HarnessLoader with the addition of
`getHarnessAtIndex` and `countHarnesses` These two new functions are intended to expand harness testing functionality by providing built-in functions for commonly used patterns. * `getHarnessAtIndex` functions similarly to `getHarness`, but returns a harness for the matching component instance with the given index. An example use case is to fetch the nth MatOptionHarness on screen. * `countHarnesses` simply counts the number of matching component instances and returns the result. Documentation is updated to reflect this changes, and adds a missing row for the `hasHarness` function Manually tested using the MatInputHarness tests
1 parent 9aa24d7 commit d6dac1d

File tree

4 files changed

+55
-3
lines changed

4 files changed

+55
-3
lines changed

src/cdk/testing/component-harness.ts

+30
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,17 @@ export interface HarnessLoader {
106106
*/
107107
getHarnessOrNull<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<T | null>;
108108

109+
/**
110+
* Searches for an instance of the component corresponding to the given harness type under the
111+
* `HarnessLoader`'s root element, and returns a `ComponentHarness` for the instance on the page
112+
* at the given index. If no matching component exists at that index, an error is thrown.
113+
* @param query A query for a harness to create
114+
* @param index The zero-indexed offset of the matching component instance to return
115+
* @return An instance of the given harness type.
116+
* @throws If a matching component instance can't be found at the given index.
117+
*/
118+
getHarnessAtIndex<T extends ComponentHarness>(query: HarnessQuery<T>, index: number): Promise<T>;
119+
109120
/**
110121
* Searches for all instances of the component corresponding to the given harness type under the
111122
* `HarnessLoader`'s root element, and returns a list `ComponentHarness` for each instance.
@@ -114,6 +125,14 @@ export interface HarnessLoader {
114125
*/
115126
getAllHarnesses<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<T[]>;
116127

128+
/**
129+
* Searches for all instances of the component corresponding to the given harness type under the
130+
* `HarnessLoader`'s root element, and returns the total count of all matching components.
131+
* @param query A query for a harness to create
132+
* @return An integer indicating the number of instances that were found.
133+
*/
134+
countHarnesses<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<number>;
135+
117136
/**
118137
* Searches for an instance of the component corresponding to the given harness type under the
119138
* `HarnessLoader`'s root element, and returns a boolean indicating if any were found.
@@ -425,10 +444,21 @@ export abstract class ContentContainerComponentHarness<S extends string = string
425444
return (await this.getRootHarnessLoader()).getHarnessOrNull(query);
426445
}
427446

447+
async getHarnessAtIndex<T extends ComponentHarness>(
448+
query: HarnessQuery<T>,
449+
index: number,
450+
): Promise<T> {
451+
return (await this.getRootHarnessLoader()).getHarnessAtIndex(query, index);
452+
}
453+
428454
async getAllHarnesses<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<T[]> {
429455
return (await this.getRootHarnessLoader()).getAllHarnesses(query);
430456
}
431457

458+
async countHarnesses<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<number> {
459+
return (await this.getRootHarnessLoader()).countHarnesses(query);
460+
}
461+
432462
async hasHarness<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<boolean> {
433463
return (await this.getRootHarnessLoader()).hasHarness(query);
434464
}

src/cdk/testing/harness-environment.ts

+20
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,31 @@ export abstract class HarnessEnvironment<E> implements HarnessLoader, LocatorFac
122122
return this.locatorForOptional(query)();
123123
}
124124

125+
// Implemented as part of the `HarnessLoader` interface.
126+
async getHarnessAtIndex<T extends ComponentHarness>(
127+
query: HarnessQuery<T>,
128+
offset: number,
129+
): Promise<T> {
130+
if (offset < 0) {
131+
throw Error('Index must not be negative');
132+
}
133+
const harnesses = await this.locatorForAll(query)();
134+
if (offset >= harnesses.length) {
135+
throw Error(`No harness was located at index ${offset}`);
136+
}
137+
return harnesses[offset];
138+
}
139+
125140
// Implemented as part of the `HarnessLoader` interface.
126141
getAllHarnesses<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<T[]> {
127142
return this.locatorForAll(query)();
128143
}
129144

145+
// Implemented as part of the `HarnessLoader` interface.
146+
async countHarnesses<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<number> {
147+
return (await this.locatorForAll(query)()).length;
148+
}
149+
130150
// Implemented as part of the `HarnessLoader` interface.
131151
async hasHarness<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<boolean> {
132152
return (await this.locatorForOptional(query)()) !== null;

src/cdk/testing/test-harnesses.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,12 @@ are used to create `ComponentHarness` instances for elements under this root ele
134134
| `getChildLoader(selector: string): Promise<HarnessLoader>` | Searches for an element matching the given selector below the root element of this `HarnessLoader`, and returns a new `HarnessLoader` rooted at the first matching element |
135135
| `getAllChildLoaders(selector: string): Promise<HarnessLoader[]>` | Acts like `getChildLoader`, but returns an array of `HarnessLoader` instances, one for each matching element, rather than just the first matching element |
136136
| `getHarness<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T> \| HarnessPredicate<T>): Promise<T>` | Searches for an instance of the given `ComponentHarness` class or `HarnessPredicate` below the root element of this `HarnessLoader` and returns an instance of the harness corresponding to the first matching element |
137+
| `getHarnessAtIndex<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T> \| HarnessPredicate<T>, index: number): Promise<T>` | Acts like `getHarness`, but returns an instance of the harness corresponding to the matching element with the given index (zero-indexed) |
137138
| `getAllHarnesses<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T> \| HarnessPredicate<T>): Promise<T[]>` | Acts like `getHarness`, but returns an array of harness instances, one for each matching element, rather than just the first matching element |
139+
| `countHarnesses<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T> \| HarnessPredicate<T>): Promise<number>` | Counts the number of instances of the given `ComponentHarness` class or `HarnessPredicate` below the root element of this `HarnessLoader`, and returns the result. |
140+
| `hasHarness<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T> \| HarnessPredicate<T>): Promise<boolean>` | Returns true if an instance of the given `ComponentHarness` class or `HarnessPredicate` exists below the root element of this `HarnessLoader` |
138141

139-
Calls to `getHarness` and `getAllHarnesses` can either take `ComponentHarness` subclass or a
142+
Calls to the harness functions can either take `ComponentHarness` subclass or a
140143
`HarnessPredicate`. `HarnessPredicate` applies additional restrictions to the search (e.g. searching
141144
for a button that has some particular text, etc). The
142145
[details of `HarnessPredicate`](#filtering-harness-instances-with-harnesspredicate) are discussed in

src/material/input/testing/input-harness.spec.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ describe('MatInputHarness', () => {
1818
});
1919

2020
it('should load all input harnesses', async () => {
21-
const inputs = await loader.getAllHarnesses(MatInputHarness);
22-
expect(inputs.length).toBe(7);
21+
expect(await loader.countHarnesses(MatInputHarness)).toBe(7);
2322
});
2423

2524
it('should load input with specific id', async () => {

0 commit comments

Comments
 (0)