@@ -7,6 +7,7 @@ const parseTestArgs = require('./parse-test-args');
7
7
const snapshotManager = require ( './snapshot-manager' ) ;
8
8
const serializeError = require ( './serialize-error' ) ;
9
9
const Runnable = require ( './test' ) ;
10
+ const { Task, TaskList} = require ( './task-list' ) ;
10
11
11
12
class Runner extends Emittery {
12
13
constructor ( options = { } ) {
@@ -29,17 +30,7 @@ class Runner extends Emittery {
29
30
this . boundCompareTestSnapshot = this . compareTestSnapshot . bind ( this ) ;
30
31
this . interrupted = false ;
31
32
this . snapshots = null ;
32
- this . tasks = {
33
- after : [ ] ,
34
- afterAlways : [ ] ,
35
- afterEach : [ ] ,
36
- afterEachAlways : [ ] ,
37
- before : [ ] ,
38
- beforeEach : [ ] ,
39
- concurrent : [ ] ,
40
- serial : [ ] ,
41
- todo : [ ]
42
- } ;
33
+ this . tasks = new TaskList ( ) ;
43
34
44
35
const uniqueTestTitles = new Set ( ) ;
45
36
this . registerUniqueTitle = title => {
@@ -61,8 +52,7 @@ class Runner extends Emittery {
61
52
failing : false ,
62
53
inline : false , // Default value; only attempts created by `t.try()` have this annotation set to `true`.
63
54
serial : false ,
64
- skipped : false ,
65
- todo : false
55
+ skipped : false
66
56
} ,
67
57
meta : Object . freeze ( {
68
58
file : options . file ,
@@ -74,11 +64,9 @@ class Runner extends Emittery {
74
64
declare : ( { // eslint-disable-line complexity
75
65
annotations,
76
66
args : declarationArguments ,
77
- options : {
78
- allowExperimentalMacros,
79
- allowImplementationTitleFns,
80
- allowMultipleImplementations
81
- } } ) => {
67
+ options,
68
+ type
69
+ } ) => {
82
70
if ( hasStarted ) {
83
71
throw new Error ( 'All tests and hooks must be declared synchronously in your test file, and cannot be nested within other tests or hooks.' ) ;
84
72
}
@@ -91,13 +79,9 @@ class Runner extends Emittery {
91
79
} ) ;
92
80
}
93
81
94
- const { args, buildTitle, implementations, rawTitle} = parseTestArgs ( declarationArguments , {
95
- allowExperimentalMacros,
96
- allowImplementationTitleFns,
97
- allowMultipleImplementations
98
- } ) ;
82
+ const { args, buildTitle, implementations, rawTitle} = parseTestArgs ( declarationArguments , options ) ;
99
83
100
- if ( annotations . todo ) {
84
+ if ( type === ' todo' ) {
101
85
if ( implementations . length > 0 ) {
102
86
throw new TypeError ( '`todo` tests are not allowed to have an implementation. Use `test.skip()` for tests with an implementation.' ) ;
103
87
}
@@ -118,7 +102,7 @@ class Runner extends Emittery {
118
102
}
119
103
}
120
104
121
- this . tasks . todo . push ( { title : rawTitle , annotations } ) ;
105
+ this . tasks . add ( Task . todo ( { annotations , title : rawTitle } ) ) ;
122
106
this . emit ( 'stateChange' , {
123
107
type : 'declared-test' ,
124
108
title : rawTitle ,
@@ -138,48 +122,51 @@ class Runner extends Emittery {
138
122
}
139
123
140
124
if ( isEmpty ) {
141
- if ( annotations . type === 'test' ) {
125
+ if ( type === 'test' ) {
142
126
throw new TypeError ( 'Tests must have a title' ) ;
143
127
} else if ( annotations . always ) {
144
- title = `${ annotations . type } .always hook` ;
128
+ title = `${ type } .always hook` ;
145
129
} else {
146
- title = `${ annotations . type } hook` ;
130
+ title = `${ type } hook` ;
147
131
}
148
132
}
149
133
150
- if ( annotations . type === 'test' && ! this . registerUniqueTitle ( title ) ) {
134
+ if ( type === 'test' && ! this . registerUniqueTitle ( title ) ) {
151
135
throw new Error ( `Duplicate test title: ${ title } ` ) ;
152
136
}
153
137
154
- const task = {
155
- allowExperimentalMacros,
156
- allowImplementationTitleFns,
157
- allowMultipleImplementations,
158
- annotations : { ...annotations } ,
159
- args,
160
- implementation,
161
- title
162
- } ;
163
-
164
- if ( annotations . type === 'test' ) {
138
+ if ( type === 'test' ) {
139
+ let { exclusive} = annotations ;
165
140
if ( this . match . length > 0 ) {
166
141
// --match overrides .only()
167
- task . annotations . exclusive = matcher ( [ title ] , this . match ) . length === 1 ;
142
+ exclusive = matcher ( [ title ] , this . match ) . length === 1 ;
168
143
}
169
144
170
- if ( task . annotations . exclusive ) {
145
+ if ( exclusive ) {
171
146
this . runOnlyExclusive = true ;
172
147
}
173
148
174
- this . tasks [ annotations . serial ? 'serial' : 'concurrent' ] . push ( task ) ;
149
+ this . tasks . add ( Task . test ( {
150
+ annotations : { ...annotations , exclusive} ,
151
+ args,
152
+ implementation,
153
+ options,
154
+ title
155
+ } ) ) ;
175
156
this . emit ( 'stateChange' , {
176
157
type : 'declared-test' ,
177
158
title,
178
159
knownFailing : annotations . failing ,
179
160
todo : false
180
161
} ) ;
181
- } else if ( ! annotations . skipped ) {
182
- this . tasks [ annotations . type + ( annotations . always ? 'Always' : '' ) ] . push ( task ) ;
162
+ } else {
163
+ this . tasks . add ( Task [ type ] ( {
164
+ annotations,
165
+ args,
166
+ implementation,
167
+ options,
168
+ title
169
+ } ) ) ;
183
170
}
184
171
}
185
172
}
@@ -305,11 +292,9 @@ class Runner extends Emittery {
305
292
return result ;
306
293
}
307
294
308
- async runHooks ( tasks , contextRef , titleSuffix , testPassed ) {
309
- const hooks = tasks . map ( task => new Runnable ( {
310
- allowExperimentalMacros : task . allowExperimentalMacros ,
311
- allowImplementationTitleFns : task . allowImplementationTitleFns ,
312
- allowMultipleImplementations : task . allowMultipleImplementations ,
295
+ async runHooks ( type , contextRef , titleSuffix , testPassed ) {
296
+ const hooks = [ ...this . tasks . select ( type ) ] . map ( task => new Runnable ( {
297
+ ...task . options ,
313
298
annotations : task . annotations ,
314
299
contextRef,
315
300
experiments : this . experiments ,
@@ -348,7 +333,7 @@ class Runner extends Emittery {
348
333
349
334
async runTest ( task , contextRef ) {
350
335
const hookSuffix = ` for ${ task . title } ` ;
351
- let hooksOk = await this . runHooks ( this . tasks . beforeEach , contextRef , hookSuffix ) ;
336
+ let hooksOk = await this . runHooks ( ' beforeEach' , contextRef , hookSuffix ) ;
352
337
353
338
let testOk = false ;
354
339
if ( hooksOk ) {
@@ -383,7 +368,7 @@ class Runner extends Emittery {
383
368
logs : result . logs
384
369
} ) ;
385
370
386
- hooksOk = await this . runHooks ( this . tasks . afterEach , contextRef , hookSuffix , testOk ) ;
371
+ hooksOk = await this . runHooks ( ' afterEach' , contextRef , hookSuffix , testOk ) ;
387
372
} else {
388
373
this . emit ( 'stateChange' , {
389
374
type : 'test-failed' ,
@@ -397,14 +382,14 @@ class Runner extends Emittery {
397
382
}
398
383
}
399
384
400
- const alwaysOk = await this . runHooks ( this . tasks . afterEachAlways , contextRef , hookSuffix , testOk ) ;
385
+ const alwaysOk = await this . runHooks ( ' afterEachAlways' , contextRef , hookSuffix , testOk ) ;
401
386
return alwaysOk && hooksOk && testOk ;
402
387
}
403
388
404
389
async start ( ) {
405
- const concurrentTests = [ ] ;
406
- const serialTests = [ ] ;
407
- for ( const task of this . tasks . serial ) {
390
+ let concurrentTests = [ ] ;
391
+ let serialTests = [ ] ;
392
+ for ( const task of this . tasks . select ( 'test' ) ) {
408
393
if ( this . runOnlyExclusive && ! task . annotations . exclusive ) {
409
394
continue ;
410
395
}
@@ -417,34 +402,25 @@ class Runner extends Emittery {
417
402
todo : false
418
403
} ) ;
419
404
420
- if ( ! task . annotations . skipped ) {
421
- serialTests . push ( task ) ;
422
- }
423
- }
424
-
425
- for ( const task of this . tasks . concurrent ) {
426
- if ( this . runOnlyExclusive && ! task . annotations . exclusive ) {
405
+ if ( task . annotations . skipped ) {
427
406
continue ;
428
407
}
429
408
430
- this . emit ( 'stateChange' , {
431
- type : 'selected-test' ,
432
- title : task . title ,
433
- knownFailing : task . annotations . failing ,
434
- skip : task . annotations . skipped ,
435
- todo : false
436
- } ) ;
437
-
438
- if ( ! task . annotations . skipped ) {
439
- if ( this . serial ) {
440
- serialTests . push ( task ) ;
441
- } else {
442
- concurrentTests . push ( task ) ;
443
- }
409
+ if ( task . annotations . serial ) {
410
+ serialTests . push ( task ) ;
411
+ } else {
412
+ concurrentTests . push ( task ) ;
444
413
}
445
414
}
446
415
447
- for ( const task of this . tasks . todo ) {
416
+ // Reassign the concurrent tasks, but always run them after the explicitly
417
+ // serial ones.
418
+ if ( this . serial ) {
419
+ serialTests = [ ...serialTests , ...concurrentTests ] ;
420
+ concurrentTests = [ ] ;
421
+ }
422
+
423
+ for ( const task of this . tasks . select ( 'todo' ) ) {
448
424
if ( this . runOnlyExclusive && ! task . annotations . exclusive ) {
449
425
continue ;
450
426
}
@@ -467,7 +443,7 @@ class Runner extends Emittery {
467
443
const contextRef = new ContextRef ( ) ;
468
444
469
445
// Note that the hooks and tests always begin running asynchronously.
470
- const beforePromise = this . runHooks ( this . tasks . before , contextRef ) ;
446
+ const beforePromise = this . runHooks ( ' before' , contextRef ) ;
471
447
const serialPromise = beforePromise . then ( beforeHooksOk => { // eslint-disable-line promise/prefer-await-to-then
472
448
// Don't run tests if a `before` hook failed.
473
449
if ( ! beforeHooksOk ) {
@@ -517,11 +493,11 @@ class Runner extends Emittery {
517
493
const ok = await concurrentPromise ;
518
494
// Only run `after` hooks if all hooks and tests passed.
519
495
if ( ok ) {
520
- await this . runHooks ( this . tasks . after , contextRef ) ;
496
+ await this . runHooks ( ' after' , contextRef ) ;
521
497
}
522
498
523
499
// Always run `after.always` hooks.
524
- await this . runHooks ( this . tasks . afterAlways , contextRef ) ;
500
+ await this . runHooks ( ' afterAlways' , contextRef ) ;
525
501
process . removeListener ( 'beforeExit' , beforeExitHandler ) ;
526
502
await this . emit ( 'finish' ) ;
527
503
} catch ( error ) {
0 commit comments