@@ -36,6 +36,32 @@ const testOnlyFlag = !isTestRunner && getOptionValue('--test-only');
3636// TODO(cjihrig): Use uv_available_parallelism() once it lands.
3737const rootConcurrency = isTestRunner ? cpus ( ) . length : 1 ;
3838
39+
40+ function parseTestProps ( parent , name , options , fn , overrides ) {
41+ if ( typeof name === 'function' ) {
42+ fn = name ;
43+ } else if ( name !== null && typeof name === 'object' ) {
44+ fn = options ;
45+ options = name ;
46+ } else if ( typeof options === 'function' ) {
47+ fn = options ;
48+ }
49+
50+ if ( options === null || typeof options !== 'object' ) {
51+ options = kEmptyObject ;
52+ }
53+
54+ // If this test has already ended, attach this test to the root test so
55+ // that the error can be properly reported.
56+ if ( parent . finished ) {
57+ while ( parent . parent !== null ) {
58+ parent = parent . parent ;
59+ }
60+ }
61+
62+ return { fn, name, parent, ...options , ...overrides } ;
63+ }
64+
3965class TestContext {
4066 #test;
4167
@@ -189,34 +215,11 @@ class Test extends AsyncResource {
189215 }
190216 }
191217
192- createSubtest ( name , options , fn ) {
193- if ( typeof name === 'function' ) {
194- fn = name ;
195- } else if ( name !== null && typeof name === 'object' ) {
196- fn = options ;
197- options = name ;
198- } else if ( typeof options === 'function' ) {
199- fn = options ;
200- }
218+ createSubtest ( ...props ) {
219+ const test = new Test ( parseTestProps ( this , ...props ) ) ;
201220
202- if ( options === null || typeof options !== 'object' ) {
203- options = kEmptyObject ;
204- }
205-
206- let parent = this ;
207-
208- // If this test has already ended, attach this test to the root test so
209- // that the error can be properly reported.
210- if ( this . finished ) {
211- while ( parent . parent !== null ) {
212- parent = parent . parent ;
213- }
214- }
215-
216- const test = new Test ( { fn, name, parent, ...options } ) ;
217-
218- if ( parent . waitingOn === 0 ) {
219- parent . waitingOn = test . testNumber ;
221+ if ( test . parent . waitingOn === 0 ) {
222+ test . parent . waitingOn = test . testNumber ;
220223 }
221224
222225 if ( this . finished ) {
@@ -228,10 +231,22 @@ class Test extends AsyncResource {
228231 ) ;
229232 }
230233
231- ArrayPrototypePush ( parent . subtests , test ) ;
234+ ArrayPrototypePush ( test . parent . subtests , test ) ;
232235 return test ;
233236 }
234237
238+ createSubSuite ( ...props ) {
239+ // eslint-disable-next-line no-use-before-define
240+ const suite = new Suite ( parseTestProps ( this , ...props ) ) ;
241+
242+ if ( suite . parent . waitingOn === 0 ) {
243+ suite . parent . waitingOn = suite . testNumber ;
244+ }
245+
246+ ArrayPrototypePush ( suite . parent . subtests , suite ) ;
247+ return suite ;
248+ }
249+
235250 cancel ( ) {
236251 if ( this . endTime !== null ) {
237252 return ;
@@ -448,5 +463,19 @@ class Test extends AsyncResource {
448463 }
449464 }
450465}
466+ class Suite extends Test {
467+ constructor ( options ) {
468+ super ( options ) ;
469+
470+ this . runInAsyncScope ( this . fn ) ;
471+ this . fn = ( ) => { } ;
472+ }
473+ async run ( ) {
474+ for ( const subtest of this . subtests ) {
475+ await subtest . run ( ) ;
476+ }
477+ await super . run ( ) ;
478+ }
479+ }
451480
452481module . exports = { kDefaultIndent, kSubtestsFailed, kTestCodeFailure, Test } ;
0 commit comments