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