@@ -118,40 +118,7 @@ function NestedSuspending({ promise }: { promise: Promise<unknown> }) {
118
118
) ;
119
119
}
120
120
121
- testGateReact19 ( 'handles nested suspense boundaries' , async ( ) => {
122
- let resolveOuterPromise : ( value : unknown ) => void ;
123
- const outerPromise = new Promise ( ( resolve ) => {
124
- resolveOuterPromise = resolve ;
125
- } ) ;
126
-
127
- await renderAsync (
128
- < React . Suspense fallback = { < Text > Outer Loading...</ Text > } >
129
- < NestedSuspending promise = { outerPromise } />
130
- </ React . Suspense > ,
131
- ) ;
132
-
133
- expect ( screen . getByText ( 'Outer Loading...' ) ) . toBeOnTheScreen ( ) ;
134
- expect ( screen . queryByText ( 'Inner Loading...' ) ) . not . toBeOnTheScreen ( ) ;
135
-
136
- // eslint-disable-next-line require-await
137
- await act ( async ( ) => resolveOuterPromise ( null ) ) ;
138
-
139
- expect ( screen . getByTestId ( 'inner-resolved' ) ) . toBeOnTheScreen ( ) ;
140
- expect ( screen . queryByText ( 'Outer Loading...' ) ) . not . toBeOnTheScreen ( ) ;
141
- expect ( screen . queryByText ( 'Inner Loading...' ) ) . not . toBeOnTheScreen ( ) ;
142
- } ) ;
143
-
144
- function FirstSuspending ( { promise } : { promise : Promise < unknown > } ) {
145
- React . use ( promise ) ;
146
- return < View testID = "first-resolved" /> ;
147
- }
148
-
149
- function SecondSuspending ( { promise } : { promise : Promise < unknown > } ) {
150
- React . use ( promise ) ;
151
- return < View testID = "second-resolved" /> ;
152
- }
153
-
154
- testGateReact19 ( 'handles multiple suspending promises in same boundary' , async ( ) => {
121
+ testGateReact19 ( 'handles multiple suspense boundaries independently' , async ( ) => {
155
122
let resolvePromise1 : ( value : unknown ) => void ;
156
123
let resolvePromise2 : ( value : unknown ) => void ;
157
124
@@ -163,93 +130,33 @@ testGateReact19('handles multiple suspending promises in same boundary', async (
163
130
} ) ;
164
131
165
132
await renderAsync (
166
- < React . Suspense fallback = { < Text > Multiple Loading...</ Text > } >
167
- < FirstSuspending promise = { promise1 } />
168
- < SecondSuspending promise = { promise2 } />
169
- </ React . Suspense > ,
133
+ < View >
134
+ < React . Suspense fallback = { < Text > First Loading...</ Text > } >
135
+ < Suspending promise = { promise1 } />
136
+ </ React . Suspense >
137
+ < React . Suspense fallback = { < Text > Second Loading...</ Text > } >
138
+ < View testID = "second-boundary" >
139
+ < Suspending promise = { promise2 } />
140
+ </ View >
141
+ </ React . Suspense >
142
+ </ View >
170
143
) ;
171
144
172
- expect ( screen . getByText ( 'Multiple Loading...' ) ) . toBeOnTheScreen ( ) ;
173
- expect ( screen . queryByTestId ( 'first-resolved' ) ) . not . toBeOnTheScreen ( ) ;
174
- expect ( screen . queryByTestId ( 'second-resolved' ) ) . not . toBeOnTheScreen ( ) ;
145
+ expect ( screen . getByText ( 'First Loading...' ) ) . toBeOnTheScreen ( ) ;
146
+ expect ( screen . getByText ( 'Second Loading...' ) ) . toBeOnTheScreen ( ) ;
147
+ expect ( screen . queryByTestId ( 'content' ) ) . not . toBeOnTheScreen ( ) ;
148
+ expect ( screen . queryByTestId ( 'second-boundary' ) ) . not . toBeOnTheScreen ( ) ;
175
149
176
- // Resolve first promise - should still be loading because second is pending
150
+ // Resolve first promise
177
151
// eslint-disable-next-line require-await
178
152
await act ( async ( ) => resolvePromise1 ( null ) ) ;
179
- expect ( screen . getByText ( 'Multiple Loading... ') ) . toBeOnTheScreen ( ) ;
180
- expect ( screen . queryByTestId ( 'first-resolved ') ) . not . toBeOnTheScreen ( ) ;
181
- expect ( screen . queryByTestId ( 'second-resolved ') ) . not . toBeOnTheScreen ( ) ;
153
+ expect ( screen . getByTestId ( 'content ') ) . toBeOnTheScreen ( ) ;
154
+ expect ( screen . queryByText ( 'First Loading... ') ) . not . toBeOnTheScreen ( ) ;
155
+ expect ( screen . getByText ( 'Second Loading... ') ) . toBeOnTheScreen ( ) ;
182
156
183
- // Resolve second promise - should now render all content
157
+ // Resolve second promise
184
158
// eslint-disable-next-line require-await
185
159
await act ( async ( ) => resolvePromise2 ( null ) ) ;
186
- expect ( screen . getByTestId ( 'first-resolved' ) ) . toBeOnTheScreen ( ) ;
187
- expect ( screen . getByTestId ( 'second-resolved' ) ) . toBeOnTheScreen ( ) ;
188
- expect ( screen . queryByText ( 'Multiple Loading...' ) ) . not . toBeOnTheScreen ( ) ;
189
- } ) ;
190
-
191
- function ConditionalSuspending ( { shouldSuspend, promiseResolver } : { shouldSuspend : boolean ; promiseResolver ?: ( ) => void } ) {
192
- if ( shouldSuspend ) {
193
- const promise = React . useMemo ( ( ) => new Promise < void > ( ( resolve ) => {
194
- if ( promiseResolver ) {
195
- promiseResolver = resolve ;
196
- }
197
- } ) , [ promiseResolver ] ) ;
198
- React . use ( promise ) ;
199
- }
200
- return < View testID = "conditional-content" /> ;
201
- }
202
-
203
- testGateReact19 ( 'handles conditional suspense' , async ( ) => {
204
- const result = await renderAsync (
205
- < React . Suspense fallback = { < Text > Conditional Loading...</ Text > } >
206
- < ConditionalSuspending shouldSuspend = { false } />
207
- </ React . Suspense > ,
208
- ) ;
209
-
210
- // Should render immediately when not suspending
211
- expect ( screen . getByTestId ( 'conditional-content' ) ) . toBeOnTheScreen ( ) ;
212
- expect ( screen . queryByText ( 'Conditional Loading...' ) ) . not . toBeOnTheScreen ( ) ;
213
-
214
- // Re-render with suspense - this creates a new component that will suspend
215
- await result . rerenderAsync (
216
- < React . Suspense fallback = { < Text > Conditional Loading...</ Text > } >
217
- < ConditionalSuspending shouldSuspend = { true } />
218
- </ React . Suspense > ,
219
- ) ;
220
-
221
- // Should now be suspended
222
- expect ( screen . getByText ( 'Conditional Loading...' ) ) . toBeOnTheScreen ( ) ;
223
- expect ( screen . queryByTestId ( 'conditional-content' ) ) . not . toBeOnTheScreen ( ) ;
224
- } ) ;
225
-
226
- function SuspendingWithState ( ) {
227
- const [ isReady , setIsReady ] = React . useState ( false ) ;
228
-
229
- React . useEffect ( ( ) => {
230
- const timer = setTimeout ( ( ) => setIsReady ( true ) , 100 ) ;
231
- return ( ) => clearTimeout ( timer ) ;
232
- } , [ ] ) ;
233
-
234
- if ( ! isReady ) {
235
- const promise = new Promise ( ( ) => { } ) ; // Never resolves
236
- React . use ( promise ) ;
237
- }
238
-
239
- return < View testID = "state-ready-content" /> ;
240
- }
241
-
242
- testGateReact19 ( 'handles suspense with state updates' , async ( ) => {
243
- await renderAsync (
244
- < React . Suspense fallback = { < Text > State Loading...</ Text > } >
245
- < SuspendingWithState />
246
- </ React . Suspense > ,
247
- ) ;
248
-
249
- expect ( screen . getByText ( 'State Loading...' ) ) . toBeOnTheScreen ( ) ;
250
- expect ( screen . queryByTestId ( 'state-ready-content' ) ) . not . toBeOnTheScreen ( ) ;
251
-
252
- // Wait for state update to resolve suspense
253
- expect ( await screen . findByTestId ( 'state-ready-content' ) ) . toBeOnTheScreen ( ) ;
254
- expect ( screen . queryByText ( 'State Loading...' ) ) . not . toBeOnTheScreen ( ) ;
160
+ expect ( screen . getByTestId ( 'second-boundary' ) ) . toBeOnTheScreen ( ) ;
161
+ expect ( screen . queryByText ( 'Second Loading...' ) ) . not . toBeOnTheScreen ( ) ;
255
162
} ) ;
0 commit comments