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 type {FrontendBridge} from 'react-devtools-shared/src/bridge';
    
  11. import type Store from 'react-devtools-shared/src/devtools/store';
    
  12. 
    
  13. describe('InspectedElementContext', () => {
    
  14.   let React;
    
  15.   let ReactDOM;
    
  16.   let bridge: FrontendBridge;
    
  17.   let store: Store;
    
  18. 
    
  19.   let backendAPI;
    
  20. 
    
  21.   const act = (callback: Function) => {
    
  22.     callback();
    
  23. 
    
  24.     jest.runAllTimers(); // Flush Bridge operations
    
  25.   };
    
  26. 
    
  27.   async function read(
    
  28.     id: number,
    
  29.     path: Array<string | number> = null,
    
  30.   ): Promise<Object> {
    
  31.     const rendererID = ((store.getRendererIDForElement(id): any): number);
    
  32.     const promise = backendAPI
    
  33.       .inspectElement(bridge, false, id, path, rendererID)
    
  34.       .then(data =>
    
  35.         backendAPI.convertInspectedElementBackendToFrontend(data.value),
    
  36.       );
    
  37. 
    
  38.     jest.runOnlyPendingTimers();
    
  39. 
    
  40.     return promise;
    
  41.   }
    
  42. 
    
  43.   beforeEach(() => {
    
  44.     bridge = global.bridge;
    
  45.     store = global.store;
    
  46. 
    
  47.     backendAPI = require('react-devtools-shared/src/backendAPI');
    
  48. 
    
  49.     // Redirect all React/ReactDOM requires to the v15 UMD.
    
  50.     // We use the UMD because Jest doesn't enable us to mock deep imports (e.g. "react/lib/Something").
    
  51.     jest.mock('react', () => jest.requireActual('react-15/dist/react.js'));
    
  52.     jest.mock('react-dom', () =>
    
  53.       jest.requireActual('react-dom-15/dist/react-dom.js'),
    
  54.     );
    
  55. 
    
  56.     React = require('react');
    
  57.     ReactDOM = require('react-dom');
    
  58.   });
    
  59. 
    
  60.   // @reactVersion >= 16.0
    
  61.   it('should inspect the currently selected element', async () => {
    
  62.     const Example = () => null;
    
  63. 
    
  64.     act(() =>
    
  65.       ReactDOM.render(<Example a={1} b="abc" />, document.createElement('div')),
    
  66.     );
    
  67. 
    
  68.     const id = ((store.getElementIDAtIndex(0): any): number);
    
  69.     const inspectedElement = await read(id);
    
  70. 
    
  71.     expect(inspectedElement).toMatchInlineSnapshot(`
    
  72.       {
    
  73.         "context": {},
    
  74.         "events": undefined,
    
  75.         "hooks": null,
    
  76.         "id": 2,
    
  77.         "owners": null,
    
  78.         "props": {
    
  79.           "a": 1,
    
  80.           "b": "abc",
    
  81.         },
    
  82.         "rootType": null,
    
  83.         "state": null,
    
  84.       }
    
  85.     `);
    
  86.   });
    
  87. 
    
  88.   // @reactVersion >= 16.0
    
  89.   it('should support simple data types', async () => {
    
  90.     const Example = () => null;
    
  91. 
    
  92.     act(() =>
    
  93.       ReactDOM.render(
    
  94.         <Example
    
  95.           boolean_false={false}
    
  96.           boolean_true={true}
    
  97.           infinity={Infinity}
    
  98.           integer_zero={0}
    
  99.           integer_one={1}
    
  100.           float={1.23}
    
  101.           string="abc"
    
  102.           string_empty=""
    
  103.           nan={NaN}
    
  104.           value_null={null}
    
  105.           value_undefined={undefined}
    
  106.         />,
    
  107.         document.createElement('div'),
    
  108.       ),
    
  109.     );
    
  110. 
    
  111.     const id = ((store.getElementIDAtIndex(0): any): number);
    
  112.     const inspectedElement = await read(id);
    
  113. 
    
  114.     expect(inspectedElement).toMatchInlineSnapshot(`
    
  115.       {
    
  116.         "context": {},
    
  117.         "events": undefined,
    
  118.         "hooks": null,
    
  119.         "id": 2,
    
  120.         "owners": null,
    
  121.         "props": {
    
  122.           "boolean_false": false,
    
  123.           "boolean_true": true,
    
  124.           "float": 1.23,
    
  125.           "infinity": Infinity,
    
  126.           "integer_one": 1,
    
  127.           "integer_zero": 0,
    
  128.           "nan": NaN,
    
  129.           "string": "abc",
    
  130.           "string_empty": "",
    
  131.           "value_null": null,
    
  132.           "value_undefined": undefined,
    
  133.         },
    
  134.         "rootType": null,
    
  135.         "state": null,
    
  136.       }
    
  137.     `);
    
  138.   });
    
  139. 
    
  140.   // @reactVersion >= 16.0
    
  141.   it('should support complex data types', async () => {
    
  142.     const Immutable = require('immutable');
    
  143. 
    
  144.     const Example = () => null;
    
  145. 
    
  146.     const arrayOfArrays = [[['abc', 123, true], []]];
    
  147.     const div = document.createElement('div');
    
  148.     const exampleFunction = () => {};
    
  149.     const setShallow = new Set(['abc', 123]);
    
  150.     const mapShallow = new Map([
    
  151.       ['name', 'Brian'],
    
  152.       ['food', 'sushi'],
    
  153.     ]);
    
  154.     const setOfSets = new Set([new Set(['a', 'b', 'c']), new Set([1, 2, 3])]);
    
  155.     const mapOfMaps = new Map([
    
  156.       ['first', mapShallow],
    
  157.       ['second', mapShallow],
    
  158.     ]);
    
  159.     const objectOfObjects = {
    
  160.       inner: {string: 'abc', number: 123, boolean: true},
    
  161.     };
    
  162.     const typedArray = Int8Array.from([100, -100, 0]);
    
  163.     const arrayBuffer = typedArray.buffer;
    
  164.     const dataView = new DataView(arrayBuffer);
    
  165.     const immutableMap = Immutable.fromJS({
    
  166.       a: [{hello: 'there'}, 'fixed', true],
    
  167.       b: 123,
    
  168.       c: {
    
  169.         '1': 'xyz',
    
  170.         xyz: 1,
    
  171.       },
    
  172.     });
    
  173. 
    
  174.     class Class {
    
  175.       anonymousFunction = () => {};
    
  176.     }
    
  177.     const instance = new Class();
    
  178. 
    
  179.     act(() =>
    
  180.       ReactDOM.render(
    
  181.         <Example
    
  182.           anonymous_fn={instance.anonymousFunction}
    
  183.           array_buffer={arrayBuffer}
    
  184.           array_of_arrays={arrayOfArrays}
    
  185.           // eslint-disable-next-line no-undef
    
  186.           big_int={BigInt(123)}
    
  187.           bound_fn={exampleFunction.bind(this)}
    
  188.           data_view={dataView}
    
  189.           date={new Date(123)}
    
  190.           fn={exampleFunction}
    
  191.           html_element={div}
    
  192.           immutable={immutableMap}
    
  193.           map={mapShallow}
    
  194.           map_of_maps={mapOfMaps}
    
  195.           object_of_objects={objectOfObjects}
    
  196.           react_element={<span />}
    
  197.           regexp={/abc/giu}
    
  198.           set={setShallow}
    
  199.           set_of_sets={setOfSets}
    
  200.           symbol={Symbol('symbol')}
    
  201.           typed_array={typedArray}
    
  202.         />,
    
  203.         document.createElement('div'),
    
  204.       ),
    
  205.     );
    
  206. 
    
  207.     const id = ((store.getElementIDAtIndex(0): any): number);
    
  208.     const inspectedElement = await read(id);
    
  209. 
    
  210.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  211.       {
    
  212.         "anonymous_fn": Dehydrated {
    
  213.           "preview_short": ƒ () {},
    
  214.           "preview_long": ƒ () {},
    
  215.         },
    
  216.         "array_buffer": Dehydrated {
    
  217.           "preview_short": ArrayBuffer(3),
    
  218.           "preview_long": ArrayBuffer(3),
    
  219.         },
    
  220.         "array_of_arrays": [
    
  221.           Dehydrated {
    
  222.             "preview_short": Array(2),
    
  223.             "preview_long": [Array(3), Array(0)],
    
  224.           },
    
  225.         ],
    
  226.         "big_int": Dehydrated {
    
  227.           "preview_short": 123n,
    
  228.           "preview_long": 123n,
    
  229.         },
    
  230.         "bound_fn": Dehydrated {
    
  231.           "preview_short": ƒ bound exampleFunction() {},
    
  232.           "preview_long": ƒ bound exampleFunction() {},
    
  233.         },
    
  234.         "data_view": Dehydrated {
    
  235.           "preview_short": DataView(3),
    
  236.           "preview_long": DataView(3),
    
  237.         },
    
  238.         "date": Dehydrated {
    
  239.           "preview_short": Thu Jan 01 1970 00:00:00 GMT+0000 (Coordinated Universal Time),
    
  240.           "preview_long": Thu Jan 01 1970 00:00:00 GMT+0000 (Coordinated Universal Time),
    
  241.         },
    
  242.         "fn": Dehydrated {
    
  243.           "preview_short": ƒ exampleFunction() {},
    
  244.           "preview_long": ƒ exampleFunction() {},
    
  245.         },
    
  246.         "html_element": Dehydrated {
    
  247.           "preview_short": <div />,
    
  248.           "preview_long": <div />,
    
  249.         },
    
  250.         "immutable": {
    
  251.           "0": Dehydrated {
    
  252.             "preview_short": Array(2),
    
  253.             "preview_long": ["a", List(3)],
    
  254.           },
    
  255.           "1": Dehydrated {
    
  256.             "preview_short": Array(2),
    
  257.             "preview_long": ["b", 123],
    
  258.           },
    
  259.           "2": Dehydrated {
    
  260.             "preview_short": Array(2),
    
  261.             "preview_long": ["c", Map(2)],
    
  262.           },
    
  263.         },
    
  264.         "map": {
    
  265.           "0": Dehydrated {
    
  266.             "preview_short": Array(2),
    
  267.             "preview_long": ["name", "Brian"],
    
  268.           },
    
  269.           "1": Dehydrated {
    
  270.             "preview_short": Array(2),
    
  271.             "preview_long": ["food", "sushi"],
    
  272.           },
    
  273.         },
    
  274.         "map_of_maps": {
    
  275.           "0": Dehydrated {
    
  276.             "preview_short": Array(2),
    
  277.             "preview_long": ["first", Map(2)],
    
  278.           },
    
  279.           "1": Dehydrated {
    
  280.             "preview_short": Array(2),
    
  281.             "preview_long": ["second", Map(2)],
    
  282.           },
    
  283.         },
    
  284.         "object_of_objects": {
    
  285.           "inner": Dehydrated {
    
  286.             "preview_short": {…},
    
  287.             "preview_long": {boolean: true, number: 123, string: "abc"},
    
  288.           },
    
  289.         },
    
  290.         "react_element": Dehydrated {
    
  291.           "preview_short": <span />,
    
  292.           "preview_long": <span />,
    
  293.         },
    
  294.         "regexp": Dehydrated {
    
  295.           "preview_short": /abc/giu,
    
  296.           "preview_long": /abc/giu,
    
  297.         },
    
  298.         "set": {
    
  299.           "0": "abc",
    
  300.           "1": 123,
    
  301.         },
    
  302.         "set_of_sets": {
    
  303.           "0": Dehydrated {
    
  304.             "preview_short": Set(3),
    
  305.             "preview_long": Set(3) {"a", "b", "c"},
    
  306.           },
    
  307.           "1": Dehydrated {
    
  308.             "preview_short": Set(3),
    
  309.             "preview_long": Set(3) {1, 2, 3},
    
  310.           },
    
  311.         },
    
  312.         "symbol": Dehydrated {
    
  313.           "preview_short": Symbol(symbol),
    
  314.           "preview_long": Symbol(symbol),
    
  315.         },
    
  316.         "typed_array": {
    
  317.           "0": 100,
    
  318.           "1": -100,
    
  319.           "2": 0,
    
  320.         },
    
  321.       }
    
  322.     `);
    
  323.   });
    
  324. 
    
  325.   // @reactVersion >= 16.0
    
  326.   it('should support objects with no prototype', async () => {
    
  327.     const Example = () => null;
    
  328. 
    
  329.     const object = Object.create(null);
    
  330.     object.string = 'abc';
    
  331.     object.number = 123;
    
  332.     object.boolean = true;
    
  333. 
    
  334.     act(() =>
    
  335.       ReactDOM.render(
    
  336.         <Example object={object} />,
    
  337.         document.createElement('div'),
    
  338.       ),
    
  339.     );
    
  340. 
    
  341.     const id = ((store.getElementIDAtIndex(0): any): number);
    
  342.     const inspectedElement = await read(id);
    
  343. 
    
  344.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  345.       {
    
  346.         "object": {
    
  347.           "boolean": true,
    
  348.           "number": 123,
    
  349.           "string": "abc",
    
  350.         },
    
  351.       }
    
  352.     `);
    
  353.   });
    
  354. 
    
  355.   // @reactVersion >= 16.0
    
  356.   it('should support objects with overridden hasOwnProperty', async () => {
    
  357.     const Example = () => null;
    
  358. 
    
  359.     const object = {
    
  360.       name: 'blah',
    
  361.       hasOwnProperty: true,
    
  362.     };
    
  363. 
    
  364.     act(() =>
    
  365.       ReactDOM.render(
    
  366.         <Example object={object} />,
    
  367.         document.createElement('div'),
    
  368.       ),
    
  369.     );
    
  370. 
    
  371.     const id = ((store.getElementIDAtIndex(0): any): number);
    
  372.     const inspectedElement = await read(id);
    
  373. 
    
  374.     // TRICKY: Don't use toMatchInlineSnapshot() for this test!
    
  375.     // Our snapshot serializer relies on hasOwnProperty() for feature detection.
    
  376.     expect(inspectedElement.props.object.name).toBe('blah');
    
  377.     expect(inspectedElement.props.object.hasOwnProperty).toBe(true);
    
  378.   });
    
  379. 
    
  380.   // @reactVersion >= 16.0
    
  381.   it('should not consume iterables while inspecting', async () => {
    
  382.     const Example = () => null;
    
  383. 
    
  384.     function* generator() {
    
  385.       yield 1;
    
  386.       yield 2;
    
  387.     }
    
  388. 
    
  389.     const iteratable = generator();
    
  390. 
    
  391.     act(() =>
    
  392.       ReactDOM.render(
    
  393.         <Example iteratable={iteratable} />,
    
  394.         document.createElement('div'),
    
  395.       ),
    
  396.     );
    
  397. 
    
  398.     const id = ((store.getElementIDAtIndex(0): any): number);
    
  399.     const inspectedElement = await read(id);
    
  400. 
    
  401.     expect(inspectedElement).toMatchInlineSnapshot(`
    
  402.       {
    
  403.         "context": {},
    
  404.         "events": undefined,
    
  405.         "hooks": null,
    
  406.         "id": 2,
    
  407.         "owners": null,
    
  408.         "props": {
    
  409.           "iteratable": Dehydrated {
    
  410.             "preview_short": Generator,
    
  411.             "preview_long": Generator,
    
  412.           },
    
  413.         },
    
  414.         "rootType": null,
    
  415.         "state": null,
    
  416.       }
    
  417.     `);
    
  418. 
    
  419.     // Inspecting should not consume the iterable.
    
  420.     expect(iteratable.next().value).toEqual(1);
    
  421.     expect(iteratable.next().value).toEqual(2);
    
  422.     expect(iteratable.next().value).toBeUndefined();
    
  423.   });
    
  424. 
    
  425.   // @reactVersion >= 16.0
    
  426.   it('should support custom objects with enumerable properties and getters', async () => {
    
  427.     class CustomData {
    
  428.       _number = 42;
    
  429.       get number() {
    
  430.         return this._number;
    
  431.       }
    
  432.       set number(value) {
    
  433.         this._number = value;
    
  434.       }
    
  435.     }
    
  436. 
    
  437.     const descriptor = ((Object.getOwnPropertyDescriptor(
    
  438.       CustomData.prototype,
    
  439.       'number',
    
  440.     ): any): PropertyDescriptor<number>);
    
  441.     descriptor.enumerable = true;
    
  442.     Object.defineProperty(CustomData.prototype, 'number', descriptor);
    
  443. 
    
  444.     const Example = ({data}) => null;
    
  445. 
    
  446.     act(() =>
    
  447.       ReactDOM.render(
    
  448.         <Example data={new CustomData()} />,
    
  449.         document.createElement('div'),
    
  450.       ),
    
  451.     );
    
  452. 
    
  453.     const id = ((store.getElementIDAtIndex(0): any): number);
    
  454.     const inspectedElement = await read(id);
    
  455. 
    
  456.     expect(inspectedElement).toMatchInlineSnapshot(`
    
  457.       {
    
  458.         "context": {},
    
  459.         "events": undefined,
    
  460.         "hooks": null,
    
  461.         "id": 2,
    
  462.         "owners": null,
    
  463.         "props": {
    
  464.           "data": {
    
  465.             "_number": 42,
    
  466.             "number": 42,
    
  467.           },
    
  468.         },
    
  469.         "rootType": null,
    
  470.         "state": null,
    
  471.       }
    
  472.     `);
    
  473.   });
    
  474. 
    
  475.   // @reactVersion >= 16.0
    
  476.   it('should support objects with inherited keys', async () => {
    
  477.     const Example = () => null;
    
  478. 
    
  479.     const base = Object.create(Object.prototype, {
    
  480.       enumerableStringBase: {
    
  481.         value: 1,
    
  482.         writable: true,
    
  483.         enumerable: true,
    
  484.         configurable: true,
    
  485.       },
    
  486.       [Symbol('enumerableSymbolBase')]: {
    
  487.         value: 1,
    
  488.         writable: true,
    
  489.         enumerable: true,
    
  490.         configurable: true,
    
  491.       },
    
  492.       nonEnumerableStringBase: {
    
  493.         value: 1,
    
  494.         writable: true,
    
  495.         enumerable: false,
    
  496.         configurable: true,
    
  497.       },
    
  498.       [Symbol('nonEnumerableSymbolBase')]: {
    
  499.         value: 1,
    
  500.         writable: true,
    
  501.         enumerable: false,
    
  502.         configurable: true,
    
  503.       },
    
  504.     });
    
  505. 
    
  506.     const object = Object.create(base, {
    
  507.       enumerableString: {
    
  508.         value: 2,
    
  509.         writable: true,
    
  510.         enumerable: true,
    
  511.         configurable: true,
    
  512.       },
    
  513.       nonEnumerableString: {
    
  514.         value: 3,
    
  515.         writable: true,
    
  516.         enumerable: false,
    
  517.         configurable: true,
    
  518.       },
    
  519.       123: {
    
  520.         value: 3,
    
  521.         writable: true,
    
  522.         enumerable: true,
    
  523.         configurable: true,
    
  524.       },
    
  525.       [Symbol('nonEnumerableSymbol')]: {
    
  526.         value: 2,
    
  527.         writable: true,
    
  528.         enumerable: false,
    
  529.         configurable: true,
    
  530.       },
    
  531.       [Symbol('enumerableSymbol')]: {
    
  532.         value: 3,
    
  533.         writable: true,
    
  534.         enumerable: true,
    
  535.         configurable: true,
    
  536.       },
    
  537.     });
    
  538. 
    
  539.     act(() =>
    
  540.       ReactDOM.render(<Example data={object} />, document.createElement('div')),
    
  541.     );
    
  542. 
    
  543.     const id = ((store.getElementIDAtIndex(0): any): number);
    
  544.     const inspectedElement = await read(id);
    
  545. 
    
  546.     expect(inspectedElement).toMatchInlineSnapshot(`
    
  547.       {
    
  548.         "context": {},
    
  549.         "events": undefined,
    
  550.         "hooks": null,
    
  551.         "id": 2,
    
  552.         "owners": null,
    
  553.         "props": {
    
  554.           "data": {
    
  555.             "123": 3,
    
  556.             "Symbol(enumerableSymbol)": 3,
    
  557.             "Symbol(enumerableSymbolBase)": 1,
    
  558.             "enumerableString": 2,
    
  559.             "enumerableStringBase": 1,
    
  560.           },
    
  561.         },
    
  562.         "rootType": null,
    
  563.         "state": null,
    
  564.       }
    
  565.     `);
    
  566.   });
    
  567. 
    
  568.   // @reactVersion >= 16.0
    
  569.   it('should allow component prop value and value`s prototype has same name params.', async () => {
    
  570.     const testData = Object.create(
    
  571.       {
    
  572.         a: undefined,
    
  573.         b: Infinity,
    
  574.         c: NaN,
    
  575.         d: 'normal',
    
  576.       },
    
  577.       {
    
  578.         a: {
    
  579.           value: undefined,
    
  580.           writable: true,
    
  581.           enumerable: true,
    
  582.           configurable: true,
    
  583.         },
    
  584.         b: {
    
  585.           value: Infinity,
    
  586.           writable: true,
    
  587.           enumerable: true,
    
  588.           configurable: true,
    
  589.         },
    
  590.         c: {
    
  591.           value: NaN,
    
  592.           writable: true,
    
  593.           enumerable: true,
    
  594.           configurable: true,
    
  595.         },
    
  596.         d: {
    
  597.           value: 'normal',
    
  598.           writable: true,
    
  599.           enumerable: true,
    
  600.           configurable: true,
    
  601.         },
    
  602.       },
    
  603.     );
    
  604. 
    
  605.     const Example = ({data}) => null;
    
  606.     act(() =>
    
  607.       ReactDOM.render(
    
  608.         <Example data={testData} />,
    
  609.         document.createElement('div'),
    
  610.       ),
    
  611.     );
    
  612. 
    
  613.     const id = ((store.getElementIDAtIndex(0): any): number);
    
  614.     const inspectedElement = await read(id);
    
  615. 
    
  616.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  617.       {
    
  618.         "data": {
    
  619.           "a": undefined,
    
  620.           "b": Infinity,
    
  621.           "c": NaN,
    
  622.           "d": "normal",
    
  623.         },
    
  624.       }
    
  625.     `);
    
  626.   });
    
  627. 
    
  628.   // @reactVersion >= 16.0
    
  629.   it('should not dehydrate nested values until explicitly requested', async () => {
    
  630.     const Example = () => null;
    
  631. 
    
  632.     act(() =>
    
  633.       ReactDOM.render(
    
  634.         <Example
    
  635.           nestedObject={{
    
  636.             a: {
    
  637.               b: {
    
  638.                 c: [
    
  639.                   {
    
  640.                     d: {
    
  641.                       e: {},
    
  642.                     },
    
  643.                   },
    
  644.                 ],
    
  645.               },
    
  646.             },
    
  647.           }}
    
  648.         />,
    
  649.         document.createElement('div'),
    
  650.       ),
    
  651.     );
    
  652. 
    
  653.     const id = ((store.getElementIDAtIndex(0): any): number);
    
  654. 
    
  655.     let inspectedElement = await read(id);
    
  656.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  657.       {
    
  658.         "nestedObject": {
    
  659.           "a": Dehydrated {
    
  660.             "preview_short": {…},
    
  661.             "preview_long": {b: {…}},
    
  662.           },
    
  663.         },
    
  664.       }
    
  665.     `);
    
  666. 
    
  667.     inspectedElement = await read(id, ['props', 'nestedObject', 'a']);
    
  668.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  669.       {
    
  670.         "nestedObject": {
    
  671.           "a": {
    
  672.             "b": {
    
  673.               "c": Dehydrated {
    
  674.                 "preview_short": Array(1),
    
  675.                 "preview_long": [{…}],
    
  676.               },
    
  677.             },
    
  678.           },
    
  679.         },
    
  680.       }
    
  681.     `);
    
  682. 
    
  683.     inspectedElement = await read(id, ['props', 'nestedObject', 'a', 'b', 'c']);
    
  684.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  685.       {
    
  686.         "nestedObject": {
    
  687.           "a": {
    
  688.             "b": {
    
  689.               "c": [
    
  690.                 {
    
  691.                   "d": Dehydrated {
    
  692.                     "preview_short": {…},
    
  693.                     "preview_long": {e: {…}},
    
  694.                   },
    
  695.                 },
    
  696.               ],
    
  697.             },
    
  698.           },
    
  699.         },
    
  700.       }
    
  701.     `);
    
  702. 
    
  703.     inspectedElement = await read(id, [
    
  704.       'props',
    
  705.       'nestedObject',
    
  706.       'a',
    
  707.       'b',
    
  708.       'c',
    
  709.       0,
    
  710.       'd',
    
  711.     ]);
    
  712.     expect(inspectedElement.props).toMatchInlineSnapshot(`
    
  713.       {
    
  714.         "nestedObject": {
    
  715.           "a": {
    
  716.             "b": {
    
  717.               "c": [
    
  718.                 {
    
  719.                   "d": {
    
  720.                     "e": {},
    
  721.                   },
    
  722.                 },
    
  723.               ],
    
  724.             },
    
  725.           },
    
  726.         },
    
  727.       }
    
  728.     `);
    
  729.   });
    
  730. 
    
  731.   // @reactVersion >= 16.0
    
  732.   it('should enable inspected values to be stored as global variables', () => {
    
  733.     const Example = () => null;
    
  734. 
    
  735.     const nestedObject = {
    
  736.       a: {
    
  737.         value: 1,
    
  738.         b: {
    
  739.           value: 1,
    
  740.           c: {
    
  741.             value: 1,
    
  742.           },
    
  743.         },
    
  744.       },
    
  745.     };
    
  746. 
    
  747.     act(() =>
    
  748.       ReactDOM.render(
    
  749.         <Example nestedObject={nestedObject} />,
    
  750.         document.createElement('div'),
    
  751.       ),
    
  752.     );
    
  753. 
    
  754.     const id = ((store.getElementIDAtIndex(0): any): number);
    
  755.     const rendererID = ((store.getRendererIDForElement(id): any): number);
    
  756. 
    
  757.     const logSpy = jest.fn();
    
  758.     jest.spyOn(console, 'log').mockImplementation(logSpy);
    
  759. 
    
  760.     // Should store the whole value (not just the hydrated parts)
    
  761.     backendAPI.storeAsGlobal({
    
  762.       bridge,
    
  763.       id,
    
  764.       path: ['props', 'nestedObject'],
    
  765.       rendererID,
    
  766.     });
    
  767. 
    
  768.     jest.runOnlyPendingTimers();
    
  769.     expect(logSpy).toHaveBeenCalledWith('$reactTemp0');
    
  770.     expect(global.$reactTemp0).toBe(nestedObject);
    
  771. 
    
  772.     logSpy.mockReset();
    
  773. 
    
  774.     // Should store the nested property specified (not just the outer value)
    
  775.     backendAPI.storeAsGlobal({
    
  776.       bridge,
    
  777.       id,
    
  778.       path: ['props', 'nestedObject', 'a', 'b'],
    
  779.       rendererID,
    
  780.     });
    
  781. 
    
  782.     jest.runOnlyPendingTimers();
    
  783.     expect(logSpy).toHaveBeenCalledWith('$reactTemp1');
    
  784.     expect(global.$reactTemp1).toBe(nestedObject.a.b);
    
  785.   });
    
  786. 
    
  787.   // @reactVersion >= 16.0
    
  788.   it('should enable inspected values to be copied to the clipboard', () => {
    
  789.     const Example = () => null;
    
  790. 
    
  791.     const nestedObject = {
    
  792.       a: {
    
  793.         value: 1,
    
  794.         b: {
    
  795.           value: 1,
    
  796.           c: {
    
  797.             value: 1,
    
  798.           },
    
  799.         },
    
  800.       },
    
  801.     };
    
  802. 
    
  803.     act(() =>
    
  804.       ReactDOM.render(
    
  805.         <Example nestedObject={nestedObject} />,
    
  806.         document.createElement('div'),
    
  807.       ),
    
  808.     );
    
  809. 
    
  810.     const id = ((store.getElementIDAtIndex(0): any): number);
    
  811.     const rendererID = ((store.getRendererIDForElement(id): any): number);
    
  812. 
    
  813.     // Should copy the whole value (not just the hydrated parts)
    
  814.     backendAPI.copyInspectedElementPath({
    
  815.       bridge,
    
  816.       id,
    
  817.       path: ['props', 'nestedObject'],
    
  818.       rendererID,
    
  819.     });
    
  820. 
    
  821.     jest.runOnlyPendingTimers();
    
  822.     expect(global.mockClipboardCopy).toHaveBeenCalledTimes(1);
    
  823.     expect(global.mockClipboardCopy).toHaveBeenCalledWith(
    
  824.       JSON.stringify(nestedObject, undefined, 2),
    
  825.     );
    
  826. 
    
  827.     global.mockClipboardCopy.mockReset();
    
  828. 
    
  829.     // Should copy the nested property specified (not just the outer value)
    
  830.     backendAPI.copyInspectedElementPath({
    
  831.       bridge,
    
  832.       id,
    
  833.       path: ['props', 'nestedObject', 'a', 'b'],
    
  834.       rendererID,
    
  835.     });
    
  836. 
    
  837.     jest.runOnlyPendingTimers();
    
  838.     expect(global.mockClipboardCopy).toHaveBeenCalledTimes(1);
    
  839.     expect(global.mockClipboardCopy).toHaveBeenCalledWith(
    
  840.       JSON.stringify(nestedObject.a.b, undefined, 2),
    
  841.     );
    
  842.   });
    
  843. 
    
  844.   // @reactVersion >= 16.0
    
  845.   it('should enable complex values to be copied to the clipboard', () => {
    
  846.     const Immutable = require('immutable');
    
  847. 
    
  848.     const Example = () => null;
    
  849. 
    
  850.     const set = new Set(['abc', 123]);
    
  851.     const map = new Map([
    
  852.       ['name', 'Brian'],
    
  853.       ['food', 'sushi'],
    
  854.     ]);
    
  855.     const setOfSets = new Set([new Set(['a', 'b', 'c']), new Set([1, 2, 3])]);
    
  856.     const mapOfMaps = new Map([
    
  857.       ['first', map],
    
  858.       ['second', map],
    
  859.     ]);
    
  860.     const typedArray = Int8Array.from([100, -100, 0]);
    
  861.     const arrayBuffer = typedArray.buffer;
    
  862.     const dataView = new DataView(arrayBuffer);
    
  863.     const immutable = Immutable.fromJS({
    
  864.       a: [{hello: 'there'}, 'fixed', true],
    
  865.       b: 123,
    
  866.       c: {
    
  867.         '1': 'xyz',
    
  868.         xyz: 1,
    
  869.       },
    
  870.     });
    
  871.     const bigInt = BigInt(123); // eslint-disable-line no-undef
    
  872. 
    
  873.     act(() =>
    
  874.       ReactDOM.render(
    
  875.         <Example
    
  876.           arrayBuffer={arrayBuffer}
    
  877.           dataView={dataView}
    
  878.           map={map}
    
  879.           set={set}
    
  880.           mapOfMaps={mapOfMaps}
    
  881.           setOfSets={setOfSets}
    
  882.           typedArray={typedArray}
    
  883.           immutable={immutable}
    
  884.           bigInt={bigInt}
    
  885.         />,
    
  886.         document.createElement('div'),
    
  887.       ),
    
  888.     );
    
  889. 
    
  890.     const id = ((store.getElementIDAtIndex(0): any): number);
    
  891.     const rendererID = ((store.getRendererIDForElement(id): any): number);
    
  892. 
    
  893.     // Should copy the whole value (not just the hydrated parts)
    
  894.     backendAPI.copyInspectedElementPath({
    
  895.       bridge,
    
  896.       id,
    
  897.       path: ['props'],
    
  898.       rendererID,
    
  899.     });
    
  900.     jest.runOnlyPendingTimers();
    
  901.     // Should not error despite lots of unserialized values.
    
  902. 
    
  903.     global.mockClipboardCopy.mockReset();
    
  904. 
    
  905.     // Should copy the nested property specified (not just the outer value)
    
  906.     backendAPI.copyInspectedElementPath({
    
  907.       bridge,
    
  908.       id,
    
  909.       path: ['props', 'bigInt'],
    
  910.       rendererID,
    
  911.     });
    
  912.     jest.runOnlyPendingTimers();
    
  913.     expect(global.mockClipboardCopy).toHaveBeenCalledTimes(1);
    
  914.     expect(global.mockClipboardCopy).toHaveBeenCalledWith(
    
  915.       JSON.stringify('123n'),
    
  916.     );
    
  917. 
    
  918.     global.mockClipboardCopy.mockReset();
    
  919. 
    
  920.     // Should copy the nested property specified (not just the outer value)
    
  921.     backendAPI.copyInspectedElementPath({
    
  922.       bridge,
    
  923.       id,
    
  924.       path: ['props', 'typedArray'],
    
  925.       rendererID,
    
  926.     });
    
  927.     jest.runOnlyPendingTimers();
    
  928.     expect(global.mockClipboardCopy).toHaveBeenCalledTimes(1);
    
  929.     expect(global.mockClipboardCopy).toHaveBeenCalledWith(
    
  930.       JSON.stringify({0: 100, 1: -100, 2: 0}, undefined, 2),
    
  931.     );
    
  932.   });
    
  933. });