@@ -85,6 +85,7 @@ const {
85
85
testOnlyFlag,
86
86
} = parseCommandLine ( ) ;
87
87
let kResistStopPropagation ;
88
+ let assertObj ;
88
89
let findSourceMap ;
89
90
90
91
function lazyFindSourceMap ( file ) {
@@ -95,6 +96,34 @@ function lazyFindSourceMap(file) {
95
96
return findSourceMap ( file ) ;
96
97
}
97
98
99
+ function lazyAssertObject ( ) {
100
+ if ( assertObj === undefined ) {
101
+ const assert = require ( 'assert' ) ;
102
+
103
+ assertObj = new SafeMap ( [
104
+ [ 'deepEqual' , assert . deepEqual ] ,
105
+ [ 'deepStrictEqual' , assert . deepStrictEqual ] ,
106
+ [ 'doesNotMatch' , assert . doesNotMatch ] ,
107
+ [ 'doesNotReject' , assert . doesNotReject ] ,
108
+ [ 'doesNotThrow' , assert . doesNotThrow ] ,
109
+ [ 'equal' , assert . equal ] ,
110
+ [ 'fail' , assert . fail ] ,
111
+ [ 'ifError' , assert . ifError ] ,
112
+ [ 'match' , assert . match ] ,
113
+ [ 'notDeepEqual' , assert . notDeepEqual ] ,
114
+ [ 'notDeepStrictEqual' , assert . notDeepStrictEqual ] ,
115
+ [ 'notEqual' , assert . notEqual ] ,
116
+ [ 'notStrictEqual' , assert . notStrictEqual ] ,
117
+ [ 'ok' , assert . ok ] ,
118
+ [ 'rejects' , assert . rejects ] ,
119
+ [ 'strictEqual' , assert . strictEqual ] ,
120
+ [ 'throws' , assert . throws ] ,
121
+ ] ) ;
122
+ }
123
+
124
+ return assertObj ;
125
+ }
126
+
98
127
function stopTest ( timeout , signal ) {
99
128
const deferred = createDeferredPromise ( ) ;
100
129
const abortListener = addAbortListener ( signal , deferred . resolve ) ;
@@ -134,7 +163,25 @@ function stopTest(timeout, signal) {
134
163
return deferred . promise ;
135
164
}
136
165
166
+ class TestPlan {
167
+ constructor ( count ) {
168
+ validateUint32 ( count , 'count' , 0 ) ;
169
+ this . expected = count ;
170
+ this . actual = 0 ;
171
+ }
172
+
173
+ check ( ) {
174
+ if ( this . actual !== this . expected ) {
175
+ throw new ERR_TEST_FAILURE (
176
+ `plan expected ${ this . expected } assertions but received ${ this . actual } ` ,
177
+ kTestCodeFailure ,
178
+ ) ;
179
+ }
180
+ }
181
+ }
182
+
137
183
class TestContext {
184
+ #assert;
138
185
#test;
139
186
140
187
constructor ( test ) {
@@ -161,6 +208,38 @@ class TestContext {
161
208
this . #test. diagnostic ( message ) ;
162
209
}
163
210
211
+ plan ( count ) {
212
+ if ( this . #test. plan !== null ) {
213
+ throw new ERR_TEST_FAILURE (
214
+ 'cannot set plan more than once' ,
215
+ kTestCodeFailure ,
216
+ ) ;
217
+ }
218
+
219
+ this . #test. plan = new TestPlan ( count ) ;
220
+ }
221
+
222
+ get assert ( ) {
223
+ if ( this . #assert === undefined ) {
224
+ const { plan } = this . #test;
225
+ const map = lazyAssertObject ( ) ;
226
+ const assert = { __proto__ : null } ;
227
+
228
+ this . #assert = assert ;
229
+ map . forEach ( ( method , name ) => {
230
+ assert [ name ] = function ( ...args ) {
231
+ if ( plan !== null ) {
232
+ plan . actual ++ ;
233
+ }
234
+
235
+ return ReflectApply ( method , assert , args ) ;
236
+ } ;
237
+ } ) ;
238
+ }
239
+
240
+ return this . #assert;
241
+ }
242
+
164
243
get mock ( ) {
165
244
this . #test. mock ??= new MockTracker ( ) ;
166
245
return this . #test. mock ;
@@ -347,6 +426,7 @@ class Test extends AsyncResource {
347
426
this . fn = fn ;
348
427
this . harness = null ; // Configured on the root test by the test harness.
349
428
this . mock = null ;
429
+ this . plan = null ;
350
430
this . cancelled = false ;
351
431
this . skipped = skip !== undefined && skip !== false ;
352
432
this . isTodo = todo !== undefined && todo !== false ;
@@ -708,6 +788,7 @@ class Test extends AsyncResource {
708
788
return ;
709
789
}
710
790
791
+ this . plan ?. check ( ) ;
711
792
this . pass ( ) ;
712
793
try {
713
794
await afterEach ( ) ;
0 commit comments