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(`