1. let React;
    
  2. let ReactFeatureFlags;
    
  3. let ReactNoop;
    
  4. let Scheduler;
    
  5. let waitForAll;
    
  6. let assertLog;
    
  7. let ReactCache;
    
  8. let Suspense;
    
  9. let TextResource;
    
  10. let act;
    
  11. 
    
  12. describe('ReactBlockingMode', () => {
    
  13.   beforeEach(() => {
    
  14.     jest.resetModules();
    
  15.     ReactFeatureFlags = require('shared/ReactFeatureFlags');
    
  16. 
    
  17.     ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
    
  18.     React = require('react');
    
  19.     ReactNoop = require('react-noop-renderer');
    
  20.     Scheduler = require('scheduler');
    
  21.     ReactCache = require('react-cache');
    
  22.     Suspense = React.Suspense;
    
  23. 
    
  24.     const InternalTestUtils = require('internal-test-utils');
    
  25.     waitForAll = InternalTestUtils.waitForAll;
    
  26.     assertLog = InternalTestUtils.assertLog;
    
  27.     act = InternalTestUtils.act;
    
  28. 
    
  29.     TextResource = ReactCache.unstable_createResource(
    
  30.       ([text, ms = 0]) => {
    
  31.         return new Promise((resolve, reject) =>
    
  32.           setTimeout(() => {
    
  33.             Scheduler.log(`Promise resolved [${text}]`);
    
  34.             resolve(text);
    
  35.           }, ms),
    
  36.         );
    
  37.       },
    
  38.       ([text, ms]) => text,
    
  39.     );
    
  40.   });
    
  41. 
    
  42.   function Text(props) {
    
  43.     Scheduler.log(props.text);
    
  44.     return props.text;
    
  45.   }
    
  46. 
    
  47.   function AsyncText(props) {
    
  48.     const text = props.text;
    
  49.     try {
    
  50.       TextResource.read([props.text, props.ms]);
    
  51.       Scheduler.log(text);
    
  52.       return props.text;
    
  53.     } catch (promise) {
    
  54.       if (typeof promise.then === 'function') {
    
  55.         Scheduler.log(`Suspend! [${text}]`);
    
  56.       } else {
    
  57.         Scheduler.log(`Error! [${text}]`);
    
  58.       }
    
  59.       throw promise;
    
  60.     }
    
  61.   }
    
  62. 
    
  63.   it('updates flush without yielding in the next event', async () => {
    
  64.     const root = ReactNoop.createRoot();
    
  65. 
    
  66.     root.render(
    
  67.       <>
    
  68.         <Text text="A" />
    
  69.         <Text text="B" />
    
  70.         <Text text="C" />
    
  71.       </>,
    
  72.     );
    
  73. 
    
  74.     // Nothing should have rendered yet
    
  75.     expect(root).toMatchRenderedOutput(null);
    
  76. 
    
  77.     await waitForAll(['A', 'B', 'C']);
    
  78.     expect(root).toMatchRenderedOutput('ABC');
    
  79.   });
    
  80. 
    
  81.   it('layout updates flush synchronously in same event', async () => {
    
  82.     const {useLayoutEffect} = React;
    
  83. 
    
  84.     function App() {
    
  85.       useLayoutEffect(() => {
    
  86.         Scheduler.log('Layout effect');
    
  87.       });
    
  88.       return <Text text="Hi" />;
    
  89.     }
    
  90. 
    
  91.     const root = ReactNoop.createRoot();
    
  92.     root.render(<App />);
    
  93.     expect(root).toMatchRenderedOutput(null);
    
  94.     assertLog([]);
    
  95. 
    
  96.     await waitForAll(['Hi', 'Layout effect']);
    
  97.     expect(root).toMatchRenderedOutput('Hi');
    
  98.   });
    
  99. 
    
  100.   it('uses proper Suspense semantics, not legacy ones', async () => {
    
  101.     const root = ReactNoop.createRoot();
    
  102.     root.render(
    
  103.       <Suspense fallback={<Text text="Loading..." />}>
    
  104.         <span>
    
  105.           <Text text="A" />
    
  106.         </span>
    
  107.         <span>
    
  108.           <AsyncText text="B" />
    
  109.         </span>
    
  110.         <span>
    
  111.           <Text text="C" />
    
  112.         </span>
    
  113.       </Suspense>,
    
  114.     );
    
  115. 
    
  116.     await waitForAll(['A', 'Suspend! [B]', 'Loading...']);
    
  117.     // In Legacy Mode, A and B would mount in a hidden primary tree. In
    
  118.     // Concurrent Mode, nothing in the primary tree should mount. But the
    
  119.     // fallback should mount immediately.
    
  120.     expect(root).toMatchRenderedOutput('Loading...');
    
  121. 
    
  122.     await act(() => jest.advanceTimersByTime(1000));
    
  123.     assertLog(['Promise resolved [B]', 'A', 'B', 'C']);
    
  124.     expect(root).toMatchRenderedOutput(
    
  125.       <>
    
  126.         <span>A</span>
    
  127.         <span>B</span>
    
  128.         <span>C</span>
    
  129.       </>,
    
  130.     );
    
  131.   });
    
  132. 
    
  133.   it('flushSync does not flush batched work', async () => {
    
  134.     const {useState, forwardRef, useImperativeHandle} = React;
    
  135.     const root = ReactNoop.createRoot();
    
  136. 
    
  137.     const Foo = forwardRef(({label}, ref) => {
    
  138.       const [step, setStep] = useState(0);
    
  139.       useImperativeHandle(ref, () => ({setStep}));
    
  140.       return <Text text={label + step} />;
    
  141.     });
    
  142. 
    
  143.     const foo1 = React.createRef(null);
    
  144.     const foo2 = React.createRef(null);
    
  145.     root.render(
    
  146.       <>
    
  147.         <Foo label="A" ref={foo1} />
    
  148.         <Foo label="B" ref={foo2} />
    
  149.       </>,
    
  150.     );
    
  151. 
    
  152.     await waitForAll(['A0', 'B0']);
    
  153.     expect(root).toMatchRenderedOutput('A0B0');
    
  154. 
    
  155.     // Schedule a batched update to the first sibling
    
  156.     ReactNoop.batchedUpdates(() => foo1.current.setStep(1));
    
  157. 
    
  158.     // Before it flushes, update the second sibling inside flushSync
    
  159.     ReactNoop.batchedUpdates(() =>
    
  160.       ReactNoop.flushSync(() => {
    
  161.         foo2.current.setStep(1);
    
  162.       }),
    
  163.     );
    
  164. 
    
  165.     // Now flush the first update
    
  166.     if (gate(flags => flags.enableUnifiedSyncLane)) {
    
  167.       assertLog(['A1', 'B1']);
    
  168.       expect(root).toMatchRenderedOutput('A1B1');
    
  169.     } else {
    
  170.       // Only the second update should have flushed synchronously
    
  171.       assertLog(['B1']);
    
  172.       expect(root).toMatchRenderedOutput('A0B1');
    
  173. 
    
  174.       // Now flush the first update
    
  175.       await waitForAll(['A1']);
    
  176.       expect(root).toMatchRenderedOutput('A1B1');
    
  177.     }
    
  178.   });
    
  179. });