@@ -8,7 +8,6 @@ const plur = require('plur');
88const assert = require ( './assert' ) ;
99const formatAssertError = require ( './format-assert-error' ) ;
1010const globals = require ( './globals' ) ;
11- const throwsHelper = require ( './throws-helper' ) ;
1211
1312class SkipApi {
1413 constructor ( test ) {
@@ -60,6 +59,13 @@ class ExecutionContext {
6059
6160 contextRef . context = context ;
6261 }
62+
63+ _throwsArgStart ( assertion , file , line ) {
64+ this . _test . trackThrows ( { assertion, file, line} ) ;
65+ }
66+ _throwsArgEnd ( ) {
67+ this . _test . trackThrows ( null ) ;
68+ }
6369}
6470Object . defineProperty ( ExecutionContext . prototype , 'context' , { enumerable : true } ) ;
6571
@@ -101,9 +107,11 @@ class Test {
101107 this . calledEnd = false ;
102108 this . duration = null ;
103109 this . endCallbackFinisher = null ;
110+ this . finishDueToAttributedError = null ;
104111 this . finishDueToInactivity = null ;
105112 this . finishing = false ;
106113 this . pendingAssertions = [ ] ;
114+ this . pendingThrowsAssertion = null ;
107115 this . planCount = null ;
108116 this . startedAt = 0 ;
109117 }
@@ -204,6 +212,60 @@ class Test {
204212 }
205213 }
206214
215+ trackThrows ( pending ) {
216+ this . pendingThrowsAssertion = pending ;
217+ }
218+
219+ detectImproperThrows ( err ) {
220+ if ( ! this . pendingThrowsAssertion ) {
221+ return false ;
222+ }
223+
224+ const pending = this . pendingThrowsAssertion ;
225+ this . pendingThrowsAssertion = null ;
226+
227+ const values = [ ] ;
228+ if ( err ) {
229+ values . push ( formatAssertError . formatWithLabel ( `The following error was thrown, possibly before \`t.${ pending . assertion } ()\` could be called:` , err ) ) ;
230+ }
231+
232+ this . saveFirstError ( new assert . AssertionError ( {
233+ assertion : pending . assertion ,
234+ fixedSource : { file : pending . file , line : pending . line } ,
235+ improperUsage : true ,
236+ message : `Improper usage of \`t.${ pending . assertion } ()\` detected` ,
237+ stack : err instanceof Error && err . stack ,
238+ values
239+ } ) ) ;
240+ return true ;
241+ }
242+
243+ waitForPendingThrowsAssertion ( ) {
244+ return new Promise ( resolve => {
245+ this . finishDueToAttributedError = ( ) => {
246+ resolve ( this . finishPromised ( ) ) ;
247+ } ;
248+
249+ this . finishDueToInactivity = ( ) => {
250+ this . detectImproperThrows ( ) ;
251+ resolve ( this . finishPromised ( ) ) ;
252+ } ;
253+
254+ // Wait up to a second to see if an error can be attributed to the
255+ // pending assertion.
256+ globals . setTimeout ( ( ) => this . finishDueToInactivity ( ) , 1000 ) . unref ( ) ;
257+ } ) ;
258+ }
259+
260+ attributeLeakedError ( err ) {
261+ if ( ! this . detectImproperThrows ( err ) ) {
262+ return false ;
263+ }
264+
265+ this . finishDueToAttributedError ( ) ;
266+ return true ;
267+ }
268+
207269 callFn ( ) {
208270 try {
209271 return {
@@ -223,13 +285,13 @@ class Test {
223285
224286 const result = this . callFn ( ) ;
225287 if ( ! result . ok ) {
226- throwsHelper ( result . error ) ;
227-
228- this . saveFirstError ( new assert . AssertionError ( {
229- message : 'Error thrown in test' ,
230- stack : result . error instanceof Error && result . error . stack ,
231- values : [ formatAssertError . formatWithLabel ( 'Error:' , result . error ) ]
232- } ) ) ;
288+ if ( ! this . detectImproperThrows ( result . error ) ) {
289+ this . saveFirstError ( new assert . AssertionError ( {
290+ message : 'Error thrown in test' ,
291+ stack : result . error instanceof Error && result . error . stack ,
292+ values : [ formatAssertError . formatWithLabel ( ' Error:' , result . error ) ]
293+ } ) ) ;
294+ }
233295 return this . finish ( ) ;
234296 }
235297
@@ -260,6 +322,10 @@ class Test {
260322 resolve ( this . finishPromised ( ) ) ;
261323 } ;
262324
325+ this . finishDueToAttributedError = ( ) => {
326+ resolve ( this . finishPromised ( ) ) ;
327+ } ;
328+
263329 this . finishDueToInactivity = ( ) => {
264330 this . saveFirstError ( new Error ( '`t.end()` was never called' ) ) ;
265331 resolve ( this . finishPromised ( ) ) ;
@@ -269,6 +335,10 @@ class Test {
269335
270336 if ( promise ) {
271337 return new Promise ( resolve => {
338+ this . finishDueToAttributedError = ( ) => {
339+ resolve ( this . finishPromised ( ) ) ;
340+ } ;
341+
272342 this . finishDueToInactivity = ( ) => {
273343 const err = returnedObservable ?
274344 new Error ( 'Observable returned by test never completed' ) :
@@ -279,13 +349,13 @@ class Test {
279349
280350 promise
281351 . catch ( err => {
282- throwsHelper ( err ) ;
283-
284- this . saveFirstError ( new assert . AssertionError ( {
285- message : 'Rejected promise returned by test' ,
286- stack : err instanceof Error && err . stack ,
287- values : [ formatAssertError . formatWithLabel ( 'Rejection reason:' , err ) ]
288- } ) ) ;
352+ if ( ! this . detectImproperThrows ( err ) ) {
353+ this . saveFirstError ( new assert . AssertionError ( {
354+ message : 'Rejected promise returned by test' ,
355+ stack : err instanceof Error && err . stack ,
356+ values : [ formatAssertError . formatWithLabel ( 'Rejection reason:' , err ) ]
357+ } ) ) ;
358+ }
289359 } )
290360 . then ( ( ) => resolve ( this . finishPromised ( ) ) ) ;
291361 } ) ;
@@ -296,6 +366,11 @@ class Test {
296366
297367 finish ( ) {
298368 this . finishing = true ;
369+
370+ if ( ! this . assertError && this . pendingThrowsAssertion ) {
371+ return this . waitForPendingThrowsAssertion ( ) ;
372+ }
373+
299374 this . verifyPlan ( ) ;
300375 this . verifyAssertions ( ) ;
301376
0 commit comments