Remove RTR from ReactSuspense-test (#28390)
## Summary Cleaning up internal usage of ReactTestRenderer ## How did you test this change? `yarn test packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js`
Jack Pope committed
Feb 20, 2024 at 15:40 UTC
90a0ae101571f58781225dd0dc1948b0a15ea9e4
1 file changed
+225
-229
packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js
+225
-229
@@ -1,10 +1,12 @@
1
let React;
2
-let ReactTestRenderer;
2
+let ReactDOMClient;
3
+let ReactDOM;
4
let ReactFeatureFlags;
5
let Scheduler;
6
let Suspense;
7
let act;
8
let textCache;
9
+let container;
10
11
let assertLog;
12
let waitForPaint;
@@ -18,9 +20,11 @@ describe('ReactSuspense', () => {
20
21
ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
22
React = require('react');
21
- ReactTestRenderer = require('react-test-renderer');
23
+ ReactDOM = require('react-dom');
24
+ ReactDOMClient = require('react-dom/client');
25
act = require('internal-test-utils').act;
26
Scheduler = require('scheduler');
27
+ container = document.createElement('div');
28
29
Suspense = React.Suspense;
30
@@ -115,18 +119,19 @@ describe('ReactSuspense', () => {
119
);
120
}
121
122
+ const root = ReactDOMClient.createRoot(container);
123
// Render an empty shell
119
- const root = ReactTestRenderer.create(<Foo />, {
120
- isConcurrent: true,
124
+ await act(() => {
125
+ root.render(<Foo />);
126
});
122
-
123
- await waitForAll(['Foo']);
124
- expect(root).toMatchRenderedOutput(null);
127
+ assertLog(['Foo']);
128
+ const renderedEl = container;
129
+ expect(renderedEl.innerText).toBeUndefined();
130
131
// Navigate the shell to now render the child content.
132
// This should suspend.
133
React.startTransition(() => {
129
- root.update(<Foo renderBar={true} />);
134
+ root.render(<Foo renderBar={true} />);
135
});
136
137
await waitForAll([
@@ -136,19 +141,20 @@ describe('ReactSuspense', () => {
141
'Suspend! [A]',
142
'Loading...',
143
]);
139
- expect(root).toMatchRenderedOutput(null);
144
+ expect(container.textContent).toEqual('');
145
146
await waitForAll([]);
142
- expect(root).toMatchRenderedOutput(null);
147
+ expect(container.textContent).toEqual('');
148
144
- await resolveText('A');
149
+ resolveText('A');
150
await waitForAll(['Foo', 'Bar', 'A', 'B']);
146
- expect(root).toMatchRenderedOutput('AB');
151
+ expect(container.textContent).toEqual('AB');
152
});
153
154
it('suspends siblings and later recovers each independently', async () => {
155
+ const root = ReactDOMClient.createRoot(container);
156
// Render two sibling Suspense components
151
- const root = ReactTestRenderer.create(
157
+ root.render(
158
<>
159
<Suspense fallback={<Text text="Loading A..." />}>
160
<AsyncText text="A" ms={5000} />
@@ -157,9 +163,6 @@ describe('ReactSuspense', () => {
163
<AsyncText text="B" ms={6000} />
164
</Suspense>
165
</>,
160
- {
161
- isConcurrent: true,
162
- },
166
);
167
168
await waitForAll([
@@ -168,19 +171,19 @@ describe('ReactSuspense', () => {
171
'Suspend! [B]',
172
'Loading B...',
173
]);
171
- expect(root).toMatchRenderedOutput('Loading A...Loading B...');
174
+ expect(container.innerHTML).toEqual('Loading A...Loading B...');
175
176
// Resolve first Suspense's promise and switch back to the normal view. The
177
// second Suspense should still show the placeholder
178
await act(() => resolveText('A'));
179
assertLog(['A']);
177
- expect(root).toMatchRenderedOutput('ALoading B...');
180
+ expect(container.textContent).toEqual('ALoading B...');
181
182
// Resolve the second Suspense's promise resolves and switche back to the
183
// normal view
184
await act(() => resolveText('B'));
185
assertLog(['B']);
183
- expect(root).toMatchRenderedOutput('AB');
186
+ expect(container.textContent).toEqual('AB');
187
});
188
189
it('interrupts current render if promise resolves before current render phase', async () => {
@@ -210,22 +213,22 @@ describe('ReactSuspense', () => {
213
Scheduler.log('Async');
214
return 'Async';
215
}
216
+ const root = ReactDOMClient.createRoot(container);
217
+ await act(() => {
218
+ root.render(
219
+ <>
220
+ <Suspense fallback={<Text text="Loading..." />} />
221
+ <Text text="Initial" />
222
+ </>,
223
+ );
224
+ });
225
214
- const root = ReactTestRenderer.create(
215
- <>
216
- <Suspense fallback={<Text text="Loading..." />} />
217
- <Text text="Initial" />
218
- </>,
219
- {
220
- isConcurrent: true,
221
- },
222
- );
223
- await waitForAll(['Initial']);
224
- expect(root).toMatchRenderedOutput('Initial');
226
+ assertLog(['Initial']);
227
+ expect(container.textContent).toEqual('Initial');
228
229
// The update will suspend.
230
React.startTransition(() => {
228
- root.update(
231
+ root.render(
232
<>
233
<Suspense fallback={<Text text="Loading..." />}>
234
<Async />
@@ -242,7 +245,7 @@ describe('ReactSuspense', () => {
245
// The promise resolves before the current render phase has completed
246
resolveThenable();
247
assertLog([]);
245
- expect(root).toMatchRenderedOutput('Initial');
248
+ expect(container.textContent).toEqual('Initial');
249
250
// Start over from the root, instead of continuing.
251
await waitForAll([
@@ -251,7 +254,7 @@ describe('ReactSuspense', () => {
254
'After Suspense',
255
'Sibling',
256
]);
254
- expect(root).toMatchRenderedOutput('AsyncAfter SuspenseSibling');
257
+ expect(container.textContent).toEqual('AsyncAfter SuspenseSibling');
258
});
259
260
it('throttles fallback committing globally', async () => {
@@ -267,12 +270,13 @@ describe('ReactSuspense', () => {
270
);
271
}
272
270
- const root = ReactTestRenderer.create(<Foo />, {
271
- isConcurrent: true,
273
+ const root = ReactDOMClient.createRoot(container);
274
+ await act(() => {
275
+ root.render(<Foo />);
276
});
277
274
- await waitForAll(['Foo', 'Suspend! [A]', 'Loading...']);
275
- expect(root).toMatchRenderedOutput('Loading...');
278
+ assertLog(['Foo', 'Suspend! [A]', 'Loading...']);
279
+ expect(container.textContent).toEqual('Loading...');
280
281
await resolveText('A');
282
await waitForAll(['A', 'Suspend! [B]', 'Loading more...']);
@@ -280,13 +284,13 @@ describe('ReactSuspense', () => {
284
// By this point, we have enough info to show "A" and "Loading more..."
285
// However, we've just shown the outer fallback. So we'll delay
286
// showing the inner fallback hoping that B will resolve soon enough.
283
- expect(root).toMatchRenderedOutput('Loading...');
287
+ expect(container.textContent).toEqual('Loading...');
288
289
await act(() => resolveText('B'));
290
// By this point, B has resolved.
291
// The contents of both should pop in together.
292
assertLog(['A', 'B']);
289
- expect(root).toMatchRenderedOutput('AB');
293
+ expect(container.textContent).toEqual('AB');
294
});
295
296
it('does not throttle fallback committing for too long', async () => {
@@ -302,12 +306,12 @@ describe('ReactSuspense', () => {
306
);
307
}
308
305
- const root = ReactTestRenderer.create(<Foo />, {
306
- isConcurrent: true,
309
+ const root = ReactDOMClient.createRoot(container);
310
+ await act(() => {
311
+ root.render(<Foo />);
312
});
308
-
309
- await waitForAll(['Foo', 'Suspend! [A]', 'Loading...']);
310
- expect(root).toMatchRenderedOutput('Loading...');
313
+ assertLog(['Foo', 'Suspend! [A]', 'Loading...']);
314
+ expect(container.textContent).toEqual('Loading...');
315
316
await resolveText('A');
317
await waitForAll(['A', 'Suspend! [B]', 'Loading more...']);
@@ -315,16 +319,16 @@ describe('ReactSuspense', () => {
319
// By this point, we have enough info to show "A" and "Loading more..."
320
// However, we've just shown the outer fallback. So we'll delay
321
// showing the inner fallback hoping that B will resolve soon enough.
318
- expect(root).toMatchRenderedOutput('Loading...');
322
+ expect(container.textContent).toEqual('Loading...');
323
// But if we wait a bit longer, eventually we'll give up and show a
324
// fallback. The exact value here isn't important. It's a JND ("Just
325
// Noticeable Difference").
326
jest.advanceTimersByTime(500);
323
- expect(root).toMatchRenderedOutput('ALoading more...');
327
+ expect(container.textContent).toEqual('ALoading more...');
328
329
await act(() => resolveText('B'));
330
assertLog(['B']);
327
- expect(root).toMatchRenderedOutput('AB');
331
+ expect(container.textContent).toEqual('AB');
332
});
333
334
// @gate forceConcurrentByDefaultForTesting
@@ -333,10 +337,12 @@ describe('ReactSuspense', () => {
337
"delay and we've already skipped over a lower priority update in " +
338
'a parent',
339
async () => {
340
+ const root = ReactDOMClient.createRoot(container);
341
+
342
function interrupt() {
343
// React has a heuristic to batch all updates that occur within the same
344
// event. This is a trick to circumvent that heuristic.
339
- ReactTestRenderer.create('whatever');
345
+ ReactDOM.render('whatever', document.createElement('div'));
346
}
347
348
function App({shouldSuspend, step}) {
@@ -352,16 +358,12 @@ describe('ReactSuspense', () => {
358
);
359
}
360
355
- const root = ReactTestRenderer.create(null, {
356
- isConcurrent: true,
357
- });
358
-
359
- root.update(<App shouldSuspend={false} step={0} />);
361
+ root.render(<App shouldSuspend={false} step={0} />);
362
await waitForAll(['A0', 'B0', 'C0']);
361
- expect(root).toMatchRenderedOutput('A0B0C0');
363
+ expect(container.textContent).toEqual('A0B0C0');
364
365
// This update will suspend.
364
- root.update(<App shouldSuspend={true} step={1} />);
366
+ root.render(<App shouldSuspend={true} step={1} />);
367
368
// Do a bit of work
369
await waitFor(['A1']);
@@ -369,7 +371,7 @@ describe('ReactSuspense', () => {
371
// Schedule another update. This will have lower priority because it's
372
// a transition.
373
React.startTransition(() => {
372
- root.update(<App shouldSuspend={false} step={2} />);
374
+ root.render(<App shouldSuspend={false} step={2} />);
375
});
376
377
// Interrupt to trigger a restart.
@@ -384,7 +386,7 @@ describe('ReactSuspense', () => {
386
]);
387
388
// Should not have committed loading state
387
- expect(root).toMatchRenderedOutput('A0B0C0');
389
+ expect(container.textContent).toEqual('A0B0C0');
390
391
// After suspending, should abort the first update and switch to the
392
// second update. So, C1 should not appear in the log.
@@ -393,11 +395,11 @@ describe('ReactSuspense', () => {
395
// the render before the end of the current slice of work.
396
await waitForAll(['A2', 'B2', 'C2']);
397
396
- expect(root).toMatchRenderedOutput('A2B2C2');
398
+ expect(container.textContent).toEqual('A2B2C2');
399
},
400
);
401
400
- it('mounts a lazy class component in non-concurrent mode', async () => {
402
+ it('mounts a lazy class component in non-concurrent mode (legacy)', async () => {
403
class Class extends React.Component {
404
componentDidMount() {
405
Scheduler.log('Did mount: ' + this.props.label);
@@ -416,19 +418,20 @@ describe('ReactSuspense', () => {
418
419
const LazyClass = React.lazy(() => fakeImport(Class));
420
419
- const root = ReactTestRenderer.create(
421
+ ReactDOM.render(
422
<Suspense fallback={<Text text="Loading..." />}>
423
<LazyClass label="Hi" />
424
</Suspense>,
425
+ container,
426
);
427
428
assertLog(['Loading...']);
426
- expect(root).toMatchRenderedOutput('Loading...');
429
+ expect(container.textContent).toEqual('Loading...');
430
431
await LazyClass;
432
433
await waitForPaint(['Hi', 'Did mount: Hi']);
431
- expect(root).toMatchRenderedOutput('Hi');
434
+ expect(container.textContent).toEqual('Hi');
435
});
436
437
it('updates memoized child of suspense component when context updates (simple memo)', async () => {
@@ -455,21 +458,22 @@ describe('ReactSuspense', () => {
458
);
459
}
460
458
- const root = ReactTestRenderer.create(<App />, {
459
- isConcurrent: true,
461
+ const root = ReactDOMClient.createRoot(container);
462
+ await act(() => {
463
+ root.render(<App />);
464
});
461
- await waitForAll(['Suspend! [default]', 'Loading...']);
465
+ assertLog(['Suspend! [default]', 'Loading...']);
466
467
await act(() => resolveText('default'));
468
assertLog(['default']);
465
- expect(root).toMatchRenderedOutput('default');
469
+ expect(container.textContent).toEqual('default');
470
471
await act(() => setValue('new value'));
472
assertLog(['Suspend! [new value]', 'Loading...']);
473
474
await act(() => resolveText('new value'));
475
assertLog(['new value']);
472
- expect(root).toMatchRenderedOutput('new value');
476
+ expect(container.textContent).toEqual('new value');
477
});
478
479
it('updates memoized child of suspense component when context updates (manual memo)', async () => {
@@ -501,21 +505,22 @@ describe('ReactSuspense', () => {
505
);
506
}
507
504
- const root = ReactTestRenderer.create(<App />, {
505
- isConcurrent: true,
508
+ const root = ReactDOMClient.createRoot(container);
509
+ await act(() => {
510
+ root.render(<App />);
511
});
507
- await waitForAll(['Suspend! [default]', 'Loading...']);
512
+ assertLog(['Suspend! [default]', 'Loading...']);
513
514
await act(() => resolveText('default'));
515
assertLog(['default']);
511
- expect(root).toMatchRenderedOutput('default');
516
+ expect(container.textContent).toEqual('default');
517
518
await act(() => setValue('new value'));
519
assertLog(['Suspend! [new value]', 'Loading...']);
520
521
await act(() => resolveText('new value'));
522
assertLog(['new value']);
518
- expect(root).toMatchRenderedOutput('new value');
523
+ expect(container.textContent).toEqual('new value');
524
});
525
526
it('updates memoized child of suspense component when context updates (function)', async () => {
@@ -538,28 +543,28 @@ describe('ReactSuspense', () => {
543
);
544
}
545
541
- const root = ReactTestRenderer.create(
542
- <App>
543
- <Suspense fallback={<Text text="Loading..." />}>
544
- <MemoizedChild />
545
- </Suspense>
546
- </App>,
547
- {
548
- isConcurrent: true,
549
- },
550
- );
551
- await waitForAll(['Suspend! [default]', 'Loading...']);
546
+ const root = ReactDOMClient.createRoot(container);
547
+ await act(() => {
548
+ root.render(
549
+ <App>
550
+ <Suspense fallback={<Text text="Loading..." />}>
551
+ <MemoizedChild />
552
+ </Suspense>
553
+ </App>,
554
+ );
555
+ });
556
+ assertLog(['Suspend! [default]', 'Loading...']);
557
558
await act(() => resolveText('default'));
559
assertLog(['default']);
555
- expect(root).toMatchRenderedOutput('default');
560
+ expect(container.textContent).toEqual('default');
561
562
await act(() => setValue('new value'));
563
assertLog(['Suspend! [new value]', 'Loading...']);
564
565
await act(() => resolveText('new value'));
566
assertLog(['new value']);
562
- expect(root).toMatchRenderedOutput('new value');
567
+ expect(container.textContent).toEqual('new value');
568
});
569
570
it('updates memoized child of suspense component when context updates (forwardRef)', async () => {
@@ -582,28 +587,28 @@ describe('ReactSuspense', () => {
587
);
588
}
589
585
- const root = ReactTestRenderer.create(
586
- <App>
587
- <Suspense fallback={<Text text="Loading..." />}>
588
- <MemoizedChild />
589
- </Suspense>
590
- </App>,
591
- {
592
- isConcurrent: true,
593
- },
594
- );
595
- await waitForAll(['Suspend! [default]', 'Loading...']);
590
+ const root = ReactDOMClient.createRoot(container);
591
+ await act(() => {
592
+ root.render(
593
+ <App>
594
+ <Suspense fallback={<Text text="Loading..." />}>
595
+ <MemoizedChild />
596
+ </Suspense>
597
+ </App>,
598
+ );
599
+ });
600
+ assertLog(['Suspend! [default]', 'Loading...']);
601
602
await act(() => resolveText('default'));
603
assertLog(['default']);
599
- expect(root).toMatchRenderedOutput('default');
604
+ expect(container.textContent).toEqual('default');
605
606
await act(() => setValue('new value'));
607
assertLog(['Suspend! [new value]', 'Loading...']);
608
609
await act(() => resolveText('new value'));
610
assertLog(['new value']);
606
- expect(root).toMatchRenderedOutput('new value');
611
+ expect(container.textContent).toEqual('new value');
612
});
613
614
it('re-fires layout effects when re-showing Suspense', async () => {
@@ -630,12 +635,13 @@ describe('ReactSuspense', () => {
635
);
636
}
637
633
- const root = ReactTestRenderer.create(<App />, {
634
- isConcurrent: true,
638
+ const root = ReactDOMClient.createRoot(container);
639
+ await act(() => {
640
+ root.render(<App />);
641
});
642
637
- await waitForAll(['Child 1', 'create layout']);
638
- expect(root).toMatchRenderedOutput('Child 1');
643
+ assertLog(['Child 1', 'create layout']);
644
+ expect(container.textContent).toEqual('Child 1');
645
646
await act(() => {
647
_setShow(true);
@@ -649,10 +655,36 @@ describe('ReactSuspense', () => {
655
656
await act(() => resolveText('Child 2'));
657
assertLog(['Child 1', 'Child 2', 'create layout']);
652
- expect(root).toMatchRenderedOutput(['Child 1', 'Child 2'].join(''));
658
+ expect(container.textContent).toEqual(['Child 1', 'Child 2'].join(''));
659
});
660
655
- describe('outside concurrent mode', () => {
661
+ it('does not get stuck with fallback in concurrent mode for a large delay', async () => {
662
+ function App(props) {
663
+ return (
664
+ <Suspense fallback={<Text text="Loading..." />}>
665
+ <AsyncText text="Child 1" />
666
+ <AsyncText text="Child 2" />
667
+ </Suspense>
668
+ );
669
+ }
670
+
671
+ const root = ReactDOMClient.createRoot(container);
672
+ await act(() => {
673
+ root.render(<App />);
674
+ });
675
+
676
+ assertLog(['Suspend! [Child 1]', 'Loading...']);
677
+ await resolveText('Child 1');
678
+ await waitForAll(['Child 1', 'Suspend! [Child 2]']);
679
+
680
+ jest.advanceTimersByTime(6000);
681
+
682
+ await act(() => resolveText('Child 2'));
683
+ assertLog(['Child 1', 'Child 2']);
684
+ expect(container.textContent).toEqual(['Child 1', 'Child 2'].join(''));
685
+ });
686
+
687
+ describe('outside concurrent mode (legacy)', () => {
688
it('a mounted class component can suspend without losing state', async () => {
689
class TextWithLifecycle extends React.Component {
690
componentDidMount() {
@@ -698,8 +730,7 @@ describe('ReactSuspense', () => {
730
);
731
}
732
701
- const root = ReactTestRenderer.create(<App />);
702
-
733
+ ReactDOM.render(<App />, container);
734
assertLog([
735
'A',
736
'Suspend! [B:1]',
@@ -712,24 +743,24 @@ describe('ReactSuspense', () => {
743
'Mount [C]',
744
'Mount [Loading...]',
745
]);
715
- expect(root).toMatchRenderedOutput('Loading...');
746
+ expect(container.textContent).toEqual('Loading...');
747
748
await resolveText('B:1');
718
- await waitForPaint([
749
+ assertLog([
750
'B:1',
751
'Unmount [Loading...]',
752
// Should be a mount, not an update
753
'Mount [B:1]',
754
]);
724
- expect(root).toMatchRenderedOutput('AB:1C');
755
+ expect(container.textContent).toEqual('AB:1C');
756
757
instance.setState({step: 2});
758
assertLog(['Suspend! [B:2]', 'Loading...', 'Mount [Loading...]']);
728
- expect(root).toMatchRenderedOutput('Loading...');
759
+ expect(container.textContent).toEqual('Loading...');
760
761
await resolveText('B:2');
731
- await waitForPaint(['B:2', 'Unmount [Loading...]', 'Update [B:2]']);
732
- expect(root).toMatchRenderedOutput('AB:2C');
762
+ assertLog(['B:2', 'Unmount [Loading...]', 'Update [B:2]']);
763
+ expect(container.textContent).toEqual('AB:2C');
764
});
765
766
it('bails out on timed-out primary children even if they receive an update', async () => {
@@ -751,25 +782,25 @@ describe('ReactSuspense', () => {
782
);
783
}
784
754
- const root = ReactTestRenderer.create(<App text="A" />);
785
+ ReactDOM.render(<App text="A" />, container);
786
787
assertLog(['Stateful: 1', 'Suspend! [A]', 'Loading...']);
788
789
await resolveText('A');
759
- await waitForPaint(['A']);
760
- expect(root).toMatchRenderedOutput('Stateful: 1A');
790
+ assertLog(['A']);
791
+ expect(container.textContent).toEqual('Stateful: 1A');
792
762
- root.update(<App text="B" />);
793
+ ReactDOM.render(<App text="B" />, container);
794
assertLog(['Stateful: 1', 'Suspend! [B]', 'Loading...']);
764
- expect(root).toMatchRenderedOutput('Loading...');
795
+ expect(container.textContent).toEqual('Loading...');
796
797
instance.setState({step: 2});
798
assertLog(['Stateful: 2', 'Suspend! [B]']);
768
- expect(root).toMatchRenderedOutput('Loading...');
799
+ expect(container.textContent).toEqual('Loading...');
800
801
await resolveText('B');
771
- await waitForPaint(['B']);
772
- expect(root).toMatchRenderedOutput('Stateful: 2B');
802
+ assertLog(['B']);
803
+ expect(container.textContent).toEqual('Stateful: 2B');
804
});
805
806
it('when updating a timed-out tree, always retries the suspended component', async () => {
@@ -799,17 +830,17 @@ describe('ReactSuspense', () => {
830
);
831
}
832
802
- const root = ReactTestRenderer.create(<App text="A" />);
833
+ ReactDOM.render(<App text="A" />, container);
834
835
assertLog(['Stateful: 1', 'Suspend! [A]', 'Loading...']);
836
837
await resolveText('A');
807
- await waitForPaint(['A']);
808
- expect(root).toMatchRenderedOutput('Stateful: 1A');
838
+ assertLog(['A']);
839
+ expect(container.textContent).toEqual('Stateful: 1A');
840
810
- root.update(<App text="B" />);
841
+ ReactDOM.render(<App text="B" />, container);
842
assertLog(['Stateful: 1', 'Suspend! [B]', 'Loading...']);
812
- expect(root).toMatchRenderedOutput('Loading...');
843
+ expect(container.textContent).toEqual('Loading...');
844
845
instance.setState({step: 2});
846
assertLog([
@@ -820,14 +851,14 @@ describe('ReactSuspense', () => {
851
// pending work, so it was improperly treated as complete.
852
'Suspend! [B]',
853
]);
823
- expect(root).toMatchRenderedOutput('Loading...');
854
+ expect(container.textContent).toEqual('Loading...');
855
856
await resolveText('B');
826
- await waitForPaint(['B']);
827
- expect(root).toMatchRenderedOutput('Stateful: 2B');
857
+ assertLog(['B']);
858
+ expect(container.textContent).toEqual('Stateful: 2B');
859
});
860
830
- it('suspends in a class that has componentWillUnmount and is then deleted', () => {
861
+ it('suspends in a class that has componentWillUnmount and is then deleted', async () => {
862
class AsyncTextWithUnmount extends React.Component {
863
componentWillUnmount() {
864
Scheduler.log('will unmount');
@@ -845,12 +876,12 @@ describe('ReactSuspense', () => {
876
);
877
}
878
848
- const root = ReactTestRenderer.create(<App text="A" />);
879
+ ReactDOM.render(<App text="A" />, container);
880
assertLog(['Suspend! [A]', 'Loading...']);
850
- root.update(<Text text="B" />);
881
+ ReactDOM.render(<Text text="B" />, container);
882
// Should not fire componentWillUnmount
883
assertLog(['B']);
853
- expect(root).toMatchRenderedOutput('B');
884
+ expect(container.textContent).toEqual('B');
885
});
886
887
it('suspends in a component that also contains useEffect', async () => {
@@ -874,10 +905,10 @@ describe('ReactSuspense', () => {
905
);
906
}
907
877
- ReactTestRenderer.create(<App text="A" />);
908
+ ReactDOM.render(<App text="A" />, container);
909
assertLog(['Suspend! [A]', 'Loading...']);
910
await resolveText('A');
880
- await waitForPaint(['A', 'Did commit: A']);
911
+ assertLog(['A', 'Did commit: A']);
912
});
913
914
it('retries when an update is scheduled on a timed out tree', async () => {
@@ -898,35 +929,33 @@ describe('ReactSuspense', () => {
929
);
930
}
931
901
- const root = ReactTestRenderer.create(<App />, {
902
- isConcurrent: true,
903
- });
932
+ ReactDOM.render(<App />, container);
933
934
// Initial render
906
- await waitForAll(['Suspend! [Step: 1]', 'Loading...']);
935
+ assertLog(['Suspend! [Step: 1]', 'Loading...']);
936
937
await act(() => resolveText('Step: 1'));
938
assertLog(['Step: 1']);
910
- expect(root).toMatchRenderedOutput('Step: 1');
939
+ expect(container.textContent).toEqual('Step: 1');
940
941
// Update that suspends
942
await act(() => {
943
instance.setState({step: 2});
944
});
945
assertLog(['Suspend! [Step: 2]', 'Loading...']);
917
- expect(root).toMatchRenderedOutput('Loading...');
946
+ expect(container.textContent).toEqual('Loading...');
947
948
// Update while still suspended
949
instance.setState({step: 3});
921
- await waitForAll(['Suspend! [Step: 3]']);
922
- expect(root).toMatchRenderedOutput('Loading...');
950
+ assertLog(['Suspend! [Step: 3]']);
951
+ expect(container.textContent).toEqual('Loading...');
952
953
await act(() => {
954
resolveText('Step: 2');
955
resolveText('Step: 3');
956
});
957
assertLog(['Step: 3']);
929
- expect(root).toMatchRenderedOutput('Step: 3');
958
+ expect(container.textContent).toEqual('Step: 3');
959
});
960
961
it('does not remount the fallback while suspended children resolve in legacy mode', async () => {
@@ -950,7 +979,7 @@ describe('ReactSuspense', () => {
979
);
980
}
981
953
- const root = ReactTestRenderer.create(<App />);
982
+ ReactDOM.render(<App />, container);
983
984
// Initial render
985
assertLog([
@@ -962,48 +991,19 @@ describe('ReactSuspense', () => {
991
await waitForAll([]);
992
993
await resolveText('Child 1');
965
- await waitForPaint([
966
- 'Child 1',
967
- 'Suspend! [Child 2]',
968
- 'Suspend! [Child 3]',
969
- ]);
994
+ assertLog(['Child 1', 'Suspend! [Child 2]', 'Suspend! [Child 3]']);
995
996
await resolveText('Child 2');
972
- await waitForPaint(['Child 2', 'Suspend! [Child 3]']);
997
+ assertLog(['Child 2', 'Suspend! [Child 3]']);
998
999
await resolveText('Child 3');
975
- await waitForPaint(['Child 3']);
976
- expect(root).toMatchRenderedOutput(
1000
+ assertLog(['Child 3']);
1001
+ expect(container.textContent).toEqual(
1002
['Child 1', 'Child 2', 'Child 3'].join(''),
1003
);
1004
expect(mounts).toBe(1);
1005
});
1006
982
- it('does not get stuck with fallback in concurrent mode for a large delay', async () => {
983
- function App(props) {
984
- return (
985
- <Suspense fallback={<Text text="Loading..." />}>
986
- <AsyncText text="Child 1" />
987
- <AsyncText text="Child 2" />
988
- </Suspense>
989
- );
990
- }
991
-
992
- const root = ReactTestRenderer.create(<App />, {
993
- isConcurrent: true,
994
- });
995
-
996
- await waitForAll(['Suspend! [Child 1]', 'Loading...']);
997
- await resolveText('Child 1');
998
- await waitForAll(['Child 1', 'Suspend! [Child 2]']);
999
-
1000
- jest.advanceTimersByTime(6000);
1001
-
1002
- await act(() => resolveText('Child 2'));
1003
- assertLog(['Child 1', 'Child 2']);
1004
- expect(root).toMatchRenderedOutput(['Child 1', 'Child 2'].join(''));
1005
- });
1006
-
1007
it('reuses effects, including deletions, from the suspended tree', async () => {
1008
const {useState} = React;
1009
@@ -1020,35 +1020,35 @@ describe('ReactSuspense', () => {
1020
);
1021
}
1022
1023
- const root = ReactTestRenderer.create(<App />);
1023
+ ReactDOM.render(<App />, container);
1024
assertLog(['Suspend! [Tab: 0]', ' + sibling', 'Loading...']);
1025
- expect(root).toMatchRenderedOutput('Loading...');
1025
+ expect(container.textContent).toEqual('Loading...');
1026
1027
await resolveText('Tab: 0');
1028
- await waitForPaint(['Tab: 0']);
1029
- expect(root).toMatchRenderedOutput('Tab: 0 + sibling');
1028
+ assertLog(['Tab: 0']);
1029
+ expect(container.textContent).toEqual('Tab: 0 + sibling');
1030
1031
await act(() => setTab(1));
1032
assertLog(['Suspend! [Tab: 1]', ' + sibling', 'Loading...']);
1033
- expect(root).toMatchRenderedOutput('Loading...');
1033
+ expect(container.textContent).toEqual('Loading...');
1034
1035
await resolveText('Tab: 1');
1036
- await waitForPaint(['Tab: 1']);
1037
- expect(root).toMatchRenderedOutput('Tab: 1 + sibling');
1036
+ assertLog(['Tab: 1']);
1037
+ expect(container.textContent).toEqual('Tab: 1 + sibling');
1038
1039
await act(() => setTab(2));
1040
assertLog(['Suspend! [Tab: 2]', ' + sibling', 'Loading...']);
1041
- expect(root).toMatchRenderedOutput('Loading...');
1041
+ expect(container.textContent).toEqual('Loading...');
1042
1043
await resolveText('Tab: 2');
1044
- await waitForPaint(['Tab: 2']);
1045
- expect(root).toMatchRenderedOutput('Tab: 2 + sibling');
1044
+ assertLog(['Tab: 2']);
1045
+ expect(container.textContent).toEqual('Tab: 2 + sibling');
1046
});
1047
1048
it('does not warn if a mounted component is pinged', async () => {
1049
const {useState} = React;
1050
1051
- const root = ReactTestRenderer.create(null);
1051
+ ReactDOM.render(null, container);
1052
1053
let setStep;
1054
function UpdatingText({text, ms}) {
@@ -1058,25 +1058,22 @@ describe('ReactSuspense', () => {
1058
return <Text text={readText(fullText)} />;
1059
}
1060
1061
- root.update(
1061
+ ReactDOM.render(
1062
<Suspense fallback={<Text text="Loading..." />}>
1063
<UpdatingText text="A" ms={1000} />
1064
</Suspense>,
1065
+ container,
1066
);
1067
1068
assertLog(['Suspend! [A:0]', 'Loading...']);
1069
1070
await resolveText('A:0');
1070
- await waitForPaint(['A:0']);
1071
- expect(root).toMatchRenderedOutput('A:0');
1071
+ assertLog(['A:0']);
1072
+ expect(container.textContent).toEqual('A:0');
1073
1074
await act(() => setStep(1));
1075
assertLog(['Suspend! [A:1]', 'Loading...']);
1075
- expect(root).toMatchRenderedOutput('Loading...');
1076
-
1077
- await act(() => {
1078
- root.update(null);
1079
- });
1076
+ expect(container.textContent).toEqual('Loading...');
1077
});
1078
1079
it('memoizes promise listeners per thread ID to prevent redundant renders', async () => {
@@ -1090,14 +1087,14 @@ describe('ReactSuspense', () => {
1087
);
1088
}
1089
1093
- const root = ReactTestRenderer.create(null);
1090
+ ReactDOM.render(null, container);
1091
1095
- root.update(<App />);
1092
+ ReactDOM.render(<App />, container);
1093
1094
assertLog(['Suspend! [A]', 'Suspend! [B]', 'Suspend! [C]', 'Loading...']);
1095
1096
await resolveText('A');
1100
- await waitForPaint([
1097
+ assertLog([
1098
'A',
1099
// The promises for B and C have now been thrown twice
1100
'Suspend! [B]',
@@ -1105,7 +1102,7 @@ describe('ReactSuspense', () => {
1102
]);
1103
1104
await resolveText('B');
1108
- await waitForPaint([
1105
+ assertLog([
1106
// Even though the promise for B was thrown twice, we should only
1107
// re-render once.
1108
'B',
@@ -1114,7 +1111,7 @@ describe('ReactSuspense', () => {
1111
]);
1112
1113
await resolveText('C');
1117
- await waitForPaint([
1114
+ assertLog([
1115
// Even though the promise for C was thrown three times, we should only
1116
// re-render once.
1117
'C',
@@ -1153,11 +1150,9 @@ describe('ReactSuspense', () => {
1150
}
1151
}
1152
1156
- const root = ReactTestRenderer.create(null);
1153
+ ReactDOM.render(null, container);
1154
1158
- await act(() => {
1159
- root.update(<App name="world" />);
1160
- });
1155
+ ReactDOM.render(<App name="world" />, container);
1156
});
1157
1158
it('updates memoized child of suspense component when context updates (simple memo)', async () => {
@@ -1184,19 +1179,19 @@ describe('ReactSuspense', () => {
1179
);
1180
}
1181
1187
- const root = ReactTestRenderer.create(<App />);
1182
+ ReactDOM.render(<App />, container);
1183
assertLog(['Suspend! [default]', 'Loading...']);
1184
1185
await resolveText('default');
1191
- await waitForPaint(['default']);
1192
- expect(root).toMatchRenderedOutput('default');
1186
+ assertLog(['default']);
1187
+ expect(container.textContent).toEqual('default');
1188
1189
await act(() => setValue('new value'));
1190
assertLog(['Suspend! [new value]', 'Loading...']);
1191
1192
await resolveText('new value');
1198
- await waitForPaint(['new value']);
1199
- expect(root).toMatchRenderedOutput('new value');
1193
+ assertLog(['new value']);
1194
+ expect(container.textContent).toEqual('new value');
1195
});
1196
1197
it('updates memoized child of suspense component when context updates (manual memo)', async () => {
@@ -1228,19 +1223,19 @@ describe('ReactSuspense', () => {
1223
);
1224
}
1225
1231
- const root = ReactTestRenderer.create(<App />);
1226
+ ReactDOM.render(<App />, container);
1227
assertLog(['Suspend! [default]', 'Loading...']);
1228
1229
await resolveText('default');
1235
- await waitForPaint(['default']);
1236
- expect(root).toMatchRenderedOutput('default');
1230
+ assertLog(['default']);
1231
+ expect(container.textContent).toEqual('default');
1232
1233
await act(() => setValue('new value'));
1234
assertLog(['Suspend! [new value]', 'Loading...']);
1235
1236
await resolveText('new value');
1242
- await waitForPaint(['new value']);
1243
- expect(root).toMatchRenderedOutput('new value');
1237
+ assertLog(['new value']);
1238
+ expect(container.textContent).toEqual('new value');
1239
});
1240
1241
it('updates memoized child of suspense component when context updates (function)', async () => {
@@ -1265,25 +1260,26 @@ describe('ReactSuspense', () => {
1260
);
1261
}
1262
1268
- const root = ReactTestRenderer.create(
1263
+ ReactDOM.render(
1264
<App>
1265
<Suspense fallback={<Text text="Loading..." />}>
1266
<MemoizedChild />
1267
</Suspense>
1268
</App>,
1269
+ container,
1270
);
1271
assertLog(['Suspend! [default]', 'Loading...']);
1272
1273
await resolveText('default');
1278
- await waitForPaint(['default']);
1279
- expect(root).toMatchRenderedOutput('default');
1274
+ assertLog(['default']);
1275
+ expect(container.textContent).toEqual('default');
1276
1277
await act(() => setValue('new value'));
1278
assertLog(['Suspend! [new value]', 'Loading...']);
1279
1280
await resolveText('new value');
1285
- await waitForPaint(['new value']);
1286
- expect(root).toMatchRenderedOutput('new value');
1281
+ assertLog(['new value']);
1282
+ expect(container.textContent).toEqual('new value');
1283
});
1284
1285
it('updates memoized child of suspense component when context updates (forwardRef)', async () => {
@@ -1310,19 +1306,19 @@ describe('ReactSuspense', () => {
1306
);
1307
}
1308
1313
- const root = ReactTestRenderer.create(<App />);
1309
+ ReactDOM.render(<App />, container);
1310
assertLog(['Suspend! [default]', 'Loading...']);
1311
1312
await resolveText('default');
1317
- await waitForPaint(['default']);
1318
- expect(root).toMatchRenderedOutput('default');
1313
+ assertLog(['default']);
1314
+ expect(container.textContent).toEqual('default');
1315
1316
await act(() => setValue('new value'));
1317
assertLog(['Suspend! [new value]', 'Loading...']);
1318
1319
await resolveText('new value');
1324
- await waitForPaint(['new value']);
1325
- expect(root).toMatchRenderedOutput('new value');
1320
+ assertLog(['new value']);
1321
+ expect(container.textContent).toEqual('new value');
1322
});
1323
1324
it('updates context consumer within child of suspended suspense component when context updates', async () => {
@@ -1364,17 +1360,17 @@ describe('ReactSuspense', () => {
1360
);
1361
}
1362
1367
- const root = ReactTestRenderer.create(<App />);
1363
+ ReactDOM.render(<App />, container);
1364
assertLog(['Received context value [default]', 'default']);
1369
- expect(root).toMatchRenderedOutput('default');
1365
+ expect(container.textContent).toEqual('default');
1366
1367
await act(() => setValue('new value'));
1368
assertLog(['Received context value [new value]', 'Loading...']);
1373
- expect(root).toMatchRenderedOutput('Loading...');
1369
+ expect(container.textContent).toEqual('Loading...');
1370
1371
await act(() => setValue('default'));
1372
assertLog(['Received context value [default]', 'default']);
1377
- expect(root).toMatchRenderedOutput('default');
1373
+ expect(container.textContent).toEqual('default');
1374
});
1375
});
1376
});