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 = require('prop-types');
    
  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. });
    
  226. 
    
  227. it('warns for keys for arrays of elements with owner info', () => {
    
  228.   class InnerComponent extends React.Component {
    
  229.     render() {
    
  230.       return <Component>{this.props.childSet}</Component>;
    
  231.     }
    
  232.   }
    
  233. 
    
  234.   class ComponentWrapper extends React.Component {
    
  235.     render() {
    
  236.       return <InnerComponent childSet={[<Component />, <Component />]} />;
    
  237.     }
    
  238.   }
    
  239. 
    
  240.   expect(() =>
    
  241.     ReactTestUtils.renderIntoDocument(<ComponentWrapper />)
    
  242.   ).toErrorDev(
    
  243.     'Each child in a list should have a unique "key" prop.' +
    
  244.       '\n\nCheck the render method of `InnerComponent`. ' +
    
  245.       'It was passed a child from ComponentWrapper. '
    
  246.   );
    
  247. });
    
  248. 
    
  249. it('does not warn for arrays of elements with keys', () => {
    
  250.   ReactTestUtils.renderIntoDocument(
    
  251.     <Component>{[<Component key="#1" />, <Component key="#2" />]}</Component>
    
  252.   );
    
  253. });
    
  254. 
    
  255. it('does not warn for iterable elements with keys', () => {
    
  256.   const iterable = {
    
  257.     '@@iterator': function () {
    
  258.       let i = 0;
    
  259.       return {
    
  260.         next: function () {
    
  261.           const done = ++i > 2;
    
  262.           return {
    
  263.             value: done ? undefined : <Component key={'#' + i} />,
    
  264.             done: done,
    
  265.           };
    
  266.         },
    
  267.       };
    
  268.     },
    
  269.   };
    
  270. 
    
  271.   ReactTestUtils.renderIntoDocument(<Component>{iterable}</Component>);
    
  272. });
    
  273. 
    
  274. it('does not warn for numeric keys in entry iterable as a child', () => {
    
  275.   const iterable = {
    
  276.     '@@iterator': function () {
    
  277.       let i = 0;
    
  278.       return {
    
  279.         next: function () {
    
  280.           const done = ++i > 2;
    
  281.           return {value: done ? undefined : [i, <Component />], done: done};
    
  282.         },
    
  283.       };
    
  284.     },
    
  285.   };
    
  286.   iterable.entries = iterable['@@iterator'];
    
  287. 
    
  288.   ReactTestUtils.renderIntoDocument(<Component>{iterable}</Component>);
    
  289. });
    
  290. 
    
  291. it('does not warn when the element is directly as children', () => {
    
  292.   ReactTestUtils.renderIntoDocument(
    
  293.     <Component>
    
  294.       <Component />
    
  295.       <Component />
    
  296.     </Component>
    
  297.   );
    
  298. });
    
  299. 
    
  300. it('does not warn when the child array contains non-elements', () => {
    
  301.   void (<Component>{[{}, {}]}</Component>);
    
  302. });
    
  303. 
    
  304. it('should give context for PropType errors in nested components.', () => {
    
  305.   // In this test, we're making sure that if a proptype error is found in a
    
  306.   // component, we give a small hint as to which parent instantiated that
    
  307.   // component as per warnings about key usage in ReactElementValidator.
    
  308.   function MyComp({color}) {
    
  309.     return <div>My color is {color}</div>;
    
  310.   }
    
  311.   MyComp.propTypes = {
    
  312.     color: PropTypes.string,
    
  313.   };
    
  314.   class ParentComp extends React.Component {
    
  315.     render() {
    
  316.       return <MyComp color={123} />;
    
  317.     }
    
  318.   }
    
  319.   expect(() => ReactTestUtils.renderIntoDocument(<ParentComp />)).toErrorDev(
    
  320.     'Warning: Failed prop type: ' +
    
  321.       'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
    
  322.       'expected `string`.\n' +
    
  323.       '    in MyComp (at **)\n' +
    
  324.       '    in ParentComp (at **)'
    
  325.   );
    
  326. });
    
  327. 
    
  328. it('gives a helpful error when passing null, undefined, or boolean', () => {
    
  329.   const Undefined = undefined;
    
  330.   const Null = null;
    
  331.   const True = true;
    
  332.   const Div = 'div';
    
  333.   expect(() => void (<Undefined />)).toErrorDev(
    
  334.     'Warning: React.jsx: type is invalid -- expected a string ' +
    
  335.       '(for built-in components) or a class/function (for composite ' +
    
  336.       'components) but got: undefined. You likely forgot to export your ' +
    
  337.       "component from the file it's defined in, or you might have mixed up " +
    
  338.       'default and named imports.' +
    
  339.       (process.env.BABEL_ENV === 'development'
    
  340.         ? '\n\nCheck your code at **.'
    
  341.         : ''),
    
  342.     {withoutStack: true}
    
  343.   );
    
  344.   expect(() => void (<Null />)).toErrorDev(
    
  345.     'Warning: React.jsx: type is invalid -- expected a string ' +
    
  346.       '(for built-in components) or a class/function (for composite ' +
    
  347.       'components) but got: null.' +
    
  348.       (process.env.BABEL_ENV === 'development'
    
  349.         ? '\n\nCheck your code at **.'
    
  350.         : ''),
    
  351.     {withoutStack: true}
    
  352.   );
    
  353.   expect(() => void (<True />)).toErrorDev(
    
  354.     'Warning: React.jsx: type is invalid -- expected a string ' +
    
  355.       '(for built-in components) or a class/function (for composite ' +
    
  356.       'components) but got: boolean.' +
    
  357.       (process.env.BABEL_ENV === 'development'
    
  358.         ? '\n\nCheck your code at **.'
    
  359.         : ''),
    
  360.     {withoutStack: true}
    
  361.   );
    
  362.   // No error expected
    
  363.   void (<Div />);
    
  364. });
    
  365. 
    
  366. it('should check default prop values', () => {
    
  367.   RequiredPropComponent.defaultProps = {prop: null};
    
  368. 
    
  369.   expect(() =>
    
  370.     ReactTestUtils.renderIntoDocument(<RequiredPropComponent />)
    
  371.   ).toErrorDev(
    
  372.     'Warning: Failed prop type: The prop `prop` is marked as required in ' +
    
  373.       '`RequiredPropComponent`, but its value is `null`.\n' +
    
  374.       '    in RequiredPropComponent (at **)'
    
  375.   );
    
  376. });
    
  377. 
    
  378. it('should warn on invalid prop types', () => {
    
  379.   // Since there is no prevalidation step for ES6 classes, there is no hook
    
  380.   // for us to issue a warning earlier than element creation when the error
    
  381.   // actually occurs. Since this step is skipped in production, we should just
    
  382.   // warn instead of throwing for this case.
    
  383.   class NullPropTypeComponent extends React.Component {
    
  384.     render() {
    
  385.       return <span>{this.props.prop}</span>;
    
  386.     }
    
  387.   }
    
  388.   NullPropTypeComponent.propTypes = {
    
  389.     prop: null,
    
  390.   };
    
  391.   expect(() =>
    
  392.     ReactTestUtils.renderIntoDocument(<NullPropTypeComponent />)
    
  393.   ).toErrorDev(
    
  394.     'NullPropTypeComponent: prop type `prop` is invalid; it must be a ' +
    
  395.       'function, usually from the `prop-types` package,'
    
  396.   );
    
  397. });
    
  398. 
    
  399. xit('should warn on invalid context types', () => {
    
  400.   class NullContextTypeComponent extends React.Component {
    
  401.     render() {
    
  402.       return <span>{this.props.prop}</span>;
    
  403.     }
    
  404.   }
    
  405.   NullContextTypeComponent.contextTypes = {
    
  406.     prop: null,
    
  407.   };
    
  408.   expect(() =>
    
  409.     ReactTestUtils.renderIntoDocument(<NullContextTypeComponent />)
    
  410.   ).toErrorDev(
    
  411.     'NullContextTypeComponent: type `prop` is invalid; it must ' +
    
  412.       'be a function, usually from the `prop-types` package,'
    
  413.   );
    
  414. });
    
  415. 
    
  416. it('should warn if getDefaultProps is specified on the class', () => {
    
  417.   class GetDefaultPropsComponent extends React.Component {
    
  418.     render() {
    
  419.       return <span>{this.props.prop}</span>;
    
  420.     }
    
  421.   }
    
  422.   GetDefaultPropsComponent.getDefaultProps = () => ({
    
  423.     prop: 'foo',
    
  424.   });
    
  425.   expect(() =>
    
  426.     ReactTestUtils.renderIntoDocument(<GetDefaultPropsComponent />)
    
  427.   ).toErrorDev(
    
  428.     'getDefaultProps is only used on classic React.createClass definitions.' +
    
  429.       ' Use a static property named `defaultProps` instead.',
    
  430.     {withoutStack: true}
    
  431.   );
    
  432. });
    
  433. 
    
  434. it('should warn if component declares PropTypes instead of propTypes', () => {
    
  435.   class MisspelledPropTypesComponent extends React.Component {
    
  436.     render() {
    
  437.       return <span>{this.props.prop}</span>;
    
  438.     }
    
  439.   }
    
  440.   MisspelledPropTypesComponent.PropTypes = {
    
  441.     prop: PropTypes.string,
    
  442.   };
    
  443.   expect(() =>
    
  444.     ReactTestUtils.renderIntoDocument(
    
  445.       <MisspelledPropTypesComponent prop="hi" />
    
  446.     )
    
  447.   ).toErrorDev(
    
  448.     'Warning: Component MisspelledPropTypesComponent declared `PropTypes` ' +
    
  449.       'instead of `propTypes`. Did you misspell the property assignment?',
    
  450.     {withoutStack: true}
    
  451.   );
    
  452. });
    
  453. 
    
  454. // Not supported.
    
  455. xit('warns for fragments with illegal attributes', () => {
    
  456.   class Foo extends React.Component {
    
  457.     render() {
    
  458.       return <React.Fragment a={1}>hello</React.Fragment>;
    
  459.     }
    
  460.   }
    
  461. 
    
  462.   expect(() => ReactTestUtils.renderIntoDocument(<Foo />)).toErrorDev(
    
  463.     'Invalid prop `a` supplied to `React.Fragment`. React.Fragment ' +
    
  464.       'can only have `key` and `children` props.'
    
  465.   );
    
  466. });
    
  467. 
    
  468. // Not supported.
    
  469. xit('warns for fragments with refs', () => {
    
  470.   class Foo extends React.Component {
    
  471.     render() {
    
  472.       return (
    
  473.         <React.Fragment
    
  474.           ref={bar => {
    
  475.             this.foo = bar;
    
  476.           }}>
    
  477.           hello
    
  478.         </React.Fragment>
    
  479.       );
    
  480.     }
    
  481.   }
    
  482. 
    
  483.   expect(() => ReactTestUtils.renderIntoDocument(<Foo />)).toErrorDev(
    
  484.     'Invalid attribute `ref` supplied to `React.Fragment`.'
    
  485.   );
    
  486. });
    
  487. 
    
  488. // Not supported.
    
  489. xit('does not warn for fragments of multiple elements without keys', () => {
    
  490.   ReactTestUtils.renderIntoDocument(
    
  491.     <>
    
  492.       <span>1</span>
    
  493.       <span>2</span>
    
  494.     </>
    
  495.   );
    
  496. });
    
  497. 
    
  498. // Not supported.
    
  499. xit('warns for fragments of multiple elements with same key', () => {
    
  500.   expect(() =>
    
  501.     ReactTestUtils.renderIntoDocument(
    
  502.       <>
    
  503.         <span key="a">1</span>
    
  504.         <span key="a">2</span>
    
  505.         <span key="b">3</span>
    
  506.       </>
    
  507.     )
    
  508.   ).toErrorDev('Encountered two children with the same key, `a`.', {
    
  509.     withoutStack: true,
    
  510.   });
    
  511. });
    
  512. 
    
  513. // Not supported.
    
  514. xit('does not call lazy initializers eagerly', () => {
    
  515.   let didCall = false;
    
  516.   const Lazy = React.lazy(() => {
    
  517.     didCall = true;
    
  518.     return {then() {}};
    
  519.   });
    
  520.   <Lazy />;
    
  521.   expect(didCall).toBe(false);
    
  522. });
    
  523. 
    
  524. it('supports classic refs', () => {
    
  525.   class Foo extends React.Component {
    
  526.     render() {
    
  527.       return <div className="foo" ref="inner" />;
    
  528.     }
    
  529.   }
    
  530.   const container = document.createElement('div');
    
  531.   const instance = ReactDOM.render(<Foo />, container);
    
  532.   expect(instance.refs.inner.className).toBe('foo');
    
  533. });
    
  534. 
    
  535. it('should support refs on owned components', () => {
    
  536.   const innerObj = {};
    
  537.   const outerObj = {};
    
  538. 
    
  539.   class Wrapper extends React.Component {
    
  540.     getObject = () => {
    
  541.       return this.props.object;
    
  542.     };
    
  543. 
    
  544.     render() {
    
  545.       return <div>{this.props.children}</div>;
    
  546.     }
    
  547.   }
    
  548. 
    
  549.   class Component extends React.Component {
    
  550.     render() {
    
  551.       const inner = <Wrapper object={innerObj} ref="inner" />;
    
  552.       const outer = (
    
  553.         <Wrapper object={outerObj} ref="outer">
    
  554.           {inner}
    
  555.         </Wrapper>
    
  556.       );
    
  557.       return outer;
    
  558.     }
    
  559. 
    
  560.     componentDidMount() {
    
  561.       expect(this.refs.inner.getObject()).toEqual(innerObj);
    
  562.       expect(this.refs.outer.getObject()).toEqual(outerObj);
    
  563.     }
    
  564.   }
    
  565. 
    
  566.   ReactTestUtils.renderIntoDocument(<Component />);
    
  567. });
    
  568. 
    
  569. it('should support callback-style refs', () => {
    
  570.   const innerObj = {};
    
  571.   const outerObj = {};
    
  572. 
    
  573.   class Wrapper extends React.Component {
    
  574.     getObject = () => {
    
  575.       return this.props.object;
    
  576.     };
    
  577. 
    
  578.     render() {
    
  579.       return <div>{this.props.children}</div>;
    
  580.     }
    
  581.   }
    
  582. 
    
  583.   let mounted = false;
    
  584. 
    
  585.   class Component extends React.Component {
    
  586.     render() {
    
  587.       const inner = (
    
  588.         <Wrapper object={innerObj} ref={c => (this.innerRef = c)} />
    
  589.       );
    
  590.       const outer = (
    
  591.         <Wrapper object={outerObj} ref={c => (this.outerRef = c)}>
    
  592.           {inner}
    
  593.         </Wrapper>
    
  594.       );
    
  595.       return outer;
    
  596.     }
    
  597. 
    
  598.     componentDidMount() {
    
  599.       expect(this.innerRef.getObject()).toEqual(innerObj);
    
  600.       expect(this.outerRef.getObject()).toEqual(outerObj);
    
  601.       mounted = true;
    
  602.     }
    
  603.   }
    
  604. 
    
  605.   ReactTestUtils.renderIntoDocument(<Component />);
    
  606.   expect(mounted).toBe(true);
    
  607. });
    
  608. 
    
  609. // Not supported.
    
  610. xit('should support object-style refs', () => {
    
  611.   const innerObj = {};
    
  612.   const outerObj = {};
    
  613. 
    
  614.   class Wrapper extends React.Component {
    
  615.     getObject = () => {
    
  616.       return this.props.object;
    
  617.     };
    
  618. 
    
  619.     render() {
    
  620.       return <div>{this.props.children}</div>;
    
  621.     }
    
  622.   }
    
  623. 
    
  624.   let mounted = false;
    
  625. 
    
  626.   class Component extends React.Component {
    
  627.     constructor() {
    
  628.       super();
    
  629.       this.innerRef = React.createRef();
    
  630.       this.outerRef = React.createRef();
    
  631.     }
    
  632.     render() {
    
  633.       const inner = <Wrapper object={innerObj} ref={this.innerRef} />;
    
  634.       const outer = (
    
  635.         <Wrapper object={outerObj} ref={this.outerRef}>
    
  636.           {inner}
    
  637.         </Wrapper>
    
  638.       );
    
  639.       return outer;
    
  640.     }
    
  641. 
    
  642.     componentDidMount() {
    
  643.       expect(this.innerRef.current.getObject()).toEqual(innerObj);
    
  644.       expect(this.outerRef.current.getObject()).toEqual(outerObj);
    
  645.       mounted = true;
    
  646.     }
    
  647.   }
    
  648. 
    
  649.   ReactTestUtils.renderIntoDocument(<Component />);
    
  650.   expect(mounted).toBe(true);
    
  651. });
    
  652. 
    
  653. it('should support new-style refs with mixed-up owners', () => {
    
  654.   class Wrapper extends React.Component {
    
  655.     getTitle = () => {
    
  656.       return this.props.title;
    
  657.     };
    
  658. 
    
  659.     render() {
    
  660.       return this.props.getContent();
    
  661.     }
    
  662.   }
    
  663. 
    
  664.   let mounted = false;
    
  665. 
    
  666.   class Component extends React.Component {
    
  667.     getInner = () => {
    
  668.       // (With old-style refs, it's impossible to get a ref to this div
    
  669.       // because Wrapper is the current owner when this function is called.)
    
  670.       return <div className="inner" ref={c => (this.innerRef = c)} />;
    
  671.     };
    
  672. 
    
  673.     render() {
    
  674.       return (
    
  675.         <Wrapper
    
  676.           title="wrapper"
    
  677.           ref={c => (this.wrapperRef = c)}
    
  678.           getContent={this.getInner}
    
  679.         />
    
  680.       );
    
  681.     }
    
  682. 
    
  683.     componentDidMount() {
    
  684.       // Check .props.title to make sure we got the right elements back
    
  685.       expect(this.wrapperRef.getTitle()).toBe('wrapper');
    
  686.       expect(this.innerRef.className).toBe('inner');
    
  687.       mounted = true;
    
  688.     }
    
  689.   }
    
  690. 
    
  691.   ReactTestUtils.renderIntoDocument(<Component />);
    
  692.   expect(mounted).toBe(true);
    
  693. });
    
  694. 
    
  695. it('should warn when `key` is being accessed on composite element', () => {
    
  696.   const container = document.createElement('div');
    
  697.   class Child extends React.Component {
    
  698.     render() {
    
  699.       return <div> {this.props.key} </div>;
    
  700.     }
    
  701.   }
    
  702.   class Parent extends React.Component {
    
  703.     render() {
    
  704.       return (
    
  705.         <div>
    
  706.           <Child key="0" />
    
  707.           <Child key="1" />
    
  708.           <Child key="2" />
    
  709.         </div>
    
  710.       );
    
  711.     }
    
  712.   }
    
  713.   expect(() => ReactDOM.render(<Parent />, container)).toErrorDev(
    
  714.     'Child: `key` is not a prop. Trying to access it will result ' +
    
  715.       'in `undefined` being returned. If you need to access the same ' +
    
  716.       'value within the child component, you should pass it as a different ' +
    
  717.       'prop. (https://reactjs.org/link/special-props)',
    
  718.     {withoutStack: true}
    
  719.   );
    
  720. });
    
  721. 
    
  722. it('should warn when `ref` is being accessed', () => {
    
  723.   const container = document.createElement('div');
    
  724.   class Child extends React.Component {
    
  725.     render() {
    
  726.       return <div> {this.props.ref} </div>;
    
  727.     }
    
  728.   }
    
  729.   class Parent extends React.Component {
    
  730.     render() {
    
  731.       return (
    
  732.         <div>
    
  733.           <Child ref="childElement" />
    
  734.         </div>
    
  735.       );
    
  736.     }
    
  737.   }
    
  738.   expect(() => ReactDOM.render(<Parent />, container)).toErrorDev(
    
  739.     'Child: `ref` is not a prop. Trying to access it will result ' +
    
  740.       'in `undefined` being returned. If you need to access the same ' +
    
  741.       'value within the child component, you should pass it as a different ' +
    
  742.       'prop. (https://reactjs.org/link/special-props)',
    
  743.     {withoutStack: true}
    
  744.   );
    
  745. });
    
  746. 
    
  747. // Note: no warning before 16.
    
  748. it('should NOT warn when owner and self are different for string refs', () => {
    
  749.   class ClassWithRenderProp extends React.Component {
    
  750.     render() {
    
  751.       return this.props.children();
    
  752.     }
    
  753.   }
    
  754. 
    
  755.   class ClassParent extends React.Component {
    
  756.     render() {
    
  757.       return (
    
  758.         <ClassWithRenderProp>{() => <div ref="myRef" />}</ClassWithRenderProp>
    
  759.       );
    
  760.     }
    
  761.   }
    
  762. 
    
  763.   const container = document.createElement('div');
    
  764.   ReactDOM.render(<ClassParent />, container);
    
  765. });