1. let React;
    
  2. let ReactTestRenderer;
    
  3. let ReactFeatureFlags;
    
  4. let Scheduler;
    
  5. let Suspense;
    
  6. let act;
    
  7. let textCache;
    
  8. 
    
  9. let assertLog;
    
  10. let waitForPaint;
    
  11. let waitForAll;
    
  12. let waitFor;
    
  13. 
    
  14. describe('ReactSuspense', () => {
    
  15.   beforeEach(() => {
    
  16.     jest.resetModules();
    
  17.     ReactFeatureFlags = require('shared/ReactFeatureFlags');
    
  18. 
    
  19.     ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
    
  20.     React = require('react');
    
  21.     ReactTestRenderer = require('react-test-renderer');
    
  22.     act = require('internal-test-utils').act;
    
  23.     Scheduler = require('scheduler');
    
  24. 
    
  25.     Suspense = React.Suspense;
    
  26. 
    
  27.     const InternalTestUtils = require('internal-test-utils');
    
  28.     waitForAll = InternalTestUtils.waitForAll;
    
  29.     waitForPaint = InternalTestUtils.waitForPaint;
    
  30.     assertLog = InternalTestUtils.assertLog;
    
  31.     waitFor = InternalTestUtils.waitFor;
    
  32. 
    
  33.     textCache = new Map();
    
  34.   });
    
  35. 
    
  36.   function resolveText(text) {
    
  37.     const record = textCache.get(text);
    
  38.     if (record === undefined) {
    
  39.       const newRecord = {
    
  40.         status: 'resolved',
    
  41.         value: text,
    
  42.       };
    
  43.       textCache.set(text, newRecord);
    
  44.     } else if (record.status === 'pending') {
    
  45.       const thenable = record.value;
    
  46.       record.status = 'resolved';
    
  47.       record.value = text;
    
  48.       thenable.pings.forEach(t => t());
    
  49.     }
    
  50.   }
    
  51. 
    
  52.   function readText(text) {
    
  53.     const record = textCache.get(text);
    
  54.     if (record !== undefined) {
    
  55.       switch (record.status) {
    
  56.         case 'pending':
    
  57.           Scheduler.log(`Suspend! [${text}]`);
    
  58.           throw record.value;
    
  59.         case 'rejected':
    
  60.           throw record.value;
    
  61.         case 'resolved':
    
  62.           return record.value;
    
  63.       }
    
  64.     } else {
    
  65.       Scheduler.log(`Suspend! [${text}]`);
    
  66.       const thenable = {
    
  67.         pings: [],
    
  68.         then(resolve) {
    
  69.           if (newRecord.status === 'pending') {
    
  70.             thenable.pings.push(resolve);
    
  71.           } else {
    
  72.             Promise.resolve().then(() => resolve(newRecord.value));
    
  73.           }
    
  74.         },
    
  75.       };
    
  76. 
    
  77.       const newRecord = {
    
  78.         status: 'pending',
    
  79.         value: thenable,
    
  80.       };
    
  81.       textCache.set(text, newRecord);
    
  82. 
    
  83.       throw thenable;
    
  84.     }
    
  85.   }
    
  86. 
    
  87.   function Text({text}) {
    
  88.     Scheduler.log(text);
    
  89.     return text;
    
  90.   }
    
  91. 
    
  92.   function AsyncText({text}) {
    
  93.     readText(text);
    
  94.     Scheduler.log(text);
    
  95.     return text;
    
  96.   }
    
  97. 
    
  98.   it('suspends rendering and continues later', async () => {
    
  99.     function Bar(props) {
    
  100.       Scheduler.log('Bar');
    
  101.       return props.children;
    
  102.     }
    
  103. 
    
  104.     function Foo({renderBar}) {
    
  105.       Scheduler.log('Foo');
    
  106.       return (
    
  107.         <Suspense fallback={<Text text="Loading..." />}>
    
  108.           {renderBar ? (
    
  109.             <Bar>
    
  110.               <AsyncText text="A" ms={100} />
    
  111.               <Text text="B" />
    
  112.             </Bar>
    
  113.           ) : null}
    
  114.         </Suspense>
    
  115.       );
    
  116.     }
    
  117. 
    
  118.     // Render an empty shell
    
  119.     const root = ReactTestRenderer.create(<Foo />, {
    
  120.       unstable_isConcurrent: true,
    
  121.     });
    
  122. 
    
  123.     await waitForAll(['Foo']);
    
  124.     expect(root).toMatchRenderedOutput(null);
    
  125. 
    
  126.     // Navigate the shell to now render the child content.
    
  127.     // This should suspend.
    
  128.     React.startTransition(() => {
    
  129.       root.update(<Foo renderBar={true} />);
    
  130.     });
    
  131. 
    
  132.     await waitForAll([
    
  133.       'Foo',
    
  134.       'Bar',
    
  135.       // A suspends
    
  136.       'Suspend! [A]',
    
  137.       'Loading...',
    
  138.     ]);
    
  139.     expect(root).toMatchRenderedOutput(null);
    
  140. 
    
  141.     await waitForAll([]);
    
  142.     expect(root).toMatchRenderedOutput(null);
    
  143. 
    
  144.     await resolveText('A');
    
  145.     await waitForAll(['Foo', 'Bar', 'A', 'B']);
    
  146.     expect(root).toMatchRenderedOutput('AB');
    
  147.   });
    
  148. 
    
  149.   it('suspends siblings and later recovers each independently', async () => {
    
  150.     // Render two sibling Suspense components
    
  151.     const root = ReactTestRenderer.create(
    
  152.       <>
    
  153.         <Suspense fallback={<Text text="Loading A..." />}>
    
  154.           <AsyncText text="A" ms={5000} />
    
  155.         </Suspense>
    
  156.         <Suspense fallback={<Text text="Loading B..." />}>
    
  157.           <AsyncText text="B" ms={6000} />
    
  158.         </Suspense>
    
  159.       </>,
    
  160.       {
    
  161.         unstable_isConcurrent: true,
    
  162.       },
    
  163.     );
    
  164. 
    
  165.     await waitForAll([
    
  166.       'Suspend! [A]',
    
  167.       'Loading A...',
    
  168.       'Suspend! [B]',
    
  169.       'Loading B...',
    
  170.     ]);
    
  171.     expect(root).toMatchRenderedOutput('Loading A...Loading B...');
    
  172. 
    
  173.     // Resolve first Suspense's promise and switch back to the normal view. The
    
  174.     // second Suspense should still show the placeholder
    
  175.     await act(() => resolveText('A'));
    
  176.     assertLog(['A']);
    
  177.     expect(root).toMatchRenderedOutput('ALoading B...');
    
  178. 
    
  179.     // Resolve the second Suspense's promise resolves and switche back to the
    
  180.     // normal view
    
  181.     await act(() => resolveText('B'));
    
  182.     assertLog(['B']);
    
  183.     expect(root).toMatchRenderedOutput('AB');
    
  184.   });
    
  185. 
    
  186.   it('interrupts current render if promise resolves before current render phase', async () => {
    
  187.     let didResolve = false;
    
  188.     const listeners = [];
    
  189. 
    
  190.     const thenable = {
    
  191.       then(resolve) {
    
  192.         if (!didResolve) {
    
  193.           listeners.push(resolve);
    
  194.         } else {
    
  195.           resolve();
    
  196.         }
    
  197.       },
    
  198.     };
    
  199. 
    
  200.     function resolveThenable() {
    
  201.       didResolve = true;
    
  202.       listeners.forEach(l => l());
    
  203.     }
    
  204. 
    
  205.     function Async() {
    
  206.       if (!didResolve) {
    
  207.         Scheduler.log('Suspend!');
    
  208.         throw thenable;
    
  209.       }
    
  210.       Scheduler.log('Async');
    
  211.       return 'Async';
    
  212.     }
    
  213. 
    
  214.     const root = ReactTestRenderer.create(
    
  215.       <>
    
  216.         <Suspense fallback={<Text text="Loading..." />} />
    
  217.         <Text text="Initial" />
    
  218.       </>,
    
  219.       {
    
  220.         unstable_isConcurrent: true,
    
  221.       },
    
  222.     );
    
  223.     await waitForAll(['Initial']);
    
  224.     expect(root).toMatchRenderedOutput('Initial');
    
  225. 
    
  226.     // The update will suspend.
    
  227.     React.startTransition(() => {
    
  228.       root.update(
    
  229.         <>
    
  230.           <Suspense fallback={<Text text="Loading..." />}>
    
  231.             <Async />
    
  232.           </Suspense>
    
  233.           <Text text="After Suspense" />
    
  234.           <Text text="Sibling" />
    
  235.         </>,
    
  236.       );
    
  237.     });
    
  238. 
    
  239.     // Yield past the Suspense boundary but don't complete the last sibling.
    
  240.     await waitFor(['Suspend!', 'Loading...', 'After Suspense']);
    
  241. 
    
  242.     // The promise resolves before the current render phase has completed
    
  243.     resolveThenable();
    
  244.     assertLog([]);
    
  245.     expect(root).toMatchRenderedOutput('Initial');
    
  246. 
    
  247.     // Start over from the root, instead of continuing.
    
  248.     await waitForAll([
    
  249.       // Async renders again *before* Sibling
    
  250.       'Async',
    
  251.       'After Suspense',
    
  252.       'Sibling',
    
  253.     ]);
    
  254.     expect(root).toMatchRenderedOutput('AsyncAfter SuspenseSibling');
    
  255.   });
    
  256. 
    
  257.   it('throttles fallback committing globally', async () => {
    
  258.     function Foo() {
    
  259.       Scheduler.log('Foo');
    
  260.       return (
    
  261.         <Suspense fallback={<Text text="Loading..." />}>
    
  262.           <AsyncText text="A" ms={200} />
    
  263.           <Suspense fallback={<Text text="Loading more..." />}>
    
  264.             <AsyncText text="B" ms={300} />
    
  265.           </Suspense>
    
  266.         </Suspense>
    
  267.       );
    
  268.     }
    
  269. 
    
  270.     const root = ReactTestRenderer.create(<Foo />, {
    
  271.       unstable_isConcurrent: true,
    
  272.     });
    
  273. 
    
  274.     await waitForAll(['Foo', 'Suspend! [A]', 'Loading...']);
    
  275.     expect(root).toMatchRenderedOutput('Loading...');
    
  276. 
    
  277.     await resolveText('A');
    
  278.     await waitForAll(['A', 'Suspend! [B]', 'Loading more...']);
    
  279. 
    
  280.     // By this point, we have enough info to show "A" and "Loading more..."
    
  281.     // However, we've just shown the outer fallback. So we'll delay
    
  282.     // showing the inner fallback hoping that B will resolve soon enough.
    
  283.     expect(root).toMatchRenderedOutput('Loading...');
    
  284. 
    
  285.     await act(() => resolveText('B'));
    
  286.     // By this point, B has resolved.
    
  287.     // The contents of both should pop in together.
    
  288.     assertLog(['A', 'B']);
    
  289.     expect(root).toMatchRenderedOutput('AB');
    
  290.   });
    
  291. 
    
  292.   it('does not throttle fallback committing for too long', async () => {
    
  293.     function Foo() {
    
  294.       Scheduler.log('Foo');
    
  295.       return (
    
  296.         <Suspense fallback={<Text text="Loading..." />}>
    
  297.           <AsyncText text="A" ms={200} />
    
  298.           <Suspense fallback={<Text text="Loading more..." />}>
    
  299.             <AsyncText text="B" ms={1200} />
    
  300.           </Suspense>
    
  301.         </Suspense>
    
  302.       );
    
  303.     }
    
  304. 
    
  305.     const root = ReactTestRenderer.create(<Foo />, {
    
  306.       unstable_isConcurrent: true,
    
  307.     });
    
  308. 
    
  309.     await waitForAll(['Foo', 'Suspend! [A]', 'Loading...']);
    
  310.     expect(root).toMatchRenderedOutput('Loading...');
    
  311. 
    
  312.     await resolveText('A');
    
  313.     await waitForAll(['A', 'Suspend! [B]', 'Loading more...']);
    
  314. 
    
  315.     // By this point, we have enough info to show "A" and "Loading more..."
    
  316.     // However, we've just shown the outer fallback. So we'll delay
    
  317.     // showing the inner fallback hoping that B will resolve soon enough.
    
  318.     expect(root).toMatchRenderedOutput('Loading...');
    
  319.     // But if we wait a bit longer, eventually we'll give up and show a
    
  320.     // fallback. The exact value here isn't important. It's a JND ("Just
    
  321.     // Noticeable Difference").
    
  322.     jest.advanceTimersByTime(500);
    
  323.     expect(root).toMatchRenderedOutput('ALoading more...');
    
  324. 
    
  325.     await act(() => resolveText('B'));
    
  326.     assertLog(['B']);
    
  327.     expect(root).toMatchRenderedOutput('AB');
    
  328.   });
    
  329. 
    
  330.   // @gate forceConcurrentByDefaultForTesting
    
  331.   it(
    
  332.     'interrupts current render when something suspends with a ' +
    
  333.       "delay and we've already skipped over a lower priority update in " +
    
  334.       'a parent',
    
  335.     async () => {
    
  336.       function interrupt() {
    
  337.         // React has a heuristic to batch all updates that occur within the same
    
  338.         // event. This is a trick to circumvent that heuristic.
    
  339.         ReactTestRenderer.create('whatever');
    
  340.       }
    
  341. 
    
  342.       function App({shouldSuspend, step}) {
    
  343.         return (
    
  344.           <>
    
  345.             <Text text={`A${step}`} />
    
  346.             <Suspense fallback={<Text text="Loading..." />}>
    
  347.               {shouldSuspend ? <AsyncText text="Async" ms={2000} /> : null}
    
  348.             </Suspense>
    
  349.             <Text text={`B${step}`} />
    
  350.             <Text text={`C${step}`} />
    
  351.           </>
    
  352.         );
    
  353.       }
    
  354. 
    
  355.       const root = ReactTestRenderer.create(null, {
    
  356.         unstable_isConcurrent: true,
    
  357.       });
    
  358. 
    
  359.       root.update(<App shouldSuspend={false} step={0} />);
    
  360.       await waitForAll(['A0', 'B0', 'C0']);
    
  361.       expect(root).toMatchRenderedOutput('A0B0C0');
    
  362. 
    
  363.       // This update will suspend.
    
  364.       root.update(<App shouldSuspend={true} step={1} />);
    
  365. 
    
  366.       // Do a bit of work
    
  367.       await waitFor(['A1']);
    
  368. 
    
  369.       // Schedule another update. This will have lower priority because it's
    
  370.       // a transition.
    
  371.       React.startTransition(() => {
    
  372.         root.update(<App shouldSuspend={false} step={2} />);
    
  373.       });
    
  374. 
    
  375.       // Interrupt to trigger a restart.
    
  376.       interrupt();
    
  377. 
    
  378.       await waitFor([
    
  379.         // Should have restarted the first update, because of the interruption
    
  380.         'A1',
    
  381.         'Suspend! [Async]',
    
  382.         'Loading...',
    
  383.         'B1',
    
  384.       ]);
    
  385. 
    
  386.       // Should not have committed loading state
    
  387.       expect(root).toMatchRenderedOutput('A0B0C0');
    
  388. 
    
  389.       // After suspending, should abort the first update and switch to the
    
  390.       // second update. So, C1 should not appear in the log.
    
  391.       // TODO: This should work even if React does not yield to the main
    
  392.       // thread. Should use same mechanism as selective hydration to interrupt
    
  393.       // the render before the end of the current slice of work.
    
  394.       await waitForAll(['A2', 'B2', 'C2']);
    
  395. 
    
  396.       expect(root).toMatchRenderedOutput('A2B2C2');
    
  397.     },
    
  398.   );
    
  399. 
    
  400.   it('mounts a lazy class component in non-concurrent mode', async () => {
    
  401.     class Class extends React.Component {
    
  402.       componentDidMount() {
    
  403.         Scheduler.log('Did mount: ' + this.props.label);
    
  404.       }
    
  405.       componentDidUpdate() {
    
  406.         Scheduler.log('Did update: ' + this.props.label);
    
  407.       }
    
  408.       render() {
    
  409.         return <Text text={this.props.label} />;
    
  410.       }
    
  411.     }
    
  412. 
    
  413.     async function fakeImport(result) {
    
  414.       return {default: result};
    
  415.     }
    
  416. 
    
  417.     const LazyClass = React.lazy(() => fakeImport(Class));
    
  418. 
    
  419.     const root = ReactTestRenderer.create(
    
  420.       <Suspense fallback={<Text text="Loading..." />}>
    
  421.         <LazyClass label="Hi" />
    
  422.       </Suspense>,
    
  423.     );
    
  424. 
    
  425.     assertLog(['Loading...']);
    
  426.     expect(root).toMatchRenderedOutput('Loading...');
    
  427. 
    
  428.     await LazyClass;
    
  429. 
    
  430.     await waitForPaint(['Hi', 'Did mount: Hi']);
    
  431.     expect(root).toMatchRenderedOutput('Hi');
    
  432.   });
    
  433. 
    
  434.   it('updates memoized child of suspense component when context updates (simple memo)', async () => {
    
  435.     const {useContext, createContext, useState, memo} = React;
    
  436. 
    
  437.     const ValueContext = createContext(null);
    
  438. 
    
  439.     const MemoizedChild = memo(function MemoizedChild() {
    
  440.       const text = useContext(ValueContext);
    
  441.       return <Text text={readText(text)} />;
    
  442.     });
    
  443. 
    
  444.     let setValue;
    
  445.     function App() {
    
  446.       const [value, _setValue] = useState('default');
    
  447.       setValue = _setValue;
    
  448. 
    
  449.       return (
    
  450.         <ValueContext.Provider value={value}>
    
  451.           <Suspense fallback={<Text text="Loading..." />}>
    
  452.             <MemoizedChild />
    
  453.           </Suspense>
    
  454.         </ValueContext.Provider>
    
  455.       );
    
  456.     }
    
  457. 
    
  458.     const root = ReactTestRenderer.create(<App />, {
    
  459.       unstable_isConcurrent: true,
    
  460.     });
    
  461.     await waitForAll(['Suspend! [default]', 'Loading...']);
    
  462. 
    
  463.     await act(() => resolveText('default'));
    
  464.     assertLog(['default']);
    
  465.     expect(root).toMatchRenderedOutput('default');
    
  466. 
    
  467.     await act(() => setValue('new value'));
    
  468.     assertLog(['Suspend! [new value]', 'Loading...']);
    
  469. 
    
  470.     await act(() => resolveText('new value'));
    
  471.     assertLog(['new value']);
    
  472.     expect(root).toMatchRenderedOutput('new value');
    
  473.   });
    
  474. 
    
  475.   it('updates memoized child of suspense component when context updates (manual memo)', async () => {
    
  476.     const {useContext, createContext, useState, memo} = React;
    
  477. 
    
  478.     const ValueContext = createContext(null);
    
  479. 
    
  480.     const MemoizedChild = memo(
    
  481.       function MemoizedChild() {
    
  482.         const text = useContext(ValueContext);
    
  483.         return <Text text={readText(text)} />;
    
  484.       },
    
  485.       function areEqual(prevProps, nextProps) {
    
  486.         return true;
    
  487.       },
    
  488.     );
    
  489. 
    
  490.     let setValue;
    
  491.     function App() {
    
  492.       const [value, _setValue] = useState('default');
    
  493.       setValue = _setValue;
    
  494. 
    
  495.       return (
    
  496.         <ValueContext.Provider value={value}>
    
  497.           <Suspense fallback={<Text text="Loading..." />}>
    
  498.             <MemoizedChild />
    
  499.           </Suspense>
    
  500.         </ValueContext.Provider>
    
  501.       );
    
  502.     }
    
  503. 
    
  504.     const root = ReactTestRenderer.create(<App />, {
    
  505.       unstable_isConcurrent: true,
    
  506.     });
    
  507.     await waitForAll(['Suspend! [default]', 'Loading...']);
    
  508. 
    
  509.     await act(() => resolveText('default'));
    
  510.     assertLog(['default']);
    
  511.     expect(root).toMatchRenderedOutput('default');
    
  512. 
    
  513.     await act(() => setValue('new value'));
    
  514.     assertLog(['Suspend! [new value]', 'Loading...']);
    
  515. 
    
  516.     await act(() => resolveText('new value'));
    
  517.     assertLog(['new value']);
    
  518.     expect(root).toMatchRenderedOutput('new value');
    
  519.   });
    
  520. 
    
  521.   it('updates memoized child of suspense component when context updates (function)', async () => {
    
  522.     const {useContext, createContext, useState} = React;
    
  523. 
    
  524.     const ValueContext = createContext(null);
    
  525. 
    
  526.     function MemoizedChild() {
    
  527.       const text = useContext(ValueContext);
    
  528.       return <Text text={readText(text)} />;
    
  529.     }
    
  530. 
    
  531.     let setValue;
    
  532.     function App({children}) {
    
  533.       const [value, _setValue] = useState('default');
    
  534.       setValue = _setValue;
    
  535. 
    
  536.       return (
    
  537.         <ValueContext.Provider value={value}>{children}</ValueContext.Provider>
    
  538.       );
    
  539.     }
    
  540. 
    
  541.     const root = ReactTestRenderer.create(
    
  542.       <App>
    
  543.         <Suspense fallback={<Text text="Loading..." />}>
    
  544.           <MemoizedChild />
    
  545.         </Suspense>
    
  546.       </App>,
    
  547.       {
    
  548.         unstable_isConcurrent: true,
    
  549.       },
    
  550.     );
    
  551.     await waitForAll(['Suspend! [default]', 'Loading...']);
    
  552. 
    
  553.     await act(() => resolveText('default'));
    
  554.     assertLog(['default']);
    
  555.     expect(root).toMatchRenderedOutput('default');
    
  556. 
    
  557.     await act(() => setValue('new value'));
    
  558.     assertLog(['Suspend! [new value]', 'Loading...']);
    
  559. 
    
  560.     await act(() => resolveText('new value'));
    
  561.     assertLog(['new value']);
    
  562.     expect(root).toMatchRenderedOutput('new value');
    
  563.   });
    
  564. 
    
  565.   it('updates memoized child of suspense component when context updates (forwardRef)', async () => {
    
  566.     const {forwardRef, useContext, createContext, useState} = React;
    
  567. 
    
  568.     const ValueContext = createContext(null);
    
  569. 
    
  570.     const MemoizedChild = forwardRef(() => {
    
  571.       const text = useContext(ValueContext);
    
  572.       return <Text text={readText(text)} />;
    
  573.     });
    
  574. 
    
  575.     let setValue;
    
  576.     function App({children}) {
    
  577.       const [value, _setValue] = useState('default');
    
  578.       setValue = _setValue;
    
  579. 
    
  580.       return (
    
  581.         <ValueContext.Provider value={value}>{children}</ValueContext.Provider>
    
  582.       );
    
  583.     }
    
  584. 
    
  585.     const root = ReactTestRenderer.create(
    
  586.       <App>
    
  587.         <Suspense fallback={<Text text="Loading..." />}>
    
  588.           <MemoizedChild />
    
  589.         </Suspense>
    
  590.       </App>,
    
  591.       {
    
  592.         unstable_isConcurrent: true,
    
  593.       },
    
  594.     );
    
  595.     await waitForAll(['Suspend! [default]', 'Loading...']);
    
  596. 
    
  597.     await act(() => resolveText('default'));
    
  598.     assertLog(['default']);
    
  599.     expect(root).toMatchRenderedOutput('default');
    
  600. 
    
  601.     await act(() => setValue('new value'));
    
  602.     assertLog(['Suspend! [new value]', 'Loading...']);
    
  603. 
    
  604.     await act(() => resolveText('new value'));
    
  605.     assertLog(['new value']);
    
  606.     expect(root).toMatchRenderedOutput('new value');
    
  607.   });
    
  608. 
    
  609.   it('re-fires layout effects when re-showing Suspense', async () => {
    
  610.     function TextWithLayout(props) {
    
  611.       Scheduler.log(props.text);
    
  612.       React.useLayoutEffect(() => {
    
  613.         Scheduler.log('create layout');
    
  614.         return () => {
    
  615.           Scheduler.log('destroy layout');
    
  616.         };
    
  617.       }, []);
    
  618.       return props.text;
    
  619.     }
    
  620. 
    
  621.     let _setShow;
    
  622.     function App(props) {
    
  623.       const [show, setShow] = React.useState(false);
    
  624.       _setShow = setShow;
    
  625.       return (
    
  626.         <Suspense fallback={<Text text="Loading..." />}>
    
  627.           <TextWithLayout text="Child 1" />
    
  628.           {show && <AsyncText ms={1000} text="Child 2" />}
    
  629.         </Suspense>
    
  630.       );
    
  631.     }
    
  632. 
    
  633.     const root = ReactTestRenderer.create(<App />, {
    
  634.       unstable_isConcurrent: true,
    
  635.     });
    
  636. 
    
  637.     await waitForAll(['Child 1', 'create layout']);
    
  638.     expect(root).toMatchRenderedOutput('Child 1');
    
  639. 
    
  640.     await act(() => {
    
  641.       _setShow(true);
    
  642.     });
    
  643.     assertLog([
    
  644.       'Child 1',
    
  645.       'Suspend! [Child 2]',
    
  646.       'Loading...',
    
  647.       'destroy layout',
    
  648.     ]);
    
  649. 
    
  650.     await act(() => resolveText('Child 2'));
    
  651.     assertLog(['Child 1', 'Child 2', 'create layout']);
    
  652.     expect(root).toMatchRenderedOutput(['Child 1', 'Child 2'].join(''));
    
  653.   });
    
  654. 
    
  655.   describe('outside concurrent mode', () => {
    
  656.     it('a mounted class component can suspend without losing state', async () => {
    
  657.       class TextWithLifecycle extends React.Component {
    
  658.         componentDidMount() {
    
  659.           Scheduler.log(`Mount [${this.props.text}]`);
    
  660.         }
    
  661.         componentDidUpdate() {
    
  662.           Scheduler.log(`Update [${this.props.text}]`);
    
  663.         }
    
  664.         componentWillUnmount() {
    
  665.           Scheduler.log(`Unmount [${this.props.text}]`);
    
  666.         }
    
  667.         render() {
    
  668.           return <Text {...this.props} />;
    
  669.         }
    
  670.       }
    
  671. 
    
  672.       let instance;
    
  673.       class AsyncTextWithLifecycle extends React.Component {
    
  674.         state = {step: 1};
    
  675.         componentDidMount() {
    
  676.           Scheduler.log(`Mount [${this.props.text}:${this.state.step}]`);
    
  677.         }
    
  678.         componentDidUpdate() {
    
  679.           Scheduler.log(`Update [${this.props.text}:${this.state.step}]`);
    
  680.         }
    
  681.         componentWillUnmount() {
    
  682.           Scheduler.log(`Unmount [${this.props.text}:${this.state.step}]`);
    
  683.         }
    
  684.         render() {
    
  685.           instance = this;
    
  686.           const text = readText(`${this.props.text}:${this.state.step}`);
    
  687.           return <Text text={text} />;
    
  688.         }
    
  689.       }
    
  690. 
    
  691.       function App() {
    
  692.         return (
    
  693.           <Suspense fallback={<TextWithLifecycle text="Loading..." />}>
    
  694.             <TextWithLifecycle text="A" />
    
  695.             <AsyncTextWithLifecycle ms={100} text="B" ref={instance} />
    
  696.             <TextWithLifecycle text="C" />
    
  697.           </Suspense>
    
  698.         );
    
  699.       }
    
  700. 
    
  701.       const root = ReactTestRenderer.create(<App />);
    
  702. 
    
  703.       assertLog([
    
  704.         'A',
    
  705.         'Suspend! [B:1]',
    
  706.         'C',
    
  707.         'Loading...',
    
  708. 
    
  709.         'Mount [A]',
    
  710.         // B's lifecycle should not fire because it suspended
    
  711.         // 'Mount [B]',
    
  712.         'Mount [C]',
    
  713.         'Mount [Loading...]',
    
  714.       ]);
    
  715.       expect(root).toMatchRenderedOutput('Loading...');
    
  716. 
    
  717.       await resolveText('B:1');
    
  718.       await waitForPaint([
    
  719.         'B:1',
    
  720.         'Unmount [Loading...]',
    
  721.         // Should be a mount, not an update
    
  722.         'Mount [B:1]',
    
  723.       ]);
    
  724.       expect(root).toMatchRenderedOutput('AB:1C');
    
  725. 
    
  726.       instance.setState({step: 2});
    
  727.       assertLog(['Suspend! [B:2]', 'Loading...', 'Mount [Loading...]']);
    
  728.       expect(root).toMatchRenderedOutput('Loading...');
    
  729. 
    
  730.       await resolveText('B:2');
    
  731.       await waitForPaint(['B:2', 'Unmount [Loading...]', 'Update [B:2]']);
    
  732.       expect(root).toMatchRenderedOutput('AB:2C');
    
  733.     });
    
  734. 
    
  735.     it('bails out on timed-out primary children even if they receive an update', async () => {
    
  736.       let instance;
    
  737.       class Stateful extends React.Component {
    
  738.         state = {step: 1};
    
  739.         render() {
    
  740.           instance = this;
    
  741.           return <Text text={`Stateful: ${this.state.step}`} />;
    
  742.         }
    
  743.       }
    
  744. 
    
  745.       function App(props) {
    
  746.         return (
    
  747.           <Suspense fallback={<Text text="Loading..." />}>
    
  748.             <Stateful />
    
  749.             <AsyncText ms={1000} text={props.text} />
    
  750.           </Suspense>
    
  751.         );
    
  752.       }
    
  753. 
    
  754.       const root = ReactTestRenderer.create(<App text="A" />);
    
  755. 
    
  756.       assertLog(['Stateful: 1', 'Suspend! [A]', 'Loading...']);
    
  757. 
    
  758.       await resolveText('A');
    
  759.       await waitForPaint(['A']);
    
  760.       expect(root).toMatchRenderedOutput('Stateful: 1A');
    
  761. 
    
  762.       root.update(<App text="B" />);
    
  763.       assertLog(['Stateful: 1', 'Suspend! [B]', 'Loading...']);
    
  764.       expect(root).toMatchRenderedOutput('Loading...');
    
  765. 
    
  766.       instance.setState({step: 2});
    
  767.       assertLog(['Stateful: 2', 'Suspend! [B]']);
    
  768.       expect(root).toMatchRenderedOutput('Loading...');
    
  769. 
    
  770.       await resolveText('B');
    
  771.       await waitForPaint(['B']);
    
  772.       expect(root).toMatchRenderedOutput('Stateful: 2B');
    
  773.     });
    
  774. 
    
  775.     it('when updating a timed-out tree, always retries the suspended component', async () => {
    
  776.       let instance;
    
  777.       class Stateful extends React.Component {
    
  778.         state = {step: 1};
    
  779.         render() {
    
  780.           instance = this;
    
  781.           return <Text text={`Stateful: ${this.state.step}`} />;
    
  782.         }
    
  783.       }
    
  784. 
    
  785.       const Indirection = React.Fragment;
    
  786. 
    
  787.       function App(props) {
    
  788.         return (
    
  789.           <Suspense fallback={<Text text="Loading..." />}>
    
  790.             <Stateful />
    
  791.             <Indirection>
    
  792.               <Indirection>
    
  793.                 <Indirection>
    
  794.                   <AsyncText ms={1000} text={props.text} />
    
  795.                 </Indirection>
    
  796.               </Indirection>
    
  797.             </Indirection>
    
  798.           </Suspense>
    
  799.         );
    
  800.       }
    
  801. 
    
  802.       const root = ReactTestRenderer.create(<App text="A" />);
    
  803. 
    
  804.       assertLog(['Stateful: 1', 'Suspend! [A]', 'Loading...']);
    
  805. 
    
  806.       await resolveText('A');
    
  807.       await waitForPaint(['A']);
    
  808.       expect(root).toMatchRenderedOutput('Stateful: 1A');
    
  809. 
    
  810.       root.update(<App text="B" />);
    
  811.       assertLog(['Stateful: 1', 'Suspend! [B]', 'Loading...']);
    
  812.       expect(root).toMatchRenderedOutput('Loading...');
    
  813. 
    
  814.       instance.setState({step: 2});
    
  815.       assertLog([
    
  816.         'Stateful: 2',
    
  817. 
    
  818.         // The suspended component should suspend again. If it doesn't, the
    
  819.         // likely mistake is that the suspended fiber wasn't marked with
    
  820.         // pending work, so it was improperly treated as complete.
    
  821.         'Suspend! [B]',
    
  822.       ]);
    
  823.       expect(root).toMatchRenderedOutput('Loading...');
    
  824. 
    
  825.       await resolveText('B');
    
  826.       await waitForPaint(['B']);
    
  827.       expect(root).toMatchRenderedOutput('Stateful: 2B');
    
  828.     });
    
  829. 
    
  830.     it('suspends in a class that has componentWillUnmount and is then deleted', () => {
    
  831.       class AsyncTextWithUnmount extends React.Component {
    
  832.         componentWillUnmount() {
    
  833.           Scheduler.log('will unmount');
    
  834.         }
    
  835.         render() {
    
  836.           return <Text text={readText(this.props.text)} />;
    
  837.         }
    
  838.       }
    
  839. 
    
  840.       function App({text}) {
    
  841.         return (
    
  842.           <Suspense fallback={<Text text="Loading..." />}>
    
  843.             <AsyncTextWithUnmount text={text} ms={100} />
    
  844.           </Suspense>
    
  845.         );
    
  846.       }
    
  847. 
    
  848.       const root = ReactTestRenderer.create(<App text="A" />);
    
  849.       assertLog(['Suspend! [A]', 'Loading...']);
    
  850.       root.update(<Text text="B" />);
    
  851.       // Should not fire componentWillUnmount
    
  852.       assertLog(['B']);
    
  853.       expect(root).toMatchRenderedOutput('B');
    
  854.     });
    
  855. 
    
  856.     it('suspends in a component that also contains useEffect', async () => {
    
  857.       const {useLayoutEffect} = React;
    
  858. 
    
  859.       function AsyncTextWithEffect(props) {
    
  860.         const text = props.text;
    
  861. 
    
  862.         useLayoutEffect(() => {
    
  863.           Scheduler.log('Did commit: ' + text);
    
  864.         }, [text]);
    
  865. 
    
  866.         return <Text text={readText(text)} />;
    
  867.       }
    
  868. 
    
  869.       function App({text}) {
    
  870.         return (
    
  871.           <Suspense fallback={<Text text="Loading..." />}>
    
  872.             <AsyncTextWithEffect text={text} ms={100} />
    
  873.           </Suspense>
    
  874.         );
    
  875.       }
    
  876. 
    
  877.       ReactTestRenderer.create(<App text="A" />);
    
  878.       assertLog(['Suspend! [A]', 'Loading...']);
    
  879.       await resolveText('A');
    
  880.       await waitForPaint(['A', 'Did commit: A']);
    
  881.     });
    
  882. 
    
  883.     it('retries when an update is scheduled on a timed out tree', async () => {
    
  884.       let instance;
    
  885.       class Stateful extends React.Component {
    
  886.         state = {step: 1};
    
  887.         render() {
    
  888.           instance = this;
    
  889.           return <AsyncText ms={1000} text={`Step: ${this.state.step}`} />;
    
  890.         }
    
  891.       }
    
  892. 
    
  893.       function App(props) {
    
  894.         return (
    
  895.           <Suspense fallback={<Text text="Loading..." />}>
    
  896.             <Stateful />
    
  897.           </Suspense>
    
  898.         );
    
  899.       }
    
  900. 
    
  901.       const root = ReactTestRenderer.create(<App />, {
    
  902.         unstable_isConcurrent: true,
    
  903.       });
    
  904. 
    
  905.       // Initial render
    
  906.       await waitForAll(['Suspend! [Step: 1]', 'Loading...']);
    
  907. 
    
  908.       await act(() => resolveText('Step: 1'));
    
  909.       assertLog(['Step: 1']);
    
  910.       expect(root).toMatchRenderedOutput('Step: 1');
    
  911. 
    
  912.       // Update that suspends
    
  913.       await act(() => {
    
  914.         instance.setState({step: 2});
    
  915.       });
    
  916.       assertLog(['Suspend! [Step: 2]', 'Loading...']);
    
  917.       expect(root).toMatchRenderedOutput('Loading...');
    
  918. 
    
  919.       // Update while still suspended
    
  920.       instance.setState({step: 3});
    
  921.       await waitForAll(['Suspend! [Step: 3]']);
    
  922.       expect(root).toMatchRenderedOutput('Loading...');
    
  923. 
    
  924.       await act(() => {
    
  925.         resolveText('Step: 2');
    
  926.         resolveText('Step: 3');
    
  927.       });
    
  928.       assertLog(['Step: 3']);
    
  929.       expect(root).toMatchRenderedOutput('Step: 3');
    
  930.     });
    
  931. 
    
  932.     it('does not remount the fallback while suspended children resolve in legacy mode', async () => {
    
  933.       let mounts = 0;
    
  934.       class ShouldMountOnce extends React.Component {
    
  935.         componentDidMount() {
    
  936.           mounts++;
    
  937.         }
    
  938.         render() {
    
  939.           return <Text text="Loading..." />;
    
  940.         }
    
  941.       }
    
  942. 
    
  943.       function App(props) {
    
  944.         return (
    
  945.           <Suspense fallback={<ShouldMountOnce />}>
    
  946.             <AsyncText ms={1000} text="Child 1" />
    
  947.             <AsyncText ms={2000} text="Child 2" />
    
  948.             <AsyncText ms={3000} text="Child 3" />
    
  949.           </Suspense>
    
  950.         );
    
  951.       }
    
  952. 
    
  953.       const root = ReactTestRenderer.create(<App />);
    
  954. 
    
  955.       // Initial render
    
  956.       assertLog([
    
  957.         'Suspend! [Child 1]',
    
  958.         'Suspend! [Child 2]',
    
  959.         'Suspend! [Child 3]',
    
  960.         'Loading...',
    
  961.       ]);
    
  962.       await waitForAll([]);
    
  963. 
    
  964.       await resolveText('Child 1');
    
  965.       await waitForPaint([
    
  966.         'Child 1',
    
  967.         'Suspend! [Child 2]',
    
  968.         'Suspend! [Child 3]',
    
  969.       ]);
    
  970. 
    
  971.       await resolveText('Child 2');
    
  972.       await waitForPaint(['Child 2', 'Suspend! [Child 3]']);
    
  973. 
    
  974.       await resolveText('Child 3');
    
  975.       await waitForPaint(['Child 3']);
    
  976.       expect(root).toMatchRenderedOutput(
    
  977.         ['Child 1', 'Child 2', 'Child 3'].join(''),
    
  978.       );
    
  979.       expect(mounts).toBe(1);
    
  980.     });
    
  981. 
    
  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.         unstable_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. 
    
  1010.       let setTab;
    
  1011.       function App() {
    
  1012.         const [tab, _setTab] = useState(0);
    
  1013.         setTab = _setTab;
    
  1014. 
    
  1015.         return (
    
  1016.           <Suspense fallback={<Text text="Loading..." />}>
    
  1017.             <AsyncText key={tab} text={'Tab: ' + tab} ms={1000} />
    
  1018.             <Text key={tab + 'sibling'} text=" + sibling" />
    
  1019.           </Suspense>
    
  1020.         );
    
  1021.       }
    
  1022. 
    
  1023.       const root = ReactTestRenderer.create(<App />);
    
  1024.       assertLog(['Suspend! [Tab: 0]', ' + sibling', 'Loading...']);
    
  1025.       expect(root).toMatchRenderedOutput('Loading...');
    
  1026. 
    
  1027.       await resolveText('Tab: 0');
    
  1028.       await waitForPaint(['Tab: 0']);
    
  1029.       expect(root).toMatchRenderedOutput('Tab: 0 + sibling');
    
  1030. 
    
  1031.       await act(() => setTab(1));
    
  1032.       assertLog(['Suspend! [Tab: 1]', ' + sibling', 'Loading...']);
    
  1033.       expect(root).toMatchRenderedOutput('Loading...');
    
  1034. 
    
  1035.       await resolveText('Tab: 1');
    
  1036.       await waitForPaint(['Tab: 1']);
    
  1037.       expect(root).toMatchRenderedOutput('Tab: 1 + sibling');
    
  1038. 
    
  1039.       await act(() => setTab(2));
    
  1040.       assertLog(['Suspend! [Tab: 2]', ' + sibling', 'Loading...']);
    
  1041.       expect(root).toMatchRenderedOutput('Loading...');
    
  1042. 
    
  1043.       await resolveText('Tab: 2');
    
  1044.       await waitForPaint(['Tab: 2']);
    
  1045.       expect(root).toMatchRenderedOutput('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);
    
  1052. 
    
  1053.       let setStep;
    
  1054.       function UpdatingText({text, ms}) {
    
  1055.         const [step, _setStep] = useState(0);
    
  1056.         setStep = _setStep;
    
  1057.         const fullText = `${text}:${step}`;
    
  1058.         return <Text text={readText(fullText)} />;
    
  1059.       }
    
  1060. 
    
  1061.       root.update(
    
  1062.         <Suspense fallback={<Text text="Loading..." />}>
    
  1063.           <UpdatingText text="A" ms={1000} />
    
  1064.         </Suspense>,
    
  1065.       );
    
  1066. 
    
  1067.       assertLog(['Suspend! [A:0]', 'Loading...']);
    
  1068. 
    
  1069.       await resolveText('A:0');
    
  1070.       await waitForPaint(['A:0']);
    
  1071.       expect(root).toMatchRenderedOutput('A:0');
    
  1072. 
    
  1073.       await act(() => setStep(1));
    
  1074.       assertLog(['Suspend! [A:1]', 'Loading...']);
    
  1075.       expect(root).toMatchRenderedOutput('Loading...');
    
  1076. 
    
  1077.       await act(() => {
    
  1078.         root.update(null);
    
  1079.       });
    
  1080.     });
    
  1081. 
    
  1082.     it('memoizes promise listeners per thread ID to prevent redundant renders', async () => {
    
  1083.       function App() {
    
  1084.         return (
    
  1085.           <Suspense fallback={<Text text="Loading..." />}>
    
  1086.             <AsyncText text="A" ms={1000} />
    
  1087.             <AsyncText text="B" ms={2000} />
    
  1088.             <AsyncText text="C" ms={3000} />
    
  1089.           </Suspense>
    
  1090.         );
    
  1091.       }
    
  1092. 
    
  1093.       const root = ReactTestRenderer.create(null);
    
  1094. 
    
  1095.       root.update(<App />);
    
  1096. 
    
  1097.       assertLog(['Suspend! [A]', 'Suspend! [B]', 'Suspend! [C]', 'Loading...']);
    
  1098. 
    
  1099.       await resolveText('A');
    
  1100.       await waitForPaint([
    
  1101.         'A',
    
  1102.         // The promises for B and C have now been thrown twice
    
  1103.         'Suspend! [B]',
    
  1104.         'Suspend! [C]',
    
  1105.       ]);
    
  1106. 
    
  1107.       await resolveText('B');
    
  1108.       await waitForPaint([
    
  1109.         // Even though the promise for B was thrown twice, we should only
    
  1110.         // re-render once.
    
  1111.         'B',
    
  1112.         // The promise for C has now been thrown three times
    
  1113.         'Suspend! [C]',
    
  1114.       ]);
    
  1115. 
    
  1116.       await resolveText('C');
    
  1117.       await waitForPaint([
    
  1118.         // Even though the promise for C was thrown three times, we should only
    
  1119.         // re-render once.
    
  1120.         'C',
    
  1121.       ]);
    
  1122.     });
    
  1123. 
    
  1124.     it('#14162', async () => {
    
  1125.       const {lazy} = React;
    
  1126. 
    
  1127.       function Hello() {
    
  1128.         return <span>hello</span>;
    
  1129.       }
    
  1130. 
    
  1131.       async function fetchComponent() {
    
  1132.         return new Promise(r => {
    
  1133.           // simulating a delayed import() call
    
  1134.           setTimeout(r, 1000, {default: Hello});
    
  1135.         });
    
  1136.       }
    
  1137. 
    
  1138.       const LazyHello = lazy(fetchComponent);
    
  1139. 
    
  1140.       class App extends React.Component {
    
  1141.         state = {render: false};
    
  1142. 
    
  1143.         componentDidMount() {
    
  1144.           setTimeout(() => this.setState({render: true}));
    
  1145.         }
    
  1146. 
    
  1147.         render() {
    
  1148.           return (
    
  1149.             <Suspense fallback={<span>loading...</span>}>
    
  1150.               {this.state.render && <LazyHello />}
    
  1151.             </Suspense>
    
  1152.           );
    
  1153.         }
    
  1154.       }
    
  1155. 
    
  1156.       const root = ReactTestRenderer.create(null);
    
  1157. 
    
  1158.       await act(() => {
    
  1159.         root.update(<App name="world" />);
    
  1160.       });
    
  1161.     });
    
  1162. 
    
  1163.     it('updates memoized child of suspense component when context updates (simple memo)', async () => {
    
  1164.       const {useContext, createContext, useState, memo} = React;
    
  1165. 
    
  1166.       const ValueContext = createContext(null);
    
  1167. 
    
  1168.       const MemoizedChild = memo(function MemoizedChild() {
    
  1169.         const text = useContext(ValueContext);
    
  1170.         return <Text text={readText(text)} />;
    
  1171.       });
    
  1172. 
    
  1173.       let setValue;
    
  1174.       function App() {
    
  1175.         const [value, _setValue] = useState('default');
    
  1176.         setValue = _setValue;
    
  1177. 
    
  1178.         return (
    
  1179.           <ValueContext.Provider value={value}>
    
  1180.             <Suspense fallback={<Text text="Loading..." />}>
    
  1181.               <MemoizedChild />
    
  1182.             </Suspense>
    
  1183.           </ValueContext.Provider>
    
  1184.         );
    
  1185.       }
    
  1186. 
    
  1187.       const root = ReactTestRenderer.create(<App />);
    
  1188.       assertLog(['Suspend! [default]', 'Loading...']);
    
  1189. 
    
  1190.       await resolveText('default');
    
  1191.       await waitForPaint(['default']);
    
  1192.       expect(root).toMatchRenderedOutput('default');
    
  1193. 
    
  1194.       await act(() => setValue('new value'));
    
  1195.       assertLog(['Suspend! [new value]', 'Loading...']);
    
  1196. 
    
  1197.       await resolveText('new value');
    
  1198.       await waitForPaint(['new value']);
    
  1199.       expect(root).toMatchRenderedOutput('new value');
    
  1200.     });
    
  1201. 
    
  1202.     it('updates memoized child of suspense component when context updates (manual memo)', async () => {
    
  1203.       const {useContext, createContext, useState, memo} = React;
    
  1204. 
    
  1205.       const ValueContext = createContext(null);
    
  1206. 
    
  1207.       const MemoizedChild = memo(
    
  1208.         function MemoizedChild() {
    
  1209.           const text = useContext(ValueContext);
    
  1210.           return <Text text={readText(text)} />;
    
  1211.         },
    
  1212.         function areEqual(prevProps, nextProps) {
    
  1213.           return true;
    
  1214.         },
    
  1215.       );
    
  1216. 
    
  1217.       let setValue;
    
  1218.       function App() {
    
  1219.         const [value, _setValue] = useState('default');
    
  1220.         setValue = _setValue;
    
  1221. 
    
  1222.         return (
    
  1223.           <ValueContext.Provider value={value}>
    
  1224.             <Suspense fallback={<Text text="Loading..." />}>
    
  1225.               <MemoizedChild />
    
  1226.             </Suspense>
    
  1227.           </ValueContext.Provider>
    
  1228.         );
    
  1229.       }
    
  1230. 
    
  1231.       const root = ReactTestRenderer.create(<App />);
    
  1232.       assertLog(['Suspend! [default]', 'Loading...']);
    
  1233. 
    
  1234.       await resolveText('default');
    
  1235.       await waitForPaint(['default']);
    
  1236.       expect(root).toMatchRenderedOutput('default');
    
  1237. 
    
  1238.       await act(() => setValue('new value'));
    
  1239.       assertLog(['Suspend! [new value]', 'Loading...']);
    
  1240. 
    
  1241.       await resolveText('new value');
    
  1242.       await waitForPaint(['new value']);
    
  1243.       expect(root).toMatchRenderedOutput('new value');
    
  1244.     });
    
  1245. 
    
  1246.     it('updates memoized child of suspense component when context updates (function)', async () => {
    
  1247.       const {useContext, createContext, useState} = React;
    
  1248. 
    
  1249.       const ValueContext = createContext(null);
    
  1250. 
    
  1251.       function MemoizedChild() {
    
  1252.         const text = useContext(ValueContext);
    
  1253.         return <Text text={readText(text)} />;
    
  1254.       }
    
  1255. 
    
  1256.       let setValue;
    
  1257.       function App({children}) {
    
  1258.         const [value, _setValue] = useState('default');
    
  1259.         setValue = _setValue;
    
  1260. 
    
  1261.         return (
    
  1262.           <ValueContext.Provider value={value}>
    
  1263.             {children}
    
  1264.           </ValueContext.Provider>
    
  1265.         );
    
  1266.       }
    
  1267. 
    
  1268.       const root = ReactTestRenderer.create(
    
  1269.         <App>
    
  1270.           <Suspense fallback={<Text text="Loading..." />}>
    
  1271.             <MemoizedChild />
    
  1272.           </Suspense>
    
  1273.         </App>,
    
  1274.       );
    
  1275.       assertLog(['Suspend! [default]', 'Loading...']);
    
  1276. 
    
  1277.       await resolveText('default');
    
  1278.       await waitForPaint(['default']);
    
  1279.       expect(root).toMatchRenderedOutput('default');
    
  1280. 
    
  1281.       await act(() => setValue('new value'));
    
  1282.       assertLog(['Suspend! [new value]', 'Loading...']);
    
  1283. 
    
  1284.       await resolveText('new value');
    
  1285.       await waitForPaint(['new value']);
    
  1286.       expect(root).toMatchRenderedOutput('new value');
    
  1287.     });
    
  1288. 
    
  1289.     it('updates memoized child of suspense component when context updates (forwardRef)', async () => {
    
  1290.       const {forwardRef, useContext, createContext, useState} = React;
    
  1291. 
    
  1292.       const ValueContext = createContext(null);
    
  1293. 
    
  1294.       const MemoizedChild = forwardRef(function MemoizedChild() {
    
  1295.         const text = useContext(ValueContext);
    
  1296.         return <Text text={readText(text)} />;
    
  1297.       });
    
  1298. 
    
  1299.       let setValue;
    
  1300.       function App() {
    
  1301.         const [value, _setValue] = useState('default');
    
  1302.         setValue = _setValue;
    
  1303. 
    
  1304.         return (
    
  1305.           <ValueContext.Provider value={value}>
    
  1306.             <Suspense fallback={<Text text="Loading..." />}>
    
  1307.               <MemoizedChild />
    
  1308.             </Suspense>
    
  1309.           </ValueContext.Provider>
    
  1310.         );
    
  1311.       }
    
  1312. 
    
  1313.       const root = ReactTestRenderer.create(<App />);
    
  1314.       assertLog(['Suspend! [default]', 'Loading...']);
    
  1315. 
    
  1316.       await resolveText('default');
    
  1317.       await waitForPaint(['default']);
    
  1318.       expect(root).toMatchRenderedOutput('default');
    
  1319. 
    
  1320.       await act(() => setValue('new value'));
    
  1321.       assertLog(['Suspend! [new value]', 'Loading...']);
    
  1322. 
    
  1323.       await resolveText('new value');
    
  1324.       await waitForPaint(['new value']);
    
  1325.       expect(root).toMatchRenderedOutput('new value');
    
  1326.     });
    
  1327. 
    
  1328.     it('updates context consumer within child of suspended suspense component when context updates', async () => {
    
  1329.       const {createContext, useState} = React;
    
  1330. 
    
  1331.       const ValueContext = createContext(null);
    
  1332. 
    
  1333.       const promiseThatNeverResolves = new Promise(() => {});
    
  1334.       function Child() {
    
  1335.         return (
    
  1336.           <ValueContext.Consumer>
    
  1337.             {value => {
    
  1338.               Scheduler.log(`Received context value [${value}]`);
    
  1339.               if (value === 'default') return <Text text="default" />;
    
  1340.               throw promiseThatNeverResolves;
    
  1341.             }}
    
  1342.           </ValueContext.Consumer>
    
  1343.         );
    
  1344.       }
    
  1345. 
    
  1346.       let setValue;
    
  1347.       function Wrapper({children}) {
    
  1348.         const [value, _setValue] = useState('default');
    
  1349.         setValue = _setValue;
    
  1350.         return (
    
  1351.           <ValueContext.Provider value={value}>
    
  1352.             {children}
    
  1353.           </ValueContext.Provider>
    
  1354.         );
    
  1355.       }
    
  1356. 
    
  1357.       function App() {
    
  1358.         return (
    
  1359.           <Wrapper>
    
  1360.             <Suspense fallback={<Text text="Loading..." />}>
    
  1361.               <Child />
    
  1362.             </Suspense>
    
  1363.           </Wrapper>
    
  1364.         );
    
  1365.       }
    
  1366. 
    
  1367.       const root = ReactTestRenderer.create(<App />);
    
  1368.       assertLog(['Received context value [default]', 'default']);
    
  1369.       expect(root).toMatchRenderedOutput('default');
    
  1370. 
    
  1371.       await act(() => setValue('new value'));
    
  1372.       assertLog(['Received context value [new value]', 'Loading...']);
    
  1373.       expect(root).toMatchRenderedOutput('Loading...');
    
  1374. 
    
  1375.       await act(() => setValue('default'));
    
  1376.       assertLog(['Received context value [default]', 'default']);
    
  1377.       expect(root).toMatchRenderedOutput('default');
    
  1378.     });
    
  1379.   });
    
  1380. });