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 {Element} from 'react-devtools-shared/src/frontend/types';
    
  12. import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
    
  13. import type Store from 'react-devtools-shared/src/devtools/store';
    
  14. 
    
  15. describe('OwnersListContext', () => {
    
  16.   let React;
    
  17.   let TestRenderer: ReactTestRenderer;
    
  18.   let bridge: FrontendBridge;
    
  19.   let legacyRender;
    
  20.   let store: Store;
    
  21.   let utils;
    
  22. 
    
  23.   let BridgeContext;
    
  24.   let OwnersListContext;
    
  25.   let OwnersListContextController;
    
  26.   let StoreContext;
    
  27.   let TreeContextController;
    
  28. 
    
  29.   beforeEach(() => {
    
  30.     utils = require('./utils');
    
  31.     utils.beforeEachProfiling();
    
  32. 
    
  33.     legacyRender = utils.legacyRender;
    
  34. 
    
  35.     bridge = global.bridge;
    
  36.     store = global.store;
    
  37.     store.collapseNodesByDefault = false;
    
  38. 
    
  39.     React = require('react');
    
  40.     TestRenderer = utils.requireTestRenderer();
    
  41. 
    
  42.     BridgeContext =
    
  43.       require('react-devtools-shared/src/devtools/views/context').BridgeContext;
    
  44.     OwnersListContext =
    
  45.       require('react-devtools-shared/src/devtools/views/Components/OwnersListContext').OwnersListContext;
    
  46.     OwnersListContextController =
    
  47.       require('react-devtools-shared/src/devtools/views/Components/OwnersListContext').OwnersListContextController;
    
  48.     StoreContext =
    
  49.       require('react-devtools-shared/src/devtools/views/context').StoreContext;
    
  50.     TreeContextController =
    
  51.       require('react-devtools-shared/src/devtools/views/Components/TreeContext').TreeContextController;
    
  52.   });
    
  53. 
    
  54.   const Contexts = ({children, defaultOwnerID = null}) => (
    
  55.     <BridgeContext.Provider value={bridge}>
    
  56.       <StoreContext.Provider value={store}>
    
  57.         <TreeContextController defaultOwnerID={defaultOwnerID}>
    
  58.           <OwnersListContextController>{children}</OwnersListContextController>
    
  59.         </TreeContextController>
    
  60.       </StoreContext.Provider>
    
  61.     </BridgeContext.Provider>
    
  62.   );
    
  63. 
    
  64.   async function getOwnersListForOwner(owner) {
    
  65.     let ownerDisplayNames = null;
    
  66. 
    
  67.     function Suspender() {
    
  68.       const read = React.useContext(OwnersListContext);
    
  69.       const owners = read(owner.id);
    
  70.       ownerDisplayNames = owners.map(({displayName}) => displayName);
    
  71.       return null;
    
  72.     }
    
  73. 
    
  74.     await utils.actAsync(() =>
    
  75.       TestRenderer.create(
    
  76.         <Contexts defaultOwnerID={owner.id}>
    
  77.           <React.Suspense fallback={null}>
    
  78.             <Suspender owner={owner} />
    
  79.           </React.Suspense>
    
  80.         </Contexts>,
    
  81.       ),
    
  82.     );
    
  83. 
    
  84.     expect(ownerDisplayNames).not.toBeNull();
    
  85. 
    
  86.     return ownerDisplayNames;
    
  87.   }
    
  88. 
    
  89.   it('should fetch the owners list for the selected element', async () => {
    
  90.     const Grandparent = () => <Parent />;
    
  91.     const Parent = () => {
    
  92.       return (
    
  93.         <React.Fragment>
    
  94.           <Child />
    
  95.           <Child />
    
  96.         </React.Fragment>
    
  97.       );
    
  98.     };
    
  99.     const Child = () => null;
    
  100. 
    
  101.     utils.act(() =>
    
  102.       legacyRender(<Grandparent />, document.createElement('div')),
    
  103.     );
    
  104. 
    
  105.     expect(store).toMatchInlineSnapshot(`
    
  106.       [root]
    
  107.         ▾ <Grandparent>
    
  108.           ▾ <Parent>
    
  109.               <Child>
    
  110.               <Child>
    
  111.     `);
    
  112. 
    
  113.     const parent = ((store.getElementAtIndex(1): any): Element);
    
  114.     const firstChild = ((store.getElementAtIndex(2): any): Element);
    
  115. 
    
  116.     expect(await getOwnersListForOwner(parent)).toMatchInlineSnapshot(`
    
  117.       [
    
  118.         "Grandparent",
    
  119.         "Parent",
    
  120.       ]
    
  121.     `);
    
  122. 
    
  123.     expect(await getOwnersListForOwner(firstChild)).toMatchInlineSnapshot(`
    
  124.       [
    
  125.         "Grandparent",
    
  126.         "Parent",
    
  127.         "Child",
    
  128.       ]
    
  129.     `);
    
  130.   });
    
  131. 
    
  132.   it('should fetch the owners list for the selected element that includes filtered components', async () => {
    
  133.     store.componentFilters = [utils.createDisplayNameFilter('^Parent$')];
    
  134. 
    
  135.     const Grandparent = () => <Parent />;
    
  136.     const Parent = () => {
    
  137.       return (
    
  138.         <React.Fragment>
    
  139.           <Child />
    
  140.           <Child />
    
  141.         </React.Fragment>
    
  142.       );
    
  143.     };
    
  144.     const Child = () => null;
    
  145. 
    
  146.     utils.act(() =>
    
  147.       legacyRender(<Grandparent />, document.createElement('div')),
    
  148.     );
    
  149. 
    
  150.     expect(store).toMatchInlineSnapshot(`
    
  151.       [root]
    
  152.         ▾ <Grandparent>
    
  153.             <Child>
    
  154.             <Child>
    
  155.     `);
    
  156. 
    
  157.     const firstChild = ((store.getElementAtIndex(1): any): Element);
    
  158. 
    
  159.     expect(await getOwnersListForOwner(firstChild)).toMatchInlineSnapshot(`
    
  160.       [
    
  161.         "Grandparent",
    
  162.         "Parent",
    
  163.         "Child",
    
  164.       ]
    
  165.     `);
    
  166.   });
    
  167. 
    
  168.   it('should include the current element even if there are no other owners', async () => {
    
  169.     store.componentFilters = [utils.createDisplayNameFilter('^Parent$')];
    
  170. 
    
  171.     const Grandparent = () => <Parent />;
    
  172.     const Parent = () => null;
    
  173. 
    
  174.     utils.act(() =>
    
  175.       legacyRender(<Grandparent />, document.createElement('div')),
    
  176.     );
    
  177. 
    
  178.     expect(store).toMatchInlineSnapshot(`
    
  179.       [root]
    
  180.           <Grandparent>
    
  181.     `);
    
  182. 
    
  183.     const grandparent = ((store.getElementAtIndex(0): any): Element);
    
  184. 
    
  185.     expect(await getOwnersListForOwner(grandparent)).toMatchInlineSnapshot(`
    
  186.       [
    
  187.         "Grandparent",
    
  188.       ]
    
  189.     `);
    
  190.   });
    
  191. 
    
  192.   it('should include all owners for a component wrapped in react memo', async () => {
    
  193.     const InnerComponent = (props, ref) => <div ref={ref} />;
    
  194.     const ForwardRef = React.forwardRef(InnerComponent);
    
  195.     const Memo = React.memo(ForwardRef);
    
  196.     const Grandparent = () => {
    
  197.       const ref = React.createRef();
    
  198.       return <Memo ref={ref} />;
    
  199.     };
    
  200. 
    
  201.     utils.act(() =>
    
  202.       legacyRender(<Grandparent />, document.createElement('div')),
    
  203.     );
    
  204. 
    
  205.     expect(store).toMatchInlineSnapshot(`
    
  206.       [root]
    
  207.         ▾ <Grandparent>
    
  208.           ▾ <InnerComponent> [Memo]
    
  209.               <InnerComponent> [ForwardRef]
    
  210.     `);
    
  211. 
    
  212.     const wrapped = ((store.getElementAtIndex(2): any): Element);
    
  213. 
    
  214.     expect(await getOwnersListForOwner(wrapped)).toMatchInlineSnapshot(`
    
  215.       [
    
  216.         "Grandparent",
    
  217.         "InnerComponent",
    
  218.         "InnerComponent",
    
  219.       ]
    
  220.     `);
    
  221.   });
    
  222. });