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 clientModuleError;
    
  26. let webpackMap;
    
  27. let Stream;
    
  28. let FlightReact;
    
  29. let React;
    
  30. let FlightReactDOM;
    
  31. let ReactDOMClient;
    
  32. let ReactServerDOMServer;
    
  33. let ReactServerDOMClient;
    
  34. let ReactDOMFizzServer;
    
  35. let Suspense;
    
  36. let ErrorBoundary;
    
  37. let JSDOM;
    
  38. 
    
  39. describe('ReactFlightDOM', () => {
    
  40.   beforeEach(() => {
    
  41.     // For this first reset we are going to load the dom-node version of react-server-dom-webpack/server
    
  42.     // This can be thought of as essentially being the React Server Components scope with react-server
    
  43.     // condition
    
  44.     jest.resetModules();
    
  45. 
    
  46.     JSDOM = require('jsdom').JSDOM;
    
  47. 
    
  48.     // Simulate the condition resolution
    
  49.     jest.mock('react-server-dom-webpack/server', () =>
    
  50.       require('react-server-dom-webpack/server.node.unbundled'),
    
  51.     );
    
  52.     jest.mock('react', () => require('react/react.shared-subset'));
    
  53. 
    
  54.     const WebpackMock = require('./utils/WebpackMock');
    
  55.     clientExports = WebpackMock.clientExports;
    
  56.     clientModuleError = WebpackMock.clientModuleError;
    
  57.     webpackMap = WebpackMock.webpackMap;
    
  58. 
    
  59.     ReactServerDOMServer = require('react-server-dom-webpack/server');
    
  60.     FlightReact = require('react');
    
  61.     FlightReactDOM = require('react-dom');
    
  62. 
    
  63.     // This reset is to load modules for the SSR/Browser scope.
    
  64.     jest.unmock('react-server-dom-webpack/server');
    
  65.     __unmockReact();
    
  66.     jest.resetModules();
    
  67.     act = require('internal-test-utils').act;
    
  68.     Stream = require('stream');
    
  69.     React = require('react');
    
  70.     use = React.use;
    
  71.     Suspense = React.Suspense;
    
  72.     ReactDOMClient = require('react-dom/client');
    
  73.     ReactDOMFizzServer = require('react-dom/server.node');
    
  74.     ReactServerDOMClient = require('react-server-dom-webpack/client');
    
  75. 
    
  76.     ErrorBoundary = class extends React.Component {
    
  77.       state = {hasError: false, error: null};
    
  78.       static getDerivedStateFromError(error) {
    
  79.         return {
    
  80.           hasError: true,
    
  81.           error,
    
  82.         };
    
  83.       }
    
  84.       render() {
    
  85.         if (this.state.hasError) {
    
  86.           return this.props.fallback(this.state.error);
    
  87.         }
    
  88.         return this.props.children;
    
  89.       }
    
  90.     };
    
  91.   });
    
  92. 
    
  93.   function getTestStream() {
    
  94.     const writable = new Stream.PassThrough();
    
  95.     const readable = new ReadableStream({
    
  96.       start(controller) {
    
  97.         writable.on('data', chunk => {
    
  98.           controller.enqueue(chunk);
    
  99.         });
    
  100.         writable.on('end', () => {
    
  101.           controller.close();
    
  102.         });
    
  103.       },
    
  104.     });
    
  105.     return {
    
  106.       readable,
    
  107.       writable,
    
  108.     };
    
  109.   }
    
  110. 
    
  111.   const theInfinitePromise = new Promise(() => {});
    
  112.   function InfiniteSuspend() {
    
  113.     throw theInfinitePromise;
    
  114.   }
    
  115. 
    
  116.   function getMeaningfulChildren(element) {
    
  117.     const children = [];
    
  118.     let node = element.firstChild;
    
  119.     while (node) {
    
  120.       if (node.nodeType === 1) {
    
  121.         if (
    
  122.           // some tags are ambiguous and might be hidden because they look like non-meaningful children
    
  123.           // so we have a global override where if this data attribute is included we also include the node
    
  124.           node.hasAttribute('data-meaningful') ||
    
  125.           (node.tagName === 'SCRIPT' &&
    
  126.             node.hasAttribute('src') &&
    
  127.             node.hasAttribute('async')) ||
    
  128.           (node.tagName !== 'SCRIPT' &&
    
  129.             node.tagName !== 'TEMPLATE' &&
    
  130.             node.tagName !== 'template' &&
    
  131.             !node.hasAttribute('hidden') &&
    
  132.             !node.hasAttribute('aria-hidden'))
    
  133.         ) {
    
  134.           const props = {};
    
  135.           const attributes = node.attributes;
    
  136.           for (let i = 0; i < attributes.length; i++) {
    
  137.             if (
    
  138.               attributes[i].name === 'id' &&
    
  139.               attributes[i].value.includes(':')
    
  140.             ) {
    
  141.               // We assume this is a React added ID that's a non-visual implementation detail.
    
  142.               continue;
    
  143.             }
    
  144.             props[attributes[i].name] = attributes[i].value;
    
  145.           }
    
  146.           props.children = getMeaningfulChildren(node);
    
  147.           children.push(React.createElement(node.tagName.toLowerCase(), props));
    
  148.         }
    
  149.       } else if (node.nodeType === 3) {
    
  150.         children.push(node.data);
    
  151.       }
    
  152.       node = node.nextSibling;
    
  153.     }
    
  154.     return children.length === 0
    
  155.       ? undefined
    
  156.       : children.length === 1
    
  157.       ? children[0]
    
  158.       : children;
    
  159.   }
    
  160. 
    
  161.   it('should resolve HTML using Node streams', async () => {
    
  162.     function Text({children}) {
    
  163.       return <span>{children}</span>;
    
  164.     }
    
  165.     function HTML() {
    
  166.       return (
    
  167.         <div>
    
  168.           <Text>hello</Text>
    
  169.           <Text>world</Text>
    
  170.         </div>
    
  171.       );
    
  172.     }
    
  173. 
    
  174.     function App() {
    
  175.       const model = {
    
  176.         html: <HTML />,
    
  177.       };
    
  178.       return model;
    
  179.     }
    
  180. 
    
  181.     const {writable, readable} = getTestStream();
    
  182.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  183.       <App />,
    
  184.       webpackMap,
    
  185.     );
    
  186.     pipe(writable);
    
  187.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  188.     const model = await response;
    
  189.     expect(model).toEqual({
    
  190.       html: (
    
  191.         <div>
    
  192.           <span>hello</span>
    
  193.           <span>world</span>
    
  194.         </div>
    
  195.       ),
    
  196.     });
    
  197.   });
    
  198. 
    
  199.   it('should resolve the root', async () => {
    
  200.     // Model
    
  201.     function Text({children}) {
    
  202.       return <span>{children}</span>;
    
  203.     }
    
  204.     function HTML() {
    
  205.       return (
    
  206.         <div>
    
  207.           <Text>hello</Text>
    
  208.           <Text>world</Text>
    
  209.         </div>
    
  210.       );
    
  211.     }
    
  212.     function RootModel() {
    
  213.       return {
    
  214.         html: <HTML />,
    
  215.       };
    
  216.     }
    
  217. 
    
  218.     // View
    
  219.     function Message({response}) {
    
  220.       return <section>{use(response).html}</section>;
    
  221.     }
    
  222.     function App({response}) {
    
  223.       return (
    
  224.         <Suspense fallback={<h1>Loading...</h1>}>
    
  225.           <Message response={response} />
    
  226.         </Suspense>
    
  227.       );
    
  228.     }
    
  229. 
    
  230.     const {writable, readable} = getTestStream();
    
  231.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  232.       <RootModel />,
    
  233.       webpackMap,
    
  234.     );
    
  235.     pipe(writable);
    
  236.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  237. 
    
  238.     const container = document.createElement('div');
    
  239.     const root = ReactDOMClient.createRoot(container);
    
  240.     await act(() => {
    
  241.       root.render(<App response={response} />);
    
  242.     });
    
  243.     expect(container.innerHTML).toBe(
    
  244.       '<section><div><span>hello</span><span>world</span></div></section>',
    
  245.     );
    
  246.   });
    
  247. 
    
  248.   it('should not get confused by $', async () => {
    
  249.     // Model
    
  250.     function RootModel() {
    
  251.       return {text: '$1'};
    
  252.     }
    
  253. 
    
  254.     // View
    
  255.     function Message({response}) {
    
  256.       return <p>{use(response).text}</p>;
    
  257.     }
    
  258.     function App({response}) {
    
  259.       return (
    
  260.         <Suspense fallback={<h1>Loading...</h1>}>
    
  261.           <Message response={response} />
    
  262.         </Suspense>
    
  263.       );
    
  264.     }
    
  265. 
    
  266.     const {writable, readable} = getTestStream();
    
  267.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  268.       <RootModel />,
    
  269.       webpackMap,
    
  270.     );
    
  271.     pipe(writable);
    
  272.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  273. 
    
  274.     const container = document.createElement('div');
    
  275.     const root = ReactDOMClient.createRoot(container);
    
  276.     await act(() => {
    
  277.       root.render(<App response={response} />);
    
  278.     });
    
  279.     expect(container.innerHTML).toBe('<p>$1</p>');
    
  280.   });
    
  281. 
    
  282.   it('should not get confused by @', async () => {
    
  283.     // Model
    
  284.     function RootModel() {
    
  285.       return {text: '@div'};
    
  286.     }
    
  287. 
    
  288.     // View
    
  289.     function Message({response}) {
    
  290.       return <p>{use(response).text}</p>;
    
  291.     }
    
  292.     function App({response}) {
    
  293.       return (
    
  294.         <Suspense fallback={<h1>Loading...</h1>}>
    
  295.           <Message response={response} />
    
  296.         </Suspense>
    
  297.       );
    
  298.     }
    
  299. 
    
  300.     const {writable, readable} = getTestStream();
    
  301.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  302.       <RootModel />,
    
  303.       webpackMap,
    
  304.     );
    
  305.     pipe(writable);
    
  306.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  307. 
    
  308.     const container = document.createElement('div');
    
  309.     const root = ReactDOMClient.createRoot(container);
    
  310.     await act(() => {
    
  311.       root.render(<App response={response} />);
    
  312.     });
    
  313.     expect(container.innerHTML).toBe('<p>@div</p>');
    
  314.   });
    
  315. 
    
  316.   it('should be able to esm compat test module references', async () => {
    
  317.     const ESMCompatModule = {
    
  318.       __esModule: true,
    
  319.       default: function ({greeting}) {
    
  320.         return greeting + ' World';
    
  321.       },
    
  322.       hi: 'Hello',
    
  323.     };
    
  324. 
    
  325.     function Print({response}) {
    
  326.       return <p>{use(response)}</p>;
    
  327.     }
    
  328. 
    
  329.     function App({response}) {
    
  330.       return (
    
  331.         <Suspense fallback={<h1>Loading...</h1>}>
    
  332.           <Print response={response} />
    
  333.         </Suspense>
    
  334.       );
    
  335.     }
    
  336. 
    
  337.     function interopWebpack(obj) {
    
  338.       // Basically what Webpack's ESM interop feature testing does.
    
  339.       if (typeof obj === 'object' && obj.__esModule) {
    
  340.         return obj;
    
  341.       }
    
  342.       return Object.assign({default: obj}, obj);
    
  343.     }
    
  344. 
    
  345.     const {default: Component, hi} = interopWebpack(
    
  346.       clientExports(ESMCompatModule),
    
  347.     );
    
  348. 
    
  349.     const {writable, readable} = getTestStream();
    
  350.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  351.       <Component greeting={hi} />,
    
  352.       webpackMap,
    
  353.     );
    
  354.     pipe(writable);
    
  355.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  356. 
    
  357.     const container = document.createElement('div');
    
  358.     const root = ReactDOMClient.createRoot(container);
    
  359.     await act(() => {
    
  360.       root.render(<App response={response} />);
    
  361.     });
    
  362.     expect(container.innerHTML).toBe('<p>Hello World</p>');
    
  363.   });
    
  364. 
    
  365.   it('should be able to render a named component export', async () => {
    
  366.     const Module = {
    
  367.       Component: function ({greeting}) {
    
  368.         return greeting + ' World';
    
  369.       },
    
  370.     };
    
  371. 
    
  372.     function Print({response}) {
    
  373.       return <p>{use(response)}</p>;
    
  374.     }
    
  375. 
    
  376.     function App({response}) {
    
  377.       return (
    
  378.         <Suspense fallback={<h1>Loading...</h1>}>
    
  379.           <Print response={response} />
    
  380.         </Suspense>
    
  381.       );
    
  382.     }
    
  383. 
    
  384.     const {Component} = clientExports(Module);
    
  385. 
    
  386.     const {writable, readable} = getTestStream();
    
  387.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  388.       <Component greeting={'Hello'} />,
    
  389.       webpackMap,
    
  390.     );
    
  391.     pipe(writable);
    
  392.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  393. 
    
  394.     const container = document.createElement('div');
    
  395.     const root = ReactDOMClient.createRoot(container);
    
  396.     await act(() => {
    
  397.       root.render(<App response={response} />);
    
  398.     });
    
  399.     expect(container.innerHTML).toBe('<p>Hello World</p>');
    
  400.   });
    
  401. 
    
  402.   it('should be able to render a module split named component export', async () => {
    
  403.     const Module = {
    
  404.       // This gets split into a separate module from the original one.
    
  405.       split: function ({greeting}) {
    
  406.         return greeting + ' World';
    
  407.       },
    
  408.     };
    
  409. 
    
  410.     function Print({response}) {
    
  411.       return <p>{use(response)}</p>;
    
  412.     }
    
  413. 
    
  414.     function App({response}) {
    
  415.       return (
    
  416.         <Suspense fallback={<h1>Loading...</h1>}>
    
  417.           <Print response={response} />
    
  418.         </Suspense>
    
  419.       );
    
  420.     }
    
  421. 
    
  422.     const {split: Component} = clientExports(Module);
    
  423. 
    
  424.     const {writable, readable} = getTestStream();
    
  425.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  426.       <Component greeting={'Hello'} />,
    
  427.       webpackMap,
    
  428.     );
    
  429.     pipe(writable);
    
  430.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  431. 
    
  432.     const container = document.createElement('div');
    
  433.     const root = ReactDOMClient.createRoot(container);
    
  434.     await act(() => {
    
  435.       root.render(<App response={response} />);
    
  436.     });
    
  437.     expect(container.innerHTML).toBe('<p>Hello World</p>');
    
  438.   });
    
  439. 
    
  440.   it('should unwrap async module references', async () => {
    
  441.     const AsyncModule = Promise.resolve(function AsyncModule({text}) {
    
  442.       return 'Async: ' + text;
    
  443.     });
    
  444. 
    
  445.     const AsyncModule2 = Promise.resolve({
    
  446.       exportName: 'Module',
    
  447.     });
    
  448. 
    
  449.     function Print({response}) {
    
  450.       return <p>{use(response)}</p>;
    
  451.     }
    
  452. 
    
  453.     function App({response}) {
    
  454.       return (
    
  455.         <Suspense fallback={<h1>Loading...</h1>}>
    
  456.           <Print response={response} />
    
  457.         </Suspense>
    
  458.       );
    
  459.     }
    
  460. 
    
  461.     const AsyncModuleRef = await clientExports(AsyncModule);
    
  462.     const AsyncModuleRef2 = await clientExports(AsyncModule2);
    
  463. 
    
  464.     const {writable, readable} = getTestStream();
    
  465.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  466.       <AsyncModuleRef text={AsyncModuleRef2.exportName} />,
    
  467.       webpackMap,
    
  468.     );
    
  469.     pipe(writable);
    
  470.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  471. 
    
  472.     const container = document.createElement('div');
    
  473.     const root = ReactDOMClient.createRoot(container);
    
  474.     await act(() => {
    
  475.       root.render(<App response={response} />);
    
  476.     });
    
  477.     expect(container.innerHTML).toBe('<p>Async: Module</p>');
    
  478.   });
    
  479. 
    
  480.   it('should unwrap async module references using use', async () => {
    
  481.     const AsyncModule = Promise.resolve('Async Text');
    
  482. 
    
  483.     function Print({response}) {
    
  484.       return use(response);
    
  485.     }
    
  486. 
    
  487.     function App({response}) {
    
  488.       return (
    
  489.         <Suspense fallback={<h1>Loading...</h1>}>
    
  490.           <Print response={response} />
    
  491.         </Suspense>
    
  492.       );
    
  493.     }
    
  494. 
    
  495.     const AsyncModuleRef = clientExports(AsyncModule);
    
  496. 
    
  497.     function ServerComponent() {
    
  498.       const text = FlightReact.use(AsyncModuleRef);
    
  499.       return <p>{text}</p>;
    
  500.     }
    
  501. 
    
  502.     const {writable, readable} = getTestStream();
    
  503.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  504.       <ServerComponent />,
    
  505.       webpackMap,
    
  506.     );
    
  507.     pipe(writable);
    
  508.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  509. 
    
  510.     const container = document.createElement('div');
    
  511.     const root = ReactDOMClient.createRoot(container);
    
  512.     await act(() => {
    
  513.       root.render(<App response={response} />);
    
  514.     });
    
  515.     expect(container.innerHTML).toBe('<p>Async Text</p>');
    
  516.   });
    
  517. 
    
  518.   it('should be able to import a name called "then"', async () => {
    
  519.     const thenExports = {
    
  520.       then: function then() {
    
  521.         return 'and then';
    
  522.       },
    
  523.     };
    
  524. 
    
  525.     function Print({response}) {
    
  526.       return <p>{use(response)}</p>;
    
  527.     }
    
  528. 
    
  529.     function App({response}) {
    
  530.       return (
    
  531.         <Suspense fallback={<h1>Loading...</h1>}>
    
  532.           <Print response={response} />
    
  533.         </Suspense>
    
  534.       );
    
  535.     }
    
  536. 
    
  537.     const ThenRef = clientExports(thenExports).then;
    
  538. 
    
  539.     const {writable, readable} = getTestStream();
    
  540.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  541.       <ThenRef />,
    
  542.       webpackMap,
    
  543.     );
    
  544.     pipe(writable);
    
  545.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  546. 
    
  547.     const container = document.createElement('div');
    
  548.     const root = ReactDOMClient.createRoot(container);
    
  549.     await act(() => {
    
  550.       root.render(<App response={response} />);
    
  551.     });
    
  552.     expect(container.innerHTML).toBe('<p>and then</p>');
    
  553.   });
    
  554. 
    
  555.   it('throws when accessing a member below the client exports', () => {
    
  556.     const ClientModule = clientExports({
    
  557.       Component: {deep: 'thing'},
    
  558.     });
    
  559.     function dotting() {
    
  560.       return ClientModule.Component.deep;
    
  561.     }
    
  562.     expect(dotting).toThrowError(
    
  563.       'Cannot access Component.deep on the server. ' +
    
  564.         'You cannot dot into a client module from a server component. ' +
    
  565.         'You can only pass the imported name through.',
    
  566.     );
    
  567.   });
    
  568. 
    
  569.   it('does not throw when React inspects any deep props', () => {
    
  570.     const ClientModule = clientExports({
    
  571.       Component: function () {},
    
  572.     });
    
  573.     <ClientModule.Component key="this adds instrumentation" />;
    
  574.   });
    
  575. 
    
  576.   it('throws when accessing a Context.Provider below the client exports', () => {
    
  577.     const Context = React.createContext();
    
  578.     const ClientModule = clientExports({
    
  579.       Context,
    
  580.     });
    
  581.     function dotting() {
    
  582.       return ClientModule.Context.Provider;
    
  583.     }
    
  584.     expect(dotting).toThrowError(
    
  585.       `Cannot render a Client Context Provider on the Server. ` +
    
  586.         `Instead, you can export a Client Component wrapper ` +
    
  587.         `that itself renders a Client Context Provider.`,
    
  588.     );
    
  589.   });
    
  590. 
    
  591.   it('should progressively reveal server components', async () => {
    
  592.     let reportedErrors = [];
    
  593. 
    
  594.     // Client Components
    
  595. 
    
  596.     function MyErrorBoundary({children}) {
    
  597.       return (
    
  598.         <ErrorBoundary
    
  599.           fallback={e => (
    
  600.             <p>
    
  601.               {__DEV__ ? e.message + ' + ' : null}
    
  602.               {e.digest}
    
  603.             </p>
    
  604.           )}>
    
  605.           {children}
    
  606.         </ErrorBoundary>
    
  607.       );
    
  608.     }
    
  609. 
    
  610.     // Model
    
  611.     function Text({children}) {
    
  612.       return children;
    
  613.     }
    
  614. 
    
  615.     function makeDelayedText() {
    
  616.       let _resolve, _reject;
    
  617.       let promise = new Promise((resolve, reject) => {
    
  618.         _resolve = () => {
    
  619.           promise = null;
    
  620.           resolve();
    
  621.         };
    
  622.         _reject = e => {
    
  623.           promise = null;
    
  624.           reject(e);
    
  625.         };
    
  626.       });
    
  627.       async function DelayedText({children}) {
    
  628.         await promise;
    
  629.         return <Text>{children}</Text>;
    
  630.       }
    
  631.       return [DelayedText, _resolve, _reject];
    
  632.     }
    
  633. 
    
  634.     const [Friends, resolveFriends] = makeDelayedText();
    
  635.     const [Name, resolveName] = makeDelayedText();
    
  636.     const [Posts, resolvePosts] = makeDelayedText();
    
  637.     const [Photos, resolvePhotos] = makeDelayedText();
    
  638.     const [Games, , rejectGames] = makeDelayedText();
    
  639. 
    
  640.     // View
    
  641.     function ProfileDetails({avatar}) {
    
  642.       return (
    
  643.         <div>
    
  644.           <Name>:name:</Name>
    
  645.           {avatar}
    
  646.         </div>
    
  647.       );
    
  648.     }
    
  649.     function ProfileSidebar({friends}) {
    
  650.       return (
    
  651.         <div>
    
  652.           <Photos>:photos:</Photos>
    
  653.           {friends}
    
  654.         </div>
    
  655.       );
    
  656.     }
    
  657.     function ProfilePosts({posts}) {
    
  658.       return <div>{posts}</div>;
    
  659.     }
    
  660.     function ProfileGames({games}) {
    
  661.       return <div>{games}</div>;
    
  662.     }
    
  663. 
    
  664.     const MyErrorBoundaryClient = clientExports(MyErrorBoundary);
    
  665. 
    
  666.     function ProfileContent() {
    
  667.       return (
    
  668.         <>
    
  669.           <ProfileDetails avatar={<Text>:avatar:</Text>} />
    
  670.           <Suspense fallback={<p>(loading sidebar)</p>}>
    
  671.             <ProfileSidebar friends={<Friends>:friends:</Friends>} />
    
  672.           </Suspense>
    
  673.           <Suspense fallback={<p>(loading posts)</p>}>
    
  674.             <ProfilePosts posts={<Posts>:posts:</Posts>} />
    
  675.           </Suspense>
    
  676.           <MyErrorBoundaryClient>
    
  677.             <Suspense fallback={<p>(loading games)</p>}>
    
  678.               <ProfileGames games={<Games>:games:</Games>} />
    
  679.             </Suspense>
    
  680.           </MyErrorBoundaryClient>
    
  681.         </>
    
  682.       );
    
  683.     }
    
  684. 
    
  685.     const model = {
    
  686.       rootContent: <ProfileContent />,
    
  687.     };
    
  688. 
    
  689.     function ProfilePage({response}) {
    
  690.       return use(response).rootContent;
    
  691.     }
    
  692. 
    
  693.     const {writable, readable} = getTestStream();
    
  694.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  695.       model,
    
  696.       webpackMap,
    
  697.       {
    
  698.         onError(x) {
    
  699.           reportedErrors.push(x);
    
  700.           return __DEV__ ? 'a dev digest' : `digest("${x.message}")`;
    
  701.         },
    
  702.       },
    
  703.     );
    
  704.     pipe(writable);
    
  705.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  706. 
    
  707.     const container = document.createElement('div');
    
  708.     const root = ReactDOMClient.createRoot(container);
    
  709.     await act(() => {
    
  710.       root.render(
    
  711.         <Suspense fallback={<p>(loading)</p>}>
    
  712.           <ProfilePage response={response} />
    
  713.         </Suspense>,
    
  714.       );
    
  715.     });
    
  716.     expect(container.innerHTML).toBe('<p>(loading)</p>');
    
  717. 
    
  718.     // This isn't enough to show anything.
    
  719.     await act(() => {
    
  720.       resolveFriends();
    
  721.     });
    
  722.     expect(container.innerHTML).toBe('<p>(loading)</p>');
    
  723. 
    
  724.     // We can now show the details. Sidebar and posts are still loading.
    
  725.     await act(() => {
    
  726.       resolveName();
    
  727.     });
    
  728.     // Advance time enough to trigger a nested fallback.
    
  729.     await act(() => {
    
  730.       jest.advanceTimersByTime(500);
    
  731.     });
    
  732.     expect(container.innerHTML).toBe(
    
  733.       '<div>:name::avatar:</div>' +
    
  734.         '<p>(loading sidebar)</p>' +
    
  735.         '<p>(loading posts)</p>' +
    
  736.         '<p>(loading games)</p>',
    
  737.     );
    
  738. 
    
  739.     expect(reportedErrors).toEqual([]);
    
  740. 
    
  741.     const theError = new Error('Game over');
    
  742.     // Let's *fail* loading games.
    
  743.     await act(async () => {
    
  744.       await rejectGames(theError);
    
  745.       await 'the inner async function';
    
  746.     });
    
  747.     const expectedGamesValue = __DEV__
    
  748.       ? '<p>Game over + a dev digest</p>'
    
  749.       : '<p>digest("Game over")</p>';
    
  750.     expect(container.innerHTML).toBe(
    
  751.       '<div>:name::avatar:</div>' +
    
  752.         '<p>(loading sidebar)</p>' +
    
  753.         '<p>(loading posts)</p>' +
    
  754.         expectedGamesValue,
    
  755.     );
    
  756. 
    
  757.     expect(reportedErrors).toEqual([theError]);
    
  758.     reportedErrors = [];
    
  759. 
    
  760.     // We can now show the sidebar.
    
  761.     await act(async () => {
    
  762.       await resolvePhotos();
    
  763.       await 'the inner async function';
    
  764.     });
    
  765.     expect(container.innerHTML).toBe(
    
  766.       '<div>:name::avatar:</div>' +
    
  767.         '<div>:photos::friends:</div>' +
    
  768.         '<p>(loading posts)</p>' +
    
  769.         expectedGamesValue,
    
  770.     );
    
  771. 
    
  772.     // Show everything.
    
  773.     await act(async () => {
    
  774.       await resolvePosts();
    
  775.       await 'the inner async function';
    
  776.     });
    
  777.     expect(container.innerHTML).toBe(
    
  778.       '<div>:name::avatar:</div>' +
    
  779.         '<div>:photos::friends:</div>' +
    
  780.         '<div>:posts:</div>' +
    
  781.         expectedGamesValue,
    
  782.     );
    
  783. 
    
  784.     expect(reportedErrors).toEqual([]);
    
  785.   });
    
  786. 
    
  787.   it('should preserve state of client components on refetch', async () => {
    
  788.     // Client
    
  789. 
    
  790.     function Page({response}) {
    
  791.       return use(response);
    
  792.     }
    
  793. 
    
  794.     function Input() {
    
  795.       return <input />;
    
  796.     }
    
  797. 
    
  798.     const InputClient = clientExports(Input);
    
  799. 
    
  800.     // Server
    
  801. 
    
  802.     function App({color}) {
    
  803.       // Verify both DOM and Client children.
    
  804.       return (
    
  805.         <div style={{color}}>
    
  806.           <input />
    
  807.           <InputClient />
    
  808.         </div>
    
  809.       );
    
  810.     }
    
  811. 
    
  812.     const container = document.createElement('div');
    
  813.     const root = ReactDOMClient.createRoot(container);
    
  814. 
    
  815.     const stream1 = getTestStream();
    
  816.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  817.       <App color="red" />,
    
  818.       webpackMap,
    
  819.     );
    
  820.     pipe(stream1.writable);
    
  821.     const response1 = ReactServerDOMClient.createFromReadableStream(
    
  822.       stream1.readable,
    
  823.     );
    
  824.     await act(() => {
    
  825.       root.render(
    
  826.         <Suspense fallback={<p>(loading)</p>}>
    
  827.           <Page response={response1} />
    
  828.         </Suspense>,
    
  829.       );
    
  830.     });
    
  831.     expect(container.children.length).toBe(1);
    
  832.     expect(container.children[0].tagName).toBe('DIV');
    
  833.     expect(container.children[0].style.color).toBe('red');
    
  834. 
    
  835.     // Change the DOM state for both inputs.
    
  836.     const inputA = container.children[0].children[0];
    
  837.     expect(inputA.tagName).toBe('INPUT');
    
  838.     inputA.value = 'hello';
    
  839.     const inputB = container.children[0].children[1];
    
  840.     expect(inputB.tagName).toBe('INPUT');
    
  841.     inputB.value = 'goodbye';
    
  842. 
    
  843.     const stream2 = getTestStream();
    
  844.     const {pipe: pipe2} = ReactServerDOMServer.renderToPipeableStream(
    
  845.       <App color="blue" />,
    
  846.       webpackMap,
    
  847.     );
    
  848.     pipe2(stream2.writable);
    
  849.     const response2 = ReactServerDOMClient.createFromReadableStream(
    
  850.       stream2.readable,
    
  851.     );
    
  852.     await act(() => {
    
  853.       root.render(
    
  854.         <Suspense fallback={<p>(loading)</p>}>
    
  855.           <Page response={response2} />
    
  856.         </Suspense>,
    
  857.       );
    
  858.     });
    
  859.     expect(container.children.length).toBe(1);
    
  860.     expect(container.children[0].tagName).toBe('DIV');
    
  861.     expect(container.children[0].style.color).toBe('blue');
    
  862. 
    
  863.     // Verify we didn't destroy the DOM for either input.
    
  864.     expect(inputA === container.children[0].children[0]).toBe(true);
    
  865.     expect(inputA.tagName).toBe('INPUT');
    
  866.     expect(inputA.value).toBe('hello');
    
  867.     expect(inputB === container.children[0].children[1]).toBe(true);
    
  868.     expect(inputB.tagName).toBe('INPUT');
    
  869.     expect(inputB.value).toBe('goodbye');
    
  870.   });
    
  871. 
    
  872.   it('should be able to complete after aborting and throw the reason client-side', async () => {
    
  873.     const reportedErrors = [];
    
  874. 
    
  875.     const {writable, readable} = getTestStream();
    
  876.     const {pipe, abort} = ReactServerDOMServer.renderToPipeableStream(
    
  877.       <div>
    
  878.         <InfiniteSuspend />
    
  879.       </div>,
    
  880.       webpackMap,
    
  881.       {
    
  882.         onError(x) {
    
  883.           reportedErrors.push(x);
    
  884.           const message = typeof x === 'string' ? x : x.message;
    
  885.           return __DEV__ ? 'a dev digest' : `digest("${message}")`;
    
  886.         },
    
  887.       },
    
  888.     );
    
  889.     pipe(writable);
    
  890.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  891. 
    
  892.     const container = document.createElement('div');
    
  893.     const root = ReactDOMClient.createRoot(container);
    
  894. 
    
  895.     function App({res}) {
    
  896.       return use(res);
    
  897.     }
    
  898. 
    
  899.     await act(() => {
    
  900.       root.render(
    
  901.         <ErrorBoundary
    
  902.           fallback={e => (
    
  903.             <p>
    
  904.               {__DEV__ ? e.message + ' + ' : null}
    
  905.               {e.digest}
    
  906.             </p>
    
  907.           )}>
    
  908.           <Suspense fallback={<p>(loading)</p>}>
    
  909.             <App res={response} />
    
  910.           </Suspense>
    
  911.         </ErrorBoundary>,
    
  912.       );
    
  913.     });
    
  914.     expect(container.innerHTML).toBe('<p>(loading)</p>');
    
  915. 
    
  916.     await act(() => {
    
  917.       abort('for reasons');
    
  918.     });
    
  919.     if (__DEV__) {
    
  920.       expect(container.innerHTML).toBe(
    
  921.         '<p>Error: for reasons + a dev digest</p>',
    
  922.       );
    
  923.     } else {
    
  924.       expect(container.innerHTML).toBe('<p>digest("for reasons")</p>');
    
  925.     }
    
  926. 
    
  927.     expect(reportedErrors).toEqual(['for reasons']);
    
  928.   });
    
  929. 
    
  930.   it('should be able to recover from a direct reference erroring client-side', async () => {
    
  931.     const reportedErrors = [];
    
  932. 
    
  933.     const ClientComponent = clientExports(function ({prop}) {
    
  934.       return 'This should never render';
    
  935.     });
    
  936. 
    
  937.     const ClientReference = clientModuleError(new Error('module init error'));
    
  938. 
    
  939.     const {writable, readable} = getTestStream();
    
  940.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  941.       <div>
    
  942.         <ClientComponent prop={ClientReference} />
    
  943.       </div>,
    
  944.       webpackMap,
    
  945.       {
    
  946.         onError(x) {
    
  947.           reportedErrors.push(x);
    
  948.         },
    
  949.       },
    
  950.     );
    
  951.     pipe(writable);
    
  952.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  953. 
    
  954.     const container = document.createElement('div');
    
  955.     const root = ReactDOMClient.createRoot(container);
    
  956. 
    
  957.     function App({res}) {
    
  958.       return use(res);
    
  959.     }
    
  960. 
    
  961.     await act(() => {
    
  962.       root.render(
    
  963.         <ErrorBoundary fallback={e => <p>{e.message}</p>}>
    
  964.           <Suspense fallback={<p>(loading)</p>}>
    
  965.             <App res={response} />
    
  966.           </Suspense>
    
  967.         </ErrorBoundary>,
    
  968.       );
    
  969.     });
    
  970.     expect(container.innerHTML).toBe('<p>module init error</p>');
    
  971. 
    
  972.     expect(reportedErrors).toEqual([]);
    
  973.   });
    
  974. 
    
  975.   it('should be able to recover from a direct reference erroring client-side async', async () => {
    
  976.     const reportedErrors = [];
    
  977. 
    
  978.     const ClientComponent = clientExports(function ({prop}) {
    
  979.       return 'This should never render';
    
  980.     });
    
  981. 
    
  982.     let rejectPromise;
    
  983.     const ClientReference = await clientExports(
    
  984.       new Promise((resolve, reject) => {
    
  985.         rejectPromise = reject;
    
  986.       }),
    
  987.     );
    
  988. 
    
  989.     const {writable, readable} = getTestStream();
    
  990.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  991.       <div>
    
  992.         <ClientComponent prop={ClientReference} />
    
  993.       </div>,
    
  994.       webpackMap,
    
  995.       {
    
  996.         onError(x) {
    
  997.           reportedErrors.push(x);
    
  998.         },
    
  999.       },
    
  1000.     );
    
  1001.     pipe(writable);
    
  1002.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  1003. 
    
  1004.     const container = document.createElement('div');
    
  1005.     const root = ReactDOMClient.createRoot(container);
    
  1006. 
    
  1007.     function App({res}) {
    
  1008.       return use(res);
    
  1009.     }
    
  1010. 
    
  1011.     await act(() => {
    
  1012.       root.render(
    
  1013.         <ErrorBoundary fallback={e => <p>{e.message}</p>}>
    
  1014.           <Suspense fallback={<p>(loading)</p>}>
    
  1015.             <App res={response} />
    
  1016.           </Suspense>
    
  1017.         </ErrorBoundary>,
    
  1018.       );
    
  1019.     });
    
  1020. 
    
  1021.     expect(container.innerHTML).toBe('<p>(loading)</p>');
    
  1022. 
    
  1023.     await act(() => {
    
  1024.       rejectPromise(new Error('async module init error'));
    
  1025.     });
    
  1026. 
    
  1027.     expect(container.innerHTML).toBe('<p>async module init error</p>');
    
  1028. 
    
  1029.     expect(reportedErrors).toEqual([]);
    
  1030.   });
    
  1031. 
    
  1032.   it('should be able to recover from a direct reference erroring server-side', async () => {
    
  1033.     const reportedErrors = [];
    
  1034. 
    
  1035.     const ClientComponent = clientExports(function ({prop}) {
    
  1036.       return 'This should never render';
    
  1037.     });
    
  1038. 
    
  1039.     // We simulate a bug in the Webpack bundler which causes an error on the server.
    
  1040.     for (const id in webpackMap) {
    
  1041.       Object.defineProperty(webpackMap, id, {
    
  1042.         get: () => {
    
  1043.           throw new Error('bug in the bundler');
    
  1044.         },
    
  1045.       });
    
  1046.     }
    
  1047. 
    
  1048.     const {writable, readable} = getTestStream();
    
  1049.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  1050.       <div>
    
  1051.         <ClientComponent />
    
  1052.       </div>,
    
  1053.       webpackMap,
    
  1054.       {
    
  1055.         onError(x) {
    
  1056.           reportedErrors.push(x.message);
    
  1057.           return __DEV__ ? 'a dev digest' : `digest("${x.message}")`;
    
  1058.         },
    
  1059.       },
    
  1060.     );
    
  1061.     pipe(writable);
    
  1062. 
    
  1063.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  1064. 
    
  1065.     const container = document.createElement('div');
    
  1066.     const root = ReactDOMClient.createRoot(container);
    
  1067. 
    
  1068.     function App({res}) {
    
  1069.       return use(res);
    
  1070.     }
    
  1071. 
    
  1072.     await act(() => {
    
  1073.       root.render(
    
  1074.         <ErrorBoundary
    
  1075.           fallback={e => (
    
  1076.             <p>
    
  1077.               {__DEV__ ? e.message + ' + ' : null}
    
  1078.               {e.digest}
    
  1079.             </p>
    
  1080.           )}>
    
  1081.           <Suspense fallback={<p>(loading)</p>}>
    
  1082.             <App res={response} />
    
  1083.           </Suspense>
    
  1084.         </ErrorBoundary>,
    
  1085.       );
    
  1086.     });
    
  1087.     if (__DEV__) {
    
  1088.       expect(container.innerHTML).toBe(
    
  1089.         '<p>bug in the bundler + a dev digest</p>',
    
  1090.       );
    
  1091.     } else {
    
  1092.       expect(container.innerHTML).toBe('<p>digest("bug in the bundler")</p>');
    
  1093.     }
    
  1094. 
    
  1095.     expect(reportedErrors).toEqual(['bug in the bundler']);
    
  1096.   });
    
  1097. 
    
  1098.   it('should pass a Promise through props and be able use() it on the client', async () => {
    
  1099.     async function getData() {
    
  1100.       return 'async hello';
    
  1101.     }
    
  1102. 
    
  1103.     function Component({data}) {
    
  1104.       const text = use(data);
    
  1105.       return <p>{text}</p>;
    
  1106.     }
    
  1107. 
    
  1108.     const ClientComponent = clientExports(Component);
    
  1109. 
    
  1110.     function ServerComponent() {
    
  1111.       const data = getData(); // no await here
    
  1112.       return <ClientComponent data={data} />;
    
  1113.     }
    
  1114. 
    
  1115.     function Print({response}) {
    
  1116.       return use(response);
    
  1117.     }
    
  1118. 
    
  1119.     function App({response}) {
    
  1120.       return (
    
  1121.         <Suspense fallback={<h1>Loading...</h1>}>
    
  1122.           <Print response={response} />
    
  1123.         </Suspense>
    
  1124.       );
    
  1125.     }
    
  1126. 
    
  1127.     const {writable, readable} = getTestStream();
    
  1128.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  1129.       <ServerComponent />,
    
  1130.       webpackMap,
    
  1131.     );
    
  1132.     pipe(writable);
    
  1133.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  1134. 
    
  1135.     const container = document.createElement('div');
    
  1136.     const root = ReactDOMClient.createRoot(container);
    
  1137.     await act(() => {
    
  1138.       root.render(<App response={response} />);
    
  1139.     });
    
  1140.     expect(container.innerHTML).toBe('<p>async hello</p>');
    
  1141.   });
    
  1142. 
    
  1143.   it('should throw on the client if a passed promise eventually rejects', async () => {
    
  1144.     const reportedErrors = [];
    
  1145.     const theError = new Error('Server throw');
    
  1146. 
    
  1147.     async function getData() {
    
  1148.       throw theError;
    
  1149.     }
    
  1150. 
    
  1151.     function Component({data}) {
    
  1152.       const text = use(data);
    
  1153.       return <p>{text}</p>;
    
  1154.     }
    
  1155. 
    
  1156.     const ClientComponent = clientExports(Component);
    
  1157. 
    
  1158.     function ServerComponent() {
    
  1159.       const data = getData(); // no await here
    
  1160.       return <ClientComponent data={data} />;
    
  1161.     }
    
  1162. 
    
  1163.     function Await({response}) {
    
  1164.       return use(response);
    
  1165.     }
    
  1166. 
    
  1167.     function App({response}) {
    
  1168.       return (
    
  1169.         <Suspense fallback={<h1>Loading...</h1>}>
    
  1170.           <ErrorBoundary
    
  1171.             fallback={e => (
    
  1172.               <p>
    
  1173.                 {__DEV__ ? e.message + ' + ' : null}
    
  1174.                 {e.digest}
    
  1175.               </p>
    
  1176.             )}>
    
  1177.             <Await response={response} />
    
  1178.           </ErrorBoundary>
    
  1179.         </Suspense>
    
  1180.       );
    
  1181.     }
    
  1182. 
    
  1183.     const {writable, readable} = getTestStream();
    
  1184.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  1185.       <ServerComponent />,
    
  1186.       webpackMap,
    
  1187.       {
    
  1188.         onError(x) {
    
  1189.           reportedErrors.push(x);
    
  1190.           return __DEV__ ? 'a dev digest' : `digest("${x.message}")`;
    
  1191.         },
    
  1192.       },
    
  1193.     );
    
  1194.     pipe(writable);
    
  1195.     const response = ReactServerDOMClient.createFromReadableStream(readable);
    
  1196. 
    
  1197.     const container = document.createElement('div');
    
  1198.     const root = ReactDOMClient.createRoot(container);
    
  1199.     await act(() => {
    
  1200.       root.render(<App response={response} />);
    
  1201.     });
    
  1202.     expect(container.innerHTML).toBe(
    
  1203.       __DEV__
    
  1204.         ? '<p>Server throw + a dev digest</p>'
    
  1205.         : '<p>digest("Server throw")</p>',
    
  1206.     );
    
  1207.     expect(reportedErrors).toEqual([theError]);
    
  1208.   });
    
  1209. 
    
  1210.   it('should support float methods when rendering in Fiber', async () => {
    
  1211.     function Component() {
    
  1212.       return <p>hello world</p>;
    
  1213.     }
    
  1214. 
    
  1215.     const ClientComponent = clientExports(Component);
    
  1216. 
    
  1217.     async function ServerComponent() {
    
  1218.       FlightReactDOM.prefetchDNS('d before');
    
  1219.       FlightReactDOM.preconnect('c before');
    
  1220.       FlightReactDOM.preconnect('c2 before', {crossOrigin: 'anonymous'});
    
  1221.       FlightReactDOM.preload('l before', {as: 'style'});
    
  1222.       FlightReactDOM.preloadModule('lm before');
    
  1223.       FlightReactDOM.preloadModule('lm2 before', {crossOrigin: 'anonymous'});
    
  1224.       FlightReactDOM.preinit('i before', {as: 'script'});
    
  1225.       FlightReactDOM.preinitModule('m before');
    
  1226.       FlightReactDOM.preinitModule('m2 before', {crossOrigin: 'anonymous'});
    
  1227.       await 1;
    
  1228.       FlightReactDOM.prefetchDNS('d after');
    
  1229.       FlightReactDOM.preconnect('c after');
    
  1230.       FlightReactDOM.preconnect('c2 after', {crossOrigin: 'anonymous'});
    
  1231.       FlightReactDOM.preload('l after', {as: 'style'});
    
  1232.       FlightReactDOM.preloadModule('lm after');
    
  1233.       FlightReactDOM.preloadModule('lm2 after', {crossOrigin: 'anonymous'});
    
  1234.       FlightReactDOM.preinit('i after', {as: 'script'});
    
  1235.       FlightReactDOM.preinitModule('m after');
    
  1236.       FlightReactDOM.preinitModule('m2 after', {crossOrigin: 'anonymous'});
    
  1237.       return <ClientComponent />;
    
  1238.     }
    
  1239. 
    
  1240.     const {writable, readable} = getTestStream();
    
  1241.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  1242.       <ServerComponent />,
    
  1243.       webpackMap,
    
  1244.     );
    
  1245.     pipe(writable);
    
  1246. 
    
  1247.     let response = null;
    
  1248.     function getResponse() {
    
  1249.       if (response === null) {
    
  1250.         response = ReactServerDOMClient.createFromReadableStream(readable);
    
  1251.       }
    
  1252.       return response;
    
  1253.     }
    
  1254. 
    
  1255.     function App() {
    
  1256.       return getResponse();
    
  1257.     }
    
  1258. 
    
  1259.     // We pause to allow the float call after the await point to process before the
    
  1260.     // HostDispatcher gets set for Fiber by createRoot. This is only needed in testing
    
  1261.     // because the module graphs are not different and the HostDispatcher is shared.
    
  1262.     // In a real environment the Fiber and Flight code would each have their own independent
    
  1263.     // dispatcher.
    
  1264.     // @TODO consider what happens when Server-Components-On-The-Client exist. we probably
    
  1265.     // want to use the Fiber HostDispatcher there too since it is more about the host than the runtime
    
  1266.     // but we need to make sure that actually makes sense
    
  1267.     await 1;
    
  1268. 
    
  1269.     const container = document.createElement('div');
    
  1270.     const root = ReactDOMClient.createRoot(container);
    
  1271.     await act(() => {
    
  1272.       root.render(<App />);
    
  1273.     });
    
  1274. 
    
  1275.     expect(getMeaningfulChildren(document)).toEqual(
    
  1276.       <html>
    
  1277.         <head>
    
  1278.           <link rel="dns-prefetch" href="d before" />
    
  1279.           <link rel="preconnect" href="c before" />
    
  1280.           <link rel="preconnect" href="c2 before" crossorigin="" />
    
  1281.           <link rel="preload" as="style" href="l before" />
    
  1282.           <link rel="modulepreload" href="lm before" />
    
  1283.           <link rel="modulepreload" href="lm2 before" crossorigin="" />
    
  1284.           <script async="" src="i before" />
    
  1285.           <script type="module" async="" src="m before" />
    
  1286.           <script type="module" async="" src="m2 before" crossorigin="" />
    
  1287.           <link rel="dns-prefetch" href="d after" />
    
  1288.           <link rel="preconnect" href="c after" />
    
  1289.           <link rel="preconnect" href="c2 after" crossorigin="" />
    
  1290.           <link rel="preload" as="style" href="l after" />
    
  1291.           <link rel="modulepreload" href="lm after" />
    
  1292.           <link rel="modulepreload" href="lm2 after" crossorigin="" />
    
  1293.           <script async="" src="i after" />
    
  1294.           <script type="module" async="" src="m after" />
    
  1295.           <script type="module" async="" src="m2 after" crossorigin="" />
    
  1296.         </head>
    
  1297.         <body />
    
  1298.       </html>,
    
  1299.     );
    
  1300.     expect(getMeaningfulChildren(container)).toEqual(<p>hello world</p>);
    
  1301.   });
    
  1302. 
    
  1303.   it('should support float methods when rendering in Fizz', async () => {
    
  1304.     function Component() {
    
  1305.       return <p>hello world</p>;
    
  1306.     }
    
  1307. 
    
  1308.     const ClientComponent = clientExports(Component);
    
  1309. 
    
  1310.     async function ServerComponent() {
    
  1311.       FlightReactDOM.prefetchDNS('d before');
    
  1312.       FlightReactDOM.preconnect('c before');
    
  1313.       FlightReactDOM.preconnect('c2 before', {crossOrigin: 'anonymous'});
    
  1314.       FlightReactDOM.preload('l before', {as: 'style'});
    
  1315.       FlightReactDOM.preloadModule('lm before');
    
  1316.       FlightReactDOM.preloadModule('lm2 before', {crossOrigin: 'anonymous'});
    
  1317.       FlightReactDOM.preinit('i before', {as: 'script'});
    
  1318.       FlightReactDOM.preinitModule('m before');
    
  1319.       FlightReactDOM.preinitModule('m2 before', {crossOrigin: 'anonymous'});
    
  1320.       await 1;
    
  1321.       FlightReactDOM.prefetchDNS('d after');
    
  1322.       FlightReactDOM.preconnect('c after');
    
  1323.       FlightReactDOM.preconnect('c2 after', {crossOrigin: 'anonymous'});
    
  1324.       FlightReactDOM.preload('l after', {as: 'style'});
    
  1325.       FlightReactDOM.preloadModule('lm after');
    
  1326.       FlightReactDOM.preloadModule('lm2 after', {crossOrigin: 'anonymous'});
    
  1327.       FlightReactDOM.preinit('i after', {as: 'script'});
    
  1328.       FlightReactDOM.preinitModule('m after');
    
  1329.       FlightReactDOM.preinitModule('m2 after', {crossOrigin: 'anonymous'});
    
  1330.       return <ClientComponent />;
    
  1331.     }
    
  1332. 
    
  1333.     const {writable: flightWritable, readable: flightReadable} =
    
  1334.       getTestStream();
    
  1335.     const {writable: fizzWritable, readable: fizzReadable} = getTestStream();
    
  1336. 
    
  1337.     // In a real environment you would want to call the render during the Fizz render.
    
  1338.     // The reason we cannot do this in our test is because we don't actually have two separate
    
  1339.     // module graphs and we are contriving the sequencing to work in a way where
    
  1340.     // the right HostDispatcher is in scope during the Flight Server Float calls and the
    
  1341.     // Flight Client hint dispatches
    
  1342.     const {pipe} = ReactServerDOMServer.renderToPipeableStream(
    
  1343.       <ServerComponent />,
    
  1344.       webpackMap,
    
  1345.     );
    
  1346.     pipe(flightWritable);
    
  1347. 
    
  1348.     let response = null;
    
  1349.     function getResponse() {
    
  1350.       if (response === null) {
    
  1351.         response =
    
  1352.           ReactServerDOMClient.createFromReadableStream(flightReadable);
    
  1353.       }
    
  1354.       return response;
    
  1355.     }
    
  1356. 
    
  1357.     function App() {
    
  1358.       return (
    
  1359.         <html>
    
  1360.           <body>{getResponse()}</body>
    
  1361.         </html>
    
  1362.       );
    
  1363.     }
    
  1364. 
    
  1365.     await act(async () => {
    
  1366.       ReactDOMFizzServer.renderToPipeableStream(<App />).pipe(fizzWritable);
    
  1367.     });
    
  1368. 
    
  1369.     const decoder = new TextDecoder();
    
  1370.     const reader = fizzReadable.getReader();
    
  1371.     let content = '';
    
  1372.     while (true) {
    
  1373.       const {done, value} = await reader.read();
    
  1374.       if (done) {
    
  1375.         content += decoder.decode();
    
  1376.         break;
    
  1377.       }
    
  1378.       content += decoder.decode(value, {stream: true});
    
  1379.     }
    
  1380. 
    
  1381.     const doc = new JSDOM(content).window.document;
    
  1382.     expect(getMeaningfulChildren(doc)).toEqual(
    
  1383.       <html>
    
  1384.         <head>
    
  1385.           <link rel="dns-prefetch" href="d before" />
    
  1386.           <link rel="preconnect" href="c before" />
    
  1387.           <link rel="preconnect" href="c2 before" crossorigin="" />
    
  1388.           <link rel="dns-prefetch" href="d after" />
    
  1389.           <link rel="preconnect" href="c after" />
    
  1390.           <link rel="preconnect" href="c2 after" crossorigin="" />
    
  1391.           <script async="" src="i before" />
    
  1392.           <script type="module" async="" src="m before" />
    
  1393.           <script type="module" async="" src="m2 before" crossorigin="" />
    
  1394.           <script async="" src="i after" />
    
  1395.           <script type="module" async="" src="m after" />
    
  1396.           <script type="module" async="" src="m2 after" crossorigin="" />
    
  1397.           <link rel="preload" as="style" href="l before" />
    
  1398.           <link rel="modulepreload" href="lm before" />
    
  1399.           <link rel="modulepreload" href="lm2 before" crossorigin="" />
    
  1400.           <link rel="preload" as="style" href="l after" />
    
  1401.           <link rel="modulepreload" href="lm after" />
    
  1402.           <link rel="modulepreload" href="lm2 after" crossorigin="" />
    
  1403.         </head>
    
  1404.         <body>
    
  1405.           <p>hello world</p>
    
  1406.         </body>
    
  1407.       </html>,
    
  1408.     );
    
  1409.   });
    
  1410. 
    
  1411.   it('supports Float hints from concurrent Flight -> Fizz renders', async () => {
    
  1412.     function Component() {
    
  1413.       return <p>hello world</p>;
    
  1414.     }
    
  1415. 
    
  1416.     const ClientComponent = clientExports(Component);
    
  1417. 
    
  1418.     async function ServerComponent1() {
    
  1419.       FlightReactDOM.preload('before1', {as: 'style'});
    
  1420.       await 1;
    
  1421.       FlightReactDOM.preload('after1', {as: 'style'});
    
  1422.       return <ClientComponent />;
    
  1423.     }
    
  1424. 
    
  1425.     async function ServerComponent2() {
    
  1426.       FlightReactDOM.preload('before2', {as: 'style'});
    
  1427.       await 1;
    
  1428.       FlightReactDOM.preload('after2', {as: 'style'});
    
  1429.       return <ClientComponent />;
    
  1430.     }
    
  1431. 
    
  1432.     const {writable: flightWritable1, readable: flightReadable1} =
    
  1433.       getTestStream();
    
  1434.     const {writable: flightWritable2, readable: flightReadable2} =
    
  1435.       getTestStream();
    
  1436. 
    
  1437.     ReactServerDOMServer.renderToPipeableStream(
    
  1438.       <ServerComponent1 />,
    
  1439.       webpackMap,
    
  1440.     ).pipe(flightWritable1);
    
  1441. 
    
  1442.     ReactServerDOMServer.renderToPipeableStream(
    
  1443.       <ServerComponent2 />,
    
  1444.       webpackMap,
    
  1445.     ).pipe(flightWritable2);
    
  1446. 
    
  1447.     const responses = new Map();
    
  1448.     function getResponse(stream) {
    
  1449.       let response = responses.get(stream);
    
  1450.       if (!response) {
    
  1451.         response = ReactServerDOMClient.createFromReadableStream(stream);
    
  1452.         responses.set(stream, response);
    
  1453.       }
    
  1454.       return response;
    
  1455.     }
    
  1456. 
    
  1457.     function App({stream}) {
    
  1458.       return (
    
  1459.         <html>
    
  1460.           <body>{getResponse(stream)}</body>
    
  1461.         </html>
    
  1462.       );
    
  1463.     }
    
  1464. 
    
  1465.     // pausing to let Flight runtime tick. This is a test only artifact of the fact that
    
  1466.     // we aren't operating separate module graphs for flight and fiber. In a real app
    
  1467.     // each would have their own dispatcher and there would be no cross dispatching.
    
  1468.     await 1;
    
  1469. 
    
  1470.     const {writable: fizzWritable1, readable: fizzReadable1} = getTestStream();
    
  1471.     const {writable: fizzWritable2, readable: fizzReadable2} = getTestStream();
    
  1472.     await act(async () => {
    
  1473.       ReactDOMFizzServer.renderToPipeableStream(
    
  1474.         <App stream={flightReadable1} />,
    
  1475.       ).pipe(fizzWritable1);
    
  1476.       ReactDOMFizzServer.renderToPipeableStream(
    
  1477.         <App stream={flightReadable2} />,
    
  1478.       ).pipe(fizzWritable2);
    
  1479.     });
    
  1480. 
    
  1481.     async function read(stream) {
    
  1482.       const decoder = new TextDecoder();
    
  1483.       const reader = stream.getReader();
    
  1484.       let buffer = '';
    
  1485.       while (true) {
    
  1486.         const {done, value} = await reader.read();
    
  1487.         if (done) {
    
  1488.           buffer += decoder.decode();
    
  1489.           break;
    
  1490.         }
    
  1491.         buffer += decoder.decode(value, {stream: true});
    
  1492.       }
    
  1493.       return buffer;
    
  1494.     }
    
  1495. 
    
  1496.     const [content1, content2] = await Promise.all([
    
  1497.       read(fizzReadable1),
    
  1498.       read(fizzReadable2),
    
  1499.     ]);
    
  1500. 
    
  1501.     expect(content1).toEqual(
    
  1502.       '<!DOCTYPE html><html><head><link rel="preload" href="before1" as="style"/>' +
    
  1503.         '<link rel="preload" href="after1" as="style"/></head><body><p>hello world</p></body></html>',
    
  1504.     );
    
  1505.     expect(content2).toEqual(
    
  1506.       '<!DOCTYPE html><html><head><link rel="preload" href="before2" as="style"/>' +
    
  1507.         '<link rel="preload" href="after2" as="style"/></head><body><p>hello world</p></body></html>',
    
  1508.     );
    
  1509.   });
    
  1510. 
    
  1511.   it('supports deduping hints by Float key', async () => {
    
  1512.     function Component() {
    
  1513.       return <p>hello world</p>;
    
  1514.     }
    
  1515. 
    
  1516.     const ClientComponent = clientExports(Component);
    
  1517. 
    
  1518.     async function ServerComponent() {
    
  1519.       FlightReactDOM.prefetchDNS('dns');
    
  1520.       FlightReactDOM.preconnect('preconnect');
    
  1521.       FlightReactDOM.preload('load', {as: 'style'});
    
  1522.       FlightReactDOM.preinit('init', {as: 'script'});
    
  1523.       // again but vary preconnect to demonstrate crossOrigin participates in the key
    
  1524.       FlightReactDOM.prefetchDNS('dns');
    
  1525.       FlightReactDOM.preconnect('preconnect', {crossOrigin: 'anonymous'});
    
  1526.       FlightReactDOM.preload('load', {as: 'style'});
    
  1527.       FlightReactDOM.preinit('init', {as: 'script'});
    
  1528.       await 1;
    
  1529.       // after an async point
    
  1530.       FlightReactDOM.prefetchDNS('dns');
    
  1531.       FlightReactDOM.preconnect('preconnect', {crossOrigin: 'use-credentials'});
    
  1532.       FlightReactDOM.preload('load', {as: 'style'});
    
  1533.       FlightReactDOM.preinit('init', {as: 'script'});
    
  1534.       return <ClientComponent />;
    
  1535.     }
    
  1536. 
    
  1537.     const {writable, readable} = getTestStream();
    
  1538. 
    
  1539.     ReactServerDOMServer.renderToPipeableStream(
    
  1540.       <ServerComponent />,
    
  1541.       webpackMap,
    
  1542.     ).pipe(writable);
    
  1543. 
    
  1544.     const hintRows = [];
    
  1545.     async function collectHints(stream) {
    
  1546.       const decoder = new TextDecoder();
    
  1547.       const reader = stream.getReader();
    
  1548.       let buffer = '';
    
  1549.       while (true) {
    
  1550.         const {done, value} = await reader.read();
    
  1551.         if (done) {
    
  1552.           buffer += decoder.decode();
    
  1553.           if (buffer.includes(':H')) {
    
  1554.             hintRows.push(buffer);
    
  1555.           }
    
  1556.           break;
    
  1557.         }
    
  1558.         buffer += decoder.decode(value, {stream: true});
    
  1559.         let line;
    
  1560.         while ((line = buffer.indexOf('\n')) > -1) {
    
  1561.           const row = buffer.slice(0, line);
    
  1562.           buffer = buffer.slice(line + 1);
    
  1563.           if (row.includes(':H')) {
    
  1564.             hintRows.push(row);
    
  1565.           }
    
  1566.         }
    
  1567.       }
    
  1568.     }
    
  1569. 
    
  1570.     await collectHints(readable);
    
  1571.     expect(hintRows.length).toEqual(6);
    
  1572.   });
    
  1573. });