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 type {FrontendBridge} from 'react-devtools-shared/src/bridge';
    
  12. import type Store from 'react-devtools-shared/src/devtools/store';
    
  13. import type {
    
  14.   DispatcherContext,
    
  15.   StateContext,
    
  16. } from 'react-devtools-shared/src/devtools/views/Components/TreeContext';
    
  17. 
    
  18. describe('TreeListContext', () => {
    
  19.   let React;
    
  20.   let ReactDOM;
    
  21.   let TestRenderer: ReactTestRenderer;
    
  22.   let bridge: FrontendBridge;
    
  23.   let legacyRender;
    
  24.   let store: Store;
    
  25.   let utils;
    
  26.   let withErrorsOrWarningsIgnored;
    
  27. 
    
  28.   let BridgeContext;
    
  29.   let StoreContext;
    
  30.   let TreeContext;
    
  31. 
    
  32.   let dispatch: DispatcherContext;
    
  33.   let state: StateContext;
    
  34. 
    
  35.   beforeEach(() => {
    
  36.     utils = require('./utils');
    
  37.     utils.beforeEachProfiling();
    
  38. 
    
  39.     legacyRender = utils.legacyRender;
    
  40.     withErrorsOrWarningsIgnored = utils.withErrorsOrWarningsIgnored;
    
  41. 
    
  42.     bridge = global.bridge;
    
  43.     store = global.store;
    
  44.     store.collapseNodesByDefault = false;
    
  45. 
    
  46.     React = require('react');
    
  47.     ReactDOM = require('react-dom');
    
  48.     TestRenderer = utils.requireTestRenderer();
    
  49. 
    
  50.     BridgeContext =
    
  51.       require('react-devtools-shared/src/devtools/views/context').BridgeContext;
    
  52.     StoreContext =
    
  53.       require('react-devtools-shared/src/devtools/views/context').StoreContext;
    
  54.     TreeContext = require('react-devtools-shared/src/devtools/views/Components/TreeContext');
    
  55.   });
    
  56. 
    
  57.   afterEach(() => {
    
  58.     // Reset between tests
    
  59.     dispatch = ((null: any): DispatcherContext);
    
  60.     state = ((null: any): StateContext);
    
  61.   });
    
  62. 
    
  63.   const Capture = () => {
    
  64.     dispatch = React.useContext(TreeContext.TreeDispatcherContext);
    
  65.     state = React.useContext(TreeContext.TreeStateContext);
    
  66.     return null;
    
  67.   };
    
  68. 
    
  69.   const Contexts = () => {
    
  70.     return (
    
  71.       <BridgeContext.Provider value={bridge}>
    
  72.         <StoreContext.Provider value={store}>
    
  73.           <TreeContext.TreeContextController>
    
  74.             <Capture />
    
  75.           </TreeContext.TreeContextController>
    
  76.         </StoreContext.Provider>
    
  77.       </BridgeContext.Provider>
    
  78.     );
    
  79.   };
    
  80. 
    
  81.   describe('tree state', () => {
    
  82.     it('should select the next and previous elements in the tree', () => {
    
  83.       const Grandparent = () => <Parent />;
    
  84.       const Parent = () => (
    
  85.         <React.Fragment>
    
  86.           <Child />
    
  87.           <Child />
    
  88.         </React.Fragment>
    
  89.       );
    
  90.       const Child = () => null;
    
  91. 
    
  92.       utils.act(() =>
    
  93.         legacyRender(<Grandparent />, document.createElement('div')),
    
  94.       );
    
  95. 
    
  96.       let renderer;
    
  97.       utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
    
  98.       expect(state).toMatchInlineSnapshot(`
    
  99.         [root]
    
  100.            ▾ <Grandparent>
    
  101.              ▾ <Parent>
    
  102.                  <Child>
    
  103.                  <Child>
    
  104.       `);
    
  105. 
    
  106.       // Test stepping through to the end
    
  107. 
    
  108.       utils.act(() => dispatch({type: 'SELECT_NEXT_ELEMENT_IN_TREE'}));
    
  109.       utils.act(() => renderer.update(<Contexts />));
    
  110.       expect(state).toMatchInlineSnapshot(`
    
  111.         [root]
    
  112.         →  ▾ <Grandparent>
    
  113.              ▾ <Parent>
    
  114.                  <Child>
    
  115.                  <Child>
    
  116.       `);
    
  117. 
    
  118.       utils.act(() => dispatch({type: 'SELECT_NEXT_ELEMENT_IN_TREE'}));
    
  119.       utils.act(() => renderer.update(<Contexts />));
    
  120.       expect(state).toMatchInlineSnapshot(`
    
  121.         [root]
    
  122.            ▾ <Grandparent>
    
  123.         →    ▾ <Parent>
    
  124.                  <Child>
    
  125.                  <Child>
    
  126.       `);
    
  127. 
    
  128.       utils.act(() => dispatch({type: 'SELECT_NEXT_ELEMENT_IN_TREE'}));
    
  129.       utils.act(() => renderer.update(<Contexts />));
    
  130.       expect(state).toMatchInlineSnapshot(`
    
  131.         [root]
    
  132.            ▾ <Grandparent>
    
  133.              ▾ <Parent>
    
  134.         →        <Child>
    
  135.                  <Child>
    
  136.       `);
    
  137. 
    
  138.       utils.act(() => dispatch({type: 'SELECT_NEXT_ELEMENT_IN_TREE'}));
    
  139.       utils.act(() => renderer.update(<Contexts />));
    
  140.       expect(state).toMatchInlineSnapshot(`
    
  141.         [root]
    
  142.            ▾ <Grandparent>
    
  143.              ▾ <Parent>
    
  144.                  <Child>
    
  145.         →        <Child>
    
  146.       `);
    
  147. 
    
  148.       // Test stepping back to the beginning
    
  149. 
    
  150.       utils.act(() => dispatch({type: 'SELECT_PREVIOUS_ELEMENT_IN_TREE'}));
    
  151.       utils.act(() => renderer.update(<Contexts />));
    
  152.       expect(state).toMatchInlineSnapshot(`
    
  153.         [root]
    
  154.            ▾ <Grandparent>
    
  155.              ▾ <Parent>
    
  156.         →        <Child>
    
  157.                  <Child>
    
  158.       `);
    
  159. 
    
  160.       utils.act(() => dispatch({type: 'SELECT_PREVIOUS_ELEMENT_IN_TREE'}));
    
  161.       utils.act(() => renderer.update(<Contexts />));
    
  162.       expect(state).toMatchInlineSnapshot(`
    
  163.         [root]
    
  164.            ▾ <Grandparent>
    
  165.         →    ▾ <Parent>
    
  166.                  <Child>
    
  167.                  <Child>
    
  168.       `);
    
  169. 
    
  170.       utils.act(() => dispatch({type: 'SELECT_PREVIOUS_ELEMENT_IN_TREE'}));
    
  171.       utils.act(() => renderer.update(<Contexts />));
    
  172.       expect(state).toMatchInlineSnapshot(`
    
  173.         [root]
    
  174.         →  ▾ <Grandparent>
    
  175.              ▾ <Parent>
    
  176.                  <Child>
    
  177.                  <Child>
    
  178.       `);
    
  179. 
    
  180.       // Test wrap around behavior
    
  181. 
    
  182.       utils.act(() => dispatch({type: 'SELECT_PREVIOUS_ELEMENT_IN_TREE'}));
    
  183.       utils.act(() => renderer.update(<Contexts />));
    
  184.       expect(state).toMatchInlineSnapshot(`
    
  185.         [root]
    
  186.            ▾ <Grandparent>
    
  187.              ▾ <Parent>
    
  188.                  <Child>
    
  189.         →        <Child>
    
  190.       `);
    
  191. 
    
  192.       utils.act(() => dispatch({type: 'SELECT_NEXT_ELEMENT_IN_TREE'}));
    
  193.       utils.act(() => renderer.update(<Contexts />));
    
  194.       expect(state).toMatchInlineSnapshot(`
    
  195.         [root]
    
  196.         →  ▾ <Grandparent>
    
  197.              ▾ <Parent>
    
  198.                  <Child>
    
  199.                  <Child>
    
  200.       `);
    
  201.     });
    
  202. 
    
  203.     it('should select child elements', () => {
    
  204.       const Grandparent = () => (
    
  205.         <React.Fragment>
    
  206.           <Parent />
    
  207.           <Parent />
    
  208.         </React.Fragment>
    
  209.       );
    
  210.       const Parent = () => (
    
  211.         <React.Fragment>
    
  212.           <Child />
    
  213.           <Child />
    
  214.         </React.Fragment>
    
  215.       );
    
  216.       const Child = () => null;
    
  217. 
    
  218.       utils.act(() =>
    
  219.         legacyRender(<Grandparent />, document.createElement('div')),
    
  220.       );
    
  221. 
    
  222.       let renderer;
    
  223.       utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
    
  224.       expect(state).toMatchInlineSnapshot(`
    
  225.         [root]
    
  226.            ▾ <Grandparent>
    
  227.              ▾ <Parent>
    
  228.                  <Child>
    
  229.                  <Child>
    
  230.              ▾ <Parent>
    
  231.                  <Child>
    
  232.                  <Child>
    
  233.       `);
    
  234. 
    
  235.       utils.act(() => dispatch({type: 'SELECT_ELEMENT_AT_INDEX', payload: 0}));
    
  236.       utils.act(() => renderer.update(<Contexts />));
    
  237.       expect(state).toMatchInlineSnapshot(`
    
  238.         [root]
    
  239.         →  ▾ <Grandparent>
    
  240.              ▾ <Parent>
    
  241.                  <Child>
    
  242.                  <Child>
    
  243.              ▾ <Parent>
    
  244.                  <Child>
    
  245.                  <Child>
    
  246.       `);
    
  247. 
    
  248.       utils.act(() => dispatch({type: 'SELECT_CHILD_ELEMENT_IN_TREE'}));
    
  249.       utils.act(() => renderer.update(<Contexts />));
    
  250.       expect(state).toMatchInlineSnapshot(`
    
  251.         [root]
    
  252.            ▾ <Grandparent>
    
  253.         →    ▾ <Parent>
    
  254.                  <Child>
    
  255.                  <Child>
    
  256.              ▾ <Parent>
    
  257.                  <Child>
    
  258.                  <Child>
    
  259.       `);
    
  260. 
    
  261.       utils.act(() => dispatch({type: 'SELECT_CHILD_ELEMENT_IN_TREE'}));
    
  262.       utils.act(() => renderer.update(<Contexts />));
    
  263.       expect(state).toMatchInlineSnapshot(`
    
  264.         [root]
    
  265.            ▾ <Grandparent>
    
  266.              ▾ <Parent>
    
  267.         →        <Child>
    
  268.                  <Child>
    
  269.              ▾ <Parent>
    
  270.                  <Child>
    
  271.                  <Child>
    
  272.       `);
    
  273. 
    
  274.       // There are no more children to select, so this should be a no-op
    
  275.       utils.act(() => dispatch({type: 'SELECT_CHILD_ELEMENT_IN_TREE'}));
    
  276.       utils.act(() => renderer.update(<Contexts />));
    
  277.       expect(state).toMatchInlineSnapshot(`
    
  278.         [root]
    
  279.            ▾ <Grandparent>
    
  280.              ▾ <Parent>
    
  281.         →        <Child>
    
  282.                  <Child>
    
  283.              ▾ <Parent>
    
  284.                  <Child>
    
  285.                  <Child>
    
  286.       `);
    
  287.     });
    
  288. 
    
  289.     it('should select parent elements and then collapse', () => {
    
  290.       const Grandparent = () => (
    
  291.         <React.Fragment>
    
  292.           <Parent />
    
  293.           <Parent />
    
  294.         </React.Fragment>
    
  295.       );
    
  296.       const Parent = () => (
    
  297.         <React.Fragment>
    
  298.           <Child />
    
  299.           <Child />
    
  300.         </React.Fragment>
    
  301.       );
    
  302.       const Child = () => null;
    
  303. 
    
  304.       utils.act(() =>
    
  305.         legacyRender(<Grandparent />, document.createElement('div')),
    
  306.       );
    
  307. 
    
  308.       let renderer;
    
  309.       utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
    
  310.       expect(state).toMatchInlineSnapshot(`
    
  311.         [root]
    
  312.            ▾ <Grandparent>
    
  313.              ▾ <Parent>
    
  314.                  <Child>
    
  315.                  <Child>
    
  316.              ▾ <Parent>
    
  317.                  <Child>
    
  318.                  <Child>
    
  319.       `);
    
  320. 
    
  321.       const lastChildID = store.getElementIDAtIndex(store.numElements - 1);
    
  322. 
    
  323.       // Select the last child
    
  324.       utils.act(() =>
    
  325.         dispatch({type: 'SELECT_ELEMENT_BY_ID', payload: lastChildID}),
    
  326.       );
    
  327.       utils.act(() => renderer.update(<Contexts />));
    
  328.       expect(state).toMatchInlineSnapshot(`
    
  329.         [root]
    
  330.            ▾ <Grandparent>
    
  331.              ▾ <Parent>
    
  332.                  <Child>
    
  333.                  <Child>
    
  334.              ▾ <Parent>
    
  335.                  <Child>
    
  336.         →        <Child>
    
  337.       `);
    
  338. 
    
  339.       // Select its parent
    
  340.       utils.act(() => dispatch({type: 'SELECT_PARENT_ELEMENT_IN_TREE'}));
    
  341.       utils.act(() => renderer.update(<Contexts />));
    
  342.       expect(state).toMatchInlineSnapshot(`
    
  343.         [root]
    
  344.            ▾ <Grandparent>
    
  345.              ▾ <Parent>
    
  346.                  <Child>
    
  347.                  <Child>
    
  348.         →    ▾ <Parent>
    
  349.                  <Child>
    
  350.                  <Child>
    
  351.       `);
    
  352. 
    
  353.       // Select grandparent
    
  354.       utils.act(() => dispatch({type: 'SELECT_PARENT_ELEMENT_IN_TREE'}));
    
  355.       utils.act(() => renderer.update(<Contexts />));
    
  356.       expect(state).toMatchInlineSnapshot(`
    
  357.         [root]
    
  358.         →  ▾ <Grandparent>
    
  359.              ▾ <Parent>
    
  360.                  <Child>
    
  361.                  <Child>
    
  362.              ▾ <Parent>
    
  363.                  <Child>
    
  364.                  <Child>
    
  365.       `);
    
  366. 
    
  367.       // No-op
    
  368.       utils.act(() => dispatch({type: 'SELECT_PARENT_ELEMENT_IN_TREE'}));
    
  369.       utils.act(() => renderer.update(<Contexts />));
    
  370.       expect(state).toMatchInlineSnapshot(`
    
  371.         [root]
    
  372.         →  ▾ <Grandparent>
    
  373.              ▾ <Parent>
    
  374.                  <Child>
    
  375.                  <Child>
    
  376.              ▾ <Parent>
    
  377.                  <Child>
    
  378.                  <Child>
    
  379.       `);
    
  380. 
    
  381.       const previousState = state;
    
  382. 
    
  383.       // There are no more ancestors to select, so this should be a no-op
    
  384.       utils.act(() => dispatch({type: 'SELECT_PARENT_ELEMENT_IN_TREE'}));
    
  385.       utils.act(() => renderer.update(<Contexts />));
    
  386.       expect(state).toEqual(previousState);
    
  387.     });
    
  388. 
    
  389.     it('should clear selection if the selected element is unmounted', async () => {
    
  390.       const Grandparent = props => props.children || null;
    
  391.       const Parent = props => props.children || null;
    
  392.       const Child = () => null;
    
  393. 
    
  394.       const container = document.createElement('div');
    
  395.       utils.act(() =>
    
  396.         legacyRender(
    
  397.           <Grandparent>
    
  398.             <Parent>
    
  399.               <Child />
    
  400.               <Child />
    
  401.             </Parent>
    
  402.           </Grandparent>,
    
  403.           container,
    
  404.         ),
    
  405.       );
    
  406. 
    
  407.       let renderer;
    
  408.       utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
    
  409.       expect(state).toMatchInlineSnapshot(`
    
  410.         [root]
    
  411.            ▾ <Grandparent>
    
  412.              ▾ <Parent>
    
  413.                  <Child>
    
  414.                  <Child>
    
  415.       `);
    
  416. 
    
  417.       // Select the second child
    
  418.       utils.act(() => dispatch({type: 'SELECT_ELEMENT_AT_INDEX', payload: 3}));
    
  419.       utils.act(() => renderer.update(<Contexts />));
    
  420.       expect(state).toMatchInlineSnapshot(`
    
  421.         [root]
    
  422.            ▾ <Grandparent>
    
  423.              ▾ <Parent>
    
  424.                  <Child>
    
  425.         →        <Child>
    
  426.       `);
    
  427. 
    
  428.       // Remove the child (which should auto-select the parent)
    
  429.       await utils.actAsync(() =>
    
  430.         legacyRender(
    
  431.           <Grandparent>
    
  432.             <Parent />
    
  433.           </Grandparent>,
    
  434.           container,
    
  435.         ),
    
  436.       );
    
  437.       expect(state).toMatchInlineSnapshot(`
    
  438.         [root]
    
  439.            ▾ <Grandparent>
    
  440.         →      <Parent>
    
  441.       `);
    
  442. 
    
  443.       // Unmount the root (so that nothing is selected)
    
  444.       await utils.actAsync(() => ReactDOM.unmountComponentAtNode(container));
    
  445.       expect(state).toMatchInlineSnapshot(``);
    
  446.     });
    
  447. 
    
  448.     it('should navigate next/previous sibling and skip over children in between', () => {
    
  449.       const Grandparent = () => (
    
  450.         <React.Fragment>
    
  451.           <Parent numChildren={1} />
    
  452.           <Parent numChildren={3} />
    
  453.           <Parent numChildren={2} />
    
  454.         </React.Fragment>
    
  455.       );
    
  456.       const Parent = ({numChildren}) =>
    
  457.         new Array(numChildren)
    
  458.           .fill(true)
    
  459.           .map((_, index) => <Child key={index} />);
    
  460.       const Child = () => null;
    
  461. 
    
  462.       utils.act(() =>
    
  463.         legacyRender(<Grandparent />, document.createElement('div')),
    
  464.       );
    
  465. 
    
  466.       let renderer;
    
  467.       utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
    
  468. 
    
  469.       const firstParentID = ((store.getElementIDAtIndex(1): any): number);
    
  470. 
    
  471.       utils.act(() =>
    
  472.         dispatch({type: 'SELECT_ELEMENT_BY_ID', payload: firstParentID}),
    
  473.       );
    
  474.       utils.act(() => renderer.update(<Contexts />));
    
  475.       expect(state).toMatchInlineSnapshot(`
    
  476.         [root]
    
  477.            ▾ <Grandparent>
    
  478.         →    ▾ <Parent>
    
  479.                  <Child key="0">
    
  480.              ▾ <Parent>
    
  481.                  <Child key="0">
    
  482.                  <Child key="1">
    
  483.                  <Child key="2">
    
  484.              ▾ <Parent>
    
  485.                  <Child key="0">
    
  486.                  <Child key="1">
    
  487.       `);
    
  488. 
    
  489.       utils.act(() => dispatch({type: 'SELECT_NEXT_SIBLING_IN_TREE'}));
    
  490.       utils.act(() => renderer.update(<Contexts />));
    
  491.       expect(state).toMatchInlineSnapshot(`
    
  492.         [root]
    
  493.            ▾ <Grandparent>
    
  494.              ▾ <Parent>
    
  495.                  <Child key="0">
    
  496.         →    ▾ <Parent>
    
  497.                  <Child key="0">
    
  498.                  <Child key="1">
    
  499.                  <Child key="2">
    
  500.              ▾ <Parent>
    
  501.                  <Child key="0">
    
  502.                  <Child key="1">
    
  503.       `);
    
  504. 
    
  505.       utils.act(() => dispatch({type: 'SELECT_NEXT_SIBLING_IN_TREE'}));
    
  506.       utils.act(() => renderer.update(<Contexts />));
    
  507.       expect(state).toMatchInlineSnapshot(`
    
  508.         [root]
    
  509.            ▾ <Grandparent>
    
  510.              ▾ <Parent>
    
  511.                  <Child key="0">
    
  512.              ▾ <Parent>
    
  513.                  <Child key="0">
    
  514.                  <Child key="1">
    
  515.                  <Child key="2">
    
  516.         →    ▾ <Parent>
    
  517.                  <Child key="0">
    
  518.                  <Child key="1">
    
  519.       `);
    
  520. 
    
  521.       utils.act(() => dispatch({type: 'SELECT_NEXT_SIBLING_IN_TREE'}));
    
  522.       utils.act(() => renderer.update(<Contexts />));
    
  523.       expect(state).toMatchInlineSnapshot(`
    
  524.         [root]
    
  525.            ▾ <Grandparent>
    
  526.         →    ▾ <Parent>
    
  527.                  <Child key="0">
    
  528.              ▾ <Parent>
    
  529.                  <Child key="0">
    
  530.                  <Child key="1">
    
  531.                  <Child key="2">
    
  532.              ▾ <Parent>
    
  533.                  <Child key="0">
    
  534.                  <Child key="1">
    
  535.       `);
    
  536. 
    
  537.       utils.act(() => dispatch({type: 'SELECT_PREVIOUS_SIBLING_IN_TREE'}));
    
  538.       utils.act(() => renderer.update(<Contexts />));
    
  539.       expect(state).toMatchInlineSnapshot(`
    
  540.         [root]
    
  541.            ▾ <Grandparent>
    
  542.              ▾ <Parent>
    
  543.                  <Child key="0">
    
  544.              ▾ <Parent>
    
  545.                  <Child key="0">
    
  546.                  <Child key="1">
    
  547.                  <Child key="2">
    
  548.         →    ▾ <Parent>
    
  549.                  <Child key="0">
    
  550.                  <Child key="1">
    
  551.       `);
    
  552. 
    
  553.       utils.act(() => dispatch({type: 'SELECT_PREVIOUS_SIBLING_IN_TREE'}));
    
  554.       utils.act(() => renderer.update(<Contexts />));
    
  555.       expect(state).toMatchInlineSnapshot(`
    
  556.         [root]
    
  557.            ▾ <Grandparent>
    
  558.              ▾ <Parent>
    
  559.                  <Child key="0">
    
  560.         →    ▾ <Parent>
    
  561.                  <Child key="0">
    
  562.                  <Child key="1">
    
  563.                  <Child key="2">
    
  564.              ▾ <Parent>
    
  565.                  <Child key="0">
    
  566.                  <Child key="1">
    
  567.       `);
    
  568. 
    
  569.       utils.act(() => dispatch({type: 'SELECT_PREVIOUS_SIBLING_IN_TREE'}));
    
  570.       utils.act(() => renderer.update(<Contexts />));
    
  571.       expect(state).toMatchInlineSnapshot(`
    
  572.         [root]
    
  573.            ▾ <Grandparent>
    
  574.         →    ▾ <Parent>
    
  575.                  <Child key="0">
    
  576.              ▾ <Parent>
    
  577.                  <Child key="0">
    
  578.                  <Child key="1">
    
  579.                  <Child key="2">
    
  580.              ▾ <Parent>
    
  581.                  <Child key="0">
    
  582.                  <Child key="1">
    
  583.       `);
    
  584. 
    
  585.       utils.act(() => dispatch({type: 'SELECT_PREVIOUS_SIBLING_IN_TREE'}));
    
  586.       utils.act(() => renderer.update(<Contexts />));
    
  587.       expect(state).toMatchInlineSnapshot(`
    
  588.         [root]
    
  589.            ▾ <Grandparent>
    
  590.              ▾ <Parent>
    
  591.                  <Child key="0">
    
  592.              ▾ <Parent>
    
  593.                  <Child key="0">
    
  594.                  <Child key="1">
    
  595.                  <Child key="2">
    
  596.         →    ▾ <Parent>
    
  597.                  <Child key="0">
    
  598.                  <Child key="1">
    
  599.       `);
    
  600.     });
    
  601. 
    
  602.     it('should navigate the owner hierarchy', () => {
    
  603.       const Wrapper = ({children}) => children;
    
  604.       const Grandparent = () => (
    
  605.         <React.Fragment>
    
  606.           <Wrapper>
    
  607.             <Parent numChildren={1} />
    
  608.           </Wrapper>
    
  609.           <Wrapper>
    
  610.             <Parent numChildren={3} />
    
  611.           </Wrapper>
    
  612.           <Wrapper>
    
  613.             <Parent numChildren={2} />
    
  614.           </Wrapper>
    
  615.         </React.Fragment>
    
  616.       );
    
  617.       const Parent = ({numChildren}) =>
    
  618.         new Array(numChildren)
    
  619.           .fill(true)
    
  620.           .map((_, index) => <Child key={index} />);
    
  621.       const Child = () => null;
    
  622. 
    
  623.       utils.act(() =>
    
  624.         legacyRender(<Grandparent />, document.createElement('div')),
    
  625.       );
    
  626. 
    
  627.       let renderer;
    
  628.       utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
    
  629. 
    
  630.       const childID = ((store.getElementIDAtIndex(7): any): number);
    
  631.       utils.act(() =>
    
  632.         dispatch({type: 'SELECT_ELEMENT_BY_ID', payload: childID}),
    
  633.       );
    
  634.       utils.act(() => renderer.update(<Contexts />));
    
  635.       expect(state).toMatchInlineSnapshot(`
    
  636.         [root]
    
  637.            ▾ <Grandparent>
    
  638.              ▾ <Wrapper>
    
  639.                ▾ <Parent>
    
  640.                    <Child key="0">
    
  641.              ▾ <Wrapper>
    
  642.                ▾ <Parent>
    
  643.                    <Child key="0">
    
  644.         →          <Child key="1">
    
  645.                    <Child key="2">
    
  646.              ▾ <Wrapper>
    
  647.                ▾ <Parent>
    
  648.                    <Child key="0">
    
  649.                    <Child key="1">
    
  650.       `);
    
  651. 
    
  652.       // Basic navigation test
    
  653.       utils.act(() =>
    
  654.         dispatch({type: 'SELECT_OWNER_LIST_PREVIOUS_ELEMENT_IN_TREE'}),
    
  655.       );
    
  656.       utils.act(() => renderer.update(<Contexts />));
    
  657.       expect(state).toMatchInlineSnapshot(`
    
  658.         [root]
    
  659.            ▾ <Grandparent>
    
  660.              ▾ <Wrapper>
    
  661.                ▾ <Parent>
    
  662.                    <Child key="0">
    
  663.              ▾ <Wrapper>
    
  664.         →      ▾ <Parent>
    
  665.                    <Child key="0">
    
  666.                    <Child key="1">
    
  667.                    <Child key="2">
    
  668.              ▾ <Wrapper>
    
  669.                ▾ <Parent>
    
  670.                    <Child key="0">
    
  671.                    <Child key="1">
    
  672.       `);
    
  673. 
    
  674.       utils.act(() =>
    
  675.         dispatch({type: 'SELECT_OWNER_LIST_PREVIOUS_ELEMENT_IN_TREE'}),
    
  676.       );
    
  677.       utils.act(() => renderer.update(<Contexts />));
    
  678.       expect(state).toMatchInlineSnapshot(`
    
  679.         [root]
    
  680.         →  ▾ <Grandparent>
    
  681.              ▾ <Wrapper>
    
  682.                ▾ <Parent>
    
  683.                    <Child key="0">
    
  684.              ▾ <Wrapper>
    
  685.                ▾ <Parent>
    
  686.                    <Child key="0">
    
  687.                    <Child key="1">
    
  688.                    <Child key="2">
    
  689.              ▾ <Wrapper>
    
  690.                ▾ <Parent>
    
  691.                    <Child key="0">
    
  692.                    <Child key="1">
    
  693.       `);
    
  694. 
    
  695.       // Noop (since we're at the root already)
    
  696.       utils.act(() =>
    
  697.         dispatch({type: 'SELECT_OWNER_LIST_PREVIOUS_ELEMENT_IN_TREE'}),
    
  698.       );
    
  699.       utils.act(() => renderer.update(<Contexts />));
    
  700.       expect(state).toMatchInlineSnapshot(`
    
  701.         [root]
    
  702.         →  ▾ <Grandparent>
    
  703.              ▾ <Wrapper>
    
  704.                ▾ <Parent>
    
  705.                    <Child key="0">
    
  706.              ▾ <Wrapper>
    
  707.                ▾ <Parent>
    
  708.                    <Child key="0">
    
  709.                    <Child key="1">
    
  710.                    <Child key="2">
    
  711.              ▾ <Wrapper>
    
  712.                ▾ <Parent>
    
  713.                    <Child key="0">
    
  714.                    <Child key="1">
    
  715.       `);
    
  716. 
    
  717.       utils.act(() =>
    
  718.         dispatch({type: 'SELECT_OWNER_LIST_NEXT_ELEMENT_IN_TREE'}),
    
  719.       );
    
  720.       utils.act(() => renderer.update(<Contexts />));
    
  721.       expect(state).toMatchInlineSnapshot(`
    
  722.         [root]
    
  723.            ▾ <Grandparent>
    
  724.              ▾ <Wrapper>
    
  725.                ▾ <Parent>
    
  726.                    <Child key="0">
    
  727.              ▾ <Wrapper>
    
  728.         →      ▾ <Parent>
    
  729.                    <Child key="0">
    
  730.                    <Child key="1">
    
  731.                    <Child key="2">
    
  732.              ▾ <Wrapper>
    
  733.                ▾ <Parent>
    
  734.                    <Child key="0">
    
  735.                    <Child key="1">
    
  736.       `);
    
  737. 
    
  738.       utils.act(() =>
    
  739.         dispatch({type: 'SELECT_OWNER_LIST_NEXT_ELEMENT_IN_TREE'}),
    
  740.       );
    
  741.       utils.act(() => renderer.update(<Contexts />));
    
  742.       expect(state).toMatchInlineSnapshot(`
    
  743.         [root]
    
  744.            ▾ <Grandparent>
    
  745.              ▾ <Wrapper>
    
  746.                ▾ <Parent>
    
  747.                    <Child key="0">
    
  748.              ▾ <Wrapper>
    
  749.                ▾ <Parent>
    
  750.                    <Child key="0">
    
  751.         →          <Child key="1">
    
  752.                    <Child key="2">
    
  753.              ▾ <Wrapper>
    
  754.                ▾ <Parent>
    
  755.                    <Child key="0">
    
  756.                    <Child key="1">
    
  757.       `);
    
  758. 
    
  759.       // Noop (since we're at the leaf node)
    
  760.       utils.act(() =>
    
  761.         dispatch({type: 'SELECT_OWNER_LIST_NEXT_ELEMENT_IN_TREE'}),
    
  762.       );
    
  763.       utils.act(() => renderer.update(<Contexts />));
    
  764.       expect(state).toMatchInlineSnapshot(`
    
  765.         [root]
    
  766.            ▾ <Grandparent>
    
  767.              ▾ <Wrapper>
    
  768.                ▾ <Parent>
    
  769.                    <Child key="0">
    
  770.              ▾ <Wrapper>
    
  771.                ▾ <Parent>
    
  772.                    <Child key="0">
    
  773.         →          <Child key="1">
    
  774.                    <Child key="2">
    
  775.              ▾ <Wrapper>
    
  776.                ▾ <Parent>
    
  777.                    <Child key="0">
    
  778.                    <Child key="1">
    
  779.       `);
    
  780. 
    
  781.       // Other navigational actions should clear out the temporary owner chain.
    
  782.       utils.act(() => dispatch({type: 'SELECT_PREVIOUS_ELEMENT_IN_TREE'}));
    
  783.       utils.act(() => renderer.update(<Contexts />));
    
  784.       expect(state).toMatchInlineSnapshot(`
    
  785.         [root]
    
  786.            ▾ <Grandparent>
    
  787.              ▾ <Wrapper>
    
  788.                ▾ <Parent>
    
  789.                    <Child key="0">
    
  790.              ▾ <Wrapper>
    
  791.                ▾ <Parent>
    
  792.         →          <Child key="0">
    
  793.                    <Child key="1">
    
  794.                    <Child key="2">
    
  795.              ▾ <Wrapper>
    
  796.                ▾ <Parent>
    
  797.                    <Child key="0">
    
  798.                    <Child key="1">
    
  799.       `);
    
  800. 
    
  801.       // Start a new tree on parent
    
  802.       const parentID = ((store.getElementIDAtIndex(5): any): number);
    
  803.       utils.act(() =>
    
  804.         dispatch({type: 'SELECT_ELEMENT_BY_ID', payload: parentID}),
    
  805.       );
    
  806.       utils.act(() => renderer.update(<Contexts />));
    
  807.       expect(state).toMatchInlineSnapshot(`
    
  808.         [root]
    
  809.            ▾ <Grandparent>
    
  810.              ▾ <Wrapper>
    
  811.                ▾ <Parent>
    
  812.                    <Child key="0">
    
  813.              ▾ <Wrapper>
    
  814.         →      ▾ <Parent>
    
  815.                    <Child key="0">
    
  816.                    <Child key="1">
    
  817.                    <Child key="2">
    
  818.              ▾ <Wrapper>
    
  819.                ▾ <Parent>
    
  820.                    <Child key="0">
    
  821.                    <Child key="1">
    
  822.       `);
    
  823. 
    
  824.       utils.act(() =>
    
  825.         dispatch({type: 'SELECT_OWNER_LIST_PREVIOUS_ELEMENT_IN_TREE'}),
    
  826.       );
    
  827.       utils.act(() => renderer.update(<Contexts />));
    
  828.       expect(state).toMatchInlineSnapshot(`
    
  829.         [root]
    
  830.         →  ▾ <Grandparent>
    
  831.              ▾ <Wrapper>
    
  832.                ▾ <Parent>
    
  833.                    <Child key="0">
    
  834.              ▾ <Wrapper>
    
  835.                ▾ <Parent>
    
  836.                    <Child key="0">
    
  837.                    <Child key="1">
    
  838.                    <Child key="2">
    
  839.              ▾ <Wrapper>
    
  840.                ▾ <Parent>
    
  841.                    <Child key="0">
    
  842.                    <Child key="1">
    
  843.       `);
    
  844. 
    
  845.       // Noop (since we're at the top)
    
  846.       utils.act(() =>
    
  847.         dispatch({type: 'SELECT_OWNER_LIST_PREVIOUS_ELEMENT_IN_TREE'}),
    
  848.       );
    
  849.       utils.act(() => renderer.update(<Contexts />));
    
  850.       expect(state).toMatchInlineSnapshot(`
    
  851.         [root]
    
  852.         →  ▾ <Grandparent>
    
  853.              ▾ <Wrapper>
    
  854.                ▾ <Parent>
    
  855.                    <Child key="0">
    
  856.              ▾ <Wrapper>
    
  857.                ▾ <Parent>
    
  858.                    <Child key="0">
    
  859.                    <Child key="1">
    
  860.                    <Child key="2">
    
  861.              ▾ <Wrapper>
    
  862.                ▾ <Parent>
    
  863.                    <Child key="0">
    
  864.                    <Child key="1">
    
  865.       `);
    
  866. 
    
  867.       utils.act(() =>
    
  868.         dispatch({type: 'SELECT_OWNER_LIST_NEXT_ELEMENT_IN_TREE'}),
    
  869.       );
    
  870.       utils.act(() => renderer.update(<Contexts />));
    
  871.       expect(state).toMatchInlineSnapshot(`
    
  872.         [root]
    
  873.            ▾ <Grandparent>
    
  874.              ▾ <Wrapper>
    
  875.                ▾ <Parent>
    
  876.                    <Child key="0">
    
  877.              ▾ <Wrapper>
    
  878.         →      ▾ <Parent>
    
  879.                    <Child key="0">
    
  880.                    <Child key="1">
    
  881.                    <Child key="2">
    
  882.              ▾ <Wrapper>
    
  883.                ▾ <Parent>
    
  884.                    <Child key="0">
    
  885.                    <Child key="1">
    
  886.       `);
    
  887. 
    
  888.       // Noop (since we're at the leaf of this owner tree)
    
  889.       // It should not be possible to navigate beyond the owner chain leaf.
    
  890.       utils.act(() =>
    
  891.         dispatch({type: 'SELECT_OWNER_LIST_NEXT_ELEMENT_IN_TREE'}),
    
  892.       );
    
  893.       utils.act(() => renderer.update(<Contexts />));
    
  894.       expect(state).toMatchInlineSnapshot(`
    
  895.         [root]
    
  896.            ▾ <Grandparent>
    
  897.              ▾ <Wrapper>
    
  898.                ▾ <Parent>
    
  899.                    <Child key="0">
    
  900.              ▾ <Wrapper>
    
  901.         →      ▾ <Parent>
    
  902.                    <Child key="0">
    
  903.                    <Child key="1">
    
  904.                    <Child key="2">
    
  905.              ▾ <Wrapper>
    
  906.                ▾ <Parent>
    
  907.                    <Child key="0">
    
  908.                    <Child key="1">
    
  909.       `);
    
  910.     });
    
  911.   });
    
  912. 
    
  913.   describe('search state', () => {
    
  914.     it('should find elements matching search text', () => {
    
  915.       const Foo = () => null;
    
  916.       const Bar = () => null;
    
  917.       const Baz = () => null;
    
  918.       const Qux = () => null;
    
  919. 
    
  920.       Qux.displayName = `withHOC(${Qux.name})`;
    
  921. 
    
  922.       utils.act(() =>
    
  923.         legacyRender(
    
  924.           <React.Fragment>
    
  925.             <Foo />
    
  926.             <Bar />
    
  927.             <Baz />
    
  928.             <Qux />
    
  929.           </React.Fragment>,
    
  930.           document.createElement('div'),
    
  931.         ),
    
  932.       );
    
  933. 
    
  934.       let renderer;
    
  935.       utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
    
  936.       expect(state).toMatchInlineSnapshot(`
    
  937.         [root]
    
  938.              <Foo>
    
  939.              <Bar>
    
  940.              <Baz>
    
  941.              <Qux> [withHOC]
    
  942.       `);
    
  943. 
    
  944.       // NOTE: multi-match
    
  945.       utils.act(() => dispatch({type: 'SET_SEARCH_TEXT', payload: 'ba'}));
    
  946.       utils.act(() => renderer.update(<Contexts />));
    
  947.       expect(state).toMatchInlineSnapshot(`
    
  948.         [root]
    
  949.              <Foo>
    
  950.         →    <Bar>
    
  951.              <Baz>
    
  952.              <Qux> [withHOC]
    
  953.       `);
    
  954. 
    
  955.       // NOTE: single match
    
  956.       utils.act(() => dispatch({type: 'SET_SEARCH_TEXT', payload: 'f'}));
    
  957.       utils.act(() => renderer.update(<Contexts />));
    
  958.       expect(state).toMatchInlineSnapshot(`
    
  959.         [root]
    
  960.         →    <Foo>
    
  961.              <Bar>
    
  962.              <Baz>
    
  963.              <Qux> [withHOC]
    
  964.       `);
    
  965. 
    
  966.       // NOTE: no match
    
  967.       utils.act(() => dispatch({type: 'SET_SEARCH_TEXT', payload: 'y'}));
    
  968.       utils.act(() => renderer.update(<Contexts />));
    
  969.       expect(state).toMatchInlineSnapshot(`
    
  970.         [root]
    
  971.         →    <Foo>
    
  972.              <Bar>
    
  973.              <Baz>
    
  974.              <Qux> [withHOC]
    
  975.       `);
    
  976. 
    
  977.       // NOTE: HOC match
    
  978.       utils.act(() => dispatch({type: 'SET_SEARCH_TEXT', payload: 'w'}));
    
  979.       utils.act(() => renderer.update(<Contexts />));
    
  980.       expect(state).toMatchInlineSnapshot(`
    
  981.         [root]
    
  982.              <Foo>
    
  983.              <Bar>
    
  984.              <Baz>
    
  985.         →    <Qux> [withHOC]
    
  986.       `);
    
  987.     });
    
  988. 
    
  989.     it('should select the next and previous items within the search results', () => {
    
  990.       const Foo = () => null;
    
  991.       const Bar = () => null;
    
  992.       const Baz = () => null;
    
  993. 
    
  994.       utils.act(() =>
    
  995.         legacyRender(
    
  996.           <React.Fragment>
    
  997.             <Foo />
    
  998.             <Baz />
    
  999.             <Bar />
    
  1000.             <Baz />
    
  1001.           </React.Fragment>,
    
  1002.           document.createElement('div'),
    
  1003.         ),
    
  1004.       );
    
  1005. 
    
  1006.       let renderer;
    
  1007.       utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
    
  1008.       expect(state).toMatchInlineSnapshot(`
    
  1009.         [root]
    
  1010.              <Foo>
    
  1011.              <Baz>
    
  1012.              <Bar>
    
  1013.              <Baz>
    
  1014.       `);
    
  1015. 
    
  1016.       // search for "ba"
    
  1017.       utils.act(() => dispatch({type: 'SET_SEARCH_TEXT', payload: 'ba'}));
    
  1018.       utils.act(() => renderer.update(<Contexts />));
    
  1019.       expect(state).toMatchInlineSnapshot(`
    
  1020.         [root]
    
  1021.              <Foo>
    
  1022.         →    <Baz>
    
  1023.              <Bar>
    
  1024.              <Baz>
    
  1025.       `);
    
  1026. 
    
  1027.       // go to second result
    
  1028.       utils.act(() => dispatch({type: 'GO_TO_NEXT_SEARCH_RESULT'}));
    
  1029.       utils.act(() => renderer.update(<Contexts />));
    
  1030.       expect(state).toMatchInlineSnapshot(`
    
  1031.         [root]
    
  1032.              <Foo>
    
  1033.              <Baz>
    
  1034.         →    <Bar>
    
  1035.              <Baz>
    
  1036.       `);
    
  1037. 
    
  1038.       // go to third result
    
  1039.       utils.act(() => dispatch({type: 'GO_TO_NEXT_SEARCH_RESULT'}));
    
  1040.       utils.act(() => renderer.update(<Contexts />));
    
  1041.       expect(state).toMatchInlineSnapshot(`
    
  1042.         [root]
    
  1043.              <Foo>
    
  1044.              <Baz>
    
  1045.              <Bar>
    
  1046.         →    <Baz>
    
  1047.       `);
    
  1048. 
    
  1049.       // go to second result
    
  1050.       utils.act(() => dispatch({type: 'GO_TO_PREVIOUS_SEARCH_RESULT'}));
    
  1051.       utils.act(() => renderer.update(<Contexts />));
    
  1052.       expect(state).toMatchInlineSnapshot(`
    
  1053.         [root]
    
  1054.              <Foo>
    
  1055.              <Baz>
    
  1056.         →    <Bar>
    
  1057.              <Baz>
    
  1058.       `);
    
  1059. 
    
  1060.       // go to first result
    
  1061.       utils.act(() => dispatch({type: 'GO_TO_PREVIOUS_SEARCH_RESULT'}));
    
  1062.       utils.act(() => renderer.update(<Contexts />));
    
  1063.       expect(state).toMatchInlineSnapshot(`
    
  1064.         [root]
    
  1065.              <Foo>
    
  1066.         →    <Baz>
    
  1067.              <Bar>
    
  1068.              <Baz>
    
  1069.       `);
    
  1070. 
    
  1071.       // wrap to last result
    
  1072.       utils.act(() => dispatch({type: 'GO_TO_PREVIOUS_SEARCH_RESULT'}));
    
  1073.       utils.act(() => renderer.update(<Contexts />));
    
  1074.       expect(state).toMatchInlineSnapshot(`
    
  1075.         [root]
    
  1076.              <Foo>
    
  1077.              <Baz>
    
  1078.              <Bar>
    
  1079.         →    <Baz>
    
  1080.       `);
    
  1081. 
    
  1082.       // wrap to first result
    
  1083.       utils.act(() => dispatch({type: 'GO_TO_NEXT_SEARCH_RESULT'}));
    
  1084.       utils.act(() => renderer.update(<Contexts />));
    
  1085.       expect(state).toMatchInlineSnapshot(`
    
  1086.         [root]
    
  1087.              <Foo>
    
  1088.         →    <Baz>
    
  1089.              <Bar>
    
  1090.              <Baz>
    
  1091.       `);
    
  1092.     });
    
  1093. 
    
  1094.     it('should add newly mounted elements to the search results set if they match the current text', async () => {
    
  1095.       const Foo = () => null;
    
  1096.       const Bar = () => null;
    
  1097.       const Baz = () => null;
    
  1098. 
    
  1099.       const container = document.createElement('div');
    
  1100. 
    
  1101.       utils.act(() =>
    
  1102.         legacyRender(
    
  1103.           <React.Fragment>
    
  1104.             <Foo />
    
  1105.             <Bar />
    
  1106.           </React.Fragment>,
    
  1107.           container,
    
  1108.         ),
    
  1109.       );
    
  1110. 
    
  1111.       let renderer;
    
  1112.       utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
    
  1113.       expect(state).toMatchInlineSnapshot(`
    
  1114.         [root]
    
  1115.              <Foo>
    
  1116.              <Bar>
    
  1117.       `);
    
  1118. 
    
  1119.       utils.act(() => dispatch({type: 'SET_SEARCH_TEXT', payload: 'ba'}));
    
  1120.       utils.act(() => renderer.update(<Contexts />));
    
  1121.       expect(state).toMatchInlineSnapshot(`
    
  1122.         [root]
    
  1123.              <Foo>
    
  1124.         →    <Bar>
    
  1125.       `);
    
  1126. 
    
  1127.       await utils.actAsync(() =>
    
  1128.         legacyRender(
    
  1129.           <React.Fragment>
    
  1130.             <Foo />
    
  1131.             <Bar />
    
  1132.             <Baz />
    
  1133.           </React.Fragment>,
    
  1134.           container,
    
  1135.         ),
    
  1136.       );
    
  1137.       utils.act(() => renderer.update(<Contexts />));
    
  1138.       expect(state).toMatchInlineSnapshot(`
    
  1139.         [root]
    
  1140.              <Foo>
    
  1141.         →    <Bar>
    
  1142.              <Baz>
    
  1143.       `);
    
  1144. 
    
  1145.       utils.act(() => dispatch({type: 'GO_TO_NEXT_SEARCH_RESULT'}));
    
  1146.       utils.act(() => renderer.update(<Contexts />));
    
  1147.       expect(state).toMatchInlineSnapshot(`
    
  1148.         [root]
    
  1149.              <Foo>
    
  1150.              <Bar>
    
  1151.         →    <Baz>
    
  1152.       `);
    
  1153.     });
    
  1154. 
    
  1155.     it('should remove unmounted elements from the search results set', async () => {
    
  1156.       const Foo = () => null;
    
  1157.       const Bar = () => null;
    
  1158.       const Baz = () => null;
    
  1159. 
    
  1160.       const container = document.createElement('div');
    
  1161. 
    
  1162.       utils.act(() =>
    
  1163.         legacyRender(
    
  1164.           <React.Fragment>
    
  1165.             <Foo />
    
  1166.             <Bar />
    
  1167.             <Baz />
    
  1168.           </React.Fragment>,
    
  1169.           container,
    
  1170.         ),
    
  1171.       );
    
  1172. 
    
  1173.       let renderer;
    
  1174.       utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
    
  1175.       expect(state).toMatchInlineSnapshot(`
    
  1176.         [root]
    
  1177.              <Foo>
    
  1178.              <Bar>
    
  1179.              <Baz>
    
  1180.       `);
    
  1181. 
    
  1182.       utils.act(() => dispatch({type: 'SET_SEARCH_TEXT', payload: 'ba'}));
    
  1183.       utils.act(() => renderer.update(<Contexts />));
    
  1184.       expect(state).toMatchInlineSnapshot(`
    
  1185.         [root]
    
  1186.              <Foo>
    
  1187.         →    <Bar>
    
  1188.              <Baz>
    
  1189.       `);
    
  1190. 
    
  1191.       utils.act(() => dispatch({type: 'GO_TO_NEXT_SEARCH_RESULT'}));
    
  1192.       utils.act(() => renderer.update(<Contexts />));
    
  1193.       expect(state).toMatchInlineSnapshot(`
    
  1194.         [root]
    
  1195.              <Foo>
    
  1196.              <Bar>
    
  1197.         →    <Baz>
    
  1198.       `);
    
  1199. 
    
  1200.       await utils.actAsync(() =>
    
  1201.         legacyRender(
    
  1202.           <React.Fragment>
    
  1203.             <Foo />
    
  1204.             <Bar />
    
  1205.           </React.Fragment>,
    
  1206.           container,
    
  1207.         ),
    
  1208.       );
    
  1209.       utils.act(() => renderer.update(<Contexts />));
    
  1210.       expect(state).toMatchInlineSnapshot(`
    
  1211.         [root]
    
  1212.              <Foo>
    
  1213.              <Bar>
    
  1214.       `);
    
  1215. 
    
  1216.       utils.act(() => dispatch({type: 'GO_TO_NEXT_SEARCH_RESULT'}));
    
  1217.       utils.act(() => renderer.update(<Contexts />));
    
  1218.       expect(state).toMatchInlineSnapshot(`
    
  1219.         [root]
    
  1220.              <Foo>
    
  1221.         →    <Bar>
    
  1222.       `);
    
  1223. 
    
  1224.       // Noop since the list is now one item long
    
  1225.       utils.act(() => dispatch({type: 'GO_TO_NEXT_SEARCH_RESULT'}));
    
  1226.       utils.act(() => renderer.update(<Contexts />));
    
  1227.       expect(state).toMatchInlineSnapshot(`
    
  1228.         [root]
    
  1229.              <Foo>
    
  1230.         →    <Bar>
    
  1231.       `);
    
  1232.     });
    
  1233.   });
    
  1234. 
    
  1235.   describe('owners state', () => {
    
  1236.     it('should support entering and existing the owners tree view', () => {
    
  1237.       const Grandparent = () => <Parent />;
    
  1238.       const Parent = () => (
    
  1239.         <React.Fragment>
    
  1240.           <Child />
    
  1241.           <Child />
    
  1242.         </React.Fragment>
    
  1243.       );
    
  1244.       const Child = () => null;
    
  1245. 
    
  1246.       utils.act(() =>
    
  1247.         legacyRender(<Grandparent />, document.createElement('div')),
    
  1248.       );
    
  1249. 
    
  1250.       let renderer;
    
  1251.       utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
    
  1252.       expect(state).toMatchInlineSnapshot(`
    
  1253.         [root]
    
  1254.            ▾ <Grandparent>
    
  1255.              ▾ <Parent>
    
  1256.                  <Child>
    
  1257.                  <Child>
    
  1258.       `);
    
  1259. 
    
  1260.       const parentID = ((store.getElementIDAtIndex(1): any): number);
    
  1261.       utils.act(() => dispatch({type: 'SELECT_OWNER', payload: parentID}));
    
  1262.       utils.act(() => renderer.update(<Contexts />));
    
  1263.       expect(state).toMatchInlineSnapshot(`
    
  1264.         [owners]
    
  1265.         →  ▾ <Parent>
    
  1266.                <Child>
    
  1267.                <Child>
    
  1268.       `);
    
  1269. 
    
  1270.       utils.act(() => dispatch({type: 'RESET_OWNER_STACK'}));
    
  1271.       utils.act(() => renderer.update(<Contexts />));
    
  1272.       expect(state).toMatchInlineSnapshot(`
    
  1273.         [root]
    
  1274.            ▾ <Grandparent>
    
  1275.         →    ▾ <Parent>
    
  1276.                  <Child>
    
  1277.                  <Child>
    
  1278.       `);
    
  1279.     });
    
  1280. 
    
  1281.     it('should remove an element from the owners list if it is unmounted', async () => {
    
  1282.       const Grandparent = ({count}) => <Parent count={count} />;
    
  1283.       const Parent = ({count}) =>
    
  1284.         new Array(count).fill(true).map((_, index) => <Child key={index} />);
    
  1285.       const Child = () => null;
    
  1286. 
    
  1287.       const container = document.createElement('div');
    
  1288.       utils.act(() => legacyRender(<Grandparent count={2} />, container));
    
  1289. 
    
  1290.       let renderer;
    
  1291.       utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
    
  1292.       expect(state).toMatchInlineSnapshot(`
    
  1293.         [root]
    
  1294.            ▾ <Grandparent>
    
  1295.              ▾ <Parent>
    
  1296.                  <Child key="0">
    
  1297.                  <Child key="1">
    
  1298.       `);
    
  1299. 
    
  1300.       const parentID = ((store.getElementIDAtIndex(1): any): number);
    
  1301.       utils.act(() => dispatch({type: 'SELECT_OWNER', payload: parentID}));
    
  1302.       utils.act(() => renderer.update(<Contexts />));
    
  1303.       expect(state).toMatchInlineSnapshot(`
    
  1304.         [owners]
    
  1305.         →  ▾ <Parent>
    
  1306.                <Child key="0">
    
  1307.                <Child key="1">
    
  1308.       `);
    
  1309. 
    
  1310.       await utils.actAsync(() =>
    
  1311.         legacyRender(<Grandparent count={1} />, container),
    
  1312.       );
    
  1313.       expect(state).toMatchInlineSnapshot(`
    
  1314.         [owners]
    
  1315.         →  ▾ <Parent>
    
  1316.                <Child key="0">
    
  1317.       `);
    
  1318. 
    
  1319.       await utils.actAsync(() =>
    
  1320.         legacyRender(<Grandparent count={0} />, container),
    
  1321.       );
    
  1322.       expect(state).toMatchInlineSnapshot(`
    
  1323.         [owners]
    
  1324.         →    <Parent>
    
  1325.       `);
    
  1326.     });
    
  1327. 
    
  1328.     it('should exit the owners list if the current owner is unmounted', async () => {
    
  1329.       const Parent = props => props.children || null;
    
  1330.       const Child = () => null;
    
  1331. 
    
  1332.       const container = document.createElement('div');
    
  1333.       utils.act(() =>
    
  1334.         legacyRender(
    
  1335.           <Parent>
    
  1336.             <Child />
    
  1337.           </Parent>,
    
  1338.           container,
    
  1339.         ),
    
  1340.       );
    
  1341. 
    
  1342.       let renderer;
    
  1343.       utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
    
  1344.       expect(state).toMatchInlineSnapshot(`
    
  1345.         [root]
    
  1346.            ▾ <Parent>
    
  1347.                <Child>
    
  1348.       `);
    
  1349. 
    
  1350.       const childID = ((store.getElementIDAtIndex(1): any): number);
    
  1351.       utils.act(() => dispatch({type: 'SELECT_OWNER', payload: childID}));
    
  1352.       utils.act(() => renderer.update(<Contexts />));
    
  1353.       expect(state).toMatchInlineSnapshot(`
    
  1354.         [owners]
    
  1355.         →    <Child>
    
  1356.       `);
    
  1357. 
    
  1358.       await utils.actAsync(() => legacyRender(<Parent />, container));
    
  1359.       expect(state).toMatchInlineSnapshot(`
    
  1360.         [root]
    
  1361.         →    <Parent>
    
  1362.       `);
    
  1363. 
    
  1364.       const parentID = ((store.getElementIDAtIndex(0): any): number);
    
  1365.       utils.act(() => dispatch({type: 'SELECT_OWNER', payload: parentID}));
    
  1366.       utils.act(() => renderer.update(<Contexts />));
    
  1367.       expect(state).toMatchInlineSnapshot(`
    
  1368.         [owners]
    
  1369.         →    <Parent>
    
  1370.       `);
    
  1371. 
    
  1372.       await utils.actAsync(() => ReactDOM.unmountComponentAtNode(container));
    
  1373.       expect(state).toMatchInlineSnapshot(``);
    
  1374.     });
    
  1375. 
    
  1376.     // This tests ensures support for toggling Suspense boundaries outside of the active owners list.
    
  1377.     it('should exit the owners list if an element outside the list is selected', () => {
    
  1378.       const Grandchild = () => null;
    
  1379.       const Child = () => (
    
  1380.         <React.Suspense fallback="Loading">
    
  1381.           <Grandchild />
    
  1382.         </React.Suspense>
    
  1383.       );
    
  1384.       const Parent = () => (
    
  1385.         <React.Suspense fallback="Loading">
    
  1386.           <Child />
    
  1387.         </React.Suspense>
    
  1388.       );
    
  1389. 
    
  1390.       const container = document.createElement('div');
    
  1391.       utils.act(() => legacyRender(<Parent />, container));
    
  1392. 
    
  1393.       let renderer;
    
  1394.       utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
    
  1395.       expect(state).toMatchInlineSnapshot(`
    
  1396.         [root]
    
  1397.            ▾ <Parent>
    
  1398.              ▾ <Suspense>
    
  1399.                ▾ <Child>
    
  1400.                  ▾ <Suspense>
    
  1401.                      <Grandchild>
    
  1402.       `);
    
  1403. 
    
  1404.       const outerSuspenseID = ((store.getElementIDAtIndex(1): any): number);
    
  1405.       const childID = ((store.getElementIDAtIndex(2): any): number);
    
  1406.       const innerSuspenseID = ((store.getElementIDAtIndex(3): any): number);
    
  1407. 
    
  1408.       utils.act(() => dispatch({type: 'SELECT_OWNER', payload: childID}));
    
  1409.       utils.act(() => renderer.update(<Contexts />));
    
  1410.       expect(state).toMatchInlineSnapshot(`
    
  1411.         [owners]
    
  1412.         →  ▾ <Child>
    
  1413.              ▾ <Suspense>
    
  1414.                  <Grandchild>
    
  1415.       `);
    
  1416. 
    
  1417.       // Toggling a Suspense boundary inside of the flat list should update selected index
    
  1418.       utils.act(() =>
    
  1419.         dispatch({type: 'SELECT_ELEMENT_BY_ID', payload: innerSuspenseID}),
    
  1420.       );
    
  1421.       utils.act(() => renderer.update(<Contexts />));
    
  1422.       expect(state).toMatchInlineSnapshot(`
    
  1423.         [owners]
    
  1424.            ▾ <Child>
    
  1425.         →    ▾ <Suspense>
    
  1426.                  <Grandchild>
    
  1427.       `);
    
  1428. 
    
  1429.       // Toggling a Suspense boundary outside of the flat list should exit owners list and update index
    
  1430.       utils.act(() =>
    
  1431.         dispatch({type: 'SELECT_ELEMENT_BY_ID', payload: outerSuspenseID}),
    
  1432.       );
    
  1433.       utils.act(() => renderer.update(<Contexts />));
    
  1434.       expect(state).toMatchInlineSnapshot(`
    
  1435.         [root]
    
  1436.            ▾ <Parent>
    
  1437.         →    ▾ <Suspense>
    
  1438.                ▾ <Child>
    
  1439.                  ▾ <Suspense>
    
  1440.                      <Grandchild>
    
  1441.       `);
    
  1442.     });
    
  1443.   });
    
  1444. 
    
  1445.   describe('inline errors/warnings state', () => {
    
  1446.     const {
    
  1447.       clearErrorsAndWarnings: clearErrorsAndWarningsAPI,
    
  1448.       clearErrorsForElement: clearErrorsForElementAPI,
    
  1449.       clearWarningsForElement: clearWarningsForElementAPI,
    
  1450.     } = require('react-devtools-shared/src/backendAPI');
    
  1451. 
    
  1452.     function clearAllErrors() {
    
  1453.       utils.act(() => clearErrorsAndWarningsAPI({bridge, store}));
    
  1454.       // flush events to the renderer
    
  1455.       jest.runAllTimers();
    
  1456.     }
    
  1457. 
    
  1458.     function clearErrorsForElement(id) {
    
  1459.       const rendererID = store.getRendererIDForElement(id);
    
  1460.       utils.act(() => clearErrorsForElementAPI({bridge, id, rendererID}));
    
  1461.       // flush events to the renderer
    
  1462.       jest.runAllTimers();
    
  1463.     }
    
  1464. 
    
  1465.     function clearWarningsForElement(id) {
    
  1466.       const rendererID = store.getRendererIDForElement(id);
    
  1467.       utils.act(() => clearWarningsForElementAPI({bridge, id, rendererID}));
    
  1468.       // flush events to the renderer
    
  1469.       jest.runAllTimers();
    
  1470.     }
    
  1471. 
    
  1472.     function selectNextErrorOrWarning() {
    
  1473.       utils.act(() =>
    
  1474.         dispatch({type: 'SELECT_NEXT_ELEMENT_WITH_ERROR_OR_WARNING_IN_TREE'}),
    
  1475.       );
    
  1476.     }
    
  1477. 
    
  1478.     function selectPreviousErrorOrWarning() {
    
  1479.       utils.act(() =>
    
  1480.         dispatch({
    
  1481.           type: 'SELECT_PREVIOUS_ELEMENT_WITH_ERROR_OR_WARNING_IN_TREE',
    
  1482.         }),
    
  1483.       );
    
  1484.     }
    
  1485. 
    
  1486.     function Child({logError = false, logWarning = false}) {
    
  1487.       if (logError === true) {
    
  1488.         console.error('test-only: error');
    
  1489.       }
    
  1490.       if (logWarning === true) {
    
  1491.         console.warn('test-only: warning');
    
  1492.       }
    
  1493.       return null;
    
  1494.     }
    
  1495. 
    
  1496.     it('should handle when there are no errors/warnings', () => {
    
  1497.       utils.act(() =>
    
  1498.         legacyRender(
    
  1499.           <React.Fragment>
    
  1500.             <Child />
    
  1501.             <Child />
    
  1502.             <Child />
    
  1503.           </React.Fragment>,
    
  1504.           document.createElement('div'),
    
  1505.         ),
    
  1506.       );
    
  1507. 
    
  1508.       utils.act(() => TestRenderer.create(<Contexts />));
    
  1509. 
    
  1510.       expect(state).toMatchInlineSnapshot(`
    
  1511.         [root]
    
  1512.              <Child>
    
  1513.              <Child>
    
  1514.              <Child>
    
  1515.       `);
    
  1516. 
    
  1517.       // Next/previous errors should be a no-op
    
  1518.       selectPreviousErrorOrWarning();
    
  1519.       expect(state).toMatchInlineSnapshot(`
    
  1520.         [root]
    
  1521.              <Child>
    
  1522.              <Child>
    
  1523.              <Child>
    
  1524.       `);
    
  1525.       selectNextErrorOrWarning();
    
  1526.       expect(state).toMatchInlineSnapshot(`
    
  1527.         [root]
    
  1528.              <Child>
    
  1529.              <Child>
    
  1530.              <Child>
    
  1531.       `);
    
  1532. 
    
  1533.       utils.act(() => dispatch({type: 'SELECT_ELEMENT_AT_INDEX', payload: 0}));
    
  1534.       expect(state).toMatchInlineSnapshot(`
    
  1535.         [root]
    
  1536.         →    <Child>
    
  1537.              <Child>
    
  1538.              <Child>
    
  1539.       `);
    
  1540. 
    
  1541.       // Next/previous errors should still be a no-op
    
  1542.       selectPreviousErrorOrWarning();
    
  1543.       expect(state).toMatchInlineSnapshot(`
    
  1544.         [root]
    
  1545.         →    <Child>
    
  1546.              <Child>
    
  1547.              <Child>
    
  1548.       `);
    
  1549.       selectNextErrorOrWarning();
    
  1550.       expect(state).toMatchInlineSnapshot(`
    
  1551.         [root]
    
  1552.         →    <Child>
    
  1553.              <Child>
    
  1554.              <Child>
    
  1555.       `);
    
  1556.     });
    
  1557. 
    
  1558.     it('should cycle through the next errors/warnings and wrap around', () => {
    
  1559.       withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  1560.         utils.act(() =>
    
  1561.           legacyRender(
    
  1562.             <React.Fragment>
    
  1563.               <Child />
    
  1564.               <Child logWarning={true} />
    
  1565.               <Child />
    
  1566.               <Child logError={true} />
    
  1567.               <Child />
    
  1568.             </React.Fragment>,
    
  1569.             document.createElement('div'),
    
  1570.           ),
    
  1571.         ),
    
  1572.       );
    
  1573. 
    
  1574.       utils.act(() => TestRenderer.create(<Contexts />));
    
  1575.       expect(state).toMatchInlineSnapshot(`
    
  1576.         ✕ 1, ⚠ 1
    
  1577.         [root]
    
  1578.              <Child>
    
  1579.              <Child> ⚠
    
  1580.              <Child>
    
  1581.              <Child> ✕
    
  1582.              <Child>
    
  1583.       `);
    
  1584. 
    
  1585.       selectNextErrorOrWarning();
    
  1586.       expect(state).toMatchInlineSnapshot(`
    
  1587.         ✕ 1, ⚠ 1
    
  1588.         [root]
    
  1589.              <Child>
    
  1590.         →    <Child> ⚠
    
  1591.              <Child>
    
  1592.              <Child> ✕
    
  1593.              <Child>
    
  1594.       `);
    
  1595. 
    
  1596.       selectNextErrorOrWarning();
    
  1597.       expect(state).toMatchInlineSnapshot(`
    
  1598.         ✕ 1, ⚠ 1
    
  1599.         [root]
    
  1600.              <Child>
    
  1601.              <Child> ⚠
    
  1602.              <Child>
    
  1603.         →    <Child> ✕
    
  1604.              <Child>
    
  1605.       `);
    
  1606. 
    
  1607.       selectNextErrorOrWarning();
    
  1608.       expect(state).toMatchInlineSnapshot(`
    
  1609.         ✕ 1, ⚠ 1
    
  1610.         [root]
    
  1611.              <Child>
    
  1612.         →    <Child> ⚠
    
  1613.              <Child>
    
  1614.              <Child> ✕
    
  1615.              <Child>
    
  1616.       `);
    
  1617.     });
    
  1618. 
    
  1619.     it('should cycle through the previous errors/warnings and wrap around', () => {
    
  1620.       withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  1621.         utils.act(() =>
    
  1622.           legacyRender(
    
  1623.             <React.Fragment>
    
  1624.               <Child />
    
  1625.               <Child logWarning={true} />
    
  1626.               <Child />
    
  1627.               <Child logError={true} />
    
  1628.               <Child />
    
  1629.             </React.Fragment>,
    
  1630.             document.createElement('div'),
    
  1631.           ),
    
  1632.         ),
    
  1633.       );
    
  1634. 
    
  1635.       utils.act(() => TestRenderer.create(<Contexts />));
    
  1636.       expect(state).toMatchInlineSnapshot(`
    
  1637.         ✕ 1, ⚠ 1
    
  1638.         [root]
    
  1639.              <Child>
    
  1640.              <Child> ⚠
    
  1641.              <Child>
    
  1642.              <Child> ✕
    
  1643.              <Child>
    
  1644.       `);
    
  1645. 
    
  1646.       selectPreviousErrorOrWarning();
    
  1647.       expect(state).toMatchInlineSnapshot(`
    
  1648.         ✕ 1, ⚠ 1
    
  1649.         [root]
    
  1650.              <Child>
    
  1651.              <Child> ⚠
    
  1652.              <Child>
    
  1653.         →    <Child> ✕
    
  1654.              <Child>
    
  1655.       `);
    
  1656. 
    
  1657.       selectPreviousErrorOrWarning();
    
  1658.       expect(state).toMatchInlineSnapshot(`
    
  1659.         ✕ 1, ⚠ 1
    
  1660.         [root]
    
  1661.              <Child>
    
  1662.         →    <Child> ⚠
    
  1663.              <Child>
    
  1664.              <Child> ✕
    
  1665.              <Child>
    
  1666.       `);
    
  1667. 
    
  1668.       selectPreviousErrorOrWarning();
    
  1669.       expect(state).toMatchInlineSnapshot(`
    
  1670.         ✕ 1, ⚠ 1
    
  1671.         [root]
    
  1672.              <Child>
    
  1673.              <Child> ⚠
    
  1674.              <Child>
    
  1675.         →    <Child> ✕
    
  1676.              <Child>
    
  1677.       `);
    
  1678.     });
    
  1679. 
    
  1680.     it('should cycle through the next errors/warnings and wrap around with multiple roots', () => {
    
  1681.       withErrorsOrWarningsIgnored(['test-only:'], () => {
    
  1682.         utils.act(() => {
    
  1683.           legacyRender(
    
  1684.             <React.Fragment>
    
  1685.               <Child />
    
  1686.               <Child logWarning={true} />,
    
  1687.             </React.Fragment>,
    
  1688.             document.createElement('div'),
    
  1689.           );
    
  1690.           legacyRender(
    
  1691.             <React.Fragment>
    
  1692.               <Child />
    
  1693.               <Child logError={true} />
    
  1694.               <Child />
    
  1695.             </React.Fragment>,
    
  1696.             document.createElement('div'),
    
  1697.           );
    
  1698.         });
    
  1699.       });
    
  1700. 
    
  1701.       utils.act(() => TestRenderer.create(<Contexts />));
    
  1702.       expect(state).toMatchInlineSnapshot(`
    
  1703.         ✕ 1, ⚠ 1
    
  1704.         [root]
    
  1705.              <Child>
    
  1706.              <Child> ⚠
    
  1707.         [root]
    
  1708.              <Child>
    
  1709.              <Child> ✕
    
  1710.              <Child>
    
  1711.       `);
    
  1712. 
    
  1713.       selectNextErrorOrWarning();
    
  1714.       expect(state).toMatchInlineSnapshot(`
    
  1715.         ✕ 1, ⚠ 1
    
  1716.         [root]
    
  1717.              <Child>
    
  1718.         →    <Child> ⚠
    
  1719.         [root]
    
  1720.              <Child>
    
  1721.              <Child> ✕
    
  1722.              <Child>
    
  1723.       `);
    
  1724. 
    
  1725.       selectNextErrorOrWarning();
    
  1726.       expect(state).toMatchInlineSnapshot(`
    
  1727.         ✕ 1, ⚠ 1
    
  1728.         [root]
    
  1729.              <Child>
    
  1730.              <Child> ⚠
    
  1731.         [root]
    
  1732.              <Child>
    
  1733.         →    <Child> ✕
    
  1734.              <Child>
    
  1735.       `);
    
  1736. 
    
  1737.       selectNextErrorOrWarning();
    
  1738.       expect(state).toMatchInlineSnapshot(`
    
  1739.         ✕ 1, ⚠ 1
    
  1740.         [root]
    
  1741.              <Child>
    
  1742.         →    <Child> ⚠
    
  1743.         [root]
    
  1744.              <Child>
    
  1745.              <Child> ✕
    
  1746.              <Child>
    
  1747.       `);
    
  1748.     });
    
  1749. 
    
  1750.     it('should cycle through the previous errors/warnings and wrap around with multiple roots', () => {
    
  1751.       withErrorsOrWarningsIgnored(['test-only:'], () => {
    
  1752.         utils.act(() => {
    
  1753.           legacyRender(
    
  1754.             <React.Fragment>
    
  1755.               <Child />
    
  1756.               <Child logWarning={true} />,
    
  1757.             </React.Fragment>,
    
  1758.             document.createElement('div'),
    
  1759.           );
    
  1760.           legacyRender(
    
  1761.             <React.Fragment>
    
  1762.               <Child />
    
  1763.               <Child logError={true} />
    
  1764.               <Child />
    
  1765.             </React.Fragment>,
    
  1766.             document.createElement('div'),
    
  1767.           );
    
  1768.         });
    
  1769.       });
    
  1770. 
    
  1771.       utils.act(() => TestRenderer.create(<Contexts />));
    
  1772.       expect(state).toMatchInlineSnapshot(`
    
  1773.         ✕ 1, ⚠ 1
    
  1774.         [root]
    
  1775.              <Child>
    
  1776.              <Child> ⚠
    
  1777.         [root]
    
  1778.              <Child>
    
  1779.              <Child> ✕
    
  1780.              <Child>
    
  1781.       `);
    
  1782. 
    
  1783.       selectPreviousErrorOrWarning();
    
  1784.       expect(state).toMatchInlineSnapshot(`
    
  1785.         ✕ 1, ⚠ 1
    
  1786.         [root]
    
  1787.              <Child>
    
  1788.              <Child> ⚠
    
  1789.         [root]
    
  1790.              <Child>
    
  1791.         →    <Child> ✕
    
  1792.              <Child>
    
  1793.       `);
    
  1794. 
    
  1795.       selectPreviousErrorOrWarning();
    
  1796.       expect(state).toMatchInlineSnapshot(`
    
  1797.         ✕ 1, ⚠ 1
    
  1798.         [root]
    
  1799.              <Child>
    
  1800.         →    <Child> ⚠
    
  1801.         [root]
    
  1802.              <Child>
    
  1803.              <Child> ✕
    
  1804.              <Child>
    
  1805.       `);
    
  1806. 
    
  1807.       selectPreviousErrorOrWarning();
    
  1808.       expect(state).toMatchInlineSnapshot(`
    
  1809.         ✕ 1, ⚠ 1
    
  1810.         [root]
    
  1811.              <Child>
    
  1812.              <Child> ⚠
    
  1813.         [root]
    
  1814.              <Child>
    
  1815.         →    <Child> ✕
    
  1816.              <Child>
    
  1817.       `);
    
  1818.     });
    
  1819. 
    
  1820.     it('should select the next or previous element relative to the current selection', () => {
    
  1821.       withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  1822.         utils.act(() =>
    
  1823.           legacyRender(
    
  1824.             <React.Fragment>
    
  1825.               <Child />
    
  1826.               <Child logWarning={true} />
    
  1827.               <Child />
    
  1828.               <Child logError={true} />
    
  1829.               <Child />
    
  1830.             </React.Fragment>,
    
  1831.             document.createElement('div'),
    
  1832.           ),
    
  1833.         ),
    
  1834.       );
    
  1835. 
    
  1836.       utils.act(() => TestRenderer.create(<Contexts />));
    
  1837.       utils.act(() => dispatch({type: 'SELECT_ELEMENT_AT_INDEX', payload: 2}));
    
  1838.       expect(state).toMatchInlineSnapshot(`
    
  1839.         ✕ 1, ⚠ 1
    
  1840.         [root]
    
  1841.              <Child>
    
  1842.              <Child> ⚠
    
  1843.         →    <Child>
    
  1844.              <Child> ✕
    
  1845.              <Child>
    
  1846.       `);
    
  1847. 
    
  1848.       selectNextErrorOrWarning();
    
  1849.       expect(state).toMatchInlineSnapshot(`
    
  1850.         ✕ 1, ⚠ 1
    
  1851.         [root]
    
  1852.              <Child>
    
  1853.              <Child> ⚠
    
  1854.              <Child>
    
  1855.         →    <Child> ✕
    
  1856.              <Child>
    
  1857.       `);
    
  1858. 
    
  1859.       utils.act(() => dispatch({type: 'SELECT_ELEMENT_AT_INDEX', payload: 2}));
    
  1860.       expect(state).toMatchInlineSnapshot(`
    
  1861.         ✕ 1, ⚠ 1
    
  1862.         [root]
    
  1863.              <Child>
    
  1864.              <Child> ⚠
    
  1865.         →    <Child>
    
  1866.              <Child> ✕
    
  1867.              <Child>
    
  1868.       `);
    
  1869. 
    
  1870.       selectPreviousErrorOrWarning();
    
  1871.       expect(state).toMatchInlineSnapshot(`
    
  1872.         ✕ 1, ⚠ 1
    
  1873.         [root]
    
  1874.              <Child>
    
  1875.         →    <Child> ⚠
    
  1876.              <Child>
    
  1877.              <Child> ✕
    
  1878.              <Child>
    
  1879.       `);
    
  1880.     });
    
  1881. 
    
  1882.     it('should update correctly when errors/warnings are cleared for a fiber in the list', () => {
    
  1883.       withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  1884.         utils.act(() =>
    
  1885.           legacyRender(
    
  1886.             <React.Fragment>
    
  1887.               <Child logWarning={true} />
    
  1888.               <Child logError={true} />
    
  1889.               <Child logError={true} />
    
  1890.               <Child logWarning={true} />
    
  1891.             </React.Fragment>,
    
  1892.             document.createElement('div'),
    
  1893.           ),
    
  1894.         ),
    
  1895.       );
    
  1896. 
    
  1897.       utils.act(() => TestRenderer.create(<Contexts />));
    
  1898.       expect(state).toMatchInlineSnapshot(`
    
  1899.         ✕ 2, ⚠ 2
    
  1900.         [root]
    
  1901.              <Child> ⚠
    
  1902.              <Child> ✕
    
  1903.              <Child> ✕
    
  1904.              <Child> ⚠
    
  1905.       `);
    
  1906. 
    
  1907.       // Select the first item in the list
    
  1908.       selectNextErrorOrWarning();
    
  1909.       expect(state).toMatchInlineSnapshot(`
    
  1910.         ✕ 2, ⚠ 2
    
  1911.         [root]
    
  1912.         →    <Child> ⚠
    
  1913.              <Child> ✕
    
  1914.              <Child> ✕
    
  1915.              <Child> ⚠
    
  1916.       `);
    
  1917. 
    
  1918.       // Clear warnings (but the next Fiber has only errors)
    
  1919.       clearWarningsForElement(store.getElementIDAtIndex(1));
    
  1920.       selectNextErrorOrWarning();
    
  1921.       expect(state).toMatchInlineSnapshot(`
    
  1922.         ✕ 2, ⚠ 2
    
  1923.         [root]
    
  1924.              <Child> ⚠
    
  1925.         →    <Child> ✕
    
  1926.              <Child> ✕
    
  1927.              <Child> ⚠
    
  1928.       `);
    
  1929. 
    
  1930.       clearErrorsForElement(store.getElementIDAtIndex(2));
    
  1931. 
    
  1932.       // Should step to the (now) next one in the list.
    
  1933.       selectNextErrorOrWarning();
    
  1934.       expect(state).toMatchInlineSnapshot(`
    
  1935.         ✕ 1, ⚠ 2
    
  1936.         [root]
    
  1937.              <Child> ⚠
    
  1938.              <Child> ✕
    
  1939.              <Child>
    
  1940.         →    <Child> ⚠
    
  1941.       `);
    
  1942. 
    
  1943.       // Should skip over the (now) cleared Fiber
    
  1944.       selectPreviousErrorOrWarning();
    
  1945.       expect(state).toMatchInlineSnapshot(`
    
  1946.         ✕ 1, ⚠ 2
    
  1947.         [root]
    
  1948.              <Child> ⚠
    
  1949.         →    <Child> ✕
    
  1950.              <Child>
    
  1951.              <Child> ⚠
    
  1952.       `);
    
  1953.     });
    
  1954. 
    
  1955.     it('should update correctly when errors/warnings are cleared for the currently selected fiber', () => {
    
  1956.       withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  1957.         utils.act(() =>
    
  1958.           legacyRender(
    
  1959.             <React.Fragment>
    
  1960.               <Child logWarning={true} />
    
  1961.               <Child logError={true} />
    
  1962.             </React.Fragment>,
    
  1963.             document.createElement('div'),
    
  1964.           ),
    
  1965.         ),
    
  1966.       );
    
  1967. 
    
  1968.       utils.act(() => TestRenderer.create(<Contexts />));
    
  1969.       expect(state).toMatchInlineSnapshot(`
    
  1970.         ✕ 1, ⚠ 1
    
  1971.         [root]
    
  1972.              <Child> ⚠
    
  1973.              <Child> ✕
    
  1974.       `);
    
  1975. 
    
  1976.       selectNextErrorOrWarning();
    
  1977.       expect(state).toMatchInlineSnapshot(`
    
  1978.         ✕ 1, ⚠ 1
    
  1979.         [root]
    
  1980.         →    <Child> ⚠
    
  1981.              <Child> ✕
    
  1982.       `);
    
  1983. 
    
  1984.       clearWarningsForElement(store.getElementIDAtIndex(0));
    
  1985.       selectNextErrorOrWarning();
    
  1986.       expect(state).toMatchInlineSnapshot(`
    
  1987.         ✕ 1, ⚠ 0
    
  1988.         [root]
    
  1989.              <Child>
    
  1990.         →    <Child> ✕
    
  1991.       `);
    
  1992.     });
    
  1993. 
    
  1994.     it('should update correctly when new errors/warnings are added', () => {
    
  1995.       const container = document.createElement('div');
    
  1996. 
    
  1997.       withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  1998.         utils.act(() =>
    
  1999.           legacyRender(
    
  2000.             <React.Fragment>
    
  2001.               <Child logWarning={true} />
    
  2002.               <Child />
    
  2003.               <Child />
    
  2004.               <Child logError={true} />
    
  2005.             </React.Fragment>,
    
  2006.             container,
    
  2007.           ),
    
  2008.         ),
    
  2009.       );
    
  2010. 
    
  2011.       utils.act(() => TestRenderer.create(<Contexts />));
    
  2012.       expect(state).toMatchInlineSnapshot(`
    
  2013.         ✕ 1, ⚠ 1
    
  2014.         [root]
    
  2015.              <Child> ⚠
    
  2016.              <Child>
    
  2017.              <Child>
    
  2018.              <Child> ✕
    
  2019.       `);
    
  2020. 
    
  2021.       selectNextErrorOrWarning();
    
  2022.       expect(state).toMatchInlineSnapshot(`
    
  2023.         ✕ 1, ⚠ 1
    
  2024.         [root]
    
  2025.         →    <Child> ⚠
    
  2026.              <Child>
    
  2027.              <Child>
    
  2028.              <Child> ✕
    
  2029.       `);
    
  2030. 
    
  2031.       withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  2032.         utils.act(() =>
    
  2033.           legacyRender(
    
  2034.             <React.Fragment>
    
  2035.               <Child />
    
  2036.               <Child logWarning={true} />
    
  2037.               <Child />
    
  2038.               <Child />
    
  2039.             </React.Fragment>,
    
  2040.             container,
    
  2041.           ),
    
  2042.         ),
    
  2043.       );
    
  2044. 
    
  2045.       selectNextErrorOrWarning();
    
  2046.       expect(state).toMatchInlineSnapshot(`
    
  2047.         ✕ 1, ⚠ 2
    
  2048.         [root]
    
  2049.              <Child> ⚠
    
  2050.         →    <Child> ⚠
    
  2051.              <Child>
    
  2052.              <Child> ✕
    
  2053.       `);
    
  2054. 
    
  2055.       selectNextErrorOrWarning();
    
  2056.       expect(state).toMatchInlineSnapshot(`
    
  2057.         ✕ 1, ⚠ 2
    
  2058.         [root]
    
  2059.              <Child> ⚠
    
  2060.              <Child> ⚠
    
  2061.              <Child>
    
  2062.         →    <Child> ✕
    
  2063.       `);
    
  2064. 
    
  2065.       selectNextErrorOrWarning();
    
  2066.       expect(state).toMatchInlineSnapshot(`
    
  2067.         ✕ 1, ⚠ 2
    
  2068.         [root]
    
  2069.         →    <Child> ⚠
    
  2070.              <Child> ⚠
    
  2071.              <Child>
    
  2072.              <Child> ✕
    
  2073.       `);
    
  2074.     });
    
  2075. 
    
  2076.     it('should update correctly when all errors/warnings are cleared', () => {
    
  2077.       withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  2078.         utils.act(() =>
    
  2079.           legacyRender(
    
  2080.             <React.Fragment>
    
  2081.               <Child logWarning={true} />
    
  2082.               <Child logError={true} />
    
  2083.             </React.Fragment>,
    
  2084.             document.createElement('div'),
    
  2085.           ),
    
  2086.         ),
    
  2087.       );
    
  2088. 
    
  2089.       utils.act(() => TestRenderer.create(<Contexts />));
    
  2090.       expect(state).toMatchInlineSnapshot(`
    
  2091.         ✕ 1, ⚠ 1
    
  2092.         [root]
    
  2093.              <Child> ⚠
    
  2094.              <Child> ✕
    
  2095.       `);
    
  2096. 
    
  2097.       selectNextErrorOrWarning();
    
  2098.       expect(state).toMatchInlineSnapshot(`
    
  2099.         ✕ 1, ⚠ 1
    
  2100.         [root]
    
  2101.         →    <Child> ⚠
    
  2102.              <Child> ✕
    
  2103.       `);
    
  2104. 
    
  2105.       clearAllErrors();
    
  2106. 
    
  2107.       selectNextErrorOrWarning();
    
  2108.       expect(state).toMatchInlineSnapshot(`
    
  2109.         [root]
    
  2110.         →    <Child>
    
  2111.              <Child>
    
  2112.       `);
    
  2113. 
    
  2114.       selectPreviousErrorOrWarning();
    
  2115.       expect(state).toMatchInlineSnapshot(`
    
  2116.         [root]
    
  2117.         →    <Child>
    
  2118.              <Child>
    
  2119.       `);
    
  2120.     });
    
  2121. 
    
  2122.     it('should update correctly when elements are added/removed', () => {
    
  2123.       const container = document.createElement('div');
    
  2124.       let errored = false;
    
  2125.       function ErrorOnce() {
    
  2126.         if (!errored) {
    
  2127.           errored = true;
    
  2128.           console.error('test-only:one-time-error');
    
  2129.         }
    
  2130.         return null;
    
  2131.       }
    
  2132.       withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  2133.         utils.act(() =>
    
  2134.           legacyRender(
    
  2135.             <React.Fragment>
    
  2136.               <ErrorOnce key="error" />
    
  2137.             </React.Fragment>,
    
  2138.             container,
    
  2139.           ),
    
  2140.         ),
    
  2141.       );
    
  2142. 
    
  2143.       let renderer;
    
  2144.       utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
    
  2145.       expect(state).toMatchInlineSnapshot(`
    
  2146.         ✕ 1, ⚠ 0
    
  2147.         [root]
    
  2148.              <ErrorOnce key="error"> ✕
    
  2149.       `);
    
  2150. 
    
  2151.       withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  2152.         utils.act(() =>
    
  2153.           legacyRender(
    
  2154.             <React.Fragment>
    
  2155.               <Child />
    
  2156.               <ErrorOnce key="error" />
    
  2157.             </React.Fragment>,
    
  2158.             container,
    
  2159.           ),
    
  2160.         ),
    
  2161.       );
    
  2162. 
    
  2163.       utils.act(() => renderer.update(<Contexts />));
    
  2164.       expect(state).toMatchInlineSnapshot(`
    
  2165.         ✕ 1, ⚠ 0
    
  2166.         [root]
    
  2167.              <Child>
    
  2168.              <ErrorOnce key="error"> ✕
    
  2169.       `);
    
  2170. 
    
  2171.       selectNextErrorOrWarning();
    
  2172.       expect(state).toMatchInlineSnapshot(`
    
  2173.         ✕ 1, ⚠ 0
    
  2174.         [root]
    
  2175.              <Child>
    
  2176.         →    <ErrorOnce key="error"> ✕
    
  2177.       `);
    
  2178.     });
    
  2179. 
    
  2180.     it('should update correctly when elements are re-ordered', () => {
    
  2181.       const container = document.createElement('div');
    
  2182.       function ErrorOnce() {
    
  2183.         const didErrorRef = React.useRef(false);
    
  2184.         if (!didErrorRef.current) {
    
  2185.           didErrorRef.current = true;
    
  2186.           console.error('test-only:one-time-error');
    
  2187.         }
    
  2188.         return null;
    
  2189.       }
    
  2190.       withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  2191.         utils.act(() =>
    
  2192.           legacyRender(
    
  2193.             <React.Fragment>
    
  2194.               <Child key="A" />
    
  2195.               <ErrorOnce key="B" />
    
  2196.               <Child key="C" />
    
  2197.               <ErrorOnce key="D" />
    
  2198.             </React.Fragment>,
    
  2199.             container,
    
  2200.           ),
    
  2201.         ),
    
  2202.       );
    
  2203. 
    
  2204.       let renderer;
    
  2205.       utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
    
  2206.       expect(state).toMatchInlineSnapshot(`
    
  2207.         ✕ 2, ⚠ 0
    
  2208.         [root]
    
  2209.              <Child key="A">
    
  2210.              <ErrorOnce key="B"> ✕
    
  2211.              <Child key="C">
    
  2212.              <ErrorOnce key="D"> ✕
    
  2213.       `);
    
  2214. 
    
  2215.       // Select a child
    
  2216.       selectNextErrorOrWarning();
    
  2217.       utils.act(() => renderer.update(<Contexts />));
    
  2218.       expect(state).toMatchInlineSnapshot(`
    
  2219.         ✕ 2, ⚠ 0
    
  2220.         [root]
    
  2221.              <Child key="A">
    
  2222.         →    <ErrorOnce key="B"> ✕
    
  2223.              <Child key="C">
    
  2224.              <ErrorOnce key="D"> ✕
    
  2225.       `);
    
  2226. 
    
  2227.       // Re-order the tree and ensure indices are updated.
    
  2228.       withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  2229.         utils.act(() =>
    
  2230.           legacyRender(
    
  2231.             <React.Fragment>
    
  2232.               <ErrorOnce key="B" />
    
  2233.               <Child key="A" />
    
  2234.               <ErrorOnce key="D" />
    
  2235.               <Child key="C" />
    
  2236.             </React.Fragment>,
    
  2237.             container,
    
  2238.           ),
    
  2239.         ),
    
  2240.       );
    
  2241.       expect(state).toMatchInlineSnapshot(`
    
  2242.         ✕ 2, ⚠ 0
    
  2243.         [root]
    
  2244.         →    <ErrorOnce key="B"> ✕
    
  2245.              <Child key="A">
    
  2246.              <ErrorOnce key="D"> ✕
    
  2247.              <Child key="C">
    
  2248.       `);
    
  2249. 
    
  2250.       // Select the next child and ensure the index doesn't break.
    
  2251.       selectNextErrorOrWarning();
    
  2252.       expect(state).toMatchInlineSnapshot(`
    
  2253.         ✕ 2, ⚠ 0
    
  2254.         [root]
    
  2255.              <ErrorOnce key="B"> ✕
    
  2256.              <Child key="A">
    
  2257.         →    <ErrorOnce key="D"> ✕
    
  2258.              <Child key="C">
    
  2259.       `);
    
  2260. 
    
  2261.       // Re-order the tree and ensure indices are updated.
    
  2262.       withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  2263.         utils.act(() =>
    
  2264.           legacyRender(
    
  2265.             <React.Fragment>
    
  2266.               <ErrorOnce key="D" />
    
  2267.               <ErrorOnce key="B" />
    
  2268.               <Child key="A" />
    
  2269.               <Child key="C" />
    
  2270.             </React.Fragment>,
    
  2271.             container,
    
  2272.           ),
    
  2273.         ),
    
  2274.       );
    
  2275.       expect(state).toMatchInlineSnapshot(`
    
  2276.         ✕ 2, ⚠ 0
    
  2277.         [root]
    
  2278.         →    <ErrorOnce key="D"> ✕
    
  2279.              <ErrorOnce key="B"> ✕
    
  2280.              <Child key="A">
    
  2281.              <Child key="C">
    
  2282.       `);
    
  2283.     });
    
  2284. 
    
  2285.     it('should update select and auto-expand parts components within hidden parts of the tree', () => {
    
  2286.       const Wrapper = ({children}) => children;
    
  2287. 
    
  2288.       store.collapseNodesByDefault = true;
    
  2289. 
    
  2290.       withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  2291.         utils.act(() =>
    
  2292.           legacyRender(
    
  2293.             <React.Fragment>
    
  2294.               <Wrapper>
    
  2295.                 <Child logWarning={true} />
    
  2296.               </Wrapper>
    
  2297.               <Wrapper>
    
  2298.                 <Wrapper>
    
  2299.                   <Child logWarning={true} />
    
  2300.                 </Wrapper>
    
  2301.               </Wrapper>
    
  2302.             </React.Fragment>,
    
  2303.             document.createElement('div'),
    
  2304.           ),
    
  2305.         ),
    
  2306.       );
    
  2307. 
    
  2308.       utils.act(() => TestRenderer.create(<Contexts />));
    
  2309.       expect(state).toMatchInlineSnapshot(`
    
  2310.         ✕ 0, ⚠ 2
    
  2311.         [root]
    
  2312.            ▸ <Wrapper>
    
  2313.            ▸ <Wrapper>
    
  2314.       `);
    
  2315. 
    
  2316.       selectNextErrorOrWarning();
    
  2317.       expect(state).toMatchInlineSnapshot(`
    
  2318.         ✕ 0, ⚠ 2
    
  2319.         [root]
    
  2320.            ▾ <Wrapper>
    
  2321.         →      <Child> ⚠
    
  2322.            ▸ <Wrapper>
    
  2323.       `);
    
  2324. 
    
  2325.       selectNextErrorOrWarning();
    
  2326.       expect(state).toMatchInlineSnapshot(`
    
  2327.         ✕ 0, ⚠ 2
    
  2328.         [root]
    
  2329.            ▾ <Wrapper>
    
  2330.                <Child> ⚠
    
  2331.            ▾ <Wrapper>
    
  2332.              ▾ <Wrapper>
    
  2333.         →        <Child> ⚠
    
  2334.       `);
    
  2335.     });
    
  2336. 
    
  2337.     it('should properly handle when components filters are updated', () => {
    
  2338.       const Wrapper = ({children}) => children;
    
  2339. 
    
  2340.       withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  2341.         utils.act(() =>
    
  2342.           legacyRender(
    
  2343.             <React.Fragment>
    
  2344.               <Wrapper>
    
  2345.                 <Child logWarning={true} />
    
  2346.               </Wrapper>
    
  2347.               <Wrapper>
    
  2348.                 <Wrapper>
    
  2349.                   <Child logWarning={true} />
    
  2350.                 </Wrapper>
    
  2351.               </Wrapper>
    
  2352.             </React.Fragment>,
    
  2353.             document.createElement('div'),
    
  2354.           ),
    
  2355.         ),
    
  2356.       );
    
  2357. 
    
  2358.       utils.act(() => TestRenderer.create(<Contexts />));
    
  2359.       expect(state).toMatchInlineSnapshot(`
    
  2360.         ✕ 0, ⚠ 2
    
  2361.         [root]
    
  2362.            ▾ <Wrapper>
    
  2363.                <Child> ⚠
    
  2364.            ▾ <Wrapper>
    
  2365.              ▾ <Wrapper>
    
  2366.                  <Child> ⚠
    
  2367.       `);
    
  2368. 
    
  2369.       selectNextErrorOrWarning();
    
  2370.       expect(state).toMatchInlineSnapshot(`
    
  2371.         ✕ 0, ⚠ 2
    
  2372.         [root]
    
  2373.            ▾ <Wrapper>
    
  2374.         →      <Child> ⚠
    
  2375.            ▾ <Wrapper>
    
  2376.              ▾ <Wrapper>
    
  2377.                  <Child> ⚠
    
  2378.       `);
    
  2379. 
    
  2380.       utils.act(() => {
    
  2381.         store.componentFilters = [utils.createDisplayNameFilter('Wrapper')];
    
  2382.       });
    
  2383.       expect(state).toMatchInlineSnapshot(`
    
  2384.         ✕ 0, ⚠ 2
    
  2385.         [root]
    
  2386.         →    <Child> ⚠
    
  2387.              <Child> ⚠
    
  2388.       `);
    
  2389. 
    
  2390.       selectNextErrorOrWarning();
    
  2391.       expect(state).toMatchInlineSnapshot(`
    
  2392.         ✕ 0, ⚠ 2
    
  2393.         [root]
    
  2394.              <Child> ⚠
    
  2395.         →    <Child> ⚠
    
  2396.       `);
    
  2397. 
    
  2398.       utils.act(() => {
    
  2399.         store.componentFilters = [];
    
  2400.       });
    
  2401.       expect(state).toMatchInlineSnapshot(`
    
  2402.         ✕ 0, ⚠ 2
    
  2403.         [root]
    
  2404.            ▾ <Wrapper>
    
  2405.                <Child> ⚠
    
  2406.            ▾ <Wrapper>
    
  2407.              ▾ <Wrapper>
    
  2408.         →        <Child> ⚠
    
  2409.       `);
    
  2410. 
    
  2411.       selectPreviousErrorOrWarning();
    
  2412.       expect(state).toMatchInlineSnapshot(`
    
  2413.         ✕ 0, ⚠ 2
    
  2414.         [root]
    
  2415.            ▾ <Wrapper>
    
  2416.         →      <Child> ⚠
    
  2417.            ▾ <Wrapper>
    
  2418.              ▾ <Wrapper>
    
  2419.                  <Child> ⚠
    
  2420.       `);
    
  2421.     });
    
  2422. 
    
  2423.     it('should preserve errors for fibers even if they are filtered out of the tree initially', () => {
    
  2424.       const Wrapper = ({children}) => children;
    
  2425. 
    
  2426.       withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  2427.         utils.act(() =>
    
  2428.           legacyRender(
    
  2429.             <React.Fragment>
    
  2430.               <Wrapper>
    
  2431.                 <Child logWarning={true} />
    
  2432.               </Wrapper>
    
  2433.               <Wrapper>
    
  2434.                 <Wrapper>
    
  2435.                   <Child logWarning={true} />
    
  2436.                 </Wrapper>
    
  2437.               </Wrapper>
    
  2438.             </React.Fragment>,
    
  2439.             document.createElement('div'),
    
  2440.           ),
    
  2441.         ),
    
  2442.       );
    
  2443. 
    
  2444.       store.componentFilters = [utils.createDisplayNameFilter('Child')];
    
  2445. 
    
  2446.       utils.act(() => TestRenderer.create(<Contexts />));
    
  2447.       expect(state).toMatchInlineSnapshot(`
    
  2448.         [root]
    
  2449.              <Wrapper>
    
  2450.            ▾ <Wrapper>
    
  2451.                <Wrapper>
    
  2452.       `);
    
  2453. 
    
  2454.       utils.act(() => {
    
  2455.         store.componentFilters = [];
    
  2456.       });
    
  2457.       expect(state).toMatchInlineSnapshot(`
    
  2458.         ✕ 0, ⚠ 2
    
  2459.         [root]
    
  2460.            ▾ <Wrapper>
    
  2461.                <Child> ⚠
    
  2462.            ▾ <Wrapper>
    
  2463.              ▾ <Wrapper>
    
  2464.                  <Child> ⚠
    
  2465.       `);
    
  2466. 
    
  2467.       selectNextErrorOrWarning();
    
  2468.       expect(state).toMatchInlineSnapshot(`
    
  2469.         ✕ 0, ⚠ 2
    
  2470.         [root]
    
  2471.            ▾ <Wrapper>
    
  2472.         →      <Child> ⚠
    
  2473.            ▾ <Wrapper>
    
  2474.              ▾ <Wrapper>
    
  2475.                  <Child> ⚠
    
  2476.       `);
    
  2477.     });
    
  2478. 
    
  2479.     describe('suspense', () => {
    
  2480.       // This verifies that we don't flush before the tree has been committed.
    
  2481.       it('should properly handle errors/warnings from components inside of delayed Suspense', async () => {
    
  2482.         const NeverResolves = React.lazy(() => new Promise(() => {}));
    
  2483. 
    
  2484.         withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  2485.           utils.act(() =>
    
  2486.             legacyRender(
    
  2487.               <React.Suspense fallback={null}>
    
  2488.                 <Child logWarning={true} />
    
  2489.                 <NeverResolves />
    
  2490.               </React.Suspense>,
    
  2491.               document.createElement('div'),
    
  2492.             ),
    
  2493.           ),
    
  2494.         );
    
  2495.         utils.act(() => TestRenderer.create(<Contexts />));
    
  2496. 
    
  2497.         jest.runAllTimers();
    
  2498. 
    
  2499.         expect(state).toMatchInlineSnapshot(`
    
  2500.                   [root]
    
  2501.                        <Suspense>
    
  2502.               `);
    
  2503. 
    
  2504.         selectNextErrorOrWarning();
    
  2505. 
    
  2506.         expect(state).toMatchInlineSnapshot(`
    
  2507.                   [root]
    
  2508.                        <Suspense>
    
  2509.               `);
    
  2510.       });
    
  2511. 
    
  2512.       it('should properly handle errors/warnings from components that dont mount because of Suspense', async () => {
    
  2513.         async function fakeImport(result) {
    
  2514.           return {default: result};
    
  2515.         }
    
  2516.         const LazyComponent = React.lazy(() => fakeImport(Child));
    
  2517. 
    
  2518.         const container = document.createElement('div');
    
  2519. 
    
  2520.         withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  2521.           utils.act(() =>
    
  2522.             legacyRender(
    
  2523.               <React.Suspense fallback={null}>
    
  2524.                 <Child logWarning={true} />
    
  2525.                 <LazyComponent />
    
  2526.               </React.Suspense>,
    
  2527.               container,
    
  2528.             ),
    
  2529.           ),
    
  2530.         );
    
  2531.         utils.act(() => TestRenderer.create(<Contexts />));
    
  2532. 
    
  2533.         expect(state).toMatchInlineSnapshot(`
    
  2534.                   [root]
    
  2535.                        <Suspense>
    
  2536.               `);
    
  2537. 
    
  2538.         await Promise.resolve();
    
  2539.         withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  2540.           utils.act(() =>
    
  2541.             legacyRender(
    
  2542.               <React.Suspense fallback={null}>
    
  2543.                 <Child logWarning={true} />
    
  2544.                 <LazyComponent />
    
  2545.               </React.Suspense>,
    
  2546.               container,
    
  2547.             ),
    
  2548.           ),
    
  2549.         );
    
  2550. 
    
  2551.         expect(state).toMatchInlineSnapshot(`
    
  2552.           ✕ 0, ⚠ 1
    
  2553.           [root]
    
  2554.              ▾ <Suspense>
    
  2555.                  <Child> ⚠
    
  2556.                  <Child>
    
  2557.         `);
    
  2558.       });
    
  2559. 
    
  2560.       it('should properly show errors/warnings from components in the Suspense fallback tree', async () => {
    
  2561.         async function fakeImport(result) {
    
  2562.           return {default: result};
    
  2563.         }
    
  2564.         const LazyComponent = React.lazy(() => fakeImport(Child));
    
  2565. 
    
  2566.         const Fallback = () => <Child logError={true} />;
    
  2567. 
    
  2568.         const container = document.createElement('div');
    
  2569. 
    
  2570.         withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  2571.           utils.act(() =>
    
  2572.             legacyRender(
    
  2573.               <React.Suspense fallback={<Fallback />}>
    
  2574.                 <LazyComponent />
    
  2575.               </React.Suspense>,
    
  2576.               container,
    
  2577.             ),
    
  2578.           ),
    
  2579.         );
    
  2580.         utils.act(() => TestRenderer.create(<Contexts />));
    
  2581. 
    
  2582.         expect(state).toMatchInlineSnapshot(`
    
  2583.           ✕ 1, ⚠ 0
    
  2584.           [root]
    
  2585.              ▾ <Suspense>
    
  2586.                ▾ <Fallback>
    
  2587.                    <Child> ✕
    
  2588.         `);
    
  2589. 
    
  2590.         await Promise.resolve();
    
  2591.         withErrorsOrWarningsIgnored(['test-only:'], () =>
    
  2592.           utils.act(() =>
    
  2593.             legacyRender(
    
  2594.               <React.Suspense fallback={<Fallback />}>
    
  2595.                 <LazyComponent />
    
  2596.               </React.Suspense>,
    
  2597.               container,
    
  2598.             ),
    
  2599.           ),
    
  2600.         );
    
  2601. 
    
  2602.         expect(state).toMatchInlineSnapshot(`
    
  2603.                   [root]
    
  2604.                      ▾ <Suspense>
    
  2605.                          <Child>
    
  2606.               `);
    
  2607.       });
    
  2608.     });
    
  2609. 
    
  2610.     describe('error boundaries', () => {
    
  2611.       it('should properly handle errors/warnings from components that dont mount because of an error', () => {
    
  2612.         class ErrorBoundary extends React.Component {
    
  2613.           state = {error: null};
    
  2614.           static getDerivedStateFromError(error) {
    
  2615.             return {error};
    
  2616.           }
    
  2617.           render() {
    
  2618.             if (this.state.error) {
    
  2619.               return null;
    
  2620.             }
    
  2621.             return this.props.children;
    
  2622.           }
    
  2623.         }
    
  2624. 
    
  2625.         class BadRender extends React.Component {
    
  2626.           render() {
    
  2627.             console.error('test-only: I am about to throw!');
    
  2628.             throw new Error('test-only: Oops!');
    
  2629.           }
    
  2630.         }
    
  2631. 
    
  2632.         const container = document.createElement('div');
    
  2633.         withErrorsOrWarningsIgnored(
    
  2634.           ['test-only:', 'React will try to recreate this component tree'],
    
  2635.           () => {
    
  2636.             utils.act(() =>
    
  2637.               legacyRender(
    
  2638.                 <ErrorBoundary>
    
  2639.                   <BadRender />
    
  2640.                 </ErrorBoundary>,
    
  2641.                 container,
    
  2642.               ),
    
  2643.             );
    
  2644.           },
    
  2645.         );
    
  2646. 
    
  2647.         utils.act(() => TestRenderer.create(<Contexts />));
    
  2648. 
    
  2649.         expect(store).toMatchInlineSnapshot(`
    
  2650.           ✕ 1, ⚠ 0
    
  2651.           [root]
    
  2652.               <ErrorBoundary> ✕
    
  2653.         `);
    
  2654. 
    
  2655.         selectNextErrorOrWarning();
    
  2656.         expect(state).toMatchInlineSnapshot(`
    
  2657.           ✕ 1, ⚠ 0
    
  2658.           [root]
    
  2659.           →    <ErrorBoundary> ✕
    
  2660.         `);
    
  2661. 
    
  2662.         utils.act(() => ReactDOM.unmountComponentAtNode(container));
    
  2663.         expect(state).toMatchInlineSnapshot(``);
    
  2664. 
    
  2665.         // Should be a noop
    
  2666.         selectNextErrorOrWarning();
    
  2667.         expect(state).toMatchInlineSnapshot(``);
    
  2668.       });
    
  2669. 
    
  2670.       it('should properly handle errors/warnings from components that dont mount because of an error', () => {
    
  2671.         class ErrorBoundary extends React.Component {
    
  2672.           state = {error: null};
    
  2673.           static getDerivedStateFromError(error) {
    
  2674.             return {error};
    
  2675.           }
    
  2676.           render() {
    
  2677.             if (this.state.error) {
    
  2678.               return null;
    
  2679.             }
    
  2680.             return this.props.children;
    
  2681.           }
    
  2682.         }
    
  2683. 
    
  2684.         class LogsWarning extends React.Component {
    
  2685.           render() {
    
  2686.             console.warn('test-only: I am about to throw!');
    
  2687.             return <ThrowsError />;
    
  2688.           }
    
  2689.         }
    
  2690.         class ThrowsError extends React.Component {
    
  2691.           render() {
    
  2692.             throw new Error('test-only: Oops!');
    
  2693.           }
    
  2694.         }
    
  2695. 
    
  2696.         const container = document.createElement('div');
    
  2697.         withErrorsOrWarningsIgnored(
    
  2698.           ['test-only:', 'React will try to recreate this component tree'],
    
  2699.           () => {
    
  2700.             utils.act(() =>
    
  2701.               legacyRender(
    
  2702.                 <ErrorBoundary>
    
  2703.                   <LogsWarning />
    
  2704.                 </ErrorBoundary>,
    
  2705.                 container,
    
  2706.               ),
    
  2707.             );
    
  2708.           },
    
  2709.         );
    
  2710. 
    
  2711.         utils.act(() => TestRenderer.create(<Contexts />));
    
  2712. 
    
  2713.         expect(store).toMatchInlineSnapshot(`
    
  2714.           ✕ 1, ⚠ 0
    
  2715.           [root]
    
  2716.               <ErrorBoundary> ✕
    
  2717.         `);
    
  2718. 
    
  2719.         selectNextErrorOrWarning();
    
  2720.         expect(state).toMatchInlineSnapshot(`
    
  2721.           ✕ 1, ⚠ 0
    
  2722.           [root]
    
  2723.           →    <ErrorBoundary> ✕
    
  2724.         `);
    
  2725. 
    
  2726.         utils.act(() => ReactDOM.unmountComponentAtNode(container));
    
  2727.         expect(state).toMatchInlineSnapshot(``);
    
  2728. 
    
  2729.         // Should be a noop
    
  2730.         selectNextErrorOrWarning();
    
  2731.         expect(state).toMatchInlineSnapshot(``);
    
  2732.       });
    
  2733. 
    
  2734.       it('should properly handle errors/warnings from inside of an error boundary', () => {
    
  2735.         class ErrorBoundary extends React.Component {
    
  2736.           state = {error: null};
    
  2737.           static getDerivedStateFromError(error) {
    
  2738.             return {error};
    
  2739.           }
    
  2740.           render() {
    
  2741.             if (this.state.error) {
    
  2742.               return <Child logError={true} />;
    
  2743.             }
    
  2744.             return this.props.children;
    
  2745.           }
    
  2746.         }
    
  2747. 
    
  2748.         class BadRender extends React.Component {
    
  2749.           render() {
    
  2750.             console.error('test-only: I am about to throw!');
    
  2751.             throw new Error('test-only: Oops!');
    
  2752.           }
    
  2753.         }
    
  2754. 
    
  2755.         const container = document.createElement('div');
    
  2756.         withErrorsOrWarningsIgnored(
    
  2757.           ['test-only:', 'React will try to recreate this component tree'],
    
  2758.           () => {
    
  2759.             utils.act(() =>
    
  2760.               legacyRender(
    
  2761.                 <ErrorBoundary>
    
  2762.                   <BadRender />
    
  2763.                 </ErrorBoundary>,
    
  2764.                 container,
    
  2765.               ),
    
  2766.             );
    
  2767.           },
    
  2768.         );
    
  2769. 
    
  2770.         utils.act(() => TestRenderer.create(<Contexts />));
    
  2771. 
    
  2772.         expect(store).toMatchInlineSnapshot(`
    
  2773.           ✕ 2, ⚠ 0
    
  2774.           [root]
    
  2775.             ▾ <ErrorBoundary> ✕
    
  2776.                 <Child> ✕
    
  2777.         `);
    
  2778. 
    
  2779.         selectNextErrorOrWarning();
    
  2780.         expect(state).toMatchInlineSnapshot(`
    
  2781.           ✕ 2, ⚠ 0
    
  2782.           [root]
    
  2783.           →  ▾ <ErrorBoundary> ✕
    
  2784.                  <Child> ✕
    
  2785.         `);
    
  2786.       });
    
  2787.     });
    
  2788.   });
    
  2789. });