@@ -142,117 +142,36 @@ testGateReact19('handles suspense with error boundary in fake timers', async ()
142
142
expect ( screen . queryByText ( 'Loading...' ) ) . not . toBeOnTheScreen ( ) ;
143
143
} ) ;
144
144
145
- function AnimationSuspending ( ) {
146
- const [ progress , setProgress ] = React . useState ( 0 ) ;
145
+ function MultiComponentSuspense ( ) {
146
+ let resolveFirst : ( value : unknown ) => void ;
147
+ let resolveSecond : ( value : unknown ) => void ;
147
148
148
- React . useEffect ( ( ) => {
149
- const animate = ( ) => {
150
- setProgress ( p => {
151
- if ( p >= 100 ) return 100 ;
152
- setTimeout ( animate , 16 ) ; // 60fps
153
- return p + 1 ;
154
- } ) ;
155
- } ;
156
- animate ( ) ;
157
- } , [ ] ) ;
158
-
159
- if ( progress < 100 ) {
160
- const promise = new Promise ( ( ) => { } ) ; // Suspend until animation complete
161
- React . use ( promise ) ;
162
- }
163
-
164
- return < View testID = "animation-complete" /> ;
165
- }
166
-
167
- testGateReact19 ( 'handles animation-like suspense with fake timers' , async ( ) => {
168
- await renderAsync (
169
- < React . Suspense fallback = { < Text > Animating...</ Text > } >
170
- < AnimationSuspending />
171
- </ React . Suspense > ,
172
- ) ;
173
-
174
- expect ( screen . getByText ( 'Animating...' ) ) . toBeOnTheScreen ( ) ;
175
- expect ( screen . queryByTestId ( 'animation-complete' ) ) . not . toBeOnTheScreen ( ) ;
176
-
177
- // Should complete after animation finishes
178
- expect ( await screen . findByTestId ( 'animation-complete' ) ) . toBeOnTheScreen ( ) ;
179
- expect ( screen . queryByText ( 'Animating...' ) ) . not . toBeOnTheScreen ( ) ;
180
- } ) ;
181
-
182
- function RetryingSuspending ( { maxRetries = 3 } : { maxRetries ?: number } ) {
183
- const [ retries , setRetries ] = React . useState ( 0 ) ;
184
-
185
- const promise = React . useMemo ( ( ) => {
186
- if ( retries < maxRetries ) {
187
- // Simulate a failing request that retries
188
- setTimeout ( ( ) => setRetries ( r => r + 1 ) , 100 ) ;
189
- return new Promise ( ( ) => { } ) ; // Never resolves, will retry
190
- }
191
- // Success case
192
- return Promise . resolve ( 'success' ) ;
193
- } , [ retries , maxRetries ] ) ;
149
+ const firstPromise = new Promise ( ( resolve ) => {
150
+ resolveFirst = resolve ;
151
+ } ) ;
152
+ const secondPromise = new Promise ( ( resolve ) => {
153
+ resolveSecond = resolve ;
154
+ } ) ;
194
155
195
- const data = React . use ( promise ) ;
196
- return < View testID = { `retry-content-${ data } ` } /> ;
197
- }
198
-
199
- testGateReact19 ( 'handles retry logic with fake timers' , async ( ) => {
200
- await renderAsync (
201
- < React . Suspense fallback = { < Text > Retrying...</ Text > } >
202
- < RetryingSuspending maxRetries = { 2 } />
203
- </ React . Suspense > ,
204
- ) ;
205
-
206
- expect ( screen . getByText ( 'Retrying...' ) ) . toBeOnTheScreen ( ) ;
207
- expect ( screen . queryByTestId ( 'retry-content-success' ) ) . not . toBeOnTheScreen ( ) ;
208
-
209
- // Should eventually succeed after retries
210
- expect ( await screen . findByTestId ( 'retry-content-success' ) ) . toBeOnTheScreen ( ) ;
211
- expect ( screen . queryByText ( 'Retrying...' ) ) . not . toBeOnTheScreen ( ) ;
212
- } ) ;
213
-
214
- function CascadingSuspending ( { level } : { level : number } ) {
215
- const delay = level * 50 ;
216
- const promise = React . useMemo ( ( ) =>
217
- new Promise ( ( resolve ) => {
218
- setTimeout ( ( ) => resolve ( level ) , delay ) ;
219
- } ) , [ delay , level ]
156
+ return (
157
+ < View >
158
+ < React . Suspense fallback = { < Text > First Loading...</ Text > } >
159
+ < Suspending promise = { firstPromise } />
160
+ </ React . Suspense >
161
+ < React . Suspense fallback = { < Text > Second Loading...</ Text > } >
162
+ < View testID = "second-boundary" >
163
+ < Suspending promise = { secondPromise } />
164
+ </ View >
165
+ </ React . Suspense >
166
+ </ View >
220
167
) ;
221
-
222
- const data = React . use ( promise ) ;
223
-
224
- if ( level > 1 ) {
225
- return (
226
- < View >
227
- < View testID = { `cascade-${ data } ` } />
228
- < React . Suspense fallback = { < Text > Cascade Loading { level - 1 } ...</ Text > } >
229
- < CascadingSuspending level = { level - 1 } />
230
- </ React . Suspense >
231
- </ View >
232
- ) ;
233
- }
234
-
235
- return < View testID = { `cascade-${ data } ` } /> ;
236
168
}
237
169
238
- testGateReact19 ( 'handles cascading suspense with fake timers' , async ( ) => {
239
- await renderAsync (
240
- < React . Suspense fallback = { < Text > Cascade Loading 3...</ Text > } >
241
- < CascadingSuspending level = { 3 } />
242
- </ React . Suspense > ,
243
- ) ;
244
-
245
- expect ( screen . getByText ( 'Cascade Loading 3...' ) ) . toBeOnTheScreen ( ) ;
246
-
247
- // Should resolve level by level
248
- expect ( await screen . findByTestId ( 'cascade-3' ) ) . toBeOnTheScreen ( ) ;
249
- expect ( screen . getByText ( 'Cascade Loading 2...' ) ) . toBeOnTheScreen ( ) ;
170
+ testGateReact19 ( 'handles multiple independent suspense boundaries' , async ( ) => {
171
+ await renderAsync ( < MultiComponentSuspense /> ) ;
250
172
251
- expect ( await screen . findByTestId ( 'cascade-2' ) ) . toBeOnTheScreen ( ) ;
252
- expect ( screen . getByText ( 'Cascade Loading 1...' ) ) . toBeOnTheScreen ( ) ;
253
-
254
- expect ( await screen . findByTestId ( 'cascade-1' ) ) . toBeOnTheScreen ( ) ;
255
- expect ( screen . queryByText ( 'Cascade Loading 1...' ) ) . not . toBeOnTheScreen ( ) ;
256
- expect ( screen . queryByText ( 'Cascade Loading 2...' ) ) . not . toBeOnTheScreen ( ) ;
257
- expect ( screen . queryByText ( 'Cascade Loading 3...' ) ) . not . toBeOnTheScreen ( ) ;
173
+ expect ( screen . getByText ( 'First Loading...' ) ) . toBeOnTheScreen ( ) ;
174
+ expect ( screen . getByText ( 'Second Loading...' ) ) . toBeOnTheScreen ( ) ;
175
+ expect ( screen . queryByTestId ( 'content' ) ) . not . toBeOnTheScreen ( ) ;
176
+ expect ( screen . queryByTestId ( 'second-boundary' ) ) . not . toBeOnTheScreen ( ) ;
258
177
} ) ;
0 commit comments