@@ -52,7 +52,7 @@ function waitFor(
5252 } = { } ,
5353) {
5454 function getElementError ( message ) {
55- const prettifiedDOM = prettyFormat ( container )
55+ const prettifiedDOM = prettyFormat . format ( container )
5656 const error = new Error (
5757 [ message , prettifiedDOM ] . filter ( Boolean ) . join ( '\n\n' ) ,
5858 )
@@ -126,6 +126,22 @@ function waitFor(
126126 return result
127127 }
128128
129+ const { MutationObserver} = getWindowFromNode ( container )
130+ const observer = new MutationObserver ( ( ) => {
131+ const result = checkCallbackWithExpensiveErrorDiagnosticsDisabled ( )
132+ if ( typeof result ?. then === 'function' ) {
133+ result . then ( resolvedValue => {
134+ onDone ( null , resolvedValue )
135+ } )
136+ } else {
137+ onDone ( null , result )
138+ }
139+ } )
140+ observer . observe ( container , mutationObserverOptions )
141+ controller . signal . addEventListener ( 'abort' , ( ) => {
142+ observer . disconnect ( )
143+ } )
144+
129145 waitForWeb ( checkCallbackWithExpensiveErrorDiagnosticsDisabled , {
130146 clock,
131147 interval,
@@ -147,18 +163,77 @@ function waitFor(
147163 }
148164 } ,
149165 )
150-
151- const { MutationObserver} = getWindowFromNode ( container )
152- const observer = new MutationObserver (
153- checkCallbackWithExpensiveErrorDiagnosticsDisabled ,
154- )
155- observer . observe ( container , mutationObserverOptions )
156- controller . signal . addEventListener ( 'abort' , ( ) => {
157- observer . disconnect ( )
158- } )
159166 } )
160167}
161168
162- test ( 'runs' , async ( ) => {
163- await expect ( waitFor ( ( ) => { } ) ) . resolves . toBeUndefined ( )
169+ describe . each ( [
170+ [ 'real timers' , ( ) => jest . useRealTimers ( ) ] ,
171+ [ 'fake legacy timers' , ( ) => jest . useFakeTimers ( 'legacy' ) ] ,
172+ [ 'fake modern timers' , ( ) => jest . useFakeTimers ( 'modern' ) ] ,
173+ ] ) ( 'waitFor DOM reference implementation using %s' , ( label , useTimers ) => {
174+ beforeEach ( ( ) => {
175+ useTimers ( )
176+ } )
177+
178+ afterEach ( ( ) => {
179+ jest . useRealTimers ( )
180+ } )
181+
182+ test ( 'void callback' , async ( ) => {
183+ await expect ( waitFor ( ( ) => { } ) ) . resolves . toBeUndefined ( )
184+ } )
185+
186+ test ( 'callback passes after timeout' , async ( ) => {
187+ let state = 'pending'
188+ setTimeout ( ( ) => {
189+ state = 'done'
190+ } , 10 )
191+
192+ await expect (
193+ waitFor (
194+ ( ) => {
195+ if ( state !== 'done' ) {
196+ throw new Error ( 'Not done' )
197+ }
198+ } ,
199+ { interval : 5 } ,
200+ ) ,
201+ ) . resolves . toBeUndefined ( )
202+ } )
203+
204+ test ( 'timeout' , async ( ) => {
205+ const state = 'pending'
206+
207+ await expect (
208+ waitFor (
209+ ( ) => {
210+ if ( state !== 'done' ) {
211+ throw new Error ( 'Not done' )
212+ }
213+ } ,
214+ { timeout : 10 } ,
215+ ) ,
216+ ) . rejects . toThrowErrorMatchingSnapshot ( )
217+ } )
218+
219+ test ( 'can resolve early due to mutations' , async ( ) => {
220+ const container = document . createElement ( 'div' )
221+
222+ setTimeout ( ( ) => {
223+ container . appendChild ( document . createTextNode ( 'Done' ) )
224+ } , 50 )
225+
226+ const p = waitFor (
227+ ( ) => {
228+ if ( container . textContent !== 'Done' ) {
229+ throw new Error ( 'Not done' )
230+ }
231+ return container . textContent
232+ } ,
233+ // this would never resolve with real timers without using a MutationObserver
234+ { container, interval : 200 , timeout : 200 } ,
235+ )
236+
237+ await expect ( p ) . resolves . toBe ( 'Done' )
238+ } )
164239} )
0 commit comments