9
9
import { TestElement } from './test-element' ;
10
10
11
11
/** 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 > ;
13
19
14
20
/**
15
21
* Interface used to load ComponentHarness objects. This interface is used by test authors to
@@ -44,17 +50,17 @@ export interface HarnessLoader {
44
50
* @return An instance of the given harness type
45
51
* @throws If a matching component instance can't be found.
46
52
*/
47
- getHarness < T extends ComponentHarness > ( harnessType : ComponentHarnessConstructor < T > ) :
48
- Promise < T > ;
53
+ getHarness < T extends ComponentHarness > (
54
+ harnessType : ComponentHarnessConstructor < T > | HarnessPredicate < T > ) : Promise < T > ;
49
55
50
56
/**
51
57
* Searches for all instances of the component corresponding to the given harness type under the
52
58
* `HarnessLoader`'s root element, and returns a list `ComponentHarness` for each instance.
53
59
* @param harnessType The type of harness to create
54
60
* @return A list instances of the given harness type.
55
61
*/
56
- getAllHarnesses < T extends ComponentHarness > ( harnessType : ComponentHarnessConstructor < T > ) :
57
- Promise < T [ ] > ;
62
+ getAllHarnesses < T extends ComponentHarness > (
63
+ harnessType : ComponentHarnessConstructor < T > | HarnessPredicate < T > ) : Promise < T [ ] > ;
58
64
}
59
65
60
66
/**
@@ -78,7 +84,7 @@ export interface LocatorFactory {
78
84
* @return An asynchronous locator function that searches for elements with the given selector,
79
85
* and either finds one or throws an error
80
86
*/
81
- locatorFor ( selector : string ) : AsyncFn < TestElement > ;
87
+ locatorFor ( selector : string ) : AsyncFactoryFn < TestElement > ;
82
88
83
89
/**
84
90
* Creates an asynchronous locator function that can be used to find a `ComponentHarness` for a
@@ -89,8 +95,8 @@ export interface LocatorFactory {
89
95
* @return An asynchronous locator function that searches components matching the given harness
90
96
* type, and either returns a `ComponentHarness` for the component, or throws an error.
91
97
*/
92
- locatorFor < T extends ComponentHarness > ( harnessType : ComponentHarnessConstructor < T > ) :
93
- AsyncFn < T > ;
98
+ locatorFor < T extends ComponentHarness > (
99
+ harnessType : ComponentHarnessConstructor < T > | HarnessPredicate < T > ) : AsyncFactoryFn < T > ;
94
100
95
101
/**
96
102
* Creates an asynchronous locator function that can be used to search for elements with the given
@@ -101,7 +107,7 @@ export interface LocatorFactory {
101
107
* @return An asynchronous locator function that searches for elements with the given selector,
102
108
* and either finds one or returns null.
103
109
*/
104
- locatorForOptional ( selector : string ) : AsyncFn < TestElement | null > ;
110
+ locatorForOptional ( selector : string ) : AsyncFactoryFn < TestElement | null > ;
105
111
106
112
/**
107
113
* Creates an asynchronous locator function that can be used to find a `ComponentHarness` for a
@@ -112,8 +118,8 @@ export interface LocatorFactory {
112
118
* @return An asynchronous locator function that searches components matching the given harness
113
119
* type, and either returns a `ComponentHarness` for the component, or null if none is found.
114
120
*/
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 > ;
117
123
118
124
/**
119
125
* Creates an asynchronous locator function that can be used to search for a list of elements with
@@ -123,7 +129,7 @@ export interface LocatorFactory {
123
129
* @return An asynchronous locator function that searches for elements with the given selector,
124
130
* and either finds one or throws an error
125
131
*/
126
- locatorForAll ( selector : string ) : AsyncFn < TestElement [ ] > ;
132
+ locatorForAll ( selector : string ) : AsyncFactoryFn < TestElement [ ] > ;
127
133
128
134
/**
129
135
* Creates an asynchronous locator function that can be used to find a list of
@@ -134,8 +140,8 @@ export interface LocatorFactory {
134
140
* @return An asynchronous locator function that searches components matching the given harness
135
141
* type, and returns a list of `ComponentHarness`es.
136
142
*/
137
- locatorForAll < T extends ComponentHarness > ( harnessType : ComponentHarnessConstructor < T > ) :
138
- AsyncFn < T [ ] > ;
143
+ locatorForAll < T extends ComponentHarness > (
144
+ harnessType : ComponentHarnessConstructor < T > | HarnessPredicate < T > ) : AsyncFactoryFn < T [ ] > ;
139
145
}
140
146
141
147
/**
@@ -169,7 +175,7 @@ export abstract class ComponentHarness {
169
175
* @return An asynchronous locator function that searches for elements with the given selector,
170
176
* and either finds one or throws an error
171
177
*/
172
- protected locatorFor ( selector : string ) : AsyncFn < TestElement > ;
178
+ protected locatorFor ( selector : string ) : AsyncFactoryFn < TestElement > ;
173
179
174
180
/**
175
181
* Creates an asynchronous locator function that can be used to find a `ComponentHarness` for a
@@ -181,7 +187,7 @@ export abstract class ComponentHarness {
181
187
* type, and either returns a `ComponentHarness` for the component, or throws an error.
182
188
*/
183
189
protected locatorFor < T extends ComponentHarness > (
184
- harnessType : ComponentHarnessConstructor < T > ) : AsyncFn < T > ;
190
+ harnessType : ComponentHarnessConstructor < T > | HarnessPredicate < T > ) : AsyncFactoryFn < T > ;
185
191
186
192
protected locatorFor ( arg : any ) : any {
187
193
return this . locatorFactory . locatorFor ( arg ) ;
@@ -196,7 +202,7 @@ export abstract class ComponentHarness {
196
202
* @return An asynchronous locator function that searches for elements with the given selector,
197
203
* and either finds one or returns null.
198
204
*/
199
- protected locatorForOptional ( selector : string ) : AsyncFn < TestElement | null > ;
205
+ protected locatorForOptional ( selector : string ) : AsyncFactoryFn < TestElement | null > ;
200
206
201
207
/**
202
208
* Creates an asynchronous locator function that can be used to find a `ComponentHarness` for a
@@ -208,7 +214,7 @@ export abstract class ComponentHarness {
208
214
* type, and either returns a `ComponentHarness` for the component, or null if none is found.
209
215
*/
210
216
protected locatorForOptional < T extends ComponentHarness > (
211
- harnessType : ComponentHarnessConstructor < T > ) : AsyncFn < T | null > ;
217
+ harnessType : ComponentHarnessConstructor < T > | HarnessPredicate < T > ) : AsyncFactoryFn < T | null > ;
212
218
213
219
protected locatorForOptional ( arg : any ) : any {
214
220
return this . locatorFactory . locatorForOptional ( arg ) ;
@@ -222,7 +228,7 @@ export abstract class ComponentHarness {
222
228
* @return An asynchronous locator function that searches for elements with the given selector,
223
229
* and either finds one or throws an error
224
230
*/
225
- protected locatorForAll ( selector : string ) : AsyncFn < TestElement [ ] > ;
231
+ protected locatorForAll ( selector : string ) : AsyncFactoryFn < TestElement [ ] > ;
226
232
227
233
/**
228
234
* Creates an asynchronous locator function that can be used to find a list of
@@ -233,8 +239,8 @@ export abstract class ComponentHarness {
233
239
* @return An asynchronous locator function that searches components matching the given harness
234
240
* type, and returns a list of `ComponentHarness`es.
235
241
*/
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 [ ] > ;
238
244
239
245
protected locatorForAll ( arg : any ) : any {
240
246
return this . locatorFactory . locatorForAll ( arg ) ;
@@ -252,3 +258,82 @@ export interface ComponentHarnessConstructor<T extends ComponentHarness> {
252
258
*/
253
259
hostSelector : string ;
254
260
}
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