11/* globals jest */
22import { act } from './act' ;
3+ import { addToCleanupQueue , removeFromCleanupQueue } from './cleanup' ;
34import { getConfig } from './config' ;
45import { flushMicroTasks } from './flush-micro-tasks' ;
56import { copyStackTraceIfNeeded , ErrorWithStack } from './helpers/errors' ;
@@ -35,11 +36,13 @@ function waitForInternal<T>(
3536
3637 // eslint-disable-next-line no-async-promise-executor
3738 return new Promise ( async ( resolve , reject ) => {
38- let lastError : unknown , intervalId : ReturnType < typeof setTimeout > ;
39+ let lastError : unknown ;
40+ let intervalId : ReturnType < typeof setInterval > | null = null ;
3941 let finished = false ;
4042 let promiseStatus = 'idle' ;
4143
42- let overallTimeoutTimer : NodeJS . Timeout | null = null ;
44+ let overallTimeoutTimer : ReturnType < typeof setTimeout > | null = null ;
45+ const cleanupQueueCallback = ( ) => finalizeWaitFor ( { rejectOnAbort : true } ) ;
4346
4447 const fakeTimersType = getJestFakeTimersType ( ) ;
4548
@@ -94,19 +97,42 @@ function waitForInternal<T>(
9497 } else {
9598 overallTimeoutTimer = setTimeout ( handleTimeout , timeout ) ;
9699 intervalId = setInterval ( checkRealTimersCallback , interval ) ;
100+ addToCleanupQueue ( cleanupQueueCallback ) ;
97101 checkExpectation ( ) ;
98102 }
99103
100- function onDone ( done : { type : 'result' ; result : T } | { type : 'error' ; error : unknown } ) {
104+ function finalizeWaitFor ( { rejectOnAbort = false } = { } ) {
105+ /* istanbul ignore next */
106+ if ( finished ) {
107+ return ;
108+ }
109+
101110 finished = true ;
111+
112+ removeFromCleanupQueue ( cleanupQueueCallback ) ;
113+
114+ if ( rejectOnAbort ) {
115+ reject ( new Error ( 'waitFor was aborted by cleanup' ) ) ;
116+ }
117+
102118 if ( overallTimeoutTimer ) {
103119 clearTimeout ( overallTimeoutTimer ) ;
120+ overallTimeoutTimer = null ;
104121 }
105122
106- if ( ! fakeTimersType ) {
123+ if ( intervalId ) {
107124 clearInterval ( intervalId ) ;
125+ intervalId = null ;
126+ }
127+ }
128+
129+ function onDone ( done : { type : 'result' ; result : T } | { type : 'error' ; error : unknown } ) {
130+ if ( finished ) {
131+ return ;
108132 }
109133
134+ finalizeWaitFor ( ) ;
135+
110136 if ( done . type === 'error' ) {
111137 reject ( done . error ) ;
112138 } else {
@@ -120,7 +146,7 @@ function waitForInternal<T>(
120146 `Changed from using real timers to fake timers while using waitFor. This is not allowed and will result in very strange behavior. Please ensure you're awaiting all async things your test is doing before changing to fake timers. For more info, please go to https://github.com/testing-library/dom-testing-library/issues/830` ,
121147 ) ;
122148 copyStackTraceIfNeeded ( error , stackTraceError ) ;
123- return reject ( error ) ;
149+ return onDone ( { type : ' error' , error } ) ;
124150 } else {
125151 return checkExpectation ( ) ;
126152 }
@@ -176,13 +202,19 @@ function waitForInternal<T>(
176202 error = new Error ( 'Timed out in waitFor.' ) ;
177203 copyStackTraceIfNeeded ( error , stackTraceError ) ;
178204 }
205+
206+ let errorForRejection : unknown = error ;
179207 if ( typeof onTimeout === 'function' ) {
180- const result = onTimeout ( error ) ;
181- if ( result ) {
182- error = result ;
208+ try {
209+ const result = onTimeout ( error ) ;
210+ if ( result ) {
211+ errorForRejection = result ;
212+ }
213+ } catch ( onTimeoutError ) {
214+ errorForRejection = onTimeoutError ;
183215 }
184216 }
185- onDone ( { type : 'error' , error } ) ;
217+ onDone ( { type : 'error' , error : errorForRejection } ) ;
186218 }
187219 } ) ;
188220}
0 commit comments