1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @emails react-core
    
  8.  */
    
  9. 
    
  10. 'use strict';
    
  11. 
    
  12. let ChildUpdates;
    
  13. let MorphingComponent;
    
  14. let React;
    
  15. let ReactDOM;
    
  16. let ReactCurrentOwner;
    
  17. let ReactTestUtils;
    
  18. let PropTypes;
    
  19. 
    
  20. describe('ReactCompositeComponent', () => {
    
  21.   const hasOwnProperty = Object.prototype.hasOwnProperty;
    
  22. 
    
  23.   /**
    
  24.    * Performs equality by iterating through keys on an object and returning false
    
  25.    * when any key has values which are not strictly equal between the arguments.
    
  26.    * Returns true when the values of all keys are strictly equal.
    
  27.    */
    
  28.   function shallowEqual(objA: mixed, objB: mixed): boolean {
    
  29.     if (Object.is(objA, objB)) {
    
  30.       return true;
    
  31.     }
    
  32.     if (
    
  33.       typeof objA !== 'object' ||
    
  34.       objA === null ||
    
  35.       typeof objB !== 'object' ||
    
  36.       objB === null
    
  37.     ) {
    
  38.       return false;
    
  39.     }
    
  40.     const keysA = Object.keys(objA);
    
  41.     const keysB = Object.keys(objB);
    
  42.     if (keysA.length !== keysB.length) {
    
  43.       return false;
    
  44.     }
    
  45.     for (let i = 0; i < keysA.length; i++) {
    
  46.       if (
    
  47.         !hasOwnProperty.call(objB, keysA[i]) ||
    
  48.         !Object.is(objA[keysA[i]], objB[keysA[i]])
    
  49.       ) {
    
  50.         return false;
    
  51.       }
    
  52.     }
    
  53.     return true;
    
  54.   }
    
  55. 
    
  56.   function shallowCompare(instance, nextProps, nextState) {
    
  57.     return (
    
  58.       !shallowEqual(instance.props, nextProps) ||
    
  59.       !shallowEqual(instance.state, nextState)
    
  60.     );
    
  61.   }
    
  62. 
    
  63.   beforeEach(() => {
    
  64.     jest.resetModules();
    
  65.     React = require('react');
    
  66.     ReactDOM = require('react-dom');
    
  67.     ReactCurrentOwner =
    
  68.       require('react').__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
    
  69.         .ReactCurrentOwner;
    
  70.     ReactTestUtils = require('react-dom/test-utils');
    
  71.     PropTypes = require('prop-types');
    
  72. 
    
  73.     MorphingComponent = class extends React.Component {
    
  74.       state = {activated: false};
    
  75. 
    
  76.       xRef = React.createRef();
    
  77. 
    
  78.       _toggleActivatedState = () => {
    
  79.         this.setState({activated: !this.state.activated});
    
  80.       };
    
  81. 
    
  82.       render() {
    
  83.         const toggleActivatedState = this._toggleActivatedState;
    
  84.         return !this.state.activated ? (
    
  85.           <a ref={this.xRef} onClick={toggleActivatedState} />
    
  86.         ) : (
    
  87.           <b ref={this.xRef} onClick={toggleActivatedState} />
    
  88.         );
    
  89.       }
    
  90.     };
    
  91. 
    
  92.     /**
    
  93.      * We'll use this to ensure that an old version is not cached when it is
    
  94.      * reallocated again.
    
  95.      */
    
  96.     ChildUpdates = class extends React.Component {
    
  97.       anchorRef = React.createRef();
    
  98. 
    
  99.       getAnchor = () => {
    
  100.         return this.anchorRef.current;
    
  101.       };
    
  102. 
    
  103.       render() {
    
  104.         const className = this.props.anchorClassOn ? 'anchorClass' : '';
    
  105.         return this.props.renderAnchor ? (
    
  106.           <a ref={this.anchorRef} className={className} />
    
  107.         ) : (
    
  108.           <b />
    
  109.         );
    
  110.       }
    
  111.     };
    
  112.   });
    
  113. 
    
  114.   if (require('shared/ReactFeatureFlags').disableModulePatternComponents) {
    
  115.     it('should not support module pattern components', () => {
    
  116.       function Child({test}) {
    
  117.         return {
    
  118.           render() {
    
  119.             return <div>{test}</div>;
    
  120.           },
    
  121.         };
    
  122.       }
    
  123. 
    
  124.       const el = document.createElement('div');
    
  125.       expect(() => {
    
  126.         expect(() => ReactDOM.render(<Child test="test" />, el)).toThrow(
    
  127.           'Objects are not valid as a React child (found: object with keys {render}).',
    
  128.         );
    
  129.       }).toErrorDev(
    
  130.         'Warning: The <Child /> component appears to be a function component that returns a class instance. ' +
    
  131.           'Change Child to a class that extends React.Component instead. ' +
    
  132.           "If you can't use a class try assigning the prototype on the function as a workaround. " +
    
  133.           '`Child.prototype = React.Component.prototype`. ' +
    
  134.           "Don't use an arrow function since it cannot be called with `new` by React.",
    
  135.       );
    
  136. 
    
  137.       expect(el.textContent).toBe('');
    
  138.     });
    
  139.   } else {
    
  140.     it('should support module pattern components', () => {
    
  141.       function Child({test}) {
    
  142.         return {
    
  143.           render() {
    
  144.             return <div>{test}</div>;
    
  145.           },
    
  146.         };
    
  147.       }
    
  148. 
    
  149.       const el = document.createElement('div');
    
  150.       expect(() => ReactDOM.render(<Child test="test" />, el)).toErrorDev(
    
  151.         'Warning: The <Child /> component appears to be a function component that returns a class instance. ' +
    
  152.           'Change Child to a class that extends React.Component instead. ' +
    
  153.           "If you can't use a class try assigning the prototype on the function as a workaround. " +
    
  154.           '`Child.prototype = React.Component.prototype`. ' +
    
  155.           "Don't use an arrow function since it cannot be called with `new` by React.",
    
  156.       );
    
  157. 
    
  158.       expect(el.textContent).toBe('test');
    
  159.     });
    
  160.   }
    
  161. 
    
  162.   it('should support rendering to different child types over time', () => {
    
  163.     const instance = ReactTestUtils.renderIntoDocument(<MorphingComponent />);
    
  164.     let el = ReactDOM.findDOMNode(instance);
    
  165.     expect(el.tagName).toBe('A');
    
  166. 
    
  167.     instance._toggleActivatedState();
    
  168.     el = ReactDOM.findDOMNode(instance);
    
  169.     expect(el.tagName).toBe('B');
    
  170. 
    
  171.     instance._toggleActivatedState();
    
  172.     el = ReactDOM.findDOMNode(instance);
    
  173.     expect(el.tagName).toBe('A');
    
  174.   });
    
  175. 
    
  176.   it('should react to state changes from callbacks', () => {
    
  177.     const container = document.createElement('div');
    
  178.     document.body.appendChild(container);
    
  179.     try {
    
  180.       const instance = ReactDOM.render(<MorphingComponent />, container);
    
  181.       let el = ReactDOM.findDOMNode(instance);
    
  182.       expect(el.tagName).toBe('A');
    
  183.       el.click();
    
  184.       el = ReactDOM.findDOMNode(instance);
    
  185.       expect(el.tagName).toBe('B');
    
  186.     } finally {
    
  187.       document.body.removeChild(container);
    
  188.     }
    
  189.   });
    
  190. 
    
  191.   it('should rewire refs when rendering to different child types', () => {
    
  192.     const instance = ReactTestUtils.renderIntoDocument(<MorphingComponent />);
    
  193. 
    
  194.     expect(instance.xRef.current.tagName).toBe('A');
    
  195.     instance._toggleActivatedState();
    
  196.     expect(instance.xRef.current.tagName).toBe('B');
    
  197.     instance._toggleActivatedState();
    
  198.     expect(instance.xRef.current.tagName).toBe('A');
    
  199.   });
    
  200. 
    
  201.   it('should not cache old DOM nodes when switching constructors', () => {
    
  202.     const container = document.createElement('div');
    
  203.     const instance = ReactDOM.render(
    
  204.       <ChildUpdates renderAnchor={true} anchorClassOn={false} />,
    
  205.       container,
    
  206.     );
    
  207.     ReactDOM.render(
    
  208.       // Warm any cache
    
  209.       <ChildUpdates renderAnchor={true} anchorClassOn={true} />,
    
  210.       container,
    
  211.     );
    
  212.     ReactDOM.render(
    
  213.       // Clear out the anchor
    
  214.       <ChildUpdates renderAnchor={false} anchorClassOn={true} />,
    
  215.       container,
    
  216.     );
    
  217.     ReactDOM.render(
    
  218.       // rerender
    
  219.       <ChildUpdates renderAnchor={true} anchorClassOn={false} />,
    
  220.       container,
    
  221.     );
    
  222.     expect(instance.getAnchor().className).toBe('');
    
  223.   });
    
  224. 
    
  225.   it('should use default values for undefined props', () => {
    
  226.     class Component extends React.Component {
    
  227.       static defaultProps = {prop: 'testKey'};
    
  228. 
    
  229.       render() {
    
  230.         return <span />;
    
  231.       }
    
  232.     }
    
  233. 
    
  234.     const instance1 = ReactTestUtils.renderIntoDocument(<Component />);
    
  235.     expect(instance1.props).toEqual({prop: 'testKey'});
    
  236. 
    
  237.     const instance2 = ReactTestUtils.renderIntoDocument(
    
  238.       <Component prop={undefined} />,
    
  239.     );
    
  240.     expect(instance2.props).toEqual({prop: 'testKey'});
    
  241. 
    
  242.     const instance3 = ReactTestUtils.renderIntoDocument(
    
  243.       <Component prop={null} />,
    
  244.     );
    
  245.     expect(instance3.props).toEqual({prop: null});
    
  246.   });
    
  247. 
    
  248.   it('should not mutate passed-in props object', () => {
    
  249.     class Component extends React.Component {
    
  250.       static defaultProps = {prop: 'testKey'};
    
  251. 
    
  252.       render() {
    
  253.         return <span />;
    
  254.       }
    
  255.     }
    
  256. 
    
  257.     const inputProps = {};
    
  258.     let instance1 = <Component {...inputProps} />;
    
  259.     instance1 = ReactTestUtils.renderIntoDocument(instance1);
    
  260.     expect(instance1.props.prop).toBe('testKey');
    
  261. 
    
  262.     // We don't mutate the input, just in case the caller wants to do something
    
  263.     // with it after using it to instantiate a component
    
  264.     expect(inputProps.prop).not.toBeDefined();
    
  265.   });
    
  266. 
    
  267.   it('should warn about `forceUpdate` on not-yet-mounted components', () => {
    
  268.     class MyComponent extends React.Component {
    
  269.       constructor(props) {
    
  270.         super(props);
    
  271.         this.forceUpdate();
    
  272.       }
    
  273.       render() {
    
  274.         return <div />;
    
  275.       }
    
  276.     }
    
  277. 
    
  278.     const container = document.createElement('div');
    
  279.     expect(() => ReactDOM.render(<MyComponent />, container)).toErrorDev(
    
  280.       "Warning: Can't call forceUpdate on a component that is not yet mounted. " +
    
  281.         'This is a no-op, but it might indicate a bug in your application. ' +
    
  282.         'Instead, assign to `this.state` directly or define a `state = {};` ' +
    
  283.         'class property with the desired state in the MyComponent component.',
    
  284.     );
    
  285. 
    
  286.     // No additional warning should be recorded
    
  287.     const container2 = document.createElement('div');
    
  288.     ReactDOM.render(<MyComponent />, container2);
    
  289.   });
    
  290. 
    
  291.   it('should warn about `setState` on not-yet-mounted components', () => {
    
  292.     class MyComponent extends React.Component {
    
  293.       constructor(props) {
    
  294.         super(props);
    
  295.         this.setState();
    
  296.       }
    
  297.       render() {
    
  298.         return <div />;
    
  299.       }
    
  300.     }
    
  301. 
    
  302.     const container = document.createElement('div');
    
  303.     expect(() => ReactDOM.render(<MyComponent />, container)).toErrorDev(
    
  304.       "Warning: Can't call setState on a component that is not yet mounted. " +
    
  305.         'This is a no-op, but it might indicate a bug in your application. ' +
    
  306.         'Instead, assign to `this.state` directly or define a `state = {};` ' +
    
  307.         'class property with the desired state in the MyComponent component.',
    
  308.     );
    
  309. 
    
  310.     // No additional warning should be recorded
    
  311.     const container2 = document.createElement('div');
    
  312.     ReactDOM.render(<MyComponent />, container2);
    
  313.   });
    
  314. 
    
  315.   it('should not warn about `forceUpdate` on unmounted components', () => {
    
  316.     const container = document.createElement('div');
    
  317.     document.body.appendChild(container);
    
  318. 
    
  319.     class Component extends React.Component {
    
  320.       render() {
    
  321.         return <div />;
    
  322.       }
    
  323.     }
    
  324. 
    
  325.     let instance = <Component />;
    
  326.     expect(instance.forceUpdate).not.toBeDefined();
    
  327. 
    
  328.     instance = ReactDOM.render(instance, container);
    
  329.     instance.forceUpdate();
    
  330. 
    
  331.     ReactDOM.unmountComponentAtNode(container);
    
  332. 
    
  333.     instance.forceUpdate();
    
  334.     instance.forceUpdate();
    
  335.   });
    
  336. 
    
  337.   it('should not warn about `setState` on unmounted components', () => {
    
  338.     const container = document.createElement('div');
    
  339.     document.body.appendChild(container);
    
  340. 
    
  341.     let renders = 0;
    
  342. 
    
  343.     class Component extends React.Component {
    
  344.       state = {value: 0};
    
  345. 
    
  346.       render() {
    
  347.         renders++;
    
  348.         return <div />;
    
  349.       }
    
  350.     }
    
  351. 
    
  352.     let instance;
    
  353.     ReactDOM.render(
    
  354.       <div>
    
  355.         <span>
    
  356.           <Component ref={c => (instance = c || instance)} />
    
  357.         </span>
    
  358.       </div>,
    
  359.       container,
    
  360.     );
    
  361. 
    
  362.     expect(renders).toBe(1);
    
  363. 
    
  364.     instance.setState({value: 1});
    
  365.     expect(renders).toBe(2);
    
  366. 
    
  367.     ReactDOM.render(<div />, container);
    
  368.     instance.setState({value: 2});
    
  369.     expect(renders).toBe(2);
    
  370.   });
    
  371. 
    
  372.   it('should silently allow `setState`, not call cb on unmounting components', () => {
    
  373.     let cbCalled = false;
    
  374.     const container = document.createElement('div');
    
  375.     document.body.appendChild(container);
    
  376. 
    
  377.     class Component extends React.Component {
    
  378.       state = {value: 0};
    
  379. 
    
  380.       componentWillUnmount() {
    
  381.         expect(() => {
    
  382.           this.setState({value: 2}, function () {
    
  383.             cbCalled = true;
    
  384.           });
    
  385.         }).not.toThrow();
    
  386.       }
    
  387. 
    
  388.       render() {
    
  389.         return <div />;
    
  390.       }
    
  391.     }
    
  392. 
    
  393.     const instance = ReactDOM.render(<Component />, container);
    
  394.     instance.setState({value: 1});
    
  395. 
    
  396.     ReactDOM.unmountComponentAtNode(container);
    
  397.     expect(cbCalled).toBe(false);
    
  398.   });
    
  399. 
    
  400.   it('should warn when rendering a class with a render method that does not extend React.Component', () => {
    
  401.     const container = document.createElement('div');
    
  402.     class ClassWithRenderNotExtended {
    
  403.       render() {
    
  404.         return <div />;
    
  405.       }
    
  406.     }
    
  407.     expect(() => {
    
  408.       expect(() => {
    
  409.         ReactDOM.render(<ClassWithRenderNotExtended />, container);
    
  410.       }).toThrow(TypeError);
    
  411.     }).toErrorDev(
    
  412.       'Warning: The <ClassWithRenderNotExtended /> component appears to have a render method, ' +
    
  413.         "but doesn't extend React.Component. This is likely to cause errors. " +
    
  414.         'Change ClassWithRenderNotExtended to extend React.Component instead.',
    
  415.     );
    
  416. 
    
  417.     // Test deduplication
    
  418.     expect(() => {
    
  419.       ReactDOM.render(<ClassWithRenderNotExtended />, container);
    
  420.     }).toThrow(TypeError);
    
  421.   });
    
  422. 
    
  423.   it('should warn about `setState` in render', () => {
    
  424.     const container = document.createElement('div');
    
  425. 
    
  426.     let renderedState = -1;
    
  427.     let renderPasses = 0;
    
  428. 
    
  429.     class Component extends React.Component {
    
  430.       state = {value: 0};
    
  431. 
    
  432.       render() {
    
  433.         renderPasses++;
    
  434.         renderedState = this.state.value;
    
  435.         if (this.state.value === 0) {
    
  436.           this.setState({value: 1});
    
  437.         }
    
  438.         return <div />;
    
  439.       }
    
  440.     }
    
  441. 
    
  442.     let instance;
    
  443. 
    
  444.     expect(() => {
    
  445.       instance = ReactDOM.render(<Component />, container);
    
  446.     }).toErrorDev(
    
  447.       'Cannot update during an existing state transition (such as within ' +
    
  448.         '`render`). Render methods should be a pure function of props and state.',
    
  449.     );
    
  450. 
    
  451.     // The setState call is queued and then executed as a second pass. This
    
  452.     // behavior is undefined though so we're free to change it to suit the
    
  453.     // implementation details.
    
  454.     expect(renderPasses).toBe(2);
    
  455.     expect(renderedState).toBe(1);
    
  456.     expect(instance.state.value).toBe(1);
    
  457. 
    
  458.     // Forcing a rerender anywhere will cause the update to happen.
    
  459.     const instance2 = ReactDOM.render(<Component prop={123} />, container);
    
  460.     expect(instance).toBe(instance2);
    
  461.     expect(renderedState).toBe(1);
    
  462.     expect(instance2.state.value).toBe(1);
    
  463. 
    
  464.     // Test deduplication; (no additional warnings are expected).
    
  465.     ReactDOM.unmountComponentAtNode(container);
    
  466.     ReactDOM.render(<Component prop={123} />, container);
    
  467.   });
    
  468. 
    
  469.   it('should cleanup even if render() fatals', () => {
    
  470.     class BadComponent extends React.Component {
    
  471.       render() {
    
  472.         throw new Error();
    
  473.       }
    
  474.     }
    
  475. 
    
  476.     let instance = <BadComponent />;
    
  477. 
    
  478.     expect(ReactCurrentOwner.current).toBe(null);
    
  479. 
    
  480.     expect(() => {
    
  481.       instance = ReactTestUtils.renderIntoDocument(instance);
    
  482.     }).toThrow();
    
  483. 
    
  484.     expect(ReactCurrentOwner.current).toBe(null);
    
  485.   });
    
  486. 
    
  487.   it('should call componentWillUnmount before unmounting', () => {
    
  488.     const container = document.createElement('div');
    
  489.     let innerUnmounted = false;
    
  490. 
    
  491.     class Component extends React.Component {
    
  492.       render() {
    
  493.         return (
    
  494.           <div>
    
  495.             <Inner />
    
  496.             Text
    
  497.           </div>
    
  498.         );
    
  499.       }
    
  500.     }
    
  501. 
    
  502.     class Inner extends React.Component {
    
  503.       componentWillUnmount() {
    
  504.         innerUnmounted = true;
    
  505.       }
    
  506. 
    
  507.       render() {
    
  508.         return <div />;
    
  509.       }
    
  510.     }
    
  511. 
    
  512.     ReactDOM.render(<Component />, container);
    
  513.     ReactDOM.unmountComponentAtNode(container);
    
  514.     expect(innerUnmounted).toBe(true);
    
  515.   });
    
  516. 
    
  517.   it('should warn when shouldComponentUpdate() returns undefined', () => {
    
  518.     class ClassComponent extends React.Component {
    
  519.       state = {bogus: false};
    
  520. 
    
  521.       shouldComponentUpdate() {
    
  522.         return undefined;
    
  523.       }
    
  524. 
    
  525.       render() {
    
  526.         return <div />;
    
  527.       }
    
  528.     }
    
  529. 
    
  530.     const instance = ReactTestUtils.renderIntoDocument(<ClassComponent />);
    
  531. 
    
  532.     expect(() => instance.setState({bogus: true})).toErrorDev(
    
  533.       'Warning: ClassComponent.shouldComponentUpdate(): Returned undefined instead of a ' +
    
  534.         'boolean value. Make sure to return true or false.',
    
  535.     );
    
  536.   });
    
  537. 
    
  538.   it('should warn when componentDidUnmount method is defined', () => {
    
  539.     class Component extends React.Component {
    
  540.       componentDidUnmount = () => {};
    
  541. 
    
  542.       render() {
    
  543.         return <div />;
    
  544.       }
    
  545.     }
    
  546. 
    
  547.     expect(() => ReactTestUtils.renderIntoDocument(<Component />)).toErrorDev(
    
  548.       'Warning: Component has a method called ' +
    
  549.         'componentDidUnmount(). But there is no such lifecycle method. ' +
    
  550.         'Did you mean componentWillUnmount()?',
    
  551.     );
    
  552.   });
    
  553. 
    
  554.   it('should warn when componentDidReceiveProps method is defined', () => {
    
  555.     class Component extends React.Component {
    
  556.       componentDidReceiveProps = () => {};
    
  557. 
    
  558.       render() {
    
  559.         return <div />;
    
  560.       }
    
  561.     }
    
  562. 
    
  563.     expect(() => ReactTestUtils.renderIntoDocument(<Component />)).toErrorDev(
    
  564.       'Warning: Component has a method called ' +
    
  565.         'componentDidReceiveProps(). But there is no such lifecycle method. ' +
    
  566.         'If you meant to update the state in response to changing props, ' +
    
  567.         'use componentWillReceiveProps(). If you meant to fetch data or ' +
    
  568.         'run side-effects or mutations after React has updated the UI, use componentDidUpdate().',
    
  569.     );
    
  570.   });
    
  571. 
    
  572.   it('should warn when defaultProps was defined as an instance property', () => {
    
  573.     class Component extends React.Component {
    
  574.       constructor(props) {
    
  575.         super(props);
    
  576.         this.defaultProps = {name: 'Abhay'};
    
  577.       }
    
  578. 
    
  579.       render() {
    
  580.         return <div />;
    
  581.       }
    
  582.     }
    
  583. 
    
  584.     expect(() => ReactTestUtils.renderIntoDocument(<Component />)).toErrorDev(
    
  585.       'Warning: Setting defaultProps as an instance property on Component is not supported ' +
    
  586.         'and will be ignored. Instead, define defaultProps as a static property on Component.',
    
  587.     );
    
  588.   });
    
  589. 
    
  590.   // @gate !disableLegacyContext
    
  591.   it('should pass context to children when not owner', () => {
    
  592.     class Parent extends React.Component {
    
  593.       render() {
    
  594.         return (
    
  595.           <Child>
    
  596.             <Grandchild />
    
  597.           </Child>
    
  598.         );
    
  599.       }
    
  600.     }
    
  601. 
    
  602.     class Child extends React.Component {
    
  603.       static childContextTypes = {
    
  604.         foo: PropTypes.string,
    
  605.       };
    
  606. 
    
  607.       getChildContext() {
    
  608.         return {
    
  609.           foo: 'bar',
    
  610.         };
    
  611.       }
    
  612. 
    
  613.       render() {
    
  614.         return React.Children.only(this.props.children);
    
  615.       }
    
  616.     }
    
  617. 
    
  618.     class Grandchild extends React.Component {
    
  619.       static contextTypes = {
    
  620.         foo: PropTypes.string,
    
  621.       };
    
  622. 
    
  623.       render() {
    
  624.         return <div>{this.context.foo}</div>;
    
  625.       }
    
  626.     }
    
  627. 
    
  628.     const component = ReactTestUtils.renderIntoDocument(<Parent />);
    
  629.     expect(ReactDOM.findDOMNode(component).innerHTML).toBe('bar');
    
  630.   });
    
  631. 
    
  632.   it('should skip update when rerendering element in container', () => {
    
  633.     class Parent extends React.Component {
    
  634.       render() {
    
  635.         return <div>{this.props.children}</div>;
    
  636.       }
    
  637.     }
    
  638. 
    
  639.     let childRenders = 0;
    
  640. 
    
  641.     class Child extends React.Component {
    
  642.       render() {
    
  643.         childRenders++;
    
  644.         return <div />;
    
  645.       }
    
  646.     }
    
  647. 
    
  648.     const container = document.createElement('div');
    
  649.     const child = <Child />;
    
  650. 
    
  651.     ReactDOM.render(<Parent>{child}</Parent>, container);
    
  652.     ReactDOM.render(<Parent>{child}</Parent>, container);
    
  653.     expect(childRenders).toBe(1);
    
  654.   });
    
  655. 
    
  656.   // @gate !disableLegacyContext
    
  657.   it('should pass context when re-rendered for static child', () => {
    
  658.     let parentInstance = null;
    
  659.     let childInstance = null;
    
  660. 
    
  661.     class Parent extends React.Component {
    
  662.       static childContextTypes = {
    
  663.         foo: PropTypes.string,
    
  664.         flag: PropTypes.bool,
    
  665.       };
    
  666. 
    
  667.       state = {
    
  668.         flag: false,
    
  669.       };
    
  670. 
    
  671.       getChildContext() {
    
  672.         return {
    
  673.           foo: 'bar',
    
  674.           flag: this.state.flag,
    
  675.         };
    
  676.       }
    
  677. 
    
  678.       render() {
    
  679.         return React.Children.only(this.props.children);
    
  680.       }
    
  681.     }
    
  682. 
    
  683.     class Middle extends React.Component {
    
  684.       render() {
    
  685.         return this.props.children;
    
  686.       }
    
  687.     }
    
  688. 
    
  689.     class Child extends React.Component {
    
  690.       static contextTypes = {
    
  691.         foo: PropTypes.string,
    
  692.         flag: PropTypes.bool,
    
  693.       };
    
  694. 
    
  695.       render() {
    
  696.         childInstance = this;
    
  697.         return <span>Child</span>;
    
  698.       }
    
  699.     }
    
  700. 
    
  701.     parentInstance = ReactTestUtils.renderIntoDocument(
    
  702.       <Parent>
    
  703.         <Middle>
    
  704.           <Child />
    
  705.         </Middle>
    
  706.       </Parent>,
    
  707.     );
    
  708. 
    
  709.     expect(parentInstance.state.flag).toBe(false);
    
  710.     expect(childInstance.context).toEqual({foo: 'bar', flag: false});
    
  711. 
    
  712.     parentInstance.setState({flag: true});
    
  713.     expect(parentInstance.state.flag).toBe(true);
    
  714.     expect(childInstance.context).toEqual({foo: 'bar', flag: true});
    
  715.   });
    
  716. 
    
  717.   // @gate !disableLegacyContext
    
  718.   it('should pass context when re-rendered for static child within a composite component', () => {
    
  719.     class Parent extends React.Component {
    
  720.       static childContextTypes = {
    
  721.         flag: PropTypes.bool,
    
  722.       };
    
  723. 
    
  724.       state = {
    
  725.         flag: true,
    
  726.       };
    
  727. 
    
  728.       getChildContext() {
    
  729.         return {
    
  730.           flag: this.state.flag,
    
  731.         };
    
  732.       }
    
  733. 
    
  734.       render() {
    
  735.         return <div>{this.props.children}</div>;
    
  736.       }
    
  737.     }
    
  738. 
    
  739.     class Child extends React.Component {
    
  740.       static contextTypes = {
    
  741.         flag: PropTypes.bool,
    
  742.       };
    
  743. 
    
  744.       render() {
    
  745.         return <div />;
    
  746.       }
    
  747.     }
    
  748. 
    
  749.     class Wrapper extends React.Component {
    
  750.       parentRef = React.createRef();
    
  751.       childRef = React.createRef();
    
  752. 
    
  753.       render() {
    
  754.         return (
    
  755.           <Parent ref={this.parentRef}>
    
  756.             <Child ref={this.childRef} />
    
  757.           </Parent>
    
  758.         );
    
  759.       }
    
  760.     }
    
  761. 
    
  762.     const wrapper = ReactTestUtils.renderIntoDocument(<Wrapper />);
    
  763. 
    
  764.     expect(wrapper.parentRef.current.state.flag).toEqual(true);
    
  765.     expect(wrapper.childRef.current.context).toEqual({flag: true});
    
  766. 
    
  767.     // We update <Parent /> while <Child /> is still a static prop relative to this update
    
  768.     wrapper.parentRef.current.setState({flag: false});
    
  769. 
    
  770.     expect(wrapper.parentRef.current.state.flag).toEqual(false);
    
  771.     expect(wrapper.childRef.current.context).toEqual({flag: false});
    
  772.   });
    
  773. 
    
  774.   // @gate !disableLegacyContext
    
  775.   it('should pass context transitively', () => {
    
  776.     let childInstance = null;
    
  777.     let grandchildInstance = null;
    
  778. 
    
  779.     class Parent extends React.Component {
    
  780.       static childContextTypes = {
    
  781.         foo: PropTypes.string,
    
  782.         depth: PropTypes.number,
    
  783.       };
    
  784. 
    
  785.       getChildContext() {
    
  786.         return {
    
  787.           foo: 'bar',
    
  788.           depth: 0,
    
  789.         };
    
  790.       }
    
  791. 
    
  792.       render() {
    
  793.         return <Child />;
    
  794.       }
    
  795.     }
    
  796. 
    
  797.     class Child extends React.Component {
    
  798.       static contextTypes = {
    
  799.         foo: PropTypes.string,
    
  800.         depth: PropTypes.number,
    
  801.       };
    
  802. 
    
  803.       static childContextTypes = {
    
  804.         depth: PropTypes.number,
    
  805.       };
    
  806. 
    
  807.       getChildContext() {
    
  808.         return {
    
  809.           depth: this.context.depth + 1,
    
  810.         };
    
  811.       }
    
  812. 
    
  813.       render() {
    
  814.         childInstance = this;
    
  815.         return <Grandchild />;
    
  816.       }
    
  817.     }
    
  818. 
    
  819.     class Grandchild extends React.Component {
    
  820.       static contextTypes = {
    
  821.         foo: PropTypes.string,
    
  822.         depth: PropTypes.number,
    
  823.       };
    
  824. 
    
  825.       render() {
    
  826.         grandchildInstance = this;
    
  827.         return <div />;
    
  828.       }
    
  829.     }
    
  830. 
    
  831.     ReactTestUtils.renderIntoDocument(<Parent />);
    
  832.     expect(childInstance.context).toEqual({foo: 'bar', depth: 0});
    
  833.     expect(grandchildInstance.context).toEqual({foo: 'bar', depth: 1});
    
  834.   });
    
  835. 
    
  836.   // @gate !disableLegacyContext
    
  837.   it('should pass context when re-rendered', () => {
    
  838.     let parentInstance = null;
    
  839.     let childInstance = null;
    
  840. 
    
  841.     class Parent extends React.Component {
    
  842.       static childContextTypes = {
    
  843.         foo: PropTypes.string,
    
  844.         depth: PropTypes.number,
    
  845.       };
    
  846. 
    
  847.       state = {
    
  848.         flag: false,
    
  849.       };
    
  850. 
    
  851.       getChildContext() {
    
  852.         return {
    
  853.           foo: 'bar',
    
  854.           depth: 0,
    
  855.         };
    
  856.       }
    
  857. 
    
  858.       render() {
    
  859.         let output = <Child />;
    
  860.         if (!this.state.flag) {
    
  861.           output = <span>Child</span>;
    
  862.         }
    
  863.         return output;
    
  864.       }
    
  865.     }
    
  866. 
    
  867.     class Child extends React.Component {
    
  868.       static contextTypes = {
    
  869.         foo: PropTypes.string,
    
  870.         depth: PropTypes.number,
    
  871.       };
    
  872. 
    
  873.       render() {
    
  874.         childInstance = this;
    
  875.         return <span>Child</span>;
    
  876.       }
    
  877.     }
    
  878. 
    
  879.     parentInstance = ReactTestUtils.renderIntoDocument(<Parent />);
    
  880.     expect(childInstance).toBeNull();
    
  881. 
    
  882.     expect(parentInstance.state.flag).toBe(false);
    
  883.     ReactDOM.unstable_batchedUpdates(function () {
    
  884.       parentInstance.setState({flag: true});
    
  885.     });
    
  886.     expect(parentInstance.state.flag).toBe(true);
    
  887. 
    
  888.     expect(childInstance.context).toEqual({foo: 'bar', depth: 0});
    
  889.   });
    
  890. 
    
  891.   // @gate !disableLegacyContext
    
  892.   it('unmasked context propagates through updates', () => {
    
  893.     class Leaf extends React.Component {
    
  894.       static contextTypes = {
    
  895.         foo: PropTypes.string.isRequired,
    
  896.       };
    
  897. 
    
  898.       UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
    
  899.         expect('foo' in nextContext).toBe(true);
    
  900.       }
    
  901. 
    
  902.       shouldComponentUpdate(nextProps, nextState, nextContext) {
    
  903.         expect('foo' in nextContext).toBe(true);
    
  904.         return true;
    
  905.       }
    
  906. 
    
  907.       render() {
    
  908.         return <span>{this.context.foo}</span>;
    
  909.       }
    
  910.     }
    
  911. 
    
  912.     class Intermediary extends React.Component {
    
  913.       UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
    
  914.         expect('foo' in nextContext).toBe(false);
    
  915.       }
    
  916. 
    
  917.       shouldComponentUpdate(nextProps, nextState, nextContext) {
    
  918.         expect('foo' in nextContext).toBe(false);
    
  919.         return true;
    
  920.       }
    
  921. 
    
  922.       render() {
    
  923.         return <Leaf />;
    
  924.       }
    
  925.     }
    
  926. 
    
  927.     class Parent extends React.Component {
    
  928.       static childContextTypes = {
    
  929.         foo: PropTypes.string,
    
  930.       };
    
  931. 
    
  932.       getChildContext() {
    
  933.         return {
    
  934.           foo: this.props.cntxt,
    
  935.         };
    
  936.       }
    
  937. 
    
  938.       render() {
    
  939.         return <Intermediary />;
    
  940.       }
    
  941.     }
    
  942. 
    
  943.     const div = document.createElement('div');
    
  944.     ReactDOM.render(<Parent cntxt="noise" />, div);
    
  945.     expect(div.children[0].innerHTML).toBe('noise');
    
  946.     div.children[0].innerHTML = 'aliens';
    
  947.     div.children[0].id = 'aliens';
    
  948.     expect(div.children[0].innerHTML).toBe('aliens');
    
  949.     expect(div.children[0].id).toBe('aliens');
    
  950.     ReactDOM.render(<Parent cntxt="bar" />, div);
    
  951.     expect(div.children[0].innerHTML).toBe('bar');
    
  952.     expect(div.children[0].id).toBe('aliens');
    
  953.   });
    
  954. 
    
  955.   // @gate !disableLegacyContext
    
  956.   it('should trigger componentWillReceiveProps for context changes', () => {
    
  957.     let contextChanges = 0;
    
  958.     let propChanges = 0;
    
  959. 
    
  960.     class GrandChild extends React.Component {
    
  961.       static contextTypes = {
    
  962.         foo: PropTypes.string.isRequired,
    
  963.       };
    
  964. 
    
  965.       UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
    
  966.         expect('foo' in nextContext).toBe(true);
    
  967. 
    
  968.         if (nextProps !== this.props) {
    
  969.           propChanges++;
    
  970.         }
    
  971. 
    
  972.         if (nextContext !== this.context) {
    
  973.           contextChanges++;
    
  974.         }
    
  975.       }
    
  976. 
    
  977.       render() {
    
  978.         return <span className="grand-child">{this.props.children}</span>;
    
  979.       }
    
  980.     }
    
  981. 
    
  982.     class ChildWithContext extends React.Component {
    
  983.       static contextTypes = {
    
  984.         foo: PropTypes.string.isRequired,
    
  985.       };
    
  986. 
    
  987.       UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
    
  988.         expect('foo' in nextContext).toBe(true);
    
  989. 
    
  990.         if (nextProps !== this.props) {
    
  991.           propChanges++;
    
  992.         }
    
  993. 
    
  994.         if (nextContext !== this.context) {
    
  995.           contextChanges++;
    
  996.         }
    
  997.       }
    
  998. 
    
  999.       render() {
    
  1000.         return <div className="child-with">{this.props.children}</div>;
    
  1001.       }
    
  1002.     }
    
  1003. 
    
  1004.     class ChildWithoutContext extends React.Component {
    
  1005.       UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
    
  1006.         expect('foo' in nextContext).toBe(false);
    
  1007. 
    
  1008.         if (nextProps !== this.props) {
    
  1009.           propChanges++;
    
  1010.         }
    
  1011. 
    
  1012.         if (nextContext !== this.context) {
    
  1013.           contextChanges++;
    
  1014.         }
    
  1015.       }
    
  1016. 
    
  1017.       render() {
    
  1018.         return <div className="child-without">{this.props.children}</div>;
    
  1019.       }
    
  1020.     }
    
  1021. 
    
  1022.     class Parent extends React.Component {
    
  1023.       static childContextTypes = {
    
  1024.         foo: PropTypes.string,
    
  1025.       };
    
  1026. 
    
  1027.       state = {
    
  1028.         foo: 'abc',
    
  1029.       };
    
  1030. 
    
  1031.       getChildContext() {
    
  1032.         return {
    
  1033.           foo: this.state.foo,
    
  1034.         };
    
  1035.       }
    
  1036. 
    
  1037.       render() {
    
  1038.         return <div className="parent">{this.props.children}</div>;
    
  1039.       }
    
  1040.     }
    
  1041. 
    
  1042.     const div = document.createElement('div');
    
  1043. 
    
  1044.     let parentInstance = null;
    
  1045.     ReactDOM.render(
    
  1046.       <Parent ref={inst => (parentInstance = inst)}>
    
  1047.         <ChildWithoutContext>
    
  1048.           A1
    
  1049.           <GrandChild>A2</GrandChild>
    
  1050.         </ChildWithoutContext>
    
  1051. 
    
  1052.         <ChildWithContext>
    
  1053.           B1
    
  1054.           <GrandChild>B2</GrandChild>
    
  1055.         </ChildWithContext>
    
  1056.       </Parent>,
    
  1057.       div,
    
  1058.     );
    
  1059. 
    
  1060.     parentInstance.setState({
    
  1061.       foo: 'def',
    
  1062.     });
    
  1063. 
    
  1064.     expect(propChanges).toBe(0);
    
  1065.     expect(contextChanges).toBe(3); // ChildWithContext, GrandChild x 2
    
  1066.   });
    
  1067. 
    
  1068.   it('should disallow nested render calls', () => {
    
  1069.     class Inner extends React.Component {
    
  1070.       render() {
    
  1071.         return <div />;
    
  1072.       }
    
  1073.     }
    
  1074. 
    
  1075.     class Outer extends React.Component {
    
  1076.       render() {
    
  1077.         ReactTestUtils.renderIntoDocument(<Inner />);
    
  1078.         return <div />;
    
  1079.       }
    
  1080.     }
    
  1081. 
    
  1082.     expect(() => ReactTestUtils.renderIntoDocument(<Outer />)).toErrorDev(
    
  1083.       'Render methods should be a pure function of props and state; ' +
    
  1084.         'triggering nested component updates from render is not allowed. If ' +
    
  1085.         'necessary, trigger nested updates in componentDidUpdate.\n\nCheck the ' +
    
  1086.         'render method of Outer.',
    
  1087.     );
    
  1088.   });
    
  1089. 
    
  1090.   it('only renders once if updated in componentWillReceiveProps', () => {
    
  1091.     let renders = 0;
    
  1092. 
    
  1093.     class Component extends React.Component {
    
  1094.       state = {updated: false};
    
  1095. 
    
  1096.       UNSAFE_componentWillReceiveProps(props) {
    
  1097.         expect(props.update).toBe(1);
    
  1098.         expect(renders).toBe(1);
    
  1099.         this.setState({updated: true});
    
  1100.         expect(renders).toBe(1);
    
  1101.       }
    
  1102. 
    
  1103.       render() {
    
  1104.         renders++;
    
  1105.         return <div />;
    
  1106.       }
    
  1107.     }
    
  1108. 
    
  1109.     const container = document.createElement('div');
    
  1110.     const instance = ReactDOM.render(<Component update={0} />, container);
    
  1111.     expect(renders).toBe(1);
    
  1112.     expect(instance.state.updated).toBe(false);
    
  1113.     ReactDOM.render(<Component update={1} />, container);
    
  1114.     expect(renders).toBe(2);
    
  1115.     expect(instance.state.updated).toBe(true);
    
  1116.   });
    
  1117. 
    
  1118.   it('only renders once if updated in componentWillReceiveProps when batching', () => {
    
  1119.     let renders = 0;
    
  1120. 
    
  1121.     class Component extends React.Component {
    
  1122.       state = {updated: false};
    
  1123. 
    
  1124.       UNSAFE_componentWillReceiveProps(props) {
    
  1125.         expect(props.update).toBe(1);
    
  1126.         expect(renders).toBe(1);
    
  1127.         this.setState({updated: true});
    
  1128.         expect(renders).toBe(1);
    
  1129.       }
    
  1130. 
    
  1131.       render() {
    
  1132.         renders++;
    
  1133.         return <div />;
    
  1134.       }
    
  1135.     }
    
  1136. 
    
  1137.     const container = document.createElement('div');
    
  1138.     const instance = ReactDOM.render(<Component update={0} />, container);
    
  1139.     expect(renders).toBe(1);
    
  1140.     expect(instance.state.updated).toBe(false);
    
  1141.     ReactDOM.unstable_batchedUpdates(() => {
    
  1142.       ReactDOM.render(<Component update={1} />, container);
    
  1143.     });
    
  1144.     expect(renders).toBe(2);
    
  1145.     expect(instance.state.updated).toBe(true);
    
  1146.   });
    
  1147. 
    
  1148.   it('should update refs if shouldComponentUpdate gives false', () => {
    
  1149.     class Static extends React.Component {
    
  1150.       shouldComponentUpdate() {
    
  1151.         return false;
    
  1152.       }
    
  1153. 
    
  1154.       render() {
    
  1155.         return <div>{this.props.children}</div>;
    
  1156.       }
    
  1157.     }
    
  1158. 
    
  1159.     class Component extends React.Component {
    
  1160.       static0Ref = React.createRef();
    
  1161.       static1Ref = React.createRef();
    
  1162. 
    
  1163.       render() {
    
  1164.         if (this.props.flipped) {
    
  1165.           return (
    
  1166.             <div>
    
  1167.               <Static ref={this.static0Ref} key="B">
    
  1168.                 B (ignored)
    
  1169.               </Static>
    
  1170.               <Static ref={this.static1Ref} key="A">
    
  1171.                 A (ignored)
    
  1172.               </Static>
    
  1173.             </div>
    
  1174.           );
    
  1175.         } else {
    
  1176.           return (
    
  1177.             <div>
    
  1178.               <Static ref={this.static0Ref} key="A">
    
  1179.                 A
    
  1180.               </Static>
    
  1181.               <Static ref={this.static1Ref} key="B">
    
  1182.                 B
    
  1183.               </Static>
    
  1184.             </div>
    
  1185.           );
    
  1186.         }
    
  1187.       }
    
  1188.     }
    
  1189. 
    
  1190.     const container = document.createElement('div');
    
  1191.     const comp = ReactDOM.render(<Component flipped={false} />, container);
    
  1192.     expect(ReactDOM.findDOMNode(comp.static0Ref.current).textContent).toBe('A');
    
  1193.     expect(ReactDOM.findDOMNode(comp.static1Ref.current).textContent).toBe('B');
    
  1194. 
    
  1195.     // When flipping the order, the refs should update even though the actual
    
  1196.     // contents do not
    
  1197.     ReactDOM.render(<Component flipped={true} />, container);
    
  1198.     expect(ReactDOM.findDOMNode(comp.static0Ref.current).textContent).toBe('B');
    
  1199.     expect(ReactDOM.findDOMNode(comp.static1Ref.current).textContent).toBe('A');
    
  1200.   });
    
  1201. 
    
  1202.   it('should allow access to findDOMNode in componentWillUnmount', () => {
    
  1203.     let a = null;
    
  1204.     let b = null;
    
  1205. 
    
  1206.     class Component extends React.Component {
    
  1207.       componentDidMount() {
    
  1208.         a = ReactDOM.findDOMNode(this);
    
  1209.         expect(a).not.toBe(null);
    
  1210.       }
    
  1211. 
    
  1212.       componentWillUnmount() {
    
  1213.         b = ReactDOM.findDOMNode(this);
    
  1214.         expect(b).not.toBe(null);
    
  1215.       }
    
  1216. 
    
  1217.       render() {
    
  1218.         return <div />;
    
  1219.       }
    
  1220.     }
    
  1221. 
    
  1222.     const container = document.createElement('div');
    
  1223.     expect(a).toBe(container.firstChild);
    
  1224.     ReactDOM.render(<Component />, container);
    
  1225.     ReactDOM.unmountComponentAtNode(container);
    
  1226.     expect(a).toBe(b);
    
  1227.   });
    
  1228. 
    
  1229.   // @gate !disableLegacyContext || !__DEV__
    
  1230.   it('context should be passed down from the parent', () => {
    
  1231.     class Parent extends React.Component {
    
  1232.       static childContextTypes = {
    
  1233.         foo: PropTypes.string,
    
  1234.       };
    
  1235. 
    
  1236.       getChildContext() {
    
  1237.         return {
    
  1238.           foo: 'bar',
    
  1239.         };
    
  1240.       }
    
  1241. 
    
  1242.       render() {
    
  1243.         return <div>{this.props.children}</div>;
    
  1244.       }
    
  1245.     }
    
  1246. 
    
  1247.     class Component extends React.Component {
    
  1248.       static contextTypes = {
    
  1249.         foo: PropTypes.string.isRequired,
    
  1250.       };
    
  1251. 
    
  1252.       render() {
    
  1253.         return <div />;
    
  1254.       }
    
  1255.     }
    
  1256. 
    
  1257.     const div = document.createElement('div');
    
  1258.     ReactDOM.render(
    
  1259.       <Parent>
    
  1260.         <Component />
    
  1261.       </Parent>,
    
  1262.       div,
    
  1263.     );
    
  1264.   });
    
  1265. 
    
  1266.   it('should replace state', () => {
    
  1267.     class Moo extends React.Component {
    
  1268.       state = {x: 1};
    
  1269.       render() {
    
  1270.         return <div />;
    
  1271.       }
    
  1272.     }
    
  1273. 
    
  1274.     const moo = ReactTestUtils.renderIntoDocument(<Moo />);
    
  1275.     // No longer a public API, but we can test that it works internally by
    
  1276.     // reaching into the updater.
    
  1277.     moo.updater.enqueueReplaceState(moo, {y: 2});
    
  1278.     expect('x' in moo.state).toBe(false);
    
  1279.     expect(moo.state.y).toBe(2);
    
  1280.   });
    
  1281. 
    
  1282.   it('should support objects with prototypes as state', () => {
    
  1283.     const NotActuallyImmutable = function (str) {
    
  1284.       this.str = str;
    
  1285.     };
    
  1286.     NotActuallyImmutable.prototype.amIImmutable = function () {
    
  1287.       return true;
    
  1288.     };
    
  1289.     class Moo extends React.Component {
    
  1290.       state = new NotActuallyImmutable('first');
    
  1291.       // No longer a public API, but we can test that it works internally by
    
  1292.       // reaching into the updater.
    
  1293.       _replaceState = update => this.updater.enqueueReplaceState(this, update);
    
  1294.       render() {
    
  1295.         return <div />;
    
  1296.       }
    
  1297.     }
    
  1298. 
    
  1299.     const moo = ReactTestUtils.renderIntoDocument(<Moo />);
    
  1300.     expect(moo.state.str).toBe('first');
    
  1301.     expect(moo.state.amIImmutable()).toBe(true);
    
  1302. 
    
  1303.     const secondState = new NotActuallyImmutable('second');
    
  1304.     moo._replaceState(secondState);
    
  1305.     expect(moo.state.str).toBe('second');
    
  1306.     expect(moo.state.amIImmutable()).toBe(true);
    
  1307.     expect(moo.state).toBe(secondState);
    
  1308. 
    
  1309.     moo.setState({str: 'third'});
    
  1310.     expect(moo.state.str).toBe('third');
    
  1311.     // Here we lose the prototype.
    
  1312.     expect(moo.state.amIImmutable).toBe(undefined);
    
  1313. 
    
  1314.     // When more than one state update is enqueued, we have the same behavior
    
  1315.     const fifthState = new NotActuallyImmutable('fifth');
    
  1316.     ReactDOM.unstable_batchedUpdates(function () {
    
  1317.       moo.setState({str: 'fourth'});
    
  1318.       moo._replaceState(fifthState);
    
  1319.     });
    
  1320.     expect(moo.state).toBe(fifthState);
    
  1321. 
    
  1322.     // When more than one state update is enqueued, we have the same behavior
    
  1323.     const sixthState = new NotActuallyImmutable('sixth');
    
  1324.     ReactDOM.unstable_batchedUpdates(function () {
    
  1325.       moo._replaceState(sixthState);
    
  1326.       moo.setState({str: 'seventh'});
    
  1327.     });
    
  1328.     expect(moo.state.str).toBe('seventh');
    
  1329.     expect(moo.state.amIImmutable).toBe(undefined);
    
  1330.   });
    
  1331. 
    
  1332.   it('should not warn about unmounting during unmounting', () => {
    
  1333.     const container = document.createElement('div');
    
  1334.     const layer = document.createElement('div');
    
  1335. 
    
  1336.     class Component extends React.Component {
    
  1337.       componentDidMount() {
    
  1338.         ReactDOM.render(<div />, layer);
    
  1339.       }
    
  1340. 
    
  1341.       componentWillUnmount() {
    
  1342.         ReactDOM.unmountComponentAtNode(layer);
    
  1343.       }
    
  1344. 
    
  1345.       render() {
    
  1346.         return <div />;
    
  1347.       }
    
  1348.     }
    
  1349. 
    
  1350.     class Outer extends React.Component {
    
  1351.       render() {
    
  1352.         return <div>{this.props.children}</div>;
    
  1353.       }
    
  1354.     }
    
  1355. 
    
  1356.     ReactDOM.render(
    
  1357.       <Outer>
    
  1358.         <Component />
    
  1359.       </Outer>,
    
  1360.       container,
    
  1361.     );
    
  1362.     ReactDOM.render(<Outer />, container);
    
  1363.   });
    
  1364. 
    
  1365.   it('should warn when mutated props are passed', () => {
    
  1366.     const container = document.createElement('div');
    
  1367. 
    
  1368.     class Foo extends React.Component {
    
  1369.       constructor(props) {
    
  1370.         const _props = {idx: props.idx + '!'};
    
  1371.         super(_props);
    
  1372.       }
    
  1373. 
    
  1374.       render() {
    
  1375.         return <span />;
    
  1376.       }
    
  1377.     }
    
  1378. 
    
  1379.     expect(() => ReactDOM.render(<Foo idx="qwe" />, container)).toErrorDev(
    
  1380.       'Foo(...): When calling super() in `Foo`, make sure to pass ' +
    
  1381.         "up the same props that your component's constructor was passed.",
    
  1382.     );
    
  1383.   });
    
  1384. 
    
  1385.   it('should only call componentWillUnmount once', () => {
    
  1386.     let app;
    
  1387.     let count = 0;
    
  1388. 
    
  1389.     class App extends React.Component {
    
  1390.       render() {
    
  1391.         if (this.props.stage === 1) {
    
  1392.           return <UnunmountableComponent />;
    
  1393.         } else {
    
  1394.           return null;
    
  1395.         }
    
  1396.       }
    
  1397.     }
    
  1398. 
    
  1399.     class UnunmountableComponent extends React.Component {
    
  1400.       componentWillUnmount() {
    
  1401.         app.setState({});
    
  1402.         count++;
    
  1403.         throw Error('always fails');
    
  1404.       }
    
  1405. 
    
  1406.       render() {
    
  1407.         return <div>Hello {this.props.name}</div>;
    
  1408.       }
    
  1409.     }
    
  1410. 
    
  1411.     const container = document.createElement('div');
    
  1412. 
    
  1413.     const setRef = ref => {
    
  1414.       if (ref) {
    
  1415.         app = ref;
    
  1416.       }
    
  1417.     };
    
  1418. 
    
  1419.     expect(() => {
    
  1420.       ReactDOM.render(<App ref={setRef} stage={1} />, container);
    
  1421.       ReactDOM.render(<App ref={setRef} stage={2} />, container);
    
  1422.     }).toThrow();
    
  1423.     expect(count).toBe(1);
    
  1424.   });
    
  1425. 
    
  1426.   it('prepares new child before unmounting old', () => {
    
  1427.     const log = [];
    
  1428. 
    
  1429.     class Spy extends React.Component {
    
  1430.       UNSAFE_componentWillMount() {
    
  1431.         log.push(this.props.name + ' componentWillMount');
    
  1432.       }
    
  1433.       render() {
    
  1434.         log.push(this.props.name + ' render');
    
  1435.         return <div />;
    
  1436.       }
    
  1437.       componentDidMount() {
    
  1438.         log.push(this.props.name + ' componentDidMount');
    
  1439.       }
    
  1440.       componentWillUnmount() {
    
  1441.         log.push(this.props.name + ' componentWillUnmount');
    
  1442.       }
    
  1443.     }
    
  1444. 
    
  1445.     class Wrapper extends React.Component {
    
  1446.       render() {
    
  1447.         return <Spy key={this.props.name} name={this.props.name} />;
    
  1448.       }
    
  1449.     }
    
  1450. 
    
  1451.     const container = document.createElement('div');
    
  1452.     ReactDOM.render(<Wrapper name="A" />, container);
    
  1453.     ReactDOM.render(<Wrapper name="B" />, container);
    
  1454. 
    
  1455.     expect(log).toEqual([
    
  1456.       'A componentWillMount',
    
  1457.       'A render',
    
  1458.       'A componentDidMount',
    
  1459. 
    
  1460.       'B componentWillMount',
    
  1461.       'B render',
    
  1462.       'A componentWillUnmount',
    
  1463.       'B componentDidMount',
    
  1464.     ]);
    
  1465.   });
    
  1466. 
    
  1467.   it('respects a shallow shouldComponentUpdate implementation', () => {
    
  1468.     let renderCalls = 0;
    
  1469.     class PlasticWrap extends React.Component {
    
  1470.       constructor(props, context) {
    
  1471.         super(props, context);
    
  1472.         this.state = {
    
  1473.           color: 'green',
    
  1474.         };
    
  1475.         this.appleRef = React.createRef();
    
  1476.       }
    
  1477. 
    
  1478.       render() {
    
  1479.         return <Apple color={this.state.color} ref={this.appleRef} />;
    
  1480.       }
    
  1481.     }
    
  1482. 
    
  1483.     class Apple extends React.Component {
    
  1484.       state = {
    
  1485.         cut: false,
    
  1486.         slices: 1,
    
  1487.       };
    
  1488. 
    
  1489.       shouldComponentUpdate(nextProps, nextState) {
    
  1490.         return shallowCompare(this, nextProps, nextState);
    
  1491.       }
    
  1492. 
    
  1493.       cut() {
    
  1494.         this.setState({
    
  1495.           cut: true,
    
  1496.           slices: 10,
    
  1497.         });
    
  1498.       }
    
  1499. 
    
  1500.       eatSlice() {
    
  1501.         this.setState({
    
  1502.           slices: this.state.slices - 1,
    
  1503.         });
    
  1504.       }
    
  1505. 
    
  1506.       render() {
    
  1507.         renderCalls++;
    
  1508.         return <div />;
    
  1509.       }
    
  1510.     }
    
  1511. 
    
  1512.     const container = document.createElement('div');
    
  1513.     const instance = ReactDOM.render(<PlasticWrap />, container);
    
  1514.     expect(renderCalls).toBe(1);
    
  1515. 
    
  1516.     // Do not re-render based on props
    
  1517.     instance.setState({color: 'green'});
    
  1518.     expect(renderCalls).toBe(1);
    
  1519. 
    
  1520.     // Re-render based on props
    
  1521.     instance.setState({color: 'red'});
    
  1522.     expect(renderCalls).toBe(2);
    
  1523. 
    
  1524.     // Re-render base on state
    
  1525.     instance.appleRef.current.cut();
    
  1526.     expect(renderCalls).toBe(3);
    
  1527. 
    
  1528.     // No re-render based on state
    
  1529.     instance.appleRef.current.cut();
    
  1530.     expect(renderCalls).toBe(3);
    
  1531. 
    
  1532.     // Re-render based on state again
    
  1533.     instance.appleRef.current.eatSlice();
    
  1534.     expect(renderCalls).toBe(4);
    
  1535.   });
    
  1536. 
    
  1537.   it('does not do a deep comparison for a shallow shouldComponentUpdate implementation', () => {
    
  1538.     function getInitialState() {
    
  1539.       return {
    
  1540.         foo: [1, 2, 3],
    
  1541.         bar: {a: 4, b: 5, c: 6},
    
  1542.       };
    
  1543.     }
    
  1544. 
    
  1545.     let renderCalls = 0;
    
  1546.     const initialSettings = getInitialState();
    
  1547. 
    
  1548.     class Component extends React.Component {
    
  1549.       state = initialSettings;
    
  1550. 
    
  1551.       shouldComponentUpdate(nextProps, nextState) {
    
  1552.         return shallowCompare(this, nextProps, nextState);
    
  1553.       }
    
  1554. 
    
  1555.       render() {
    
  1556.         renderCalls++;
    
  1557.         return <div />;
    
  1558.       }
    
  1559.     }
    
  1560. 
    
  1561.     const container = document.createElement('div');
    
  1562.     const instance = ReactDOM.render(<Component />, container);
    
  1563.     expect(renderCalls).toBe(1);
    
  1564. 
    
  1565.     // Do not re-render if state is equal
    
  1566.     const settings = {
    
  1567.       foo: initialSettings.foo,
    
  1568.       bar: initialSettings.bar,
    
  1569.     };
    
  1570.     instance.setState(settings);
    
  1571.     expect(renderCalls).toBe(1);
    
  1572. 
    
  1573.     // Re-render because one field changed
    
  1574.     initialSettings.foo = [1, 2, 3];
    
  1575.     instance.setState(initialSettings);
    
  1576.     expect(renderCalls).toBe(2);
    
  1577. 
    
  1578.     // Re-render because the object changed
    
  1579.     instance.setState(getInitialState());
    
  1580.     expect(renderCalls).toBe(3);
    
  1581.   });
    
  1582. 
    
  1583.   it('should call setState callback with no arguments', () => {
    
  1584.     let mockArgs;
    
  1585.     class Component extends React.Component {
    
  1586.       componentDidMount() {
    
  1587.         this.setState({}, (...args) => (mockArgs = args));
    
  1588.       }
    
  1589.       render() {
    
  1590.         return false;
    
  1591.       }
    
  1592.     }
    
  1593. 
    
  1594.     ReactTestUtils.renderIntoDocument(<Component />);
    
  1595.     expect(mockArgs.length).toEqual(0);
    
  1596.   });
    
  1597. 
    
  1598.   it('this.state should be updated on setState callback inside componentWillMount', () => {
    
  1599.     const div = document.createElement('div');
    
  1600.     let stateSuccessfullyUpdated = false;
    
  1601. 
    
  1602.     class Component extends React.Component {
    
  1603.       constructor(props, context) {
    
  1604.         super(props, context);
    
  1605.         this.state = {
    
  1606.           hasUpdatedState: false,
    
  1607.         };
    
  1608.       }
    
  1609. 
    
  1610.       UNSAFE_componentWillMount() {
    
  1611.         this.setState(
    
  1612.           {hasUpdatedState: true},
    
  1613.           () => (stateSuccessfullyUpdated = this.state.hasUpdatedState),
    
  1614.         );
    
  1615.       }
    
  1616. 
    
  1617.       render() {
    
  1618.         return <div>{this.props.children}</div>;
    
  1619.       }
    
  1620.     }
    
  1621. 
    
  1622.     ReactDOM.render(<Component />, div);
    
  1623.     expect(stateSuccessfullyUpdated).toBe(true);
    
  1624.   });
    
  1625. 
    
  1626.   it('should call the setState callback even if shouldComponentUpdate = false', done => {
    
  1627.     const mockFn = jest.fn().mockReturnValue(false);
    
  1628.     const div = document.createElement('div');
    
  1629. 
    
  1630.     let instance;
    
  1631. 
    
  1632.     class Component extends React.Component {
    
  1633.       constructor(props, context) {
    
  1634.         super(props, context);
    
  1635.         this.state = {
    
  1636.           hasUpdatedState: false,
    
  1637.         };
    
  1638.       }
    
  1639. 
    
  1640.       UNSAFE_componentWillMount() {
    
  1641.         instance = this;
    
  1642.       }
    
  1643. 
    
  1644.       shouldComponentUpdate() {
    
  1645.         return mockFn();
    
  1646.       }
    
  1647. 
    
  1648.       render() {
    
  1649.         return <div>{this.state.hasUpdatedState}</div>;
    
  1650.       }
    
  1651.     }
    
  1652. 
    
  1653.     ReactDOM.render(<Component />, div);
    
  1654. 
    
  1655.     expect(instance).toBeDefined();
    
  1656.     expect(mockFn).not.toBeCalled();
    
  1657. 
    
  1658.     instance.setState({hasUpdatedState: true}, () => {
    
  1659.       expect(mockFn).toBeCalled();
    
  1660.       expect(instance.state.hasUpdatedState).toBe(true);
    
  1661.       done();
    
  1662.     });
    
  1663.   });
    
  1664. 
    
  1665.   it('should return a meaningful warning when constructor is returned', () => {
    
  1666.     class RenderTextInvalidConstructor extends React.Component {
    
  1667.       constructor(props) {
    
  1668.         super(props);
    
  1669.         return {something: false};
    
  1670.       }
    
  1671. 
    
  1672.       render() {
    
  1673.         return <div />;
    
  1674.       }
    
  1675.     }
    
  1676. 
    
  1677.     expect(() => {
    
  1678.       expect(() => {
    
  1679.         ReactTestUtils.renderIntoDocument(<RenderTextInvalidConstructor />);
    
  1680.       }).toThrow();
    
  1681.     }).toErrorDev([
    
  1682.       // Expect two errors because invokeGuardedCallback will dispatch an error event,
    
  1683.       // Causing the warning to be logged again.
    
  1684.       'Warning: RenderTextInvalidConstructor(...): No `render` method found on the returned component instance: ' +
    
  1685.         'did you accidentally return an object from the constructor?',
    
  1686.       'Warning: RenderTextInvalidConstructor(...): No `render` method found on the returned component instance: ' +
    
  1687.         'did you accidentally return an object from the constructor?',
    
  1688.     ]);
    
  1689.   });
    
  1690. 
    
  1691.   it('should warn about reassigning this.props while rendering', () => {
    
  1692.     class Bad extends React.Component {
    
  1693.       componentDidMount() {}
    
  1694.       componentDidUpdate() {}
    
  1695.       render() {
    
  1696.         this.props = {...this.props};
    
  1697.         return null;
    
  1698.       }
    
  1699.     }
    
  1700. 
    
  1701.     const container = document.createElement('div');
    
  1702.     expect(() => {
    
  1703.       ReactDOM.render(<Bad />, container);
    
  1704.     }).toErrorDev(
    
  1705.       'It looks like Bad is reassigning its own `this.props` while rendering. ' +
    
  1706.         'This is not supported and can lead to confusing bugs.',
    
  1707.     );
    
  1708.   });
    
  1709. 
    
  1710.   it('should return error if render is not defined', () => {
    
  1711.     class RenderTestUndefinedRender extends React.Component {}
    
  1712. 
    
  1713.     expect(() => {
    
  1714.       expect(() => {
    
  1715.         ReactTestUtils.renderIntoDocument(<RenderTestUndefinedRender />);
    
  1716.       }).toThrow();
    
  1717.     }).toErrorDev([
    
  1718.       // Expect two errors because invokeGuardedCallback will dispatch an error event,
    
  1719.       // Causing the warning to be logged again.
    
  1720.       'Warning: RenderTestUndefinedRender(...): No `render` method found on the returned ' +
    
  1721.         'component instance: you may have forgotten to define `render`.',
    
  1722.       'Warning: RenderTestUndefinedRender(...): No `render` method found on the returned ' +
    
  1723.         'component instance: you may have forgotten to define `render`.',
    
  1724.     ]);
    
  1725.   });
    
  1726. 
    
  1727.   // Regression test for accidental breaking change
    
  1728.   // https://github.com/facebook/react/issues/13580
    
  1729.   it('should support classes shadowing isReactComponent', () => {
    
  1730.     class Shadow extends React.Component {
    
  1731.       isReactComponent() {}
    
  1732.       render() {
    
  1733.         return <div />;
    
  1734.       }
    
  1735.     }
    
  1736.     const container = document.createElement('div');
    
  1737.     ReactDOM.render(<Shadow />, container);
    
  1738.     expect(container.firstChild.tagName).toBe('DIV');
    
  1739.   });
    
  1740. 
    
  1741.   it('should not warn on updating function component from componentWillMount', () => {
    
  1742.     let _setState;
    
  1743.     function A() {
    
  1744.       _setState = React.useState()[1];
    
  1745.       return null;
    
  1746.     }
    
  1747.     class B extends React.Component {
    
  1748.       UNSAFE_componentWillMount() {
    
  1749.         _setState({});
    
  1750.       }
    
  1751.       render() {
    
  1752.         return null;
    
  1753.       }
    
  1754.     }
    
  1755.     function Parent() {
    
  1756.       return (
    
  1757.         <div>
    
  1758.           <A />
    
  1759.           <B />
    
  1760.         </div>
    
  1761.       );
    
  1762.     }
    
  1763.     const container = document.createElement('div');
    
  1764.     ReactDOM.render(<Parent />, container);
    
  1765.   });
    
  1766. 
    
  1767.   it('should not warn on updating function component from componentWillUpdate', () => {
    
  1768.     let _setState;
    
  1769.     function A() {
    
  1770.       _setState = React.useState()[1];
    
  1771.       return null;
    
  1772.     }
    
  1773.     class B extends React.Component {
    
  1774.       UNSAFE_componentWillUpdate() {
    
  1775.         _setState({});
    
  1776.       }
    
  1777.       render() {
    
  1778.         return null;
    
  1779.       }
    
  1780.     }
    
  1781.     function Parent() {
    
  1782.       return (
    
  1783.         <div>
    
  1784.           <A />
    
  1785.           <B />
    
  1786.         </div>
    
  1787.       );
    
  1788.     }
    
  1789.     const container = document.createElement('div');
    
  1790.     ReactDOM.render(<Parent />, container);
    
  1791.     ReactDOM.render(<Parent />, container);
    
  1792.   });
    
  1793. 
    
  1794.   it('should not warn on updating function component from componentWillReceiveProps', () => {
    
  1795.     let _setState;
    
  1796.     function A() {
    
  1797.       _setState = React.useState()[1];
    
  1798.       return null;
    
  1799.     }
    
  1800.     class B extends React.Component {
    
  1801.       UNSAFE_componentWillReceiveProps() {
    
  1802.         _setState({});
    
  1803.       }
    
  1804.       render() {
    
  1805.         return null;
    
  1806.       }
    
  1807.     }
    
  1808.     function Parent() {
    
  1809.       return (
    
  1810.         <div>
    
  1811.           <A />
    
  1812.           <B />
    
  1813.         </div>
    
  1814.       );
    
  1815.     }
    
  1816.     const container = document.createElement('div');
    
  1817.     ReactDOM.render(<Parent />, container);
    
  1818.     ReactDOM.render(<Parent />, container);
    
  1819.   });
    
  1820. 
    
  1821.   it('should warn on updating function component from render', () => {
    
  1822.     let _setState;
    
  1823.     function A() {
    
  1824.       _setState = React.useState()[1];
    
  1825.       return null;
    
  1826.     }
    
  1827.     class B extends React.Component {
    
  1828.       render() {
    
  1829.         _setState({});
    
  1830.         return null;
    
  1831.       }
    
  1832.     }
    
  1833.     function Parent() {
    
  1834.       return (
    
  1835.         <div>
    
  1836.           <A />
    
  1837.           <B />
    
  1838.         </div>
    
  1839.       );
    
  1840.     }
    
  1841.     const container = document.createElement('div');
    
  1842.     expect(() => {
    
  1843.       ReactDOM.render(<Parent />, container);
    
  1844.     }).toErrorDev(
    
  1845.       'Cannot update a component (`A`) while rendering a different component (`B`)',
    
  1846.     );
    
  1847.     // Dedupe.
    
  1848.     ReactDOM.render(<Parent />, container);
    
  1849.   });
    
  1850. });