/*** Copyright (c) Meta Platforms, Inc. and affiliates.** This source code is licensed under the MIT license found in the* LICENSE file in the root directory of this source tree.** @flow*/import type Store from 'react-devtools-shared/src/devtools/store';
describe('profiling charts', () => {
let React;
let Scheduler;
let legacyRender;
let store: Store;
let utils;
beforeEach(() => {
utils = require('./utils');
utils.beforeEachProfiling();
legacyRender = utils.legacyRender;
store = global.store;
store.collapseNodesByDefault = false;
store.recordChangeDescriptions = true;
React = require('react');
Scheduler = require('scheduler');
});function getFlamegraphChartData(rootID, commitIndex) {
const commitTree = store.profilerStore.profilingCache.getCommitTree({
commitIndex,
rootID,
});const chartData = store.profilerStore.profilingCache.getFlamegraphChartData(
{commitIndex,
commitTree,
rootID,
},);return {commitTree, chartData};
}function getRankedChartData(rootID, commitIndex) {
const commitTree = store.profilerStore.profilingCache.getCommitTree({
commitIndex,
rootID,
});const chartData = store.profilerStore.profilingCache.getRankedChartData({
commitIndex,
commitTree,
rootID,
});return {commitTree, chartData};
}describe('flamegraph chart', () => {
// @reactVersion >= 16.9
it('should contain valid data', () => {
const Parent = (_: {}) => {
Scheduler.unstable_advanceTime(10);
return (
<React.Fragment>
<Child key="first" duration={3} />
<Child key="second" duration={2} />
<Child key="third" duration={0} />
</React.Fragment>
);};// Memoize children to verify that chart doesn't include in the update.
const Child = React.memo(function Child({duration}) {
Scheduler.unstable_advanceTime(duration);
return null;
});const container = document.createElement('div');
utils.act(() => store.profilerStore.startProfiling());
utils.act(() => legacyRender(<Parent />, container));
expect(store).toMatchInlineSnapshot(`
[root]▾ <Parent><Child key="first"> [Memo]<Child key="second"> [Memo]<Child key="third"> [Memo]`);
utils.act(() => legacyRender(<Parent />, container));
expect(store).toMatchInlineSnapshot(`
[root]▾ <Parent><Child key="first"> [Memo]<Child key="second"> [Memo]<Child key="third"> [Memo]`);
utils.act(() => store.profilerStore.stopProfiling());
const rootID = store.roots[0];
const firstCommitData = getFlamegraphChartData(rootID, 0);
expect(firstCommitData.commitTree.nodes.size).toBe(5);
expect(firstCommitData.chartData.rows).toMatchInlineSnapshot(`
[[{"actualDuration": 15,"didRender": true,"id": 2,"label": "Parent (10ms of 15ms)","name": "Parent","offset": 0,"selfDuration": 10,"treeBaseDuration": 15,},],[{"actualDuration": 0,"didRender": true,"id": 5,"label": "Memo(Child) key="third" (<0.1ms of <0.1ms)","name": "Memo(Child)","offset": 15,"selfDuration": 0,"treeBaseDuration": 0,},{"actualDuration": 2,"didRender": true,"id": 4,"label": "Memo(Child) key="second" (2ms of 2ms)","name": "Memo(Child)","offset": 13,"selfDuration": 2,"treeBaseDuration": 2,},{"actualDuration": 3,"didRender": true,"id": 3,"label": "Memo(Child) key="first" (3ms of 3ms)","name": "Memo(Child)","offset": 10,"selfDuration": 3,"treeBaseDuration": 3,},],]`);
const secondCommitData = getFlamegraphChartData(rootID, 1);
expect(secondCommitData.commitTree.nodes.size).toBe(5);
expect(secondCommitData.chartData.rows).toMatchInlineSnapshot(`
[[{"actualDuration": 10,"didRender": true,"id": 2,"label": "Parent (10ms of 10ms)","name": "Parent","offset": 0,"selfDuration": 10,"treeBaseDuration": 15,},],[{"actualDuration": 0,"didRender": false,"id": 5,"label": "Memo(Child) key="third"","name": "Memo(Child)","offset": 15,"selfDuration": 0,"treeBaseDuration": 0,},{"actualDuration": 0,"didRender": false,"id": 4,"label": "Memo(Child) key="second"","name": "Memo(Child)","offset": 13,"selfDuration": 0,"treeBaseDuration": 2,},{"actualDuration": 0,"didRender": false,"id": 3,"label": "Memo(Child) key="first"","name": "Memo(Child)","offset": 10,"selfDuration": 0,"treeBaseDuration": 3,},],]`);
});});
describe('ranked chart', () => {
// @reactVersion >= 16.9
it('should contain valid data', () => {
const Parent = (_: {}) => {
Scheduler.unstable_advanceTime(10);
return (
<React.Fragment>
<Child key="first" duration={3} />
<Child key="second" duration={2} />
<Child key="third" duration={0} />
</React.Fragment>
);};// Memoize children to verify that chart doesn't include in the update.
const Child = React.memo(function Child({duration}) {
Scheduler.unstable_advanceTime(duration);
return null;
});const container = document.createElement('div');
utils.act(() => store.profilerStore.startProfiling());
utils.act(() => legacyRender(<Parent />, container));
expect(store).toMatchInlineSnapshot(`
[root]▾ <Parent><Child key="first"> [Memo]<Child key="second"> [Memo]<Child key="third"> [Memo]`);
utils.act(() => legacyRender(<Parent />, container));
expect(store).toMatchInlineSnapshot(`
[root]▾ <Parent><Child key="first"> [Memo]<Child key="second"> [Memo]<Child key="third"> [Memo]`);
utils.act(() => store.profilerStore.stopProfiling());
const rootID = store.roots[0];
// Everything should render the first time.
const firstCommitData = getRankedChartData(rootID, 0);
expect(firstCommitData.commitTree.nodes.size).toBe(5);
expect(firstCommitData.chartData.nodes).toMatchInlineSnapshot(`
[{"id": 2,"label": "Parent (10ms)","name": "Parent","value": 10,},{"id": 3,"label": "Memo(Child) (Memo) key="first" (3ms)","name": "Memo(Child)","value": 3,},{"id": 4,"label": "Memo(Child) (Memo) key="second" (2ms)","name": "Memo(Child)","value": 2,},{"id": 5,"label": "Memo(Child) (Memo) key="third" (<0.1ms)","name": "Memo(Child)","value": 0,},]`);
// Only parent should render the second time, since child props have not changed.
const secondCommitData = getRankedChartData(rootID, 1);
expect(secondCommitData.commitTree.nodes.size).toBe(5);
expect(secondCommitData.chartData.nodes).toMatchInlineSnapshot(`
[{"id": 2,"label": "Parent (10ms)","name": "Parent","value": 10,},]`);
});});
});