Skip to content

Commit ad720f3

Browse files
authored
chore: use versioned render in profilerContext test (facebook#28243)
1 parent 5446b09 commit ad720f3

File tree

1 file changed

+230
-15
lines changed

1 file changed

+230
-15
lines changed

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

+230-15
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,9 @@ describe('ProfilerContext', () => {
7783
</BridgeContext.Provider>
7884
);
7985

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 () => {
8189
const Component = () => null;
8290

8391
let context: Context = ((null: any): Context);
@@ -110,10 +118,47 @@ describe('ProfilerContext', () => {
110118
expect(context.supportsProfiling).toBe(false);
111119
});
112120

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+
113158
it('should gracefully handle an empty profiling session (with no recorded commits)', async () => {
114159
const Example = () => null;
115160

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

118163
let context: Context = ((null: any): Context);
119164

@@ -145,7 +190,9 @@ describe('ProfilerContext', () => {
145190
expect(context.profilingData).toBe(null);
146191
});
147192

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 () => {
149196
const Parent = () => <Child />;
150197
const Child = () => null;
151198

@@ -191,7 +238,60 @@ describe('ProfilerContext', () => {
191238
);
192239
});
193240

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 () => {
195295
const Parent = () => <Child />;
196296
const Child = () => null;
197297

@@ -237,7 +337,60 @@ describe('ProfilerContext', () => {
237337
);
238338
});
239339

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 () => {
241394
const Parent = () => <Child />;
242395
const Child = () => null;
243396

@@ -300,17 +453,83 @@ describe('ProfilerContext', () => {
300453
expect(context.rootID).toBe(store.getRootIDForElement(id));
301454
});
302455

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+
303525
it('should sync selected element in the Components tab too, provided the element is a match', async () => {
304526
const GrandParent = ({includeChild}) => (
305527
<Parent includeChild={includeChild} />
306528
);
307529
const Parent = ({includeChild}) => (includeChild ? <Child /> : null);
308530
const Child = () => null;
309531

310-
const container = document.createElement('div');
311-
utils.act(() =>
312-
legacyRender(<GrandParent includeChild={true} />, container),
313-
);
532+
utils.act(() => render(<GrandParent includeChild={true} />));
314533
expect(store).toMatchInlineSnapshot(`
315534
[root]
316535
▾ <GrandParent>
@@ -323,12 +542,8 @@ describe('ProfilerContext', () => {
323542

324543
// Profile and record updates.
325544
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} />));
332547
await utils.actAsync(() => store.profilerStore.stopProfiling());
333548

334549
expect(store).toMatchInlineSnapshot(`

0 commit comments

Comments
 (0)