@@ -11,6 +11,7 @@ const {
11
11
FunctionPrototype,
12
12
MathMax,
13
13
Number,
14
+ ObjectEntries,
14
15
ObjectSeal,
15
16
PromisePrototypeThen,
16
17
PromiseResolve,
@@ -85,6 +86,7 @@ const {
85
86
testOnlyFlag,
86
87
} = parseCommandLine ( ) ;
87
88
let kResistStopPropagation ;
89
+ let assertObj ;
88
90
let findSourceMap ;
89
91
90
92
const kRunOnceOptions = { __proto__ : null , preserveReturnValue : true } ;
@@ -97,6 +99,19 @@ function lazyFindSourceMap(file) {
97
99
return findSourceMap ( file ) ;
98
100
}
99
101
102
+ function lazyAssertObject ( ) {
103
+ if ( assertObj === undefined ) {
104
+ assertObj = new SafeMap ( ) ;
105
+ const assert = require ( 'assert' ) ;
106
+ for ( const { 0 : key , 1 : value } of ObjectEntries ( assert ) ) {
107
+ if ( typeof value === 'function' ) {
108
+ assertObj . set ( value , key ) ;
109
+ }
110
+ }
111
+ }
112
+ return assertObj ;
113
+ }
114
+
100
115
function stopTest ( timeout , signal ) {
101
116
const deferred = createDeferredPromise ( ) ;
102
117
const abortListener = addAbortListener ( signal , deferred . resolve ) ;
@@ -136,7 +151,25 @@ function stopTest(timeout, signal) {
136
151
return deferred . promise ;
137
152
}
138
153
154
+ class TestPlan {
155
+ constructor ( count ) {
156
+ validateUint32 ( count , 'count' , 0 ) ;
157
+ this . expected = count ;
158
+ this . actual = 0 ;
159
+ }
160
+
161
+ check ( ) {
162
+ if ( this . actual !== this . expected ) {
163
+ throw new ERR_TEST_FAILURE (
164
+ `plan expected ${ this . expected } assertions but received ${ this . actual } ` ,
165
+ kTestCodeFailure ,
166
+ ) ;
167
+ }
168
+ }
169
+ }
170
+
139
171
class TestContext {
172
+ #assert;
140
173
#test;
141
174
142
175
constructor ( test ) {
@@ -163,6 +196,36 @@ class TestContext {
163
196
this . #test. diagnostic ( message ) ;
164
197
}
165
198
199
+ plan ( count ) {
200
+ if ( this . #test. plan !== null ) {
201
+ throw new ERR_TEST_FAILURE (
202
+ 'cannot set plan more than once' ,
203
+ kTestCodeFailure ,
204
+ ) ;
205
+ }
206
+
207
+ this . #test. plan = new TestPlan ( count ) ;
208
+ }
209
+
210
+ get assert ( ) {
211
+ if ( this . #assert === undefined ) {
212
+ const { plan } = this . #test;
213
+ const assertions = lazyAssertObject ( ) ;
214
+ const assert = { __proto__ : null } ;
215
+
216
+ this . #assert = assert ;
217
+ for ( const { 0 : method , 1 : name } of assertions . entries ( ) ) {
218
+ assert [ name ] = ( ...args ) => {
219
+ if ( plan !== null ) {
220
+ plan . actual ++ ;
221
+ }
222
+ return ReflectApply ( method , assert , args ) ;
223
+ } ;
224
+ }
225
+ }
226
+ return this . #assert;
227
+ }
228
+
166
229
get mock ( ) {
167
230
this . #test. mock ??= new MockTracker ( ) ;
168
231
return this . #test. mock ;
@@ -186,6 +249,11 @@ class TestContext {
186
249
loc : getCallerLocation ( ) ,
187
250
} ;
188
251
252
+ const { plan } = this . #test;
253
+ if ( plan !== null ) {
254
+ plan . actual ++ ;
255
+ }
256
+
189
257
const subtest = this . #test. createSubtest (
190
258
// eslint-disable-next-line no-use-before-define
191
259
Test , name , options , fn , overrides ,
@@ -240,7 +308,7 @@ class Test extends AsyncResource {
240
308
super ( 'Test' ) ;
241
309
242
310
let { fn, name, parent, skip } = options ;
243
- const { concurrency, loc, only, timeout, todo, signal } = options ;
311
+ const { concurrency, loc, only, timeout, todo, signal, plan } = options ;
244
312
245
313
if ( typeof fn !== 'function' ) {
246
314
fn = noop ;
@@ -351,6 +419,8 @@ class Test extends AsyncResource {
351
419
this . fn = fn ;
352
420
this . harness = null ; // Configured on the root test by the test harness.
353
421
this . mock = null ;
422
+ this . plan = null ;
423
+ this . expectedAssertions = plan ;
354
424
this . cancelled = false ;
355
425
this . skipped = skip !== undefined && skip !== false ;
356
426
this . isTodo = todo !== undefined && todo !== false ;
@@ -643,6 +713,11 @@ class Test extends AsyncResource {
643
713
644
714
const hookArgs = this . getRunArgs ( ) ;
645
715
const { args, ctx } = hookArgs ;
716
+
717
+ if ( this . plan === null && this . expectedAssertions ) {
718
+ ctx . plan ( this . expectedAssertions ) ;
719
+ }
720
+
646
721
const after = async ( ) => {
647
722
if ( this . hooks . after . length > 0 ) {
648
723
await this . runHook ( 'after' , hookArgs ) ;
@@ -694,7 +769,7 @@ class Test extends AsyncResource {
694
769
this . postRun ( ) ;
695
770
return ;
696
771
}
697
-
772
+ this . plan ?. check ( ) ;
698
773
this . pass ( ) ;
699
774
await afterEach ( ) ;
700
775
await after ( ) ;
0 commit comments