1
- import { Merged , assertMergedWithoutOverlap , mergeParams } from '../internal/params_utils.js' ;
1
+ import { Merged , mergeParams , mergeParamsChecked } from '../internal/params_utils.js' ;
2
+ import { comparePublicParamsPaths , Ordering } from '../internal/query/compare.js' ;
2
3
import { stringifyPublicParams } from '../internal/query/stringify_params.js' ;
3
4
import { assert , mapLazy } from '../util/util.js' ;
4
5
6
+ import { TestParams } from './fixture.js' ;
7
+
5
8
// ================================================================
6
9
// "Public" ParamsBuilder API / Documentation
7
10
// ================================================================
@@ -102,27 +105,32 @@ export type CaseSubcaseIterable<CaseP, SubcaseP> = Iterable<
102
105
* Base class for `CaseParamsBuilder` and `SubcaseParamsBuilder`.
103
106
*/
104
107
export abstract class ParamsBuilderBase < CaseP extends { } , SubcaseP extends { } > {
105
- protected readonly cases : ( ) => Generator < CaseP > ;
108
+ protected readonly cases : ( caseFilter : TestParams | null ) => Generator < CaseP > ;
106
109
107
- constructor ( cases : ( ) => Generator < CaseP > ) {
110
+ constructor ( cases : ( caseFilter : TestParams | null ) => Generator < CaseP > ) {
108
111
this . cases = cases ;
109
112
}
110
113
111
114
/**
112
115
* Hidden from test files. Use `builderIterateCasesWithSubcases` to access this.
113
116
*/
114
- protected abstract iterateCasesWithSubcases ( ) : CaseSubcaseIterable < CaseP , SubcaseP > ;
117
+ protected abstract iterateCasesWithSubcases (
118
+ caseFilter : TestParams | null
119
+ ) : CaseSubcaseIterable < CaseP , SubcaseP > ;
115
120
}
116
121
117
122
/**
118
123
* Calls the (normally hidden) `iterateCasesWithSubcases()` method.
119
124
*/
120
- export function builderIterateCasesWithSubcases ( builder : ParamsBuilderBase < { } , { } > ) {
125
+ export function builderIterateCasesWithSubcases (
126
+ builder : ParamsBuilderBase < { } , { } > ,
127
+ caseFilter : TestParams | null
128
+ ) {
121
129
interface IterableParamsBuilder {
122
- iterateCasesWithSubcases ( ) : CaseSubcaseIterable < { } , { } > ;
130
+ iterateCasesWithSubcases ( caseFilter : TestParams | null ) : CaseSubcaseIterable < { } , { } > ;
123
131
}
124
132
125
- return ( ( builder as unknown ) as IterableParamsBuilder ) . iterateCasesWithSubcases ( ) ;
133
+ return ( ( builder as unknown ) as IterableParamsBuilder ) . iterateCasesWithSubcases ( caseFilter ) ;
126
134
}
127
135
128
136
/**
@@ -136,31 +144,66 @@ export function builderIterateCasesWithSubcases(builder: ParamsBuilderBase<{}, {
136
144
export class CaseParamsBuilder < CaseP extends { } >
137
145
extends ParamsBuilderBase < CaseP , { } >
138
146
implements Iterable < CaseP > , ParamsBuilder {
139
- * iterateCasesWithSubcases ( ) : CaseSubcaseIterable < CaseP , { } > {
140
- for ( const a of this . cases ( ) ) {
141
- yield [ a , undefined ] ;
147
+ * iterateCasesWithSubcases ( caseFilter : TestParams | null ) : CaseSubcaseIterable < CaseP , { } > {
148
+ for ( const caseP of this . cases ( caseFilter ) ) {
149
+ if ( caseFilter ) {
150
+ // this.cases() only filters out cases which conflict with caseFilter. Now that we have
151
+ // the final caseP, filter out cases which are missing keys that caseFilter requires.
152
+ const ordering = comparePublicParamsPaths ( caseP , caseFilter ) ;
153
+ if ( ordering === Ordering . StrictSuperset || ordering === Ordering . Unordered ) {
154
+ continue ;
155
+ }
156
+ }
157
+
158
+ yield [ caseP , undefined ] ;
142
159
}
143
160
}
144
161
145
162
[ Symbol . iterator ] ( ) : Iterator < CaseP > {
146
- return this . cases ( ) ;
163
+ return this . cases ( null ) ;
147
164
}
148
165
149
166
/** @inheritDoc */
150
167
expandWithParams < NewP extends { } > (
151
- expander : ( _ : Merged < { } , CaseP > ) => Iterable < NewP >
168
+ expander : ( _ : CaseP ) => Iterable < NewP >
152
169
) : CaseParamsBuilder < Merged < CaseP , NewP > > {
153
- const newGenerator = genExpandWithParams ( this . cases , expander ) ;
154
- return new CaseParamsBuilder ( ( ) => newGenerator ( { } ) ) ;
170
+ const baseGenerator = this . cases ;
171
+ return new CaseParamsBuilder ( function * ( caseFilter ) {
172
+ for ( const a of baseGenerator ( caseFilter ) ) {
173
+ for ( const b of expander ( a ) ) {
174
+ if ( caseFilter ) {
175
+ // If the expander generated any key-value pair that conflicts with caseFilter, skip.
176
+ if ( Object . entries ( b ) . some ( ( [ k , v ] ) => k in caseFilter && caseFilter [ k ] !== v ) ) {
177
+ continue ;
178
+ }
179
+ }
180
+
181
+ yield mergeParamsChecked ( a , b ) ;
182
+ }
183
+ }
184
+ } ) ;
155
185
}
156
186
157
187
/** @inheritDoc */
158
188
expand < NewPKey extends string , NewPValue > (
159
189
key : NewPKey ,
160
- expander : ( _ : Merged < { } , CaseP > ) => Iterable < NewPValue >
190
+ expander : ( _ : CaseP ) => Iterable < NewPValue >
161
191
) : CaseParamsBuilder < Merged < CaseP , { [ name in NewPKey ] : NewPValue } > > {
162
- const newGenerator = genExpand ( this . cases , key , expander ) ;
163
- return new CaseParamsBuilder ( ( ) => newGenerator ( { } ) ) ;
192
+ const baseGenerator = this . cases ;
193
+ return new CaseParamsBuilder ( function * ( caseFilter ) {
194
+ for ( const a of baseGenerator ( caseFilter ) ) {
195
+ assert ( ! ( key in a ) , `New key '${ key } ' already exists in ${ JSON . stringify ( a ) } ` ) ;
196
+
197
+ const caseFilterV = caseFilter ?. [ key ] ;
198
+ for ( const v of expander ( a ) ) {
199
+ // If the expander generated a value for this key that conflicts with caseFilter, skip.
200
+ if ( caseFilter && ( caseFilterV as { } ) !== v ) {
201
+ continue ;
202
+ }
203
+ yield { ...a , [ key ] : v } as Merged < CaseP , { [ name in NewPKey ] : NewPValue } > ;
204
+ }
205
+ }
206
+ } ) ;
164
207
}
165
208
166
209
/** @inheritDoc */
@@ -189,13 +232,17 @@ export class CaseParamsBuilder<CaseP extends {}>
189
232
}
190
233
191
234
/** @inheritDoc */
192
- filter ( pred : ( _ : Merged < { } , CaseP > ) => boolean ) : CaseParamsBuilder < CaseP > {
193
- const newGenerator = filterGenerator ( this . cases , pred ) ;
194
- return new CaseParamsBuilder ( ( ) => newGenerator ( { } ) ) ;
235
+ filter ( pred : ( _ : CaseP ) => boolean ) : CaseParamsBuilder < CaseP > {
236
+ const baseGenerator = this . cases ;
237
+ return new CaseParamsBuilder ( function * ( caseFilter ) {
238
+ for ( const a of baseGenerator ( caseFilter ) ) {
239
+ if ( pred ( a ) ) yield a ;
240
+ }
241
+ } ) ;
195
242
}
196
243
197
244
/** @inheritDoc */
198
- unless ( pred : ( _ : Merged < { } , CaseP > ) => boolean ) : CaseParamsBuilder < CaseP > {
245
+ unless ( pred : ( _ : CaseP ) => boolean ) : CaseParamsBuilder < CaseP > {
199
246
return this . filter ( x => ! pred ( x ) ) ;
200
247
}
201
248
@@ -205,12 +252,9 @@ export class CaseParamsBuilder<CaseP extends {}>
205
252
* generate new subcases instead of new cases.
206
253
*/
207
254
beginSubcases ( ) : SubcaseParamsBuilder < CaseP , { } > {
208
- return new SubcaseParamsBuilder (
209
- ( ) => this . cases ( ) ,
210
- function * ( ) {
211
- yield { } ;
212
- }
213
- ) ;
255
+ return new SubcaseParamsBuilder ( this . cases , function * ( ) {
256
+ yield { } ;
257
+ } ) ;
214
258
}
215
259
}
216
260
@@ -235,13 +279,25 @@ export class SubcaseParamsBuilder<CaseP extends {}, SubcaseP extends {}>
235
279
implements ParamsBuilder {
236
280
protected readonly subcases : ( _ : CaseP ) => Generator < SubcaseP > ;
237
281
238
- constructor ( cases : ( ) => Generator < CaseP > , generator : ( _ : CaseP ) => Generator < SubcaseP > ) {
282
+ constructor (
283
+ cases : ( caseFilter : TestParams | null ) => Generator < CaseP > ,
284
+ generator : ( _ : CaseP ) => Generator < SubcaseP >
285
+ ) {
239
286
super ( cases ) ;
240
287
this . subcases = generator ;
241
288
}
242
289
243
- * iterateCasesWithSubcases ( ) : CaseSubcaseIterable < CaseP , SubcaseP > {
244
- for ( const caseP of this . cases ( ) ) {
290
+ * iterateCasesWithSubcases ( caseFilter : TestParams | null ) : CaseSubcaseIterable < CaseP , SubcaseP > {
291
+ for ( const caseP of this . cases ( caseFilter ) ) {
292
+ if ( caseFilter ) {
293
+ // this.cases() only filters out cases which conflict with caseFilter. Now that we have
294
+ // the final caseP, filter out cases which are missing keys that caseFilter requires.
295
+ const ordering = comparePublicParamsPaths ( caseP , caseFilter ) ;
296
+ if ( ordering === Ordering . StrictSuperset || ordering === Ordering . Unordered ) {
297
+ continue ;
298
+ }
299
+ }
300
+
245
301
const subcases = Array . from ( this . subcases ( caseP ) ) ;
246
302
if ( subcases . length ) {
247
303
yield [ caseP , subcases ] ;
@@ -253,15 +309,32 @@ export class SubcaseParamsBuilder<CaseP extends {}, SubcaseP extends {}>
253
309
expandWithParams < NewP extends { } > (
254
310
expander : ( _ : Merged < CaseP , SubcaseP > ) => Iterable < NewP >
255
311
) : SubcaseParamsBuilder < CaseP , Merged < SubcaseP , NewP > > {
256
- return new SubcaseParamsBuilder ( this . cases , genExpandWithParams ( this . subcases , expander ) ) ;
312
+ const baseGenerator = this . subcases ;
313
+ return new SubcaseParamsBuilder ( this . cases , function * ( base ) {
314
+ for ( const a of baseGenerator ( base ) ) {
315
+ for ( const b of expander ( mergeParams ( base , a ) ) ) {
316
+ yield mergeParamsChecked ( a , b ) ;
317
+ }
318
+ }
319
+ } ) ;
257
320
}
258
321
259
322
/** @inheritDoc */
260
323
expand < NewPKey extends string , NewPValue > (
261
324
key : NewPKey ,
262
325
expander : ( _ : Merged < CaseP , SubcaseP > ) => Iterable < NewPValue >
263
326
) : SubcaseParamsBuilder < CaseP , Merged < SubcaseP , { [ name in NewPKey ] : NewPValue } > > {
264
- return new SubcaseParamsBuilder ( this . cases , genExpand ( this . subcases , key , expander ) ) ;
327
+ const baseGenerator = this . subcases ;
328
+ return new SubcaseParamsBuilder ( this . cases , function * ( base ) {
329
+ for ( const a of baseGenerator ( base ) ) {
330
+ const before = mergeParams ( base , a ) ;
331
+ assert ( ! ( key in before ) , ( ) => `Key '${ key } ' already exists in ${ JSON . stringify ( before ) } ` ) ;
332
+
333
+ for ( const v of expander ( before ) ) {
334
+ yield { ...a , [ key ] : v } as Merged < SubcaseP , { [ k in NewPKey ] : NewPValue } > ;
335
+ }
336
+ }
337
+ } ) ;
265
338
}
266
339
267
340
/** @inheritDoc */
@@ -283,7 +356,12 @@ export class SubcaseParamsBuilder<CaseP extends {}, SubcaseP extends {}>
283
356
284
357
/** @inheritDoc */
285
358
filter ( pred : ( _ : Merged < CaseP , SubcaseP > ) => boolean ) : SubcaseParamsBuilder < CaseP , SubcaseP > {
286
- return new SubcaseParamsBuilder ( this . cases , filterGenerator ( this . subcases , pred ) ) ;
359
+ const baseGenerator = this . subcases ;
360
+ return new SubcaseParamsBuilder ( this . cases , function * ( base ) {
361
+ for ( const a of baseGenerator ( base ) ) {
362
+ if ( pred ( mergeParams ( base , a ) ) ) yield a ;
363
+ }
364
+ } ) ;
287
365
}
288
366
289
367
/** @inheritDoc */
@@ -292,54 +370,6 @@ export class SubcaseParamsBuilder<CaseP extends {}, SubcaseP extends {}>
292
370
}
293
371
}
294
372
295
- /** Creates a generator function for expandWithParams() methods above. */
296
- function genExpandWithParams < Base , A , B > (
297
- baseGenerator : ( _ : Base ) => Generator < A > ,
298
- expander : ( _ : Merged < Base , A > ) => Iterable < B >
299
- ) : ( _ : Base ) => Generator < Merged < A , B > > {
300
- return function * ( base : Base ) {
301
- for ( const a of baseGenerator ( base ) ) {
302
- for ( const b of expander ( mergeParams ( base , a ) ) ) {
303
- const merged = mergeParams ( a , b ) ;
304
- assertMergedWithoutOverlap ( [ a , b ] , merged ) ;
305
-
306
- yield merged ;
307
- }
308
- }
309
- } ;
310
- }
311
-
312
- /** Creates a generator function for expand() methods above. */
313
- function genExpand < Base , A , NewPKey extends string , NewPValue > (
314
- baseGenerator : ( _ : Base ) => Generator < A > ,
315
- key : NewPKey ,
316
- expander : ( _ : Merged < Base , A > ) => Iterable < NewPValue >
317
- ) : ( _ : Base ) => Generator < Merged < A , { [ k in NewPKey ] : NewPValue } > > {
318
- return function * ( base : Base ) {
319
- for ( const a of baseGenerator ( base ) ) {
320
- const before = mergeParams ( base , a ) ;
321
- assert ( ! ( key in before ) , ( ) => `Key '${ key } ' already exists in ${ JSON . stringify ( before ) } ` ) ;
322
-
323
- for ( const v of expander ( before ) ) {
324
- yield { ...a , [ key ] : v } as Merged < A , { [ k in NewPKey ] : NewPValue } > ;
325
- }
326
- }
327
- } ;
328
- }
329
-
330
- function filterGenerator < Base , A > (
331
- baseGenerator : ( _ : Base ) => Generator < A > ,
332
- pred : ( _ : Merged < Base , A > ) => boolean
333
- ) : ( _ : Base ) => Generator < A > {
334
- return function * ( base : Base ) {
335
- for ( const a of baseGenerator ( base ) ) {
336
- if ( pred ( mergeParams ( base , a ) ) ) {
337
- yield a ;
338
- }
339
- }
340
- } ;
341
- }
342
-
343
373
/** Assert an object is not a Generator (a thing returned from a generator function). */
344
374
function assertNotGenerator ( x : object ) {
345
375
if ( 'constructor' in x ) {
0 commit comments