@@ -66,6 +66,10 @@ class TestContext {
66
66
this . #test = test ;
67
67
}
68
68
69
+ get name ( ) {
70
+ return this . #test. name ;
71
+ }
72
+
69
73
diagnostic ( message ) {
70
74
this . #test. diagnostic ( message ) ;
71
75
}
@@ -88,6 +92,14 @@ class TestContext {
88
92
89
93
return subtest . start ( ) ;
90
94
}
95
+
96
+ beforeEach ( fn , options ) {
97
+ this . #test. createBeforeEachHook ( fn , options ) ;
98
+ }
99
+
100
+ afterEach ( fn , options ) {
101
+ this . #test. createAfterEachHook ( fn , options ) ;
102
+ }
91
103
}
92
104
93
105
class Test extends AsyncResource {
@@ -165,6 +177,8 @@ class Test extends AsyncResource {
165
177
this . pendingSubtests = [ ] ;
166
178
this . readySubtests = new SafeMap ( ) ;
167
179
this . subtests = [ ] ;
180
+ this . beforeEachHooks = [ ] ;
181
+ this . afterEachHooks = [ ] ;
168
182
this . waitingOn = 0 ;
169
183
this . finished = false ;
170
184
}
@@ -267,6 +281,18 @@ class Test extends AsyncResource {
267
281
return test ;
268
282
}
269
283
284
+ createBeforeEachHook ( fn , options ) {
285
+ const hook = new TestHook ( fn , options ) ;
286
+ ArrayPrototypePush ( this . beforeEachHooks , hook ) ;
287
+ return hook ;
288
+ }
289
+
290
+ createAfterEachHook ( fn , options ) {
291
+ const hook = new TestHook ( fn , options ) ;
292
+ ArrayPrototypePush ( this . afterEachHooks , hook ) ;
293
+ return hook ;
294
+ }
295
+
270
296
cancel ( ) {
271
297
if ( this . endTime !== null ) {
272
298
return ;
@@ -278,6 +304,7 @@ class Test extends AsyncResource {
278
304
kCancelledByParent
279
305
)
280
306
) ;
307
+ this . startTime = this . startTime || this . endTime ; // if a test was canceled before it was started, e.g inside a hook
281
308
this . cancelled = true ;
282
309
}
283
310
@@ -334,12 +361,24 @@ class Test extends AsyncResource {
334
361
return { ctx, args : [ ctx ] } ;
335
362
}
336
363
337
- async run ( ) {
338
- this . parent . activeSubtests ++ ;
364
+ async #runHooks( hooks ) {
365
+ await ArrayPrototypeReduce ( hooks , async ( prev , hook ) => {
366
+ await prev ;
367
+ await hook . run ( this . getRunArgs ( ) ) ;
368
+ } , PromiseResolve ( ) ) ;
369
+ }
370
+
371
+ async run ( ...runArgs ) {
372
+ if ( this . parent !== null ) {
373
+ this . parent . activeSubtests ++ ;
374
+ }
375
+ if ( this . parent ?. beforeEachHooks . length > 0 ) {
376
+ await this . #runHooks( this . parent . beforeEachHooks ) ;
377
+ }
339
378
this . startTime = hrtime ( ) ;
340
379
341
380
try {
342
- const { args, ctx } = this . getRunArgs ( ) ;
381
+ const { args, ctx } = ReflectApply ( this . getRunArgs , this , runArgs ) ;
343
382
ArrayPrototypeUnshift ( args , this . fn , ctx ) ; // Note that if it's not OK to mutate args, we need to first clone it.
344
383
345
384
if ( this . fn . length === args . length - 1 ) {
@@ -372,9 +411,13 @@ class Test extends AsyncResource {
372
411
}
373
412
}
374
413
414
+ if ( this . parent ?. afterEachHooks . length > 0 ) {
415
+ await this . #runHooks( this . parent . afterEachHooks ) ;
416
+ }
417
+
375
418
// Clean up the test. Then, try to report the results and execute any
376
419
// tests that were pending due to available concurrency.
377
- this . postRun ( ) ;
420
+ await this . postRun ( ) ;
378
421
}
379
422
380
423
postRun ( ) {
@@ -413,7 +456,7 @@ class Test extends AsyncResource {
413
456
this . parent . activeSubtests -- ;
414
457
this . parent . addReadySubtest ( this ) ;
415
458
this . parent . processReadySubtestRange ( false ) ;
416
- this . parent . processPendingSubtests ( ) ;
459
+ return this . parent . processPendingSubtests ( ) ;
417
460
}
418
461
}
419
462
@@ -473,10 +516,23 @@ class Test extends AsyncResource {
473
516
}
474
517
}
475
518
519
+ class TestHook extends Test {
520
+ constructor ( fn , options ) {
521
+ if ( options === null || typeof options !== 'object' ) {
522
+ options = kEmptyObject ;
523
+ }
524
+ super ( { fn, ...options } ) ;
525
+ }
526
+ getRunArgs ( testContext ) {
527
+ return testContext ;
528
+ }
529
+ }
530
+
476
531
class ItTest extends Test {
477
532
constructor ( opt ) { super ( opt ) ; } // eslint-disable-line no-useless-constructor
478
533
getRunArgs ( ) {
479
- return { ctx : { } , args : [ ] } ;
534
+ const ctx = new TestContext ( this ) ;
535
+ return { ctx, args : [ ] } ;
480
536
}
481
537
}
482
538
class Suite extends Test {
0 commit comments