1. let React;
    
  2. let ReactNoop;
    
  3. let Scheduler;
    
  4. let ContinuousEventPriority;
    
  5. let startTransition;
    
  6. let useState;
    
  7. let useEffect;
    
  8. let act;
    
  9. let waitFor;
    
  10. let waitForPaint;
    
  11. let assertLog;
    
  12. 
    
  13. describe('ReactUpdatePriority', () => {
    
  14.   beforeEach(() => {
    
  15.     jest.resetModules();
    
  16. 
    
  17.     React = require('react');
    
  18.     ReactNoop = require('react-noop-renderer');
    
  19.     Scheduler = require('scheduler');
    
  20.     act = require('internal-test-utils').act;
    
  21.     ContinuousEventPriority =
    
  22.       require('react-reconciler/constants').ContinuousEventPriority;
    
  23.     startTransition = React.startTransition;
    
  24.     useState = React.useState;
    
  25.     useEffect = React.useEffect;
    
  26. 
    
  27.     const InternalTestUtils = require('internal-test-utils');
    
  28.     waitFor = InternalTestUtils.waitFor;
    
  29.     waitForPaint = InternalTestUtils.waitForPaint;
    
  30.     assertLog = InternalTestUtils.assertLog;
    
  31.   });
    
  32. 
    
  33.   function Text({text}) {
    
  34.     Scheduler.log(text);
    
  35.     return text;
    
  36.   }
    
  37. 
    
  38.   test('setState inside passive effect triggered by sync update should have default priority', async () => {
    
  39.     const root = ReactNoop.createRoot();
    
  40. 
    
  41.     function App() {
    
  42.       const [state, setState] = useState(1);
    
  43.       useEffect(() => {
    
  44.         setState(2);
    
  45.       }, []);
    
  46.       return <Text text={state} />;
    
  47.     }
    
  48. 
    
  49.     await act(() => {
    
  50.       ReactNoop.flushSync(() => {
    
  51.         root.render(<App />);
    
  52.       });
    
  53.       // Should not have flushed the effect update yet
    
  54.       assertLog([1]);
    
  55.     });
    
  56.     assertLog([2]);
    
  57.   });
    
  58. 
    
  59.   test('setState inside passive effect triggered by idle update should have idle priority', async () => {
    
  60.     const root = ReactNoop.createRoot();
    
  61. 
    
  62.     let setDefaultState;
    
  63.     function App() {
    
  64.       const [idleState, setIdleState] = useState(1);
    
  65.       const [defaultState, _setDefaultState] = useState(1);
    
  66.       setDefaultState = _setDefaultState;
    
  67.       useEffect(() => {
    
  68.         Scheduler.log('Idle update');
    
  69.         setIdleState(2);
    
  70.       }, []);
    
  71.       return <Text text={`Idle: ${idleState}, Default: ${defaultState}`} />;
    
  72.     }
    
  73. 
    
  74.     await act(async () => {
    
  75.       ReactNoop.idleUpdates(() => {
    
  76.         root.render(<App />);
    
  77.       });
    
  78.       // Should not have flushed the effect update yet
    
  79.       await waitForPaint(['Idle: 1, Default: 1']);
    
  80. 
    
  81.       // Schedule another update at default priority
    
  82.       setDefaultState(2);
    
  83. 
    
  84.       // The default update flushes first, because
    
  85.       await waitForPaint([
    
  86.         // Idle update is scheduled
    
  87.         'Idle update',
    
  88. 
    
  89.         // The default update flushes first, without including the idle update
    
  90.         'Idle: 1, Default: 2',
    
  91.       ]);
    
  92.     });
    
  93.     // Now the idle update has flushed
    
  94.     assertLog(['Idle: 2, Default: 2']);
    
  95.   });
    
  96. 
    
  97.   test('continuous updates should interrupt transitions', async () => {
    
  98.     const root = ReactNoop.createRoot();
    
  99. 
    
  100.     let setCounter;
    
  101.     let setIsHidden;
    
  102.     function App() {
    
  103.       const [counter, _setCounter] = useState(1);
    
  104.       const [isHidden, _setIsHidden] = useState(false);
    
  105.       setCounter = _setCounter;
    
  106.       setIsHidden = _setIsHidden;
    
  107.       if (isHidden) {
    
  108.         return <Text text={'(hidden)'} />;
    
  109.       }
    
  110.       return (
    
  111.         <>
    
  112.           <Text text={'A' + counter} />
    
  113.           <Text text={'B' + counter} />
    
  114.           <Text text={'C' + counter} />
    
  115.         </>
    
  116.       );
    
  117.     }
    
  118. 
    
  119.     await act(() => {
    
  120.       root.render(<App />);
    
  121.     });
    
  122.     assertLog(['A1', 'B1', 'C1']);
    
  123.     expect(root).toMatchRenderedOutput('A1B1C1');
    
  124. 
    
  125.     await act(async () => {
    
  126.       startTransition(() => {
    
  127.         setCounter(2);
    
  128.       });
    
  129.       await waitFor(['A2']);
    
  130.       ReactNoop.unstable_runWithPriority(ContinuousEventPriority, () => {
    
  131.         setIsHidden(true);
    
  132.       });
    
  133.     });
    
  134.     assertLog([
    
  135.       // Because the hide update has continuous priority, it should interrupt the
    
  136.       // in-progress transition
    
  137.       '(hidden)',
    
  138.       // When the transition resumes, it's a no-op because the children are
    
  139.       // now hidden.
    
  140.       '(hidden)',
    
  141.     ]);
    
  142.     expect(root).toMatchRenderedOutput('(hidden)');
    
  143.   });
    
  144. });