99import { TestElement } from './test-element' ;
1010
1111/** An async function that returns a promise when called. */
12- export type AsyncFn < T > = ( ) => Promise < T > ;
12+ export type AsyncFactoryFn < T > = ( ) => Promise < T > ;
13+
14+ /** An async function that takes an item and returns a boolean promise */
15+ export type AsyncPredicate < T > = ( item : T ) => Promise < boolean > ;
16+
17+ /** An async function that takes an item and an option value and returns a boolean promise. */
18+ export type AsyncOptionPredicate < T , O > = ( item : T , option : O ) => Promise < boolean > ;
1319
1420/**
1521 * Interface used to load ComponentHarness objects. This interface is used by test authors to
@@ -44,17 +50,17 @@ export interface HarnessLoader {
4450 * @return An instance of the given harness type
4551 * @throws If a matching component instance can't be found.
4652 */
47- getHarness < T extends ComponentHarness > ( harnessType : ComponentHarnessConstructor < T > ) :
48- Promise < T > ;
53+ getHarness < T extends ComponentHarness > (
54+ harnessType : ComponentHarnessConstructor < T > | HarnessPredicate < T > ) : Promise < T > ;
4955
5056 /**
5157 * Searches for all instances of the component corresponding to the given harness type under the
5258 * `HarnessLoader`'s root element, and returns a list `ComponentHarness` for each instance.
5359 * @param harnessType The type of harness to create
5460 * @return A list instances of the given harness type.
5561 */
56- getAllHarnesses < T extends ComponentHarness > ( harnessType : ComponentHarnessConstructor < T > ) :
57- Promise < T [ ] > ;
62+ getAllHarnesses < T extends ComponentHarness > (
63+ harnessType : ComponentHarnessConstructor < T > | HarnessPredicate < T > ) : Promise < T [ ] > ;
5864}
5965
6066/**
@@ -78,7 +84,7 @@ export interface LocatorFactory {
7884 * @return An asynchronous locator function that searches for elements with the given selector,
7985 * and either finds one or throws an error
8086 */
81- locatorFor ( selector : string ) : AsyncFn < TestElement > ;
87+ locatorFor ( selector : string ) : AsyncFactoryFn < TestElement > ;
8288
8389 /**
8490 * Creates an asynchronous locator function that can be used to find a `ComponentHarness` for a
@@ -89,8 +95,8 @@ export interface LocatorFactory {
8995 * @return An asynchronous locator function that searches components matching the given harness
9096 * type, and either returns a `ComponentHarness` for the component, or throws an error.
9197 */
92- locatorFor < T extends ComponentHarness > ( harnessType : ComponentHarnessConstructor < T > ) :
93- AsyncFn < T > ;
98+ locatorFor < T extends ComponentHarness > (
99+ harnessType : ComponentHarnessConstructor < T > | HarnessPredicate < T > ) : AsyncFactoryFn < T > ;
94100
95101 /**
96102 * Creates an asynchronous locator function that can be used to search for elements with the given
@@ -101,7 +107,7 @@ export interface LocatorFactory {
101107 * @return An asynchronous locator function that searches for elements with the given selector,
102108 * and either finds one or returns null.
103109 */
104- locatorForOptional ( selector : string ) : AsyncFn < TestElement | null > ;
110+ locatorForOptional ( selector : string ) : AsyncFactoryFn < TestElement | null > ;
105111
106112 /**
107113 * Creates an asynchronous locator function that can be used to find a `ComponentHarness` for a
@@ -112,8 +118,8 @@ export interface LocatorFactory {
112118 * @return An asynchronous locator function that searches components matching the given harness
113119 * type, and either returns a `ComponentHarness` for the component, or null if none is found.
114120 */
115- locatorForOptional < T extends ComponentHarness > ( harnessType : ComponentHarnessConstructor < T > ) :
116- AsyncFn < T | null > ;
121+ locatorForOptional < T extends ComponentHarness > (
122+ harnessType : ComponentHarnessConstructor < T > | HarnessPredicate < T > ) : AsyncFactoryFn < T | null > ;
117123
118124 /**
119125 * Creates an asynchronous locator function that can be used to search for a list of elements with
@@ -123,7 +129,7 @@ export interface LocatorFactory {
123129 * @return An asynchronous locator function that searches for elements with the given selector,
124130 * and either finds one or throws an error
125131 */
126- locatorForAll ( selector : string ) : AsyncFn < TestElement [ ] > ;
132+ locatorForAll ( selector : string ) : AsyncFactoryFn < TestElement [ ] > ;
127133
128134 /**
129135 * Creates an asynchronous locator function that can be used to find a list of
@@ -134,8 +140,8 @@ export interface LocatorFactory {
134140 * @return An asynchronous locator function that searches components matching the given harness
135141 * type, and returns a list of `ComponentHarness`es.
136142 */
137- locatorForAll < T extends ComponentHarness > ( harnessType : ComponentHarnessConstructor < T > ) :
138- AsyncFn < T [ ] > ;
143+ locatorForAll < T extends ComponentHarness > (
144+ harnessType : ComponentHarnessConstructor < T > | HarnessPredicate < T > ) : AsyncFactoryFn < T [ ] > ;
139145}
140146
141147/**
@@ -169,7 +175,7 @@ export abstract class ComponentHarness {
169175 * @return An asynchronous locator function that searches for elements with the given selector,
170176 * and either finds one or throws an error
171177 */
172- protected locatorFor ( selector : string ) : AsyncFn < TestElement > ;
178+ protected locatorFor ( selector : string ) : AsyncFactoryFn < TestElement > ;
173179
174180 /**
175181 * Creates an asynchronous locator function that can be used to find a `ComponentHarness` for a
@@ -181,7 +187,7 @@ export abstract class ComponentHarness {
181187 * type, and either returns a `ComponentHarness` for the component, or throws an error.
182188 */
183189 protected locatorFor < T extends ComponentHarness > (
184- harnessType : ComponentHarnessConstructor < T > ) : AsyncFn < T > ;
190+ harnessType : ComponentHarnessConstructor < T > | HarnessPredicate < T > ) : AsyncFactoryFn < T > ;
185191
186192 protected locatorFor ( arg : any ) : any {
187193 return this . locatorFactory . locatorFor ( arg ) ;
@@ -196,7 +202,7 @@ export abstract class ComponentHarness {
196202 * @return An asynchronous locator function that searches for elements with the given selector,
197203 * and either finds one or returns null.
198204 */
199- protected locatorForOptional ( selector : string ) : AsyncFn < TestElement | null > ;
205+ protected locatorForOptional ( selector : string ) : AsyncFactoryFn < TestElement | null > ;
200206
201207 /**
202208 * Creates an asynchronous locator function that can be used to find a `ComponentHarness` for a
@@ -208,7 +214,7 @@ export abstract class ComponentHarness {
208214 * type, and either returns a `ComponentHarness` for the component, or null if none is found.
209215 */
210216 protected locatorForOptional < T extends ComponentHarness > (
211- harnessType : ComponentHarnessConstructor < T > ) : AsyncFn < T | null > ;
217+ harnessType : ComponentHarnessConstructor < T > | HarnessPredicate < T > ) : AsyncFactoryFn < T | null > ;
212218
213219 protected locatorForOptional ( arg : any ) : any {
214220 return this . locatorFactory . locatorForOptional ( arg ) ;
@@ -222,7 +228,7 @@ export abstract class ComponentHarness {
222228 * @return An asynchronous locator function that searches for elements with the given selector,
223229 * and either finds one or throws an error
224230 */
225- protected locatorForAll ( selector : string ) : AsyncFn < TestElement [ ] > ;
231+ protected locatorForAll ( selector : string ) : AsyncFactoryFn < TestElement [ ] > ;
226232
227233 /**
228234 * Creates an asynchronous locator function that can be used to find a list of
@@ -233,8 +239,8 @@ export abstract class ComponentHarness {
233239 * @return An asynchronous locator function that searches components matching the given harness
234240 * type, and returns a list of `ComponentHarness`es.
235241 */
236- protected locatorForAll < T extends ComponentHarness > ( harnessType : ComponentHarnessConstructor < T > ) :
237- AsyncFn < T [ ] > ;
242+ protected locatorForAll < T extends ComponentHarness > (
243+ harnessType : ComponentHarnessConstructor < T > | HarnessPredicate < T > ) : AsyncFactoryFn < T [ ] > ;
238244
239245 protected locatorForAll ( arg : any ) : any {
240246 return this . locatorFactory . locatorForAll ( arg ) ;
@@ -252,3 +258,82 @@ export interface ComponentHarnessConstructor<T extends ComponentHarness> {
252258 */
253259 hostSelector : string ;
254260}
261+
262+ /**
263+ * A class used to associate a ComponentHarness class with predicates functions that can be used to
264+ * filter instances of the class.
265+ */
266+ export class HarnessPredicate < T extends ComponentHarness > {
267+ private _predicates : AsyncPredicate < T > [ ] = [ ] ;
268+ private _descriptions : string [ ] = [ ] ;
269+
270+ constructor ( public harnessType : ComponentHarnessConstructor < T > ) { }
271+
272+ /**
273+ * Checks if a string matches the given pattern.
274+ * @param s The string to check, or a Promise for the string to check.
275+ * @param pattern The pattern the string is expected to match. If `pattern` is a string, `s` is
276+ * expected to match exactly. If `pattern` is a regex, a partial match is allowed.
277+ * @return A Promise that resolves to whether the string matches the pattern.
278+ */
279+ static async stringMatches ( s : string | Promise < string > , pattern : string | RegExp ) :
280+ Promise < boolean > {
281+ s = await s ;
282+ return typeof pattern === 'string' ? s === pattern : pattern . test ( s ) ;
283+ }
284+
285+ /**
286+ * Adds a predicate function to be run against candidate harnesses.
287+ * @param description A description of this predicate that may be used in error messages.
288+ * @param predicate An async predicate function.
289+ * @return this (for method chaining).
290+ */
291+ add ( description : string , predicate : AsyncPredicate < T > ) {
292+ this . _descriptions . push ( description ) ;
293+ this . _predicates . push ( predicate ) ;
294+ return this ;
295+ }
296+
297+ /**
298+ * Adds a predicate function that depends on an option value to be run against candidate
299+ * harnesses. If the option value is undefined, the predicate will be ignored.
300+ * @param name The name of the option (may be used in error messages).
301+ * @param option The option value.
302+ * @param predicate The predicate function to run if the option value is not undefined.
303+ * @return this (for method chaining).
304+ */
305+ addOption < O > ( name : string , option : O | undefined , predicate : AsyncOptionPredicate < T , O > ) {
306+ // Add quotes around strings to differentiate them from other values
307+ const value = typeof option === 'string' ? `"${ option } "` : `${ option } ` ;
308+ if ( option !== undefined ) {
309+ this . add ( `${ name } = ${ value } ` , item => predicate ( item , option ) ) ;
310+ }
311+ return this ;
312+ }
313+
314+ /**
315+ * Filters a list of harnesses on this predicate.
316+ * @param harnesses The list of harnesses to filter.
317+ * @return A list of harnesses that satisfy this predicate.
318+ */
319+ async filter ( harnesses : T [ ] ) : Promise < T [ ] > {
320+ const results = await Promise . all ( harnesses . map ( h => this . evaluate ( h ) ) ) ;
321+ return harnesses . filter ( ( _ , i ) => results [ i ] ) ;
322+ }
323+
324+ /**
325+ * Evaluates whether the given harness satisfies this predicate.
326+ * @param harness The harness to check
327+ * @return A promise that resolves to true if the harness satisfies this predicate,
328+ * and resolves to false otherwise.
329+ */
330+ async evaluate ( harness : T ) : Promise < boolean > {
331+ const results = await Promise . all ( this . _predicates . map ( p => p ( harness ) ) ) ;
332+ return results . reduce ( ( combined , current ) => combined && current , true ) ;
333+ }
334+
335+ /** Gets a description of this predicate for use in error messages. */
336+ getDescription ( ) {
337+ return this . _descriptions . join ( ', ' ) ;
338+ }
339+ }
0 commit comments