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 ReactFeatureFlags = require('shared/ReactFeatureFlags');
    
  14. ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
    
  15. const React = require('react');
    
  16. const ReactTestRenderer = require('react-test-renderer');
    
  17. const {format: prettyFormat} = require('pretty-format');
    
  18. 
    
  19. // Isolate noop renderer
    
  20. jest.resetModules();
    
  21. const ReactNoop = require('react-noop-renderer');
    
  22. 
    
  23. const InternalTestUtils = require('internal-test-utils');
    
  24. const waitForAll = InternalTestUtils.waitForAll;
    
  25. 
    
  26. // Kind of hacky, but we nullify all the instances to test the tree structure
    
  27. // with jasmine's deep equality function, and test the instances separate. We
    
  28. // also delete children props because testing them is more annoying and not
    
  29. // really important to verify.
    
  30. function cleanNodeOrArray(node) {
    
  31.   if (!node) {
    
  32.     return;
    
  33.   }
    
  34.   if (Array.isArray(node)) {
    
  35.     node.forEach(cleanNodeOrArray);
    
  36.     return;
    
  37.   }
    
  38.   if (node && node.instance) {
    
  39.     node.instance = null;
    
  40.   }
    
  41.   if (node && node.props && node.props.children) {
    
  42.     // eslint-disable-next-line no-unused-vars
    
  43.     const {children, ...props} = node.props;
    
  44.     node.props = props;
    
  45.   }
    
  46.   if (Array.isArray(node.rendered)) {
    
  47.     node.rendered.forEach(cleanNodeOrArray);
    
  48.   } else if (typeof node.rendered === 'object') {
    
  49.     cleanNodeOrArray(node.rendered);
    
  50.   }
    
  51. }
    
  52. 
    
  53. describe('ReactTestRenderer', () => {
    
  54.   it('renders a simple component', () => {
    
  55.     function Link() {
    
  56.       return <a role="link" />;
    
  57.     }
    
  58.     const renderer = ReactTestRenderer.create(<Link />);
    
  59.     expect(renderer.toJSON()).toEqual({
    
  60.       type: 'a',
    
  61.       props: {role: 'link'},
    
  62.       children: null,
    
  63.     });
    
  64.   });
    
  65. 
    
  66.   it('renders a top-level empty component', () => {
    
  67.     function Empty() {
    
  68.       return null;
    
  69.     }
    
  70.     const renderer = ReactTestRenderer.create(<Empty />);
    
  71.     expect(renderer.toJSON()).toEqual(null);
    
  72.   });
    
  73. 
    
  74.   it('exposes a type flag', () => {
    
  75.     function Link() {
    
  76.       return <a role="link" />;
    
  77.     }
    
  78.     const renderer = ReactTestRenderer.create(<Link />);
    
  79.     const object = renderer.toJSON();
    
  80.     expect(object.$$typeof).toBe(Symbol.for('react.test.json'));
    
  81. 
    
  82.     // $$typeof should not be enumerable.
    
  83.     for (const key in object) {
    
  84.       if (object.hasOwnProperty(key)) {
    
  85.         expect(key).not.toBe('$$typeof');
    
  86.       }
    
  87.     }
    
  88.   });
    
  89. 
    
  90.   it('can render a composite component', () => {
    
  91.     class Component extends React.Component {
    
  92.       render() {
    
  93.         return (
    
  94.           <div className="purple">
    
  95.             <Child />
    
  96.           </div>
    
  97.         );
    
  98.       }
    
  99.     }
    
  100. 
    
  101.     const Child = () => {
    
  102.       return <moo />;
    
  103.     };
    
  104. 
    
  105.     const renderer = ReactTestRenderer.create(<Component />);
    
  106.     expect(renderer.toJSON()).toEqual({
    
  107.       type: 'div',
    
  108.       props: {className: 'purple'},
    
  109.       children: [{type: 'moo', props: {}, children: null}],
    
  110.     });
    
  111.   });
    
  112. 
    
  113.   it('renders some basics with an update', () => {
    
  114.     let renders = 0;
    
  115. 
    
  116.     class Component extends React.Component {
    
  117.       state = {x: 3};
    
  118. 
    
  119.       render() {
    
  120.         renders++;
    
  121.         return (
    
  122.           <div className="purple">
    
  123.             {this.state.x}
    
  124.             <Child />
    
  125.             <Null />
    
  126.           </div>
    
  127.         );
    
  128.       }
    
  129. 
    
  130.       componentDidMount() {
    
  131.         this.setState({x: 7});
    
  132.       }
    
  133.     }
    
  134. 
    
  135.     const Child = () => {
    
  136.       renders++;
    
  137.       return <moo />;
    
  138.     };
    
  139. 
    
  140.     const Null = () => {
    
  141.       renders++;
    
  142.       return null;
    
  143.     };
    
  144. 
    
  145.     const renderer = ReactTestRenderer.create(<Component />);
    
  146.     expect(renderer.toJSON()).toEqual({
    
  147.       type: 'div',
    
  148.       props: {className: 'purple'},
    
  149.       children: ['7', {type: 'moo', props: {}, children: null}],
    
  150.     });
    
  151.     expect(renders).toBe(6);
    
  152.   });
    
  153. 
    
  154.   it('exposes the instance', () => {
    
  155.     class Mouse extends React.Component {
    
  156.       constructor() {
    
  157.         super();
    
  158.         this.state = {mouse: 'mouse'};
    
  159.       }
    
  160.       handleMoose() {
    
  161.         this.setState({mouse: 'moose'});
    
  162.       }
    
  163.       render() {
    
  164.         return <div>{this.state.mouse}</div>;
    
  165.       }
    
  166.     }
    
  167.     const renderer = ReactTestRenderer.create(<Mouse />);
    
  168. 
    
  169.     expect(renderer.toJSON()).toEqual({
    
  170.       type: 'div',
    
  171.       props: {},
    
  172.       children: ['mouse'],
    
  173.     });
    
  174. 
    
  175.     const mouse = renderer.getInstance();
    
  176.     mouse.handleMoose();
    
  177.     expect(renderer.toJSON()).toEqual({
    
  178.       type: 'div',
    
  179.       children: ['moose'],
    
  180.       props: {},
    
  181.     });
    
  182.   });
    
  183. 
    
  184.   it('updates types', () => {
    
  185.     const renderer = ReactTestRenderer.create(<div>mouse</div>);
    
  186.     expect(renderer.toJSON()).toEqual({
    
  187.       type: 'div',
    
  188.       props: {},
    
  189.       children: ['mouse'],
    
  190.     });
    
  191. 
    
  192.     renderer.update(<span>mice</span>);
    
  193.     expect(renderer.toJSON()).toEqual({
    
  194.       type: 'span',
    
  195.       props: {},
    
  196.       children: ['mice'],
    
  197.     });
    
  198.   });
    
  199. 
    
  200.   it('updates children', () => {
    
  201.     const renderer = ReactTestRenderer.create(
    
  202.       <div>
    
  203.         <span key="a">A</span>
    
  204.         <span key="b">B</span>
    
  205.         <span key="c">C</span>
    
  206.       </div>,
    
  207.     );
    
  208.     expect(renderer.toJSON()).toEqual({
    
  209.       type: 'div',
    
  210.       props: {},
    
  211.       children: [
    
  212.         {type: 'span', props: {}, children: ['A']},
    
  213.         {type: 'span', props: {}, children: ['B']},
    
  214.         {type: 'span', props: {}, children: ['C']},
    
  215.       ],
    
  216.     });
    
  217. 
    
  218.     renderer.update(
    
  219.       <div>
    
  220.         <span key="d">D</span>
    
  221.         <span key="c">C</span>
    
  222.         <span key="b">B</span>
    
  223.       </div>,
    
  224.     );
    
  225.     expect(renderer.toJSON()).toEqual({
    
  226.       type: 'div',
    
  227.       props: {},
    
  228.       children: [
    
  229.         {type: 'span', props: {}, children: ['D']},
    
  230.         {type: 'span', props: {}, children: ['C']},
    
  231.         {type: 'span', props: {}, children: ['B']},
    
  232.       ],
    
  233.     });
    
  234.   });
    
  235. 
    
  236.   it('does the full lifecycle', () => {
    
  237.     const log = [];
    
  238.     class Log extends React.Component {
    
  239.       render() {
    
  240.         log.push('render ' + this.props.name);
    
  241.         return <div />;
    
  242.       }
    
  243.       componentDidMount() {
    
  244.         log.push('mount ' + this.props.name);
    
  245.       }
    
  246.       componentWillUnmount() {
    
  247.         log.push('unmount ' + this.props.name);
    
  248.       }
    
  249.     }
    
  250. 
    
  251.     const renderer = ReactTestRenderer.create(<Log key="foo" name="Foo" />);
    
  252.     renderer.update(<Log key="bar" name="Bar" />);
    
  253.     renderer.unmount();
    
  254. 
    
  255.     expect(log).toEqual([
    
  256.       'render Foo',
    
  257.       'mount Foo',
    
  258.       'render Bar',
    
  259.       'unmount Foo',
    
  260.       'mount Bar',
    
  261.       'unmount Bar',
    
  262.     ]);
    
  263.   });
    
  264. 
    
  265.   it('gives a ref to native components', () => {
    
  266.     const log = [];
    
  267.     ReactTestRenderer.create(<div ref={r => log.push(r)} />);
    
  268.     expect(log).toEqual([null]);
    
  269.   });
    
  270. 
    
  271.   it('warns correctly for refs on SFCs', () => {
    
  272.     function Bar() {
    
  273.       return <div>Hello, world</div>;
    
  274.     }
    
  275.     class Foo extends React.Component {
    
  276.       fooRef = React.createRef();
    
  277.       render() {
    
  278.         return <Bar ref={this.fooRef} />;
    
  279.       }
    
  280.     }
    
  281.     class Baz extends React.Component {
    
  282.       bazRef = React.createRef();
    
  283.       render() {
    
  284.         return <div ref={this.bazRef} />;
    
  285.       }
    
  286.     }
    
  287.     ReactTestRenderer.create(<Baz />);
    
  288.     expect(() => ReactTestRenderer.create(<Foo />)).toErrorDev(
    
  289.       'Warning: Function components cannot be given refs. Attempts ' +
    
  290.         'to access this ref will fail. ' +
    
  291.         'Did you mean to use React.forwardRef()?\n\n' +
    
  292.         'Check the render method of `Foo`.\n' +
    
  293.         '    in Bar (at **)\n' +
    
  294.         '    in Foo (at **)',
    
  295.     );
    
  296.   });
    
  297. 
    
  298.   it('allows an optional createNodeMock function', () => {
    
  299.     const mockDivInstance = {appendChild: () => {}};
    
  300.     const mockInputInstance = {focus: () => {}};
    
  301.     const mockListItemInstance = {click: () => {}};
    
  302.     const mockAnchorInstance = {hover: () => {}};
    
  303.     const log = [];
    
  304.     class Foo extends React.Component {
    
  305.       barRef = React.createRef();
    
  306.       componentDidMount() {
    
  307.         log.push(this.barRef.current);
    
  308.       }
    
  309.       render() {
    
  310.         return <a ref={this.barRef}>Hello, world</a>;
    
  311.       }
    
  312.     }
    
  313.     function createNodeMock(element) {
    
  314.       switch (element.type) {
    
  315.         case 'div':
    
  316.           return mockDivInstance;
    
  317.         case 'input':
    
  318.           return mockInputInstance;
    
  319.         case 'li':
    
  320.           return mockListItemInstance;
    
  321.         case 'a':
    
  322.           return mockAnchorInstance;
    
  323.         default:
    
  324.           return {};
    
  325.       }
    
  326.     }
    
  327.     ReactTestRenderer.create(<div ref={r => log.push(r)} />, {createNodeMock});
    
  328.     ReactTestRenderer.create(<input ref={r => log.push(r)} />, {
    
  329.       createNodeMock,
    
  330.     });
    
  331.     ReactTestRenderer.create(
    
  332.       <div>
    
  333.         <span>
    
  334.           <ul>
    
  335.             <li ref={r => log.push(r)} />
    
  336.           </ul>
    
  337.           <ul>
    
  338.             <li ref={r => log.push(r)} />
    
  339.             <li ref={r => log.push(r)} />
    
  340.           </ul>
    
  341.         </span>
    
  342.       </div>,
    
  343.       {createNodeMock, foobar: true},
    
  344.     );
    
  345.     ReactTestRenderer.create(<Foo />, {createNodeMock});
    
  346.     ReactTestRenderer.create(<div ref={r => log.push(r)} />);
    
  347.     ReactTestRenderer.create(<div ref={r => log.push(r)} />, {});
    
  348.     expect(log).toEqual([
    
  349.       mockDivInstance,
    
  350.       mockInputInstance,
    
  351.       mockListItemInstance,
    
  352.       mockListItemInstance,
    
  353.       mockListItemInstance,
    
  354.       mockAnchorInstance,
    
  355.       null,
    
  356.       null,
    
  357.     ]);
    
  358.   });
    
  359. 
    
  360.   it('supports unmounting when using refs', () => {
    
  361.     class Foo extends React.Component {
    
  362.       render() {
    
  363.         return <div ref={React.createRef()} />;
    
  364.       }
    
  365.     }
    
  366.     const inst = ReactTestRenderer.create(<Foo />, {
    
  367.       createNodeMock: () => 'foo',
    
  368.     });
    
  369.     expect(() => inst.unmount()).not.toThrow();
    
  370.   });
    
  371. 
    
  372.   it('supports unmounting inner instances', () => {
    
  373.     let count = 0;
    
  374.     class Foo extends React.Component {
    
  375.       componentWillUnmount() {
    
  376.         count++;
    
  377.       }
    
  378.       render() {
    
  379.         return <div />;
    
  380.       }
    
  381.     }
    
  382.     const inst = ReactTestRenderer.create(
    
  383.       <div>
    
  384.         <Foo />
    
  385.       </div>,
    
  386.       {
    
  387.         createNodeMock: () => 'foo',
    
  388.       },
    
  389.     );
    
  390.     expect(() => inst.unmount()).not.toThrow();
    
  391.     expect(count).toEqual(1);
    
  392.   });
    
  393. 
    
  394.   it('supports updates when using refs', () => {
    
  395.     const log = [];
    
  396.     const createNodeMock = element => {
    
  397.       log.push(element.type);
    
  398.       return element.type;
    
  399.     };
    
  400.     class Foo extends React.Component {
    
  401.       render() {
    
  402.         return this.props.useDiv ? (
    
  403.           <div ref={React.createRef()} />
    
  404.         ) : (
    
  405.           <span ref={React.createRef()} />
    
  406.         );
    
  407.       }
    
  408.     }
    
  409.     const inst = ReactTestRenderer.create(<Foo useDiv={true} />, {
    
  410.       createNodeMock,
    
  411.     });
    
  412.     inst.update(<Foo useDiv={false} />);
    
  413.     expect(log).toEqual(['div', 'span']);
    
  414.   });
    
  415. 
    
  416.   it('supports error boundaries', () => {
    
  417.     const log = [];
    
  418.     class Angry extends React.Component {
    
  419.       render() {
    
  420.         log.push('Angry render');
    
  421.         throw new Error('Please, do not render me.');
    
  422.       }
    
  423. 
    
  424.       componentDidMount() {
    
  425.         log.push('Angry componentDidMount');
    
  426.       }
    
  427.       componentWillUnmount() {
    
  428.         log.push('Angry componentWillUnmount');
    
  429.       }
    
  430.     }
    
  431. 
    
  432.     class Boundary extends React.Component {
    
  433.       constructor(props) {
    
  434.         super(props);
    
  435.         this.state = {error: false};
    
  436.       }
    
  437.       render() {
    
  438.         log.push('Boundary render');
    
  439.         if (!this.state.error) {
    
  440.           return (
    
  441.             <div>
    
  442.               <button onClick={this.onClick}>ClickMe</button>
    
  443.               <Angry />
    
  444.             </div>
    
  445.           );
    
  446.         } else {
    
  447.           return <div>Happy Birthday!</div>;
    
  448.         }
    
  449.       }
    
  450.       componentDidMount() {
    
  451.         log.push('Boundary componentDidMount');
    
  452.       }
    
  453.       componentWillUnmount() {
    
  454.         log.push('Boundary componentWillUnmount');
    
  455.       }
    
  456.       onClick() {
    
  457.         /* do nothing */
    
  458.       }
    
  459.       componentDidCatch() {
    
  460.         log.push('Boundary componentDidCatch');
    
  461.         this.setState({error: true});
    
  462.       }
    
  463.     }
    
  464. 
    
  465.     const renderer = ReactTestRenderer.create(<Boundary />);
    
  466.     expect(renderer.toJSON()).toEqual({
    
  467.       type: 'div',
    
  468.       props: {},
    
  469.       children: ['Happy Birthday!'],
    
  470.     });
    
  471.     expect(log).toEqual([
    
  472.       'Boundary render',
    
  473.       'Angry render',
    
  474.       'Boundary componentDidMount',
    
  475.       'Boundary componentDidCatch',
    
  476.       'Boundary render',
    
  477.     ]);
    
  478.   });
    
  479. 
    
  480.   it('can update text nodes', () => {
    
  481.     class Component extends React.Component {
    
  482.       render() {
    
  483.         return <div>{this.props.children}</div>;
    
  484.       }
    
  485.     }
    
  486. 
    
  487.     const renderer = ReactTestRenderer.create(<Component>Hi</Component>);
    
  488.     expect(renderer.toJSON()).toEqual({
    
  489.       type: 'div',
    
  490.       children: ['Hi'],
    
  491.       props: {},
    
  492.     });
    
  493.     renderer.update(<Component>{['Hi', 'Bye']}</Component>);
    
  494.     expect(renderer.toJSON()).toEqual({
    
  495.       type: 'div',
    
  496.       children: ['Hi', 'Bye'],
    
  497.       props: {},
    
  498.     });
    
  499.     renderer.update(<Component>Bye</Component>);
    
  500.     expect(renderer.toJSON()).toEqual({
    
  501.       type: 'div',
    
  502.       children: ['Bye'],
    
  503.       props: {},
    
  504.     });
    
  505.     renderer.update(<Component>{42}</Component>);
    
  506.     expect(renderer.toJSON()).toEqual({
    
  507.       type: 'div',
    
  508.       children: ['42'],
    
  509.       props: {},
    
  510.     });
    
  511.     renderer.update(
    
  512.       <Component>
    
  513.         <div />
    
  514.       </Component>,
    
  515.     );
    
  516.     expect(renderer.toJSON()).toEqual({
    
  517.       type: 'div',
    
  518.       children: [
    
  519.         {
    
  520.           type: 'div',
    
  521.           children: null,
    
  522.           props: {},
    
  523.         },
    
  524.       ],
    
  525.       props: {},
    
  526.     });
    
  527.   });
    
  528. 
    
  529.   it('toTree() renders simple components returning host components', () => {
    
  530.     const Qoo = () => <span className="Qoo">Hello World!</span>;
    
  531. 
    
  532.     const renderer = ReactTestRenderer.create(<Qoo />);
    
  533.     const tree = renderer.toTree();
    
  534. 
    
  535.     cleanNodeOrArray(tree);
    
  536. 
    
  537.     expect(prettyFormat(tree)).toEqual(
    
  538.       prettyFormat({
    
  539.         nodeType: 'component',
    
  540.         type: Qoo,
    
  541.         props: {},
    
  542.         instance: null,
    
  543.         rendered: {
    
  544.           nodeType: 'host',
    
  545.           type: 'span',
    
  546.           props: {className: 'Qoo'},
    
  547.           instance: null,
    
  548.           rendered: ['Hello World!'],
    
  549.         },
    
  550.       }),
    
  551.     );
    
  552.   });
    
  553. 
    
  554.   it('toTree() handles nested Fragments', () => {
    
  555.     const Foo = () => (
    
  556.       <>
    
  557.         <>foo</>
    
  558.       </>
    
  559.     );
    
  560.     const renderer = ReactTestRenderer.create(<Foo />);
    
  561.     const tree = renderer.toTree();
    
  562. 
    
  563.     cleanNodeOrArray(tree);
    
  564. 
    
  565.     expect(prettyFormat(tree)).toEqual(
    
  566.       prettyFormat({
    
  567.         nodeType: 'component',
    
  568.         type: Foo,
    
  569.         instance: null,
    
  570.         props: {},
    
  571.         rendered: 'foo',
    
  572.       }),
    
  573.     );
    
  574.   });
    
  575. 
    
  576.   it('toTree() handles null rendering components', () => {
    
  577.     class Foo extends React.Component {
    
  578.       render() {
    
  579.         return null;
    
  580.       }
    
  581.     }
    
  582. 
    
  583.     const renderer = ReactTestRenderer.create(<Foo />);
    
  584.     const tree = renderer.toTree();
    
  585. 
    
  586.     expect(tree.instance).toBeInstanceOf(Foo);
    
  587. 
    
  588.     cleanNodeOrArray(tree);
    
  589. 
    
  590.     expect(tree).toEqual({
    
  591.       type: Foo,
    
  592.       nodeType: 'component',
    
  593.       props: {},
    
  594.       instance: null,
    
  595.       rendered: null,
    
  596.     });
    
  597.   });
    
  598. 
    
  599.   it('toTree() handles simple components that return arrays', () => {
    
  600.     const Foo = ({children}) => children;
    
  601. 
    
  602.     const renderer = ReactTestRenderer.create(
    
  603.       <Foo>
    
  604.         <div>One</div>
    
  605.         <div>Two</div>
    
  606.       </Foo>,
    
  607.     );
    
  608. 
    
  609.     const tree = renderer.toTree();
    
  610. 
    
  611.     cleanNodeOrArray(tree);
    
  612. 
    
  613.     expect(prettyFormat(tree)).toEqual(
    
  614.       prettyFormat({
    
  615.         type: Foo,
    
  616.         nodeType: 'component',
    
  617.         props: {},
    
  618.         instance: null,
    
  619.         rendered: [
    
  620.           {
    
  621.             instance: null,
    
  622.             nodeType: 'host',
    
  623.             props: {},
    
  624.             rendered: ['One'],
    
  625.             type: 'div',
    
  626.           },
    
  627.           {
    
  628.             instance: null,
    
  629.             nodeType: 'host',
    
  630.             props: {},
    
  631.             rendered: ['Two'],
    
  632.             type: 'div',
    
  633.           },
    
  634.         ],
    
  635.       }),
    
  636.     );
    
  637.   });
    
  638. 
    
  639.   it('toTree() handles complicated tree of arrays', () => {
    
  640.     class Foo extends React.Component {
    
  641.       render() {
    
  642.         return this.props.children;
    
  643.       }
    
  644.     }
    
  645. 
    
  646.     const renderer = ReactTestRenderer.create(
    
  647.       <div>
    
  648.         <Foo>
    
  649.           <div>One</div>
    
  650.           <div>Two</div>
    
  651.           <Foo>
    
  652.             <div>Three</div>
    
  653.           </Foo>
    
  654.         </Foo>
    
  655.         <div>Four</div>
    
  656.       </div>,
    
  657.     );
    
  658. 
    
  659.     const tree = renderer.toTree();
    
  660. 
    
  661.     cleanNodeOrArray(tree);
    
  662. 
    
  663.     expect(prettyFormat(tree)).toEqual(
    
  664.       prettyFormat({
    
  665.         type: 'div',
    
  666.         instance: null,
    
  667.         nodeType: 'host',
    
  668.         props: {},
    
  669.         rendered: [
    
  670.           {
    
  671.             type: Foo,
    
  672.             nodeType: 'component',
    
  673.             props: {},
    
  674.             instance: null,
    
  675.             rendered: [
    
  676.               {
    
  677.                 type: 'div',
    
  678.                 nodeType: 'host',
    
  679.                 props: {},
    
  680.                 instance: null,
    
  681.                 rendered: ['One'],
    
  682.               },
    
  683.               {
    
  684.                 type: 'div',
    
  685.                 nodeType: 'host',
    
  686.                 props: {},
    
  687.                 instance: null,
    
  688.                 rendered: ['Two'],
    
  689.               },
    
  690.               {
    
  691.                 type: Foo,
    
  692.                 nodeType: 'component',
    
  693.                 props: {},
    
  694.                 instance: null,
    
  695.                 rendered: {
    
  696.                   type: 'div',
    
  697.                   nodeType: 'host',
    
  698.                   props: {},
    
  699.                   instance: null,
    
  700.                   rendered: ['Three'],
    
  701.                 },
    
  702.               },
    
  703.             ],
    
  704.           },
    
  705.           {
    
  706.             type: 'div',
    
  707.             nodeType: 'host',
    
  708.             props: {},
    
  709.             instance: null,
    
  710.             rendered: ['Four'],
    
  711.           },
    
  712.         ],
    
  713.       }),
    
  714.     );
    
  715.   });
    
  716. 
    
  717.   it('toTree() handles complicated tree of fragments', () => {
    
  718.     const renderer = ReactTestRenderer.create(
    
  719.       <>
    
  720.         <>
    
  721.           <div>One</div>
    
  722.           <div>Two</div>
    
  723.           <>
    
  724.             <div>Three</div>
    
  725.           </>
    
  726.         </>
    
  727.         <div>Four</div>
    
  728.       </>,
    
  729.     );
    
  730. 
    
  731.     const tree = renderer.toTree();
    
  732. 
    
  733.     cleanNodeOrArray(tree);
    
  734. 
    
  735.     expect(prettyFormat(tree)).toEqual(
    
  736.       prettyFormat([
    
  737.         {
    
  738.           type: 'div',
    
  739.           nodeType: 'host',
    
  740.           props: {},
    
  741.           instance: null,
    
  742.           rendered: ['One'],
    
  743.         },
    
  744.         {
    
  745.           type: 'div',
    
  746.           nodeType: 'host',
    
  747.           props: {},
    
  748.           instance: null,
    
  749.           rendered: ['Two'],
    
  750.         },
    
  751.         {
    
  752.           type: 'div',
    
  753.           nodeType: 'host',
    
  754.           props: {},
    
  755.           instance: null,
    
  756.           rendered: ['Three'],
    
  757.         },
    
  758.         {
    
  759.           type: 'div',
    
  760.           nodeType: 'host',
    
  761.           props: {},
    
  762.           instance: null,
    
  763.           rendered: ['Four'],
    
  764.         },
    
  765.       ]),
    
  766.     );
    
  767.   });
    
  768. 
    
  769.   it('root instance and createNodeMock ref return the same value', () => {
    
  770.     const createNodeMock = ref => ({node: ref});
    
  771.     let refInst = null;
    
  772.     const renderer = ReactTestRenderer.create(
    
  773.       <div ref={ref => (refInst = ref)} />,
    
  774.       {createNodeMock},
    
  775.     );
    
  776.     const root = renderer.getInstance();
    
  777.     expect(root).toEqual(refInst);
    
  778.   });
    
  779. 
    
  780.   it('toTree() renders complicated trees of composites and hosts', () => {
    
  781.     // SFC returning host. no children props.
    
  782.     const Qoo = () => <span className="Qoo">Hello World!</span>;
    
  783. 
    
  784.     // SFC returning host. passes through children.
    
  785.     const Foo = ({className, children}) => (
    
  786.       <div className={'Foo ' + className}>
    
  787.         <span className="Foo2">Literal</span>
    
  788.         {children}
    
  789.       </div>
    
  790.     );
    
  791. 
    
  792.     // class composite returning composite. passes through children.
    
  793.     class Bar extends React.Component {
    
  794.       render() {
    
  795.         const {special, children} = this.props;
    
  796.         return <Foo className={special ? 'special' : 'normal'}>{children}</Foo>;
    
  797.       }
    
  798.     }
    
  799. 
    
  800.     // class composite return composite. no children props.
    
  801.     class Bam extends React.Component {
    
  802.       render() {
    
  803.         return (
    
  804.           <Bar special={true}>
    
  805.             <Qoo />
    
  806.           </Bar>
    
  807.         );
    
  808.       }
    
  809.     }
    
  810. 
    
  811.     const renderer = ReactTestRenderer.create(<Bam />);
    
  812.     const tree = renderer.toTree();
    
  813. 
    
  814.     // we test for the presence of instances before nulling them out
    
  815.     expect(tree.instance).toBeInstanceOf(Bam);
    
  816.     expect(tree.rendered.instance).toBeInstanceOf(Bar);
    
  817. 
    
  818.     cleanNodeOrArray(tree);
    
  819. 
    
  820.     expect(prettyFormat(tree)).toEqual(
    
  821.       prettyFormat({
    
  822.         type: Bam,
    
  823.         nodeType: 'component',
    
  824.         props: {},
    
  825.         instance: null,
    
  826.         rendered: {
    
  827.           type: Bar,
    
  828.           nodeType: 'component',
    
  829.           props: {special: true},
    
  830.           instance: null,
    
  831.           rendered: {
    
  832.             type: Foo,
    
  833.             nodeType: 'component',
    
  834.             props: {className: 'special'},
    
  835.             instance: null,
    
  836.             rendered: {
    
  837.               type: 'div',
    
  838.               nodeType: 'host',
    
  839.               props: {className: 'Foo special'},
    
  840.               instance: null,
    
  841.               rendered: [
    
  842.                 {
    
  843.                   type: 'span',
    
  844.                   nodeType: 'host',
    
  845.                   props: {className: 'Foo2'},
    
  846.                   instance: null,
    
  847.                   rendered: ['Literal'],
    
  848.                 },
    
  849.                 {
    
  850.                   type: Qoo,
    
  851.                   nodeType: 'component',
    
  852.                   props: {},
    
  853.                   instance: null,
    
  854.                   rendered: {
    
  855.                     type: 'span',
    
  856.                     nodeType: 'host',
    
  857.                     props: {className: 'Qoo'},
    
  858.                     instance: null,
    
  859.                     rendered: ['Hello World!'],
    
  860.                   },
    
  861.                 },
    
  862.               ],
    
  863.             },
    
  864.           },
    
  865.         },
    
  866.       }),
    
  867.     );
    
  868.   });
    
  869. 
    
  870.   it('can update text nodes when rendered as root', () => {
    
  871.     const renderer = ReactTestRenderer.create(['Hello', 'world']);
    
  872.     expect(renderer.toJSON()).toEqual(['Hello', 'world']);
    
  873.     renderer.update(42);
    
  874.     expect(renderer.toJSON()).toEqual('42');
    
  875.     renderer.update([42, 'world']);
    
  876.     expect(renderer.toJSON()).toEqual(['42', 'world']);
    
  877.   });
    
  878. 
    
  879.   it('can render and update root fragments', () => {
    
  880.     const Component = props => props.children;
    
  881. 
    
  882.     const renderer = ReactTestRenderer.create([
    
  883.       <Component key="a">Hi</Component>,
    
  884.       <Component key="b">Bye</Component>,
    
  885.     ]);
    
  886.     expect(renderer.toJSON()).toEqual(['Hi', 'Bye']);
    
  887.     renderer.update(<div />);
    
  888.     expect(renderer.toJSON()).toEqual({
    
  889.       type: 'div',
    
  890.       children: null,
    
  891.       props: {},
    
  892.     });
    
  893.     renderer.update([<div key="a">goodbye</div>, 'world']);
    
  894.     expect(renderer.toJSON()).toEqual([
    
  895.       {
    
  896.         type: 'div',
    
  897.         children: ['goodbye'],
    
  898.         props: {},
    
  899.       },
    
  900.       'world',
    
  901.     ]);
    
  902.   });
    
  903. 
    
  904.   it('supports context providers and consumers', () => {
    
  905.     const {Consumer, Provider} = React.createContext('a');
    
  906. 
    
  907.     function Child(props) {
    
  908.       return props.value;
    
  909.     }
    
  910. 
    
  911.     function App() {
    
  912.       return (
    
  913.         <Provider value="b">
    
  914.           <Consumer>{value => <Child value={value} />}</Consumer>
    
  915.         </Provider>
    
  916.       );
    
  917.     }
    
  918. 
    
  919.     const renderer = ReactTestRenderer.create(<App />);
    
  920.     const child = renderer.root.findByType(Child);
    
  921.     expect(child.children).toEqual(['b']);
    
  922.     expect(prettyFormat(renderer.toTree())).toEqual(
    
  923.       prettyFormat({
    
  924.         instance: null,
    
  925.         nodeType: 'component',
    
  926.         props: {},
    
  927.         rendered: {
    
  928.           instance: null,
    
  929.           nodeType: 'component',
    
  930.           props: {
    
  931.             value: 'b',
    
  932.           },
    
  933.           rendered: 'b',
    
  934.           type: Child,
    
  935.         },
    
  936.         type: App,
    
  937.       }),
    
  938.     );
    
  939.   });
    
  940. 
    
  941.   it('supports modes', () => {
    
  942.     function Child(props) {
    
  943.       return props.value;
    
  944.     }
    
  945. 
    
  946.     function App(props) {
    
  947.       return (
    
  948.         <React.StrictMode>
    
  949.           <Child value={props.value} />
    
  950.         </React.StrictMode>
    
  951.       );
    
  952.     }
    
  953. 
    
  954.     const renderer = ReactTestRenderer.create(<App value="a" />);
    
  955.     const child = renderer.root.findByType(Child);
    
  956.     expect(child.children).toEqual(['a']);
    
  957.     expect(prettyFormat(renderer.toTree())).toEqual(
    
  958.       prettyFormat({
    
  959.         instance: null,
    
  960.         nodeType: 'component',
    
  961.         props: {
    
  962.           value: 'a',
    
  963.         },
    
  964.         rendered: {
    
  965.           instance: null,
    
  966.           nodeType: 'component',
    
  967.           props: {
    
  968.             value: 'a',
    
  969.           },
    
  970.           rendered: 'a',
    
  971.           type: Child,
    
  972.         },
    
  973.         type: App,
    
  974.       }),
    
  975.     );
    
  976.   });
    
  977. 
    
  978.   it('supports forwardRef', () => {
    
  979.     const InnerRefed = React.forwardRef((props, ref) => (
    
  980.       <div>
    
  981.         <span ref={ref} />
    
  982.       </div>
    
  983.     ));
    
  984. 
    
  985.     class App extends React.Component {
    
  986.       render() {
    
  987.         return <InnerRefed ref={r => (this.ref = r)} />;
    
  988.       }
    
  989.     }
    
  990. 
    
  991.     const renderer = ReactTestRenderer.create(<App />);
    
  992.     const tree = renderer.toTree();
    
  993.     cleanNodeOrArray(tree);
    
  994. 
    
  995.     expect(prettyFormat(tree)).toEqual(
    
  996.       prettyFormat({
    
  997.         instance: null,
    
  998.         nodeType: 'component',
    
  999.         props: {},
    
  1000.         rendered: {
    
  1001.           instance: null,
    
  1002.           nodeType: 'host',
    
  1003.           props: {},
    
  1004.           rendered: [
    
  1005.             {
    
  1006.               instance: null,
    
  1007.               nodeType: 'host',
    
  1008.               props: {},
    
  1009.               rendered: [],
    
  1010.               type: 'span',
    
  1011.             },
    
  1012.           ],
    
  1013.           type: 'div',
    
  1014.         },
    
  1015.         type: App,
    
  1016.       }),
    
  1017.     );
    
  1018.   });
    
  1019. 
    
  1020.   it('can concurrently render context with a "primary" renderer', async () => {
    
  1021.     const Context = React.createContext(null);
    
  1022.     const Indirection = React.Fragment;
    
  1023.     const App = () => (
    
  1024.       <Context.Provider value={null}>
    
  1025.         <Indirection>
    
  1026.           <Context.Consumer>{() => null}</Context.Consumer>
    
  1027.         </Indirection>
    
  1028.       </Context.Provider>
    
  1029.     );
    
  1030.     ReactNoop.render(<App />);
    
  1031.     await waitForAll([]);
    
  1032.     ReactTestRenderer.create(<App />);
    
  1033.   });
    
  1034. 
    
  1035.   it('calling findByType() with an invalid component will fall back to "Unknown" for component name', () => {
    
  1036.     const App = () => null;
    
  1037.     const renderer = ReactTestRenderer.create(<App />);
    
  1038.     const NonComponent = {};
    
  1039. 
    
  1040.     expect(() => {
    
  1041.       renderer.root.findByType(NonComponent);
    
  1042.     }).toThrowError(`No instances found with node type: "Unknown"`);
    
  1043.   });
    
  1044. });