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. 'use strict';
    
  11. 
    
  12. let React;
    
  13. let ReactNoop;
    
  14. let waitForAll;
    
  15. 
    
  16. describe('ReactFragment', () => {
    
  17.   beforeEach(function () {
    
  18.     jest.resetModules();
    
  19. 
    
  20.     React = require('react');
    
  21.     ReactNoop = require('react-noop-renderer');
    
  22. 
    
  23.     const InternalTestUtils = require('internal-test-utils');
    
  24.     waitForAll = InternalTestUtils.waitForAll;
    
  25.   });
    
  26. 
    
  27.   it('should render a single child via noop renderer', async () => {
    
  28.     const element = (
    
  29.       <>
    
  30.         <span>foo</span>
    
  31.       </>
    
  32.     );
    
  33. 
    
  34.     ReactNoop.render(element);
    
  35.     await waitForAll([]);
    
  36. 
    
  37.     expect(ReactNoop).toMatchRenderedOutput(<span>foo</span>);
    
  38.   });
    
  39. 
    
  40.   it('should render zero children via noop renderer', async () => {
    
  41.     const element = <React.Fragment />;
    
  42. 
    
  43.     ReactNoop.render(element);
    
  44.     await waitForAll([]);
    
  45. 
    
  46.     expect(ReactNoop).toMatchRenderedOutput(null);
    
  47.   });
    
  48. 
    
  49.   it('should render multiple children via noop renderer', async () => {
    
  50.     const element = (
    
  51.       <>
    
  52.         hello <span>world</span>
    
  53.       </>
    
  54.     );
    
  55. 
    
  56.     ReactNoop.render(element);
    
  57.     await waitForAll([]);
    
  58. 
    
  59.     expect(ReactNoop).toMatchRenderedOutput(
    
  60.       <>
    
  61.         hello <span>world</span>
    
  62.       </>,
    
  63.     );
    
  64.   });
    
  65. 
    
  66.   it('should render an iterable via noop renderer', async () => {
    
  67.     const element = (
    
  68.       <>{new Set([<span key="a">hi</span>, <span key="b">bye</span>])}</>
    
  69.     );
    
  70. 
    
  71.     ReactNoop.render(element);
    
  72.     await waitForAll([]);
    
  73. 
    
  74.     expect(ReactNoop).toMatchRenderedOutput(
    
  75.       <>
    
  76.         <span>hi</span>
    
  77.         <span>bye</span>
    
  78.       </>,
    
  79.     );
    
  80.   });
    
  81. 
    
  82.   it('should preserve state of children with 1 level nesting', async function () {
    
  83.     const ops = [];
    
  84. 
    
  85.     class Stateful extends React.Component {
    
  86.       componentDidUpdate() {
    
  87.         ops.push('Update Stateful');
    
  88.       }
    
  89. 
    
  90.       render() {
    
  91.         return <div>Hello</div>;
    
  92.       }
    
  93.     }
    
  94. 
    
  95.     function Foo({condition}) {
    
  96.       return condition ? (
    
  97.         <Stateful key="a" />
    
  98.       ) : (
    
  99.         <>
    
  100.           <Stateful key="a" />
    
  101.           <div key="b">World</div>
    
  102.         </>
    
  103.       );
    
  104.     }
    
  105. 
    
  106.     ReactNoop.render(<Foo condition={true} />);
    
  107.     await waitForAll([]);
    
  108. 
    
  109.     ReactNoop.render(<Foo condition={false} />);
    
  110.     await waitForAll([]);
    
  111. 
    
  112.     expect(ops).toEqual(['Update Stateful']);
    
  113.     expect(ReactNoop).toMatchRenderedOutput(
    
  114.       <>
    
  115.         <div>Hello</div>
    
  116.         <div>World</div>
    
  117.       </>,
    
  118.     );
    
  119. 
    
  120.     ReactNoop.render(<Foo condition={true} />);
    
  121.     await waitForAll([]);
    
  122. 
    
  123.     expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
    
  124.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  125.   });
    
  126. 
    
  127.   it('should preserve state between top-level fragments', async function () {
    
  128.     const ops = [];
    
  129. 
    
  130.     class Stateful extends React.Component {
    
  131.       componentDidUpdate() {
    
  132.         ops.push('Update Stateful');
    
  133.       }
    
  134. 
    
  135.       render() {
    
  136.         return <div>Hello</div>;
    
  137.       }
    
  138.     }
    
  139. 
    
  140.     function Foo({condition}) {
    
  141.       return condition ? (
    
  142.         <>
    
  143.           <Stateful />
    
  144.         </>
    
  145.       ) : (
    
  146.         <>
    
  147.           <Stateful />
    
  148.         </>
    
  149.       );
    
  150.     }
    
  151. 
    
  152.     ReactNoop.render(<Foo condition={true} />);
    
  153.     await waitForAll([]);
    
  154. 
    
  155.     ReactNoop.render(<Foo condition={false} />);
    
  156.     await waitForAll([]);
    
  157. 
    
  158.     expect(ops).toEqual(['Update Stateful']);
    
  159.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  160. 
    
  161.     ReactNoop.render(<Foo condition={true} />);
    
  162.     await waitForAll([]);
    
  163. 
    
  164.     expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
    
  165.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  166.   });
    
  167. 
    
  168.   it('should preserve state of children nested at same level', async function () {
    
  169.     const ops = [];
    
  170. 
    
  171.     class Stateful extends React.Component {
    
  172.       componentDidUpdate() {
    
  173.         ops.push('Update Stateful');
    
  174.       }
    
  175. 
    
  176.       render() {
    
  177.         return <div>Hello</div>;
    
  178.       }
    
  179.     }
    
  180. 
    
  181.     function Foo({condition}) {
    
  182.       return condition ? (
    
  183.         <>
    
  184.           <>
    
  185.             <>
    
  186.               <Stateful key="a" />
    
  187.             </>
    
  188.           </>
    
  189.         </>
    
  190.       ) : (
    
  191.         <>
    
  192.           <>
    
  193.             <>
    
  194.               <div />
    
  195.               <Stateful key="a" />
    
  196.             </>
    
  197.           </>
    
  198.         </>
    
  199.       );
    
  200.     }
    
  201. 
    
  202.     ReactNoop.render(<Foo condition={true} />);
    
  203.     await waitForAll([]);
    
  204. 
    
  205.     ReactNoop.render(<Foo condition={false} />);
    
  206.     await waitForAll([]);
    
  207. 
    
  208.     expect(ops).toEqual(['Update Stateful']);
    
  209.     expect(ReactNoop).toMatchRenderedOutput(
    
  210.       <>
    
  211.         <div />
    
  212.         <div>Hello</div>
    
  213.       </>,
    
  214.     );
    
  215. 
    
  216.     ReactNoop.render(<Foo condition={true} />);
    
  217.     await waitForAll([]);
    
  218. 
    
  219.     expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
    
  220.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  221.   });
    
  222. 
    
  223.   it('should not preserve state in non-top-level fragment nesting', async function () {
    
  224.     const ops = [];
    
  225. 
    
  226.     class Stateful extends React.Component {
    
  227.       componentDidUpdate() {
    
  228.         ops.push('Update Stateful');
    
  229.       }
    
  230. 
    
  231.       render() {
    
  232.         return <div>Hello</div>;
    
  233.       }
    
  234.     }
    
  235. 
    
  236.     function Foo({condition}) {
    
  237.       return condition ? (
    
  238.         <>
    
  239.           <>
    
  240.             <Stateful key="a" />
    
  241.           </>
    
  242.         </>
    
  243.       ) : (
    
  244.         <>
    
  245.           <Stateful key="a" />
    
  246.         </>
    
  247.       );
    
  248.     }
    
  249. 
    
  250.     ReactNoop.render(<Foo condition={true} />);
    
  251.     await waitForAll([]);
    
  252. 
    
  253.     ReactNoop.render(<Foo condition={false} />);
    
  254.     await waitForAll([]);
    
  255. 
    
  256.     expect(ops).toEqual([]);
    
  257.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  258. 
    
  259.     ReactNoop.render(<Foo condition={true} />);
    
  260.     await waitForAll([]);
    
  261. 
    
  262.     expect(ops).toEqual([]);
    
  263.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  264.   });
    
  265. 
    
  266.   it('should not preserve state of children if nested 2 levels without siblings', async function () {
    
  267.     const ops = [];
    
  268. 
    
  269.     class Stateful extends React.Component {
    
  270.       componentDidUpdate() {
    
  271.         ops.push('Update Stateful');
    
  272.       }
    
  273. 
    
  274.       render() {
    
  275.         return <div>Hello</div>;
    
  276.       }
    
  277.     }
    
  278. 
    
  279.     function Foo({condition}) {
    
  280.       return condition ? (
    
  281.         <Stateful key="a" />
    
  282.       ) : (
    
  283.         <>
    
  284.           <>
    
  285.             <Stateful key="a" />
    
  286.           </>
    
  287.         </>
    
  288.       );
    
  289.     }
    
  290. 
    
  291.     ReactNoop.render(<Foo condition={true} />);
    
  292.     await waitForAll([]);
    
  293. 
    
  294.     ReactNoop.render(<Foo condition={false} />);
    
  295.     await waitForAll([]);
    
  296. 
    
  297.     expect(ops).toEqual([]);
    
  298.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  299. 
    
  300.     ReactNoop.render(<Foo condition={true} />);
    
  301.     await waitForAll([]);
    
  302. 
    
  303.     expect(ops).toEqual([]);
    
  304.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  305.   });
    
  306. 
    
  307.   it('should not preserve state of children if nested 2 levels with siblings', async function () {
    
  308.     const ops = [];
    
  309. 
    
  310.     class Stateful extends React.Component {
    
  311.       componentDidUpdate() {
    
  312.         ops.push('Update Stateful');
    
  313.       }
    
  314. 
    
  315.       render() {
    
  316.         return <div>Hello</div>;
    
  317.       }
    
  318.     }
    
  319. 
    
  320.     function Foo({condition}) {
    
  321.       return condition ? (
    
  322.         <Stateful key="a" />
    
  323.       ) : (
    
  324.         <>
    
  325.           <>
    
  326.             <Stateful key="a" />
    
  327.           </>
    
  328.           <div />
    
  329.         </>
    
  330.       );
    
  331.     }
    
  332. 
    
  333.     ReactNoop.render(<Foo condition={true} />);
    
  334.     await waitForAll([]);
    
  335. 
    
  336.     ReactNoop.render(<Foo condition={false} />);
    
  337.     await waitForAll([]);
    
  338. 
    
  339.     expect(ops).toEqual([]);
    
  340.     expect(ReactNoop).toMatchRenderedOutput(
    
  341.       <>
    
  342.         <div>Hello</div>
    
  343.         <div />
    
  344.       </>,
    
  345.     );
    
  346. 
    
  347.     ReactNoop.render(<Foo condition={true} />);
    
  348.     await waitForAll([]);
    
  349. 
    
  350.     expect(ops).toEqual([]);
    
  351.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  352.   });
    
  353. 
    
  354.   it('should preserve state between array nested in fragment and fragment', async function () {
    
  355.     const ops = [];
    
  356. 
    
  357.     class Stateful extends React.Component {
    
  358.       componentDidUpdate() {
    
  359.         ops.push('Update Stateful');
    
  360.       }
    
  361. 
    
  362.       render() {
    
  363.         return <div>Hello</div>;
    
  364.       }
    
  365.     }
    
  366. 
    
  367.     function Foo({condition}) {
    
  368.       return condition ? (
    
  369.         <>
    
  370.           <Stateful key="a" />
    
  371.         </>
    
  372.       ) : (
    
  373.         <>{[<Stateful key="a" />]}</>
    
  374.       );
    
  375.     }
    
  376. 
    
  377.     ReactNoop.render(<Foo condition={true} />);
    
  378.     await waitForAll([]);
    
  379. 
    
  380.     ReactNoop.render(<Foo condition={false} />);
    
  381.     await waitForAll([]);
    
  382. 
    
  383.     expect(ops).toEqual(['Update Stateful']);
    
  384.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  385. 
    
  386.     ReactNoop.render(<Foo condition={true} />);
    
  387.     await waitForAll([]);
    
  388. 
    
  389.     expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
    
  390.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  391.   });
    
  392. 
    
  393.   it('should preserve state between top level fragment and array', async function () {
    
  394.     const ops = [];
    
  395. 
    
  396.     class Stateful extends React.Component {
    
  397.       componentDidUpdate() {
    
  398.         ops.push('Update Stateful');
    
  399.       }
    
  400. 
    
  401.       render() {
    
  402.         return <div>Hello</div>;
    
  403.       }
    
  404.     }
    
  405. 
    
  406.     function Foo({condition}) {
    
  407.       return condition ? (
    
  408.         [<Stateful key="a" />]
    
  409.       ) : (
    
  410.         <>
    
  411.           <Stateful key="a" />
    
  412.         </>
    
  413.       );
    
  414.     }
    
  415. 
    
  416.     ReactNoop.render(<Foo condition={true} />);
    
  417.     await waitForAll([]);
    
  418. 
    
  419.     ReactNoop.render(<Foo condition={false} />);
    
  420.     await waitForAll([]);
    
  421. 
    
  422.     expect(ops).toEqual(['Update Stateful']);
    
  423.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  424. 
    
  425.     ReactNoop.render(<Foo condition={true} />);
    
  426.     await waitForAll([]);
    
  427. 
    
  428.     expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
    
  429.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  430.   });
    
  431. 
    
  432.   it('should not preserve state between array nested in fragment and double nested fragment', async function () {
    
  433.     const ops = [];
    
  434. 
    
  435.     class Stateful extends React.Component {
    
  436.       componentDidUpdate() {
    
  437.         ops.push('Update Stateful');
    
  438.       }
    
  439. 
    
  440.       render() {
    
  441.         return <div>Hello</div>;
    
  442.       }
    
  443.     }
    
  444. 
    
  445.     function Foo({condition}) {
    
  446.       return condition ? (
    
  447.         <>{[<Stateful key="a" />]}</>
    
  448.       ) : (
    
  449.         <>
    
  450.           <>
    
  451.             <Stateful key="a" />
    
  452.           </>
    
  453.         </>
    
  454.       );
    
  455.     }
    
  456. 
    
  457.     ReactNoop.render(<Foo condition={true} />);
    
  458.     await waitForAll([]);
    
  459. 
    
  460.     ReactNoop.render(<Foo condition={false} />);
    
  461.     await waitForAll([]);
    
  462. 
    
  463.     expect(ops).toEqual([]);
    
  464.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  465. 
    
  466.     ReactNoop.render(<Foo condition={true} />);
    
  467.     await waitForAll([]);
    
  468. 
    
  469.     expect(ops).toEqual([]);
    
  470.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  471.   });
    
  472. 
    
  473.   it('should not preserve state between array nested in fragment and double nested array', async function () {
    
  474.     const ops = [];
    
  475. 
    
  476.     class Stateful extends React.Component {
    
  477.       componentDidUpdate() {
    
  478.         ops.push('Update Stateful');
    
  479.       }
    
  480. 
    
  481.       render() {
    
  482.         return <div>Hello</div>;
    
  483.       }
    
  484.     }
    
  485. 
    
  486.     function Foo({condition}) {
    
  487.       return condition ? (
    
  488.         <>{[<Stateful key="a" />]}</>
    
  489.       ) : (
    
  490.         [[<Stateful key="a" />]]
    
  491.       );
    
  492.     }
    
  493. 
    
  494.     ReactNoop.render(<Foo condition={true} />);
    
  495.     await waitForAll([]);
    
  496. 
    
  497.     ReactNoop.render(<Foo condition={false} />);
    
  498.     await waitForAll([]);
    
  499. 
    
  500.     expect(ops).toEqual([]);
    
  501.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  502. 
    
  503.     ReactNoop.render(<Foo condition={true} />);
    
  504.     await waitForAll([]);
    
  505. 
    
  506.     expect(ops).toEqual([]);
    
  507.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  508.   });
    
  509. 
    
  510.   it('should preserve state between double nested fragment and double nested array', async function () {
    
  511.     const ops = [];
    
  512. 
    
  513.     class Stateful extends React.Component {
    
  514.       componentDidUpdate() {
    
  515.         ops.push('Update Stateful');
    
  516.       }
    
  517. 
    
  518.       render() {
    
  519.         return <div>Hello</div>;
    
  520.       }
    
  521.     }
    
  522. 
    
  523.     function Foo({condition}) {
    
  524.       return condition ? (
    
  525.         <>
    
  526.           <>
    
  527.             <Stateful key="a" />
    
  528.           </>
    
  529.         </>
    
  530.       ) : (
    
  531.         [[<Stateful key="a" />]]
    
  532.       );
    
  533.     }
    
  534. 
    
  535.     ReactNoop.render(<Foo condition={true} />);
    
  536.     await waitForAll([]);
    
  537. 
    
  538.     ReactNoop.render(<Foo condition={false} />);
    
  539.     await waitForAll([]);
    
  540. 
    
  541.     expect(ops).toEqual(['Update Stateful']);
    
  542.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  543. 
    
  544.     ReactNoop.render(<Foo condition={true} />);
    
  545.     await waitForAll([]);
    
  546. 
    
  547.     expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
    
  548.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  549.   });
    
  550. 
    
  551.   it('should not preserve state of children when the keys are different', async function () {
    
  552.     const ops = [];
    
  553. 
    
  554.     class Stateful extends React.Component {
    
  555.       componentDidUpdate() {
    
  556.         ops.push('Update Stateful');
    
  557.       }
    
  558. 
    
  559.       render() {
    
  560.         return <div>Hello</div>;
    
  561.       }
    
  562.     }
    
  563. 
    
  564.     function Foo({condition}) {
    
  565.       return condition ? (
    
  566.         <React.Fragment key="a">
    
  567.           <Stateful />
    
  568.         </React.Fragment>
    
  569.       ) : (
    
  570.         <React.Fragment key="b">
    
  571.           <Stateful />
    
  572.           <span>World</span>
    
  573.         </React.Fragment>
    
  574.       );
    
  575.     }
    
  576. 
    
  577.     ReactNoop.render(<Foo condition={true} />);
    
  578.     await waitForAll([]);
    
  579. 
    
  580.     ReactNoop.render(<Foo condition={false} />);
    
  581.     await waitForAll([]);
    
  582. 
    
  583.     expect(ops).toEqual([]);
    
  584.     expect(ReactNoop).toMatchRenderedOutput(
    
  585.       <>
    
  586.         <div>Hello</div>
    
  587.         <span>World</span>
    
  588.       </>,
    
  589.     );
    
  590. 
    
  591.     ReactNoop.render(<Foo condition={true} />);
    
  592.     await waitForAll([]);
    
  593. 
    
  594.     expect(ops).toEqual([]);
    
  595.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  596.   });
    
  597. 
    
  598.   it('should not preserve state between unkeyed and keyed fragment', async function () {
    
  599.     const ops = [];
    
  600. 
    
  601.     class Stateful extends React.Component {
    
  602.       componentDidUpdate() {
    
  603.         ops.push('Update Stateful');
    
  604.       }
    
  605. 
    
  606.       render() {
    
  607.         return <div>Hello</div>;
    
  608.       }
    
  609.     }
    
  610. 
    
  611.     function Foo({condition}) {
    
  612.       return condition ? (
    
  613.         <React.Fragment key="a">
    
  614.           <Stateful />
    
  615.         </React.Fragment>
    
  616.       ) : (
    
  617.         <>
    
  618.           <Stateful />
    
  619.         </>
    
  620.       );
    
  621.     }
    
  622. 
    
  623.     ReactNoop.render(<Foo condition={true} />);
    
  624.     await waitForAll([]);
    
  625. 
    
  626.     ReactNoop.render(<Foo condition={false} />);
    
  627.     await waitForAll([]);
    
  628. 
    
  629.     expect(ops).toEqual([]);
    
  630.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  631. 
    
  632.     ReactNoop.render(<Foo condition={true} />);
    
  633.     await waitForAll([]);
    
  634. 
    
  635.     expect(ops).toEqual([]);
    
  636.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  637.   });
    
  638. 
    
  639.   it('should preserve state with reordering in multiple levels', async function () {
    
  640.     const ops = [];
    
  641. 
    
  642.     class Stateful extends React.Component {
    
  643.       componentDidUpdate() {
    
  644.         ops.push('Update Stateful');
    
  645.       }
    
  646. 
    
  647.       render() {
    
  648.         return <div>Hello</div>;
    
  649.       }
    
  650.     }
    
  651. 
    
  652.     function Foo({condition}) {
    
  653.       return condition ? (
    
  654.         <div>
    
  655.           <React.Fragment key="c">
    
  656.             <span>foo</span>
    
  657.             <div key="b">
    
  658.               <Stateful key="a" />
    
  659.             </div>
    
  660.           </React.Fragment>
    
  661.           <span>boop</span>
    
  662.         </div>
    
  663.       ) : (
    
  664.         <div>
    
  665.           <span>beep</span>
    
  666.           <React.Fragment key="c">
    
  667.             <div key="b">
    
  668.               <Stateful key="a" />
    
  669.             </div>
    
  670.             <span>bar</span>
    
  671.           </React.Fragment>
    
  672.         </div>
    
  673.       );
    
  674.     }
    
  675. 
    
  676.     ReactNoop.render(<Foo condition={true} />);
    
  677.     await waitForAll([]);
    
  678. 
    
  679.     ReactNoop.render(<Foo condition={false} />);
    
  680.     await waitForAll([]);
    
  681. 
    
  682.     expect(ops).toEqual(['Update Stateful']);
    
  683.     expect(ReactNoop).toMatchRenderedOutput(
    
  684.       <div>
    
  685.         <span>beep</span>
    
  686.         <div>
    
  687.           <div>Hello</div>
    
  688.         </div>
    
  689.         <span>bar</span>
    
  690.       </div>,
    
  691.     );
    
  692. 
    
  693.     ReactNoop.render(<Foo condition={true} />);
    
  694.     await waitForAll([]);
    
  695. 
    
  696.     expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
    
  697.     expect(ReactNoop).toMatchRenderedOutput(
    
  698.       <div>
    
  699.         <span>foo</span>
    
  700.         <div>
    
  701.           <div>Hello</div>
    
  702.         </div>
    
  703.         <span>boop</span>
    
  704.       </div>,
    
  705.     );
    
  706.   });
    
  707. 
    
  708.   it('should not preserve state when switching to a keyed fragment to an array', async () => {
    
  709.     const ops = [];
    
  710. 
    
  711.     class Stateful extends React.Component {
    
  712.       componentDidUpdate() {
    
  713.         ops.push('Update Stateful');
    
  714.       }
    
  715. 
    
  716.       render() {
    
  717.         return <div>Hello</div>;
    
  718.       }
    
  719.     }
    
  720. 
    
  721.     function Foo({condition}) {
    
  722.       return condition ? (
    
  723.         <div>
    
  724.           {
    
  725.             <React.Fragment key="foo">
    
  726.               <Stateful />
    
  727.             </React.Fragment>
    
  728.           }
    
  729.           <span />
    
  730.         </div>
    
  731.       ) : (
    
  732.         <div>
    
  733.           {[<Stateful />]}
    
  734.           <span />
    
  735.         </div>
    
  736.       );
    
  737.     }
    
  738. 
    
  739.     ReactNoop.render(<Foo condition={true} />);
    
  740.     await waitForAll([]);
    
  741. 
    
  742.     ReactNoop.render(<Foo condition={false} />);
    
  743.     await expect(async () => await waitForAll([])).toErrorDev(
    
  744.       'Each child in a list should have a unique "key" prop.',
    
  745.     );
    
  746. 
    
  747.     expect(ops).toEqual([]);
    
  748.     expect(ReactNoop).toMatchRenderedOutput(
    
  749.       <div>
    
  750.         <div>Hello</div>
    
  751.         <span />
    
  752.       </div>,
    
  753.     );
    
  754. 
    
  755.     ReactNoop.render(<Foo condition={true} />);
    
  756.     await waitForAll([]);
    
  757. 
    
  758.     expect(ops).toEqual([]);
    
  759.     expect(ReactNoop).toMatchRenderedOutput(
    
  760.       <div>
    
  761.         <div>Hello</div>
    
  762.         <span />
    
  763.       </div>,
    
  764.     );
    
  765.   });
    
  766. 
    
  767.   it('should not preserve state when switching a nested unkeyed fragment to a passthrough component', async function () {
    
  768.     const ops = [];
    
  769. 
    
  770.     function Passthrough({children}) {
    
  771.       return children;
    
  772.     }
    
  773. 
    
  774.     class Stateful extends React.Component {
    
  775.       componentDidUpdate() {
    
  776.         ops.push('Update Stateful');
    
  777.       }
    
  778. 
    
  779.       render() {
    
  780.         return <div>Hello</div>;
    
  781.       }
    
  782.     }
    
  783. 
    
  784.     function Foo({condition}) {
    
  785.       return condition ? (
    
  786.         <>
    
  787.           <>
    
  788.             <Stateful />
    
  789.           </>
    
  790.         </>
    
  791.       ) : (
    
  792.         <>
    
  793.           <Passthrough>
    
  794.             <Stateful />
    
  795.           </Passthrough>
    
  796.         </>
    
  797.       );
    
  798.     }
    
  799. 
    
  800.     ReactNoop.render(<Foo condition={true} />);
    
  801.     await waitForAll([]);
    
  802. 
    
  803.     ReactNoop.render(<Foo condition={false} />);
    
  804.     await waitForAll([]);
    
  805. 
    
  806.     expect(ops).toEqual([]);
    
  807.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  808. 
    
  809.     ReactNoop.render(<Foo condition={true} />);
    
  810.     await waitForAll([]);
    
  811. 
    
  812.     expect(ops).toEqual([]);
    
  813.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  814.   });
    
  815. 
    
  816.   it('should not preserve state when switching a nested keyed fragment to a passthrough component', async function () {
    
  817.     const ops = [];
    
  818. 
    
  819.     function Passthrough({children}) {
    
  820.       return children;
    
  821.     }
    
  822. 
    
  823.     class Stateful extends React.Component {
    
  824.       componentDidUpdate() {
    
  825.         ops.push('Update Stateful');
    
  826.       }
    
  827. 
    
  828.       render() {
    
  829.         return <div>Hello</div>;
    
  830.       }
    
  831.     }
    
  832. 
    
  833.     function Foo({condition}) {
    
  834.       return condition ? (
    
  835.         <>
    
  836.           <React.Fragment key="a">
    
  837.             <Stateful />
    
  838.           </React.Fragment>
    
  839.         </>
    
  840.       ) : (
    
  841.         <>
    
  842.           <Passthrough>
    
  843.             <Stateful />
    
  844.           </Passthrough>
    
  845.         </>
    
  846.       );
    
  847.     }
    
  848. 
    
  849.     ReactNoop.render(<Foo condition={true} />);
    
  850.     await waitForAll([]);
    
  851. 
    
  852.     ReactNoop.render(<Foo condition={false} />);
    
  853.     await waitForAll([]);
    
  854. 
    
  855.     expect(ops).toEqual([]);
    
  856.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  857. 
    
  858.     ReactNoop.render(<Foo condition={true} />);
    
  859.     await waitForAll([]);
    
  860. 
    
  861.     expect(ops).toEqual([]);
    
  862.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  863.   });
    
  864. 
    
  865.   it('should not preserve state when switching a nested keyed array to a passthrough component', async function () {
    
  866.     const ops = [];
    
  867. 
    
  868.     function Passthrough({children}) {
    
  869.       return children;
    
  870.     }
    
  871. 
    
  872.     class Stateful extends React.Component {
    
  873.       componentDidUpdate() {
    
  874.         ops.push('Update Stateful');
    
  875.       }
    
  876. 
    
  877.       render() {
    
  878.         return <div>Hello</div>;
    
  879.       }
    
  880.     }
    
  881. 
    
  882.     function Foo({condition}) {
    
  883.       return condition ? (
    
  884.         <>{[<Stateful key="a" />]}</>
    
  885.       ) : (
    
  886.         <>
    
  887.           <Passthrough>
    
  888.             <Stateful />
    
  889.           </Passthrough>
    
  890.         </>
    
  891.       );
    
  892.     }
    
  893. 
    
  894.     ReactNoop.render(<Foo condition={true} />);
    
  895.     await waitForAll([]);
    
  896. 
    
  897.     ReactNoop.render(<Foo condition={false} />);
    
  898.     await waitForAll([]);
    
  899. 
    
  900.     expect(ops).toEqual([]);
    
  901.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  902. 
    
  903.     ReactNoop.render(<Foo condition={true} />);
    
  904.     await waitForAll([]);
    
  905. 
    
  906.     expect(ops).toEqual([]);
    
  907.     expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
    
  908.   });
    
  909. 
    
  910.   it('should preserve state when it does not change positions', async function () {
    
  911.     const ops = [];
    
  912. 
    
  913.     class Stateful extends React.Component {
    
  914.       componentDidUpdate() {
    
  915.         ops.push('Update Stateful');
    
  916.       }
    
  917. 
    
  918.       render() {
    
  919.         return <div>Hello</div>;
    
  920.       }
    
  921.     }
    
  922. 
    
  923.     function Foo({condition}) {
    
  924.       return condition
    
  925.         ? [
    
  926.             <span />,
    
  927.             <>
    
  928.               <Stateful />
    
  929.             </>,
    
  930.           ]
    
  931.         : [
    
  932.             <span />,
    
  933.             <>
    
  934.               <Stateful />
    
  935.             </>,
    
  936.           ];
    
  937.     }
    
  938. 
    
  939.     ReactNoop.render(<Foo condition={true} />);
    
  940.     await expect(async () => await waitForAll([])).toErrorDev(
    
  941.       'Each child in a list should have a unique "key" prop.',
    
  942.     );
    
  943. 
    
  944.     ReactNoop.render(<Foo condition={false} />);
    
  945.     // The key warning gets deduped because it's in the same component.
    
  946.     await waitForAll([]);
    
  947. 
    
  948.     expect(ops).toEqual(['Update Stateful']);
    
  949.     expect(ReactNoop).toMatchRenderedOutput(
    
  950.       <>
    
  951.         <span />
    
  952.         <div>Hello</div>
    
  953.       </>,
    
  954.     );
    
  955. 
    
  956.     ReactNoop.render(<Foo condition={true} />);
    
  957.     // The key warning gets deduped because it's in the same component.
    
  958.     await waitForAll([]);
    
  959. 
    
  960.     expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
    
  961.     expect(ReactNoop).toMatchRenderedOutput(
    
  962.       <>
    
  963.         <span />
    
  964.         <div>Hello</div>
    
  965.       </>,
    
  966.     );
    
  967.   });
    
  968. });