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.  * @flow
    
  8.  */
    
  9. 
    
  10. import typeof ReactTestRenderer from 'react-test-renderer';
    
  11. import {withErrorsOrWarningsIgnored} from 'react-devtools-shared/src/__tests__/utils';
    
  12. 
    
  13. import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
    
  14. import type Store from 'react-devtools-shared/src/devtools/store';
    
  15. 
    
  16. describe('InspectedElement', () => {
    
  17.   let React;
    
  18.   let ReactDOM;
    
  19.   let ReactDOMClient;
    
  20.   let PropTypes;
    
  21.   let TestRenderer: ReactTestRenderer;
    
  22.   let bridge: FrontendBridge;
    
  23.   let store: Store;
    
  24.   let utils;
    
  25. 
    
  26.   let BridgeContext;
    
  27.   let InspectedElementContext;
    
  28.   let InspectedElementContextController;
    
  29.   let SettingsContextController;
    
  30.   let StoreContext;
    
  31.   let TreeContextController;
    
  32. 
    
  33.   let TestUtilsAct;
    
  34.   let TestRendererAct;
    
  35. 
    
  36.   let legacyRender;
    
  37.   let testRendererInstance;
    
  38. 
    
  39.   let ErrorBoundary;
    
  40.   let errorBoundaryInstance;
    
  41. 
    
  42.   global.IS_REACT_ACT_ENVIRONMENT = true;
    
  43. 
    
  44.   beforeEach(() => {
    
  45.     utils = require('./utils');
    
  46.     utils.beforeEachProfiling();
    
  47. 
    
  48.     legacyRender = utils.legacyRender;
    
  49. 
    
  50.     bridge = global.bridge;
    
  51.     store = global.store;
    
  52.     store.collapseNodesByDefault = false;
    
  53. 
    
  54.     React = require('react');
    
  55.     ReactDOM = require('react-dom');
    
  56.     ReactDOMClient = require('react-dom/client');
    
  57.     PropTypes = require('prop-types');
    
  58.     TestUtilsAct = require('internal-test-utils').act;
    
  59.     TestRenderer = utils.requireTestRenderer();
    
  60.     TestRendererAct = require('internal-test-utils').act;
    
  61. 
    
  62.     BridgeContext =
    
  63.       require('react-devtools-shared/src/devtools/views/context').BridgeContext;
    
  64.     InspectedElementContext =
    
  65.       require('react-devtools-shared/src/devtools/views/Components/InspectedElementContext').InspectedElementContext;
    
  66.     InspectedElementContextController =
    
  67.       require('react-devtools-shared/src/devtools/views/Components/InspectedElementContext').InspectedElementContextController;
    
  68.     SettingsContextController =
    
  69.       require('react-devtools-shared/src/devtools/views/Settings/SettingsContext').SettingsContextController;
    
  70.     StoreContext =
    
  71.       require('react-devtools-shared/src/devtools/views/context').StoreContext;
    
  72.     TreeContextController =
    
  73.       require('react-devtools-shared/src/devtools/views/Components/TreeContext').TreeContextController;
    
  74. 
    
  75.     // Used by inspectElementAtIndex() helper function
    
  76.     utils.act(() => {
    
  77.       testRendererInstance = TestRenderer.create(null, {
    
  78.         unstable_isConcurrent: true,
    
  79.       });
    
  80.     });
    
  81. 
    
  82.     errorBoundaryInstance = null;
    
  83. 
    
  84.     ErrorBoundary = class extends React.Component {
    
  85.       state = {error: null};
    
  86.       componentDidCatch(error) {
    
  87.         this.setState({error});
    
  88.       }
    
  89.       render() {
    
  90.         errorBoundaryInstance = this;
    
  91. 
    
  92.         if (this.state.error) {
    
  93.           return null;
    
  94.         }
    
  95.         return this.props.children;
    
  96.       }
    
  97.     };
    
  98.   });
    
  99. 
    
  100.   afterEach(() => {
    
  101.     jest.restoreAllMocks();
    
  102.   });
    
  103. 
    
  104.   const Contexts = ({
    
  105.     children,
    
  106.     defaultSelectedElementID = null,
    
  107.     defaultSelectedElementIndex = null,
    
  108.   }) => (
    
  109.     <BridgeContext.Provider value={bridge}>
    
  110.       <StoreContext.Provider value={store}>
    
  111.         <SettingsContextController>
    
  112.           <TreeContextController
    
  113.             defaultSelectedElementID={defaultSelectedElementID}
    
  114.             defaultSelectedElementIndex={defaultSelectedElementIndex}>
    
  115.             <React.Suspense fallback="Loading...">
    
  116.               <InspectedElementContextController>
    
  117.                 {children}
    
  118.               </InspectedElementContextController>
    
  119.             </React.Suspense>
    
  120.           </TreeContextController>
    
  121.         </SettingsContextController>
    
  122.       </StoreContext.Provider>
    
  123.     </BridgeContext.Provider>
    
  124.   );
    
  125. 
    
  126.   function useInspectedElement() {
    
  127.     const {inspectedElement} = React.useContext(InspectedElementContext);
    
  128.     return inspectedElement;
    
  129.   }
    
  130. 
    
  131.   function useInspectElementPath() {
    
  132.     const {inspectPaths} = React.useContext(InspectedElementContext);
    
  133.     return inspectPaths;
    
  134.   }
    
  135. 
    
  136.   function noop() {}
    
  137. 
    
  138.   async function inspectElementAtIndex(
    
  139.     index,
    
  140.     useCustomHook = noop,
    
  141.     shouldThrow = false,
    
  142.   ) {
    
  143.     let didFinish = false;
    
  144.     let inspectedElement = null;
    
  145. 
    
  146.     function Suspender() {
    
  147.       useCustomHook();
    
  148.       inspectedElement = useInspectedElement();
    
  149.       didFinish = true;
    
  150.       return null;
    
  151.     }
    
  152. 
    
  153.     const id = ((store.getElementIDAtIndex(index): any): number);
    
  154. 
    
  155.     await utils.actAsync(() => {
    
  156.       testRendererInstance.update(
    
  157.         <ErrorBoundary>
    
  158.           <Contexts
    
  159.             defaultSelectedElementID={id}
    
  160.             defaultSelectedElementIndex={index}>
    
  161.             <React.Suspense fallback={null}>
    
  162.               <Suspender id={id} index={index} />
    
  163.             </React.Suspense>
    
  164.           </Contexts>
    
  165.         </ErrorBoundary>,
    
  166.       );
    
  167.     }, false);
    
  168. 
    
  169.     if (!shouldThrow) {
    
  170.       expect(didFinish).toBe(true);
    
  171.     }
    
  172. 
    
  173.     return inspectedElement;
    
  174.   }
    
  175. 
    
  176.   it('should inspect the currently selected element', async () => {
    
  177.     const Example = () => {
    
  178.       const [count] = React.useState(1);
    
  179.       return count;
    
  180.     };
    
  181. 
    
  182.     const container = document.createElement('div');
    
  183.     await utils.actAsync(() =>
    
  184.       legacyRender(<Example a={1} b="abc" />, container),
    
  185.     );
    
  186. 
    
  187.     const inspectedElement = await inspectElementAtIndex(0);
    
  188.     expect(inspectedElement).toMatchInlineSnapshot(`
    
  189.       {
    
  190.         "context": null,
    
  191.         "events": undefined,
    
  192.         "hooks": [
    
  193.           {
    
  194.             "hookSource": {
    
  195.               "columnNumber": "removed by Jest serializer",
    
  196.               "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js",
    
  197.               "functionName": "Example",
    
  198.               "lineNumber": "removed by Jest serializer",
    
  199.             },
    
  200.             "id": 0,
    
  201.             "isStateEditable": true,
    
  202.             "name": "State",
    
  203.             "subHooks": [],
    
  204.             "value": 1,
    
  205.           },
    
  206.         ],
    
  207.         "id": 2,
    
  208.         "owners": null,
    
  209.         "props": {
    
  210.           "a": 1,
    
  211.           "b": "abc",
    
  212.         },
    
  213.         "rootType": "render()",
    
  214.         "state": null,
    
  215.       }
    
  216.     `);
    
  217.   });
    
  218. 
    
  219.   it('should have hasLegacyContext flag set to either "true" or "false" depending on which context API is used.', async () => {
    
  220.     const contextData = {
    
  221.       bool: true,
    
  222.     };
    
  223. 
    
  224.     // Legacy Context API.
    
  225.     class LegacyContextProvider extends React.Component<any> {
    
  226.       static childContextTypes = {
    
  227.         bool: PropTypes.bool,
    
  228.       };
    
  229.       getChildContext() {
    
  230.         return contextData;
    
  231.       }
    
  232.       render() {
    
  233.         return this.props.children;
    
  234.       }
    
  235.     }
    
  236.     class LegacyContextConsumer extends React.Component<any> {
    
  237.       static contextTypes = {
    
  238.         bool: PropTypes.bool,
    
  239.       };
    
  240.       render() {
    
  241.         return null;
    
  242.       }
    
  243.     }
    
  244. 
    
  245.     // Modern Context API
    
  246.     const BoolContext = React.createContext(contextData.bool);
    
  247.     BoolContext.displayName = 'BoolContext';
    
  248. 
    
  249.     class ModernContextType extends React.Component<any> {
    
  250.       static contextType = BoolContext;
    
  251.       render() {
    
  252.         return null;
    
  253.       }
    
  254.     }
    
  255. 
    
  256.     const ModernContext = React.createContext();
    
  257.     ModernContext.displayName = 'ModernContext';
    
  258. 
    
  259.     const container = document.createElement('div');
    
  260.     await utils.actAsync(() =>
    
  261.       legacyRender(
    
  262.         <React.Fragment>
    
  263.           <LegacyContextProvider>
    
  264.             <LegacyContextConsumer />
    
  265.           </LegacyContextProvider>
    
  266.           <BoolContext.Consumer>{value => null}</BoolContext.Consumer>
    
  267.           <ModernContextType />
    
  268.           <ModernContext.Provider value={contextData}>
    
  269.             <ModernContext.Consumer>{value => null}</ModernContext.Consumer>
    
  270.           </ModernContext.Provider>
    
  271.         </React.Fragment>,
    
  272.         container,
    
  273.       ),
    
  274.     );
    
  275. 
    
  276.     const cases = [
    
  277.       {
    
  278.         // <LegacyContextConsumer />
    
  279.         index: 1,
    
  280.         shouldHaveLegacyContext: true,
    
  281.       },
    
  282.       {
    
  283.         // <BoolContext.Consumer>
    
  284.         index: 2,
    
  285.         shouldHaveLegacyContext: false,
    
  286.       },
    
  287.       {
    
  288.         // <ModernContextType />
    
  289.         index: 3,
    
  290.         shouldHaveLegacyContext: false,
    
  291.       },
    
  292.       {
    
  293.         // <ModernContext.Consumer>
    
  294.         index: 5,
    
  295.         shouldHaveLegacyContext: false,
    
  296.       },
    
  297.     ];
    
  298. 
    
  299.     for (let i = 0; i < cases.length; i++) {
    
  300.       const {index, shouldHaveLegacyContext} = cases[i];
    
  301. 
    
  302.       // HACK: Recreate TestRenderer instance because we rely on default state values
    
  303.       // from props like defaultSelectedElementID and it's easier to reset here than
    
  304.       // to read the TreeDispatcherContext and update the selected ID that way.
    
  305.       // We're testing the inspected values here, not the context wiring, so that's ok.
    
  306.       utils.withErrorsOrWarningsIgnored(
    
  307.         ['An update to %s inside a test was not wrapped in act'],
    
  308.         () => {
    
  309.           testRendererInstance = TestRenderer.create(null, {
    
  310.             unstable_isConcurrent: true,
    
  311.           });
    
  312.         },
    
  313.       );
    
  314. 
    
  315.       const inspectedElement = await inspectElementAtIndex(index);
    
  316. 
    
  317.       expect(inspectedElement.context).not.toBe(null);
    
  318.       expect(inspectedElement.hasLegacyContext).toBe(shouldHaveLegacyContext);
    
  319.     }
    
  320.   });
    
  321. 
    
  322.   it('should poll for updates for the currently selected element', async () => {
    
  323.     const Example = () => null;
    
  324. 
    
  325.     const container = document.createElement('div');
    
  326.     await utils.actAsync(
    
  327.       () => legacyRender(<Example a={1} b="abc" />, container),
    
  328.       false,
    
  329.     );
    
  330. 
    
  331.     let inspectedElement = await inspectElementAtIndex(0);
    
  332.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  333.       {
    
  334.         "a": 1,
    
  335.         "b": "abc",
    
  336.       }
    
  337.     `);
    
  338. 
    
  339.     await utils.actAsync(
    
  340.       () => legacyRender(<Example a={2} b="def" />, container),
    
  341.       false,
    
  342.     );
    
  343. 
    
  344.     // TODO (cache)
    
  345.     // This test only passes if both the check-for-updates poll AND the test renderer.update() call are included below.
    
  346.     // It seems like either one of the two should be sufficient but:
    
  347.     // 1. Running only check-for-updates schedules a transition that React never renders.
    
  348.     // 2. Running only renderer.update() loads stale data (first props)
    
  349. 
    
  350.     // Wait for our check-for-updates poll to get the new data.
    
  351.     jest.runOnlyPendingTimers();
    
  352.     await Promise.resolve();
    
  353. 
    
  354.     inspectedElement = await inspectElementAtIndex(0);
    
  355.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  356.       {
    
  357.         "a": 2,
    
  358.         "b": "def",
    
  359.       }
    
  360.     `);
    
  361.   });
    
  362. 
    
  363.   it('should not re-render a function with hooks if it did not update since it was last inspected', async () => {
    
  364.     let targetRenderCount = 0;
    
  365. 
    
  366.     const Wrapper = ({children}) => children;
    
  367.     const Target = React.memo(props => {
    
  368.       targetRenderCount++;
    
  369.       // Even though his hook isn't referenced, it's used to observe backend rendering.
    
  370.       React.useState(0);
    
  371.       return null;
    
  372.     });
    
  373. 
    
  374.     const container = document.createElement('div');
    
  375.     await utils.actAsync(() =>
    
  376.       legacyRender(
    
  377.         <Wrapper>
    
  378.           <Target a={1} b="abc" />
    
  379.         </Wrapper>,
    
  380.         container,
    
  381.       ),
    
  382.     );
    
  383. 
    
  384.     targetRenderCount = 0;
    
  385. 
    
  386.     let inspectedElement = await inspectElementAtIndex(1);
    
  387.     expect(targetRenderCount).toBe(1);
    
  388.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  389.       {
    
  390.         "a": 1,
    
  391.         "b": "abc",
    
  392.       }
    
  393.     `);
    
  394. 
    
  395.     const prevInspectedElement = inspectedElement;
    
  396. 
    
  397.     targetRenderCount = 0;
    
  398.     inspectedElement = await inspectElementAtIndex(1);
    
  399.     expect(targetRenderCount).toBe(0);
    
  400.     expect(inspectedElement).toEqual(prevInspectedElement);
    
  401. 
    
  402.     targetRenderCount = 0;
    
  403. 
    
  404.     await utils.actAsync(
    
  405.       () =>
    
  406.         legacyRender(
    
  407.           <Wrapper>
    
  408.             <Target a={2} b="def" />
    
  409.           </Wrapper>,
    
  410.           container,
    
  411.         ),
    
  412.       false,
    
  413.     );
    
  414. 
    
  415.     // Target should have been rendered once (by ReactDOM) and once by DevTools for inspection.
    
  416.     inspectedElement = await inspectElementAtIndex(1);
    
  417.     expect(targetRenderCount).toBe(2);
    
  418.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  419.       {
    
  420.         "a": 2,
    
  421.         "b": "def",
    
  422.       }
    
  423.     `);
    
  424.   });
    
  425. 
    
  426.   // See github.com/facebook/react/issues/22241#issuecomment-931299972
    
  427.   it('should properly recover from a cache miss on the frontend', async () => {
    
  428.     let targetRenderCount = 0;
    
  429. 
    
  430.     const Wrapper = ({children}) => children;
    
  431.     const Target = React.memo(props => {
    
  432.       targetRenderCount++;
    
  433.       // Even though his hook isn't referenced, it's used to observe backend rendering.
    
  434.       React.useState(0);
    
  435.       return null;
    
  436.     });
    
  437. 
    
  438.     const container = document.createElement('div');
    
  439.     await utils.actAsync(() =>
    
  440.       legacyRender(
    
  441.         <Wrapper>
    
  442.           <Target a={1} b="abc" />
    
  443.         </Wrapper>,
    
  444.         container,
    
  445.       ),
    
  446.     );
    
  447. 
    
  448.     targetRenderCount = 0;
    
  449. 
    
  450.     let inspectedElement = await inspectElementAtIndex(1);
    
  451.     expect(targetRenderCount).toBe(1);
    
  452.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  453.       {
    
  454.         "a": 1,
    
  455.         "b": "abc",
    
  456.       }
    
  457.     `);
    
  458. 
    
  459.     const prevInspectedElement = inspectedElement;
    
  460. 
    
  461.     // This test causes an intermediate error to be logged but we can ignore it.
    
  462.     jest.spyOn(console, 'error').mockImplementation(() => {});
    
  463. 
    
  464.     // Clear the frontend cache to simulate DevTools being closed and re-opened.
    
  465.     // The backend still thinks the most recently-inspected element is still cached,
    
  466.     // so the frontend needs to tell it to resend a full value.
    
  467.     // We can verify this by asserting that the component is re-rendered again.
    
  468.     utils.withErrorsOrWarningsIgnored(
    
  469.       ['An update to %s inside a test was not wrapped in act'],
    
  470.       () => {
    
  471.         testRendererInstance = TestRenderer.create(null, {
    
  472.           unstable_isConcurrent: true,
    
  473.         });
    
  474.       },
    
  475.     );
    
  476. 
    
  477.     const {
    
  478.       clearCacheForTests,
    
  479.     } = require('react-devtools-shared/src/inspectedElementMutableSource');
    
  480.     clearCacheForTests();
    
  481. 
    
  482.     targetRenderCount = 0;
    
  483.     inspectedElement = await inspectElementAtIndex(1);
    
  484.     expect(targetRenderCount).toBe(1);
    
  485.     expect(inspectedElement).toEqual(prevInspectedElement);
    
  486.   });
    
  487. 
    
  488.   it('should temporarily disable console logging when re-running a component to inspect its hooks', async () => {
    
  489.     let targetRenderCount = 0;
    
  490. 
    
  491.     jest.spyOn(console, 'error').mockImplementation(() => {});
    
  492.     jest.spyOn(console, 'info').mockImplementation(() => {});
    
  493.     jest.spyOn(console, 'log').mockImplementation(() => {});
    
  494.     jest.spyOn(console, 'warn').mockImplementation(() => {});
    
  495. 
    
  496.     const Target = React.memo(props => {
    
  497.       targetRenderCount++;
    
  498.       console.error('error');
    
  499.       console.info('info');
    
  500.       console.log('log');
    
  501.       console.warn('warn');
    
  502.       React.useState(0);
    
  503.       return null;
    
  504.     });
    
  505. 
    
  506.     const container = document.createElement('div');
    
  507.     const root = ReactDOMClient.createRoot(container);
    
  508.     await utils.actAsync(() => root.render(<Target a={1} b="abc" />));
    
  509. 
    
  510.     expect(targetRenderCount).toBe(1);
    
  511.     expect(console.error).toHaveBeenCalledTimes(1);
    
  512.     expect(console.error).toHaveBeenCalledWith('error');
    
  513.     expect(console.info).toHaveBeenCalledTimes(1);
    
  514.     expect(console.info).toHaveBeenCalledWith('info');
    
  515.     expect(console.log).toHaveBeenCalledTimes(1);
    
  516.     expect(console.log).toHaveBeenCalledWith('log');
    
  517.     expect(console.warn).toHaveBeenCalledTimes(1);
    
  518.     expect(console.warn).toHaveBeenCalledWith('warn');
    
  519. 
    
  520.     const inspectedElement = await inspectElementAtIndex(0);
    
  521. 
    
  522.     expect(inspectedElement).not.toBe(null);
    
  523.     expect(targetRenderCount).toBe(2);
    
  524.     expect(console.error).toHaveBeenCalledTimes(1);
    
  525.     expect(console.info).toHaveBeenCalledTimes(1);
    
  526.     expect(console.log).toHaveBeenCalledTimes(1);
    
  527.     expect(console.warn).toHaveBeenCalledTimes(1);
    
  528.   });
    
  529. 
    
  530.   it('should support simple data types', async () => {
    
  531.     const Example = () => null;
    
  532. 
    
  533.     const container = document.createElement('div');
    
  534.     await utils.actAsync(() =>
    
  535.       legacyRender(
    
  536.         <Example
    
  537.           boolean_false={false}
    
  538.           boolean_true={true}
    
  539.           infinity={Infinity}
    
  540.           integer_zero={0}
    
  541.           integer_one={1}
    
  542.           float={1.23}
    
  543.           string="abc"
    
  544.           string_empty=""
    
  545.           nan={NaN}
    
  546.           value_null={null}
    
  547.           value_undefined={undefined}
    
  548.         />,
    
  549.         container,
    
  550.       ),
    
  551.     );
    
  552. 
    
  553.     const inspectedElement = await inspectElementAtIndex(0);
    
  554. 
    
  555.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  556.       {
    
  557.         "boolean_false": false,
    
  558.         "boolean_true": true,
    
  559.         "float": 1.23,
    
  560.         "infinity": Infinity,
    
  561.         "integer_one": 1,
    
  562.         "integer_zero": 0,
    
  563.         "nan": NaN,
    
  564.         "string": "abc",
    
  565.         "string_empty": "",
    
  566.         "value_null": null,
    
  567.         "value_undefined": undefined,
    
  568.       }
    
  569.     `);
    
  570.   });
    
  571. 
    
  572.   it('should support complex data types', async () => {
    
  573.     const Immutable = require('immutable');
    
  574. 
    
  575.     const Example = () => null;
    
  576. 
    
  577.     const arrayOfArrays = [[['abc', 123, true], []]];
    
  578.     const div = document.createElement('div');
    
  579.     const exampleFunction = () => {};
    
  580.     const exampleDateISO = '2019-12-31T23:42:42.000Z';
    
  581.     const setShallow = new Set(['abc', 123]);
    
  582.     const mapShallow = new Map([
    
  583.       ['name', 'Brian'],
    
  584.       ['food', 'sushi'],
    
  585.     ]);
    
  586.     const setOfSets = new Set([new Set(['a', 'b', 'c']), new Set([1, 2, 3])]);
    
  587.     const mapOfMaps = new Map([
    
  588.       ['first', mapShallow],
    
  589.       ['second', mapShallow],
    
  590.     ]);
    
  591.     const objectOfObjects = {
    
  592.       inner: {string: 'abc', number: 123, boolean: true},
    
  593.     };
    
  594.     const objectWithSymbol = {
    
  595.       [Symbol('name')]: 'hello',
    
  596.     };
    
  597.     const typedArray = Int8Array.from([100, -100, 0]);
    
  598.     const arrayBuffer = typedArray.buffer;
    
  599.     const dataView = new DataView(arrayBuffer);
    
  600.     const immutableMap = Immutable.fromJS({
    
  601.       a: [{hello: 'there'}, 'fixed', true],
    
  602.       b: 123,
    
  603.       c: {
    
  604.         '1': 'xyz',
    
  605.         xyz: 1,
    
  606.       },
    
  607.     });
    
  608. 
    
  609.     class Class {
    
  610.       anonymousFunction = () => {};
    
  611.     }
    
  612.     const instance = new Class();
    
  613. 
    
  614.     const proxyInstance = new Proxy(() => {}, {
    
  615.       get: function (_, name) {
    
  616.         return function () {
    
  617.           return null;
    
  618.         };
    
  619.       },
    
  620.     });
    
  621. 
    
  622.     const container = document.createElement('div');
    
  623.     await utils.actAsync(() =>
    
  624.       legacyRender(
    
  625.         <Example
    
  626.           anonymous_fn={instance.anonymousFunction}
    
  627.           array_buffer={arrayBuffer}
    
  628.           array_of_arrays={arrayOfArrays}
    
  629.           // eslint-disable-next-line no-undef
    
  630.           big_int={BigInt(123)}
    
  631.           bound_fn={exampleFunction.bind(this)}
    
  632.           data_view={dataView}
    
  633.           date={new Date(exampleDateISO)}
    
  634.           fn={exampleFunction}
    
  635.           html_element={div}
    
  636.           immutable={immutableMap}
    
  637.           map={mapShallow}
    
  638.           map_of_maps={mapOfMaps}
    
  639.           object_of_objects={objectOfObjects}
    
  640.           object_with_symbol={objectWithSymbol}
    
  641.           proxy={proxyInstance}
    
  642.           react_element={<span />}
    
  643.           regexp={/abc/giu}
    
  644.           set={setShallow}
    
  645.           set_of_sets={setOfSets}
    
  646.           symbol={Symbol('symbol')}
    
  647.           typed_array={typedArray}
    
  648.         />,
    
  649.         container,
    
  650.       ),
    
  651.     );
    
  652. 
    
  653.     const inspectedElement = await inspectElementAtIndex(0);
    
  654. 
    
  655.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  656.       {
    
  657.         "anonymous_fn": Dehydrated {
    
  658.           "preview_short": ƒ () {},
    
  659.           "preview_long": ƒ () {},
    
  660.         },
    
  661.         "array_buffer": Dehydrated {
    
  662.           "preview_short": ArrayBuffer(3),
    
  663.           "preview_long": ArrayBuffer(3),
    
  664.         },
    
  665.         "array_of_arrays": [
    
  666.           Dehydrated {
    
  667.             "preview_short": Array(2),
    
  668.             "preview_long": [Array(3), Array(0)],
    
  669.           },
    
  670.         ],
    
  671.         "big_int": Dehydrated {
    
  672.           "preview_short": 123n,
    
  673.           "preview_long": 123n,
    
  674.         },
    
  675.         "bound_fn": Dehydrated {
    
  676.           "preview_short": ƒ bound exampleFunction() {},
    
  677.           "preview_long": ƒ bound exampleFunction() {},
    
  678.         },
    
  679.         "data_view": Dehydrated {
    
  680.           "preview_short": DataView(3),
    
  681.           "preview_long": DataView(3),
    
  682.         },
    
  683.         "date": Dehydrated {
    
  684.           "preview_short": Tue Dec 31 2019 23:42:42 GMT+0000 (Coordinated Universal Time),
    
  685.           "preview_long": Tue Dec 31 2019 23:42:42 GMT+0000 (Coordinated Universal Time),
    
  686.         },
    
  687.         "fn": Dehydrated {
    
  688.           "preview_short": ƒ exampleFunction() {},
    
  689.           "preview_long": ƒ exampleFunction() {},
    
  690.         },
    
  691.         "html_element": Dehydrated {
    
  692.           "preview_short": <div />,
    
  693.           "preview_long": <div />,
    
  694.         },
    
  695.         "immutable": {
    
  696.           "0": Dehydrated {
    
  697.             "preview_short": Array(2),
    
  698.             "preview_long": ["a", List(3)],
    
  699.           },
    
  700.           "1": Dehydrated {
    
  701.             "preview_short": Array(2),
    
  702.             "preview_long": ["b", 123],
    
  703.           },
    
  704.           "2": Dehydrated {
    
  705.             "preview_short": Array(2),
    
  706.             "preview_long": ["c", Map(2)],
    
  707.           },
    
  708.         },
    
  709.         "map": {
    
  710.           "0": Dehydrated {
    
  711.             "preview_short": Array(2),
    
  712.             "preview_long": ["name", "Brian"],
    
  713.           },
    
  714.           "1": Dehydrated {
    
  715.             "preview_short": Array(2),
    
  716.             "preview_long": ["food", "sushi"],
    
  717.           },
    
  718.         },
    
  719.         "map_of_maps": {
    
  720.           "0": Dehydrated {
    
  721.             "preview_short": Array(2),
    
  722.             "preview_long": ["first", Map(2)],
    
  723.           },
    
  724.           "1": Dehydrated {
    
  725.             "preview_short": Array(2),
    
  726.             "preview_long": ["second", Map(2)],
    
  727.           },
    
  728.         },
    
  729.         "object_of_objects": {
    
  730.           "inner": Dehydrated {
    
  731.             "preview_short": {…},
    
  732.             "preview_long": {boolean: true, number: 123, string: "abc"},
    
  733.           },
    
  734.         },
    
  735.         "object_with_symbol": {
    
  736.           "Symbol(name)": "hello",
    
  737.         },
    
  738.         "proxy": Dehydrated {
    
  739.           "preview_short": ƒ () {},
    
  740.           "preview_long": ƒ () {},
    
  741.         },
    
  742.         "react_element": Dehydrated {
    
  743.           "preview_short": <span />,
    
  744.           "preview_long": <span />,
    
  745.         },
    
  746.         "regexp": Dehydrated {
    
  747.           "preview_short": /abc/giu,
    
  748.           "preview_long": /abc/giu,
    
  749.         },
    
  750.         "set": {
    
  751.           "0": "abc",
    
  752.           "1": 123,
    
  753.         },
    
  754.         "set_of_sets": {
    
  755.           "0": Dehydrated {
    
  756.             "preview_short": Set(3),
    
  757.             "preview_long": Set(3) {"a", "b", "c"},
    
  758.           },
    
  759.           "1": Dehydrated {
    
  760.             "preview_short": Set(3),
    
  761.             "preview_long": Set(3) {1, 2, 3},
    
  762.           },
    
  763.         },
    
  764.         "symbol": Dehydrated {
    
  765.           "preview_short": Symbol(symbol),
    
  766.           "preview_long": Symbol(symbol),
    
  767.         },
    
  768.         "typed_array": {
    
  769.           "0": 100,
    
  770.           "1": -100,
    
  771.           "2": 0,
    
  772.         },
    
  773.       }
    
  774.     `);
    
  775.   });
    
  776. 
    
  777.   it('should not consume iterables while inspecting', async () => {
    
  778.     const Example = () => null;
    
  779. 
    
  780.     function* generator() {
    
  781.       throw Error('Should not be consumed!');
    
  782.     }
    
  783. 
    
  784.     const container = document.createElement('div');
    
  785. 
    
  786.     const iterable = generator();
    
  787.     await utils.actAsync(() =>
    
  788.       legacyRender(<Example prop={iterable} />, container),
    
  789.     );
    
  790. 
    
  791.     const inspectedElement = await inspectElementAtIndex(0);
    
  792.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  793.       {
    
  794.         "prop": Dehydrated {
    
  795.           "preview_short": Generator,
    
  796.           "preview_long": Generator,
    
  797.         },
    
  798.       }
    
  799.     `);
    
  800.   });
    
  801. 
    
  802.   it('should support objects with no prototype', async () => {
    
  803.     const Example = () => null;
    
  804. 
    
  805.     const object = Object.create(null);
    
  806.     object.string = 'abc';
    
  807.     object.number = 123;
    
  808.     object.boolean = true;
    
  809. 
    
  810.     const container = document.createElement('div');
    
  811.     await utils.actAsync(() =>
    
  812.       legacyRender(<Example object={object} />, container),
    
  813.     );
    
  814. 
    
  815.     const inspectedElement = await inspectElementAtIndex(0);
    
  816.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  817.       {
    
  818.         "object": {
    
  819.           "boolean": true,
    
  820.           "number": 123,
    
  821.           "string": "abc",
    
  822.         },
    
  823.       }
    
  824.     `);
    
  825.   });
    
  826. 
    
  827.   it('should support objects with overridden hasOwnProperty', async () => {
    
  828.     const Example = () => null;
    
  829. 
    
  830.     const object = {
    
  831.       name: 'blah',
    
  832.       hasOwnProperty: true,
    
  833.     };
    
  834. 
    
  835.     const container = document.createElement('div');
    
  836.     await utils.actAsync(() =>
    
  837.       legacyRender(<Example object={object} />, container),
    
  838.     );
    
  839. 
    
  840.     const inspectedElement = await inspectElementAtIndex(0);
    
  841. 
    
  842.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  843.       {
    
  844.         "object": {
    
  845.           "hasOwnProperty": true,
    
  846.           "name": "blah",
    
  847.         },
    
  848.       }
    
  849.     `);
    
  850.   });
    
  851. 
    
  852.   it('should support custom objects with enumerable properties and getters', async () => {
    
  853.     class CustomData {
    
  854.       _number = 42;
    
  855.       get number() {
    
  856.         return this._number;
    
  857.       }
    
  858.       set number(value) {
    
  859.         this._number = value;
    
  860.       }
    
  861.     }
    
  862. 
    
  863.     const descriptor = ((Object.getOwnPropertyDescriptor(
    
  864.       CustomData.prototype,
    
  865.       'number',
    
  866.     ): any): PropertyDescriptor<number>);
    
  867.     descriptor.enumerable = true;
    
  868.     Object.defineProperty(CustomData.prototype, 'number', descriptor);
    
  869. 
    
  870.     const Example = () => null;
    
  871. 
    
  872.     const container = document.createElement('div');
    
  873.     await utils.actAsync(() =>
    
  874.       legacyRender(<Example data={new CustomData()} />, container),
    
  875.     );
    
  876. 
    
  877.     const inspectedElement = await inspectElementAtIndex(0);
    
  878.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  879.       {
    
  880.         "data": {
    
  881.           "_number": 42,
    
  882.           "number": 42,
    
  883.         },
    
  884.       }
    
  885.     `);
    
  886.   });
    
  887. 
    
  888.   it('should support objects with inherited keys', async () => {
    
  889.     const Example = () => null;
    
  890. 
    
  891.     const base = Object.create(Object.prototype, {
    
  892.       enumerableStringBase: {
    
  893.         value: 1,
    
  894.         writable: true,
    
  895.         enumerable: true,
    
  896.         configurable: true,
    
  897.       },
    
  898.       [Symbol('enumerableSymbolBase')]: {
    
  899.         value: 1,
    
  900.         writable: true,
    
  901.         enumerable: true,
    
  902.         configurable: true,
    
  903.       },
    
  904.       nonEnumerableStringBase: {
    
  905.         value: 1,
    
  906.         writable: true,
    
  907.         enumerable: false,
    
  908.         configurable: true,
    
  909.       },
    
  910.       [Symbol('nonEnumerableSymbolBase')]: {
    
  911.         value: 1,
    
  912.         writable: true,
    
  913.         enumerable: false,
    
  914.         configurable: true,
    
  915.       },
    
  916.     });
    
  917. 
    
  918.     const object = Object.create(base, {
    
  919.       enumerableString: {
    
  920.         value: 2,
    
  921.         writable: true,
    
  922.         enumerable: true,
    
  923.         configurable: true,
    
  924.       },
    
  925.       nonEnumerableString: {
    
  926.         value: 3,
    
  927.         writable: true,
    
  928.         enumerable: false,
    
  929.         configurable: true,
    
  930.       },
    
  931.       123: {
    
  932.         value: 3,
    
  933.         writable: true,
    
  934.         enumerable: true,
    
  935.         configurable: true,
    
  936.       },
    
  937.       [Symbol('nonEnumerableSymbol')]: {
    
  938.         value: 2,
    
  939.         writable: true,
    
  940.         enumerable: false,
    
  941.         configurable: true,
    
  942.       },
    
  943.       [Symbol('enumerableSymbol')]: {
    
  944.         value: 3,
    
  945.         writable: true,
    
  946.         enumerable: true,
    
  947.         configurable: true,
    
  948.       },
    
  949.     });
    
  950. 
    
  951.     const container = document.createElement('div');
    
  952.     await utils.actAsync(() =>
    
  953.       legacyRender(<Example object={object} />, container),
    
  954.     );
    
  955. 
    
  956.     const inspectedElement = await inspectElementAtIndex(0);
    
  957.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  958.       {
    
  959.         "object": {
    
  960.           "123": 3,
    
  961.           "Symbol(enumerableSymbol)": 3,
    
  962.           "Symbol(enumerableSymbolBase)": 1,
    
  963.           "enumerableString": 2,
    
  964.           "enumerableStringBase": 1,
    
  965.         },
    
  966.       }
    
  967.     `);
    
  968.   });
    
  969. 
    
  970.   it('should allow component prop value and value`s prototype has same name params.', async () => {
    
  971.     const testData = Object.create(
    
  972.       {
    
  973.         a: undefined,
    
  974.         b: Infinity,
    
  975.         c: NaN,
    
  976.         d: 'normal',
    
  977.       },
    
  978.       {
    
  979.         a: {
    
  980.           value: undefined,
    
  981.           writable: true,
    
  982.           enumerable: true,
    
  983.           configurable: true,
    
  984.         },
    
  985.         b: {
    
  986.           value: Infinity,
    
  987.           writable: true,
    
  988.           enumerable: true,
    
  989.           configurable: true,
    
  990.         },
    
  991.         c: {
    
  992.           value: NaN,
    
  993.           writable: true,
    
  994.           enumerable: true,
    
  995.           configurable: true,
    
  996.         },
    
  997.         d: {
    
  998.           value: 'normal',
    
  999.           writable: true,
    
  1000.           enumerable: true,
    
  1001.           configurable: true,
    
  1002.         },
    
  1003.       },
    
  1004.     );
    
  1005.     const Example = ({data}) => null;
    
  1006.     const container = document.createElement('div');
    
  1007.     await utils.actAsync(() =>
    
  1008.       legacyRender(<Example data={testData} />, container),
    
  1009.     );
    
  1010. 
    
  1011.     const inspectedElement = await inspectElementAtIndex(0);
    
  1012.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  1013.       {
    
  1014.         "data": {
    
  1015.           "a": undefined,
    
  1016.           "b": Infinity,
    
  1017.           "c": NaN,
    
  1018.           "d": "normal",
    
  1019.         },
    
  1020.       }
    
  1021.     `);
    
  1022.   });
    
  1023. 
    
  1024.   it('should not dehydrate nested values until explicitly requested', async () => {
    
  1025.     const Example = () => {
    
  1026.       const [state] = React.useState({
    
  1027.         foo: {
    
  1028.           bar: {
    
  1029.             baz: 'hi',
    
  1030.           },
    
  1031.         },
    
  1032.       });
    
  1033. 
    
  1034.       return state.foo.bar.baz;
    
  1035.     };
    
  1036. 
    
  1037.     const container = document.createElement('div');
    
  1038.     await utils.actAsync(() =>
    
  1039.       legacyRender(
    
  1040.         <Example
    
  1041.           nestedObject={{
    
  1042.             a: {
    
  1043.               b: {
    
  1044.                 c: [
    
  1045.                   {
    
  1046.                     d: {
    
  1047.                       e: {},
    
  1048.                     },
    
  1049.                   },
    
  1050.                 ],
    
  1051.               },
    
  1052.             },
    
  1053.           }}
    
  1054.         />,
    
  1055.         container,
    
  1056.       ),
    
  1057.     );
    
  1058. 
    
  1059.     let inspectedElement = null;
    
  1060.     let inspectElementPath = null;
    
  1061. 
    
  1062.     // Render once to get a handle on inspectElementPath()
    
  1063.     inspectedElement = await inspectElementAtIndex(0, () => {
    
  1064.       inspectElementPath = useInspectElementPath();
    
  1065.     });
    
  1066. 
    
  1067.     async function loadPath(path) {
    
  1068.       await TestUtilsAct(async () => {
    
  1069.         await TestRendererAct(async () => {
    
  1070.           inspectElementPath(path);
    
  1071.         });
    
  1072.       });
    
  1073. 
    
  1074.       inspectedElement = await inspectElementAtIndex(0);
    
  1075.     }
    
  1076. 
    
  1077.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  1078.       {
    
  1079.         "nestedObject": {
    
  1080.           "a": Dehydrated {
    
  1081.             "preview_short": {…},
    
  1082.             "preview_long": {b: {…}},
    
  1083.           },
    
  1084.         },
    
  1085.       }
    
  1086.     `);
    
  1087. 
    
  1088.     await loadPath(['props', 'nestedObject', 'a']);
    
  1089. 
    
  1090.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  1091.       {
    
  1092.         "nestedObject": {
    
  1093.           "a": {
    
  1094.             "b": {
    
  1095.               "c": Dehydrated {
    
  1096.                 "preview_short": Array(1),
    
  1097.                 "preview_long": [{…}],
    
  1098.               },
    
  1099.             },
    
  1100.           },
    
  1101.         },
    
  1102.       }
    
  1103.     `);
    
  1104. 
    
  1105.     await loadPath(['props', 'nestedObject', 'a', 'b', 'c']);
    
  1106. 
    
  1107.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  1108.       {
    
  1109.         "nestedObject": {
    
  1110.           "a": {
    
  1111.             "b": {
    
  1112.               "c": [
    
  1113.                 {
    
  1114.                   "d": Dehydrated {
    
  1115.                     "preview_short": {…},
    
  1116.                     "preview_long": {e: {…}},
    
  1117.                   },
    
  1118.                 },
    
  1119.               ],
    
  1120.             },
    
  1121.           },
    
  1122.         },
    
  1123.       }
    
  1124.     `);
    
  1125. 
    
  1126.     await loadPath(['props', 'nestedObject', 'a', 'b', 'c', 0, 'd']);
    
  1127. 
    
  1128.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  1129.       {
    
  1130.         "nestedObject": {
    
  1131.           "a": {
    
  1132.             "b": {
    
  1133.               "c": [
    
  1134.                 {
    
  1135.                   "d": {
    
  1136.                     "e": {},
    
  1137.                   },
    
  1138.                 },
    
  1139.               ],
    
  1140.             },
    
  1141.           },
    
  1142.         },
    
  1143.       }
    
  1144.     `);
    
  1145. 
    
  1146.     await loadPath(['hooks', 0, 'value']);
    
  1147. 
    
  1148.     expect(inspectedElement.hooks).toMatchInlineSnapshot(`
    
  1149.       [
    
  1150.         {
    
  1151.           "hookSource": {
    
  1152.             "columnNumber": "removed by Jest serializer",
    
  1153.             "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js",
    
  1154.             "functionName": "Example",
    
  1155.             "lineNumber": "removed by Jest serializer",
    
  1156.           },
    
  1157.           "id": 0,
    
  1158.           "isStateEditable": true,
    
  1159.           "name": "State",
    
  1160.           "subHooks": [],
    
  1161.           "value": {
    
  1162.             "foo": {
    
  1163.               "bar": Dehydrated {
    
  1164.                 "preview_short": {…},
    
  1165.                 "preview_long": {baz: "hi"},
    
  1166.               },
    
  1167.             },
    
  1168.           },
    
  1169.         },
    
  1170.       ]
    
  1171.     `);
    
  1172. 
    
  1173.     await loadPath(['hooks', 0, 'value', 'foo', 'bar']);
    
  1174. 
    
  1175.     expect(inspectedElement.hooks).toMatchInlineSnapshot(`
    
  1176.       [
    
  1177.         {
    
  1178.           "hookSource": {
    
  1179.             "columnNumber": "removed by Jest serializer",
    
  1180.             "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js",
    
  1181.             "functionName": "Example",
    
  1182.             "lineNumber": "removed by Jest serializer",
    
  1183.           },
    
  1184.           "id": 0,
    
  1185.           "isStateEditable": true,
    
  1186.           "name": "State",
    
  1187.           "subHooks": [],
    
  1188.           "value": {
    
  1189.             "foo": {
    
  1190.               "bar": {
    
  1191.                 "baz": "hi",
    
  1192.               },
    
  1193.             },
    
  1194.           },
    
  1195.         },
    
  1196.       ]
    
  1197.     `);
    
  1198.   });
    
  1199. 
    
  1200.   it('should dehydrate complex nested values when requested', async () => {
    
  1201.     const Example = () => null;
    
  1202. 
    
  1203.     const container = document.createElement('div');
    
  1204.     await utils.actAsync(() =>
    
  1205.       legacyRender(
    
  1206.         <Example
    
  1207.           set_of_sets={new Set([new Set([1, 2, 3]), new Set(['a', 'b', 'c'])])}
    
  1208.         />,
    
  1209.         container,
    
  1210.       ),
    
  1211.     );
    
  1212. 
    
  1213.     let inspectedElement = null;
    
  1214.     let inspectElementPath = null;
    
  1215. 
    
  1216.     // Render once to get a handle on inspectElementPath()
    
  1217.     inspectedElement = await inspectElementAtIndex(0, () => {
    
  1218.       inspectElementPath = useInspectElementPath();
    
  1219.     });
    
  1220. 
    
  1221.     async function loadPath(path) {
    
  1222.       await TestUtilsAct(async () => {
    
  1223.         await TestRendererAct(async () => {
    
  1224.           inspectElementPath(path);
    
  1225.         });
    
  1226.       });
    
  1227. 
    
  1228.       inspectedElement = await inspectElementAtIndex(0);
    
  1229.     }
    
  1230. 
    
  1231.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  1232.       {
    
  1233.         "set_of_sets": {
    
  1234.           "0": Dehydrated {
    
  1235.             "preview_short": Set(3),
    
  1236.             "preview_long": Set(3) {1, 2, 3},
    
  1237.           },
    
  1238.           "1": Dehydrated {
    
  1239.             "preview_short": Set(3),
    
  1240.             "preview_long": Set(3) {"a", "b", "c"},
    
  1241.           },
    
  1242.         },
    
  1243.       }
    
  1244.     `);
    
  1245. 
    
  1246.     await loadPath(['props', 'set_of_sets', 0]);
    
  1247. 
    
  1248.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  1249.       {
    
  1250.         "set_of_sets": {
    
  1251.           "0": {
    
  1252.             "0": 1,
    
  1253.             "1": 2,
    
  1254.             "2": 3,
    
  1255.           },
    
  1256.           "1": Dehydrated {
    
  1257.             "preview_short": Set(3),
    
  1258.             "preview_long": Set(3) {"a", "b", "c"},
    
  1259.           },
    
  1260.         },
    
  1261.       }
    
  1262.     `);
    
  1263.   });
    
  1264. 
    
  1265.   it('should include updates for nested values that were previously hydrated', async () => {
    
  1266.     const Example = () => null;
    
  1267. 
    
  1268.     const container = document.createElement('div');
    
  1269.     await utils.actAsync(() =>
    
  1270.       legacyRender(
    
  1271.         <Example
    
  1272.           nestedObject={{
    
  1273.             a: {
    
  1274.               value: 1,
    
  1275.               b: {
    
  1276.                 value: 1,
    
  1277.               },
    
  1278.             },
    
  1279.             c: {
    
  1280.               value: 1,
    
  1281.               d: {
    
  1282.                 value: 1,
    
  1283.                 e: {
    
  1284.                   value: 1,
    
  1285.                 },
    
  1286.               },
    
  1287.             },
    
  1288.           }}
    
  1289.         />,
    
  1290.         container,
    
  1291.       ),
    
  1292.     );
    
  1293. 
    
  1294.     let inspectedElement = null;
    
  1295.     let inspectElementPath = null;
    
  1296. 
    
  1297.     // Render once to get a handle on inspectElementPath()
    
  1298.     inspectedElement = await inspectElementAtIndex(0, () => {
    
  1299.       inspectElementPath = useInspectElementPath();
    
  1300.     });
    
  1301. 
    
  1302.     async function loadPath(path) {
    
  1303.       await TestUtilsAct(async () => {
    
  1304.         await TestRendererAct(async () => {
    
  1305.           inspectElementPath(path);
    
  1306.         });
    
  1307.       });
    
  1308. 
    
  1309.       inspectedElement = await inspectElementAtIndex(0);
    
  1310.     }
    
  1311. 
    
  1312.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  1313.       {
    
  1314.         "nestedObject": {
    
  1315.           "a": Dehydrated {
    
  1316.             "preview_short": {…},
    
  1317.             "preview_long": {b: {…}, value: 1},
    
  1318.           },
    
  1319.           "c": Dehydrated {
    
  1320.             "preview_short": {…},
    
  1321.             "preview_long": {d: {…}, value: 1},
    
  1322.           },
    
  1323.         },
    
  1324.       }
    
  1325.     `);
    
  1326. 
    
  1327.     await loadPath(['props', 'nestedObject', 'a']);
    
  1328. 
    
  1329.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  1330.       {
    
  1331.         "nestedObject": {
    
  1332.           "a": {
    
  1333.             "b": {
    
  1334.               "value": 1,
    
  1335.             },
    
  1336.             "value": 1,
    
  1337.           },
    
  1338.           "c": Dehydrated {
    
  1339.             "preview_short": {…},
    
  1340.             "preview_long": {d: {…}, value: 1},
    
  1341.           },
    
  1342.         },
    
  1343.       }
    
  1344.     `);
    
  1345. 
    
  1346.     await loadPath(['props', 'nestedObject', 'c']);
    
  1347. 
    
  1348.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  1349.       {
    
  1350.         "nestedObject": {
    
  1351.           "a": {
    
  1352.             "b": {
    
  1353.               "value": 1,
    
  1354.             },
    
  1355.             "value": 1,
    
  1356.           },
    
  1357.           "c": {
    
  1358.             "d": {
    
  1359.               "e": Dehydrated {
    
  1360.                 "preview_short": {…},
    
  1361.                 "preview_long": {value: 1},
    
  1362.               },
    
  1363.               "value": 1,
    
  1364.             },
    
  1365.             "value": 1,
    
  1366.           },
    
  1367.         },
    
  1368.       }
    
  1369.     `);
    
  1370. 
    
  1371.     await TestRendererAct(async () => {
    
  1372.       await TestUtilsAct(async () => {
    
  1373.         legacyRender(
    
  1374.           <Example
    
  1375.             nestedObject={{
    
  1376.               a: {
    
  1377.                 value: 2,
    
  1378.                 b: {
    
  1379.                   value: 2,
    
  1380.                 },
    
  1381.               },
    
  1382.               c: {
    
  1383.                 value: 2,
    
  1384.                 d: {
    
  1385.                   value: 2,
    
  1386.                   e: {
    
  1387.                     value: 2,
    
  1388.                   },
    
  1389.                 },
    
  1390.               },
    
  1391.             }}
    
  1392.           />,
    
  1393.           container,
    
  1394.         );
    
  1395.       });
    
  1396.     });
    
  1397. 
    
  1398.     // Wait for pending poll-for-update and then update inspected element data.
    
  1399.     jest.runOnlyPendingTimers();
    
  1400.     await Promise.resolve();
    
  1401.     inspectedElement = await inspectElementAtIndex(0);
    
  1402. 
    
  1403.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  1404.       {
    
  1405.         "nestedObject": {
    
  1406.           "a": {
    
  1407.             "b": {
    
  1408.               "value": 2,
    
  1409.             },
    
  1410.             "value": 2,
    
  1411.           },
    
  1412.           "c": {
    
  1413.             "d": {
    
  1414.               "e": Dehydrated {
    
  1415.                 "preview_short": {…},
    
  1416.                 "preview_long": {value: 2},
    
  1417.               },
    
  1418.               "value": 2,
    
  1419.             },
    
  1420.             "value": 2,
    
  1421.           },
    
  1422.         },
    
  1423.       }
    
  1424.     `);
    
  1425.   });
    
  1426. 
    
  1427.   it('should return a full update if a path is inspected for an object that has other pending changes', async () => {
    
  1428.     const Example = () => null;
    
  1429. 
    
  1430.     const container = document.createElement('div');
    
  1431.     await utils.actAsync(() =>
    
  1432.       legacyRender(
    
  1433.         <Example
    
  1434.           nestedObject={{
    
  1435.             a: {
    
  1436.               value: 1,
    
  1437.               b: {
    
  1438.                 value: 1,
    
  1439.               },
    
  1440.             },
    
  1441.             c: {
    
  1442.               value: 1,
    
  1443.               d: {
    
  1444.                 value: 1,
    
  1445.                 e: {
    
  1446.                   value: 1,
    
  1447.                 },
    
  1448.               },
    
  1449.             },
    
  1450.           }}
    
  1451.         />,
    
  1452.         container,
    
  1453.       ),
    
  1454.     );
    
  1455. 
    
  1456.     let inspectedElement = null;
    
  1457.     let inspectElementPath = null;
    
  1458. 
    
  1459.     // Render once to get a handle on inspectElementPath()
    
  1460.     inspectedElement = await inspectElementAtIndex(0, () => {
    
  1461.       inspectElementPath = useInspectElementPath();
    
  1462.     });
    
  1463. 
    
  1464.     async function loadPath(path) {
    
  1465.       await TestUtilsAct(async () => {
    
  1466.         await TestRendererAct(() => {
    
  1467.           inspectElementPath(path);
    
  1468.         });
    
  1469.       });
    
  1470. 
    
  1471.       inspectedElement = await inspectElementAtIndex(0);
    
  1472.     }
    
  1473. 
    
  1474.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  1475.       {
    
  1476.         "nestedObject": {
    
  1477.           "a": Dehydrated {
    
  1478.             "preview_short": {…},
    
  1479.             "preview_long": {b: {…}, value: 1},
    
  1480.           },
    
  1481.           "c": Dehydrated {
    
  1482.             "preview_short": {…},
    
  1483.             "preview_long": {d: {…}, value: 1},
    
  1484.           },
    
  1485.         },
    
  1486.       }
    
  1487.     `);
    
  1488. 
    
  1489.     await loadPath(['props', 'nestedObject', 'a']);
    
  1490. 
    
  1491.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  1492.       {
    
  1493.         "nestedObject": {
    
  1494.           "a": {
    
  1495.             "b": {
    
  1496.               "value": 1,
    
  1497.             },
    
  1498.             "value": 1,
    
  1499.           },
    
  1500.           "c": Dehydrated {
    
  1501.             "preview_short": {…},
    
  1502.             "preview_long": {d: {…}, value: 1},
    
  1503.           },
    
  1504.         },
    
  1505.       }
    
  1506.     `);
    
  1507. 
    
  1508.     await TestRendererAct(async () => {
    
  1509.       await TestUtilsAct(async () => {
    
  1510.         legacyRender(
    
  1511.           <Example
    
  1512.             nestedObject={{
    
  1513.               a: {
    
  1514.                 value: 2,
    
  1515.                 b: {
    
  1516.                   value: 2,
    
  1517.                 },
    
  1518.               },
    
  1519.               c: {
    
  1520.                 value: 2,
    
  1521.                 d: {
    
  1522.                   value: 2,
    
  1523.                   e: {
    
  1524.                     value: 2,
    
  1525.                   },
    
  1526.                 },
    
  1527.               },
    
  1528.             }}
    
  1529.           />,
    
  1530.           container,
    
  1531.         );
    
  1532.       });
    
  1533.     });
    
  1534. 
    
  1535.     await loadPath(['props', 'nestedObject', 'c']);
    
  1536. 
    
  1537.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  1538.       {
    
  1539.         "nestedObject": {
    
  1540.           "a": {
    
  1541.             "b": {
    
  1542.               "value": 2,
    
  1543.             },
    
  1544.             "value": 2,
    
  1545.           },
    
  1546.           "c": {
    
  1547.             "d": {
    
  1548.               "e": Dehydrated {
    
  1549.                 "preview_short": {…},
    
  1550.                 "preview_long": {value: 2},
    
  1551.               },
    
  1552.               "value": 2,
    
  1553.             },
    
  1554.             "value": 2,
    
  1555.           },
    
  1556.         },
    
  1557.       }
    
  1558.     `);
    
  1559.   });
    
  1560. 
    
  1561.   it('should not tear if hydration is requested after an update', async () => {
    
  1562.     const Example = () => null;
    
  1563. 
    
  1564.     const container = document.createElement('div');
    
  1565.     await utils.actAsync(() =>
    
  1566.       legacyRender(
    
  1567.         <Example
    
  1568.           nestedObject={{
    
  1569.             value: 1,
    
  1570.             a: {
    
  1571.               value: 1,
    
  1572.               b: {
    
  1573.                 value: 1,
    
  1574.               },
    
  1575.             },
    
  1576.           }}
    
  1577.         />,
    
  1578.         container,
    
  1579.       ),
    
  1580.     );
    
  1581. 
    
  1582.     let inspectedElement = null;
    
  1583.     let inspectElementPath = null;
    
  1584. 
    
  1585.     // Render once to get a handle on inspectElementPath()
    
  1586.     inspectedElement = await inspectElementAtIndex(0, () => {
    
  1587.       inspectElementPath = useInspectElementPath();
    
  1588.     });
    
  1589. 
    
  1590.     async function loadPath(path) {
    
  1591.       await TestUtilsAct(async () => {
    
  1592.         await TestRendererAct(() => {
    
  1593.           inspectElementPath(path);
    
  1594.         });
    
  1595.       });
    
  1596. 
    
  1597.       inspectedElement = await inspectElementAtIndex(0);
    
  1598.     }
    
  1599. 
    
  1600.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  1601.       {
    
  1602.         "nestedObject": {
    
  1603.           "a": Dehydrated {
    
  1604.             "preview_short": {…},
    
  1605.             "preview_long": {b: {…}, value: 1},
    
  1606.           },
    
  1607.           "value": 1,
    
  1608.         },
    
  1609.       }
    
  1610.     `);
    
  1611. 
    
  1612.     await TestUtilsAct(async () => {
    
  1613.       legacyRender(
    
  1614.         <Example
    
  1615.           nestedObject={{
    
  1616.             value: 2,
    
  1617.             a: {
    
  1618.               value: 2,
    
  1619.               b: {
    
  1620.                 value: 2,
    
  1621.               },
    
  1622.             },
    
  1623.           }}
    
  1624.         />,
    
  1625.         container,
    
  1626.       );
    
  1627.     });
    
  1628. 
    
  1629.     await loadPath(['props', 'nestedObject', 'a']);
    
  1630. 
    
  1631.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  1632.       {
    
  1633.         "nestedObject": {
    
  1634.           "a": {
    
  1635.             "b": {
    
  1636.               "value": 2,
    
  1637.             },
    
  1638.             "value": 2,
    
  1639.           },
    
  1640.           "value": 2,
    
  1641.         },
    
  1642.       }
    
  1643.     `);
    
  1644.   });
    
  1645. 
    
  1646.   it('should inspect hooks for components that only use context', async () => {
    
  1647.     const Context = React.createContext(true);
    
  1648.     const Example = () => {
    
  1649.       const value = React.useContext(Context);
    
  1650.       return value;
    
  1651.     };
    
  1652. 
    
  1653.     const container = document.createElement('div');
    
  1654.     await utils.actAsync(() =>
    
  1655.       legacyRender(<Example a={1} b="abc" />, container),
    
  1656.     );
    
  1657. 
    
  1658.     const inspectedElement = await inspectElementAtIndex(0);
    
  1659.     expect(inspectedElement).toMatchInlineSnapshot(`
    
  1660.       {
    
  1661.         "context": null,
    
  1662.         "events": undefined,
    
  1663.         "hooks": [
    
  1664.           {
    
  1665.             "hookSource": {
    
  1666.               "columnNumber": "removed by Jest serializer",
    
  1667.               "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js",
    
  1668.               "functionName": "Example",
    
  1669.               "lineNumber": "removed by Jest serializer",
    
  1670.             },
    
  1671.             "id": null,
    
  1672.             "isStateEditable": false,
    
  1673.             "name": "Context",
    
  1674.             "subHooks": [],
    
  1675.             "value": true,
    
  1676.           },
    
  1677.         ],
    
  1678.         "id": 2,
    
  1679.         "owners": null,
    
  1680.         "props": {
    
  1681.           "a": 1,
    
  1682.           "b": "abc",
    
  1683.         },
    
  1684.         "rootType": "render()",
    
  1685.         "state": null,
    
  1686.       }
    
  1687.     `);
    
  1688.   });
    
  1689. 
    
  1690.   it('should enable inspected values to be stored as global variables', async () => {
    
  1691.     const Example = () => null;
    
  1692. 
    
  1693.     const nestedObject = {
    
  1694.       a: {
    
  1695.         value: 1,
    
  1696.         b: {
    
  1697.           value: 1,
    
  1698.           c: {
    
  1699.             value: 1,
    
  1700.           },
    
  1701.         },
    
  1702.       },
    
  1703.     };
    
  1704. 
    
  1705.     await utils.actAsync(() =>
    
  1706.       legacyRender(
    
  1707.         <Example nestedObject={nestedObject} />,
    
  1708.         document.createElement('div'),
    
  1709.       ),
    
  1710.     );
    
  1711. 
    
  1712.     let storeAsGlobal: StoreAsGlobal = ((null: any): StoreAsGlobal);
    
  1713. 
    
  1714.     const id = ((store.getElementIDAtIndex(0): any): number);
    
  1715.     await inspectElementAtIndex(0, () => {
    
  1716.       storeAsGlobal = (path: Array<string | number>) => {
    
  1717.         const rendererID = store.getRendererIDForElement(id);
    
  1718.         if (rendererID !== null) {
    
  1719.           const {
    
  1720.             storeAsGlobal: storeAsGlobalAPI,
    
  1721.           } = require('react-devtools-shared/src/backendAPI');
    
  1722.           storeAsGlobalAPI({
    
  1723.             bridge,
    
  1724.             id,
    
  1725.             path,
    
  1726.             rendererID,
    
  1727.           });
    
  1728.         }
    
  1729.       };
    
  1730.     });
    
  1731. 
    
  1732.     jest.spyOn(console, 'log').mockImplementation(() => {});
    
  1733. 
    
  1734.     // Should store the whole value (not just the hydrated parts)
    
  1735.     storeAsGlobal(['props', 'nestedObject']);
    
  1736.     jest.runOnlyPendingTimers();
    
  1737.     expect(console.log).toHaveBeenCalledWith('$reactTemp0');
    
  1738.     expect(global.$reactTemp0).toBe(nestedObject);
    
  1739. 
    
  1740.     console.log.mockReset();
    
  1741. 
    
  1742.     // Should store the nested property specified (not just the outer value)
    
  1743.     storeAsGlobal(['props', 'nestedObject', 'a', 'b']);
    
  1744.     jest.runOnlyPendingTimers();
    
  1745.     expect(console.log).toHaveBeenCalledWith('$reactTemp1');
    
  1746.     expect(global.$reactTemp1).toBe(nestedObject.a.b);
    
  1747.   });
    
  1748. 
    
  1749.   it('should enable inspected values to be copied to the clipboard', async () => {
    
  1750.     const Example = () => null;
    
  1751. 
    
  1752.     const nestedObject = {
    
  1753.       a: {
    
  1754.         value: 1,
    
  1755.         b: {
    
  1756.           value: 1,
    
  1757.           c: {
    
  1758.             value: 1,
    
  1759.           },
    
  1760.         },
    
  1761.       },
    
  1762.     };
    
  1763. 
    
  1764.     await utils.actAsync(() =>
    
  1765.       legacyRender(
    
  1766.         <Example nestedObject={nestedObject} />,
    
  1767.         document.createElement('div'),
    
  1768.       ),
    
  1769.     );
    
  1770. 
    
  1771.     let copyPath: CopyInspectedElementPath =
    
  1772.       ((null: any): CopyInspectedElementPath);
    
  1773. 
    
  1774.     const id = ((store.getElementIDAtIndex(0): any): number);
    
  1775.     await inspectElementAtIndex(0, () => {
    
  1776.       copyPath = (path: Array<string | number>) => {
    
  1777.         const rendererID = store.getRendererIDForElement(id);
    
  1778.         if (rendererID !== null) {
    
  1779.           const {
    
  1780.             copyInspectedElementPath,
    
  1781.           } = require('react-devtools-shared/src/backendAPI');
    
  1782.           copyInspectedElementPath({
    
  1783.             bridge,
    
  1784.             id,
    
  1785.             path,
    
  1786.             rendererID,
    
  1787.           });
    
  1788.         }
    
  1789.       };
    
  1790.     });
    
  1791. 
    
  1792.     // Should copy the whole value (not just the hydrated parts)
    
  1793.     copyPath(['props', 'nestedObject']);
    
  1794.     jest.runOnlyPendingTimers();
    
  1795.     expect(global.mockClipboardCopy).toHaveBeenCalledTimes(1);
    
  1796.     expect(global.mockClipboardCopy).toHaveBeenCalledWith(
    
  1797.       JSON.stringify(nestedObject, undefined, 2),
    
  1798.     );
    
  1799. 
    
  1800.     global.mockClipboardCopy.mockReset();
    
  1801. 
    
  1802.     // Should copy the nested property specified (not just the outer value)
    
  1803.     copyPath(['props', 'nestedObject', 'a', 'b']);
    
  1804.     jest.runOnlyPendingTimers();
    
  1805.     expect(global.mockClipboardCopy).toHaveBeenCalledTimes(1);
    
  1806.     expect(global.mockClipboardCopy).toHaveBeenCalledWith(
    
  1807.       JSON.stringify(nestedObject.a.b, undefined, 2),
    
  1808.     );
    
  1809.   });
    
  1810. 
    
  1811.   it('should enable complex values to be copied to the clipboard', async () => {
    
  1812.     const Immutable = require('immutable');
    
  1813. 
    
  1814.     const Example = () => null;
    
  1815. 
    
  1816.     const set = new Set(['abc', 123]);
    
  1817.     const map = new Map([
    
  1818.       ['name', 'Brian'],
    
  1819.       ['food', 'sushi'],
    
  1820.     ]);
    
  1821.     const setOfSets = new Set([new Set(['a', 'b', 'c']), new Set([1, 2, 3])]);
    
  1822.     const mapOfMaps = new Map([
    
  1823.       ['first', map],
    
  1824.       ['second', map],
    
  1825.     ]);
    
  1826.     const typedArray = Int8Array.from([100, -100, 0]);
    
  1827.     const arrayBuffer = typedArray.buffer;
    
  1828.     const dataView = new DataView(arrayBuffer);
    
  1829.     const immutable = Immutable.fromJS({
    
  1830.       a: [{hello: 'there'}, 'fixed', true],
    
  1831.       b: 123,
    
  1832.       c: {
    
  1833.         '1': 'xyz',
    
  1834.         xyz: 1,
    
  1835.       },
    
  1836.     });
    
  1837.     const bigInt = BigInt(123); // eslint-disable-line no-undef
    
  1838. 
    
  1839.     await utils.actAsync(() =>
    
  1840.       legacyRender(
    
  1841.         <Example
    
  1842.           arrayBuffer={arrayBuffer}
    
  1843.           dataView={dataView}
    
  1844.           map={map}
    
  1845.           set={set}
    
  1846.           mapOfMaps={mapOfMaps}
    
  1847.           setOfSets={setOfSets}
    
  1848.           typedArray={typedArray}
    
  1849.           immutable={immutable}
    
  1850.           bigInt={bigInt}
    
  1851.         />,
    
  1852.         document.createElement('div'),
    
  1853.       ),
    
  1854.     );
    
  1855. 
    
  1856.     const id = ((store.getElementIDAtIndex(0): any): number);
    
  1857. 
    
  1858.     let copyPath: CopyInspectedElementPath =
    
  1859.       ((null: any): CopyInspectedElementPath);
    
  1860. 
    
  1861.     await inspectElementAtIndex(0, () => {
    
  1862.       copyPath = (path: Array<string | number>) => {
    
  1863.         const rendererID = store.getRendererIDForElement(id);
    
  1864.         if (rendererID !== null) {
    
  1865.           const {
    
  1866.             copyInspectedElementPath,
    
  1867.           } = require('react-devtools-shared/src/backendAPI');
    
  1868.           copyInspectedElementPath({
    
  1869.             bridge,
    
  1870.             id,
    
  1871.             path,
    
  1872.             rendererID,
    
  1873.           });
    
  1874.         }
    
  1875.       };
    
  1876.     });
    
  1877. 
    
  1878.     // Should copy the whole value (not just the hydrated parts)
    
  1879.     copyPath(['props']);
    
  1880.     jest.runOnlyPendingTimers();
    
  1881.     // Should not error despite lots of unserialized values.
    
  1882. 
    
  1883.     global.mockClipboardCopy.mockReset();
    
  1884. 
    
  1885.     // Should copy the nested property specified (not just the outer value)
    
  1886.     copyPath(['props', 'bigInt']);
    
  1887.     jest.runOnlyPendingTimers();
    
  1888.     expect(global.mockClipboardCopy).toHaveBeenCalledTimes(1);
    
  1889.     expect(global.mockClipboardCopy).toHaveBeenCalledWith(
    
  1890.       JSON.stringify('123n', undefined, 2),
    
  1891.     );
    
  1892. 
    
  1893.     global.mockClipboardCopy.mockReset();
    
  1894. 
    
  1895.     // Should copy the nested property specified (not just the outer value)
    
  1896.     copyPath(['props', 'typedArray']);
    
  1897.     jest.runOnlyPendingTimers();
    
  1898.     expect(global.mockClipboardCopy).toHaveBeenCalledTimes(1);
    
  1899.     expect(global.mockClipboardCopy).toHaveBeenCalledWith(
    
  1900.       JSON.stringify({0: 100, 1: -100, 2: 0}, undefined, 2),
    
  1901.     );
    
  1902.   });
    
  1903. 
    
  1904.   it('should display complex values of useDebugValue', async () => {
    
  1905.     const container = document.createElement('div');
    
  1906. 
    
  1907.     function useDebuggableHook() {
    
  1908.       React.useDebugValue({foo: 2});
    
  1909.       React.useState(1);
    
  1910.       return 1;
    
  1911.     }
    
  1912.     function DisplayedComplexValue() {
    
  1913.       useDebuggableHook();
    
  1914.       return null;
    
  1915.     }
    
  1916. 
    
  1917.     await utils.actAsync(() =>
    
  1918.       legacyRender(<DisplayedComplexValue />, container),
    
  1919.     );
    
  1920. 
    
  1921.     const {hooks} = await inspectElementAtIndex(0);
    
  1922.     expect(hooks).toMatchInlineSnapshot(`
    
  1923.       [
    
  1924.         {
    
  1925.           "hookSource": {
    
  1926.             "columnNumber": "removed by Jest serializer",
    
  1927.             "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js",
    
  1928.             "functionName": "DisplayedComplexValue",
    
  1929.             "lineNumber": "removed by Jest serializer",
    
  1930.           },
    
  1931.           "id": null,
    
  1932.           "isStateEditable": false,
    
  1933.           "name": "DebuggableHook",
    
  1934.           "subHooks": [
    
  1935.             {
    
  1936.               "hookSource": {
    
  1937.                 "columnNumber": "removed by Jest serializer",
    
  1938.                 "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js",
    
  1939.                 "functionName": "useDebuggableHook",
    
  1940.                 "lineNumber": "removed by Jest serializer",
    
  1941.               },
    
  1942.               "id": 0,
    
  1943.               "isStateEditable": true,
    
  1944.               "name": "State",
    
  1945.               "subHooks": [],
    
  1946.               "value": 1,
    
  1947.             },
    
  1948.           ],
    
  1949.           "value": {
    
  1950.             "foo": 2,
    
  1951.           },
    
  1952.         },
    
  1953.       ]
    
  1954.     `);
    
  1955.   });
    
  1956. 
    
  1957.   // See github.com/facebook/react/issues/21654
    
  1958.   it('should support Proxies that dont return an iterator', async () => {
    
  1959.     const Example = () => null;
    
  1960.     const proxy = new Proxy(
    
  1961.       {},
    
  1962.       {
    
  1963.         get: (target, prop, receiver) => {
    
  1964.           target[prop] = value => {};
    
  1965.           return target[prop];
    
  1966.         },
    
  1967.       },
    
  1968.     );
    
  1969. 
    
  1970.     const container = document.createElement('div');
    
  1971.     await utils.actAsync(() =>
    
  1972.       legacyRender(<Example proxy={proxy} />, container),
    
  1973.     );
    
  1974. 
    
  1975.     const inspectedElement = await inspectElementAtIndex(0);
    
  1976. 
    
  1977.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  1978.       {
    
  1979.         "proxy": {
    
  1980.           "$$typeof": Dehydrated {
    
  1981.             "preview_short": ƒ () {},
    
  1982.             "preview_long": ƒ () {},
    
  1983.           },
    
  1984.           "Symbol(Symbol.iterator)": Dehydrated {
    
  1985.             "preview_short": ƒ () {},
    
  1986.             "preview_long": ƒ () {},
    
  1987.           },
    
  1988.           "constructor": Dehydrated {
    
  1989.             "preview_short": ƒ () {},
    
  1990.             "preview_long": ƒ () {},
    
  1991.           },
    
  1992.         },
    
  1993.       }
    
  1994.     `);
    
  1995.   });
    
  1996. 
    
  1997.   // Regression test for github.com/facebook/react/issues/22099
    
  1998.   it('should not error when an unchanged component is re-inspected after component filters changed', async () => {
    
  1999.     const Example = () => <div />;
    
  2000. 
    
  2001.     const container = document.createElement('div');
    
  2002.     await utils.actAsync(() => legacyRender(<Example />, container));
    
  2003. 
    
  2004.     // Select/inspect element
    
  2005.     let inspectedElement = await inspectElementAtIndex(0);
    
  2006.     expect(inspectedElement).toMatchInlineSnapshot(`
    
  2007.       {
    
  2008.         "context": null,
    
  2009.         "events": undefined,
    
  2010.         "hooks": null,
    
  2011.         "id": 2,
    
  2012.         "owners": null,
    
  2013.         "props": {},
    
  2014.         "rootType": "render()",
    
  2015.         "state": null,
    
  2016.       }
    
  2017.     `);
    
  2018. 
    
  2019.     await utils.actAsync(async () => {
    
  2020.       // Ignore transient warning this causes
    
  2021.       utils.withErrorsOrWarningsIgnored(['No element found with id'], () => {
    
  2022.         store.componentFilters = [];
    
  2023. 
    
  2024.         // Flush events to the renderer.
    
  2025.         jest.runOnlyPendingTimers();
    
  2026.       });
    
  2027.     }, false);
    
  2028. 
    
  2029.     // HACK: Recreate TestRenderer instance because we rely on default state values
    
  2030.     // from props like defaultSelectedElementID and it's easier to reset here than
    
  2031.     // to read the TreeDispatcherContext and update the selected ID that way.
    
  2032.     // We're testing the inspected values here, not the context wiring, so that's ok.
    
  2033.     utils.withErrorsOrWarningsIgnored(
    
  2034.       ['An update to %s inside a test was not wrapped in act'],
    
  2035.       () => {
    
  2036.         testRendererInstance = TestRenderer.create(null, {
    
  2037.           unstable_isConcurrent: true,
    
  2038.         });
    
  2039.       },
    
  2040.     );
    
  2041. 
    
  2042.     // Select/inspect the same element again
    
  2043.     inspectedElement = await inspectElementAtIndex(0);
    
  2044.     expect(inspectedElement).toMatchInlineSnapshot(`
    
  2045.       {
    
  2046.         "context": null,
    
  2047.         "events": undefined,
    
  2048.         "hooks": null,
    
  2049.         "id": 2,
    
  2050.         "owners": null,
    
  2051.         "props": {},
    
  2052.         "rootType": "render()",
    
  2053.         "state": null,
    
  2054.       }
    
  2055.     `);
    
  2056.   });
    
  2057. 
    
  2058.   it('should display the root type for ReactDOM.hydrate', async () => {
    
  2059.     const Example = () => <div />;
    
  2060. 
    
  2061.     await utils.actAsync(() => {
    
  2062.       const container = document.createElement('div');
    
  2063.       container.innerHTML = '<div></div>';
    
  2064.       withErrorsOrWarningsIgnored(
    
  2065.         ['ReactDOM.hydrate is no longer supported in React 18'],
    
  2066.         () => {
    
  2067.           ReactDOM.hydrate(<Example />, container);
    
  2068.         },
    
  2069.       );
    
  2070.     }, false);
    
  2071. 
    
  2072.     const inspectedElement = await inspectElementAtIndex(0);
    
  2073.     expect(inspectedElement.rootType).toMatchInlineSnapshot(`"hydrate()"`);
    
  2074.   });
    
  2075. 
    
  2076.   it('should display the root type for ReactDOM.render', async () => {
    
  2077.     const Example = () => <div />;
    
  2078. 
    
  2079.     await utils.actAsync(() => {
    
  2080.       const container = document.createElement('div');
    
  2081.       legacyRender(<Example />, container);
    
  2082.     }, false);
    
  2083. 
    
  2084.     const inspectedElement = await inspectElementAtIndex(0);
    
  2085.     expect(inspectedElement.rootType).toMatchInlineSnapshot(`"render()"`);
    
  2086.   });
    
  2087. 
    
  2088.   it('should display the root type for ReactDOMClient.hydrateRoot', async () => {
    
  2089.     const Example = () => <div />;
    
  2090. 
    
  2091.     await utils.actAsync(() => {
    
  2092.       const container = document.createElement('div');
    
  2093.       container.innerHTML = '<div></div>';
    
  2094.       ReactDOMClient.hydrateRoot(container, <Example />);
    
  2095.     }, false);
    
  2096. 
    
  2097.     const inspectedElement = await inspectElementAtIndex(0);
    
  2098.     expect(inspectedElement.rootType).toMatchInlineSnapshot(`"hydrateRoot()"`);
    
  2099.   });
    
  2100. 
    
  2101.   it('should display the root type for ReactDOMClient.createRoot', async () => {
    
  2102.     const Example = () => <div />;
    
  2103. 
    
  2104.     await utils.actAsync(() => {
    
  2105.       const container = document.createElement('div');
    
  2106.       ReactDOMClient.createRoot(container).render(<Example />);
    
  2107.     }, false);
    
  2108. 
    
  2109.     const inspectedElement = await inspectElementAtIndex(0);
    
  2110.     expect(inspectedElement.rootType).toMatchInlineSnapshot(`"createRoot()"`);
    
  2111.   });
    
  2112. 
    
  2113.   it('should gracefully surface backend errors on the frontend rather than timing out', async () => {
    
  2114.     jest.spyOn(console, 'error').mockImplementation(() => {});
    
  2115. 
    
  2116.     let shouldThrow = false;
    
  2117. 
    
  2118.     const Example = () => {
    
  2119.       const [count] = React.useState(0);
    
  2120. 
    
  2121.       if (shouldThrow) {
    
  2122.         throw Error('Expected');
    
  2123.       } else {
    
  2124.         return count;
    
  2125.       }
    
  2126.     };
    
  2127. 
    
  2128.     await utils.actAsync(() => {
    
  2129.       const container = document.createElement('div');
    
  2130.       ReactDOMClient.createRoot(container).render(<Example />);
    
  2131.     }, false);
    
  2132. 
    
  2133.     shouldThrow = true;
    
  2134. 
    
  2135.     const value = await inspectElementAtIndex(0, noop, true);
    
  2136. 
    
  2137.     expect(value).toBe(null);
    
  2138. 
    
  2139.     const error = errorBoundaryInstance.state.error;
    
  2140.     expect(error.message).toBe('Expected');
    
  2141.     expect(error.stack).toContain('inspectHooksOfFiber');
    
  2142.   });
    
  2143. 
    
  2144.   describe('$r', () => {
    
  2145.     it('should support function components', async () => {
    
  2146.       const Example = () => {
    
  2147.         const [count] = React.useState(1);
    
  2148.         return count;
    
  2149.       };
    
  2150. 
    
  2151.       const container = document.createElement('div');
    
  2152.       await utils.actAsync(() =>
    
  2153.         legacyRender(<Example a={1} b="abc" />, container),
    
  2154.       );
    
  2155. 
    
  2156.       await inspectElementAtIndex(0);
    
  2157. 
    
  2158.       expect(global.$r).toMatchInlineSnapshot(`
    
  2159.         {
    
  2160.           "hooks": [
    
  2161.             {
    
  2162.               "hookSource": {
    
  2163.                 "columnNumber": "removed by Jest serializer",
    
  2164.                 "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js",
    
  2165.                 "functionName": "Example",
    
  2166.                 "lineNumber": "removed by Jest serializer",
    
  2167.               },
    
  2168.               "id": 0,
    
  2169.               "isStateEditable": true,
    
  2170.               "name": "State",
    
  2171.               "subHooks": [],
    
  2172.               "value": 1,
    
  2173.             },
    
  2174.           ],
    
  2175.           "props": {
    
  2176.             "a": 1,
    
  2177.             "b": "abc",
    
  2178.           },
    
  2179.           "type": [Function],
    
  2180.         }
    
  2181.       `);
    
  2182.     });
    
  2183. 
    
  2184.     it('should support memoized function components', async () => {
    
  2185.       const Example = React.memo(function Example(props) {
    
  2186.         const [count] = React.useState(1);
    
  2187.         return count;
    
  2188.       });
    
  2189. 
    
  2190.       const container = document.createElement('div');
    
  2191.       await utils.actAsync(() =>
    
  2192.         legacyRender(<Example a={1} b="abc" />, container),
    
  2193.       );
    
  2194. 
    
  2195.       await inspectElementAtIndex(0);
    
  2196. 
    
  2197.       expect(global.$r).toMatchInlineSnapshot(`
    
  2198.         {
    
  2199.           "hooks": [
    
  2200.             {
    
  2201.               "hookSource": {
    
  2202.                 "columnNumber": "removed by Jest serializer",
    
  2203.                 "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js",
    
  2204.                 "functionName": "Example",
    
  2205.                 "lineNumber": "removed by Jest serializer",
    
  2206.               },
    
  2207.               "id": 0,
    
  2208.               "isStateEditable": true,
    
  2209.               "name": "State",
    
  2210.               "subHooks": [],
    
  2211.               "value": 1,
    
  2212.             },
    
  2213.           ],
    
  2214.           "props": {
    
  2215.             "a": 1,
    
  2216.             "b": "abc",
    
  2217.           },
    
  2218.           "type": [Function],
    
  2219.         }
    
  2220.       `);
    
  2221.     });
    
  2222. 
    
  2223.     it('should support forward refs', async () => {
    
  2224.       const Example = React.forwardRef(function Example(props, ref) {
    
  2225.         const [count] = React.useState(1);
    
  2226.         return count;
    
  2227.       });
    
  2228. 
    
  2229.       const container = document.createElement('div');
    
  2230.       await utils.actAsync(() =>
    
  2231.         legacyRender(<Example a={1} b="abc" />, container),
    
  2232.       );
    
  2233. 
    
  2234.       await inspectElementAtIndex(0);
    
  2235. 
    
  2236.       expect(global.$r).toMatchInlineSnapshot(`
    
  2237.         {
    
  2238.           "hooks": [
    
  2239.             {
    
  2240.               "hookSource": {
    
  2241.                 "columnNumber": "removed by Jest serializer",
    
  2242.                 "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js",
    
  2243.                 "functionName": "Example",
    
  2244.                 "lineNumber": "removed by Jest serializer",
    
  2245.               },
    
  2246.               "id": 0,
    
  2247.               "isStateEditable": true,
    
  2248.               "name": "State",
    
  2249.               "subHooks": [],
    
  2250.               "value": 1,
    
  2251.             },
    
  2252.           ],
    
  2253.           "props": {
    
  2254.             "a": 1,
    
  2255.             "b": "abc",
    
  2256.           },
    
  2257.           "type": [Function],
    
  2258.         }
    
  2259.       `);
    
  2260.     });
    
  2261. 
    
  2262.     it('should support class components', async () => {
    
  2263.       class Example extends React.Component {
    
  2264.         state = {
    
  2265.           count: 0,
    
  2266.         };
    
  2267.         render() {
    
  2268.           return null;
    
  2269.         }
    
  2270.       }
    
  2271. 
    
  2272.       const container = document.createElement('div');
    
  2273.       await utils.actAsync(() =>
    
  2274.         legacyRender(<Example a={1} b="abc" />, container),
    
  2275.       );
    
  2276. 
    
  2277.       await inspectElementAtIndex(0);
    
  2278. 
    
  2279.       expect(global.$r.props).toMatchInlineSnapshot(`
    
  2280.               {
    
  2281.                 "a": 1,
    
  2282.                 "b": "abc",
    
  2283.               }
    
  2284.             `);
    
  2285.       expect(global.$r.state).toMatchInlineSnapshot(`
    
  2286.               {
    
  2287.                 "count": 0,
    
  2288.               }
    
  2289.             `);
    
  2290.     });
    
  2291.   });
    
  2292. 
    
  2293.   describe('inline errors and warnings', () => {
    
  2294.     async function getErrorsAndWarningsForElementAtIndex(index) {
    
  2295.       const id = ((store.getElementIDAtIndex(index): any): number);
    
  2296.       if (id == null) {
    
  2297.         throw Error(`Element at index "${index}"" not found in store`);
    
  2298.       }
    
  2299. 
    
  2300.       let errors = null;
    
  2301.       let warnings = null;
    
  2302. 
    
  2303.       function Suspender({target}) {
    
  2304.         const inspectedElement = useInspectedElement();
    
  2305.         errors = inspectedElement.errors;
    
  2306.         warnings = inspectedElement.warnings;
    
  2307.         return null;
    
  2308.       }
    
  2309. 
    
  2310.       let root;
    
  2311.       await utils.actAsync(() => {
    
  2312.         root = TestRenderer.create(
    
  2313.           <Contexts
    
  2314.             defaultSelectedElementID={id}
    
  2315.             defaultSelectedElementIndex={index}>
    
  2316.             <React.Suspense fallback={null}>
    
  2317.               <Suspender target={id} />
    
  2318.             </React.Suspense>
    
  2319.           </Contexts>,
    
  2320.           {unstable_isConcurrent: true},
    
  2321.         );
    
  2322.       }, false);
    
  2323.       await utils.actAsync(() => {
    
  2324.         root.unmount();
    
  2325.       }, false);
    
  2326. 
    
  2327.       return {errors, warnings};
    
  2328.     }
    
  2329. 
    
  2330.     it('during render get recorded', async () => {
    
  2331.       const Example = () => {
    
  2332.         console.error('test-only: render error');
    
  2333.         console.warn('test-only: render warning');
    
  2334.         return null;
    
  2335.       };
    
  2336. 
    
  2337.       const container = document.createElement('div');
    
  2338. 
    
  2339.       await withErrorsOrWarningsIgnored(['test-only: '], async () => {
    
  2340.         await utils.actAsync(() =>
    
  2341.           legacyRender(<Example repeatWarningCount={1} />, container),
    
  2342.         );
    
  2343.       });
    
  2344. 
    
  2345.       const data = await getErrorsAndWarningsForElementAtIndex(0);
    
  2346.       expect(data).toMatchInlineSnapshot(`
    
  2347.         {
    
  2348.           "errors": [
    
  2349.             [
    
  2350.               "test-only: render error",
    
  2351.               1,
    
  2352.             ],
    
  2353.           ],
    
  2354.           "warnings": [
    
  2355.             [
    
  2356.               "test-only: render warning",
    
  2357.               1,
    
  2358.             ],
    
  2359.           ],
    
  2360.         }
    
  2361.       `);
    
  2362.     });
    
  2363. 
    
  2364.     it('during render get deduped', async () => {
    
  2365.       const Example = () => {
    
  2366.         console.error('test-only: render error');
    
  2367.         console.error('test-only: render error');
    
  2368.         console.warn('test-only: render warning');
    
  2369.         console.warn('test-only: render warning');
    
  2370.         console.warn('test-only: render warning');
    
  2371.         return null;
    
  2372.       };
    
  2373. 
    
  2374.       const container = document.createElement('div');
    
  2375.       await utils.withErrorsOrWarningsIgnored(['test-only:'], async () => {
    
  2376.         await utils.actAsync(() =>
    
  2377.           legacyRender(<Example repeatWarningCount={1} />, container),
    
  2378.         );
    
  2379.       });
    
  2380.       const data = await getErrorsAndWarningsForElementAtIndex(0);
    
  2381.       expect(data).toMatchInlineSnapshot(`
    
  2382.         {
    
  2383.           "errors": [
    
  2384.             [
    
  2385.               "test-only: render error",
    
  2386.               2,
    
  2387.             ],
    
  2388.           ],
    
  2389.           "warnings": [
    
  2390.             [
    
  2391.               "test-only: render warning",
    
  2392.               3,
    
  2393.             ],
    
  2394.           ],
    
  2395.         }
    
  2396.       `);
    
  2397.     });
    
  2398. 
    
  2399.     it('during layout (mount) get recorded', async () => {
    
  2400.       const Example = () => {
    
  2401.         // Note we only test mount because once the component unmounts,
    
  2402.         // it is no longer in the store and warnings are ignored.
    
  2403.         React.useLayoutEffect(() => {
    
  2404.           console.error('test-only: useLayoutEffect error');
    
  2405.           console.warn('test-only: useLayoutEffect warning');
    
  2406.         }, []);
    
  2407.         return null;
    
  2408.       };
    
  2409. 
    
  2410.       const container = document.createElement('div');
    
  2411.       await utils.withErrorsOrWarningsIgnored(['test-only:'], async () => {
    
  2412.         await utils.actAsync(() =>
    
  2413.           legacyRender(<Example repeatWarningCount={1} />, container),
    
  2414.         );
    
  2415.       });
    
  2416. 
    
  2417.       const data = await getErrorsAndWarningsForElementAtIndex(0);
    
  2418.       expect(data).toMatchInlineSnapshot(`
    
  2419.         {
    
  2420.           "errors": [
    
  2421.             [
    
  2422.               "test-only: useLayoutEffect error",
    
  2423.               1,
    
  2424.             ],
    
  2425.           ],
    
  2426.           "warnings": [
    
  2427.             [
    
  2428.               "test-only: useLayoutEffect warning",
    
  2429.               1,
    
  2430.             ],
    
  2431.           ],
    
  2432.         }
    
  2433.       `);
    
  2434.     });
    
  2435. 
    
  2436.     it('during passive (mount) get recorded', async () => {
    
  2437.       const Example = () => {
    
  2438.         // Note we only test mount because once the component unmounts,
    
  2439.         // it is no longer in the store and warnings are ignored.
    
  2440.         React.useEffect(() => {
    
  2441.           console.error('test-only: useEffect error');
    
  2442.           console.warn('test-only: useEffect warning');
    
  2443.         }, []);
    
  2444.         return null;
    
  2445.       };
    
  2446. 
    
  2447.       const container = document.createElement('div');
    
  2448.       await utils.withErrorsOrWarningsIgnored(['test-only:'], async () => {
    
  2449.         await utils.actAsync(() =>
    
  2450.           legacyRender(<Example repeatWarningCount={1} />, container),
    
  2451.         );
    
  2452.       });
    
  2453. 
    
  2454.       const data = await getErrorsAndWarningsForElementAtIndex(0);
    
  2455.       expect(data).toMatchInlineSnapshot(`
    
  2456.         {
    
  2457.           "errors": [
    
  2458.             [
    
  2459.               "test-only: useEffect error",
    
  2460.               1,
    
  2461.             ],
    
  2462.           ],
    
  2463.           "warnings": [
    
  2464.             [
    
  2465.               "test-only: useEffect warning",
    
  2466.               1,
    
  2467.             ],
    
  2468.           ],
    
  2469.         }
    
  2470.       `);
    
  2471.     });
    
  2472. 
    
  2473.     it('from react get recorded without a component stack', async () => {
    
  2474.       const Example = () => {
    
  2475.         return [<div />];
    
  2476.       };
    
  2477. 
    
  2478.       const container = document.createElement('div');
    
  2479.       await utils.withErrorsOrWarningsIgnored(
    
  2480.         ['Warning: Each child in a list should have a unique "key" prop.'],
    
  2481.         async () => {
    
  2482.           await utils.actAsync(() =>
    
  2483.             legacyRender(<Example repeatWarningCount={1} />, container),
    
  2484.           );
    
  2485.         },
    
  2486.       );
    
  2487. 
    
  2488.       const data = await getErrorsAndWarningsForElementAtIndex(0);
    
  2489.       expect(data).toMatchInlineSnapshot(`
    
  2490.         {
    
  2491.           "errors": [
    
  2492.             [
    
  2493.               "Warning: Each child in a list should have a unique "key" prop. See https://reactjs.org/link/warning-keys for more information.
    
  2494.             at Example",
    
  2495.               1,
    
  2496.             ],
    
  2497.           ],
    
  2498.           "warnings": [],
    
  2499.         }
    
  2500.       `);
    
  2501.     });
    
  2502. 
    
  2503.     it('can be cleared for the whole app', async () => {
    
  2504.       const Example = () => {
    
  2505.         console.error('test-only: render error');
    
  2506.         console.warn('test-only: render warning');
    
  2507.         return null;
    
  2508.       };
    
  2509. 
    
  2510.       const container = document.createElement('div');
    
  2511.       await utils.withErrorsOrWarningsIgnored(['test-only:'], async () => {
    
  2512.         await utils.actAsync(() =>
    
  2513.           legacyRender(<Example repeatWarningCount={1} />, container),
    
  2514.         );
    
  2515.       });
    
  2516. 
    
  2517.       const {
    
  2518.         clearErrorsAndWarnings,
    
  2519.       } = require('react-devtools-shared/src/backendAPI');
    
  2520.       clearErrorsAndWarnings({bridge, store});
    
  2521. 
    
  2522.       // Flush events to the renderer.
    
  2523.       jest.runOnlyPendingTimers();
    
  2524. 
    
  2525.       const data = await getErrorsAndWarningsForElementAtIndex(0);
    
  2526.       expect(data).toMatchInlineSnapshot(`
    
  2527.         {
    
  2528.           "errors": [],
    
  2529.           "warnings": [],
    
  2530.         }
    
  2531.       `);
    
  2532.     });
    
  2533. 
    
  2534.     it('can be cleared for a particular Fiber (only warnings)', async () => {
    
  2535.       const Example = ({id}) => {
    
  2536.         console.error(`test-only: render error #${id}`);
    
  2537.         console.warn(`test-only: render warning #${id}`);
    
  2538.         return null;
    
  2539.       };
    
  2540. 
    
  2541.       const container = document.createElement('div');
    
  2542.       await utils.withErrorsOrWarningsIgnored(['test-only:'], async () => {
    
  2543.         await utils.actAsync(() =>
    
  2544.           legacyRender(
    
  2545.             <React.Fragment>
    
  2546.               <Example id={1} />
    
  2547.               <Example id={2} />
    
  2548.             </React.Fragment>,
    
  2549.             container,
    
  2550.           ),
    
  2551.         );
    
  2552.       });
    
  2553. 
    
  2554.       let id = ((store.getElementIDAtIndex(1): any): number);
    
  2555.       const rendererID = store.getRendererIDForElement(id);
    
  2556. 
    
  2557.       const {
    
  2558.         clearWarningsForElement,
    
  2559.       } = require('react-devtools-shared/src/backendAPI');
    
  2560.       clearWarningsForElement({bridge, id, rendererID});
    
  2561. 
    
  2562.       // Flush events to the renderer.
    
  2563.       jest.runOnlyPendingTimers();
    
  2564. 
    
  2565.       let data = [
    
  2566.         await getErrorsAndWarningsForElementAtIndex(0),
    
  2567.         await getErrorsAndWarningsForElementAtIndex(1),
    
  2568.       ];
    
  2569.       expect(data).toMatchInlineSnapshot(`
    
  2570.         [
    
  2571.           {
    
  2572.             "errors": [
    
  2573.               [
    
  2574.                 "test-only: render error #1",
    
  2575.                 1,
    
  2576.               ],
    
  2577.             ],
    
  2578.             "warnings": [
    
  2579.               [
    
  2580.                 "test-only: render warning #1",
    
  2581.                 1,
    
  2582.               ],
    
  2583.             ],
    
  2584.           },
    
  2585.           {
    
  2586.             "errors": [
    
  2587.               [
    
  2588.                 "test-only: render error #2",
    
  2589.                 1,
    
  2590.               ],
    
  2591.             ],
    
  2592.             "warnings": [],
    
  2593.           },
    
  2594.         ]
    
  2595.       `);
    
  2596. 
    
  2597.       id = ((store.getElementIDAtIndex(0): any): number);
    
  2598.       clearWarningsForElement({bridge, id, rendererID});
    
  2599. 
    
  2600.       // Flush events to the renderer.
    
  2601.       jest.runOnlyPendingTimers();
    
  2602. 
    
  2603.       data = [
    
  2604.         await getErrorsAndWarningsForElementAtIndex(0),
    
  2605.         await getErrorsAndWarningsForElementAtIndex(1),
    
  2606.       ];
    
  2607.       expect(data).toMatchInlineSnapshot(`
    
  2608.         [
    
  2609.           {
    
  2610.             "errors": [
    
  2611.               [
    
  2612.                 "test-only: render error #1",
    
  2613.                 1,
    
  2614.               ],
    
  2615.             ],
    
  2616.             "warnings": [],
    
  2617.           },
    
  2618.           {
    
  2619.             "errors": [
    
  2620.               [
    
  2621.                 "test-only: render error #2",
    
  2622.                 1,
    
  2623.               ],
    
  2624.             ],
    
  2625.             "warnings": [],
    
  2626.           },
    
  2627.         ]
    
  2628.       `);
    
  2629.     });
    
  2630. 
    
  2631.     it('can be cleared for a particular Fiber (only errors)', async () => {
    
  2632.       const Example = ({id}) => {
    
  2633.         console.error(`test-only: render error #${id}`);
    
  2634.         console.warn(`test-only: render warning #${id}`);
    
  2635.         return null;
    
  2636.       };
    
  2637. 
    
  2638.       const container = document.createElement('div');
    
  2639.       await utils.withErrorsOrWarningsIgnored(['test-only:'], async () => {
    
  2640.         await utils.actAsync(() =>
    
  2641.           legacyRender(
    
  2642.             <React.Fragment>
    
  2643.               <Example id={1} />
    
  2644.               <Example id={2} />
    
  2645.             </React.Fragment>,
    
  2646.             container,
    
  2647.           ),
    
  2648.         );
    
  2649.       });
    
  2650. 
    
  2651.       let id = ((store.getElementIDAtIndex(1): any): number);
    
  2652.       const rendererID = store.getRendererIDForElement(id);
    
  2653. 
    
  2654.       const {
    
  2655.         clearErrorsForElement,
    
  2656.       } = require('react-devtools-shared/src/backendAPI');
    
  2657.       clearErrorsForElement({bridge, id, rendererID});
    
  2658. 
    
  2659.       // Flush events to the renderer.
    
  2660.       jest.runOnlyPendingTimers();
    
  2661. 
    
  2662.       let data = [
    
  2663.         await getErrorsAndWarningsForElementAtIndex(0),
    
  2664.         await getErrorsAndWarningsForElementAtIndex(1),
    
  2665.       ];
    
  2666.       expect(data).toMatchInlineSnapshot(`
    
  2667.         [
    
  2668.           {
    
  2669.             "errors": [
    
  2670.               [
    
  2671.                 "test-only: render error #1",
    
  2672.                 1,
    
  2673.               ],
    
  2674.             ],
    
  2675.             "warnings": [
    
  2676.               [
    
  2677.                 "test-only: render warning #1",
    
  2678.                 1,
    
  2679.               ],
    
  2680.             ],
    
  2681.           },
    
  2682.           {
    
  2683.             "errors": [],
    
  2684.             "warnings": [
    
  2685.               [
    
  2686.                 "test-only: render warning #2",
    
  2687.                 1,
    
  2688.               ],
    
  2689.             ],
    
  2690.           },
    
  2691.         ]
    
  2692.       `);
    
  2693. 
    
  2694.       id = ((store.getElementIDAtIndex(0): any): number);
    
  2695.       clearErrorsForElement({bridge, id, rendererID});
    
  2696. 
    
  2697.       // Flush events to the renderer.
    
  2698.       jest.runOnlyPendingTimers();
    
  2699. 
    
  2700.       data = [
    
  2701.         await getErrorsAndWarningsForElementAtIndex(0),
    
  2702.         await getErrorsAndWarningsForElementAtIndex(1),
    
  2703.       ];
    
  2704.       expect(data).toMatchInlineSnapshot(`
    
  2705.         [
    
  2706.           {
    
  2707.             "errors": [],
    
  2708.             "warnings": [
    
  2709.               [
    
  2710.                 "test-only: render warning #1",
    
  2711.                 1,
    
  2712.               ],
    
  2713.             ],
    
  2714.           },
    
  2715.           {
    
  2716.             "errors": [],
    
  2717.             "warnings": [
    
  2718.               [
    
  2719.                 "test-only: render warning #2",
    
  2720.                 1,
    
  2721.               ],
    
  2722.             ],
    
  2723.           },
    
  2724.         ]
    
  2725.       `);
    
  2726.     });
    
  2727.   });
    
  2728. 
    
  2729.   it('inspecting nested renderers should not throw', async () => {
    
  2730.     // Ignoring react art warnings
    
  2731.     jest.spyOn(console, 'error').mockImplementation(() => {});
    
  2732.     const ReactArt = require('react-art');
    
  2733.     const ArtSVGMode = require('art/modes/svg');
    
  2734.     const ARTCurrentMode = require('art/modes/current');
    
  2735.     store.componentFilters = [];
    
  2736. 
    
  2737.     ARTCurrentMode.setCurrent(ArtSVGMode);
    
  2738.     const {Surface, Group} = ReactArt;
    
  2739. 
    
  2740.     function Child() {
    
  2741.       return (
    
  2742.         <Surface width={1} height={1}>
    
  2743.           <Group />
    
  2744.         </Surface>
    
  2745.       );
    
  2746.     }
    
  2747.     function App() {
    
  2748.       return <Child />;
    
  2749.     }
    
  2750. 
    
  2751.     await utils.actAsync(() => {
    
  2752.       legacyRender(<App />, document.createElement('div'));
    
  2753.     });
    
  2754.     expect(store).toMatchInlineSnapshot(`
    
  2755.       [root]
    
  2756.         ▾ <App>
    
  2757.           ▾ <Child>
    
  2758.             ▾ <Surface>
    
  2759.                 <svg>
    
  2760.       [root]
    
  2761.           <Group>
    
  2762.     `);
    
  2763. 
    
  2764.     const inspectedElement = await inspectElementAtIndex(4);
    
  2765.     expect(inspectedElement.owners).toMatchInlineSnapshot(`
    
  2766.       [
    
  2767.         {
    
  2768.           "displayName": "Child",
    
  2769.           "hocDisplayNames": null,
    
  2770.           "id": 3,
    
  2771.           "key": null,
    
  2772.           "type": 5,
    
  2773.         },
    
  2774.         {
    
  2775.           "displayName": "App",
    
  2776.           "hocDisplayNames": null,
    
  2777.           "id": 2,
    
  2778.           "key": null,
    
  2779.           "type": 5,
    
  2780.         },
    
  2781.       ]
    
  2782.     `);
    
  2783.   });
    
  2784. 
    
  2785.   describe('error boundary', () => {
    
  2786.     it('can toggle error', async () => {
    
  2787.       class LocalErrorBoundary extends React.Component<any> {
    
  2788.         state = {hasError: false};
    
  2789.         static getDerivedStateFromError(error) {
    
  2790.           return {hasError: true};
    
  2791.         }
    
  2792.         render() {
    
  2793.           const {hasError} = this.state;
    
  2794.           return hasError ? 'has-error' : this.props.children;
    
  2795.         }
    
  2796.       }
    
  2797. 
    
  2798.       const Example = () => 'example';
    
  2799. 
    
  2800.       await utils.actAsync(() =>
    
  2801.         legacyRender(
    
  2802.           <LocalErrorBoundary>
    
  2803.             <Example />
    
  2804.           </LocalErrorBoundary>,
    
  2805.           document.createElement('div'),
    
  2806.         ),
    
  2807.       );
    
  2808. 
    
  2809.       const targetErrorBoundaryID = ((store.getElementIDAtIndex(
    
  2810.         0,
    
  2811.       ): any): number);
    
  2812.       const inspect = index => {
    
  2813.         // HACK: Recreate TestRenderer instance so we can inspect different elements
    
  2814.         utils.withErrorsOrWarningsIgnored(
    
  2815.           ['An update to %s inside a test was not wrapped in act'],
    
  2816.           () => {
    
  2817.             testRendererInstance = TestRenderer.create(null, {
    
  2818.               unstable_isConcurrent: true,
    
  2819.             });
    
  2820.           },
    
  2821.         );
    
  2822.         return inspectElementAtIndex(index);
    
  2823.       };
    
  2824.       const toggleError = async forceError => {
    
  2825.         await withErrorsOrWarningsIgnored(['ErrorBoundary'], async () => {
    
  2826.           await TestUtilsAct(async () => {
    
  2827.             bridge.send('overrideError', {
    
  2828.               id: targetErrorBoundaryID,
    
  2829.               rendererID: store.getRendererIDForElement(targetErrorBoundaryID),
    
  2830.               forceError,
    
  2831.             });
    
  2832.           });
    
  2833.         });
    
  2834. 
    
  2835.         await TestUtilsAct(async () => {
    
  2836.           jest.runOnlyPendingTimers();
    
  2837.         });
    
  2838.       };
    
  2839. 
    
  2840.       // Inspect <ErrorBoundary /> and see that we cannot toggle error state
    
  2841.       // on error boundary itself
    
  2842.       let inspectedElement = await inspect(0);
    
  2843.       expect(inspectedElement.canToggleError).toBe(false);
    
  2844.       expect(inspectedElement.targetErrorBoundaryID).toBe(null);
    
  2845. 
    
  2846.       // Inspect <Example />
    
  2847.       inspectedElement = await inspect(1);
    
  2848.       expect(inspectedElement.canToggleError).toBe(true);
    
  2849.       expect(inspectedElement.isErrored).toBe(false);
    
  2850.       expect(inspectedElement.targetErrorBoundaryID).toBe(
    
  2851.         targetErrorBoundaryID,
    
  2852.       );
    
  2853. 
    
  2854.       // Suppress expected error and warning.
    
  2855.       const consoleErrorMock = jest
    
  2856.         .spyOn(console, 'error')
    
  2857.         .mockImplementation(() => {});
    
  2858.       const consoleWarnMock = jest
    
  2859.         .spyOn(console, 'warn')
    
  2860.         .mockImplementation(() => {});
    
  2861. 
    
  2862.       // now force error state on <Example />
    
  2863.       await toggleError(true);
    
  2864. 
    
  2865.       consoleErrorMock.mockRestore();
    
  2866.       consoleWarnMock.mockRestore();
    
  2867. 
    
  2868.       // we are in error state now, <Example /> won't show up
    
  2869.       withErrorsOrWarningsIgnored(['Invalid index'], () => {
    
  2870.         expect(store.getElementIDAtIndex(1)).toBe(null);
    
  2871.       });
    
  2872. 
    
  2873.       // Inpsect <ErrorBoundary /> to toggle off the error state
    
  2874.       inspectedElement = await inspect(0);
    
  2875.       expect(inspectedElement.canToggleError).toBe(true);
    
  2876.       expect(inspectedElement.isErrored).toBe(true);
    
  2877.       // its error boundary ID is itself because it's caught the error
    
  2878.       expect(inspectedElement.targetErrorBoundaryID).toBe(
    
  2879.         targetErrorBoundaryID,
    
  2880.       );
    
  2881. 
    
  2882.       await toggleError(false);
    
  2883. 
    
  2884.       // We can now inspect <Example /> with ability to toggle again
    
  2885.       inspectedElement = await inspect(1);
    
  2886.       expect(inspectedElement.canToggleError).toBe(true);
    
  2887.       expect(inspectedElement.isErrored).toBe(false);
    
  2888.       expect(inspectedElement.targetErrorBoundaryID).toBe(
    
  2889.         targetErrorBoundaryID,
    
  2890.       );
    
  2891.     });
    
  2892.   });
    
  2893. });