@samitouri / QOS-React-1 / commits / ad720f36ec

chore: use versioned render in profilerContext test (#28243)

Ruslan Lesiutin committed Feb 6, 2024 at 15:58 UTC ad720f36ec3328c70aa30277f12985e30bc0e880
1 file changed +230 -15
packages/react-devtools-shared/src/__tests__/profilerContext-test.js
+230 -15
@@ -13,9 +13,12 @@ import type {Context} from 'react-devtools-shared/src/devtools/views/Profiler/Pr
13 import type {DispatcherContext} from 'react-devtools-shared/src/devtools/views/Components/TreeContext';
14 import type Store from 'react-devtools-shared/src/devtools/store';
15
16 +import {getVersionedRenderImplementation} from './utils';
17 +
18 describe('ProfilerContext', () => {
19 let React;
20 let ReactDOM;
21 + let ReactDOMClient;
22 let TestRenderer: ReactTestRenderer;
23 let bridge: FrontendBridge;
24 let legacyRender;
@@ -43,6 +46,7 @@ describe('ProfilerContext', () => {
46
47 React = require('react');
48 ReactDOM = require('react-dom');
49 + ReactDOMClient = require('react-dom/client');
50 TestRenderer = utils.requireTestRenderer();
51
52 BridgeContext =
@@ -61,6 +65,8 @@ describe('ProfilerContext', () => {
65 require('react-devtools-shared/src/devtools/views/Components/TreeContext').TreeStateContext;
66 });
67
68 + const {render} = getVersionedRenderImplementation();
69 +
70 const Contexts = ({
71 children = null,
72 defaultSelectedElementID = null,
@@ -77,7 +83,9 @@ describe('ProfilerContext', () => {
83 </BridgeContext.Provider>
84 );
85
80 - it('updates updates profiling support based on the attached roots', async () => {
86 + // @reactVersion <= 18.2
87 + // @reactVersion >= 18.0
88 + it('updates updates profiling support based on the attached roots (legacy render)', async () => {
89 const Component = () => null;
90
91 let context: Context = ((null: any): Context);
@@ -110,10 +118,47 @@ describe('ProfilerContext', () => {
118 expect(context.supportsProfiling).toBe(false);
119 });
120
121 + // @reactVersion >= 18
122 + it('updates updates profiling support based on the attached roots (createRoot)', async () => {
123 + const Component = () => null;
124 +
125 + let context: Context = ((null: any): Context);
126 +
127 + function ContextReader() {
128 + context = React.useContext(ProfilerContext);
129 + return null;
130 + }
131 + await utils.actAsync(() => {
132 + TestRenderer.create(
133 + <Contexts>
134 + <ContextReader />
135 + </Contexts>,
136 + );
137 + });
138 +
139 + expect(context.supportsProfiling).toBe(false);
140 +
141 + const containerA = document.createElement('div');
142 + const containerB = document.createElement('div');
143 +
144 + const rootA = ReactDOMClient.createRoot(containerA);
145 + const rootB = ReactDOMClient.createRoot(containerB);
146 +
147 + await utils.actAsync(() => rootA.render(<Component />));
148 + expect(context.supportsProfiling).toBe(true);
149 +
150 + await utils.actAsync(() => rootB.render(<Component />));
151 + await utils.actAsync(() => rootA.unmount());
152 + expect(context.supportsProfiling).toBe(true);
153 +
154 + await utils.actAsync(() => rootB.unmount());
155 + expect(context.supportsProfiling).toBe(false);
156 + });
157 +
158 it('should gracefully handle an empty profiling session (with no recorded commits)', async () => {
159 const Example = () => null;
160
116 - utils.act(() => legacyRender(<Example />, document.createElement('div')));
161 + utils.act(() => render(<Example />));
162
163 let context: Context = ((null: any): Context);
164
@@ -145,7 +190,9 @@ describe('ProfilerContext', () => {
190 expect(context.profilingData).toBe(null);
191 });
192
148 - it('should auto-select the root ID matching the Components tab selection if it has profiling data', async () => {
193 + // @reactVersion <= 18.2
194 + // @reactVersion >= 18.0
195 + it('should auto-select the root ID matching the Components tab selection if it has profiling data (legacy render)', async () => {
196 const Parent = () => <Child />;
197 const Child = () => null;
198
@@ -191,7 +238,60 @@ describe('ProfilerContext', () => {
238 );
239 });
240
194 - it('should not select the root ID matching the Components tab selection if it has no profiling data', async () => {
241 + // @reactVersion >= 18
242 + it('should auto-select the root ID matching the Components tab selection if it has profiling data (createRoot)', async () => {
243 + const Parent = () => <Child />;
244 + const Child = () => null;
245 +
246 + const containerOne = document.createElement('div');
247 + const containerTwo = document.createElement('div');
248 +
249 + const rootOne = ReactDOMClient.createRoot(containerOne);
250 + const rootTwo = ReactDOMClient.createRoot(containerTwo);
251 +
252 + utils.act(() => rootOne.render(<Parent />));
253 + utils.act(() => rootTwo.render(<Parent />));
254 + expect(store).toMatchInlineSnapshot(`
255 + [root]
256 + ▾ <Parent>
257 + <Child>
258 + [root]
259 + ▾ <Parent>
260 + <Child>
261 + `);
262 +
263 + // Profile and record updates to both roots.
264 + await utils.actAsync(() => store.profilerStore.startProfiling());
265 + await utils.actAsync(() => rootOne.render(<Parent />));
266 + await utils.actAsync(() => rootTwo.render(<Parent />));
267 + await utils.actAsync(() => store.profilerStore.stopProfiling());
268 +
269 + let context: Context = ((null: any): Context);
270 + function ContextReader() {
271 + context = React.useContext(ProfilerContext);
272 + return null;
273 + }
274 +
275 + // Select an element within the second root.
276 + await utils.actAsync(() =>
277 + TestRenderer.create(
278 + <Contexts
279 + defaultSelectedElementID={store.getElementIDAtIndex(3)}
280 + defaultSelectedElementIndex={3}>
281 + <ContextReader />
282 + </Contexts>,
283 + ),
284 + );
285 +
286 + expect(context).not.toBeNull();
287 + expect(context.rootID).toBe(
288 + store.getRootIDForElement(((store.getElementIDAtIndex(3): any): number)),
289 + );
290 + });
291 +
292 + // @reactVersion <= 18.2
293 + // @reactVersion >= 18.0
294 + it('should not select the root ID matching the Components tab selection if it has no profiling data (legacy render)', async () => {
295 const Parent = () => <Child />;
296 const Child = () => null;
297
@@ -237,7 +337,60 @@ describe('ProfilerContext', () => {
337 );
338 });
339
240 - it('should maintain root selection between profiling sessions so long as there is data for that root', async () => {
340 + // @reactVersion >= 18
341 + it('should not select the root ID matching the Components tab selection if it has no profiling data (createRoot)', async () => {
342 + const Parent = () => <Child />;
343 + const Child = () => null;
344 +
345 + const containerOne = document.createElement('div');
346 + const containerTwo = document.createElement('div');
347 +
348 + const rootOne = ReactDOMClient.createRoot(containerOne);
349 + const rootTwo = ReactDOMClient.createRoot(containerTwo);
350 +
351 + utils.act(() => rootOne.render(<Parent />));
352 + utils.act(() => rootTwo.render(<Parent />));
353 + expect(store).toMatchInlineSnapshot(`
354 + [root]
355 + ▾ <Parent>
356 + <Child>
357 + [root]
358 + ▾ <Parent>
359 + <Child>
360 + `);
361 +
362 + // Profile and record updates to only the first root.
363 + await utils.actAsync(() => store.profilerStore.startProfiling());
364 + await utils.actAsync(() => rootOne.render(<Parent />));
365 + await utils.actAsync(() => store.profilerStore.stopProfiling());
366 +
367 + let context: Context = ((null: any): Context);
368 + function ContextReader() {
369 + context = React.useContext(ProfilerContext);
370 + return null;
371 + }
372 +
373 + // Select an element within the second root.
374 + await utils.actAsync(() =>
375 + TestRenderer.create(
376 + <Contexts
377 + defaultSelectedElementID={store.getElementIDAtIndex(3)}
378 + defaultSelectedElementIndex={3}>
379 + <ContextReader />
380 + </Contexts>,
381 + ),
382 + );
383 +
384 + // Verify the default profiling root is the first one.
385 + expect(context).not.toBeNull();
386 + expect(context.rootID).toBe(
387 + store.getRootIDForElement(((store.getElementIDAtIndex(0): any): number)),
388 + );
389 + });
390 +
391 + // @reactVersion <= 18.2
392 + // @reactVersion >= 18.0
393 + it('should maintain root selection between profiling sessions so long as there is data for that root (legacy render)', async () => {
394 const Parent = () => <Child />;
395 const Child = () => null;
396
@@ -300,6 +453,75 @@ describe('ProfilerContext', () => {
453 expect(context.rootID).toBe(store.getRootIDForElement(id));
454 });
455
456 + // @reactVersion >= 18.0
457 + it('should maintain root selection between profiling sessions so long as there is data for that root (createRoot)', async () => {
458 + const Parent = () => <Child />;
459 + const Child = () => null;
460 +
461 + const containerA = document.createElement('div');
462 + const containerB = document.createElement('div');
463 +
464 + const rootA = ReactDOMClient.createRoot(containerA);
465 + const rootB = ReactDOMClient.createRoot(containerB);
466 +
467 + utils.act(() => rootA.render(<Parent />));
468 + utils.act(() => rootB.render(<Parent />));
469 +
470 + expect(store).toMatchInlineSnapshot(`
471 + [root]
472 + ▾ <Parent>
473 + <Child>
474 + [root]
475 + ▾ <Parent>
476 + <Child>
477 + `);
478 +
479 + // Profile and record updates.
480 + await utils.actAsync(() => store.profilerStore.startProfiling());
481 + await utils.actAsync(() => rootA.render(<Parent />));
482 + await utils.actAsync(() => rootB.render(<Parent />));
483 + await utils.actAsync(() => store.profilerStore.stopProfiling());
484 +
485 + let context: Context = ((null: any): Context);
486 + let dispatch: DispatcherContext = ((null: any): DispatcherContext);
487 + let selectedElementID = null;
488 + function ContextReader() {
489 + context = React.useContext(ProfilerContext);
490 + dispatch = React.useContext(TreeDispatcherContext);
491 + selectedElementID = React.useContext(TreeStateContext).selectedElementID;
492 + return null;
493 + }
494 +
495 + const id = ((store.getElementIDAtIndex(3): any): number);
496 +
497 + // Select an element within the second root.
498 + await utils.actAsync(() =>
499 + TestRenderer.create(
500 + <Contexts defaultSelectedElementID={id} defaultSelectedElementIndex={3}>
501 + <ContextReader />
502 + </Contexts>,
503 + ),
504 + );
505 +
506 + expect(selectedElementID).toBe(id);
507 +
508 + // Profile and record more updates to both roots
509 + await utils.actAsync(() => store.profilerStore.startProfiling());
510 + await utils.actAsync(() => rootA.render(<Parent />));
511 + await utils.actAsync(() => rootB.render(<Parent />));
512 + await utils.actAsync(() => store.profilerStore.stopProfiling());
513 +
514 + const otherID = ((store.getElementIDAtIndex(0): any): number);
515 +
516 + // Change the selected element within a the Components tab.
517 + utils.act(() => dispatch({type: 'SELECT_ELEMENT_AT_INDEX', payload: 0}));
518 +
519 + // Verify that the initial Profiler root selection is maintained.
520 + expect(selectedElementID).toBe(otherID);
521 + expect(context).not.toBeNull();
522 + expect(context.rootID).toBe(store.getRootIDForElement(id));
523 + });
524 +
525 it('should sync selected element in the Components tab too, provided the element is a match', async () => {
526 const GrandParent = ({includeChild}) => (
527 <Parent includeChild={includeChild} />
@@ -307,10 +529,7 @@ describe('ProfilerContext', () => {
529 const Parent = ({includeChild}) => (includeChild ? <Child /> : null);
530 const Child = () => null;
531
310 - const container = document.createElement('div');
311 - utils.act(() =>
312 - legacyRender(<GrandParent includeChild={true} />, container),
313 - );
532 + utils.act(() => render(<GrandParent includeChild={true} />));
533 expect(store).toMatchInlineSnapshot(`
534 [root]
535 ▾ <GrandParent>
@@ -323,12 +542,8 @@ describe('ProfilerContext', () => {
542
543 // Profile and record updates.
544 await utils.actAsync(() => store.profilerStore.startProfiling());
326 - await utils.actAsync(() =>
327 - legacyRender(<GrandParent includeChild={true} />, container),
328 - );
329 - await utils.actAsync(() =>
330 - legacyRender(<GrandParent includeChild={false} />, container),
331 - );
545 + await utils.actAsync(() => render(<GrandParent includeChild={true} />));
546 + await utils.actAsync(() => render(<GrandParent includeChild={false} />));
547 await utils.actAsync(() => store.profilerStore.stopProfiling());
548
549 expect(store).toMatchInlineSnapshot(`