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.  * @jest-environment node
    
  9.  */
    
  10. 
    
  11. 'use strict';
    
  12. 
    
  13. const heldValues = [];
    
  14. let finalizationCallback;
    
  15. function FinalizationRegistryMock(callback) {
    
  16.   finalizationCallback = callback;
    
  17. }
    
  18. FinalizationRegistryMock.prototype.register = function (target, heldValue) {
    
  19.   heldValues.push(heldValue);
    
  20. };
    
  21. global.FinalizationRegistry = FinalizationRegistryMock;
    
  22. 
    
  23. function gc() {
    
  24.   for (let i = 0; i < heldValues.length; i++) {
    
  25.     finalizationCallback(heldValues[i]);
    
  26.   }
    
  27.   heldValues.length = 0;
    
  28. }
    
  29. 
    
  30. let act;
    
  31. let use;
    
  32. let startTransition;
    
  33. let React;
    
  34. let ReactServer;
    
  35. let ReactNoop;
    
  36. let ReactNoopFlightServer;
    
  37. let ReactNoopFlightClient;
    
  38. let ErrorBoundary;
    
  39. let NoErrorExpected;
    
  40. let Scheduler;
    
  41. let assertLog;
    
  42. 
    
  43. describe('ReactFlight', () => {
    
  44.   beforeEach(() => {
    
  45.     jest.resetModules();
    
  46.     jest.mock('react', () => require('react/react.shared-subset'));
    
  47.     ReactServer = require('react');
    
  48.     ReactNoopFlightServer = require('react-noop-renderer/flight-server');
    
  49.     // This stores the state so we need to preserve it
    
  50.     const flightModules = require('react-noop-renderer/flight-modules');
    
  51.     __unmockReact();
    
  52.     jest.resetModules();
    
  53.     jest.mock('react-noop-renderer/flight-modules', () => flightModules);
    
  54.     React = require('react');
    
  55.     startTransition = React.startTransition;
    
  56.     use = React.use;
    
  57.     ReactNoop = require('react-noop-renderer');
    
  58.     ReactNoopFlightClient = require('react-noop-renderer/flight-client');
    
  59.     act = require('internal-test-utils').act;
    
  60.     Scheduler = require('scheduler');
    
  61.     const InternalTestUtils = require('internal-test-utils');
    
  62.     assertLog = InternalTestUtils.assertLog;
    
  63. 
    
  64.     ErrorBoundary = class extends React.Component {
    
  65.       state = {hasError: false, error: null};
    
  66.       static getDerivedStateFromError(error) {
    
  67.         return {
    
  68.           hasError: true,
    
  69.           error,
    
  70.         };
    
  71.       }
    
  72.       componentDidMount() {
    
  73.         expect(this.state.hasError).toBe(true);
    
  74.         expect(this.state.error).toBeTruthy();
    
  75.         if (__DEV__) {
    
  76.           expect(this.state.error.message).toContain(
    
  77.             this.props.expectedMessage,
    
  78.           );
    
  79.           expect(this.state.error.digest).toBe('a dev digest');
    
  80.         } else {
    
  81.           expect(this.state.error.message).toBe(
    
  82.             'An error occurred in the Server Components render. The specific message is omitted in production' +
    
  83.               ' builds to avoid leaking sensitive details. A digest property is included on this error instance which' +
    
  84.               ' may provide additional details about the nature of the error.',
    
  85.           );
    
  86.           expect(this.state.error.digest).toContain(this.props.expectedMessage);
    
  87.           expect(this.state.error.stack).toBe(
    
  88.             'Error: ' + this.state.error.message,
    
  89.           );
    
  90.         }
    
  91.       }
    
  92.       render() {
    
  93.         if (this.state.hasError) {
    
  94.           return this.state.error.message;
    
  95.         }
    
  96.         return this.props.children;
    
  97.       }
    
  98.     };
    
  99. 
    
  100.     NoErrorExpected = class extends React.Component {
    
  101.       state = {hasError: false, error: null};
    
  102.       static getDerivedStateFromError(error) {
    
  103.         return {
    
  104.           hasError: true,
    
  105.           error,
    
  106.         };
    
  107.       }
    
  108.       componentDidMount() {
    
  109.         expect(this.state.error).toBe(null);
    
  110.         expect(this.state.hasError).toBe(false);
    
  111.       }
    
  112.       render() {
    
  113.         if (this.state.hasError) {
    
  114.           return this.state.error.message;
    
  115.         }
    
  116.         return this.props.children;
    
  117.       }
    
  118.     };
    
  119.   });
    
  120. 
    
  121.   afterEach(() => {
    
  122.     jest.restoreAllMocks();
    
  123.   });
    
  124. 
    
  125.   function createServerContext(globalName, defaultValue, withStack) {
    
  126.     let ctx;
    
  127.     expect(() => {
    
  128.       ctx = React.createServerContext(globalName, defaultValue);
    
  129.     }).toErrorDev(
    
  130.       'Server Context is deprecated and will soon be removed. ' +
    
  131.         'It was never documented and we have found it not to be useful ' +
    
  132.         'enough to warrant the downside it imposes on all apps.',
    
  133.       {withoutStack: !withStack},
    
  134.     );
    
  135.     return ctx;
    
  136.   }
    
  137. 
    
  138.   function createServerServerContext(globalName, defaultValue, withStack) {
    
  139.     let ctx;
    
  140.     expect(() => {
    
  141.       ctx = ReactServer.createServerContext(globalName, defaultValue);
    
  142.     }).toErrorDev(
    
  143.       'Server Context is deprecated and will soon be removed. ' +
    
  144.         'It was never documented and we have found it not to be useful ' +
    
  145.         'enough to warrant the downside it imposes on all apps.',
    
  146.       {withoutStack: !withStack},
    
  147.     );
    
  148.     return ctx;
    
  149.   }
    
  150. 
    
  151.   function clientReference(value) {
    
  152.     return Object.defineProperties(
    
  153.       function () {
    
  154.         throw new Error('Cannot call a client function from the server.');
    
  155.       },
    
  156.       {
    
  157.         $$typeof: {value: Symbol.for('react.client.reference')},
    
  158.         value: {value: value},
    
  159.       },
    
  160.     );
    
  161.   }
    
  162. 
    
  163.   it('can render a Server Component', async () => {
    
  164.     function Bar({text}) {
    
  165.       return text.toUpperCase();
    
  166.     }
    
  167.     function Foo() {
    
  168.       return {
    
  169.         bar: (
    
  170.           <div>
    
  171.             <Bar text="a" />, <Bar text="b" />
    
  172.           </div>
    
  173.         ),
    
  174.       };
    
  175.     }
    
  176.     const transport = ReactNoopFlightServer.render({
    
  177.       foo: <Foo />,
    
  178.     });
    
  179.     const model = await ReactNoopFlightClient.read(transport);
    
  180.     expect(model).toEqual({
    
  181.       foo: {
    
  182.         bar: (
    
  183.           <div>
    
  184.             {'A'}
    
  185.             {', '}
    
  186.             {'B'}
    
  187.           </div>
    
  188.         ),
    
  189.       },
    
  190.     });
    
  191.   });
    
  192. 
    
  193.   it('can render a Client Component using a module reference and render there', async () => {
    
  194.     function UserClient(props) {
    
  195.       return (
    
  196.         <span>
    
  197.           {props.greeting}, {props.name}
    
  198.         </span>
    
  199.       );
    
  200.     }
    
  201.     const User = clientReference(UserClient);
    
  202. 
    
  203.     function Greeting({firstName, lastName}) {
    
  204.       return <User greeting="Hello" name={firstName + ' ' + lastName} />;
    
  205.     }
    
  206. 
    
  207.     const model = {
    
  208.       greeting: <Greeting firstName="Seb" lastName="Smith" />,
    
  209.     };
    
  210. 
    
  211.     const transport = ReactNoopFlightServer.render(model);
    
  212. 
    
  213.     await act(async () => {
    
  214.       const rootModel = await ReactNoopFlightClient.read(transport);
    
  215.       const greeting = rootModel.greeting;
    
  216.       ReactNoop.render(greeting);
    
  217.     });
    
  218. 
    
  219.     expect(ReactNoop).toMatchRenderedOutput(<span>Hello, Seb Smith</span>);
    
  220.   });
    
  221. 
    
  222.   it('can render an iterable as an array', async () => {
    
  223.     function ItemListClient(props) {
    
  224.       return <span>{props.items}</span>;
    
  225.     }
    
  226.     const ItemList = clientReference(ItemListClient);
    
  227. 
    
  228.     function Items() {
    
  229.       const iterable = {
    
  230.         [Symbol.iterator]: function* () {
    
  231.           yield 'A';
    
  232.           yield 'B';
    
  233.           yield 'C';
    
  234.         },
    
  235.       };
    
  236.       return <ItemList items={iterable} />;
    
  237.     }
    
  238. 
    
  239.     const model = <Items />;
    
  240. 
    
  241.     const transport = ReactNoopFlightServer.render(model);
    
  242. 
    
  243.     await act(async () => {
    
  244.       ReactNoop.render(await ReactNoopFlightClient.read(transport));
    
  245.     });
    
  246. 
    
  247.     expect(ReactNoop).toMatchRenderedOutput(<span>ABC</span>);
    
  248.   });
    
  249. 
    
  250.   it('can render undefined', async () => {
    
  251.     function Undefined() {
    
  252.       return undefined;
    
  253.     }
    
  254. 
    
  255.     const model = <Undefined />;
    
  256. 
    
  257.     const transport = ReactNoopFlightServer.render(model);
    
  258. 
    
  259.     await act(async () => {
    
  260.       ReactNoop.render(await ReactNoopFlightClient.read(transport));
    
  261.     });
    
  262. 
    
  263.     expect(ReactNoop).toMatchRenderedOutput(null);
    
  264.   });
    
  265. 
    
  266.   // @gate FIXME
    
  267.   it('should transport undefined object values', async () => {
    
  268.     function ServerComponent(props) {
    
  269.       return 'prop' in props
    
  270.         ? `\`prop\` in props as '${props.prop}'`
    
  271.         : '`prop` not in props';
    
  272.     }
    
  273.     const ClientComponent = clientReference(ServerComponent);
    
  274. 
    
  275.     const model = (
    
  276.       <>
    
  277.         <div>
    
  278.           Server: <ServerComponent prop={undefined} />
    
  279.         </div>
    
  280.         <div>
    
  281.           Client: <ClientComponent prop={undefined} />
    
  282.         </div>
    
  283.       </>
    
  284.     );
    
  285. 
    
  286.     const transport = ReactNoopFlightServer.render(model);
    
  287. 
    
  288.     await act(async () => {
    
  289.       ReactNoop.render(await ReactNoopFlightClient.read(transport));
    
  290.     });
    
  291. 
    
  292.     expect(ReactNoop).toMatchRenderedOutput(
    
  293.       <>
    
  294.         <div>Server: `prop` in props as 'undefined'</div>
    
  295.         <div>Client: `prop` in props as 'undefined'</div>
    
  296.       </>,
    
  297.     );
    
  298.   });
    
  299. 
    
  300.   it('can render an empty fragment', async () => {
    
  301.     function Empty() {
    
  302.       return <React.Fragment />;
    
  303.     }
    
  304. 
    
  305.     const model = <Empty />;
    
  306. 
    
  307.     const transport = ReactNoopFlightServer.render(model);
    
  308. 
    
  309.     await act(async () => {
    
  310.       ReactNoop.render(await ReactNoopFlightClient.read(transport));
    
  311.     });
    
  312. 
    
  313.     expect(ReactNoop).toMatchRenderedOutput(null);
    
  314.   });
    
  315. 
    
  316.   it('can transport weird numbers', async () => {
    
  317.     const nums = [0, -0, Infinity, -Infinity, NaN];
    
  318.     function ComponentClient({prop}) {
    
  319.       expect(prop).not.toBe(nums);
    
  320.       expect(prop).toEqual(nums);
    
  321.       expect(prop.every((p, i) => Object.is(p, nums[i]))).toBe(true);
    
  322.       return `prop: ${prop}`;
    
  323.     }
    
  324.     const Component = clientReference(ComponentClient);
    
  325. 
    
  326.     const model = <Component prop={nums} />;
    
  327. 
    
  328.     const transport = ReactNoopFlightServer.render(model);
    
  329. 
    
  330.     await act(async () => {
    
  331.       ReactNoop.render(await ReactNoopFlightClient.read(transport));
    
  332.     });
    
  333. 
    
  334.     expect(ReactNoop).toMatchRenderedOutput(
    
  335.       // already checked -0 with expects above
    
  336.       'prop: 0,0,Infinity,-Infinity,NaN',
    
  337.     );
    
  338.   });
    
  339. 
    
  340.   it('can transport BigInt', async () => {
    
  341.     function ComponentClient({prop}) {
    
  342.       return `prop: ${prop} (${typeof prop})`;
    
  343.     }
    
  344.     const Component = clientReference(ComponentClient);
    
  345. 
    
  346.     const model = <Component prop={90071992547409910000n} />;
    
  347. 
    
  348.     const transport = ReactNoopFlightServer.render(model);
    
  349. 
    
  350.     await act(async () => {
    
  351.       ReactNoop.render(await ReactNoopFlightClient.read(transport));
    
  352.     });
    
  353. 
    
  354.     expect(ReactNoop).toMatchRenderedOutput(
    
  355.       'prop: 90071992547409910000 (bigint)',
    
  356.     );
    
  357.   });
    
  358. 
    
  359.   it('can transport Date', async () => {
    
  360.     function ComponentClient({prop}) {
    
  361.       return `prop: ${prop.toISOString()}`;
    
  362.     }
    
  363.     const Component = clientReference(ComponentClient);
    
  364. 
    
  365.     const model = <Component prop={new Date(1234567890123)} />;
    
  366. 
    
  367.     const transport = ReactNoopFlightServer.render(model);
    
  368. 
    
  369.     await act(async () => {
    
  370.       ReactNoop.render(await ReactNoopFlightClient.read(transport));
    
  371.     });
    
  372. 
    
  373.     expect(ReactNoop).toMatchRenderedOutput('prop: 2009-02-13T23:31:30.123Z');
    
  374.   });
    
  375. 
    
  376.   it('can transport Map', async () => {
    
  377.     function ComponentClient({prop, selected}) {
    
  378.       return `
    
  379.         map: ${prop instanceof Map}
    
  380.         size: ${prop.size}
    
  381.         greet: ${prop.get('hi').greet}
    
  382.         content: ${JSON.stringify(Array.from(prop))}
    
  383.         selected: ${prop.get(selected)}
    
  384.       `;
    
  385.     }
    
  386.     const Component = clientReference(ComponentClient);
    
  387. 
    
  388.     const objKey = {obj: 'key'};
    
  389.     const map = new Map([
    
  390.       ['hi', {greet: 'world'}],
    
  391.       [objKey, 123],
    
  392.     ]);
    
  393.     const model = <Component prop={map} selected={objKey} />;
    
  394. 
    
  395.     const transport = ReactNoopFlightServer.render(model);
    
  396. 
    
  397.     await act(async () => {
    
  398.       ReactNoop.render(await ReactNoopFlightClient.read(transport));
    
  399.     });
    
  400. 
    
  401.     expect(ReactNoop).toMatchRenderedOutput(`
    
  402.         map: true
    
  403.         size: 2
    
  404.         greet: world
    
  405.         content: [["hi",{"greet":"world"}],[{"obj":"key"},123]]
    
  406.         selected: 123
    
  407.       `);
    
  408.   });
    
  409. 
    
  410.   it('can transport Set', async () => {
    
  411.     function ComponentClient({prop, selected}) {
    
  412.       return `
    
  413.         set: ${prop instanceof Set}
    
  414.         size: ${prop.size}
    
  415.         hi: ${prop.has('hi')}
    
  416.         content: ${JSON.stringify(Array.from(prop))}
    
  417.         selected: ${prop.has(selected)}
    
  418.       `;
    
  419.     }
    
  420.     const Component = clientReference(ComponentClient);
    
  421. 
    
  422.     const objKey = {obj: 'key'};
    
  423.     const set = new Set(['hi', objKey]);
    
  424.     const model = <Component prop={set} selected={objKey} />;
    
  425. 
    
  426.     const transport = ReactNoopFlightServer.render(model);
    
  427. 
    
  428.     await act(async () => {
    
  429.       ReactNoop.render(await ReactNoopFlightClient.read(transport));
    
  430.     });
    
  431. 
    
  432.     expect(ReactNoop).toMatchRenderedOutput(`
    
  433.         set: true
    
  434.         size: 2
    
  435.         hi: true
    
  436.         content: ["hi",{"obj":"key"}]
    
  437.         selected: true
    
  438.       `);
    
  439.   });
    
  440. 
    
  441.   it('can transport cyclic objects', async () => {
    
  442.     function ComponentClient({prop}) {
    
  443.       expect(prop.obj.obj.obj).toBe(prop.obj.obj);
    
  444.     }
    
  445.     const Component = clientReference(ComponentClient);
    
  446. 
    
  447.     const cyclic = {obj: null};
    
  448.     cyclic.obj = cyclic;
    
  449.     const model = <Component prop={cyclic} />;
    
  450. 
    
  451.     const transport = ReactNoopFlightServer.render(model);
    
  452. 
    
  453.     await act(async () => {
    
  454.       ReactNoop.render(await ReactNoopFlightClient.read(transport));
    
  455.     });
    
  456.   });
    
  457. 
    
  458.   it('can render a lazy component as a shared component on the server', async () => {
    
  459.     function SharedComponent({text}) {
    
  460.       return (
    
  461.         <div>
    
  462.           shared<span>{text}</span>
    
  463.         </div>
    
  464.       );
    
  465.     }
    
  466. 
    
  467.     let load = null;
    
  468.     const loadSharedComponent = () => {
    
  469.       return new Promise(res => {
    
  470.         load = () => res({default: SharedComponent});
    
  471.       });
    
  472.     };
    
  473. 
    
  474.     const LazySharedComponent = React.lazy(loadSharedComponent);
    
  475. 
    
  476.     function ServerComponent() {
    
  477.       return (
    
  478.         <React.Suspense fallback={'Loading...'}>
    
  479.           <LazySharedComponent text={'a'} />
    
  480.         </React.Suspense>
    
  481.       );
    
  482.     }
    
  483. 
    
  484.     const transport = ReactNoopFlightServer.render(<ServerComponent />);
    
  485. 
    
  486.     await act(async () => {
    
  487.       const rootModel = await ReactNoopFlightClient.read(transport);
    
  488.       ReactNoop.render(rootModel);
    
  489.     });
    
  490.     expect(ReactNoop).toMatchRenderedOutput('Loading...');
    
  491.     await load();
    
  492. 
    
  493.     await act(async () => {
    
  494.       const rootModel = await ReactNoopFlightClient.read(transport);
    
  495.       ReactNoop.render(rootModel);
    
  496.     });
    
  497.     expect(ReactNoop).toMatchRenderedOutput(
    
  498.       <div>
    
  499.         shared<span>a</span>
    
  500.       </div>,
    
  501.     );
    
  502.   });
    
  503. 
    
  504.   it('errors on a Lazy element being used in Component position', async () => {
    
  505.     function SharedComponent({text}) {
    
  506.       return (
    
  507.         <div>
    
  508.           shared<span>{text}</span>
    
  509.         </div>
    
  510.       );
    
  511.     }
    
  512. 
    
  513.     let load = null;
    
  514. 
    
  515.     const LazyElementDisguisedAsComponent = React.lazy(() => {
    
  516.       return new Promise(res => {
    
  517.         load = () => res({default: <SharedComponent text={'a'} />});
    
  518.       });
    
  519.     });
    
  520. 
    
  521.     function ServerComponent() {
    
  522.       return (
    
  523.         <React.Suspense fallback={'Loading...'}>
    
  524.           <LazyElementDisguisedAsComponent text={'b'} />
    
  525.         </React.Suspense>
    
  526.       );
    
  527.     }
    
  528. 
    
  529.     const transport = ReactNoopFlightServer.render(<ServerComponent />);
    
  530. 
    
  531.     await act(async () => {
    
  532.       const rootModel = await ReactNoopFlightClient.read(transport);
    
  533.       ReactNoop.render(rootModel);
    
  534.     });
    
  535.     expect(ReactNoop).toMatchRenderedOutput('Loading...');
    
  536.     spyOnDevAndProd(console, 'error').mockImplementation(() => {});
    
  537.     await load();
    
  538.     expect(console.error).toHaveBeenCalledTimes(1);
    
  539.   });
    
  540. 
    
  541.   it('can render a lazy element', async () => {
    
  542.     function SharedComponent({text}) {
    
  543.       return (
    
  544.         <div>
    
  545.           shared<span>{text}</span>
    
  546.         </div>
    
  547.       );
    
  548.     }
    
  549. 
    
  550.     let load = null;
    
  551. 
    
  552.     const lazySharedElement = React.lazy(() => {
    
  553.       return new Promise(res => {
    
  554.         load = () => res({default: <SharedComponent text={'a'} />});
    
  555.       });
    
  556.     });
    
  557. 
    
  558.     function ServerComponent() {
    
  559.       return (
    
  560.         <React.Suspense fallback={'Loading...'}>
    
  561.           {lazySharedElement}
    
  562.         </React.Suspense>
    
  563.       );
    
  564.     }
    
  565. 
    
  566.     const transport = ReactNoopFlightServer.render(<ServerComponent />);
    
  567. 
    
  568.     await act(async () => {
    
  569.       const rootModel = await ReactNoopFlightClient.read(transport);
    
  570.       ReactNoop.render(rootModel);
    
  571.     });
    
  572.     expect(ReactNoop).toMatchRenderedOutput('Loading...');
    
  573.     await load();
    
  574. 
    
  575.     await act(async () => {
    
  576.       const rootModel = await ReactNoopFlightClient.read(transport);
    
  577.       ReactNoop.render(rootModel);
    
  578.     });
    
  579.     expect(ReactNoop).toMatchRenderedOutput(
    
  580.       <div>
    
  581.         shared<span>a</span>
    
  582.       </div>,
    
  583.     );
    
  584.   });
    
  585. 
    
  586.   it('errors with lazy value in element position that resolves to Component', async () => {
    
  587.     function SharedComponent({text}) {
    
  588.       return (
    
  589.         <div>
    
  590.           shared<span>{text}</span>
    
  591.         </div>
    
  592.       );
    
  593.     }
    
  594. 
    
  595.     let load = null;
    
  596. 
    
  597.     const componentDisguisedAsElement = React.lazy(() => {
    
  598.       return new Promise(res => {
    
  599.         load = () => res({default: SharedComponent});
    
  600.       });
    
  601.     });
    
  602. 
    
  603.     function ServerComponent() {
    
  604.       return (
    
  605.         <React.Suspense fallback={'Loading...'}>
    
  606.           {componentDisguisedAsElement}
    
  607.         </React.Suspense>
    
  608.       );
    
  609.     }
    
  610. 
    
  611.     const transport = ReactNoopFlightServer.render(<ServerComponent />);
    
  612. 
    
  613.     await act(async () => {
    
  614.       const rootModel = await ReactNoopFlightClient.read(transport);
    
  615.       ReactNoop.render(rootModel);
    
  616.     });
    
  617.     expect(ReactNoop).toMatchRenderedOutput('Loading...');
    
  618.     spyOnDevAndProd(console, 'error').mockImplementation(() => {});
    
  619.     await load();
    
  620.     expect(console.error).toHaveBeenCalledTimes(1);
    
  621.   });
    
  622. 
    
  623.   it('can render a lazy module reference', async () => {
    
  624.     function ClientComponent() {
    
  625.       return <div>I am client</div>;
    
  626.     }
    
  627. 
    
  628.     const ClientComponentReference = clientReference(ClientComponent);
    
  629. 
    
  630.     let load = null;
    
  631.     const loadClientComponentReference = () => {
    
  632.       return new Promise(res => {
    
  633.         load = () => res({default: ClientComponentReference});
    
  634.       });
    
  635.     };
    
  636. 
    
  637.     const LazyClientComponentReference = React.lazy(
    
  638.       loadClientComponentReference,
    
  639.     );
    
  640. 
    
  641.     function ServerComponent() {
    
  642.       return (
    
  643.         <React.Suspense fallback={'Loading...'}>
    
  644.           <LazyClientComponentReference />
    
  645.         </React.Suspense>
    
  646.       );
    
  647.     }
    
  648. 
    
  649.     const transport = ReactNoopFlightServer.render(<ServerComponent />);
    
  650. 
    
  651.     await act(async () => {
    
  652.       const rootModel = await ReactNoopFlightClient.read(transport);
    
  653.       ReactNoop.render(rootModel);
    
  654.     });
    
  655.     expect(ReactNoop).toMatchRenderedOutput('Loading...');
    
  656.     await load();
    
  657. 
    
  658.     await act(async () => {
    
  659.       const rootModel = await ReactNoopFlightClient.read(transport);
    
  660.       ReactNoop.render(rootModel);
    
  661.     });
    
  662.     expect(ReactNoop).toMatchRenderedOutput(<div>I am client</div>);
    
  663.   });
    
  664. 
    
  665.   it('should error if a non-serializable value is passed to a host component', async () => {
    
  666.     function ClientImpl({children}) {
    
  667.       return children;
    
  668.     }
    
  669.     const Client = clientReference(ClientImpl);
    
  670. 
    
  671.     function EventHandlerProp() {
    
  672.       return (
    
  673.         <div className="foo" onClick={function () {}}>
    
  674.           Test
    
  675.         </div>
    
  676.       );
    
  677.     }
    
  678.     function FunctionProp() {
    
  679.       return <div>{() => {}}</div>;
    
  680.     }
    
  681.     function SymbolProp() {
    
  682.       return <div foo={Symbol('foo')} />;
    
  683.     }
    
  684. 
    
  685.     const ref = React.createRef();
    
  686.     function RefProp() {
    
  687.       return <div ref={ref} />;
    
  688.     }
    
  689. 
    
  690.     function EventHandlerPropClient() {
    
  691.       return (
    
  692.         <Client className="foo" onClick={function () {}}>
    
  693.           Test
    
  694.         </Client>
    
  695.       );
    
  696.     }
    
  697.     function FunctionPropClient() {
    
  698.       return <Client>{() => {}}</Client>;
    
  699.     }
    
  700.     function SymbolPropClient() {
    
  701.       return <Client foo={Symbol('foo')} />;
    
  702.     }
    
  703. 
    
  704.     function RefPropClient() {
    
  705.       return <Client ref={ref} />;
    
  706.     }
    
  707. 
    
  708.     const options = {
    
  709.       onError(x) {
    
  710.         return __DEV__ ? 'a dev digest' : `digest("${x.message}")`;
    
  711.       },
    
  712.     };
    
  713.     const event = ReactNoopFlightServer.render(<EventHandlerProp />, options);
    
  714.     const fn = ReactNoopFlightServer.render(<FunctionProp />, options);
    
  715.     const symbol = ReactNoopFlightServer.render(<SymbolProp />, options);
    
  716.     const refs = ReactNoopFlightServer.render(<RefProp />, options);
    
  717.     const eventClient = ReactNoopFlightServer.render(
    
  718.       <EventHandlerPropClient />,
    
  719.       options,
    
  720.     );
    
  721.     const fnClient = ReactNoopFlightServer.render(
    
  722.       <FunctionPropClient />,
    
  723.       options,
    
  724.     );
    
  725.     const symbolClient = ReactNoopFlightServer.render(
    
  726.       <SymbolPropClient />,
    
  727.       options,
    
  728.     );
    
  729.     const refsClient = ReactNoopFlightServer.render(<RefPropClient />, options);
    
  730. 
    
  731.     function Render({promise}) {
    
  732.       return use(promise);
    
  733.     }
    
  734. 
    
  735.     await act(() => {
    
  736.       startTransition(() => {
    
  737.         ReactNoop.render(
    
  738.           <>
    
  739.             <ErrorBoundary expectedMessage="Event handlers cannot be passed to Client Component props.">
    
  740.               <Render promise={ReactNoopFlightClient.read(event)} />
    
  741.             </ErrorBoundary>
    
  742.             <ErrorBoundary
    
  743.               expectedMessage={
    
  744.                 'Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server".'
    
  745.               }>
    
  746.               <Render promise={ReactNoopFlightClient.read(fn)} />
    
  747.             </ErrorBoundary>
    
  748.             <ErrorBoundary expectedMessage="Only global symbols received from Symbol.for(...) can be passed to Client Components.">
    
  749.               <Render promise={ReactNoopFlightClient.read(symbol)} />
    
  750.             </ErrorBoundary>
    
  751.             <ErrorBoundary expectedMessage="Refs cannot be used in Server Components, nor passed to Client Components.">
    
  752.               <Render promise={ReactNoopFlightClient.read(refs)} />
    
  753.             </ErrorBoundary>
    
  754.             <ErrorBoundary expectedMessage="Event handlers cannot be passed to Client Component props.">
    
  755.               <Render promise={ReactNoopFlightClient.read(eventClient)} />
    
  756.             </ErrorBoundary>
    
  757.             <ErrorBoundary
    
  758.               expectedMessage={
    
  759.                 'Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server".'
    
  760.               }>
    
  761.               <Render promise={ReactNoopFlightClient.read(fnClient)} />
    
  762.             </ErrorBoundary>
    
  763.             <ErrorBoundary expectedMessage="Only global symbols received from Symbol.for(...) can be passed to Client Components.">
    
  764.               <Render promise={ReactNoopFlightClient.read(symbolClient)} />
    
  765.             </ErrorBoundary>
    
  766.             <ErrorBoundary expectedMessage="Refs cannot be used in Server Components, nor passed to Client Components.">
    
  767.               <Render promise={ReactNoopFlightClient.read(refsClient)} />
    
  768.             </ErrorBoundary>
    
  769.           </>,
    
  770.         );
    
  771.       });
    
  772.     });
    
  773.   });
    
  774. 
    
  775.   it('should trigger the inner most error boundary inside a Client Component', async () => {
    
  776.     function ServerComponent() {
    
  777.       throw new Error('This was thrown in the Server Component.');
    
  778.     }
    
  779. 
    
  780.     function ClientComponent({children}) {
    
  781.       // This should catch the error thrown by the Server Component, even though it has already happened.
    
  782.       // We currently need to wrap it in a div because as it's set up right now, a lazy reference will
    
  783.       // throw during reconciliation which will trigger the parent of the error boundary.
    
  784.       // This is similar to how these will suspend the parent if it's a direct child of a Suspense boundary.
    
  785.       // That's a bug.
    
  786.       return (
    
  787.         <ErrorBoundary expectedMessage="This was thrown in the Server Component.">
    
  788.           <div>{children}</div>
    
  789.         </ErrorBoundary>
    
  790.       );
    
  791.     }
    
  792. 
    
  793.     const ClientComponentReference = clientReference(ClientComponent);
    
  794. 
    
  795.     function Server() {
    
  796.       return (
    
  797.         <ClientComponentReference>
    
  798.           <ServerComponent />
    
  799.         </ClientComponentReference>
    
  800.       );
    
  801.     }
    
  802. 
    
  803.     const data = ReactNoopFlightServer.render(<Server />, {
    
  804.       onError(x) {
    
  805.         // ignore
    
  806.       },
    
  807.     });
    
  808. 
    
  809.     function Client({promise}) {
    
  810.       return use(promise);
    
  811.     }
    
  812. 
    
  813.     await act(() => {
    
  814.       startTransition(() => {
    
  815.         ReactNoop.render(
    
  816.           <NoErrorExpected>
    
  817.             <Client promise={ReactNoopFlightClient.read(data)} />
    
  818.           </NoErrorExpected>,
    
  819.         );
    
  820.       });
    
  821.     });
    
  822.   });
    
  823. 
    
  824.   it('should warn in DEV if a toJSON instance is passed to a host component', () => {
    
  825.     const obj = {
    
  826.       toJSON() {
    
  827.         return 123;
    
  828.       },
    
  829.     };
    
  830.     expect(() => {
    
  831.       const transport = ReactNoopFlightServer.render(<input value={obj} />);
    
  832.       ReactNoopFlightClient.read(transport);
    
  833.     }).toErrorDev(
    
  834.       'Only plain objects can be passed to Client Components from Server Components. ' +
    
  835.         'Objects with toJSON methods are not supported. ' +
    
  836.         'Convert it manually to a simple value before passing it to props.\n' +
    
  837.         '  <input value={{toJSON: function}}>\n' +
    
  838.         '               ^^^^^^^^^^^^^^^^^^^^',
    
  839.       {withoutStack: true},
    
  840.     );
    
  841.   });
    
  842. 
    
  843.   it('should warn in DEV if a toJSON instance is passed to a host component child', () => {
    
  844.     class MyError extends Error {
    
  845.       toJSON() {
    
  846.         return 123;
    
  847.       }
    
  848.     }
    
  849.     expect(() => {
    
  850.       const transport = ReactNoopFlightServer.render(
    
  851.         <div>Womp womp: {new MyError('spaghetti')}</div>,
    
  852.       );
    
  853.       ReactNoopFlightClient.read(transport);
    
  854.     }).toErrorDev(
    
  855.       'Error objects cannot be rendered as text children. Try formatting it using toString().\n' +
    
  856.         '  <div>Womp womp: {Error}</div>\n' +
    
  857.         '                  ^^^^^^^',
    
  858.       {withoutStack: true},
    
  859.     );
    
  860.   });
    
  861. 
    
  862.   it('should warn in DEV if a special object is passed to a host component', () => {
    
  863.     expect(() => {
    
  864.       const transport = ReactNoopFlightServer.render(<input value={Math} />);
    
  865.       ReactNoopFlightClient.read(transport);
    
  866.     }).toErrorDev(
    
  867.       'Only plain objects can be passed to Client Components from Server Components. ' +
    
  868.         'Math objects are not supported.\n' +
    
  869.         '  <input value={Math}>\n' +
    
  870.         '               ^^^^^^',
    
  871.       {withoutStack: true},
    
  872.     );
    
  873.   });
    
  874. 
    
  875.   it('should warn in DEV if an object with symbols is passed to a host component', () => {
    
  876.     expect(() => {
    
  877.       const transport = ReactNoopFlightServer.render(
    
  878.         <input value={{[Symbol.iterator]: {}}} />,
    
  879.       );
    
  880.       ReactNoopFlightClient.read(transport);
    
  881.     }).toErrorDev(
    
  882.       'Only plain objects can be passed to Client Components from Server Components. ' +
    
  883.         'Objects with symbol properties like Symbol.iterator are not supported.',
    
  884.       {withoutStack: true},
    
  885.     );
    
  886.   });
    
  887. 
    
  888.   it('should warn in DEV if a toJSON instance is passed to a Client Component', () => {
    
  889.     const obj = {
    
  890.       toJSON() {
    
  891.         return 123;
    
  892.       },
    
  893.     };
    
  894.     function ClientImpl({value}) {
    
  895.       return <div>{value}</div>;
    
  896.     }
    
  897.     const Client = clientReference(ClientImpl);
    
  898.     expect(() => {
    
  899.       const transport = ReactNoopFlightServer.render(<Client value={obj} />);
    
  900.       ReactNoopFlightClient.read(transport);
    
  901.     }).toErrorDev(
    
  902.       'Only plain objects can be passed to Client Components from Server Components. ' +
    
  903.         'Objects with toJSON methods are not supported.',
    
  904.       {withoutStack: true},
    
  905.     );
    
  906.   });
    
  907. 
    
  908.   it('should warn in DEV if a toJSON instance is passed to a Client Component child', () => {
    
  909.     const obj = {
    
  910.       toJSON() {
    
  911.         return 123;
    
  912.       },
    
  913.     };
    
  914.     function ClientImpl({children}) {
    
  915.       return <div>{children}</div>;
    
  916.     }
    
  917.     const Client = clientReference(ClientImpl);
    
  918.     expect(() => {
    
  919.       const transport = ReactNoopFlightServer.render(
    
  920.         <Client>Current date: {obj}</Client>,
    
  921.       );
    
  922.       ReactNoopFlightClient.read(transport);
    
  923.     }).toErrorDev(
    
  924.       'Only plain objects can be passed to Client Components from Server Components. ' +
    
  925.         'Objects with toJSON methods are not supported. ' +
    
  926.         'Convert it manually to a simple value before passing it to props.\n' +
    
  927.         '  <>Current date: {{toJSON: function}}</>\n' +
    
  928.         '                  ^^^^^^^^^^^^^^^^^^^^',
    
  929.       {withoutStack: true},
    
  930.     );
    
  931.   });
    
  932. 
    
  933.   it('should warn in DEV if a special object is passed to a Client Component', () => {
    
  934.     function ClientImpl({value}) {
    
  935.       return <div>{value}</div>;
    
  936.     }
    
  937.     const Client = clientReference(ClientImpl);
    
  938.     expect(() => {
    
  939.       const transport = ReactNoopFlightServer.render(<Client value={Math} />);
    
  940.       ReactNoopFlightClient.read(transport);
    
  941.     }).toErrorDev(
    
  942.       'Only plain objects can be passed to Client Components from Server Components. ' +
    
  943.         'Math objects are not supported.\n' +
    
  944.         '  <... value={Math}>\n' +
    
  945.         '             ^^^^^^',
    
  946.       {withoutStack: true},
    
  947.     );
    
  948.   });
    
  949. 
    
  950.   it('should warn in DEV if an object with symbols is passed to a Client Component', () => {
    
  951.     function ClientImpl({value}) {
    
  952.       return <div>{value}</div>;
    
  953.     }
    
  954.     const Client = clientReference(ClientImpl);
    
  955.     expect(() => {
    
  956.       const transport = ReactNoopFlightServer.render(
    
  957.         <Client value={{[Symbol.iterator]: {}}} />,
    
  958.       );
    
  959.       ReactNoopFlightClient.read(transport);
    
  960.     }).toErrorDev(
    
  961.       'Only plain objects can be passed to Client Components from Server Components. ' +
    
  962.         'Objects with symbol properties like Symbol.iterator are not supported.',
    
  963.       {withoutStack: true},
    
  964.     );
    
  965.   });
    
  966. 
    
  967.   it('should warn in DEV if a special object is passed to a nested object in Client Component', () => {
    
  968.     function ClientImpl({value}) {
    
  969.       return <div>{value}</div>;
    
  970.     }
    
  971.     const Client = clientReference(ClientImpl);
    
  972.     expect(() => {
    
  973.       const transport = ReactNoopFlightServer.render(
    
  974.         <Client value={{hello: Math, title: <h1>hi</h1>}} />,
    
  975.       );
    
  976.       ReactNoopFlightClient.read(transport);
    
  977.     }).toErrorDev(
    
  978.       'Only plain objects can be passed to Client Components from Server Components. ' +
    
  979.         'Math objects are not supported.\n' +
    
  980.         '  {hello: Math, title: <h1/>}\n' +
    
  981.         '          ^^^^',
    
  982.       {withoutStack: true},
    
  983.     );
    
  984.   });
    
  985. 
    
  986.   it('should warn in DEV if a special object is passed to a nested array in Client Component', () => {
    
  987.     function ClientImpl({value}) {
    
  988.       return <div>{value}</div>;
    
  989.     }
    
  990.     const Client = clientReference(ClientImpl);
    
  991.     expect(() => {
    
  992.       const transport = ReactNoopFlightServer.render(
    
  993.         <Client
    
  994.           value={['looooong string takes up noise', Math, <h1>hi</h1>]}
    
  995.         />,
    
  996.       );
    
  997.       ReactNoopFlightClient.read(transport);
    
  998.     }).toErrorDev(
    
  999.       'Only plain objects can be passed to Client Components from Server Components. ' +
    
  1000.         'Math objects are not supported.\n' +
    
  1001.         '  [..., Math, <h1/>]\n' +
    
  1002.         '        ^^^^',
    
  1003.       {withoutStack: true},
    
  1004.     );
    
  1005.   });
    
  1006. 
    
  1007.   it('should NOT warn in DEV for key getters', () => {
    
  1008.     const transport = ReactNoopFlightServer.render(<div key="a" />);
    
  1009.     ReactNoopFlightClient.read(transport);
    
  1010.   });
    
  1011. 
    
  1012.   it('should error if a class instance is passed to a host component', () => {
    
  1013.     class Foo {
    
  1014.       method() {}
    
  1015.     }
    
  1016.     const errors = [];
    
  1017.     ReactNoopFlightServer.render(<input value={new Foo()} />, {
    
  1018.       onError(x) {
    
  1019.         errors.push(x.message);
    
  1020.       },
    
  1021.     });
    
  1022. 
    
  1023.     expect(errors).toEqual([
    
  1024.       'Only plain objects, and a few built-ins, can be passed to Client Components ' +
    
  1025.         'from Server Components. Classes or null prototypes are not supported.',
    
  1026.     ]);
    
  1027.   });
    
  1028. 
    
  1029.   it('should warn in DEV if a a client reference is passed to useContext()', () => {
    
  1030.     const Context = React.createContext();
    
  1031.     const ClientContext = clientReference(Context);
    
  1032.     function ServerComponent() {
    
  1033.       return ReactServer.useContext(ClientContext);
    
  1034.     }
    
  1035.     expect(() => {
    
  1036.       const transport = ReactNoopFlightServer.render(<ServerComponent />);
    
  1037.       ReactNoopFlightClient.read(transport);
    
  1038.     }).toErrorDev('Cannot read a Client Context from a Server Component.', {
    
  1039.       withoutStack: true,
    
  1040.     });
    
  1041.   });
    
  1042. 
    
  1043.   describe('Hooks', () => {
    
  1044.     function DivWithId({children}) {
    
  1045.       const id = ReactServer.useId();
    
  1046.       return <div prop={id}>{children}</div>;
    
  1047.     }
    
  1048. 
    
  1049.     it('should support useId', async () => {
    
  1050.       function App() {
    
  1051.         return (
    
  1052.           <>
    
  1053.             <DivWithId />
    
  1054.             <DivWithId />
    
  1055.           </>
    
  1056.         );
    
  1057.       }
    
  1058. 
    
  1059.       const transport = ReactNoopFlightServer.render(<App />);
    
  1060.       await act(async () => {
    
  1061.         ReactNoop.render(await ReactNoopFlightClient.read(transport));
    
  1062.       });
    
  1063.       expect(ReactNoop).toMatchRenderedOutput(
    
  1064.         <>
    
  1065.           <div prop=":S1:" />
    
  1066.           <div prop=":S2:" />
    
  1067.         </>,
    
  1068.       );
    
  1069.     });
    
  1070. 
    
  1071.     it('accepts an identifier prefix that prefixes generated ids', async () => {
    
  1072.       function App() {
    
  1073.         return (
    
  1074.           <>
    
  1075.             <DivWithId />
    
  1076.             <DivWithId />
    
  1077.           </>
    
  1078.         );
    
  1079.       }
    
  1080. 
    
  1081.       const transport = ReactNoopFlightServer.render(<App />, {
    
  1082.         identifierPrefix: 'foo',
    
  1083.       });
    
  1084.       await act(async () => {
    
  1085.         ReactNoop.render(await ReactNoopFlightClient.read(transport));
    
  1086.       });
    
  1087.       expect(ReactNoop).toMatchRenderedOutput(
    
  1088.         <>
    
  1089.           <div prop=":fooS1:" />
    
  1090.           <div prop=":fooS2:" />
    
  1091.         </>,
    
  1092.       );
    
  1093.     });
    
  1094. 
    
  1095.     it('[TODO] it does not warn if you render a server element passed to a client module reference twice on the client when using useId', async () => {
    
  1096.       // @TODO Today if you render a Server Component with useId and pass it to a Client Component and that Client Component renders the element in two or more
    
  1097.       // places the id used on the server will be duplicated in the client. This is a deviation from the guarantees useId makes for Fizz/Client and is a consequence
    
  1098.       // of the fact that the Server Component is actually rendered on the server and is reduced to a set of host elements before being passed to the Client component
    
  1099.       // so the output passed to the Client has no knowledge of the useId use. In the future we would like to add a DEV warning when this happens. For now
    
  1100.       // we just accept that it is a nuance of useId in Flight
    
  1101.       function App() {
    
  1102.         const id = ReactServer.useId();
    
  1103.         const div = <div prop={id}>{id}</div>;
    
  1104.         return <ClientDoublerModuleRef el={div} />;
    
  1105.       }
    
  1106. 
    
  1107.       function ClientDoubler({el}) {
    
  1108.         Scheduler.log('ClientDoubler');
    
  1109.         return (
    
  1110.           <>
    
  1111.             {el}
    
  1112.             {el}
    
  1113.           </>
    
  1114.         );
    
  1115.       }
    
  1116. 
    
  1117.       const ClientDoublerModuleRef = clientReference(ClientDoubler);
    
  1118. 
    
  1119.       const transport = ReactNoopFlightServer.render(<App />);
    
  1120.       assertLog([]);
    
  1121. 
    
  1122.       await act(async () => {
    
  1123.         ReactNoop.render(await ReactNoopFlightClient.read(transport));
    
  1124.       });
    
  1125. 
    
  1126.       assertLog(['ClientDoubler']);
    
  1127.       expect(ReactNoop).toMatchRenderedOutput(
    
  1128.         <>
    
  1129.           <div prop=":S1:">:S1:</div>
    
  1130.           <div prop=":S1:">:S1:</div>
    
  1131.         </>,
    
  1132.       );
    
  1133.     });
    
  1134.   });
    
  1135. 
    
  1136.   describe('ServerContext', () => {
    
  1137.     // @gate enableServerContext
    
  1138.     it('supports basic createServerContext usage', async () => {
    
  1139.       const ServerContext = createServerServerContext(
    
  1140.         'ServerContext',
    
  1141.         'hello from server',
    
  1142.       );
    
  1143.       function Foo() {
    
  1144.         const context = ReactServer.useContext(ServerContext);
    
  1145.         return <div>{context}</div>;
    
  1146.       }
    
  1147. 
    
  1148.       const transport = ReactNoopFlightServer.render(<Foo />);
    
  1149.       await act(async () => {
    
  1150.         ReactNoop.render(await ReactNoopFlightClient.read(transport));
    
  1151.       });
    
  1152. 
    
  1153.       expect(ReactNoop).toMatchRenderedOutput(<div>hello from server</div>);
    
  1154.     });
    
  1155. 
    
  1156.     // @gate enableServerContext
    
  1157.     it('propagates ServerContext providers in flight', async () => {
    
  1158.       const ServerContext = createServerServerContext(
    
  1159.         'ServerContext',
    
  1160.         'default',
    
  1161.       );
    
  1162. 
    
  1163.       function Foo() {
    
  1164.         return (
    
  1165.           <div>
    
  1166.             <ServerContext.Provider value="hi this is server">
    
  1167.               <Bar />
    
  1168.             </ServerContext.Provider>
    
  1169.           </div>
    
  1170.         );
    
  1171.       }
    
  1172.       function Bar() {
    
  1173.         const context = ReactServer.useContext(ServerContext);
    
  1174.         return context;
    
  1175.       }
    
  1176. 
    
  1177.       const transport = ReactNoopFlightServer.render(<Foo />);
    
  1178.       await act(async () => {
    
  1179.         ReactNoop.render(await ReactNoopFlightClient.read(transport));
    
  1180.       });
    
  1181. 
    
  1182.       expect(ReactNoop).toMatchRenderedOutput(<div>hi this is server</div>);
    
  1183.     });
    
  1184. 
    
  1185.     // @gate enableServerContext
    
  1186.     it('errors if you try passing JSX through ServerContext value', () => {
    
  1187.       const ServerContext = createServerServerContext('ServerContext', {
    
  1188.         foo: {
    
  1189.           bar: <span>hi this is default</span>,
    
  1190.         },
    
  1191.       });
    
  1192. 
    
  1193.       function Foo() {
    
  1194.         return (
    
  1195.           <div>
    
  1196.             <ServerContext.Provider
    
  1197.               value={{
    
  1198.                 foo: {
    
  1199.                   bar: <span>hi this is server</span>,
    
  1200.                 },
    
  1201.               }}>
    
  1202.               <Bar />
    
  1203.             </ServerContext.Provider>
    
  1204.           </div>
    
  1205.         );
    
  1206.       }
    
  1207.       function Bar() {
    
  1208.         const context = ReactServer.useContext(ServerContext);
    
  1209.         return context.foo.bar;
    
  1210.       }
    
  1211. 
    
  1212.       expect(() => {
    
  1213.         ReactNoopFlightServer.render(<Foo />);
    
  1214.       }).toErrorDev('React elements are not allowed in ServerContext', {
    
  1215.         withoutStack: true,
    
  1216.       });
    
  1217.     });
    
  1218. 
    
  1219.     // @gate enableServerContext
    
  1220.     it('propagates ServerContext and cleans up the providers in flight', async () => {
    
  1221.       const ServerContext = createServerServerContext(
    
  1222.         'ServerContext',
    
  1223.         'default',
    
  1224.       );
    
  1225. 
    
  1226.       function Foo() {
    
  1227.         return (
    
  1228.           <>
    
  1229.             <ServerContext.Provider value="hi this is server outer">
    
  1230.               <ServerContext.Provider value="hi this is server">
    
  1231.                 <Bar />
    
  1232.               </ServerContext.Provider>
    
  1233.               <ServerContext.Provider value="hi this is server2">
    
  1234.                 <Bar />
    
  1235.               </ServerContext.Provider>
    
  1236.               <Bar />
    
  1237.             </ServerContext.Provider>
    
  1238.             <ServerContext.Provider value="hi this is server outer2">
    
  1239.               <Bar />
    
  1240.             </ServerContext.Provider>
    
  1241.             <Bar />
    
  1242.           </>
    
  1243.         );
    
  1244.       }
    
  1245.       function Bar() {
    
  1246.         const context = ReactServer.useContext(ServerContext);
    
  1247.         return <span>{context}</span>;
    
  1248.       }
    
  1249. 
    
  1250.       const transport = ReactNoopFlightServer.render(<Foo />);
    
  1251.       await act(async () => {
    
  1252.         ReactNoop.render(await ReactNoopFlightClient.read(transport));
    
  1253.       });
    
  1254. 
    
  1255.       expect(ReactNoop).toMatchRenderedOutput(
    
  1256.         <>
    
  1257.           <span>hi this is server</span>
    
  1258.           <span>hi this is server2</span>
    
  1259.           <span>hi this is server outer</span>
    
  1260.           <span>hi this is server outer2</span>
    
  1261.           <span>default</span>
    
  1262.         </>,
    
  1263.       );
    
  1264.     });
    
  1265. 
    
  1266.     // @gate enableServerContext
    
  1267.     it('propagates ServerContext providers in flight after suspending', async () => {
    
  1268.       const ServerContext = createServerServerContext(
    
  1269.         'ServerContext',
    
  1270.         'default',
    
  1271.       );
    
  1272. 
    
  1273.       function Foo() {
    
  1274.         return (
    
  1275.           <div>
    
  1276.             <ServerContext.Provider value="hi this is server">
    
  1277.               <React.Suspense fallback={'Loading'}>
    
  1278.                 <Bar />
    
  1279.               </React.Suspense>
    
  1280.             </ServerContext.Provider>
    
  1281.           </div>
    
  1282.         );
    
  1283.       }
    
  1284. 
    
  1285.       let resolve;
    
  1286.       const promise = new Promise(res => {
    
  1287.         resolve = () => {
    
  1288.           promise.unsuspend = true;
    
  1289.           res();
    
  1290.         };
    
  1291.       });
    
  1292. 
    
  1293.       function Bar() {
    
  1294.         if (!promise.unsuspend) {
    
  1295.           Scheduler.log('suspended');
    
  1296.           throw promise;
    
  1297.         }
    
  1298.         Scheduler.log('rendered');
    
  1299.         const context = ReactServer.useContext(ServerContext);
    
  1300.         return context;
    
  1301.       }
    
  1302. 
    
  1303.       const transport = ReactNoopFlightServer.render(<Foo />);
    
  1304. 
    
  1305.       assertLog(['suspended']);
    
  1306. 
    
  1307.       await act(async () => {
    
  1308.         resolve();
    
  1309.         await promise;
    
  1310.         jest.runAllImmediates();
    
  1311.       });
    
  1312. 
    
  1313.       assertLog(['rendered']);
    
  1314. 
    
  1315.       await act(async () => {
    
  1316.         ReactNoop.render(await ReactNoopFlightClient.read(transport));
    
  1317.       });
    
  1318. 
    
  1319.       expect(ReactNoop).toMatchRenderedOutput(<div>hi this is server</div>);
    
  1320.     });
    
  1321. 
    
  1322.     // @gate enableServerContext
    
  1323.     it('serializes ServerContext to client', async () => {
    
  1324.       const ServerContext = createServerServerContext(
    
  1325.         'ServerContext',
    
  1326.         'default',
    
  1327.       );
    
  1328.       const ClientContext = createServerContext('ServerContext', 'default');
    
  1329. 
    
  1330.       function ClientBar() {
    
  1331.         Scheduler.log('ClientBar');
    
  1332.         const context = React.useContext(ClientContext);
    
  1333.         return <span>{context}</span>;
    
  1334.       }
    
  1335. 
    
  1336.       const Bar = clientReference(ClientBar);
    
  1337. 
    
  1338.       function Foo() {
    
  1339.         return (
    
  1340.           <ServerContext.Provider value="hi this is server">
    
  1341.             <Bar />
    
  1342.           </ServerContext.Provider>
    
  1343.         );
    
  1344.       }
    
  1345. 
    
  1346.       const model = {
    
  1347.         foo: <Foo />,
    
  1348.       };
    
  1349. 
    
  1350.       const transport = ReactNoopFlightServer.render(model);
    
  1351. 
    
  1352.       assertLog([]);
    
  1353. 
    
  1354.       await act(async () => {
    
  1355.         const flightModel = await ReactNoopFlightClient.read(transport);
    
  1356.         ReactNoop.render(flightModel.foo);
    
  1357.       });
    
  1358. 
    
  1359.       assertLog(['ClientBar']);
    
  1360.       expect(ReactNoop).toMatchRenderedOutput(<span>hi this is server</span>);
    
  1361. 
    
  1362.       expect(() => {
    
  1363.         createServerContext('ServerContext', 'default');
    
  1364.       }).toThrow('ServerContext: ServerContext already defined');
    
  1365.     });
    
  1366. 
    
  1367.     // @gate enableServerContext
    
  1368.     it('takes ServerContext from the client for refetching use cases', async () => {
    
  1369.       const ServerContext = createServerServerContext(
    
  1370.         'ServerContext',
    
  1371.         'default',
    
  1372.       );
    
  1373.       function Bar() {
    
  1374.         return <span>{ReactServer.useContext(ServerContext)}</span>;
    
  1375.       }
    
  1376.       const transport = ReactNoopFlightServer.render(<Bar />, {
    
  1377.         context: [['ServerContext', 'Override']],
    
  1378.       });
    
  1379. 
    
  1380.       await act(async () => {
    
  1381.         const flightModel = await ReactNoopFlightClient.read(transport);
    
  1382.         ReactNoop.render(flightModel);
    
  1383.       });
    
  1384.       expect(ReactNoop).toMatchRenderedOutput(<span>Override</span>);
    
  1385.     });
    
  1386. 
    
  1387.     // @gate enableServerContext
    
  1388.     it('sets default initial value when defined lazily on server or client', async () => {
    
  1389.       let ServerContext;
    
  1390.       function inlineLazyServerContextInitialization() {
    
  1391.         if (!ServerContext) {
    
  1392.           ServerContext = createServerServerContext('ServerContext', 'default');
    
  1393.         }
    
  1394.         return ServerContext;
    
  1395.       }
    
  1396. 
    
  1397.       let ClientContext;
    
  1398.       function inlineContextInitialization() {
    
  1399.         if (!ClientContext) {
    
  1400.           ClientContext = createServerContext('ServerContext', 'default', true);
    
  1401.         }
    
  1402.         return ClientContext;
    
  1403.       }
    
  1404. 
    
  1405.       function ClientBaz() {
    
  1406.         const context = inlineContextInitialization();
    
  1407.         const value = React.useContext(context);
    
  1408.         return <div>{value}</div>;
    
  1409.       }
    
  1410. 
    
  1411.       const Baz = clientReference(ClientBaz);
    
  1412. 
    
  1413.       function Bar() {
    
  1414.         return (
    
  1415.           <article>
    
  1416.             <div>
    
  1417.               {ReactServer.useContext(inlineLazyServerContextInitialization())}
    
  1418.             </div>
    
  1419.             <Baz />
    
  1420.           </article>
    
  1421.         );
    
  1422.       }
    
  1423. 
    
  1424.       function ServerApp() {
    
  1425.         const Context = inlineLazyServerContextInitialization();
    
  1426.         return (
    
  1427.           <>
    
  1428.             <Context.Provider value="test">
    
  1429.               <Bar />
    
  1430.             </Context.Provider>
    
  1431.             <Bar />
    
  1432.           </>
    
  1433.         );
    
  1434.       }
    
  1435. 
    
  1436.       function ClientApp({serverModel}) {
    
  1437.         return (
    
  1438.           <>
    
  1439.             {serverModel}
    
  1440.             <ClientBaz />
    
  1441.           </>
    
  1442.         );
    
  1443.       }
    
  1444. 
    
  1445.       const transport = ReactNoopFlightServer.render(<ServerApp />);
    
  1446. 
    
  1447.       expect(ClientContext).toBe(undefined);
    
  1448. 
    
  1449.       // Reset all modules, except flight-modules which keeps the registry of Client Components
    
  1450.       const flightModules = require('react-noop-renderer/flight-modules');
    
  1451.       jest.resetModules();
    
  1452.       jest.mock('react', () => require('react/react.shared-subset'));
    
  1453.       jest.mock('react-noop-renderer/flight-modules', () => flightModules);
    
  1454. 
    
  1455.       ReactServer = require('react');
    
  1456.       ReactNoopFlightServer = require('react-noop-renderer/flight-server');
    
  1457. 
    
  1458.       __unmockReact();
    
  1459.       jest.resetModules();
    
  1460.       jest.mock('react-noop-renderer/flight-modules', () => flightModules);
    
  1461.       React = require('react');
    
  1462.       ReactNoop = require('react-noop-renderer');
    
  1463.       ReactNoopFlightClient = require('react-noop-renderer/flight-client');
    
  1464.       act = require('internal-test-utils').act;
    
  1465.       Scheduler = require('scheduler');
    
  1466. 
    
  1467.       await act(async () => {
    
  1468.         const serverModel = await ReactNoopFlightClient.read(transport);
    
  1469.         ReactNoop.render(<ClientApp serverModel={serverModel} />);
    
  1470.       });
    
  1471. 
    
  1472.       expect(ClientContext).not.toBe(ServerContext);
    
  1473. 
    
  1474.       expect(ReactNoop).toMatchRenderedOutput(
    
  1475.         <>
    
  1476.           <article>
    
  1477.             <div>test</div>
    
  1478.             <div>test</div>
    
  1479.           </article>
    
  1480.           <article>
    
  1481.             <div>default</div>
    
  1482.             <div>default</div>
    
  1483.           </article>
    
  1484.           <div>default</div>
    
  1485.         </>,
    
  1486.       );
    
  1487.     });
    
  1488.   });
    
  1489. 
    
  1490.   // @gate enableTaint
    
  1491.   it('errors when a tainted object is serialized', async () => {
    
  1492.     function UserClient({user}) {
    
  1493.       return <span>{user.name}</span>;
    
  1494.     }
    
  1495.     const User = clientReference(UserClient);
    
  1496. 
    
  1497.     const user = {
    
  1498.       name: 'Seb',
    
  1499.       age: 'rather not say',
    
  1500.     };
    
  1501.     ReactServer.experimental_taintObjectReference(
    
  1502.       "Don't pass the raw user object to the client",
    
  1503.       user,
    
  1504.     );
    
  1505.     const errors = [];
    
  1506.     ReactNoopFlightServer.render(<User user={user} />, {
    
  1507.       onError(x) {
    
  1508.         errors.push(x.message);
    
  1509.       },
    
  1510.     });
    
  1511. 
    
  1512.     expect(errors).toEqual(["Don't pass the raw user object to the client"]);
    
  1513.   });
    
  1514. 
    
  1515.   // @gate enableTaint
    
  1516.   it('errors with a specific message when a tainted function is serialized', async () => {
    
  1517.     function UserClient({user}) {
    
  1518.       return <span>{user.name}</span>;
    
  1519.     }
    
  1520.     const User = clientReference(UserClient);
    
  1521. 
    
  1522.     function change() {}
    
  1523.     ReactServer.experimental_taintObjectReference(
    
  1524.       'A change handler cannot be passed to a client component',
    
  1525.       change,
    
  1526.     );
    
  1527.     const errors = [];
    
  1528.     ReactNoopFlightServer.render(<User onChange={change} />, {
    
  1529.       onError(x) {
    
  1530.         errors.push(x.message);
    
  1531.       },
    
  1532.     });
    
  1533. 
    
  1534.     expect(errors).toEqual([
    
  1535.       'A change handler cannot be passed to a client component',
    
  1536.     ]);
    
  1537.   });
    
  1538. 
    
  1539.   // @gate enableTaint
    
  1540.   it('errors when a tainted string is serialized', async () => {
    
  1541.     function UserClient({user}) {
    
  1542.       return <span>{user.name}</span>;
    
  1543.     }
    
  1544.     const User = clientReference(UserClient);
    
  1545. 
    
  1546.     const process = {
    
  1547.       env: {
    
  1548.         SECRET: '3e971ecc1485fe78625598bf9b6f85db',
    
  1549.       },
    
  1550.     };
    
  1551.     ReactServer.experimental_taintUniqueValue(
    
  1552.       'Cannot pass a secret token to the client',
    
  1553.       process,
    
  1554.       process.env.SECRET,
    
  1555.     );
    
  1556. 
    
  1557.     const errors = [];
    
  1558.     ReactNoopFlightServer.render(<User token={process.env.SECRET} />, {
    
  1559.       onError(x) {
    
  1560.         errors.push(x.message);
    
  1561.       },
    
  1562.     });
    
  1563. 
    
  1564.     expect(errors).toEqual(['Cannot pass a secret token to the client']);
    
  1565. 
    
  1566.     // This just ensures the process object is kept alive for the life time of
    
  1567.     // the test since we're simulating a global as an example.
    
  1568.     expect(process.env.SECRET).toBe('3e971ecc1485fe78625598bf9b6f85db');
    
  1569.   });
    
  1570. 
    
  1571.   // @gate enableTaint
    
  1572.   it('errors when a tainted bigint is serialized', async () => {
    
  1573.     function UserClient({user}) {
    
  1574.       return <span>{user.name}</span>;
    
  1575.     }
    
  1576.     const User = clientReference(UserClient);
    
  1577. 
    
  1578.     const currentUser = {
    
  1579.       name: 'Seb',
    
  1580.       token: BigInt('0x3e971ecc1485fe78625598bf9b6f85dc'),
    
  1581.     };
    
  1582.     ReactServer.experimental_taintUniqueValue(
    
  1583.       'Cannot pass a secret token to the client',
    
  1584.       currentUser,
    
  1585.       currentUser.token,
    
  1586.     );
    
  1587. 
    
  1588.     function App({user}) {
    
  1589.       return <User token={user.token} />;
    
  1590.     }
    
  1591. 
    
  1592.     const errors = [];
    
  1593.     ReactNoopFlightServer.render(<App user={currentUser} />, {
    
  1594.       onError(x) {
    
  1595.         errors.push(x.message);
    
  1596.       },
    
  1597.     });
    
  1598. 
    
  1599.     expect(errors).toEqual(['Cannot pass a secret token to the client']);
    
  1600.   });
    
  1601. 
    
  1602.   // @gate enableTaint && enableBinaryFlight
    
  1603.   it('errors when a tainted binary value is serialized', async () => {
    
  1604.     function UserClient({user}) {
    
  1605.       return <span>{user.name}</span>;
    
  1606.     }
    
  1607.     const User = clientReference(UserClient);
    
  1608. 
    
  1609.     const currentUser = {
    
  1610.       name: 'Seb',
    
  1611.       token: new Uint32Array([0x3e971ecc, 0x1485fe78, 0x625598bf, 0x9b6f85dd]),
    
  1612.     };
    
  1613.     ReactServer.experimental_taintUniqueValue(
    
  1614.       'Cannot pass a secret token to the client',
    
  1615.       currentUser,
    
  1616.       currentUser.token,
    
  1617.     );
    
  1618. 
    
  1619.     function App({user}) {
    
  1620.       const clone = user.token.slice();
    
  1621.       return <User token={clone} />;
    
  1622.     }
    
  1623. 
    
  1624.     const errors = [];
    
  1625.     ReactNoopFlightServer.render(<App user={currentUser} />, {
    
  1626.       onError(x) {
    
  1627.         errors.push(x.message);
    
  1628.       },
    
  1629.     });
    
  1630. 
    
  1631.     expect(errors).toEqual(['Cannot pass a secret token to the client']);
    
  1632.   });
    
  1633. 
    
  1634.   // @gate enableTaint
    
  1635.   it('keep a tainted value tainted until the end of any pending requests', async () => {
    
  1636.     function UserClient({user}) {
    
  1637.       return <span>{user.name}</span>;
    
  1638.     }
    
  1639.     const User = clientReference(UserClient);
    
  1640. 
    
  1641.     function getUser() {
    
  1642.       const user = {
    
  1643.         name: 'Seb',
    
  1644.         token: '3e971ecc1485fe78625598bf9b6f85db',
    
  1645.       };
    
  1646.       ReactServer.experimental_taintUniqueValue(
    
  1647.         'Cannot pass a secret token to the client',
    
  1648.         user,
    
  1649.         user.token,
    
  1650.       );
    
  1651.       return user;
    
  1652.     }
    
  1653. 
    
  1654.     function App() {
    
  1655.       const user = getUser();
    
  1656.       const derivedValue = {...user};
    
  1657.       // A garbage collection can happen at any time. Even before the end of
    
  1658.       // this request. This would clean up the user object.
    
  1659.       gc();
    
  1660.       // We should still block the tainted value.
    
  1661.       return <User user={derivedValue} />;
    
  1662.     }
    
  1663. 
    
  1664.     let errors = [];
    
  1665.     ReactNoopFlightServer.render(<App />, {
    
  1666.       onError(x) {
    
  1667.         errors.push(x.message);
    
  1668.       },
    
  1669.     });
    
  1670. 
    
  1671.     expect(errors).toEqual(['Cannot pass a secret token to the client']);
    
  1672. 
    
  1673.     // After the previous requests finishes, the token can be rendered again.
    
  1674. 
    
  1675.     errors = [];
    
  1676.     ReactNoopFlightServer.render(
    
  1677.       <User user={{token: '3e971ecc1485fe78625598bf9b6f85db'}} />,
    
  1678.       {
    
  1679.         onError(x) {
    
  1680.           errors.push(x.message);
    
  1681.         },
    
  1682.       },
    
  1683.     );
    
  1684. 
    
  1685.     expect(errors).toEqual([]);
    
  1686.   });
    
  1687. });