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. let clientExports;
    
  19. let serverExports;
    
  20. let webpackMap;
    
  21. let webpackServerMap;
    
  22. let act;
    
  23. let React;
    
  24. let ReactDOM;
    
  25. let ReactDOMClient;
    
  26. let ReactDOMFizzServer;
    
  27. let ReactServerDOMServer;
    
  28. let ReactServerDOMClient;
    
  29. let Suspense;
    
  30. let use;
    
  31. let ReactServer;
    
  32. let ReactServerDOM;
    
  33. 
    
  34. describe('ReactFlightDOMBrowser', () => {
    
  35.   beforeEach(() => {
    
  36.     jest.resetModules();
    
  37. 
    
  38.     // Simulate the condition resolution
    
  39.     jest.mock('react', () => require('react/react.shared-subset'));
    
  40.     jest.mock('react-server-dom-webpack/server', () =>
    
  41.       require('react-server-dom-webpack/server.browser'),
    
  42.     );
    
  43. 
    
  44.     const WebpackMock = require('./utils/WebpackMock');
    
  45.     clientExports = WebpackMock.clientExports;
    
  46.     serverExports = WebpackMock.serverExports;
    
  47.     webpackMap = WebpackMock.webpackMap;
    
  48.     webpackServerMap = WebpackMock.webpackServerMap;
    
  49. 
    
  50.     ReactServer = require('react');
    
  51.     ReactServerDOM = require('react-dom');
    
  52.     ReactServerDOMServer = require('react-server-dom-webpack/server.browser');
    
  53. 
    
  54.     __unmockReact();
    
  55.     jest.resetModules();
    
  56. 
    
  57.     act = require('internal-test-utils').act;
    
  58.     React = require('react');
    
  59.     ReactDOM = require('react-dom');
    
  60.     ReactDOMClient = require('react-dom/client');
    
  61.     ReactDOMFizzServer = require('react-dom/server.browser');
    
  62.     ReactServerDOMClient = require('react-server-dom-webpack/client');
    
  63.     Suspense = React.Suspense;
    
  64.     use = React.use;
    
  65.   });
    
  66. 
    
  67.   function makeDelayedText(Model) {
    
  68.     let error, _resolve, _reject;
    
  69.     let promise = new Promise((resolve, reject) => {
    
  70.       _resolve = () => {
    
  71.         promise = null;
    
  72.         resolve();
    
  73.       };
    
  74.       _reject = e => {
    
  75.         error = e;
    
  76.         promise = null;
    
  77.         reject(e);
    
  78.       };
    
  79.     });
    
  80.     function DelayedText({children}, data) {
    
  81.       if (promise) {
    
  82.         throw promise;
    
  83.       }
    
  84.       if (error) {
    
  85.         throw error;
    
  86.       }
    
  87.       return <Model>{children}</Model>;
    
  88.     }
    
  89.     return [DelayedText, _resolve, _reject];
    
  90.   }
    
  91. 
    
  92.   const theInfinitePromise = new Promise(() => {});
    
  93.   function InfiniteSuspend() {
    
  94.     throw theInfinitePromise;
    
  95.   }
    
  96. 
    
  97.   function requireServerRef(ref) {
    
  98.     let name = '';
    
  99.     let resolvedModuleData = webpackServerMap[ref];
    
  100.     if (resolvedModuleData) {
    
  101.       // The potentially aliased name.
    
  102.       name = resolvedModuleData.name;
    
  103.     } else {
    
  104.       // We didn't find this specific export name but we might have the * export
    
  105.       // which contains this name as well.
    
  106.       // TODO: It's unfortunate that we now have to parse this string. We should
    
  107.       // probably go back to encoding path and name separately on the client reference.
    
  108.       const idx = ref.lastIndexOf('#');
    
  109.       if (idx !== -1) {
    
  110.         name = ref.slice(idx + 1);
    
  111.         resolvedModuleData = webpackServerMap[ref.slice(0, idx)];
    
  112.       }
    
  113.       if (!resolvedModuleData) {
    
  114.         throw new Error(
    
  115.           'Could not find the module "' +
    
  116.             ref +
    
  117.             '" in the React Client Manifest. ' +
    
  118.             'This is probably a bug in the React Server Components bundler.',
    
  119.         );
    
  120.       }
    
  121.     }
    
  122.     const mod = __webpack_require__(resolvedModuleData.id);
    
  123.     if (name === '*') {
    
  124.       return mod;
    
  125.     }
    
  126.     return mod[name];
    
  127.   }
    
  128. 
    
  129.   async function callServer(actionId, body) {
    
  130.     const fn = requireServerRef(actionId);
    
  131.     const args = await ReactServerDOMServer.decodeReply(body, webpackServerMap);
    
  132.     return fn.apply(null, args);
    
  133.   }
    
  134. 
    
  135.   it('should resolve HTML using W3C streams', async () => {
    
  136.     function Text({children}) {
    
  137.       return <span>{children}</span>;
    
  138.     }
    
  139.     function HTML() {
    
  140.       return (
    
  141.         <div>
    
  142.           <Text>hello</Text>
    
  143.           <Text>world</Text>
    
  144.         </div>
    
  145.       );
    
  146.     }
    
  147. 
    
  148.     function App() {
    
  149.       const model = {
    
  150.         html: <HTML />,
    
  151.       };
    
  152.       return model;
    
  153.     }
    
  154. 
    
  155.     const stream = ReactServerDOMServer.renderToReadableStream(<App />);
    
  156.     const response = ReactServerDOMClient.createFromReadableStream(stream);
    
  157.     const model = await response;
    
  158.     expect(model).toEqual({
    
  159.       html: (
    
  160.         <div>
    
  161.           <span>hello</span>
    
  162.           <span>world</span>
    
  163.         </div>
    
  164.       ),
    
  165.     });
    
  166.   });
    
  167. 
    
  168.   it('should resolve HTML using W3C streams', async () => {
    
  169.     function Text({children}) {
    
  170.       return <span>{children}</span>;
    
  171.     }
    
  172.     function HTML() {
    
  173.       return (
    
  174.         <div>
    
  175.           <Text>hello</Text>
    
  176.           <Text>world</Text>
    
  177.         </div>
    
  178.       );
    
  179.     }
    
  180. 
    
  181.     function App() {
    
  182.       const model = {
    
  183.         html: <HTML />,
    
  184.       };
    
  185.       return model;
    
  186.     }
    
  187. 
    
  188.     const stream = ReactServerDOMServer.renderToReadableStream(<App />);
    
  189.     const response = ReactServerDOMClient.createFromReadableStream(stream);
    
  190.     const model = await response;
    
  191.     expect(model).toEqual({
    
  192.       html: (
    
  193.         <div>
    
  194.           <span>hello</span>
    
  195.           <span>world</span>
    
  196.         </div>
    
  197.       ),
    
  198.     });
    
  199.   });
    
  200. 
    
  201.   it('should progressively reveal server components', async () => {
    
  202.     let reportedErrors = [];
    
  203. 
    
  204.     // Client Components
    
  205. 
    
  206.     class ErrorBoundary extends React.Component {
    
  207.       state = {hasError: false, error: null};
    
  208.       static getDerivedStateFromError(error) {
    
  209.         return {
    
  210.           hasError: true,
    
  211.           error,
    
  212.         };
    
  213.       }
    
  214.       render() {
    
  215.         if (this.state.hasError) {
    
  216.           return this.props.fallback(this.state.error);
    
  217.         }
    
  218.         return this.props.children;
    
  219.       }
    
  220.     }
    
  221. 
    
  222.     let errorBoundaryFn;
    
  223.     if (__DEV__) {
    
  224.       errorBoundaryFn = e => (
    
  225.         <p>
    
  226.           {e.message} + {e.digest}
    
  227.         </p>
    
  228.       );
    
  229.     } else {
    
  230.       errorBoundaryFn = e => {
    
  231.         expect(e.message).toBe(
    
  232.           'An error occurred in the Server Components render. The specific message is omitted in production' +
    
  233.             ' builds to avoid leaking sensitive details. A digest property is included on this error instance which' +
    
  234.             ' may provide additional details about the nature of the error.',
    
  235.         );
    
  236.         return <p>{e.digest}</p>;
    
  237.       };
    
  238.     }
    
  239. 
    
  240.     function MyErrorBoundary({children}) {
    
  241.       return (
    
  242.         <ErrorBoundary fallback={errorBoundaryFn}>{children}</ErrorBoundary>
    
  243.       );
    
  244.     }
    
  245. 
    
  246.     // Model
    
  247.     function Text({children}) {
    
  248.       return children;
    
  249.     }
    
  250. 
    
  251.     const [Friends, resolveFriends] = makeDelayedText(Text);
    
  252.     const [Name, resolveName] = makeDelayedText(Text);
    
  253.     const [Posts, resolvePosts] = makeDelayedText(Text);
    
  254.     const [Photos, resolvePhotos] = makeDelayedText(Text);
    
  255.     const [Games, , rejectGames] = makeDelayedText(Text);
    
  256. 
    
  257.     // View
    
  258.     function ProfileDetails({avatar}) {
    
  259.       return (
    
  260.         <div>
    
  261.           <Name>:name:</Name>
    
  262.           {avatar}
    
  263.         </div>
    
  264.       );
    
  265.     }
    
  266.     function ProfileSidebar({friends}) {
    
  267.       return (
    
  268.         <div>
    
  269.           <Photos>:photos:</Photos>
    
  270.           {friends}
    
  271.         </div>
    
  272.       );
    
  273.     }
    
  274.     function ProfilePosts({posts}) {
    
  275.       return <div>{posts}</div>;
    
  276.     }
    
  277.     function ProfileGames({games}) {
    
  278.       return <div>{games}</div>;
    
  279.     }
    
  280. 
    
  281.     const MyErrorBoundaryClient = clientExports(MyErrorBoundary);
    
  282. 
    
  283.     function ProfileContent() {
    
  284.       return (
    
  285.         <>
    
  286.           <ProfileDetails avatar={<Text>:avatar:</Text>} />
    
  287.           <Suspense fallback={<p>(loading sidebar)</p>}>
    
  288.             <ProfileSidebar friends={<Friends>:friends:</Friends>} />
    
  289.           </Suspense>
    
  290.           <Suspense fallback={<p>(loading posts)</p>}>
    
  291.             <ProfilePosts posts={<Posts>:posts:</Posts>} />
    
  292.           </Suspense>
    
  293.           <MyErrorBoundaryClient>
    
  294.             <Suspense fallback={<p>(loading games)</p>}>
    
  295.               <ProfileGames games={<Games>:games:</Games>} />
    
  296.             </Suspense>
    
  297.           </MyErrorBoundaryClient>
    
  298.         </>
    
  299.       );
    
  300.     }
    
  301. 
    
  302.     const model = {
    
  303.       rootContent: <ProfileContent />,
    
  304.     };
    
  305. 
    
  306.     function ProfilePage({response}) {
    
  307.       return use(response).rootContent;
    
  308.     }
    
  309. 
    
  310.     const stream = ReactServerDOMServer.renderToReadableStream(
    
  311.       model,
    
  312.       webpackMap,
    
  313.       {
    
  314.         onError(x) {
    
  315.           reportedErrors.push(x);
    
  316.           return __DEV__ ? `a dev digest` : `digest("${x.message}")`;
    
  317.         },
    
  318.       },
    
  319.     );
    
  320.     const response = ReactServerDOMClient.createFromReadableStream(stream);
    
  321. 
    
  322.     const container = document.createElement('div');
    
  323.     const root = ReactDOMClient.createRoot(container);
    
  324.     await act(() => {
    
  325.       root.render(
    
  326.         <Suspense fallback={<p>(loading)</p>}>
    
  327.           <ProfilePage response={response} />
    
  328.         </Suspense>,
    
  329.       );
    
  330.     });
    
  331.     expect(container.innerHTML).toBe('<p>(loading)</p>');
    
  332. 
    
  333.     // This isn't enough to show anything.
    
  334.     await act(() => {
    
  335.       resolveFriends();
    
  336.     });
    
  337.     expect(container.innerHTML).toBe('<p>(loading)</p>');
    
  338. 
    
  339.     // We can now show the details. Sidebar and posts are still loading.
    
  340.     await act(() => {
    
  341.       resolveName();
    
  342.     });
    
  343.     // Advance time enough to trigger a nested fallback.
    
  344.     jest.advanceTimersByTime(500);
    
  345.     expect(container.innerHTML).toBe(
    
  346.       '<div>:name::avatar:</div>' +
    
  347.         '<p>(loading sidebar)</p>' +
    
  348.         '<p>(loading posts)</p>' +
    
  349.         '<p>(loading games)</p>',
    
  350.     );
    
  351. 
    
  352.     expect(reportedErrors).toEqual([]);
    
  353. 
    
  354.     const theError = new Error('Game over');
    
  355.     // Let's *fail* loading games.
    
  356.     await act(() => {
    
  357.       rejectGames(theError);
    
  358.     });
    
  359. 
    
  360.     const gamesExpectedValue = __DEV__
    
  361.       ? '<p>Game over + a dev digest</p>'
    
  362.       : '<p>digest("Game over")</p>';
    
  363. 
    
  364.     expect(container.innerHTML).toBe(
    
  365.       '<div>:name::avatar:</div>' +
    
  366.         '<p>(loading sidebar)</p>' +
    
  367.         '<p>(loading posts)</p>' +
    
  368.         gamesExpectedValue,
    
  369.     );
    
  370. 
    
  371.     expect(reportedErrors).toEqual([theError]);
    
  372.     reportedErrors = [];
    
  373. 
    
  374.     // We can now show the sidebar.
    
  375.     await act(() => {
    
  376.       resolvePhotos();
    
  377.     });
    
  378.     expect(container.innerHTML).toBe(
    
  379.       '<div>:name::avatar:</div>' +
    
  380.         '<div>:photos::friends:</div>' +
    
  381.         '<p>(loading posts)</p>' +
    
  382.         gamesExpectedValue,
    
  383.     );
    
  384. 
    
  385.     // Show everything.
    
  386.     await act(() => {
    
  387.       resolvePosts();
    
  388.     });
    
  389.     expect(container.innerHTML).toBe(
    
  390.       '<div>:name::avatar:</div>' +
    
  391.         '<div>:photos::friends:</div>' +
    
  392.         '<div>:posts:</div>' +
    
  393.         gamesExpectedValue,
    
  394.     );
    
  395. 
    
  396.     expect(reportedErrors).toEqual([]);
    
  397.   });
    
  398. 
    
  399.   it('should close the stream upon completion when rendering to W3C streams', async () => {
    
  400.     // Model
    
  401.     function Text({children}) {
    
  402.       return children;
    
  403.     }
    
  404. 
    
  405.     const [Friends, resolveFriends] = makeDelayedText(Text);
    
  406.     const [Name, resolveName] = makeDelayedText(Text);
    
  407.     const [Posts, resolvePosts] = makeDelayedText(Text);
    
  408.     const [Photos, resolvePhotos] = makeDelayedText(Text);
    
  409. 
    
  410.     // View
    
  411.     function ProfileDetails({avatar}) {
    
  412.       return (
    
  413.         <div>
    
  414.           <Name>:name:</Name>
    
  415.           {avatar}
    
  416.         </div>
    
  417.       );
    
  418.     }
    
  419.     function ProfileSidebar({friends}) {
    
  420.       return (
    
  421.         <div>
    
  422.           <Photos>:photos:</Photos>
    
  423.           {friends}
    
  424.         </div>
    
  425.       );
    
  426.     }
    
  427.     function ProfilePosts({posts}) {
    
  428.       return <div>{posts}</div>;
    
  429.     }
    
  430. 
    
  431.     function ProfileContent() {
    
  432.       return (
    
  433.         <Suspense fallback="(loading everything)">
    
  434.           <ProfileDetails avatar={<Text>:avatar:</Text>} />
    
  435.           <Suspense fallback={<p>(loading sidebar)</p>}>
    
  436.             <ProfileSidebar friends={<Friends>:friends:</Friends>} />
    
  437.           </Suspense>
    
  438.           <Suspense fallback={<p>(loading posts)</p>}>
    
  439.             <ProfilePosts posts={<Posts>:posts:</Posts>} />
    
  440.           </Suspense>
    
  441.         </Suspense>
    
  442.       );
    
  443.     }
    
  444. 
    
  445.     const model = {
    
  446.       rootContent: <ProfileContent />,
    
  447.     };
    
  448. 
    
  449.     const stream = ReactServerDOMServer.renderToReadableStream(
    
  450.       model,
    
  451.       webpackMap,
    
  452.     );
    
  453. 
    
  454.     const reader = stream.getReader();
    
  455.     const decoder = new TextDecoder();
    
  456. 
    
  457.     let flightResponse = '';
    
  458.     let isDone = false;
    
  459. 
    
  460.     reader.read().then(function progress({done, value}) {
    
  461.       if (done) {
    
  462.         isDone = true;
    
  463.         return;
    
  464.       }
    
  465. 
    
  466.       flightResponse += decoder.decode(value);
    
  467. 
    
  468.       return reader.read().then(progress);
    
  469.     });
    
  470. 
    
  471.     // Advance time enough to trigger a nested fallback.
    
  472.     jest.advanceTimersByTime(500);
    
  473. 
    
  474.     await act(() => {});
    
  475. 
    
  476.     expect(flightResponse).toContain('(loading everything)');
    
  477.     expect(flightResponse).toContain('(loading sidebar)');
    
  478.     expect(flightResponse).toContain('(loading posts)');
    
  479.     expect(flightResponse).not.toContain(':friends:');
    
  480.     expect(flightResponse).not.toContain(':name:');
    
  481. 
    
  482.     await act(() => {
    
  483.       resolveFriends();
    
  484.     });
    
  485. 
    
  486.     expect(flightResponse).toContain(':friends:');
    
  487. 
    
  488.     await act(() => {
    
  489.       resolveName();
    
  490.     });
    
  491. 
    
  492.     expect(flightResponse).toContain(':name:');
    
  493. 
    
  494.     await act(() => {
    
  495.       resolvePhotos();
    
  496.     });
    
  497. 
    
  498.     expect(flightResponse).toContain(':photos:');
    
  499. 
    
  500.     await act(() => {
    
  501.       resolvePosts();
    
  502.     });
    
  503. 
    
  504.     expect(flightResponse).toContain(':posts:');
    
  505. 
    
  506.     // Final pending chunk is written; stream should be closed.
    
  507.     expect(isDone).toBeTruthy();
    
  508.   });
    
  509. 
    
  510.   it('should be able to complete after aborting and throw the reason client-side', async () => {
    
  511.     const reportedErrors = [];
    
  512. 
    
  513.     let errorBoundaryFn;
    
  514.     if (__DEV__) {
    
  515.       errorBoundaryFn = e => (
    
  516.         <p>
    
  517.           {e.message} + {e.digest}
    
  518.         </p>
    
  519.       );
    
  520.     } else {
    
  521.       errorBoundaryFn = e => {
    
  522.         expect(e.message).toBe(
    
  523.           'An error occurred in the Server Components render. The specific message is omitted in production' +
    
  524.             ' builds to avoid leaking sensitive details. A digest property is included on this error instance which' +
    
  525.             ' may provide additional details about the nature of the error.',
    
  526.         );
    
  527.         return <p>{e.digest}</p>;
    
  528.       };
    
  529.     }
    
  530. 
    
  531.     class ErrorBoundary extends React.Component {
    
  532.       state = {hasError: false, error: null};
    
  533.       static getDerivedStateFromError(error) {
    
  534.         return {
    
  535.           hasError: true,
    
  536.           error,
    
  537.         };
    
  538.       }
    
  539.       render() {
    
  540.         if (this.state.hasError) {
    
  541.           return this.props.fallback(this.state.error);
    
  542.         }
    
  543.         return this.props.children;
    
  544.       }
    
  545.     }
    
  546. 
    
  547.     const controller = new AbortController();
    
  548.     const stream = ReactServerDOMServer.renderToReadableStream(
    
  549.       <div>
    
  550.         <InfiniteSuspend />
    
  551.       </div>,
    
  552.       webpackMap,
    
  553.       {
    
  554.         signal: controller.signal,
    
  555.         onError(x) {
    
  556.           const message = typeof x === 'string' ? x : x.message;
    
  557.           reportedErrors.push(x);
    
  558.           return __DEV__ ? 'a dev digest' : `digest("${message}")`;
    
  559.         },
    
  560.       },
    
  561.     );
    
  562.     const response = ReactServerDOMClient.createFromReadableStream(stream);
    
  563. 
    
  564.     const container = document.createElement('div');
    
  565.     const root = ReactDOMClient.createRoot(container);
    
  566. 
    
  567.     function App({res}) {
    
  568.       return use(res);
    
  569.     }
    
  570. 
    
  571.     await act(() => {
    
  572.       root.render(
    
  573.         <ErrorBoundary fallback={errorBoundaryFn}>
    
  574.           <Suspense fallback={<p>(loading)</p>}>
    
  575.             <App res={response} />
    
  576.           </Suspense>
    
  577.         </ErrorBoundary>,
    
  578.       );
    
  579.     });
    
  580.     expect(container.innerHTML).toBe('<p>(loading)</p>');
    
  581. 
    
  582.     await act(() => {
    
  583.       controller.abort('for reasons');
    
  584.     });
    
  585.     const expectedValue = __DEV__
    
  586.       ? '<p>Error: for reasons + a dev digest</p>'
    
  587.       : '<p>digest("for reasons")</p>';
    
  588.     expect(container.innerHTML).toBe(expectedValue);
    
  589. 
    
  590.     expect(reportedErrors).toEqual(['for reasons']);
    
  591.   });
    
  592. 
    
  593.   it('basic use(promise)', async () => {
    
  594.     function Server() {
    
  595.       return (
    
  596.         ReactServer.use(Promise.resolve('A')) +
    
  597.         ReactServer.use(Promise.resolve('B')) +
    
  598.         ReactServer.use(Promise.resolve('C'))
    
  599.       );
    
  600.     }
    
  601. 
    
  602.     const stream = ReactServerDOMServer.renderToReadableStream(<Server />);
    
  603.     const response = ReactServerDOMClient.createFromReadableStream(stream);
    
  604. 
    
  605.     function Client() {
    
  606.       return use(response);
    
  607.     }
    
  608. 
    
  609.     const container = document.createElement('div');
    
  610.     const root = ReactDOMClient.createRoot(container);
    
  611.     await act(() => {
    
  612.       root.render(
    
  613.         <Suspense fallback="Loading...">
    
  614.           <Client />
    
  615.         </Suspense>,
    
  616.       );
    
  617.     });
    
  618.     expect(container.innerHTML).toBe('ABC');
    
  619.   });
    
  620. 
    
  621.   // @gate enableServerContext
    
  622.   it('basic use(context)', async () => {
    
  623.     let ContextA;
    
  624.     let ContextB;
    
  625.     expect(() => {
    
  626.       ContextA = React.createServerContext('ContextA', '');
    
  627.       ContextB = React.createServerContext('ContextB', 'B');
    
  628.     }).toErrorDev(
    
  629.       [
    
  630.         'Server Context is deprecated and will soon be removed. ' +
    
  631.           'It was never documented and we have found it not to be useful ' +
    
  632.           'enough to warrant the downside it imposes on all apps.',
    
  633.         'Server Context is deprecated and will soon be removed. ' +
    
  634.           'It was never documented and we have found it not to be useful ' +
    
  635.           'enough to warrant the downside it imposes on all apps.',
    
  636.       ],
    
  637.       {withoutStack: true},
    
  638.     );
    
  639. 
    
  640.     function ServerComponent() {
    
  641.       return ReactServer.use(ContextA) + ReactServer.use(ContextB);
    
  642.     }
    
  643.     function Server() {
    
  644.       return (
    
  645.         <ContextA.Provider value="A">
    
  646.           <ServerComponent />
    
  647.         </ContextA.Provider>
    
  648.       );
    
  649.     }
    
  650.     const stream = ReactServerDOMServer.renderToReadableStream(<Server />);
    
  651.     const response = ReactServerDOMClient.createFromReadableStream(stream);
    
  652. 
    
  653.     function Client() {
    
  654.       return use(response);
    
  655.     }
    
  656. 
    
  657.     const container = document.createElement('div');
    
  658.     const root = ReactDOMClient.createRoot(container);
    
  659.     await act(() => {
    
  660.       // Client uses a different renderer.
    
  661.       // We reset _currentRenderer here to not trigger a warning about multiple
    
  662.       // renderers concurrently using this context
    
  663.       ContextA._currentRenderer = null;
    
  664.       root.render(<Client />);
    
  665.     });
    
  666.     expect(container.innerHTML).toBe('AB');
    
  667.   });
    
  668. 
    
  669.   it('use(promise) in multiple components', async () => {
    
  670.     function Child({prefix}) {
    
  671.       return (
    
  672.         prefix +
    
  673.         ReactServer.use(Promise.resolve('C')) +
    
  674.         ReactServer.use(Promise.resolve('D'))
    
  675.       );
    
  676.     }
    
  677. 
    
  678.     function Parent() {
    
  679.       return (
    
  680.         <Child
    
  681.           prefix={
    
  682.             ReactServer.use(Promise.resolve('A')) +
    
  683.             ReactServer.use(Promise.resolve('B'))
    
  684.           }
    
  685.         />
    
  686.       );
    
  687.     }
    
  688. 
    
  689.     const stream = ReactServerDOMServer.renderToReadableStream(<Parent />);
    
  690.     const response = ReactServerDOMClient.createFromReadableStream(stream);
    
  691. 
    
  692.     function Client() {
    
  693.       return use(response);
    
  694.     }
    
  695. 
    
  696.     const container = document.createElement('div');
    
  697.     const root = ReactDOMClient.createRoot(container);
    
  698.     await act(() => {
    
  699.       root.render(
    
  700.         <Suspense fallback="Loading...">
    
  701.           <Client />
    
  702.         </Suspense>,
    
  703.       );
    
  704.     });
    
  705.     expect(container.innerHTML).toBe('ABCD');
    
  706.   });
    
  707. 
    
  708.   it('using a rejected promise will throw', async () => {
    
  709.     const promiseA = Promise.resolve('A');
    
  710.     const promiseB = Promise.reject(new Error('Oops!'));
    
  711.     const promiseC = Promise.resolve('C');
    
  712. 
    
  713.     // Jest/Node will raise an unhandled rejected error unless we await this. It
    
  714.     // works fine in the browser, though.
    
  715.     await expect(promiseB).rejects.toThrow('Oops!');
    
  716. 
    
  717.     function Server() {
    
  718.       return (
    
  719.         ReactServer.use(promiseA) +
    
  720.         ReactServer.use(promiseB) +
    
  721.         ReactServer.use(promiseC)
    
  722.       );
    
  723.     }
    
  724. 
    
  725.     const reportedErrors = [];
    
  726.     const stream = ReactServerDOMServer.renderToReadableStream(
    
  727.       <Server />,
    
  728.       webpackMap,
    
  729.       {
    
  730.         onError(x) {
    
  731.           reportedErrors.push(x);
    
  732.           return __DEV__ ? 'a dev digest' : `digest("${x.message}")`;
    
  733.         },
    
  734.       },
    
  735.     );
    
  736.     const response = ReactServerDOMClient.createFromReadableStream(stream);
    
  737. 
    
  738.     class ErrorBoundary extends React.Component {
    
  739.       state = {error: null};
    
  740.       static getDerivedStateFromError(error) {
    
  741.         return {error};
    
  742.       }
    
  743.       render() {
    
  744.         if (this.state.error) {
    
  745.           return __DEV__
    
  746.             ? this.state.error.message + ' + ' + this.state.error.digest
    
  747.             : this.state.error.digest;
    
  748.         }
    
  749.         return this.props.children;
    
  750.       }
    
  751.     }
    
  752. 
    
  753.     function Client() {
    
  754.       return use(response);
    
  755.     }
    
  756. 
    
  757.     const container = document.createElement('div');
    
  758.     const root = ReactDOMClient.createRoot(container);
    
  759.     await act(() => {
    
  760.       root.render(
    
  761.         <ErrorBoundary>
    
  762.           <Client />
    
  763.         </ErrorBoundary>,
    
  764.       );
    
  765.     });
    
  766.     expect(container.innerHTML).toBe(
    
  767.       __DEV__ ? 'Oops! + a dev digest' : 'digest("Oops!")',
    
  768.     );
    
  769.     expect(reportedErrors.length).toBe(1);
    
  770.     expect(reportedErrors[0].message).toBe('Oops!');
    
  771.   });
    
  772. 
    
  773.   it("use a promise that's already been instrumented and resolved", async () => {
    
  774.     const thenable = {
    
  775.       status: 'fulfilled',
    
  776.       value: 'Hi',
    
  777.       then() {},
    
  778.     };
    
  779. 
    
  780.     // This will never suspend because the thenable already resolved
    
  781.     function Server() {
    
  782.       return ReactServer.use(thenable);
    
  783.     }
    
  784. 
    
  785.     const stream = ReactServerDOMServer.renderToReadableStream(<Server />);
    
  786.     const response = ReactServerDOMClient.createFromReadableStream(stream);
    
  787. 
    
  788.     function Client() {
    
  789.       return use(response);
    
  790.     }
    
  791. 
    
  792.     const container = document.createElement('div');
    
  793.     const root = ReactDOMClient.createRoot(container);
    
  794.     await act(() => {
    
  795.       root.render(<Client />);
    
  796.     });
    
  797.     expect(container.innerHTML).toBe('Hi');
    
  798.   });
    
  799. 
    
  800.   it('unwraps thenable that fulfills synchronously without suspending', async () => {
    
  801.     function Server() {
    
  802.       const thenable = {
    
  803.         then(resolve) {
    
  804.           // This thenable immediately resolves, synchronously, without waiting
    
  805.           // a microtask.
    
  806.           resolve('Hi');
    
  807.         },
    
  808.       };
    
  809.       try {
    
  810.         return ReactServer.use(thenable);
    
  811.       } catch {
    
  812.         throw new Error(
    
  813.           '`use` should not suspend because the thenable resolved synchronously.',
    
  814.         );
    
  815.       }
    
  816.     }
    
  817. 
    
  818.     // Because the thenable resolves synchronously, we should be able to finish
    
  819.     // rendering synchronously, with no fallback.
    
  820.     const stream = ReactServerDOMServer.renderToReadableStream(<Server />);
    
  821.     const response = ReactServerDOMClient.createFromReadableStream(stream);
    
  822. 
    
  823.     function Client() {
    
  824.       return use(response);
    
  825.     }
    
  826. 
    
  827.     const container = document.createElement('div');
    
  828.     const root = ReactDOMClient.createRoot(container);
    
  829.     await act(() => {
    
  830.       root.render(<Client />);
    
  831.     });
    
  832.     expect(container.innerHTML).toBe('Hi');
    
  833.   });
    
  834. 
    
  835.   it('can pass a higher order function by reference from server to client', async () => {
    
  836.     let actionProxy;
    
  837. 
    
  838.     function Client({action}) {
    
  839.       actionProxy = action;
    
  840.       return 'Click Me';
    
  841.     }
    
  842. 
    
  843.     function greet(transform, text) {
    
  844.       return 'Hello ' + transform(text);
    
  845.     }
    
  846. 
    
  847.     function upper(text) {
    
  848.       return text.toUpperCase();
    
  849.     }
    
  850. 
    
  851.     const ServerModuleA = serverExports({
    
  852.       greet,
    
  853.     });
    
  854.     const ServerModuleB = serverExports({
    
  855.       upper,
    
  856.     });
    
  857.     const ClientRef = clientExports(Client);
    
  858. 
    
  859.     const boundFn = ServerModuleA.greet.bind(null, ServerModuleB.upper);
    
  860. 
    
  861.     const stream = ReactServerDOMServer.renderToReadableStream(
    
  862.       <ClientRef action={boundFn} />,
    
  863.       webpackMap,
    
  864.     );
    
  865. 
    
  866.     const response = ReactServerDOMClient.createFromReadableStream(stream, {
    
  867.       async callServer(ref, args) {
    
  868.         const body = await ReactServerDOMClient.encodeReply(args);
    
  869.         return callServer(ref, body);
    
  870.       },
    
  871.     });
    
  872. 
    
  873.     function App() {
    
  874.       return use(response);
    
  875.     }
    
  876. 
    
  877.     const container = document.createElement('div');
    
  878.     const root = ReactDOMClient.createRoot(container);
    
  879.     await act(() => {
    
  880.       root.render(<App />);
    
  881.     });
    
  882.     expect(container.innerHTML).toBe('Click Me');
    
  883.     expect(typeof actionProxy).toBe('function');
    
  884.     expect(actionProxy).not.toBe(boundFn);
    
  885. 
    
  886.     const result = await actionProxy('hi');
    
  887.     expect(result).toBe('Hello HI');
    
  888.   });
    
  889. 
    
  890.   it('can call a module split server function', async () => {
    
  891.     let actionProxy;
    
  892. 
    
  893.     function Client({action}) {
    
  894.       actionProxy = action;
    
  895.       return 'Click Me';
    
  896.     }
    
  897. 
    
  898.     function greet(text) {
    
  899.       return 'Hello ' + text;
    
  900.     }
    
  901. 
    
  902.     const ServerModule = serverExports({
    
  903.       // This gets split into another module
    
  904.       split: greet,
    
  905.     });
    
  906.     const ClientRef = clientExports(Client);
    
  907. 
    
  908.     const stream = ReactServerDOMServer.renderToReadableStream(
    
  909.       <ClientRef action={ServerModule.split} />,
    
  910.       webpackMap,
    
  911.     );
    
  912. 
    
  913.     const response = ReactServerDOMClient.createFromReadableStream(stream, {
    
  914.       async callServer(ref, args) {
    
  915.         const body = await ReactServerDOMClient.encodeReply(args);
    
  916.         return callServer(ref, body);
    
  917.       },
    
  918.     });
    
  919. 
    
  920.     function App() {
    
  921.       return use(response);
    
  922.     }
    
  923. 
    
  924.     const container = document.createElement('div');
    
  925.     const root = ReactDOMClient.createRoot(container);
    
  926.     await act(() => {
    
  927.       root.render(<App />);
    
  928.     });
    
  929.     expect(container.innerHTML).toBe('Click Me');
    
  930.     expect(typeof actionProxy).toBe('function');
    
  931. 
    
  932.     const result = await actionProxy('Split');
    
  933.     expect(result).toBe('Hello Split');
    
  934.   });
    
  935. 
    
  936.   it('can pass a server function by importing from client back to server', async () => {
    
  937.     function greet(transform, text) {
    
  938.       return 'Hello ' + transform(text);
    
  939.     }
    
  940. 
    
  941.     function upper(text) {
    
  942.       return text.toUpperCase();
    
  943.     }
    
  944. 
    
  945.     const ServerModuleA = serverExports({
    
  946.       greet,
    
  947.     });
    
  948.     const ServerModuleB = serverExports({
    
  949.       upper,
    
  950.     });
    
  951. 
    
  952.     let actionProxy;
    
  953. 
    
  954.     // This is a Proxy representing ServerModuleB in the Client bundle.
    
  955.     const ServerModuleBImportedOnClient = {
    
  956.       upper: ReactServerDOMClient.createServerReference(
    
  957.         ServerModuleB.upper.$$id,
    
  958.         async function (ref, args) {
    
  959.           const body = await ReactServerDOMClient.encodeReply(args);
    
  960.           return callServer(ref, body);
    
  961.         },
    
  962.       ),
    
  963.     };
    
  964. 
    
  965.     function Client({action}) {
    
  966.       // Client side pass a Server Reference into an action.
    
  967.       actionProxy = text => action(ServerModuleBImportedOnClient.upper, text);
    
  968.       return 'Click Me';
    
  969.     }
    
  970. 
    
  971.     const ClientRef = clientExports(Client);
    
  972. 
    
  973.     const stream = ReactServerDOMServer.renderToReadableStream(
    
  974.       <ClientRef action={ServerModuleA.greet} />,
    
  975.       webpackMap,
    
  976.     );
    
  977. 
    
  978.     const response = ReactServerDOMClient.createFromReadableStream(stream, {
    
  979.       async callServer(ref, args) {
    
  980.         const body = await ReactServerDOMClient.encodeReply(args);
    
  981.         return callServer(ref, body);
    
  982.       },
    
  983.     });
    
  984. 
    
  985.     function App() {
    
  986.       return use(response);
    
  987.     }
    
  988. 
    
  989.     const container = document.createElement('div');
    
  990.     const root = ReactDOMClient.createRoot(container);
    
  991.     await act(() => {
    
  992.       root.render(<App />);
    
  993.     });
    
  994.     expect(container.innerHTML).toBe('Click Me');
    
  995. 
    
  996.     const result = await actionProxy('hi');
    
  997.     expect(result).toBe('Hello HI');
    
  998.   });
    
  999. 
    
  1000.   it('can bind arguments to a server reference', async () => {
    
  1001.     let actionProxy;
    
  1002. 
    
  1003.     function Client({action}) {
    
  1004.       actionProxy = action;
    
  1005.       return 'Click Me';
    
  1006.     }
    
  1007. 
    
  1008.     const greet = serverExports(function greet(a, b, c) {
    
  1009.       return a + ' ' + b + c;
    
  1010.     });
    
  1011.     const ClientRef = clientExports(Client);
    
  1012. 
    
  1013.     const stream = ReactServerDOMServer.renderToReadableStream(
    
  1014.       <ClientRef action={greet.bind(null, 'Hello', 'World')} />,
    
  1015.       webpackMap,
    
  1016.     );
    
  1017. 
    
  1018.     const response = ReactServerDOMClient.createFromReadableStream(stream, {
    
  1019.       async callServer(actionId, args) {
    
  1020.         const body = await ReactServerDOMClient.encodeReply(args);
    
  1021.         return callServer(actionId, body);
    
  1022.       },
    
  1023.     });
    
  1024. 
    
  1025.     function App() {
    
  1026.       return use(response);
    
  1027.     }
    
  1028. 
    
  1029.     const container = document.createElement('div');
    
  1030.     const root = ReactDOMClient.createRoot(container);
    
  1031.     await act(() => {
    
  1032.       root.render(<App />);
    
  1033.     });
    
  1034.     expect(container.innerHTML).toBe('Click Me');
    
  1035.     expect(typeof actionProxy).toBe('function');
    
  1036.     expect(actionProxy).not.toBe(greet);
    
  1037. 
    
  1038.     const result = await actionProxy('!');
    
  1039.     expect(result).toBe('Hello World!');
    
  1040.   });
    
  1041. 
    
  1042.   it('propagates server reference errors to the client', async () => {
    
  1043.     let actionProxy;
    
  1044. 
    
  1045.     function Client({action}) {
    
  1046.       actionProxy = action;
    
  1047.       return 'Click Me';
    
  1048.     }
    
  1049. 
    
  1050.     async function send(text) {
    
  1051.       return Promise.reject(new Error(`Error for ${text}`));
    
  1052.     }
    
  1053. 
    
  1054.     const ServerModule = serverExports({send});
    
  1055.     const ClientRef = clientExports(Client);
    
  1056. 
    
  1057.     const stream = ReactServerDOMServer.renderToReadableStream(
    
  1058.       <ClientRef action={ServerModule.send} />,
    
  1059.       webpackMap,
    
  1060.     );
    
  1061. 
    
  1062.     const response = ReactServerDOMClient.createFromReadableStream(stream, {
    
  1063.       async callServer(actionId, args) {
    
  1064.         const body = await ReactServerDOMClient.encodeReply(args);
    
  1065.         return ReactServerDOMClient.createFromReadableStream(
    
  1066.           ReactServerDOMServer.renderToReadableStream(
    
  1067.             callServer(actionId, body),
    
  1068.             null,
    
  1069.             {onError: error => 'test-error-digest'},
    
  1070.           ),
    
  1071.         );
    
  1072.       },
    
  1073.     });
    
  1074. 
    
  1075.     function App() {
    
  1076.       return use(response);
    
  1077.     }
    
  1078. 
    
  1079.     const container = document.createElement('div');
    
  1080.     const root = ReactDOMClient.createRoot(container);
    
  1081.     await act(() => {
    
  1082.       root.render(<App />);
    
  1083.     });
    
  1084. 
    
  1085.     if (__DEV__) {
    
  1086.       await expect(actionProxy('test')).rejects.toThrow('Error for test');
    
  1087.     } else {
    
  1088.       let thrownError;
    
  1089. 
    
  1090.       try {
    
  1091.         await actionProxy('test');
    
  1092.       } catch (error) {
    
  1093.         thrownError = error;
    
  1094.       }
    
  1095. 
    
  1096.       expect(thrownError).toEqual(
    
  1097.         new Error(
    
  1098.           'An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.',
    
  1099.         ),
    
  1100.       );
    
  1101. 
    
  1102.       expect(thrownError.digest).toBe('test-error-digest');
    
  1103.     }
    
  1104.   });
    
  1105. 
    
  1106.   it('supports Float hints before the first await in server components in Fiber', async () => {
    
  1107.     function Component() {
    
  1108.       return <p>hello world</p>;
    
  1109.     }
    
  1110. 
    
  1111.     const ClientComponent = clientExports(Component);
    
  1112. 
    
  1113.     async function ServerComponent() {
    
  1114.       ReactServerDOM.preload('before', {as: 'style'});
    
  1115.       await 1;
    
  1116.       ReactServerDOM.preload('after', {as: 'style'});
    
  1117.       return <ClientComponent />;
    
  1118.     }
    
  1119. 
    
  1120.     const stream = ReactServerDOMServer.renderToReadableStream(
    
  1121.       <ServerComponent />,
    
  1122.       webpackMap,
    
  1123.     );
    
  1124. 
    
  1125.     let response = null;
    
  1126.     function getResponse() {
    
  1127.       if (response === null) {
    
  1128.         response = ReactServerDOMClient.createFromReadableStream(stream);
    
  1129.       }
    
  1130.       return response;
    
  1131.     }
    
  1132. 
    
  1133.     function App() {
    
  1134.       return getResponse();
    
  1135.     }
    
  1136. 
    
  1137.     // pausing to let Flight runtime tick. This is a test only artifact of the fact that
    
  1138.     // we aren't operating separate module graphs for flight and fiber. In a real app
    
  1139.     // each would have their own dispatcher and there would be no cross dispatching.
    
  1140.     await 1;
    
  1141. 
    
  1142.     const container = document.createElement('div');
    
  1143.     const root = ReactDOMClient.createRoot(container);
    
  1144.     await act(() => {
    
  1145.       root.render(<App />);
    
  1146.     });
    
  1147.     expect(document.head.innerHTML).toBe(
    
  1148.       '<link rel="preload" href="before" as="style">',
    
  1149.     );
    
  1150.     expect(container.innerHTML).toBe('<p>hello world</p>');
    
  1151.   });
    
  1152. 
    
  1153.   it('Does not support Float hints in server components anywhere in Fizz', async () => {
    
  1154.     // In environments that do not support AsyncLocalStorage the Flight client has no ability
    
  1155.     // to scope hint dispatching to a specific Request. In Fiber this isn't a problem because
    
  1156.     // the Browser scope acts like a singleton and we can dispatch away. But in Fizz we need to have
    
  1157.     // a reference to Resources and this is only possible during render unless you support AsyncLocalStorage.
    
  1158.     function Component() {
    
  1159.       return <p>hello world</p>;
    
  1160.     }
    
  1161. 
    
  1162.     const ClientComponent = clientExports(Component);
    
  1163. 
    
  1164.     async function ServerComponent() {
    
  1165.       ReactDOM.preload('before', {as: 'style'});
    
  1166.       await 1;
    
  1167.       ReactDOM.preload('after', {as: 'style'});
    
  1168.       return <ClientComponent />;
    
  1169.     }
    
  1170. 
    
  1171.     const stream = ReactServerDOMServer.renderToReadableStream(
    
  1172.       <ServerComponent />,
    
  1173.       webpackMap,
    
  1174.     );
    
  1175. 
    
  1176.     let response = null;
    
  1177.     function getResponse() {
    
  1178.       if (response === null) {
    
  1179.         response = ReactServerDOMClient.createFromReadableStream(stream);
    
  1180.       }
    
  1181.       return response;
    
  1182.     }
    
  1183. 
    
  1184.     function App() {
    
  1185.       return (
    
  1186.         <html>
    
  1187.           <body>{getResponse()}</body>
    
  1188.         </html>
    
  1189.       );
    
  1190.     }
    
  1191. 
    
  1192.     // pausing to let Flight runtime tick. This is a test only artifact of the fact that
    
  1193.     // we aren't operating separate module graphs for flight and fiber. In a real app
    
  1194.     // each would have their own dispatcher and there would be no cross dispatching.
    
  1195.     await 1;
    
  1196. 
    
  1197.     let fizzStream;
    
  1198.     await act(async () => {
    
  1199.       fizzStream = await ReactDOMFizzServer.renderToReadableStream(<App />);
    
  1200.     });
    
  1201. 
    
  1202.     const decoder = new TextDecoder();
    
  1203.     const reader = fizzStream.getReader();
    
  1204.     let content = '';
    
  1205.     while (true) {
    
  1206.       const {done, value} = await reader.read();
    
  1207.       if (done) {
    
  1208.         content += decoder.decode();
    
  1209.         break;
    
  1210.       }
    
  1211.       content += decoder.decode(value, {stream: true});
    
  1212.     }
    
  1213. 
    
  1214.     expect(content).toEqual(
    
  1215.       '<!DOCTYPE html><html><head>' +
    
  1216.         '</head><body><p>hello world</p></body></html>',
    
  1217.     );
    
  1218.   });
    
  1219. 
    
  1220.   // @gate enablePostpone
    
  1221.   it('supports postpone in Server Components', async () => {
    
  1222.     function Server() {
    
  1223.       React.unstable_postpone('testing postpone');
    
  1224.       return 'Not shown';
    
  1225.     }
    
  1226. 
    
  1227.     let postponed = null;
    
  1228. 
    
  1229.     const stream = ReactServerDOMServer.renderToReadableStream(
    
  1230.       <Suspense fallback="Loading...">
    
  1231.         <Server />
    
  1232.       </Suspense>,
    
  1233.       null,
    
  1234.       {
    
  1235.         onPostpone(reason) {
    
  1236.           postponed = reason;
    
  1237.         },
    
  1238.       },
    
  1239.     );
    
  1240.     const response = ReactServerDOMClient.createFromReadableStream(stream);
    
  1241. 
    
  1242.     function Client() {
    
  1243.       return use(response);
    
  1244.     }
    
  1245. 
    
  1246.     const container = document.createElement('div');
    
  1247.     const root = ReactDOMClient.createRoot(container);
    
  1248.     await act(async () => {
    
  1249.       root.render(
    
  1250.         <div>
    
  1251.           Shell: <Client />
    
  1252.         </div>,
    
  1253.       );
    
  1254.     });
    
  1255.     // We should have reserved the shell already. Which means that the Server
    
  1256.     // Component should've been a lazy component.
    
  1257.     expect(container.innerHTML).toContain('Shell:');
    
  1258.     expect(container.innerHTML).toContain('Loading...');
    
  1259.     expect(container.innerHTML).not.toContain('Not shown');
    
  1260. 
    
  1261.     expect(postponed).toBe('testing postpone');
    
  1262.   });
    
  1263. 
    
  1264.   it('should not continue rendering after the reader cancels', async () => {
    
  1265.     let hasLoaded = false;
    
  1266.     let resolve;
    
  1267.     let rendered = false;
    
  1268.     const promise = new Promise(r => (resolve = r));
    
  1269.     function Wait() {
    
  1270.       if (!hasLoaded) {
    
  1271.         throw promise;
    
  1272.       }
    
  1273.       rendered = true;
    
  1274.       return 'Done';
    
  1275.     }
    
  1276.     const errors = [];
    
  1277.     const stream = await ReactServerDOMServer.renderToReadableStream(
    
  1278.       <div>
    
  1279.         <Suspense fallback={<div>Loading</div>}>
    
  1280.           <Wait />
    
  1281.         </Suspense>
    
  1282.       </div>,
    
  1283.       null,
    
  1284.       {
    
  1285.         onError(x) {
    
  1286.           errors.push(x.message);
    
  1287.         },
    
  1288.       },
    
  1289.     );
    
  1290. 
    
  1291.     expect(rendered).toBe(false);
    
  1292. 
    
  1293.     const reader = stream.getReader();
    
  1294.     await reader.read();
    
  1295.     await reader.cancel();
    
  1296. 
    
  1297.     expect(errors).toEqual([
    
  1298.       'The render was aborted by the server without a reason.',
    
  1299.     ]);
    
  1300. 
    
  1301.     hasLoaded = true;
    
  1302.     resolve();
    
  1303. 
    
  1304.     await jest.runAllTimers();
    
  1305. 
    
  1306.     expect(rendered).toBe(false);
    
  1307. 
    
  1308.     expect(errors).toEqual([
    
  1309.       'The render was aborted by the server without a reason.',
    
  1310.     ]);
    
  1311.   });
    
  1312. 
    
  1313.   // @gate enablePostpone
    
  1314.   it('postpones when abort passes a postpone signal', async () => {
    
  1315.     const infinitePromise = new Promise(() => {});
    
  1316.     function Server() {
    
  1317.       return infinitePromise;
    
  1318.     }
    
  1319. 
    
  1320.     let postponed = null;
    
  1321.     let error = null;
    
  1322. 
    
  1323.     const controller = new AbortController();
    
  1324.     const stream = ReactServerDOMServer.renderToReadableStream(
    
  1325.       <Suspense fallback="Loading...">
    
  1326.         <Server />
    
  1327.       </Suspense>,
    
  1328.       null,
    
  1329.       {
    
  1330.         onError(x) {
    
  1331.           error = x;
    
  1332.         },
    
  1333.         onPostpone(reason) {
    
  1334.           postponed = reason;
    
  1335.         },
    
  1336.         signal: controller.signal,
    
  1337.       },
    
  1338.     );
    
  1339. 
    
  1340.     try {
    
  1341.       React.unstable_postpone('testing postpone');
    
  1342.     } catch (reason) {
    
  1343.       controller.abort(reason);
    
  1344.     }
    
  1345. 
    
  1346.     const response = ReactServerDOMClient.createFromReadableStream(stream);
    
  1347. 
    
  1348.     function Client() {
    
  1349.       return use(response);
    
  1350.     }
    
  1351. 
    
  1352.     const container = document.createElement('div');
    
  1353.     const root = ReactDOMClient.createRoot(container);
    
  1354.     await act(async () => {
    
  1355.       root.render(
    
  1356.         <div>
    
  1357.           Shell: <Client />
    
  1358.         </div>,
    
  1359.       );
    
  1360.     });
    
  1361.     // We should have reserved the shell already. Which means that the Server
    
  1362.     // Component should've been a lazy component.
    
  1363.     expect(container.innerHTML).toContain('Shell:');
    
  1364.     expect(container.innerHTML).toContain('Loading...');
    
  1365.     expect(container.innerHTML).not.toContain('Not shown');
    
  1366. 
    
  1367.     expect(postponed).toBe('testing postpone');
    
  1368.     expect(error).toBe(null);
    
  1369.   });
    
  1370. });