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. // These tests are based on ReactJSXElement-test,
    
  11. // ReactJSXElementValidator-test, ReactComponent-test,
    
  12. // and ReactElementJSX-test.
    
  13. 
    
  14. jest.mock('react/jsx-runtime', () => require('./jsx-runtime'), {virtual: true});
    
  15. jest.mock('react/jsx-dev-runtime', () => require('./jsx-dev-runtime'), {
    
  16.   virtual: true,
    
  17. });
    
  18. 
    
  19. let React = require('react');
    
  20. let ReactDOM = require('react-dom');
    
  21. let ReactTestUtils = {
    
  22.   renderIntoDocument(el) {
    
  23.     const container = document.createElement('div');
    
  24.     return ReactDOM.render(el, container);
    
  25.   },
    
  26. };
    
  27. let PropTypes = React.PropTypes;
    
  28. let Component = class Component extends React.Component {
    
  29.   render() {
    
  30.     return <div />;
    
  31.   }
    
  32. };
    
  33. let RequiredPropComponent = class extends React.Component {
    
  34.   render() {
    
  35.     return <span>{this.props.prop}</span>;
    
  36.   }
    
  37. };
    
  38. RequiredPropComponent.displayName = 'RequiredPropComponent';
    
  39. RequiredPropComponent.propTypes = {prop: PropTypes.string.isRequired};
    
  40. 
    
  41. it('works', () => {
    
  42.   const container = document.createElement('div');
    
  43.   ReactDOM.render(<h1>hello</h1>, container);
    
  44.   expect(container.textContent).toBe('hello');
    
  45. });
    
  46. 
    
  47. it('returns a complete element according to spec', () => {
    
  48.   const element = <Component />;
    
  49.   expect(element.type).toBe(Component);
    
  50.   expect(element.key).toBe(null);
    
  51.   expect(element.ref).toBe(null);
    
  52.   const expectation = {};
    
  53.   Object.freeze(expectation);
    
  54.   expect(element.props).toEqual(expectation);
    
  55. });
    
  56. 
    
  57. it('allows a lower-case to be passed as the string type', () => {
    
  58.   const element = <div />;
    
  59.   expect(element.type).toBe('div');
    
  60.   expect(element.key).toBe(null);
    
  61.   expect(element.ref).toBe(null);
    
  62.   const expectation = {};
    
  63.   Object.freeze(expectation);
    
  64.   expect(element.props).toEqual(expectation);
    
  65. });
    
  66. 
    
  67. it('allows a string to be passed as the type', () => {
    
  68.   const TagName = 'div';
    
  69.   const element = <TagName />;
    
  70.   expect(element.type).toBe('div');
    
  71.   expect(element.key).toBe(null);
    
  72.   expect(element.ref).toBe(null);
    
  73.   const expectation = {};
    
  74.   Object.freeze(expectation);
    
  75.   expect(element.props).toEqual(expectation);
    
  76. });
    
  77. 
    
  78. it('returns an immutable element', () => {
    
  79.   const element = <Component />;
    
  80.   if (process.env.NODE_ENV === 'development') {
    
  81.     expect(() => (element.type = 'div')).toThrow();
    
  82.   } else {
    
  83.     expect(() => (element.type = 'div')).not.toThrow();
    
  84.   }
    
  85. });
    
  86. 
    
  87. it('does not reuse the object that is spread into props', () => {
    
  88.   const config = {foo: 1};
    
  89.   const element = <Component {...config} />;
    
  90.   expect(element.props.foo).toBe(1);
    
  91.   config.foo = 2;
    
  92.   expect(element.props.foo).toBe(1);
    
  93. });
    
  94. 
    
  95. it('extracts key and ref from the rest of the props', () => {
    
  96.   const element = <Component key="12" ref="34" foo="56" />;
    
  97.   expect(element.type).toBe(Component);
    
  98.   expect(element.key).toBe('12');
    
  99.   expect(element.ref).toBe('34');
    
  100.   const expectation = {foo: '56'};
    
  101.   Object.freeze(expectation);
    
  102.   expect(element.props).toEqual(expectation);
    
  103. });
    
  104. 
    
  105. it('coerces the key to a string', () => {
    
  106.   const element = <Component key={12} foo="56" />;
    
  107.   expect(element.type).toBe(Component);
    
  108.   expect(element.key).toBe('12');
    
  109.   expect(element.ref).toBe(null);
    
  110.   const expectation = {foo: '56'};
    
  111.   Object.freeze(expectation);
    
  112.   expect(element.props).toEqual(expectation);
    
  113. });
    
  114. 
    
  115. it('merges JSX children onto the children prop', () => {
    
  116.   const a = 1;
    
  117.   const element = <Component children="text">{a}</Component>;
    
  118.   expect(element.props.children).toBe(a);
    
  119. });
    
  120. 
    
  121. it('does not override children if no JSX children are provided', () => {
    
  122.   const element = <Component children="text" />;
    
  123.   expect(element.props.children).toBe('text');
    
  124. });
    
  125. 
    
  126. it('overrides children if null is provided as a JSX child', () => {
    
  127.   const element = <Component children="text">{null}</Component>;
    
  128.   expect(element.props.children).toBe(null);
    
  129. });
    
  130. 
    
  131. it('overrides children if undefined is provided as an argument', () => {
    
  132.   const element = <Component children="text">{undefined}</Component>;
    
  133.   expect(element.props.children).toBe(undefined);
    
  134. 
    
  135.   const element2 = React.cloneElement(
    
  136.     <Component children="text" />,
    
  137.     {},
    
  138.     undefined
    
  139.   );
    
  140.   expect(element2.props.children).toBe(undefined);
    
  141. });
    
  142. 
    
  143. it('merges JSX children onto the children prop in an array', () => {
    
  144.   const a = 1;
    
  145.   const b = 2;
    
  146.   const c = 3;
    
  147.   const element = (
    
  148.     <Component>
    
  149.       {a}
    
  150.       {b}
    
  151.       {c}
    
  152.     </Component>
    
  153.   );
    
  154.   expect(element.props.children).toEqual([1, 2, 3]);
    
  155. });
    
  156. 
    
  157. it('allows static methods to be called using the type property', () => {
    
  158.   class StaticMethodComponent {
    
  159.     static someStaticMethod() {
    
  160.       return 'someReturnValue';
    
  161.     }
    
  162.     render() {
    
  163.       return <div />;
    
  164.     }
    
  165.   }
    
  166. 
    
  167.   const element = <StaticMethodComponent />;
    
  168.   expect(element.type.someStaticMethod()).toBe('someReturnValue');
    
  169. });
    
  170. 
    
  171. it('identifies valid elements', () => {
    
  172.   expect(React.isValidElement(<div />)).toEqual(true);
    
  173.   expect(React.isValidElement(<Component />)).toEqual(true);
    
  174. 
    
  175.   expect(React.isValidElement(null)).toEqual(false);
    
  176.   expect(React.isValidElement(true)).toEqual(false);
    
  177.   expect(React.isValidElement({})).toEqual(false);
    
  178.   expect(React.isValidElement('string')).toEqual(false);
    
  179.   expect(React.isValidElement(Component)).toEqual(false);
    
  180.   expect(React.isValidElement({type: 'div', props: {}})).toEqual(false);
    
  181. });
    
  182. 
    
  183. it('is indistinguishable from a plain object', () => {
    
  184.   const element = <div className="foo" />;
    
  185.   const object = {};
    
  186.   expect(element.constructor).toBe(object.constructor);
    
  187. });
    
  188. 
    
  189. it('should use default prop value when removing a prop', () => {
    
  190.   Component.defaultProps = {fruit: 'persimmon'};
    
  191. 
    
  192.   const container = document.createElement('div');
    
  193.   const instance = ReactDOM.render(<Component fruit="mango" />, container);
    
  194.   expect(instance.props.fruit).toBe('mango');
    
  195. 
    
  196.   ReactDOM.render(<Component />, container);
    
  197.   expect(instance.props.fruit).toBe('persimmon');
    
  198. });
    
  199. 
    
  200. it('should normalize props with default values', () => {
    
  201.   class NormalizingComponent extends React.Component {
    
  202.     render() {
    
  203.       return <span>{this.props.prop}</span>;
    
  204.     }
    
  205.   }
    
  206.   NormalizingComponent.defaultProps = {prop: 'testKey'};
    
  207. 
    
  208.   const container = document.createElement('div');
    
  209.   const instance = ReactDOM.render(<NormalizingComponent />, container);
    
  210.   expect(instance.props.prop).toBe('testKey');
    
  211. 
    
  212.   const inst2 = ReactDOM.render(
    
  213.     <NormalizingComponent prop={null} />,
    
  214.     container
    
  215.   );
    
  216.   expect(inst2.props.prop).toBe(null);
    
  217. });
    
  218. 
    
  219. it('warns for keys for arrays of elements in children position', () => {
    
  220.   expect(() =>
    
  221.     ReactTestUtils.renderIntoDocument(
    
  222.       <Component>{[<Component />, <Component />]}</Component>
    
  223.     )
    
  224.   ).toErrorDev('Each child in a list should have a unique "key" prop.', {
    
  225.     withoutStack: true,
    
  226.   });
    
  227. });
    
  228. 
    
  229. it('warns for keys for arrays of elements with owner info', () => {
    
  230.   class InnerComponent extends React.Component {
    
  231.     render() {
    
  232.       return <Component>{this.props.childSet}</Component>;
    
  233.     }
    
  234.   }
    
  235. 
    
  236.   class ComponentWrapper extends React.Component {
    
  237.     render() {
    
  238.       return <InnerComponent childSet={[<Component />, <Component />]} />;
    
  239.     }
    
  240.   }
    
  241. 
    
  242.   expect(() =>
    
  243.     ReactTestUtils.renderIntoDocument(<ComponentWrapper />)
    
  244.   ).toErrorDev(
    
  245.     'Each child in a list should have a unique "key" prop.' +
    
  246.       '\n\nCheck the render method of `InnerComponent`. ' +
    
  247.       'It was passed a child from ComponentWrapper. ',
    
  248.     {withoutStack: true}
    
  249.   );
    
  250. });
    
  251. 
    
  252. it('does not warn for arrays of elements with keys', () => {
    
  253.   ReactTestUtils.renderIntoDocument(
    
  254.     <Component>{[<Component key="#1" />, <Component key="#2" />]}</Component>
    
  255.   );
    
  256. });
    
  257. 
    
  258. it('does not warn for iterable elements with keys', () => {
    
  259.   const iterable = {
    
  260.     '@@iterator': function () {
    
  261.       let i = 0;
    
  262.       return {
    
  263.         next: function () {
    
  264.           const done = ++i > 2;
    
  265.           return {
    
  266.             value: done ? undefined : <Component key={'#' + i} />,
    
  267.             done: done,
    
  268.           };
    
  269.         },
    
  270.       };
    
  271.     },
    
  272.   };
    
  273. 
    
  274.   ReactTestUtils.renderIntoDocument(<Component>{iterable}</Component>);
    
  275. });
    
  276. 
    
  277. it('does not warn for numeric keys in entry iterable as a child', () => {
    
  278.   const iterable = {
    
  279.     '@@iterator': function () {
    
  280.       let i = 0;
    
  281.       return {
    
  282.         next: function () {
    
  283.           const done = ++i > 2;
    
  284.           return {value: done ? undefined : [i, <Component />], done: done};
    
  285.         },
    
  286.       };
    
  287.     },
    
  288.   };
    
  289.   iterable.entries = iterable['@@iterator'];
    
  290. 
    
  291.   ReactTestUtils.renderIntoDocument(<Component>{iterable}</Component>);
    
  292. });
    
  293. 
    
  294. it('does not warn when the element is directly as children', () => {
    
  295.   ReactTestUtils.renderIntoDocument(
    
  296.     <Component>
    
  297.       <Component />
    
  298.       <Component />
    
  299.     </Component>
    
  300.   );
    
  301. });
    
  302. 
    
  303. it('does not warn when the child array contains non-elements', () => {
    
  304.   void (<Component>{[{}, {}]}</Component>);
    
  305. });
    
  306. 
    
  307. it('should give context for PropType errors in nested components.', () => {
    
  308.   // In this test, we're making sure that if a proptype error is found in a
    
  309.   // component, we give a small hint as to which parent instantiated that
    
  310.   // component as per warnings about key usage in ReactElementValidator.
    
  311.   function MyComp({color}) {
    
  312.     return <div>My color is {color}</div>;
    
  313.   }
    
  314.   MyComp.propTypes = {
    
  315.     color: PropTypes.string,
    
  316.   };
    
  317.   class ParentComp extends React.Component {
    
  318.     render() {
    
  319.       return <MyComp color={123} />;
    
  320.     }
    
  321.   }
    
  322.   expect(() => ReactTestUtils.renderIntoDocument(<ParentComp />)).toErrorDev(
    
  323.     'Warning: Failed prop type: ' +
    
  324.       'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
    
  325.       'expected `string`.',
    
  326.     {withoutStack: true}
    
  327.   );
    
  328. });
    
  329. 
    
  330. it('gives a helpful error when passing null, undefined, or boolean', () => {
    
  331.   const Undefined = undefined;
    
  332.   const Null = null;
    
  333.   const True = true;
    
  334.   const Div = 'div';
    
  335.   expect(() => void (<Undefined />)).toErrorDev(
    
  336.     'Warning: React.jsx: type is invalid -- expected a string ' +
    
  337.       '(for built-in components) or a class/function (for composite ' +
    
  338.       'components) but got: undefined. You likely forgot to export your ' +
    
  339.       "component from the file it's defined in, or you might have mixed up " +
    
  340.       'default and named imports.' +
    
  341.       (process.env.BABEL_ENV === 'development'
    
  342.         ? '\n\nCheck your code at **.'
    
  343.         : ''),
    
  344.     {withoutStack: true}
    
  345.   );
    
  346.   expect(() => void (<Null />)).toErrorDev(
    
  347.     'Warning: React.jsx: type is invalid -- expected a string ' +
    
  348.       '(for built-in components) or a class/function (for composite ' +
    
  349.       'components) but got: null.' +
    
  350.       (process.env.BABEL_ENV === 'development'
    
  351.         ? '\n\nCheck your code at **.'
    
  352.         : ''),
    
  353.     {withoutStack: true}
    
  354.   );
    
  355.   expect(() => void (<True />)).toErrorDev(
    
  356.     'Warning: React.jsx: type is invalid -- expected a string ' +
    
  357.       '(for built-in components) or a class/function (for composite ' +
    
  358.       'components) but got: boolean.' +
    
  359.       (process.env.BABEL_ENV === 'development'
    
  360.         ? '\n\nCheck your code at **.'
    
  361.         : ''),
    
  362.     {withoutStack: true}
    
  363.   );
    
  364.   // No error expected
    
  365.   void (<Div />);
    
  366. });
    
  367. 
    
  368. it('should check default prop values', () => {
    
  369.   RequiredPropComponent.defaultProps = {prop: null};
    
  370. 
    
  371.   expect(() =>
    
  372.     ReactTestUtils.renderIntoDocument(<RequiredPropComponent />)
    
  373.   ).toErrorDev(
    
  374.     'Warning: Failed prop type: Required prop `prop` was not specified in ' +
    
  375.       '`RequiredPropComponent`.',
    
  376.     {withoutStack: true}
    
  377.   );
    
  378. });
    
  379. 
    
  380. it('should warn on invalid prop types', () => {
    
  381.   // Since there is no prevalidation step for ES6 classes, there is no hook
    
  382.   // for us to issue a warning earlier than element creation when the error
    
  383.   // actually occurs. Since this step is skipped in production, we should just
    
  384.   // warn instead of throwing for this case.
    
  385.   class NullPropTypeComponent extends React.Component {
    
  386.     render() {
    
  387.       return <span>{this.props.prop}</span>;
    
  388.     }
    
  389.   }
    
  390.   NullPropTypeComponent.propTypes = {
    
  391.     prop: null,
    
  392.   };
    
  393.   expect(() =>
    
  394.     ReactTestUtils.renderIntoDocument(<NullPropTypeComponent />)
    
  395.   ).toErrorDev(
    
  396.     'NullPropTypeComponent: prop type `prop` is invalid; it must be a ' +
    
  397.       'function, usually from the `prop-types` package,',
    
  398.     {withoutStack: true}
    
  399.   );
    
  400. });
    
  401. 
    
  402. xit('should warn on invalid context types', () => {
    
  403.   class NullContextTypeComponent extends React.Component {
    
  404.     render() {
    
  405.       return <span>{this.props.prop}</span>;
    
  406.     }
    
  407.   }
    
  408.   NullContextTypeComponent.contextTypes = {
    
  409.     prop: null,
    
  410.   };
    
  411.   expect(() =>
    
  412.     ReactTestUtils.renderIntoDocument(<NullContextTypeComponent />)
    
  413.   ).toErrorDev(
    
  414.     'NullContextTypeComponent: type `prop` is invalid; it must ' +
    
  415.       'be a function, usually from the `prop-types` package,'
    
  416.   );
    
  417. });
    
  418. 
    
  419. it('should warn if getDefaultProps is specified on the class', () => {
    
  420.   class GetDefaultPropsComponent extends React.Component {
    
  421.     render() {
    
  422.       return <span>{this.props.prop}</span>;
    
  423.     }
    
  424.   }
    
  425.   GetDefaultPropsComponent.getDefaultProps = () => ({
    
  426.     prop: 'foo',
    
  427.   });
    
  428.   expect(() =>
    
  429.     ReactTestUtils.renderIntoDocument(<GetDefaultPropsComponent />)
    
  430.   ).toErrorDev(
    
  431.     'getDefaultProps is only used on classic React.createClass definitions.' +
    
  432.       ' Use a static property named `defaultProps` instead.',
    
  433.     {withoutStack: true}
    
  434.   );
    
  435. });
    
  436. 
    
  437. it('should warn if component declares PropTypes instead of propTypes', () => {
    
  438.   class MisspelledPropTypesComponent extends React.Component {
    
  439.     render() {
    
  440.       return <span>{this.props.prop}</span>;
    
  441.     }
    
  442.   }
    
  443.   MisspelledPropTypesComponent.PropTypes = {
    
  444.     prop: PropTypes.string,
    
  445.   };
    
  446.   expect(() =>
    
  447.     ReactTestUtils.renderIntoDocument(
    
  448.       <MisspelledPropTypesComponent prop="hi" />
    
  449.     )
    
  450.   ).toErrorDev(
    
  451.     'Warning: Component MisspelledPropTypesComponent declared `PropTypes` ' +
    
  452.       'instead of `propTypes`. Did you misspell the property assignment?',
    
  453.     {withoutStack: true}
    
  454.   );
    
  455. });
    
  456. 
    
  457. // Not supported.
    
  458. xit('warns for fragments with illegal attributes', () => {
    
  459.   class Foo extends React.Component {
    
  460.     render() {
    
  461.       return <React.Fragment a={1}>hello</React.Fragment>;
    
  462.     }
    
  463.   }
    
  464. 
    
  465.   expect(() => ReactTestUtils.renderIntoDocument(<Foo />)).toErrorDev(
    
  466.     'Invalid prop `a` supplied to `React.Fragment`. React.Fragment ' +
    
  467.       'can only have `key` and `children` props.'
    
  468.   );
    
  469. });
    
  470. 
    
  471. // Not supported.
    
  472. xit('warns for fragments with refs', () => {
    
  473.   class Foo extends React.Component {
    
  474.     render() {
    
  475.       return (
    
  476.         <React.Fragment
    
  477.           ref={bar => {
    
  478.             this.foo = bar;
    
  479.           }}>
    
  480.           hello
    
  481.         </React.Fragment>
    
  482.       );
    
  483.     }
    
  484.   }
    
  485. 
    
  486.   expect(() => ReactTestUtils.renderIntoDocument(<Foo />)).toErrorDev(
    
  487.     'Invalid attribute `ref` supplied to `React.Fragment`.'
    
  488.   );
    
  489. });
    
  490. 
    
  491. // Not supported.
    
  492. xit('does not warn for fragments of multiple elements without keys', () => {
    
  493.   ReactTestUtils.renderIntoDocument(
    
  494.     <>
    
  495.       <span>1</span>
    
  496.       <span>2</span>
    
  497.     </>
    
  498.   );
    
  499. });
    
  500. 
    
  501. // Not supported.
    
  502. xit('warns for fragments of multiple elements with same key', () => {
    
  503.   expect(() =>
    
  504.     ReactTestUtils.renderIntoDocument(
    
  505.       <>
    
  506.         <span key="a">1</span>
    
  507.         <span key="a">2</span>
    
  508.         <span key="b">3</span>
    
  509.       </>
    
  510.     )
    
  511.   ).toErrorDev('Encountered two children with the same key, `a`.', {
    
  512.     withoutStack: true,
    
  513.   });
    
  514. });
    
  515. 
    
  516. // Not supported.
    
  517. xit('does not call lazy initializers eagerly', () => {
    
  518.   let didCall = false;
    
  519.   const Lazy = React.lazy(() => {
    
  520.     didCall = true;
    
  521.     return {then() {}};
    
  522.   });
    
  523.   <Lazy />;
    
  524.   expect(didCall).toBe(false);
    
  525. });
    
  526. 
    
  527. it('supports classic refs', () => {
    
  528.   class Foo extends React.Component {
    
  529.     render() {
    
  530.       return <div className="foo" ref="inner" />;
    
  531.     }
    
  532.   }
    
  533.   const container = document.createElement('div');
    
  534.   const instance = ReactDOM.render(<Foo />, container);
    
  535.   expect(instance.refs.inner.className).toBe('foo');
    
  536. });
    
  537. 
    
  538. it('should support refs on owned components', () => {
    
  539.   const innerObj = {};
    
  540.   const outerObj = {};
    
  541. 
    
  542.   class Wrapper extends React.Component {
    
  543.     getObject = () => {
    
  544.       return this.props.object;
    
  545.     };
    
  546. 
    
  547.     render() {
    
  548.       return <div>{this.props.children}</div>;
    
  549.     }
    
  550.   }
    
  551. 
    
  552.   class Component extends React.Component {
    
  553.     render() {
    
  554.       const inner = <Wrapper object={innerObj} ref="inner" />;
    
  555.       const outer = (
    
  556.         <Wrapper object={outerObj} ref="outer">
    
  557.           {inner}
    
  558.         </Wrapper>
    
  559.       );
    
  560.       return outer;
    
  561.     }
    
  562. 
    
  563.     componentDidMount() {
    
  564.       expect(this.refs.inner.getObject()).toEqual(innerObj);
    
  565.       expect(this.refs.outer.getObject()).toEqual(outerObj);
    
  566.     }
    
  567.   }
    
  568. 
    
  569.   ReactTestUtils.renderIntoDocument(<Component />);
    
  570. });
    
  571. 
    
  572. it('should support callback-style refs', () => {
    
  573.   const innerObj = {};
    
  574.   const outerObj = {};
    
  575. 
    
  576.   class Wrapper extends React.Component {
    
  577.     getObject = () => {
    
  578.       return this.props.object;
    
  579.     };
    
  580. 
    
  581.     render() {
    
  582.       return <div>{this.props.children}</div>;
    
  583.     }
    
  584.   }
    
  585. 
    
  586.   let mounted = false;
    
  587. 
    
  588.   class Component extends React.Component {
    
  589.     render() {
    
  590.       const inner = (
    
  591.         <Wrapper object={innerObj} ref={c => (this.innerRef = c)} />
    
  592.       );
    
  593.       const outer = (
    
  594.         <Wrapper object={outerObj} ref={c => (this.outerRef = c)}>
    
  595.           {inner}
    
  596.         </Wrapper>
    
  597.       );
    
  598.       return outer;
    
  599.     }
    
  600. 
    
  601.     componentDidMount() {
    
  602.       expect(this.innerRef.getObject()).toEqual(innerObj);
    
  603.       expect(this.outerRef.getObject()).toEqual(outerObj);
    
  604.       mounted = true;
    
  605.     }
    
  606.   }
    
  607. 
    
  608.   ReactTestUtils.renderIntoDocument(<Component />);
    
  609.   expect(mounted).toBe(true);
    
  610. });
    
  611. 
    
  612. // Not supported.
    
  613. xit('should support object-style refs', () => {
    
  614.   const innerObj = {};
    
  615.   const outerObj = {};
    
  616. 
    
  617.   class Wrapper extends React.Component {
    
  618.     getObject = () => {
    
  619.       return this.props.object;
    
  620.     };
    
  621. 
    
  622.     render() {
    
  623.       return <div>{this.props.children}</div>;
    
  624.     }
    
  625.   }
    
  626. 
    
  627.   let mounted = false;
    
  628. 
    
  629.   class Component extends React.Component {
    
  630.     constructor() {
    
  631.       super();
    
  632.       this.innerRef = React.createRef();
    
  633.       this.outerRef = React.createRef();
    
  634.     }
    
  635.     render() {
    
  636.       const inner = <Wrapper object={innerObj} ref={this.innerRef} />;
    
  637.       const outer = (
    
  638.         <Wrapper object={outerObj} ref={this.outerRef}>
    
  639.           {inner}
    
  640.         </Wrapper>
    
  641.       );
    
  642.       return outer;
    
  643.     }
    
  644. 
    
  645.     componentDidMount() {
    
  646.       expect(this.innerRef.current.getObject()).toEqual(innerObj);
    
  647.       expect(this.outerRef.current.getObject()).toEqual(outerObj);
    
  648.       mounted = true;
    
  649.     }
    
  650.   }
    
  651. 
    
  652.   ReactTestUtils.renderIntoDocument(<Component />);
    
  653.   expect(mounted).toBe(true);
    
  654. });
    
  655. 
    
  656. it('should support new-style refs with mixed-up owners', () => {
    
  657.   class Wrapper extends React.Component {
    
  658.     getTitle = () => {
    
  659.       return this.props.title;
    
  660.     };
    
  661. 
    
  662.     render() {
    
  663.       return this.props.getContent();
    
  664.     }
    
  665.   }
    
  666. 
    
  667.   let mounted = false;
    
  668. 
    
  669.   class Component extends React.Component {
    
  670.     getInner = () => {
    
  671.       // (With old-style refs, it's impossible to get a ref to this div
    
  672.       // because Wrapper is the current owner when this function is called.)
    
  673.       return <div className="inner" ref={c => (this.innerRef = c)} />;
    
  674.     };
    
  675. 
    
  676.     render() {
    
  677.       return (
    
  678.         <Wrapper
    
  679.           title="wrapper"
    
  680.           ref={c => (this.wrapperRef = c)}
    
  681.           getContent={this.getInner}
    
  682.         />
    
  683.       );
    
  684.     }
    
  685. 
    
  686.     componentDidMount() {
    
  687.       // Check .props.title to make sure we got the right elements back
    
  688.       expect(this.wrapperRef.getTitle()).toBe('wrapper');
    
  689.       expect(this.innerRef.className).toBe('inner');
    
  690.       mounted = true;
    
  691.     }
    
  692.   }
    
  693. 
    
  694.   ReactTestUtils.renderIntoDocument(<Component />);
    
  695.   expect(mounted).toBe(true);
    
  696. });
    
  697. 
    
  698. it('should warn when `key` is being accessed on composite element', () => {
    
  699.   const container = document.createElement('div');
    
  700.   class Child extends React.Component {
    
  701.     render() {
    
  702.       return <div> {this.props.key} </div>;
    
  703.     }
    
  704.   }
    
  705.   class Parent extends React.Component {
    
  706.     render() {
    
  707.       return (
    
  708.         <div>
    
  709.           <Child key="0" />
    
  710.           <Child key="1" />
    
  711.           <Child key="2" />
    
  712.         </div>
    
  713.       );
    
  714.     }
    
  715.   }
    
  716.   expect(() => ReactDOM.render(<Parent />, container)).toErrorDev(
    
  717.     'Child: `key` is not a prop. Trying to access it will result ' +
    
  718.       'in `undefined` being returned. If you need to access the same ' +
    
  719.       'value within the child component, you should pass it as a different ' +
    
  720.       'prop. (https://reactjs.org/link/special-props)',
    
  721.     {withoutStack: true}
    
  722.   );
    
  723. });
    
  724. 
    
  725. it('should warn when `ref` is being accessed', () => {
    
  726.   const container = document.createElement('div');
    
  727.   class Child extends React.Component {
    
  728.     render() {
    
  729.       return <div> {this.props.ref} </div>;
    
  730.     }
    
  731.   }
    
  732.   class Parent extends React.Component {
    
  733.     render() {
    
  734.       return (
    
  735.         <div>
    
  736.           <Child ref="childElement" />
    
  737.         </div>
    
  738.       );
    
  739.     }
    
  740.   }
    
  741.   expect(() => ReactDOM.render(<Parent />, container)).toErrorDev(
    
  742.     'Child: `ref` is not a prop. Trying to access it will result ' +
    
  743.       'in `undefined` being returned. If you need to access the same ' +
    
  744.       'value within the child component, you should pass it as a different ' +
    
  745.       'prop. (https://reactjs.org/link/special-props)',
    
  746.     {withoutStack: true}
    
  747.   );
    
  748. });
    
  749. 
    
  750. // Note: no warning before 16.
    
  751. it('should NOT warn when owner and self are different for string refs', () => {
    
  752.   class ClassWithRenderProp extends React.Component {
    
  753.     render() {
    
  754.       return this.props.children();
    
  755.     }
    
  756.   }
    
  757. 
    
  758.   class ClassParent extends React.Component {
    
  759.     render() {
    
  760.       return (
    
  761.         <ClassWithRenderProp>{() => <div ref="myRef" />}</ClassWithRenderProp>
    
  762.       );
    
  763.     }
    
  764.   }
    
  765. 
    
  766.   const container = document.createElement('div');
    
  767.   ReactDOM.render(<ClassParent />, container);
    
  768. });