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 ReactTestRenderer;
    
  15. let ReactDebugTools;
    
  16. let act;
    
  17. let useMemoCache;
    
  18. 
    
  19. describe('ReactHooksInspectionIntegration', () => {
    
  20.   beforeEach(() => {
    
  21.     jest.resetModules();
    
  22.     React = require('react');
    
  23.     ReactTestRenderer = require('react-test-renderer');
    
  24.     act = require('internal-test-utils').act;
    
  25.     ReactDebugTools = require('react-debug-tools');
    
  26.     useMemoCache = React.unstable_useMemoCache;
    
  27.   });
    
  28. 
    
  29.   it('should inspect the current state of useState hooks', async () => {
    
  30.     const useState = React.useState;
    
  31.     function Foo(props) {
    
  32.       const [state1, setState1] = useState('hello');
    
  33.       const [state2, setState2] = useState('world');
    
  34.       return (
    
  35.         <div onMouseDown={setState1} onMouseUp={setState2}>
    
  36.           {state1} {state2}
    
  37.         </div>
    
  38.       );
    
  39.     }
    
  40.     const renderer = ReactTestRenderer.create(<Foo prop="prop" />);
    
  41. 
    
  42.     let childFiber = renderer.root.findByType(Foo)._currentFiber();
    
  43.     let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  44.     expect(tree).toEqual([
    
  45.       {
    
  46.         isStateEditable: true,
    
  47.         id: 0,
    
  48.         name: 'State',
    
  49.         value: 'hello',
    
  50.         subHooks: [],
    
  51.       },
    
  52.       {
    
  53.         isStateEditable: true,
    
  54.         id: 1,
    
  55.         name: 'State',
    
  56.         value: 'world',
    
  57.         subHooks: [],
    
  58.       },
    
  59.     ]);
    
  60. 
    
  61.     const {onMouseDown: setStateA, onMouseUp: setStateB} =
    
  62.       renderer.root.findByType('div').props;
    
  63. 
    
  64.     await act(() => setStateA('Hi'));
    
  65. 
    
  66.     childFiber = renderer.root.findByType(Foo)._currentFiber();
    
  67.     tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  68. 
    
  69.     expect(tree).toEqual([
    
  70.       {
    
  71.         isStateEditable: true,
    
  72.         id: 0,
    
  73.         name: 'State',
    
  74.         value: 'Hi',
    
  75.         subHooks: [],
    
  76.       },
    
  77.       {
    
  78.         isStateEditable: true,
    
  79.         id: 1,
    
  80.         name: 'State',
    
  81.         value: 'world',
    
  82.         subHooks: [],
    
  83.       },
    
  84.     ]);
    
  85. 
    
  86.     await act(() => setStateB('world!'));
    
  87. 
    
  88.     childFiber = renderer.root.findByType(Foo)._currentFiber();
    
  89.     tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  90. 
    
  91.     expect(tree).toEqual([
    
  92.       {
    
  93.         isStateEditable: true,
    
  94.         id: 0,
    
  95.         name: 'State',
    
  96.         value: 'Hi',
    
  97.         subHooks: [],
    
  98.       },
    
  99.       {
    
  100.         isStateEditable: true,
    
  101.         id: 1,
    
  102.         name: 'State',
    
  103.         value: 'world!',
    
  104.         subHooks: [],
    
  105.       },
    
  106.     ]);
    
  107.   });
    
  108. 
    
  109.   it('should inspect the current state of all stateful hooks', async () => {
    
  110.     const outsideRef = React.createRef();
    
  111.     function effect() {}
    
  112.     function Foo(props) {
    
  113.       const [state1, setState] = React.useState('a');
    
  114.       const [state2, dispatch] = React.useReducer((s, a) => a.value, 'b');
    
  115.       const ref = React.useRef('c');
    
  116. 
    
  117.       React.useLayoutEffect(effect);
    
  118.       React.useEffect(effect);
    
  119. 
    
  120.       React.useImperativeHandle(
    
  121.         outsideRef,
    
  122.         () => {
    
  123.           // Return a function so that jest treats them as non-equal.
    
  124.           return function Instance() {};
    
  125.         },
    
  126.         [],
    
  127.       );
    
  128. 
    
  129.       React.useMemo(() => state1 + state2, [state1]);
    
  130. 
    
  131.       function update() {
    
  132.         setState('A');
    
  133.         dispatch({value: 'B'});
    
  134.         ref.current = 'C';
    
  135.       }
    
  136.       const memoizedUpdate = React.useCallback(update, []);
    
  137.       return (
    
  138.         <div onClick={memoizedUpdate}>
    
  139.           {state1} {state2}
    
  140.         </div>
    
  141.       );
    
  142.     }
    
  143.     let renderer;
    
  144.     await act(() => {
    
  145.       renderer = ReactTestRenderer.create(<Foo prop="prop" />);
    
  146.     });
    
  147. 
    
  148.     let childFiber = renderer.root.findByType(Foo)._currentFiber();
    
  149. 
    
  150.     const {onClick: updateStates} = renderer.root.findByType('div').props;
    
  151. 
    
  152.     let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  153.     expect(tree).toEqual([
    
  154.       {
    
  155.         isStateEditable: true,
    
  156.         id: 0,
    
  157.         name: 'State',
    
  158.         value: 'a',
    
  159.         subHooks: [],
    
  160.       },
    
  161.       {
    
  162.         isStateEditable: true,
    
  163.         id: 1,
    
  164.         name: 'Reducer',
    
  165.         value: 'b',
    
  166.         subHooks: [],
    
  167.       },
    
  168.       {isStateEditable: false, id: 2, name: 'Ref', value: 'c', subHooks: []},
    
  169.       {
    
  170.         isStateEditable: false,
    
  171.         id: 3,
    
  172.         name: 'LayoutEffect',
    
  173.         value: effect,
    
  174.         subHooks: [],
    
  175.       },
    
  176.       {
    
  177.         isStateEditable: false,
    
  178.         id: 4,
    
  179.         name: 'Effect',
    
  180.         value: effect,
    
  181.         subHooks: [],
    
  182.       },
    
  183.       {
    
  184.         isStateEditable: false,
    
  185.         id: 5,
    
  186.         name: 'ImperativeHandle',
    
  187.         value: outsideRef.current,
    
  188.         subHooks: [],
    
  189.       },
    
  190.       {
    
  191.         isStateEditable: false,
    
  192.         id: 6,
    
  193.         name: 'Memo',
    
  194.         value: 'ab',
    
  195.         subHooks: [],
    
  196.       },
    
  197.       {
    
  198.         isStateEditable: false,
    
  199.         id: 7,
    
  200.         name: 'Callback',
    
  201.         value: updateStates,
    
  202.         subHooks: [],
    
  203.       },
    
  204.     ]);
    
  205. 
    
  206.     await act(() => {
    
  207.       updateStates();
    
  208.     });
    
  209. 
    
  210.     childFiber = renderer.root.findByType(Foo)._currentFiber();
    
  211.     tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  212. 
    
  213.     expect(tree).toEqual([
    
  214.       {
    
  215.         isStateEditable: true,
    
  216.         id: 0,
    
  217.         name: 'State',
    
  218.         value: 'A',
    
  219.         subHooks: [],
    
  220.       },
    
  221.       {
    
  222.         isStateEditable: true,
    
  223.         id: 1,
    
  224.         name: 'Reducer',
    
  225.         value: 'B',
    
  226.         subHooks: [],
    
  227.       },
    
  228.       {isStateEditable: false, id: 2, name: 'Ref', value: 'C', subHooks: []},
    
  229.       {
    
  230.         isStateEditable: false,
    
  231.         id: 3,
    
  232.         name: 'LayoutEffect',
    
  233.         value: effect,
    
  234.         subHooks: [],
    
  235.       },
    
  236.       {
    
  237.         isStateEditable: false,
    
  238.         id: 4,
    
  239.         name: 'Effect',
    
  240.         value: effect,
    
  241.         subHooks: [],
    
  242.       },
    
  243.       {
    
  244.         isStateEditable: false,
    
  245.         id: 5,
    
  246.         name: 'ImperativeHandle',
    
  247.         value: outsideRef.current,
    
  248.         subHooks: [],
    
  249.       },
    
  250.       {
    
  251.         isStateEditable: false,
    
  252.         id: 6,
    
  253.         name: 'Memo',
    
  254.         value: 'Ab',
    
  255.         subHooks: [],
    
  256.       },
    
  257.       {
    
  258.         isStateEditable: false,
    
  259.         id: 7,
    
  260.         name: 'Callback',
    
  261.         value: updateStates,
    
  262.         subHooks: [],
    
  263.       },
    
  264.     ]);
    
  265.   });
    
  266. 
    
  267.   it('should inspect the current state of all stateful hooks, including useInsertionEffect', async () => {
    
  268.     const useInsertionEffect = React.useInsertionEffect;
    
  269.     const outsideRef = React.createRef();
    
  270.     function effect() {}
    
  271.     function Foo(props) {
    
  272.       const [state1, setState] = React.useState('a');
    
  273.       const [state2, dispatch] = React.useReducer((s, a) => a.value, 'b');
    
  274.       const ref = React.useRef('c');
    
  275. 
    
  276.       useInsertionEffect(effect);
    
  277.       React.useLayoutEffect(effect);
    
  278.       React.useEffect(effect);
    
  279. 
    
  280.       React.useImperativeHandle(
    
  281.         outsideRef,
    
  282.         () => {
    
  283.           // Return a function so that jest treats them as non-equal.
    
  284.           return function Instance() {};
    
  285.         },
    
  286.         [],
    
  287.       );
    
  288. 
    
  289.       React.useMemo(() => state1 + state2, [state1]);
    
  290. 
    
  291.       async function update() {
    
  292.         setState('A');
    
  293.         dispatch({value: 'B'});
    
  294.         ref.current = 'C';
    
  295.       }
    
  296.       const memoizedUpdate = React.useCallback(update, []);
    
  297.       return (
    
  298.         <div onClick={memoizedUpdate}>
    
  299.           {state1} {state2}
    
  300.         </div>
    
  301.       );
    
  302.     }
    
  303.     let renderer;
    
  304.     await act(() => {
    
  305.       renderer = ReactTestRenderer.create(<Foo prop="prop" />);
    
  306.     });
    
  307. 
    
  308.     let childFiber = renderer.root.findByType(Foo)._currentFiber();
    
  309. 
    
  310.     const {onClick: updateStates} = renderer.root.findByType('div').props;
    
  311. 
    
  312.     let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  313.     expect(tree).toEqual([
    
  314.       {
    
  315.         isStateEditable: true,
    
  316.         id: 0,
    
  317.         name: 'State',
    
  318.         value: 'a',
    
  319.         subHooks: [],
    
  320.       },
    
  321.       {
    
  322.         isStateEditable: true,
    
  323.         id: 1,
    
  324.         name: 'Reducer',
    
  325.         value: 'b',
    
  326.         subHooks: [],
    
  327.       },
    
  328.       {isStateEditable: false, id: 2, name: 'Ref', value: 'c', subHooks: []},
    
  329.       {
    
  330.         isStateEditable: false,
    
  331.         id: 3,
    
  332.         name: 'InsertionEffect',
    
  333.         value: effect,
    
  334.         subHooks: [],
    
  335.       },
    
  336.       {
    
  337.         isStateEditable: false,
    
  338.         id: 4,
    
  339.         name: 'LayoutEffect',
    
  340.         value: effect,
    
  341.         subHooks: [],
    
  342.       },
    
  343.       {
    
  344.         isStateEditable: false,
    
  345.         id: 5,
    
  346.         name: 'Effect',
    
  347.         value: effect,
    
  348.         subHooks: [],
    
  349.       },
    
  350.       {
    
  351.         isStateEditable: false,
    
  352.         id: 6,
    
  353.         name: 'ImperativeHandle',
    
  354.         value: outsideRef.current,
    
  355.         subHooks: [],
    
  356.       },
    
  357.       {
    
  358.         isStateEditable: false,
    
  359.         id: 7,
    
  360.         name: 'Memo',
    
  361.         value: 'ab',
    
  362.         subHooks: [],
    
  363.       },
    
  364.       {
    
  365.         isStateEditable: false,
    
  366.         id: 8,
    
  367.         name: 'Callback',
    
  368.         value: updateStates,
    
  369.         subHooks: [],
    
  370.       },
    
  371.     ]);
    
  372. 
    
  373.     await act(() => {
    
  374.       updateStates();
    
  375.     });
    
  376. 
    
  377.     childFiber = renderer.root.findByType(Foo)._currentFiber();
    
  378.     tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  379. 
    
  380.     expect(tree).toEqual([
    
  381.       {
    
  382.         isStateEditable: true,
    
  383.         id: 0,
    
  384.         name: 'State',
    
  385.         value: 'A',
    
  386.         subHooks: [],
    
  387.       },
    
  388.       {
    
  389.         isStateEditable: true,
    
  390.         id: 1,
    
  391.         name: 'Reducer',
    
  392.         value: 'B',
    
  393.         subHooks: [],
    
  394.       },
    
  395.       {isStateEditable: false, id: 2, name: 'Ref', value: 'C', subHooks: []},
    
  396.       {
    
  397.         isStateEditable: false,
    
  398.         id: 3,
    
  399.         name: 'InsertionEffect',
    
  400.         value: effect,
    
  401.         subHooks: [],
    
  402.       },
    
  403.       {
    
  404.         isStateEditable: false,
    
  405.         id: 4,
    
  406.         name: 'LayoutEffect',
    
  407.         value: effect,
    
  408.         subHooks: [],
    
  409.       },
    
  410.       {
    
  411.         isStateEditable: false,
    
  412.         id: 5,
    
  413.         name: 'Effect',
    
  414.         value: effect,
    
  415.         subHooks: [],
    
  416.       },
    
  417.       {
    
  418.         isStateEditable: false,
    
  419.         id: 6,
    
  420.         name: 'ImperativeHandle',
    
  421.         value: outsideRef.current,
    
  422.         subHooks: [],
    
  423.       },
    
  424.       {
    
  425.         isStateEditable: false,
    
  426.         id: 7,
    
  427.         name: 'Memo',
    
  428.         value: 'Ab',
    
  429.         subHooks: [],
    
  430.       },
    
  431.       {
    
  432.         isStateEditable: false,
    
  433.         id: 8,
    
  434.         name: 'Callback',
    
  435.         value: updateStates,
    
  436.         subHooks: [],
    
  437.       },
    
  438.     ]);
    
  439.   });
    
  440. 
    
  441.   it('should inspect the value of the current provider in useContext', () => {
    
  442.     const MyContext = React.createContext('default');
    
  443.     function Foo(props) {
    
  444.       const value = React.useContext(MyContext);
    
  445.       return <div>{value}</div>;
    
  446.     }
    
  447.     const renderer = ReactTestRenderer.create(
    
  448.       <MyContext.Provider value="contextual">
    
  449.         <Foo prop="prop" />
    
  450.       </MyContext.Provider>,
    
  451.     );
    
  452.     const childFiber = renderer.root.findByType(Foo)._currentFiber();
    
  453.     const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  454.     expect(tree).toEqual([
    
  455.       {
    
  456.         isStateEditable: false,
    
  457.         id: null,
    
  458.         name: 'Context',
    
  459.         value: 'contextual',
    
  460.         subHooks: [],
    
  461.       },
    
  462.     ]);
    
  463.   });
    
  464. 
    
  465.   it('should inspect forwardRef', () => {
    
  466.     const obj = function () {};
    
  467.     const Foo = React.forwardRef(function (props, ref) {
    
  468.       React.useImperativeHandle(ref, () => obj);
    
  469.       return <div />;
    
  470.     });
    
  471.     const ref = React.createRef();
    
  472.     const renderer = ReactTestRenderer.create(<Foo ref={ref} />);
    
  473. 
    
  474.     const childFiber = renderer.root.findByType(Foo)._currentFiber();
    
  475.     const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  476.     expect(tree).toEqual([
    
  477.       {
    
  478.         isStateEditable: false,
    
  479.         id: 0,
    
  480.         name: 'ImperativeHandle',
    
  481.         value: obj,
    
  482.         subHooks: [],
    
  483.       },
    
  484.     ]);
    
  485.   });
    
  486. 
    
  487.   it('should inspect memo', () => {
    
  488.     function InnerFoo(props) {
    
  489.       const [value] = React.useState('hello');
    
  490.       return <div>{value}</div>;
    
  491.     }
    
  492.     const Foo = React.memo(InnerFoo);
    
  493.     const renderer = ReactTestRenderer.create(<Foo />);
    
  494.     // TODO: Test renderer findByType is broken for memo. Have to search for the inner.
    
  495.     const childFiber = renderer.root.findByType(InnerFoo)._currentFiber();
    
  496.     const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  497.     expect(tree).toEqual([
    
  498.       {
    
  499.         isStateEditable: true,
    
  500.         id: 0,
    
  501.         name: 'State',
    
  502.         value: 'hello',
    
  503.         subHooks: [],
    
  504.       },
    
  505.     ]);
    
  506.   });
    
  507. 
    
  508.   it('should inspect custom hooks', () => {
    
  509.     function useCustom() {
    
  510.       const [value] = React.useState('hello');
    
  511.       return value;
    
  512.     }
    
  513.     function Foo(props) {
    
  514.       const value = useCustom();
    
  515.       return <div>{value}</div>;
    
  516.     }
    
  517.     const renderer = ReactTestRenderer.create(<Foo />);
    
  518.     const childFiber = renderer.root.findByType(Foo)._currentFiber();
    
  519.     const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  520.     expect(tree).toEqual([
    
  521.       {
    
  522.         isStateEditable: false,
    
  523.         id: null,
    
  524.         name: 'Custom',
    
  525.         value: undefined,
    
  526.         subHooks: [
    
  527.           {
    
  528.             isStateEditable: true,
    
  529.             id: 0,
    
  530.             name: 'State',
    
  531.             value: 'hello',
    
  532.             subHooks: [],
    
  533.           },
    
  534.         ],
    
  535.       },
    
  536.     ]);
    
  537.   });
    
  538. 
    
  539.   it('should support composite useTransition hook', () => {
    
  540.     function Foo(props) {
    
  541.       React.useTransition();
    
  542.       const memoizedValue = React.useMemo(() => 'hello', []);
    
  543.       React.useMemo(() => 'not used', []);
    
  544.       return <div>{memoizedValue}</div>;
    
  545.     }
    
  546.     const renderer = ReactTestRenderer.create(<Foo />);
    
  547.     const childFiber = renderer.root.findByType(Foo)._currentFiber();
    
  548.     const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  549.     expect(tree).toEqual([
    
  550.       {
    
  551.         id: 0,
    
  552.         isStateEditable: false,
    
  553.         name: 'Transition',
    
  554.         value: undefined,
    
  555.         subHooks: [],
    
  556.       },
    
  557.       {
    
  558.         id: 1,
    
  559.         isStateEditable: false,
    
  560.         name: 'Memo',
    
  561.         value: 'hello',
    
  562.         subHooks: [],
    
  563.       },
    
  564.       {
    
  565.         id: 2,
    
  566.         isStateEditable: false,
    
  567.         name: 'Memo',
    
  568.         value: 'not used',
    
  569.         subHooks: [],
    
  570.       },
    
  571.     ]);
    
  572.   });
    
  573. 
    
  574.   it('should support useDeferredValue hook', () => {
    
  575.     function Foo(props) {
    
  576.       React.useDeferredValue('abc');
    
  577.       const memoizedValue = React.useMemo(() => 1, []);
    
  578.       React.useMemo(() => 2, []);
    
  579.       return <div>{memoizedValue}</div>;
    
  580.     }
    
  581.     const renderer = ReactTestRenderer.create(<Foo />);
    
  582.     const childFiber = renderer.root.findByType(Foo)._currentFiber();
    
  583.     const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  584.     expect(tree).toEqual([
    
  585.       {
    
  586.         id: 0,
    
  587.         isStateEditable: false,
    
  588.         name: 'DeferredValue',
    
  589.         value: 'abc',
    
  590.         subHooks: [],
    
  591.       },
    
  592.       {
    
  593.         id: 1,
    
  594.         isStateEditable: false,
    
  595.         name: 'Memo',
    
  596.         value: 1,
    
  597.         subHooks: [],
    
  598.       },
    
  599.       {
    
  600.         id: 2,
    
  601.         isStateEditable: false,
    
  602.         name: 'Memo',
    
  603.         value: 2,
    
  604.         subHooks: [],
    
  605.       },
    
  606.     ]);
    
  607.   });
    
  608. 
    
  609.   it('should support useId hook', () => {
    
  610.     function Foo(props) {
    
  611.       const id = React.useId();
    
  612.       const [state] = React.useState('hello');
    
  613.       return <div id={id}>{state}</div>;
    
  614.     }
    
  615. 
    
  616.     const renderer = ReactTestRenderer.create(<Foo />);
    
  617.     const childFiber = renderer.root.findByType(Foo)._currentFiber();
    
  618.     const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  619. 
    
  620.     expect(tree.length).toEqual(2);
    
  621. 
    
  622.     expect(tree[0].id).toEqual(0);
    
  623.     expect(tree[0].isStateEditable).toEqual(false);
    
  624.     expect(tree[0].name).toEqual('Id');
    
  625.     expect(String(tree[0].value).startsWith(':r')).toBe(true);
    
  626. 
    
  627.     expect(tree[1]).toEqual({
    
  628.       id: 1,
    
  629.       isStateEditable: true,
    
  630.       name: 'State',
    
  631.       value: 'hello',
    
  632.       subHooks: [],
    
  633.     });
    
  634.   });
    
  635. 
    
  636.   // @gate enableUseMemoCacheHook
    
  637.   it('useMemoCache should not be inspectable', () => {
    
  638.     function Foo() {
    
  639.       const $ = useMemoCache(1);
    
  640.       let t0;
    
  641. 
    
  642.       if ($[0] === Symbol.for('react.memo_cache_sentinel')) {
    
  643.         t0 = <div>{1}</div>;
    
  644.         $[0] = t0;
    
  645.       } else {
    
  646.         t0 = $[0];
    
  647.       }
    
  648. 
    
  649.       return t0;
    
  650.     }
    
  651. 
    
  652.     const renderer = ReactTestRenderer.create(<Foo />);
    
  653.     const childFiber = renderer.root.findByType(Foo)._currentFiber();
    
  654.     const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  655. 
    
  656.     expect(tree.length).toEqual(0);
    
  657.   });
    
  658. 
    
  659.   describe('useDebugValue', () => {
    
  660.     it('should support inspectable values for multiple custom hooks', () => {
    
  661.       function useLabeledValue(label) {
    
  662.         const [value] = React.useState(label);
    
  663.         React.useDebugValue(`custom label ${label}`);
    
  664.         return value;
    
  665.       }
    
  666.       function useAnonymous(label) {
    
  667.         const [value] = React.useState(label);
    
  668.         return value;
    
  669.       }
    
  670.       function Example() {
    
  671.         useLabeledValue('a');
    
  672.         React.useState('b');
    
  673.         useAnonymous('c');
    
  674.         useLabeledValue('d');
    
  675.         return null;
    
  676.       }
    
  677.       const renderer = ReactTestRenderer.create(<Example />);
    
  678.       const childFiber = renderer.root.findByType(Example)._currentFiber();
    
  679.       const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  680.       expect(tree).toEqual([
    
  681.         {
    
  682.           isStateEditable: false,
    
  683.           id: null,
    
  684.           name: 'LabeledValue',
    
  685.           value: __DEV__ ? 'custom label a' : undefined,
    
  686.           subHooks: [
    
  687.             {
    
  688.               isStateEditable: true,
    
  689.               id: 0,
    
  690.               name: 'State',
    
  691.               value: 'a',
    
  692.               subHooks: [],
    
  693.             },
    
  694.           ],
    
  695.         },
    
  696.         {
    
  697.           isStateEditable: true,
    
  698.           id: 1,
    
  699.           name: 'State',
    
  700.           value: 'b',
    
  701.           subHooks: [],
    
  702.         },
    
  703.         {
    
  704.           isStateEditable: false,
    
  705.           id: null,
    
  706.           name: 'Anonymous',
    
  707.           value: undefined,
    
  708.           subHooks: [
    
  709.             {
    
  710.               isStateEditable: true,
    
  711.               id: 2,
    
  712.               name: 'State',
    
  713.               value: 'c',
    
  714.               subHooks: [],
    
  715.             },
    
  716.           ],
    
  717.         },
    
  718.         {
    
  719.           isStateEditable: false,
    
  720.           id: null,
    
  721.           name: 'LabeledValue',
    
  722.           value: __DEV__ ? 'custom label d' : undefined,
    
  723.           subHooks: [
    
  724.             {
    
  725.               isStateEditable: true,
    
  726.               id: 3,
    
  727.               name: 'State',
    
  728.               value: 'd',
    
  729.               subHooks: [],
    
  730.             },
    
  731.           ],
    
  732.         },
    
  733.       ]);
    
  734.     });
    
  735. 
    
  736.     it('should support inspectable values for nested custom hooks', () => {
    
  737.       function useInner() {
    
  738.         React.useDebugValue('inner');
    
  739.         React.useState(0);
    
  740.       }
    
  741.       function useOuter() {
    
  742.         React.useDebugValue('outer');
    
  743.         useInner();
    
  744.       }
    
  745.       function Example() {
    
  746.         useOuter();
    
  747.         return null;
    
  748.       }
    
  749.       const renderer = ReactTestRenderer.create(<Example />);
    
  750.       const childFiber = renderer.root.findByType(Example)._currentFiber();
    
  751.       const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  752.       expect(tree).toEqual([
    
  753.         {
    
  754.           isStateEditable: false,
    
  755.           id: null,
    
  756.           name: 'Outer',
    
  757.           value: __DEV__ ? 'outer' : undefined,
    
  758.           subHooks: [
    
  759.             {
    
  760.               isStateEditable: false,
    
  761.               id: null,
    
  762.               name: 'Inner',
    
  763.               value: __DEV__ ? 'inner' : undefined,
    
  764.               subHooks: [
    
  765.                 {
    
  766.                   isStateEditable: true,
    
  767.                   id: 0,
    
  768.                   name: 'State',
    
  769.                   value: 0,
    
  770.                   subHooks: [],
    
  771.                 },
    
  772.               ],
    
  773.             },
    
  774.           ],
    
  775.         },
    
  776.       ]);
    
  777.     });
    
  778. 
    
  779.     it('should support multiple inspectable values per custom hooks', () => {
    
  780.       function useMultiLabelCustom() {
    
  781.         React.useDebugValue('one');
    
  782.         React.useDebugValue('two');
    
  783.         React.useDebugValue('three');
    
  784.         React.useState(0);
    
  785.       }
    
  786.       function useSingleLabelCustom(value) {
    
  787.         React.useDebugValue(`single ${value}`);
    
  788.         React.useState(0);
    
  789.       }
    
  790.       function Example() {
    
  791.         useSingleLabelCustom('one');
    
  792.         useMultiLabelCustom();
    
  793.         useSingleLabelCustom('two');
    
  794.         return null;
    
  795.       }
    
  796.       const renderer = ReactTestRenderer.create(<Example />);
    
  797.       const childFiber = renderer.root.findByType(Example)._currentFiber();
    
  798.       const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  799.       expect(tree).toEqual([
    
  800.         {
    
  801.           isStateEditable: false,
    
  802.           id: null,
    
  803.           name: 'SingleLabelCustom',
    
  804.           value: __DEV__ ? 'single one' : undefined,
    
  805.           subHooks: [
    
  806.             {
    
  807.               isStateEditable: true,
    
  808.               id: 0,
    
  809.               name: 'State',
    
  810.               value: 0,
    
  811.               subHooks: [],
    
  812.             },
    
  813.           ],
    
  814.         },
    
  815.         {
    
  816.           isStateEditable: false,
    
  817.           id: null,
    
  818.           name: 'MultiLabelCustom',
    
  819.           value: __DEV__ ? ['one', 'two', 'three'] : undefined,
    
  820.           subHooks: [
    
  821.             {
    
  822.               isStateEditable: true,
    
  823.               id: 1,
    
  824.               name: 'State',
    
  825.               value: 0,
    
  826.               subHooks: [],
    
  827.             },
    
  828.           ],
    
  829.         },
    
  830.         {
    
  831.           isStateEditable: false,
    
  832.           id: null,
    
  833.           name: 'SingleLabelCustom',
    
  834.           value: __DEV__ ? 'single two' : undefined,
    
  835.           subHooks: [
    
  836.             {
    
  837.               isStateEditable: true,
    
  838.               id: 2,
    
  839.               name: 'State',
    
  840.               value: 0,
    
  841.               subHooks: [],
    
  842.             },
    
  843.           ],
    
  844.         },
    
  845.       ]);
    
  846.     });
    
  847. 
    
  848.     it('should ignore useDebugValue() made outside of a custom hook', () => {
    
  849.       function Example() {
    
  850.         React.useDebugValue('this is invalid');
    
  851.         return null;
    
  852.       }
    
  853.       const renderer = ReactTestRenderer.create(<Example />);
    
  854.       const childFiber = renderer.root.findByType(Example)._currentFiber();
    
  855.       const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  856.       expect(tree).toHaveLength(0);
    
  857.     });
    
  858. 
    
  859.     it('should support an optional formatter function param', () => {
    
  860.       function useCustom() {
    
  861.         React.useDebugValue({bar: 123}, object => `bar:${object.bar}`);
    
  862.         React.useState(0);
    
  863.       }
    
  864.       function Example() {
    
  865.         useCustom();
    
  866.         return null;
    
  867.       }
    
  868.       const renderer = ReactTestRenderer.create(<Example />);
    
  869.       const childFiber = renderer.root.findByType(Example)._currentFiber();
    
  870.       const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  871.       expect(tree).toEqual([
    
  872.         {
    
  873.           isStateEditable: false,
    
  874.           id: null,
    
  875.           name: 'Custom',
    
  876.           value: __DEV__ ? 'bar:123' : undefined,
    
  877.           subHooks: [
    
  878.             {
    
  879.               isStateEditable: true,
    
  880.               id: 0,
    
  881.               name: 'State',
    
  882.               subHooks: [],
    
  883.               value: 0,
    
  884.             },
    
  885.           ],
    
  886.         },
    
  887.       ]);
    
  888.     });
    
  889.   });
    
  890. 
    
  891.   it('should support defaultProps and lazy', async () => {
    
  892.     const Suspense = React.Suspense;
    
  893. 
    
  894.     function Foo(props) {
    
  895.       const [value] = React.useState(props.defaultValue.slice(0, 3));
    
  896.       return <div>{value}</div>;
    
  897.     }
    
  898.     Foo.defaultProps = {
    
  899.       defaultValue: 'default',
    
  900.     };
    
  901. 
    
  902.     async function fakeImport(result) {
    
  903.       return {default: result};
    
  904.     }
    
  905. 
    
  906.     const LazyFoo = React.lazy(() => fakeImport(Foo));
    
  907. 
    
  908.     const renderer = ReactTestRenderer.create(
    
  909.       <Suspense fallback="Loading...">
    
  910.         <LazyFoo />
    
  911.       </Suspense>,
    
  912.     );
    
  913. 
    
  914.     await expect(async () => {
    
  915.       await act(async () => await LazyFoo);
    
  916.     }).toErrorDev([
    
  917.       'Foo: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.',
    
  918.     ]);
    
  919. 
    
  920.     const childFiber = renderer.root._currentFiber();
    
  921.     const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  922.     expect(tree).toEqual([
    
  923.       {
    
  924.         isStateEditable: true,
    
  925.         id: 0,
    
  926.         name: 'State',
    
  927.         value: 'def',
    
  928.         subHooks: [],
    
  929.       },
    
  930.     ]);
    
  931.   });
    
  932. 
    
  933.   it('should support an injected dispatcher', () => {
    
  934.     function Foo(props) {
    
  935.       const [state] = React.useState('hello world');
    
  936.       return <div>{state}</div>;
    
  937.     }
    
  938. 
    
  939.     const initial = {};
    
  940.     let current = initial;
    
  941.     let getterCalls = 0;
    
  942.     const setterCalls = [];
    
  943.     const FakeDispatcherRef = {
    
  944.       get current() {
    
  945.         getterCalls++;
    
  946.         return current;
    
  947.       },
    
  948.       set current(value) {
    
  949.         setterCalls.push(value);
    
  950.         current = value;
    
  951.       },
    
  952.     };
    
  953. 
    
  954.     const renderer = ReactTestRenderer.create(<Foo />);
    
  955.     const childFiber = renderer.root._currentFiber();
    
  956. 
    
  957.     let didCatch = false;
    
  958. 
    
  959.     try {
    
  960.       ReactDebugTools.inspectHooksOfFiber(childFiber, FakeDispatcherRef);
    
  961.     } catch (error) {
    
  962.       expect(error.message).toBe('Error rendering inspected component');
    
  963.       expect(error.cause).toBeInstanceOf(Error);
    
  964.       expect(error.cause.message).toBe(
    
  965.         'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
    
  966.           ' one of the following reasons:\n' +
    
  967.           '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
    
  968.           '2. You might be breaking the Rules of Hooks\n' +
    
  969.           '3. You might have more than one copy of React in the same app\n' +
    
  970.           'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.',
    
  971.       );
    
  972.       didCatch = true;
    
  973.     }
    
  974.     // avoid false positive if no error was thrown at all
    
  975.     expect(didCatch).toBe(true);
    
  976. 
    
  977.     expect(getterCalls).toBe(1);
    
  978.     expect(setterCalls).toHaveLength(2);
    
  979.     expect(setterCalls[0]).not.toBe(initial);
    
  980.     expect(setterCalls[1]).toBe(initial);
    
  981.   });
    
  982. 
    
  983.   // This test case is based on an open source bug report:
    
  984.   // https://github.com/facebookincubator/redux-react-hook/issues/34#issuecomment-466693787
    
  985.   it('should properly advance the current hook for useContext', async () => {
    
  986.     const MyContext = React.createContext(1);
    
  987. 
    
  988.     let incrementCount;
    
  989. 
    
  990.     function Foo(props) {
    
  991.       const context = React.useContext(MyContext);
    
  992.       const [data, setData] = React.useState({count: context});
    
  993. 
    
  994.       incrementCount = () => setData(({count}) => ({count: count + 1}));
    
  995. 
    
  996.       return <div>count: {data.count}</div>;
    
  997.     }
    
  998. 
    
  999.     const renderer = ReactTestRenderer.create(<Foo />);
    
  1000.     expect(renderer.toJSON()).toEqual({
    
  1001.       type: 'div',
    
  1002.       props: {},
    
  1003.       children: ['count: ', '1'],
    
  1004.     });
    
  1005. 
    
  1006.     await act(() => incrementCount());
    
  1007.     expect(renderer.toJSON()).toEqual({
    
  1008.       type: 'div',
    
  1009.       props: {},
    
  1010.       children: ['count: ', '2'],
    
  1011.     });
    
  1012. 
    
  1013.     const childFiber = renderer.root._currentFiber();
    
  1014.     const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  1015.     expect(tree).toEqual([
    
  1016.       {
    
  1017.         isStateEditable: false,
    
  1018.         id: null,
    
  1019.         name: 'Context',
    
  1020.         value: 1,
    
  1021.         subHooks: [],
    
  1022.       },
    
  1023.       {
    
  1024.         isStateEditable: true,
    
  1025.         id: 0,
    
  1026.         name: 'State',
    
  1027.         value: {count: 2},
    
  1028.         subHooks: [],
    
  1029.       },
    
  1030.     ]);
    
  1031.   });
    
  1032. 
    
  1033.   it('should support composite useSyncExternalStore hook', () => {
    
  1034.     const useSyncExternalStore = React.useSyncExternalStore;
    
  1035.     function Foo() {
    
  1036.       const value = useSyncExternalStore(
    
  1037.         () => () => {},
    
  1038.         () => 'snapshot',
    
  1039.       );
    
  1040.       React.useMemo(() => 'memo', []);
    
  1041.       React.useMemo(() => 'not used', []);
    
  1042.       return value;
    
  1043.     }
    
  1044. 
    
  1045.     const renderer = ReactTestRenderer.create(<Foo />);
    
  1046.     const childFiber = renderer.root.findByType(Foo)._currentFiber();
    
  1047.     const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
    
  1048.     expect(tree).toEqual([
    
  1049.       {
    
  1050.         id: 0,
    
  1051.         isStateEditable: false,
    
  1052.         name: 'SyncExternalStore',
    
  1053.         value: 'snapshot',
    
  1054.         subHooks: [],
    
  1055.       },
    
  1056.       {
    
  1057.         id: 1,
    
  1058.         isStateEditable: false,
    
  1059.         name: 'Memo',
    
  1060.         value: 'memo',
    
  1061.         subHooks: [],
    
  1062.       },
    
  1063.       {
    
  1064.         id: 2,
    
  1065.         isStateEditable: false,
    
  1066.         name: 'Memo',
    
  1067.         value: 'not used',
    
  1068.         subHooks: [],
    
  1069.       },
    
  1070.     ]);
    
  1071.   });
    
  1072. });