1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @emails react-core
    
  8.  * @jest-environment node
    
  9.  */
    
  10. 
    
  11. 'use strict';
    
  12. 
    
  13. const React = require('react');
    
  14. let ReactTestRenderer;
    
  15. let Context;
    
  16. 
    
  17. const RCTView = 'RCTView';
    
  18. const View = props => <RCTView {...props} />;
    
  19. 
    
  20. describe('ReactTestRendererTraversal', () => {
    
  21.   beforeEach(() => {
    
  22.     jest.resetModules();
    
  23.     ReactTestRenderer = require('react-test-renderer');
    
  24.     Context = React.createContext(null);
    
  25.   });
    
  26. 
    
  27.   class Example extends React.Component {
    
  28.     render() {
    
  29.       return (
    
  30.         <View>
    
  31.           <View foo="foo">
    
  32.             <View bar="bar" />
    
  33.             <View bar="bar" baz="baz" itself="itself" />
    
  34.             <View />
    
  35.             <ExampleSpread bar="bar" />
    
  36.             <ExampleFn bar="bar" bing="bing" />
    
  37.             <ExampleNull bar="bar" />
    
  38.             <ExampleNull null="null">
    
  39.               <View void="void" />
    
  40.               <View void="void" />
    
  41.             </ExampleNull>
    
  42.             <React.Profiler id="test" onRender={() => {}}>
    
  43.               <ExampleForwardRef qux="qux" />
    
  44.             </React.Profiler>
    
  45.             <>
    
  46.               <>
    
  47.                 <Context.Provider value={null}>
    
  48.                   <Context.Consumer>
    
  49.                     {() => <View nested={true} />}
    
  50.                   </Context.Consumer>
    
  51.                 </Context.Provider>
    
  52.               </>
    
  53.               <View nested={true} />
    
  54.               <View nested={true} />
    
  55.             </>
    
  56.           </View>
    
  57.         </View>
    
  58.       );
    
  59.     }
    
  60.   }
    
  61.   class ExampleSpread extends React.Component {
    
  62.     render = () => <View {...this.props} />;
    
  63.   }
    
  64.   const ExampleFn = props => <View baz="baz" />;
    
  65.   const ExampleNull = props => null;
    
  66. 
    
  67.   const ExampleForwardRef = React.forwardRef((props, ref) => (
    
  68.     <View {...props} ref={ref} />
    
  69.   ));
    
  70. 
    
  71.   it('initializes', () => {
    
  72.     const render = ReactTestRenderer.create(<Example />);
    
  73.     const hasFooProp = node => node.props.hasOwnProperty('foo');
    
  74. 
    
  75.     // assert .props, .type and .parent attributes
    
  76.     const foo = render.root.find(hasFooProp);
    
  77.     expect(foo.props.children).toHaveLength(9);
    
  78.     expect(foo.type).toBe(View);
    
  79.     expect(render.root.parent).toBe(null);
    
  80.     expect(foo.children[0].parent).toBe(foo);
    
  81.   });
    
  82. 
    
  83.   it('searches via .find() / .findAll()', () => {
    
  84.     const render = ReactTestRenderer.create(<Example />);
    
  85.     const hasFooProp = node => node.props.hasOwnProperty('foo');
    
  86.     const hasBarProp = node => node.props.hasOwnProperty('bar');
    
  87.     const hasBazProp = node => node.props.hasOwnProperty('baz');
    
  88.     const hasBingProp = node => node.props.hasOwnProperty('bing');
    
  89.     const hasNullProp = node => node.props.hasOwnProperty('null');
    
  90.     const hasVoidProp = node => node.props.hasOwnProperty('void');
    
  91.     const hasItselfProp = node => node.props.hasOwnProperty('itself');
    
  92.     const hasNestedProp = node => node.props.hasOwnProperty('nested');
    
  93. 
    
  94.     expect(() => render.root.find(hasFooProp)).not.toThrow(); // 1 match
    
  95.     expect(() => render.root.find(hasBarProp)).toThrow(); // >1 matches
    
  96.     expect(() => render.root.find(hasBazProp)).toThrow(); // >1 matches
    
  97.     expect(() => render.root.find(hasBingProp)).not.toThrow(); // 1 match
    
  98.     expect(() => render.root.find(hasNullProp)).not.toThrow(); // 1 match
    
  99.     expect(() => render.root.find(hasVoidProp)).toThrow(); // 0 matches
    
  100.     expect(() => render.root.find(hasNestedProp)).toThrow(); // >1 matches
    
  101. 
    
  102.     // same assertion as .find(), but confirm length
    
  103.     expect(render.root.findAll(hasFooProp, {deep: false})).toHaveLength(1);
    
  104.     expect(render.root.findAll(hasBarProp, {deep: false})).toHaveLength(5);
    
  105.     expect(render.root.findAll(hasBazProp, {deep: false})).toHaveLength(2);
    
  106.     expect(render.root.findAll(hasBingProp, {deep: false})).toHaveLength(1);
    
  107.     expect(render.root.findAll(hasNullProp, {deep: false})).toHaveLength(1);
    
  108.     expect(render.root.findAll(hasVoidProp, {deep: false})).toHaveLength(0);
    
  109.     expect(render.root.findAll(hasNestedProp, {deep: false})).toHaveLength(3);
    
  110. 
    
  111.     // note: with {deep: true}, .findAll() will continue to
    
  112.     //       search children, even after finding a match
    
  113.     expect(render.root.findAll(hasFooProp)).toHaveLength(2);
    
  114.     expect(render.root.findAll(hasBarProp)).toHaveLength(9);
    
  115.     expect(render.root.findAll(hasBazProp)).toHaveLength(4);
    
  116.     expect(render.root.findAll(hasBingProp)).toHaveLength(1); // no spread
    
  117.     expect(render.root.findAll(hasNullProp)).toHaveLength(1); // no spread
    
  118.     expect(render.root.findAll(hasVoidProp)).toHaveLength(0);
    
  119.     expect(render.root.findAll(hasNestedProp, {deep: false})).toHaveLength(3);
    
  120. 
    
  121.     const bing = render.root.find(hasBingProp);
    
  122.     expect(bing.find(hasBarProp)).toBe(bing);
    
  123.     expect(bing.find(hasBingProp)).toBe(bing);
    
  124.     expect(bing.findAll(hasBazProp, {deep: false})).toHaveLength(1);
    
  125.     expect(bing.findAll(hasBazProp)).toHaveLength(2);
    
  126. 
    
  127.     const foo = render.root.find(hasFooProp);
    
  128.     expect(foo.findAll(hasFooProp, {deep: false})).toHaveLength(1);
    
  129.     expect(foo.findAll(hasFooProp)).toHaveLength(2);
    
  130. 
    
  131.     const itself = foo.find(hasItselfProp);
    
  132.     expect(itself.find(hasBarProp)).toBe(itself);
    
  133.     expect(itself.find(hasBazProp)).toBe(itself);
    
  134.     expect(itself.findAll(hasBazProp, {deep: false})).toHaveLength(1);
    
  135.     expect(itself.findAll(hasBazProp)).toHaveLength(2);
    
  136.   });
    
  137. 
    
  138.   it('searches via .findByType() / .findAllByType()', () => {
    
  139.     const render = ReactTestRenderer.create(<Example />);
    
  140. 
    
  141.     expect(() => render.root.findByType(ExampleFn)).not.toThrow(); // 1 match
    
  142.     expect(() => render.root.findByType(View)).not.toThrow(); // 1 match
    
  143.     expect(() => render.root.findByType(ExampleForwardRef)).not.toThrow(); // 1 match
    
  144.     // note: there are clearly multiple <View /> in general, but there
    
  145.     //       is only one being rendered at root node level
    
  146.     expect(() => render.root.findByType(ExampleNull)).toThrow(); // 2 matches
    
  147. 
    
  148.     expect(render.root.findAllByType(ExampleFn)).toHaveLength(1);
    
  149.     expect(render.root.findAllByType(View, {deep: false})).toHaveLength(1);
    
  150.     expect(render.root.findAllByType(View)).toHaveLength(11);
    
  151.     expect(render.root.findAllByType(ExampleNull)).toHaveLength(2);
    
  152.     expect(render.root.findAllByType(ExampleForwardRef)).toHaveLength(1);
    
  153. 
    
  154.     const nulls = render.root.findAllByType(ExampleNull);
    
  155.     expect(nulls[0].findAllByType(View)).toHaveLength(0);
    
  156.     expect(nulls[1].findAllByType(View)).toHaveLength(0);
    
  157. 
    
  158.     const fn = render.root.findAllByType(ExampleFn);
    
  159.     expect(fn[0].findAllByType(View)).toHaveLength(1);
    
  160.   });
    
  161. 
    
  162.   it('searches via .findByProps() / .findAllByProps()', () => {
    
  163.     const render = ReactTestRenderer.create(<Example />);
    
  164.     const foo = 'foo';
    
  165.     const bar = 'bar';
    
  166.     const baz = 'baz';
    
  167.     const qux = 'qux';
    
  168. 
    
  169.     expect(() => render.root.findByProps({foo})).not.toThrow(); // 1 match
    
  170.     expect(() => render.root.findByProps({bar})).toThrow(); // >1 matches
    
  171.     expect(() => render.root.findByProps({baz})).toThrow(); // >1 matches
    
  172.     expect(() => render.root.findByProps({qux})).not.toThrow(); // 1 match
    
  173. 
    
  174.     expect(render.root.findAllByProps({foo}, {deep: false})).toHaveLength(1);
    
  175.     expect(render.root.findAllByProps({bar}, {deep: false})).toHaveLength(5);
    
  176.     expect(render.root.findAllByProps({baz}, {deep: false})).toHaveLength(2);
    
  177.     expect(render.root.findAllByProps({qux}, {deep: false})).toHaveLength(1);
    
  178. 
    
  179.     expect(render.root.findAllByProps({foo})).toHaveLength(2);
    
  180.     expect(render.root.findAllByProps({bar})).toHaveLength(9);
    
  181.     expect(render.root.findAllByProps({baz})).toHaveLength(4);
    
  182.     expect(render.root.findAllByProps({qux})).toHaveLength(3);
    
  183.   });
    
  184. 
    
  185.   it('skips special nodes', () => {
    
  186.     const render = ReactTestRenderer.create(<Example />);
    
  187.     expect(render.root.findAllByType(React.Fragment)).toHaveLength(0);
    
  188.     expect(render.root.findAllByType(Context.Consumer)).toHaveLength(0);
    
  189.     expect(render.root.findAllByType(Context.Provider)).toHaveLength(0);
    
  190. 
    
  191.     const expectedParent = render.root.findByProps({foo: 'foo'}, {deep: false})
    
  192.       .children[0];
    
  193.     const nestedViews = render.root.findAllByProps(
    
  194.       {nested: true},
    
  195.       {deep: false},
    
  196.     );
    
  197.     expect(nestedViews.length).toBe(3);
    
  198.     expect(nestedViews[0].parent).toBe(expectedParent);
    
  199.     expect(nestedViews[1].parent).toBe(expectedParent);
    
  200.     expect(nestedViews[2].parent).toBe(expectedParent);
    
  201.   });
    
  202. 
    
  203.   it('can have special nodes as roots', () => {
    
  204.     const FR = React.forwardRef((props, ref) => <section {...props} />);
    
  205.     expect(
    
  206.       ReactTestRenderer.create(
    
  207.         <FR>
    
  208.           <div />
    
  209.           <div />
    
  210.         </FR>,
    
  211.       ).root.findAllByType('div').length,
    
  212.     ).toBe(2);
    
  213.     expect(
    
  214.       ReactTestRenderer.create(
    
  215.         <>
    
  216.           <div />
    
  217.           <div />
    
  218.         </>,
    
  219.       ).root.findAllByType('div').length,
    
  220.     ).toBe(2);
    
  221.     expect(
    
  222.       ReactTestRenderer.create(
    
  223.         <React.Fragment key="foo">
    
  224.           <div />
    
  225.           <div />
    
  226.         </React.Fragment>,
    
  227.       ).root.findAllByType('div').length,
    
  228.     ).toBe(2);
    
  229.     expect(
    
  230.       ReactTestRenderer.create(
    
  231.         <React.StrictMode>
    
  232.           <div />
    
  233.           <div />
    
  234.         </React.StrictMode>,
    
  235.       ).root.findAllByType('div').length,
    
  236.     ).toBe(2);
    
  237.     expect(
    
  238.       ReactTestRenderer.create(
    
  239.         <Context.Provider value={null}>
    
  240.           <div />
    
  241.           <div />
    
  242.         </Context.Provider>,
    
  243.       ).root.findAllByType('div').length,
    
  244.     ).toBe(2);
    
  245.   });
    
  246. });