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. let React;
    
  14. let StrictMode;
    
  15. let ReactNative;
    
  16. let createReactNativeComponentClass;
    
  17. let UIManager;
    
  18. let TextInputState;
    
  19. let ReactNativePrivateInterface;
    
  20. 
    
  21. const DISPATCH_COMMAND_REQUIRES_HOST_COMPONENT =
    
  22.   "Warning: dispatchCommand was called with a ref that isn't a " +
    
  23.   'native component. Use React.forwardRef to get access to the underlying native component';
    
  24. 
    
  25. const SEND_ACCESSIBILITY_EVENT_REQUIRES_HOST_COMPONENT =
    
  26.   "Warning: sendAccessibilityEvent was called with a ref that isn't a " +
    
  27.   'native component. Use React.forwardRef to get access to the underlying native component';
    
  28. 
    
  29. describe('ReactNative', () => {
    
  30.   beforeEach(() => {
    
  31.     jest.resetModules();
    
  32. 
    
  33.     React = require('react');
    
  34.     StrictMode = React.StrictMode;
    
  35.     ReactNative = require('react-native-renderer');
    
  36.     ReactNativePrivateInterface = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface');
    
  37.     UIManager =
    
  38.       require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface').UIManager;
    
  39.     createReactNativeComponentClass =
    
  40.       require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
    
  41.         .ReactNativeViewConfigRegistry.register;
    
  42.     TextInputState =
    
  43.       require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface').TextInputState;
    
  44.   });
    
  45. 
    
  46.   it('should be able to create and render a native component', () => {
    
  47.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  48.       validAttributes: {foo: true},
    
  49.       uiViewClassName: 'RCTView',
    
  50.     }));
    
  51. 
    
  52.     ReactNative.render(<View foo="test" />, 1);
    
  53.     expect(UIManager.createView).toBeCalled();
    
  54.     expect(UIManager.setChildren).toBeCalled();
    
  55.     expect(UIManager.manageChildren).not.toBeCalled();
    
  56.     expect(UIManager.updateView).not.toBeCalled();
    
  57.   });
    
  58. 
    
  59.   it('should be able to create and update a native component', () => {
    
  60.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  61.       validAttributes: {foo: true},
    
  62.       uiViewClassName: 'RCTView',
    
  63.     }));
    
  64. 
    
  65.     ReactNative.render(<View foo="foo" />, 11);
    
  66. 
    
  67.     expect(UIManager.createView).toHaveBeenCalledTimes(1);
    
  68.     expect(UIManager.setChildren).toHaveBeenCalledTimes(1);
    
  69.     expect(UIManager.manageChildren).not.toBeCalled();
    
  70.     expect(UIManager.updateView).not.toBeCalled();
    
  71. 
    
  72.     ReactNative.render(<View foo="bar" />, 11);
    
  73. 
    
  74.     expect(UIManager.createView).toHaveBeenCalledTimes(1);
    
  75.     expect(UIManager.setChildren).toHaveBeenCalledTimes(1);
    
  76.     expect(UIManager.manageChildren).not.toBeCalled();
    
  77.     expect(UIManager.updateView).toBeCalledWith(3, 'RCTView', {foo: 'bar'});
    
  78.   });
    
  79. 
    
  80.   it('should not call UIManager.updateView after render for properties that have not changed', () => {
    
  81.     const Text = createReactNativeComponentClass('RCTText', () => ({
    
  82.       validAttributes: {foo: true},
    
  83.       uiViewClassName: 'RCTText',
    
  84.     }));
    
  85. 
    
  86.     ReactNative.render(<Text foo="a">1</Text>, 11);
    
  87.     expect(UIManager.updateView).not.toBeCalled();
    
  88. 
    
  89.     // If no properties have changed, we shouldn't call updateView.
    
  90.     ReactNative.render(<Text foo="a">1</Text>, 11);
    
  91.     expect(UIManager.updateView).not.toBeCalled();
    
  92. 
    
  93.     // Only call updateView for the changed property (and not for text).
    
  94.     ReactNative.render(<Text foo="b">1</Text>, 11);
    
  95.     expect(UIManager.updateView).toHaveBeenCalledTimes(1);
    
  96. 
    
  97.     // Only call updateView for the changed text (and no other properties).
    
  98.     ReactNative.render(<Text foo="b">2</Text>, 11);
    
  99.     expect(UIManager.updateView).toHaveBeenCalledTimes(2);
    
  100. 
    
  101.     // Call updateView for both changed text and properties.
    
  102.     ReactNative.render(<Text foo="c">3</Text>, 11);
    
  103.     expect(UIManager.updateView).toHaveBeenCalledTimes(4);
    
  104.   });
    
  105. 
    
  106.   it('should call dispatchCommand for native refs', () => {
    
  107.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  108.       validAttributes: {foo: true},
    
  109.       uiViewClassName: 'RCTView',
    
  110.     }));
    
  111. 
    
  112.     UIManager.dispatchViewManagerCommand.mockClear();
    
  113. 
    
  114.     let viewRef;
    
  115.     ReactNative.render(
    
  116.       <View
    
  117.         ref={ref => {
    
  118.           viewRef = ref;
    
  119.         }}
    
  120.       />,
    
  121.       11,
    
  122.     );
    
  123. 
    
  124.     expect(UIManager.dispatchViewManagerCommand).not.toBeCalled();
    
  125.     ReactNative.dispatchCommand(viewRef, 'updateCommand', [10, 20]);
    
  126.     expect(UIManager.dispatchViewManagerCommand).toHaveBeenCalledTimes(1);
    
  127.     expect(UIManager.dispatchViewManagerCommand).toHaveBeenCalledWith(
    
  128.       expect.any(Number),
    
  129.       'updateCommand',
    
  130.       [10, 20],
    
  131.     );
    
  132.   });
    
  133. 
    
  134.   it('should warn and no-op if calling dispatchCommand on non native refs', () => {
    
  135.     class BasicClass extends React.Component {
    
  136.       render() {
    
  137.         return <React.Fragment />;
    
  138.       }
    
  139.     }
    
  140. 
    
  141.     UIManager.dispatchViewManagerCommand.mockReset();
    
  142. 
    
  143.     let viewRef;
    
  144.     ReactNative.render(
    
  145.       <BasicClass
    
  146.         ref={ref => {
    
  147.           viewRef = ref;
    
  148.         }}
    
  149.       />,
    
  150.       11,
    
  151.     );
    
  152. 
    
  153.     expect(UIManager.dispatchViewManagerCommand).not.toBeCalled();
    
  154.     expect(() => {
    
  155.       ReactNative.dispatchCommand(viewRef, 'updateCommand', [10, 20]);
    
  156.     }).toErrorDev([DISPATCH_COMMAND_REQUIRES_HOST_COMPONENT], {
    
  157.       withoutStack: true,
    
  158.     });
    
  159. 
    
  160.     expect(UIManager.dispatchViewManagerCommand).not.toBeCalled();
    
  161.   });
    
  162. 
    
  163.   it('should call sendAccessibilityEvent for native refs', () => {
    
  164.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  165.       validAttributes: {foo: true},
    
  166.       uiViewClassName: 'RCTView',
    
  167.     }));
    
  168. 
    
  169.     ReactNativePrivateInterface.legacySendAccessibilityEvent.mockClear();
    
  170. 
    
  171.     let viewRef;
    
  172.     ReactNative.render(
    
  173.       <View
    
  174.         ref={ref => {
    
  175.           viewRef = ref;
    
  176.         }}
    
  177.       />,
    
  178.       11,
    
  179.     );
    
  180. 
    
  181.     expect(
    
  182.       ReactNativePrivateInterface.legacySendAccessibilityEvent,
    
  183.     ).not.toBeCalled();
    
  184.     ReactNative.sendAccessibilityEvent(viewRef, 'focus');
    
  185.     expect(
    
  186.       ReactNativePrivateInterface.legacySendAccessibilityEvent,
    
  187.     ).toHaveBeenCalledTimes(1);
    
  188.     expect(
    
  189.       ReactNativePrivateInterface.legacySendAccessibilityEvent,
    
  190.     ).toHaveBeenCalledWith(expect.any(Number), 'focus');
    
  191.   });
    
  192. 
    
  193.   it('should warn and no-op if calling sendAccessibilityEvent on non native refs', () => {
    
  194.     class BasicClass extends React.Component {
    
  195.       render() {
    
  196.         return <React.Fragment />;
    
  197.       }
    
  198.     }
    
  199. 
    
  200.     UIManager.sendAccessibilityEvent.mockReset();
    
  201. 
    
  202.     let viewRef;
    
  203.     ReactNative.render(
    
  204.       <BasicClass
    
  205.         ref={ref => {
    
  206.           viewRef = ref;
    
  207.         }}
    
  208.       />,
    
  209.       11,
    
  210.     );
    
  211. 
    
  212.     expect(UIManager.sendAccessibilityEvent).not.toBeCalled();
    
  213.     expect(() => {
    
  214.       ReactNative.sendAccessibilityEvent(viewRef, 'updateCommand', [10, 20]);
    
  215.     }).toErrorDev([SEND_ACCESSIBILITY_EVENT_REQUIRES_HOST_COMPONENT], {
    
  216.       withoutStack: true,
    
  217.     });
    
  218. 
    
  219.     expect(UIManager.sendAccessibilityEvent).not.toBeCalled();
    
  220.   });
    
  221. 
    
  222.   it('should not call UIManager.updateView from ref.setNativeProps for properties that have not changed', () => {
    
  223.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  224.       validAttributes: {foo: true},
    
  225.       uiViewClassName: 'RCTView',
    
  226.     }));
    
  227. 
    
  228.     UIManager.updateView.mockReset();
    
  229. 
    
  230.     let viewRef;
    
  231.     ReactNative.render(
    
  232.       <View
    
  233.         foo="bar"
    
  234.         ref={ref => {
    
  235.           viewRef = ref;
    
  236.         }}
    
  237.       />,
    
  238.       11,
    
  239.     );
    
  240. 
    
  241.     expect(UIManager.updateView).not.toBeCalled();
    
  242. 
    
  243.     viewRef.setNativeProps({});
    
  244.     expect(UIManager.updateView).not.toBeCalled();
    
  245. 
    
  246.     viewRef.setNativeProps({foo: 'baz'});
    
  247.     expect(UIManager.updateView).toHaveBeenCalledTimes(1);
    
  248.     expect(UIManager.updateView).toHaveBeenCalledWith(
    
  249.       expect.any(Number),
    
  250.       'RCTView',
    
  251.       {foo: 'baz'},
    
  252.     );
    
  253.   });
    
  254. 
    
  255.   it('should call UIManager.measure on ref.measure', () => {
    
  256.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  257.       validAttributes: {foo: true},
    
  258.       uiViewClassName: 'RCTView',
    
  259.     }));
    
  260. 
    
  261.     UIManager.measure.mockClear();
    
  262. 
    
  263.     let viewRef;
    
  264.     ReactNative.render(
    
  265.       <View
    
  266.         ref={ref => {
    
  267.           viewRef = ref;
    
  268.         }}
    
  269.       />,
    
  270.       11,
    
  271.     );
    
  272. 
    
  273.     expect(UIManager.measure).not.toBeCalled();
    
  274.     const successCallback = jest.fn();
    
  275.     viewRef.measure(successCallback);
    
  276.     expect(UIManager.measure).toHaveBeenCalledTimes(1);
    
  277.     expect(successCallback).toHaveBeenCalledTimes(1);
    
  278.     expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100, 0, 0);
    
  279.   });
    
  280. 
    
  281.   it('should call UIManager.measureInWindow on ref.measureInWindow', () => {
    
  282.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  283.       validAttributes: {foo: true},
    
  284.       uiViewClassName: 'RCTView',
    
  285.     }));
    
  286. 
    
  287.     UIManager.measureInWindow.mockClear();
    
  288. 
    
  289.     let viewRef;
    
  290.     ReactNative.render(
    
  291.       <View
    
  292.         ref={ref => {
    
  293.           viewRef = ref;
    
  294.         }}
    
  295.       />,
    
  296.       11,
    
  297.     );
    
  298. 
    
  299.     expect(UIManager.measureInWindow).not.toBeCalled();
    
  300.     const successCallback = jest.fn();
    
  301.     viewRef.measureInWindow(successCallback);
    
  302.     expect(UIManager.measureInWindow).toHaveBeenCalledTimes(1);
    
  303.     expect(successCallback).toHaveBeenCalledTimes(1);
    
  304.     expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100);
    
  305.   });
    
  306. 
    
  307.   it('should support reactTag in ref.measureLayout', () => {
    
  308.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  309.       validAttributes: {foo: true},
    
  310.       uiViewClassName: 'RCTView',
    
  311.     }));
    
  312. 
    
  313.     UIManager.measureLayout.mockClear();
    
  314. 
    
  315.     let viewRef;
    
  316.     let otherRef;
    
  317.     ReactNative.render(
    
  318.       <View>
    
  319.         <View
    
  320.           foo="bar"
    
  321.           ref={ref => {
    
  322.             viewRef = ref;
    
  323.           }}
    
  324.         />
    
  325.         <View
    
  326.           ref={ref => {
    
  327.             otherRef = ref;
    
  328.           }}
    
  329.         />
    
  330.       </View>,
    
  331.       11,
    
  332.     );
    
  333. 
    
  334.     expect(UIManager.measureLayout).not.toBeCalled();
    
  335.     const successCallback = jest.fn();
    
  336.     const failureCallback = jest.fn();
    
  337.     viewRef.measureLayout(
    
  338.       ReactNative.findNodeHandle(otherRef),
    
  339.       successCallback,
    
  340.       failureCallback,
    
  341.     );
    
  342.     expect(UIManager.measureLayout).toHaveBeenCalledTimes(1);
    
  343.     expect(successCallback).toHaveBeenCalledTimes(1);
    
  344.     expect(successCallback).toHaveBeenCalledWith(1, 1, 100, 100);
    
  345.   });
    
  346. 
    
  347.   it('should support ref in ref.measureLayout of host components', () => {
    
  348.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  349.       validAttributes: {foo: true},
    
  350.       uiViewClassName: 'RCTView',
    
  351.     }));
    
  352. 
    
  353.     UIManager.measureLayout.mockClear();
    
  354. 
    
  355.     let viewRef;
    
  356.     let otherRef;
    
  357.     ReactNative.render(
    
  358.       <View>
    
  359.         <View
    
  360.           foo="bar"
    
  361.           ref={ref => {
    
  362.             viewRef = ref;
    
  363.           }}
    
  364.         />
    
  365.         <View
    
  366.           ref={ref => {
    
  367.             otherRef = ref;
    
  368.           }}
    
  369.         />
    
  370.       </View>,
    
  371.       11,
    
  372.     );
    
  373. 
    
  374.     expect(UIManager.measureLayout).not.toBeCalled();
    
  375.     const successCallback = jest.fn();
    
  376.     const failureCallback = jest.fn();
    
  377.     viewRef.measureLayout(otherRef, successCallback, failureCallback);
    
  378.     expect(UIManager.measureLayout).toHaveBeenCalledTimes(1);
    
  379.     expect(successCallback).toHaveBeenCalledTimes(1);
    
  380.     expect(successCallback).toHaveBeenCalledWith(1, 1, 100, 100);
    
  381.   });
    
  382. 
    
  383.   it('returns the correct instance and calls it in the callback', () => {
    
  384.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  385.       validAttributes: {foo: true},
    
  386.       uiViewClassName: 'RCTView',
    
  387.     }));
    
  388. 
    
  389.     let a;
    
  390.     let b;
    
  391.     const c = ReactNative.render(
    
  392.       <View foo="foo" ref={v => (a = v)} />,
    
  393.       11,
    
  394.       function () {
    
  395.         b = this;
    
  396.       },
    
  397.     );
    
  398. 
    
  399.     expect(a).toBeTruthy();
    
  400.     expect(a).toBe(b);
    
  401.     expect(a).toBe(c);
    
  402.   });
    
  403. 
    
  404.   it('renders and reorders children', () => {
    
  405.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  406.       validAttributes: {title: true},
    
  407.       uiViewClassName: 'RCTView',
    
  408.     }));
    
  409. 
    
  410.     class Component extends React.Component {
    
  411.       render() {
    
  412.         const chars = this.props.chars.split('');
    
  413.         return (
    
  414.           <View>
    
  415.             {chars.map(text => (
    
  416.               <View key={text} title={text} />
    
  417.             ))}
    
  418.           </View>
    
  419.         );
    
  420.       }
    
  421.     }
    
  422. 
    
  423.     // Mini multi-child stress test: lots of reorders, some adds, some removes.
    
  424.     const before = 'abcdefghijklmnopqrst';
    
  425.     const after = 'mxhpgwfralkeoivcstzy';
    
  426. 
    
  427.     ReactNative.render(<Component chars={before} />, 11);
    
  428.     expect(UIManager.__dumpHierarchyForJestTestsOnly()).toMatchSnapshot();
    
  429. 
    
  430.     ReactNative.render(<Component chars={after} />, 11);
    
  431.     expect(UIManager.__dumpHierarchyForJestTestsOnly()).toMatchSnapshot();
    
  432.   });
    
  433. 
    
  434.   it('calls setState with no arguments', () => {
    
  435.     let mockArgs;
    
  436.     class Component extends React.Component {
    
  437.       componentDidMount() {
    
  438.         this.setState({}, (...args) => (mockArgs = args));
    
  439.       }
    
  440.       render() {
    
  441.         return false;
    
  442.       }
    
  443.     }
    
  444. 
    
  445.     ReactNative.render(<Component />, 11);
    
  446.     expect(mockArgs.length).toEqual(0);
    
  447.   });
    
  448. 
    
  449.   it('should not throw when <View> is used inside of a <Text> ancestor', () => {
    
  450.     const Image = createReactNativeComponentClass('RCTImage', () => ({
    
  451.       validAttributes: {},
    
  452.       uiViewClassName: 'RCTImage',
    
  453.     }));
    
  454.     const Text = createReactNativeComponentClass('RCTText', () => ({
    
  455.       validAttributes: {},
    
  456.       uiViewClassName: 'RCTText',
    
  457.     }));
    
  458.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  459.       validAttributes: {},
    
  460.       uiViewClassName: 'RCTView',
    
  461.     }));
    
  462. 
    
  463.     ReactNative.render(
    
  464.       <Text>
    
  465.         <View />
    
  466.       </Text>,
    
  467.       11,
    
  468.     );
    
  469. 
    
  470.     // Non-View things (e.g. Image) are fine
    
  471.     ReactNative.render(
    
  472.       <Text>
    
  473.         <Image />
    
  474.       </Text>,
    
  475.       11,
    
  476.     );
    
  477.   });
    
  478. 
    
  479.   it('should throw for text not inside of a <Text> ancestor', () => {
    
  480.     const ScrollView = createReactNativeComponentClass('RCTScrollView', () => ({
    
  481.       validAttributes: {},
    
  482.       uiViewClassName: 'RCTScrollView',
    
  483.     }));
    
  484.     const Text = createReactNativeComponentClass('RCTText', () => ({
    
  485.       validAttributes: {},
    
  486.       uiViewClassName: 'RCTText',
    
  487.     }));
    
  488.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  489.       validAttributes: {},
    
  490.       uiViewClassName: 'RCTView',
    
  491.     }));
    
  492. 
    
  493.     expect(() => ReactNative.render(<View>this should warn</View>, 11)).toThrow(
    
  494.       'Text strings must be rendered within a <Text> component.',
    
  495.     );
    
  496. 
    
  497.     expect(() =>
    
  498.       ReactNative.render(
    
  499.         <Text>
    
  500.           <ScrollView>hi hello hi</ScrollView>
    
  501.         </Text>,
    
  502.         11,
    
  503.       ),
    
  504.     ).toThrow('Text strings must be rendered within a <Text> component.');
    
  505.   });
    
  506. 
    
  507.   it('should not throw for text inside of an indirect <Text> ancestor', () => {
    
  508.     const Text = createReactNativeComponentClass('RCTText', () => ({
    
  509.       validAttributes: {},
    
  510.       uiViewClassName: 'RCTText',
    
  511.     }));
    
  512. 
    
  513.     const Indirection = () => 'Hi';
    
  514. 
    
  515.     ReactNative.render(
    
  516.       <Text>
    
  517.         <Indirection />
    
  518.       </Text>,
    
  519.       11,
    
  520.     );
    
  521.   });
    
  522. 
    
  523.   it('findHostInstance_DEPRECATED should warn if used to find a host component inside StrictMode', () => {
    
  524.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  525.       validAttributes: {foo: true},
    
  526.       uiViewClassName: 'RCTView',
    
  527.     }));
    
  528. 
    
  529.     let parent = undefined;
    
  530.     let child = undefined;
    
  531. 
    
  532.     class ContainsStrictModeChild extends React.Component {
    
  533.       render() {
    
  534.         return (
    
  535.           <StrictMode>
    
  536.             <View ref={n => (child = n)} />
    
  537.           </StrictMode>
    
  538.         );
    
  539.       }
    
  540.     }
    
  541. 
    
  542.     ReactNative.render(<ContainsStrictModeChild ref={n => (parent = n)} />, 11);
    
  543. 
    
  544.     let match;
    
  545.     expect(
    
  546.       () => (match = ReactNative.findHostInstance_DEPRECATED(parent)),
    
  547.     ).toErrorDev([
    
  548.       'Warning: findHostInstance_DEPRECATED is deprecated in StrictMode. ' +
    
  549.         'findHostInstance_DEPRECATED was passed an instance of ContainsStrictModeChild which renders StrictMode children. ' +
    
  550.         'Instead, add a ref directly to the element you want to reference. ' +
    
  551.         'Learn more about using refs safely here: ' +
    
  552.         'https://reactjs.org/link/strict-mode-find-node' +
    
  553.         '\n    in RCTView (at **)' +
    
  554.         '\n    in ContainsStrictModeChild (at **)',
    
  555.     ]);
    
  556.     expect(match).toBe(child);
    
  557.   });
    
  558. 
    
  559.   it('findHostInstance_DEPRECATED should warn if passed a component that is inside StrictMode', () => {
    
  560.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  561.       validAttributes: {foo: true},
    
  562.       uiViewClassName: 'RCTView',
    
  563.     }));
    
  564. 
    
  565.     let parent = undefined;
    
  566.     let child = undefined;
    
  567. 
    
  568.     class IsInStrictMode extends React.Component {
    
  569.       render() {
    
  570.         return <View ref={n => (child = n)} />;
    
  571.       }
    
  572.     }
    
  573. 
    
  574.     ReactNative.render(
    
  575.       <StrictMode>
    
  576.         <IsInStrictMode ref={n => (parent = n)} />
    
  577.       </StrictMode>,
    
  578.       11,
    
  579.     );
    
  580. 
    
  581.     let match;
    
  582.     expect(
    
  583.       () => (match = ReactNative.findHostInstance_DEPRECATED(parent)),
    
  584.     ).toErrorDev([
    
  585.       'Warning: findHostInstance_DEPRECATED is deprecated in StrictMode. ' +
    
  586.         'findHostInstance_DEPRECATED was passed an instance of IsInStrictMode which is inside StrictMode. ' +
    
  587.         'Instead, add a ref directly to the element you want to reference. ' +
    
  588.         'Learn more about using refs safely here: ' +
    
  589.         'https://reactjs.org/link/strict-mode-find-node' +
    
  590.         '\n    in RCTView (at **)' +
    
  591.         '\n    in IsInStrictMode (at **)',
    
  592.     ]);
    
  593.     expect(match).toBe(child);
    
  594.   });
    
  595. 
    
  596.   it('findNodeHandle should warn if used to find a host component inside StrictMode', () => {
    
  597.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  598.       validAttributes: {foo: true},
    
  599.       uiViewClassName: 'RCTView',
    
  600.     }));
    
  601. 
    
  602.     let parent = undefined;
    
  603.     let child = undefined;
    
  604. 
    
  605.     class ContainsStrictModeChild extends React.Component {
    
  606.       render() {
    
  607.         return (
    
  608.           <StrictMode>
    
  609.             <View ref={n => (child = n)} />
    
  610.           </StrictMode>
    
  611.         );
    
  612.       }
    
  613.     }
    
  614. 
    
  615.     ReactNative.render(<ContainsStrictModeChild ref={n => (parent = n)} />, 11);
    
  616. 
    
  617.     let match;
    
  618.     expect(() => (match = ReactNative.findNodeHandle(parent))).toErrorDev([
    
  619.       'Warning: findNodeHandle is deprecated in StrictMode. ' +
    
  620.         'findNodeHandle was passed an instance of ContainsStrictModeChild which renders StrictMode children. ' +
    
  621.         'Instead, add a ref directly to the element you want to reference. ' +
    
  622.         'Learn more about using refs safely here: ' +
    
  623.         'https://reactjs.org/link/strict-mode-find-node' +
    
  624.         '\n    in RCTView (at **)' +
    
  625.         '\n    in ContainsStrictModeChild (at **)',
    
  626.     ]);
    
  627.     expect(match).toBe(child._nativeTag);
    
  628.   });
    
  629. 
    
  630.   it('findNodeHandle should warn if passed a component that is inside StrictMode', () => {
    
  631.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  632.       validAttributes: {foo: true},
    
  633.       uiViewClassName: 'RCTView',
    
  634.     }));
    
  635. 
    
  636.     let parent = undefined;
    
  637.     let child = undefined;
    
  638. 
    
  639.     class IsInStrictMode extends React.Component {
    
  640.       render() {
    
  641.         return <View ref={n => (child = n)} />;
    
  642.       }
    
  643.     }
    
  644. 
    
  645.     ReactNative.render(
    
  646.       <StrictMode>
    
  647.         <IsInStrictMode ref={n => (parent = n)} />
    
  648.       </StrictMode>,
    
  649.       11,
    
  650.     );
    
  651. 
    
  652.     let match;
    
  653.     expect(() => (match = ReactNative.findNodeHandle(parent))).toErrorDev([
    
  654.       'Warning: findNodeHandle is deprecated in StrictMode. ' +
    
  655.         'findNodeHandle was passed an instance of IsInStrictMode which is inside StrictMode. ' +
    
  656.         'Instead, add a ref directly to the element you want to reference. ' +
    
  657.         'Learn more about using refs safely here: ' +
    
  658.         'https://reactjs.org/link/strict-mode-find-node' +
    
  659.         '\n    in RCTView (at **)' +
    
  660.         '\n    in IsInStrictMode (at **)',
    
  661.     ]);
    
  662.     expect(match).toBe(child._nativeTag);
    
  663.   });
    
  664. 
    
  665.   it('blur on host component calls TextInputState', () => {
    
  666.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  667.       validAttributes: {foo: true},
    
  668.       uiViewClassName: 'RCTView',
    
  669.     }));
    
  670. 
    
  671.     const viewRef = React.createRef();
    
  672.     ReactNative.render(<View ref={viewRef} />, 11);
    
  673. 
    
  674.     expect(TextInputState.blurTextInput).not.toBeCalled();
    
  675. 
    
  676.     viewRef.current.blur();
    
  677. 
    
  678.     expect(TextInputState.blurTextInput).toHaveBeenCalledTimes(1);
    
  679.     expect(TextInputState.blurTextInput).toHaveBeenCalledWith(viewRef.current);
    
  680.   });
    
  681. 
    
  682.   it('focus on host component calls TextInputState', () => {
    
  683.     const View = createReactNativeComponentClass('RCTView', () => ({
    
  684.       validAttributes: {foo: true},
    
  685.       uiViewClassName: 'RCTView',
    
  686.     }));
    
  687. 
    
  688.     const viewRef = React.createRef();
    
  689.     ReactNative.render(<View ref={viewRef} />, 11);
    
  690. 
    
  691.     expect(TextInputState.focusTextInput).not.toBeCalled();
    
  692. 
    
  693.     viewRef.current.focus();
    
  694. 
    
  695.     expect(TextInputState.focusTextInput).toHaveBeenCalledTimes(1);
    
  696.     expect(TextInputState.focusTextInput).toHaveBeenCalledWith(viewRef.current);
    
  697.   });
    
  698. });