@@ -2,8 +2,33 @@ import * as React from 'react';
22import { Text , View } from 'react-native' ;
33
44import { render , screen , waitFor } from '..' ;
5+ import { useTimerType } from '../test-utils/timers' ;
56
6- test ( 'waits for query' , async ( ) => {
7+ beforeEach ( ( ) => {
8+ jest . useRealTimers ( ) ;
9+ } ) ;
10+
11+ test ( 'waits for expect() assertion to pass' , async ( ) => {
12+ const mockFunction = jest . fn ( ) ;
13+
14+ function AsyncComponent ( ) {
15+ React . useEffect ( ( ) => {
16+ setTimeout ( ( ) => mockFunction ( ) , 100 ) ;
17+ } , [ ] ) ;
18+
19+ return < View /> ;
20+ }
21+
22+ await render ( < AsyncComponent /> ) ;
23+ await waitFor ( ( ) => expect ( mockFunction ) . toHaveBeenCalled ( ) ) ;
24+ expect ( mockFunction ) . toHaveBeenCalledTimes ( 1 ) ;
25+ } ) ;
26+
27+ test . each ( [
28+ { timerType : 'real' as const } ,
29+ { timerType : 'fake' as const } ,
30+ { timerType : 'fake-legacy' as const } ,
31+ ] ) ( 'waits for query with $timerType timers' , async ( { timerType } ) => {
732 function AsyncComponent ( ) {
833 const [ text , setText ] = React . useState ( 'Loading...' ) ;
934
@@ -14,7 +39,126 @@ test('waits for query', async () => {
1439 return < Text > { text } </ Text > ;
1540 }
1641
42+ useTimerType ( timerType ) ;
1743 await render ( < AsyncComponent /> ) ;
1844 await waitFor ( ( ) => screen . getByText ( 'Loaded' ) ) ;
1945 expect ( screen . getByText ( 'Loaded' ) ) . toBeOnTheScreen ( ) ;
2046} ) ;
47+
48+ test ( 'throws timeout error when condition never becomes true' , async ( ) => {
49+ function Component ( ) {
50+ return < Text > Hello</ Text > ;
51+ }
52+
53+ await render ( < Component /> ) ;
54+ await expect ( waitFor ( ( ) => screen . getByText ( 'Never appears' ) , { timeout : 100 } ) ) . rejects . toThrow (
55+ 'Unable to find an element with text: Never appears' ,
56+ ) ;
57+ } ) ;
58+
59+ test ( 'uses custom error from onTimeout callback when timeout occurs' , async ( ) => {
60+ const customErrorMessage = 'Custom timeout error: Element never appeared' ;
61+
62+ await render ( < View /> ) ;
63+ await expect (
64+ waitFor ( ( ) => screen . getByText ( 'Never appears' ) , {
65+ timeout : 100 ,
66+ onTimeout : ( ) => new Error ( customErrorMessage ) ,
67+ } ) ,
68+ ) . rejects . toThrow ( customErrorMessage ) ;
69+ } ) ;
70+
71+ test ( 'throws TypeError when expectation is not a function' , async ( ) => {
72+ await expect ( waitFor ( null as any ) ) . rejects . toThrow (
73+ 'Received `expectation` arg must be a function' ,
74+ ) ;
75+ } ) ;
76+
77+ test ( 'throws error when switching from real timers to fake timers during waitFor' , async ( ) => {
78+ await render ( < View /> ) ;
79+
80+ const waitForPromise = waitFor ( ( ) => {
81+ // This will never pass, but we'll switch timers before timeout
82+ return screen . getByText ( 'Never appears' ) ;
83+ } ) ;
84+
85+ // Switch to fake timers while waitFor is running
86+ jest . useFakeTimers ( ) ;
87+
88+ await expect ( waitForPromise ) . rejects . toThrow (
89+ 'Changed from using real timers to fake timers while using waitFor' ,
90+ ) ;
91+ } ) ;
92+
93+ test ( 'throws error when switching from fake timers to real timers during waitFor' , async ( ) => {
94+ jest . useFakeTimers ( ) ;
95+ await render ( < View /> ) ;
96+
97+ const waitForPromise = waitFor ( ( ) => {
98+ // This will never pass, but we'll switch timers before timeout
99+ return screen . getByText ( 'Never appears' ) ;
100+ } ) ;
101+
102+ // Switch to real timers while waitFor is running
103+ jest . useRealTimers ( ) ;
104+
105+ await expect ( waitForPromise ) . rejects . toThrow (
106+ 'Changed from using fake timers to real timers while using waitFor' ,
107+ ) ;
108+ } ) ;
109+
110+ test ( 'converts non-Error thrown value to Error when timeout occurs' , async ( ) => {
111+ const errorMessage = 'Custom string error' ;
112+
113+ let caughtError : unknown ;
114+ try {
115+ await waitFor (
116+ ( ) => {
117+ throw errorMessage ;
118+ } ,
119+ { timeout : 50 } ,
120+ ) ;
121+ } catch ( error ) {
122+ caughtError = error ;
123+ }
124+
125+ expect ( caughtError ) . toBeInstanceOf ( Error ) ;
126+ expect ( ( caughtError as Error ) . message ) . toBe ( errorMessage ) ;
127+ } ) ;
128+
129+ test ( 'continues waiting when expectation returns a promise that rejects' , async ( ) => {
130+ let attemptCount = 0 ;
131+ const maxAttempts = 3 ;
132+
133+ await waitFor (
134+ ( ) => {
135+ attemptCount ++ ;
136+ if ( attemptCount < maxAttempts ) {
137+ return Promise . reject ( new Error ( 'Not ready yet' ) ) ;
138+ }
139+ return Promise . resolve ( 'Success' ) ;
140+ } ,
141+ { timeout : 1000 } ,
142+ ) ;
143+
144+ expect ( attemptCount ) . toBe ( maxAttempts ) ;
145+ } ) ;
146+
147+ test ( 'throws timeout error with fake timers when condition never becomes true' , async ( ) => {
148+ jest . useFakeTimers ( ) ;
149+ await render ( < View /> ) ;
150+
151+ const waitForPromise = waitFor ( ( ) => screen . getByText ( 'Never appears' ) , { timeout : 100 } ) ;
152+
153+ await jest . advanceTimersByTimeAsync ( 100 ) ;
154+
155+ await expect ( waitForPromise ) . rejects . toThrow ( 'Unable to find an element with text: Never appears' ) ;
156+ } ) ;
157+
158+ test ( 'throws generic timeout error when promise rejects with falsy value until timeout' , async ( ) => {
159+ await expect (
160+ waitFor ( ( ) => Promise . reject ( null ) , {
161+ timeout : 100 ,
162+ } ) ,
163+ ) . rejects . toThrow ( 'Timed out in waitFor.' ) ;
164+ } ) ;
0 commit comments