@samitouri / QOS-React-2 / commits / 90a0ae1015

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 });