Skip to content

Commit 9b70551

Browse files
committed
chore: use versioned render in profilerContext test
1 parent e51cda5 commit 9b70551

File tree

1 file changed

+226
-15
lines changed

1 file changed

+226
-15
lines changed

packages/react-devtools-shared/src/__tests__/profilerContext-test.js

Lines changed: 226 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ import type {Context} from 'react-devtools-shared/src/devtools/views/Profiler/Pr
1313
import type {DispatcherContext} from 'react-devtools-shared/src/devtools/views/Components/TreeContext';
1414
import type Store from 'react-devtools-shared/src/devtools/store';
1515

16+
import {getVersionedRenderImplementation} from './utils';
17+
1618
describe('ProfilerContext', () => {
1719
let React;
1820
let ReactDOM;
21+
let ReactDOMClient;
1922
let TestRenderer: ReactTestRenderer;
2023
let bridge: FrontendBridge;
2124
let legacyRender;
@@ -43,6 +46,7 @@ describe('ProfilerContext', () => {
4346

4447
React = require('react');
4548
ReactDOM = require('react-dom');
49+
ReactDOMClient = require('react-dom/client');
4650
TestRenderer = utils.requireTestRenderer();
4751

4852
BridgeContext =
@@ -61,6 +65,8 @@ describe('ProfilerContext', () => {
6165
require('react-devtools-shared/src/devtools/views/Components/TreeContext').TreeStateContext;
6266
});
6367

68+
const {render} = getVersionedRenderImplementation();
69+
6470
const Contexts = ({
6571
children = null,
6672
defaultSelectedElementID = null,
@@ -77,7 +83,8 @@ describe('ProfilerContext', () => {
7783
</BridgeContext.Provider>
7884
);
7985

80-
it('updates updates profiling support based on the attached roots', async () => {
86+
// @reactVersion < 19
87+
it('updates updates profiling support based on the attached roots (Legacy Render)', async () => {
8188
const Component = () => null;
8289

8390
let context: Context = ((null: any): Context);
@@ -110,10 +117,47 @@ describe('ProfilerContext', () => {
110117
expect(context.supportsProfiling).toBe(false);
111118
});
112119

120+
// @reactVersion >= 18
121+
it('updates updates profiling support based on the attached roots (Concurrent Render)', async () => {
122+
const Component = () => null;
123+
124+
let context: Context = ((null: any): Context);
125+
126+
function ContextReader() {
127+
context = React.useContext(ProfilerContext);
128+
return null;
129+
}
130+
await utils.actAsync(() => {
131+
TestRenderer.create(
132+
<Contexts>
133+
<ContextReader />
134+
</Contexts>,
135+
);
136+
});
137+
138+
expect(context.supportsProfiling).toBe(false);
139+
140+
const containerA = document.createElement('div');
141+
const containerB = document.createElement('div');
142+
143+
const rootA = ReactDOMClient.createRoot(containerA);
144+
const rootB = ReactDOMClient.createRoot(containerB);
145+
146+
await utils.actAsync(() => rootA.render(<Component />));
147+
expect(context.supportsProfiling).toBe(true);
148+
149+
await utils.actAsync(() => rootB.render(<Component />));
150+
await utils.actAsync(() => rootA.unmount());
151+
expect(context.supportsProfiling).toBe(true);
152+
153+
await utils.actAsync(() => rootB.unmount());
154+
expect(context.supportsProfiling).toBe(false);
155+
});
156+
113157
it('should gracefully handle an empty profiling session (with no recorded commits)', async () => {
114158
const Example = () => null;
115159

116-
utils.act(() => legacyRender(<Example />, document.createElement('div')));
160+
utils.act(() => render(<Example />));
117161

118162
let context: Context = ((null: any): Context);
119163

@@ -145,7 +189,8 @@ describe('ProfilerContext', () => {
145189
expect(context.profilingData).toBe(null);
146190
});
147191

148-
it('should auto-select the root ID matching the Components tab selection if it has profiling data', async () => {
192+
// @reactVersion < 19
193+
it('should auto-select the root ID matching the Components tab selection if it has profiling data (Legacy Render)', async () => {
149194
const Parent = () => <Child />;
150195
const Child = () => null;
151196

@@ -191,7 +236,59 @@ describe('ProfilerContext', () => {
191236
);
192237
});
193238

194-
it('should not select the root ID matching the Components tab selection if it has no profiling data', async () => {
239+
// @reactVersion >= 18
240+
it('should auto-select the root ID matching the Components tab selection if it has profiling data (Concurrent Render)', async () => {
241+
const Parent = () => <Child />;
242+
const Child = () => null;
243+
244+
const containerOne = document.createElement('div');
245+
const containerTwo = document.createElement('div');
246+
247+
const rootOne = ReactDOMClient.createRoot(containerOne);
248+
const rootTwo = ReactDOMClient.createRoot(containerTwo);
249+
250+
utils.act(() => rootOne.render(<Parent />));
251+
utils.act(() => rootTwo.render(<Parent />));
252+
expect(store).toMatchInlineSnapshot(`
253+
[root]
254+
▾ <Parent>
255+
<Child>
256+
[root]
257+
▾ <Parent>
258+
<Child>
259+
`);
260+
261+
// Profile and record updates to both roots.
262+
await utils.actAsync(() => store.profilerStore.startProfiling());
263+
await utils.actAsync(() => rootOne.render(<Parent />));
264+
await utils.actAsync(() => rootTwo.render(<Parent />));
265+
await utils.actAsync(() => store.profilerStore.stopProfiling());
266+
267+
let context: Context = ((null: any): Context);
268+
function ContextReader() {
269+
context = React.useContext(ProfilerContext);
270+
return null;
271+
}
272+
273+
// Select an element within the second root.
274+
await utils.actAsync(() =>
275+
TestRenderer.create(
276+
<Contexts
277+
defaultSelectedElementID={store.getElementIDAtIndex(3)}
278+
defaultSelectedElementIndex={3}>
279+
<ContextReader />
280+
</Contexts>,
281+
),
282+
);
283+
284+
expect(context).not.toBeNull();
285+
expect(context.rootID).toBe(
286+
store.getRootIDForElement(((store.getElementIDAtIndex(3): any): number)),
287+
);
288+
});
289+
290+
// @reactVersion < 19
291+
it('should not select the root ID matching the Components tab selection if it has no profiling data (Legacy Render)', async () => {
195292
const Parent = () => <Child />;
196293
const Child = () => null;
197294

@@ -237,7 +334,59 @@ describe('ProfilerContext', () => {
237334
);
238335
});
239336

240-
it('should maintain root selection between profiling sessions so long as there is data for that root', async () => {
337+
// @reactVersion >= 18
338+
it('should not select the root ID matching the Components tab selection if it has no profiling data (Concurrent Render)', async () => {
339+
const Parent = () => <Child />;
340+
const Child = () => null;
341+
342+
const containerOne = document.createElement('div');
343+
const containerTwo = document.createElement('div');
344+
345+
const rootOne = ReactDOMClient.createRoot(containerOne);
346+
const rootTwo = ReactDOMClient.createRoot(containerTwo);
347+
348+
utils.act(() => rootOne.render(<Parent />));
349+
utils.act(() => rootTwo.render(<Parent />));
350+
expect(store).toMatchInlineSnapshot(`
351+
[root]
352+
▾ <Parent>
353+
<Child>
354+
[root]
355+
▾ <Parent>
356+
<Child>
357+
`);
358+
359+
// Profile and record updates to only the first root.
360+
await utils.actAsync(() => store.profilerStore.startProfiling());
361+
await utils.actAsync(() => rootOne.render(<Parent />));
362+
await utils.actAsync(() => store.profilerStore.stopProfiling());
363+
364+
let context: Context = ((null: any): Context);
365+
function ContextReader() {
366+
context = React.useContext(ProfilerContext);
367+
return null;
368+
}
369+
370+
// Select an element within the second root.
371+
await utils.actAsync(() =>
372+
TestRenderer.create(
373+
<Contexts
374+
defaultSelectedElementID={store.getElementIDAtIndex(3)}
375+
defaultSelectedElementIndex={3}>
376+
<ContextReader />
377+
</Contexts>,
378+
),
379+
);
380+
381+
// Verify the default profiling root is the first one.
382+
expect(context).not.toBeNull();
383+
expect(context.rootID).toBe(
384+
store.getRootIDForElement(((store.getElementIDAtIndex(0): any): number)),
385+
);
386+
});
387+
388+
// @reactVersion < 19
389+
it('should maintain root selection between profiling sessions so long as there is data for that root (Legacy Render)', async () => {
241390
const Parent = () => <Child />;
242391
const Child = () => null;
243392

@@ -300,17 +449,83 @@ describe('ProfilerContext', () => {
300449
expect(context.rootID).toBe(store.getRootIDForElement(id));
301450
});
302451

452+
// @reactVersion < 19
453+
it('should maintain root selection between profiling sessions so long as there is data for that root (Legacy Render)', async () => {
454+
const Parent = () => <Child />;
455+
const Child = () => null;
456+
457+
const containerA = document.createElement('div');
458+
const containerB = document.createElement('div');
459+
460+
const rootA = ReactDOMClient.createRoot(containerA);
461+
const rootB = ReactDOMClient.createRoot(containerB);
462+
463+
utils.act(() => rootA.render(<Parent />));
464+
utils.act(() => rootB.render(<Parent />));
465+
466+
expect(store).toMatchInlineSnapshot(`
467+
[root]
468+
▾ <Parent>
469+
<Child>
470+
[root]
471+
▾ <Parent>
472+
<Child>
473+
`);
474+
475+
// Profile and record updates.
476+
await utils.actAsync(() => store.profilerStore.startProfiling());
477+
await utils.actAsync(() => rootA.render(<Parent />));
478+
await utils.actAsync(() => rootB.render(<Parent />));
479+
await utils.actAsync(() => store.profilerStore.stopProfiling());
480+
481+
let context: Context = ((null: any): Context);
482+
let dispatch: DispatcherContext = ((null: any): DispatcherContext);
483+
let selectedElementID = null;
484+
function ContextReader() {
485+
context = React.useContext(ProfilerContext);
486+
dispatch = React.useContext(TreeDispatcherContext);
487+
selectedElementID = React.useContext(TreeStateContext).selectedElementID;
488+
return null;
489+
}
490+
491+
const id = ((store.getElementIDAtIndex(3): any): number);
492+
493+
// Select an element within the second root.
494+
await utils.actAsync(() =>
495+
TestRenderer.create(
496+
<Contexts defaultSelectedElementID={id} defaultSelectedElementIndex={3}>
497+
<ContextReader />
498+
</Contexts>,
499+
),
500+
);
501+
502+
expect(selectedElementID).toBe(id);
503+
504+
// Profile and record more updates to both roots
505+
await utils.actAsync(() => store.profilerStore.startProfiling());
506+
await utils.actAsync(() => rootA.render(<Parent />));
507+
await utils.actAsync(() => rootB.render(<Parent />));
508+
await utils.actAsync(() => store.profilerStore.stopProfiling());
509+
510+
const otherID = ((store.getElementIDAtIndex(0): any): number);
511+
512+
// Change the selected element within a the Components tab.
513+
utils.act(() => dispatch({type: 'SELECT_ELEMENT_AT_INDEX', payload: 0}));
514+
515+
// Verify that the initial Profiler root selection is maintained.
516+
expect(selectedElementID).toBe(otherID);
517+
expect(context).not.toBeNull();
518+
expect(context.rootID).toBe(store.getRootIDForElement(id));
519+
});
520+
303521
it('should sync selected element in the Components tab too, provided the element is a match', async () => {
304522
const GrandParent = ({includeChild}) => (
305523
<Parent includeChild={includeChild} />
306524
);
307525
const Parent = ({includeChild}) => (includeChild ? <Child /> : null);
308526
const Child = () => null;
309527

310-
const container = document.createElement('div');
311-
utils.act(() =>
312-
legacyRender(<GrandParent includeChild={true} />, container),
313-
);
528+
utils.act(() => render(<GrandParent includeChild={true} />));
314529
expect(store).toMatchInlineSnapshot(`
315530
[root]
316531
▾ <GrandParent>
@@ -323,12 +538,8 @@ describe('ProfilerContext', () => {
323538

324539
// Profile and record updates.
325540
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-
);
541+
await utils.actAsync(() => render(<GrandParent includeChild={true} />));
542+
await utils.actAsync(() => render(<GrandParent includeChild={false} />));
332543
await utils.actAsync(() => store.profilerStore.stopProfiling());
333544

334545
expect(store).toMatchInlineSnapshot(`

0 commit comments

Comments
 (0)