1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @emails react-core
    
  8.  */
    
  9. 
    
  10. 'use strict';
    
  11. 
    
  12. import {
    
  13.   insertNodesAndExecuteScripts,
    
  14.   getVisibleChildren,
    
  15. } from '../test-utils/FizzTestUtils';
    
  16. 
    
  17. // Polyfills for test environment
    
  18. global.ReadableStream =
    
  19.   require('web-streams-polyfill/ponyfill/es6').ReadableStream;
    
  20. global.TextEncoder = require('util').TextEncoder;
    
  21. 
    
  22. let act;
    
  23. let assertLog;
    
  24. let waitForPaint;
    
  25. let container;
    
  26. let React;
    
  27. let Scheduler;
    
  28. let ReactDOMServer;
    
  29. let ReactDOMClient;
    
  30. let useDeferredValue;
    
  31. let Suspense;
    
  32. 
    
  33. describe('ReactDOMFizzForm', () => {
    
  34.   beforeEach(() => {
    
  35.     jest.resetModules();
    
  36.     React = require('react');
    
  37.     Scheduler = require('scheduler');
    
  38.     ReactDOMServer = require('react-dom/server.browser');
    
  39.     ReactDOMClient = require('react-dom/client');
    
  40.     useDeferredValue = React.useDeferredValue;
    
  41.     Suspense = React.Suspense;
    
  42.     act = require('internal-test-utils').act;
    
  43.     assertLog = require('internal-test-utils').assertLog;
    
  44.     waitForPaint = require('internal-test-utils').waitForPaint;
    
  45.     container = document.createElement('div');
    
  46.     document.body.appendChild(container);
    
  47.   });
    
  48. 
    
  49.   afterEach(() => {
    
  50.     document.body.removeChild(container);
    
  51.   });
    
  52. 
    
  53.   async function readIntoContainer(stream) {
    
  54.     const reader = stream.getReader();
    
  55.     let result = '';
    
  56.     while (true) {
    
  57.       const {done, value} = await reader.read();
    
  58.       if (done) {
    
  59.         break;
    
  60.       }
    
  61.       result += Buffer.from(value).toString('utf8');
    
  62.     }
    
  63.     const temp = document.createElement('div');
    
  64.     temp.innerHTML = result;
    
  65.     insertNodesAndExecuteScripts(temp, container, null);
    
  66.   }
    
  67. 
    
  68.   function Text({text}) {
    
  69.     Scheduler.log(text);
    
  70.     return text;
    
  71.   }
    
  72. 
    
  73.   // @gate enableUseDeferredValueInitialArg
    
  74.   it('returns initialValue argument, if provided', async () => {
    
  75.     function App() {
    
  76.       return useDeferredValue('Final', 'Initial');
    
  77.     }
    
  78. 
    
  79.     const stream = await ReactDOMServer.renderToReadableStream(<App />);
    
  80.     await readIntoContainer(stream);
    
  81.     expect(container.textContent).toEqual('Initial');
    
  82. 
    
  83.     // After hydration, it's updated to the final value
    
  84.     await act(() => ReactDOMClient.hydrateRoot(container, <App />));
    
  85.     expect(container.textContent).toEqual('Final');
    
  86.   });
    
  87. 
    
  88.   // @gate enableUseDeferredValueInitialArg
    
  89.   it(
    
  90.     'useDeferredValue during hydration has higher priority than remaining ' +
    
  91.       'incremental hydration',
    
  92.     async () => {
    
  93.       function B() {
    
  94.         const text = useDeferredValue('B [Final]', 'B [Initial]');
    
  95.         return <Text text={text} />;
    
  96.       }
    
  97. 
    
  98.       function App() {
    
  99.         return (
    
  100.           <div>
    
  101.             <span>
    
  102.               <Text text="A" />
    
  103.             </span>
    
  104.             <Suspense fallback={<Text text="Loading..." />}>
    
  105.               <span>
    
  106.                 <B />
    
  107.               </span>
    
  108.               <div>
    
  109.                 <Suspense fallback={<Text text="Loading..." />}>
    
  110.                   <span id="C" ref={cRef}>
    
  111.                     <Text text="C" />
    
  112.                   </span>
    
  113.                 </Suspense>
    
  114.               </div>
    
  115.             </Suspense>
    
  116.           </div>
    
  117.         );
    
  118.       }
    
  119. 
    
  120.       const cRef = React.createRef();
    
  121. 
    
  122.       // The server renders using the "initial" value for B.
    
  123.       const stream = await ReactDOMServer.renderToReadableStream(<App />);
    
  124.       await readIntoContainer(stream);
    
  125.       assertLog(['A', 'B [Initial]', 'C']);
    
  126.       expect(getVisibleChildren(container)).toEqual(
    
  127.         <div>
    
  128.           <span>A</span>
    
  129.           <span>B [Initial]</span>
    
  130.           <div>
    
  131.             <span id="C">C</span>
    
  132.           </div>
    
  133.         </div>,
    
  134.       );
    
  135. 
    
  136.       const serverRenderedC = document.getElementById('C');
    
  137. 
    
  138.       // On the client, we first hydrate the initial value, then upgrade
    
  139.       // to final.
    
  140.       await act(async () => {
    
  141.         ReactDOMClient.hydrateRoot(container, <App />);
    
  142. 
    
  143.         // First the outermost Suspense boundary hydrates.
    
  144.         await waitForPaint(['A']);
    
  145.         expect(cRef.current).toBe(null);
    
  146. 
    
  147.         // Then the next level hydrates. This level includes a useDeferredValue,
    
  148.         // so we should prioritize upgrading it before we proceed to hydrating
    
  149.         // additional levels.
    
  150.         await waitForPaint(['B [Initial]']);
    
  151.         expect(getVisibleChildren(container)).toEqual(
    
  152.           <div>
    
  153.             <span>A</span>
    
  154.             <span>B [Initial]</span>
    
  155.             <div>
    
  156.               <span id="C">C</span>
    
  157.             </div>
    
  158.           </div>,
    
  159.         );
    
  160.         expect(cRef.current).toBe(null);
    
  161. 
    
  162.         // This paint should only update B. C should still be dehydrated.
    
  163.         await waitForPaint(['B [Final]']);
    
  164.         expect(getVisibleChildren(container)).toEqual(
    
  165.           <div>
    
  166.             <span>A</span>
    
  167.             <span>B [Final]</span>
    
  168.             <div>
    
  169.               <span id="C">C</span>
    
  170.             </div>
    
  171.           </div>,
    
  172.         );
    
  173.         expect(cRef.current).toBe(null);
    
  174.       });
    
  175.       // Finally we can hydrate C
    
  176.       assertLog(['C']);
    
  177.       expect(getVisibleChildren(container)).toEqual(
    
  178.         <div>
    
  179.           <span>A</span>
    
  180.           <span>B [Final]</span>
    
  181.           <div>
    
  182.             <span id="C">C</span>
    
  183.           </div>
    
  184.         </div>,
    
  185.       );
    
  186.       expect(cRef.current).toBe(serverRenderedC);
    
  187.     },
    
  188.   );
    
  189. });