Skip to content

Commit 1b1e2b8

Browse files
committed
feat(material/testing): Modify HarnessLoader with the addition of getHarnessWithOffset and
`countHarnesses` These two new functions are intended to expand harness testing functionality by providing built-in functions for commonly used patterns. * `getHarnessWithOffset` functions similarly to `getHarness`, but returns a harness for the matching component instance with the given offset. An example use case is to fetch the third 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 Sneakily manually tested using the MatInput test suite
1 parent 9aa24d7 commit 1b1e2b8

File tree

3 files changed

+55
-1
lines changed

3 files changed

+55
-1
lines changed

src/cdk/testing/component-harness.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,20 @@ 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+
* with the given offset. If no matching component exists with that offset, an error is thrown.
113+
* @param query A query for a harness to create
114+
* @param offset 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 with the given offset.
117+
*/
118+
getHarnessWithOffset<T extends ComponentHarness>(
119+
query: HarnessQuery<T>,
120+
index: number,
121+
): Promise<T>;
122+
109123
/**
110124
* Searches for all instances of the component corresponding to the given harness type under the
111125
* `HarnessLoader`'s root element, and returns a list `ComponentHarness` for each instance.
@@ -114,6 +128,14 @@ export interface HarnessLoader {
114128
*/
115129
getAllHarnesses<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<T[]>;
116130

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

450+
async getHarnessWithOffset<T extends ComponentHarness>(
451+
query: HarnessQuery<T>,
452+
offset: number,
453+
): Promise<T> {
454+
return (await this.getRootHarnessLoader()).getHarnessWithOffset(query, offset);
455+
}
456+
428457
async getAllHarnesses<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<T[]> {
429458
return (await this.getRootHarnessLoader()).getAllHarnesses(query);
430459
}
431460

461+
async countHarnesses<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<number> {
462+
return (await this.getRootHarnessLoader()).countHarnesses(query);
463+
}
464+
432465
async hasHarness<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<boolean> {
433466
return (await this.getRootHarnessLoader()).hasHarness(query);
434467
}

src/cdk/testing/harness-environment.ts

Lines changed: 20 additions & 0 deletions
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 getHarnessWithOffset<T extends ComponentHarness>(
127+
query: HarnessQuery<T>,
128+
offset: number,
129+
): Promise<T> {
130+
if (offset < 0) {
131+
throw Error('Offset 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 offset ${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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,10 @@ 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+
| `getHarnessWithOffset<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T> \| HarnessPredicate<T>, offset: number): Promise<T>` | Acts like `getHarness`, but returns an instance of the harness corresponding to the element with the given zero-indexed offset |
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 |
138139

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

0 commit comments

Comments
 (0)