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. // Polyfills for test environment
    
  13. global.ReadableStream =
    
  14.   require('web-streams-polyfill/ponyfill/es6').ReadableStream;
    
  15. global.TextEncoder = require('util').TextEncoder;
    
  16. global.TextDecoder = require('util').TextDecoder;
    
  17. 
    
  18. // Don't wait before processing work on the server.
    
  19. // TODO: we can replace this with FlightServer.act().
    
  20. global.setImmediate = cb => cb();
    
  21. 
    
  22. let act;
    
  23. let use;
    
  24. let clientExports;
    
  25. let turbopackMap;
    
  26. let Stream;
    
  27. let React;
    
  28. let ReactDOMClient;
    
  29. let ReactServerDOMServer;
    
  30. let ReactServerDOMClient;
    
  31. let Suspense;
    
  32. 
    
  33. describe('ReactFlightDOM', () => {
    
  34.   beforeEach(() => {
    
  35.     // For this first reset we are going to load the dom-node version of react-server-dom-turbopack/server
    
  36.     // This can be thought of as essentially being the React Server Components scope with react-server
    
  37.     // condition
    
  38.     jest.resetModules();
    
  39. 
    
  40.     // Simulate the condition resolution
    
  41.     jest.mock('react-server-dom-turbopack/server', () =>
    
  42.       require('react-server-dom-turbopack/server.node.unbundled'),
    
  43.     );
    
  44.     jest.mock('react', () => require('react/react.shared-subset'));
    
  45. 
    
  46.     const TurbopackMock = require('./utils/TurbopackMock');
    
  47.     clientExports = TurbopackMock.clientExports;
    
  48.     turbopackMap = TurbopackMock.turbopackMap;
    
  49. 
    
  50.     ReactServerDOMServer = require('react-server-dom-turbopack/server');
    
  51. 
    
  52.     // This reset is to load modules for the SSR/Browser scope.
    
  53.     jest.resetModules();
    
  54.     __unmockReact();
    
  55.     act = require('internal-test-utils').act;
    
  56.     Stream = require('stream');
    
  57.     React = require('react');
    
  58.     use = React.use;
    
  59.     Suspense = React.Suspense;
    
  60.     ReactDOMClient = require('react-dom/client');
    
  61.     ReactServerDOMClient = require('react-server-dom-turbopack/client');
    
  62.   });
    
  63. 
    
  64.   function getTestStream() {
    
  65.     const writable = new Stream.PassThrough();
    
  66.     const readable = new ReadableStream({
    
  67.       start(controller) {
    
  68.         writable.on('data', chunk => {
    
  69.           controller.enqueue(chunk);
    
  70.         });
    
  71.         writable.on('end', () => {
    
  72.           controller.close();
    
  73.         });
    
  74.       },
    
  75.     });
    
  76.     return {
    
  77.       readable,
    
  78.       writable,
    
  79.     };
    
  80.   }
    
  81. 
    
  82.   it('should resolve HTML using Node streams', async () => {
    
  83.     function Text({children}) {
    
  84.       return <span>{children}</span>;
    
  85.     }
    
  86.     function HTML() {
    
  87.       return (
    
  88.         <div>
    
  89.           <Text>hello</Text>
    
  90.           <Text>world</Text>
    
  91.         </div>
    
  92.       );
    
  93.     }
    
  94. 
    
  95.     function App() {
    
  96.       const model = {
    
  97.         html: <HTML />,
    
  98.       };
    
  99.       return model;
    
  100.     }
    
  101. 
    
  102.     const {writable, readable} = getTestStream();
    
  103.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  104.       <App />,
    
  105.       turbopackMap,
    
  106.     );
    
  107.     pipe(writable);
    
  108.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  109.     const model = await response;
    
  110.     expect(model).toEqual({
    
  111.       html: (
    
  112.         <div>
    
  113.           <span>hello</span>
    
  114.           <span>world</span>
    
  115.         </div>
    
  116.       ),
    
  117.     });
    
  118.   });
    
  119. 
    
  120.   it('should resolve the root', async () => {
    
  121.     // Model
    
  122.     function Text({children}) {
    
  123.       return <span>{children}</span>;
    
  124.     }
    
  125.     function HTML() {
    
  126.       return (
    
  127.         <div>
    
  128.           <Text>hello</Text>
    
  129.           <Text>world</Text>
    
  130.         </div>
    
  131.       );
    
  132.     }
    
  133.     function RootModel() {
    
  134.       return {
    
  135.         html: <HTML />,
    
  136.       };
    
  137.     }
    
  138. 
    
  139.     // View
    
  140.     function Message({response}) {
    
  141.       return <section>{use(response).html}</section>;
    
  142.     }
    
  143.     function App({response}) {
    
  144.       return (
    
  145.         <Suspense fallback={<h1>Loading...</h1>}>
    
  146.           <Message response={response} />
    
  147.         </Suspense>
    
  148.       );
    
  149.     }
    
  150. 
    
  151.     const {writable, readable} = getTestStream();
    
  152.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  153.       <RootModel />,
    
  154.       turbopackMap,
    
  155.     );
    
  156.     pipe(writable);
    
  157.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  158. 
    
  159.     const container = document.createElement('div');
    
  160.     const root = ReactDOMClient.createRoot(container);
    
  161.     await act(() => {
    
  162.       root.render(<App response={response} />);
    
  163.     });
    
  164.     expect(container.innerHTML).toBe(
    
  165.       '<section><div><span>hello</span><span>world</span></div></section>',
    
  166.     );
    
  167.   });
    
  168. 
    
  169.   it('should unwrap async module references', async () => {
    
  170.     const AsyncModule = Promise.resolve(function AsyncModule({text}) {
    
  171.       return 'Async: ' + text;
    
  172.     });
    
  173. 
    
  174.     const AsyncModule2 = Promise.resolve({
    
  175.       exportName: 'Module',
    
  176.     });
    
  177. 
    
  178.     function Print({response}) {
    
  179.       return <p>{use(response)}</p>;
    
  180.     }
    
  181. 
    
  182.     function App({response}) {
    
  183.       return (
    
  184.         <Suspense fallback={<h1>Loading...</h1>}>
    
  185.           <Print response={response} />
    
  186.         </Suspense>
    
  187.       );
    
  188.     }
    
  189. 
    
  190.     const AsyncModuleRef = await clientExports(AsyncModule);
    
  191.     const AsyncModuleRef2 = await clientExports(AsyncModule2);
    
  192. 
    
  193.     const {writable, readable} = getTestStream();
    
  194.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  195.       <AsyncModuleRef text={AsyncModuleRef2.exportName} />,
    
  196.       turbopackMap,
    
  197.     );
    
  198.     pipe(writable);
    
  199.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  200. 
    
  201.     const container = document.createElement('div');
    
  202.     const root = ReactDOMClient.createRoot(container);
    
  203.     await act(() => {
    
  204.       root.render(<App response={response} />);
    
  205.     });
    
  206.     expect(container.innerHTML).toBe('<p>Async: Module</p>');
    
  207.   });
    
  208. });