@@ -34,6 +34,25 @@ const waitForElement = async <T extends Element>(
3434 throw new Error ( `Timed out waiting for selector: ${ selector } ` )
3535}
3636
37+ const waitForExpectation = async ( assertion : ( ) => void , timeoutMs = 1_000 ) : Promise < void > => {
38+ const deadline = Date . now ( ) + timeoutMs
39+ let lastError : unknown
40+
41+ while ( Date . now ( ) < deadline ) {
42+ try {
43+ assertion ( )
44+ return
45+ } catch ( error ) {
46+ lastError = error
47+ await tick ( 10 )
48+ }
49+ }
50+
51+ throw lastError instanceof Error
52+ ? lastError
53+ : new Error ( 'Timed out waiting for asynchronous expectation.' )
54+ }
55+
3756afterEach ( ( ) => {
3857 const runtimeHost = globalThis as { __FICT_DEV__ ?: boolean }
3958 runtimeHost . __FICT_DEV__ = undefined
@@ -222,15 +241,19 @@ describe('reactify$', () => {
222241
223242 const consoleSpy = vi . spyOn ( console , 'error' ) . mockImplementation ( ( ) => { } )
224243 const dispose = render ( ( ) => ( { type : Remote , props : { label : 'retry' , count : 7 } } ) , container )
225- await tick ( 20 )
226- expect ( container . textContent ) . not . toContain ( 'retry:7' )
227-
228- await tick ( 140 )
229- expect ( attempts ) . toBe ( 2 )
230- expect ( container . textContent ) . toContain ( 'retry:7' )
231-
232- dispose ( )
233- consoleSpy . mockRestore ( )
244+ try {
245+ expect ( container . textContent ) . not . toContain ( 'retry:7' )
246+
247+ await waitForExpectation ( ( ) => {
248+ expect ( attempts ) . toBe ( 2 )
249+ } )
250+ await waitForExpectation ( ( ) => {
251+ expect ( container . textContent ) . toContain ( 'retry:7' )
252+ } )
253+ } finally {
254+ dispose ( )
255+ consoleSpy . mockRestore ( )
256+ }
234257 } )
235258} )
236259
@@ -283,17 +306,19 @@ describe('installReactIslands', () => {
283306 document . body . appendChild ( host )
284307
285308 const stop = installReactIslands ( )
286- await tick ( 30 )
287-
288- expect ( counters . __FICT_REACT_MOUNT_COUNT__ ) . toBe ( 1 )
289- expect ( counters . __FICT_REACT_UNMOUNT_COUNT__ ) . toBe ( 0 )
290-
291- host . remove ( )
292- await tick ( 30 )
293-
294- expect ( counters . __FICT_REACT_UNMOUNT_COUNT__ ) . toBe ( 1 )
295-
296- stop ( )
309+ try {
310+ await waitForExpectation ( ( ) => {
311+ expect ( counters . __FICT_REACT_MOUNT_COUNT__ ) . toBe ( 1 )
312+ } )
313+ expect ( counters . __FICT_REACT_UNMOUNT_COUNT__ ) . toBe ( 0 )
314+
315+ host . remove ( )
316+ await waitForExpectation ( ( ) => {
317+ expect ( counters . __FICT_REACT_UNMOUNT_COUNT__ ) . toBe ( 1 )
318+ } )
319+ } finally {
320+ stop ( )
321+ }
297322 } )
298323
299324 it ( 'rebuilds island runtime when qrl attribute changes' , async ( ) => {
@@ -407,15 +432,19 @@ describe('installReactIslands', () => {
407432
408433 const consoleSpy = vi . spyOn ( console , 'error' ) . mockImplementation ( ( ) => { } )
409434 const stop = installReactIslands ( )
410- await tick ( 20 )
411- expect ( host . textContent ) . not . toContain ( 'retry-loader:9' )
412-
413- await tick ( 140 )
414- expect ( attempts ) . toBe ( 2 )
415- expect ( host . textContent ) . toContain ( 'retry-loader:9' )
416-
417- stop ( )
418- consoleSpy . mockRestore ( )
435+ try {
436+ expect ( host . textContent ) . not . toContain ( 'retry-loader:9' )
437+
438+ await waitForExpectation ( ( ) => {
439+ expect ( attempts ) . toBe ( 2 )
440+ } )
441+ await waitForExpectation ( ( ) => {
442+ expect ( host . textContent ) . toContain ( 'retry-loader:9' )
443+ } )
444+ } finally {
445+ stop ( )
446+ consoleSpy . mockRestore ( )
447+ }
419448 } )
420449
421450 it ( 'warns once per immutable host attribute mutation in dev runtime' , async ( ) => {
0 commit comments