mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Added profiling chart data tests
This commit is contained in:
@@ -0,0 +1,333 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`profiling charts flamegraph chart should contain valid data: 0: CommitTree 1`] = `
|
||||
Object {
|
||||
"nodes": Map {
|
||||
1 => Object {
|
||||
"children": Array [
|
||||
2,
|
||||
],
|
||||
"displayName": null,
|
||||
"id": 1,
|
||||
"key": null,
|
||||
"parentID": 0,
|
||||
"treeBaseDuration": 14,
|
||||
},
|
||||
2 => Object {
|
||||
"children": Array [
|
||||
3,
|
||||
4,
|
||||
],
|
||||
"displayName": "Parent",
|
||||
"id": 2,
|
||||
"key": null,
|
||||
"parentID": 1,
|
||||
"treeBaseDuration": 14,
|
||||
},
|
||||
3 => Object {
|
||||
"children": Array [],
|
||||
"displayName": "Memo(Child)",
|
||||
"id": 3,
|
||||
"key": "first",
|
||||
"parentID": 2,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
4 => Object {
|
||||
"children": Array [],
|
||||
"displayName": "Memo(Child)",
|
||||
"id": 4,
|
||||
"key": "second",
|
||||
"parentID": 2,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
},
|
||||
"rootID": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`profiling charts flamegraph chart should contain valid data: 0: FlamegraphChartData 1`] = `
|
||||
Object {
|
||||
"baseDuration": 14,
|
||||
"depth": 2,
|
||||
"idToDepthMap": Map {
|
||||
2 => 1,
|
||||
4 => 2,
|
||||
3 => 2,
|
||||
},
|
||||
"maxSelfDuration": 10,
|
||||
"rows": Array [
|
||||
Array [
|
||||
Object {
|
||||
"actualDuration": 14,
|
||||
"didRender": true,
|
||||
"id": 2,
|
||||
"label": "Parent (10.0ms of 14.0ms)",
|
||||
"name": "Parent",
|
||||
"offset": 0,
|
||||
"selfDuration": 10,
|
||||
"treeBaseDuration": 14,
|
||||
},
|
||||
],
|
||||
Array [
|
||||
Object {
|
||||
"actualDuration": 2,
|
||||
"didRender": true,
|
||||
"id": 4,
|
||||
"label": "Memo(Child) key=\\"second\\" (2.0ms of 2.0ms)",
|
||||
"name": "Memo(Child)",
|
||||
"offset": 12,
|
||||
"selfDuration": 2,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
Object {
|
||||
"actualDuration": 2,
|
||||
"didRender": true,
|
||||
"id": 3,
|
||||
"label": "Memo(Child) key=\\"first\\" (2.0ms of 2.0ms)",
|
||||
"name": "Memo(Child)",
|
||||
"offset": 10,
|
||||
"selfDuration": 2,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
],
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`profiling charts flamegraph chart should contain valid data: 1: CommitTree 1`] = `
|
||||
Object {
|
||||
"nodes": Map {
|
||||
1 => Object {
|
||||
"children": Array [
|
||||
2,
|
||||
],
|
||||
"displayName": null,
|
||||
"id": 1,
|
||||
"key": null,
|
||||
"parentID": 0,
|
||||
"treeBaseDuration": 14,
|
||||
},
|
||||
2 => Object {
|
||||
"children": Array [
|
||||
3,
|
||||
4,
|
||||
],
|
||||
"displayName": "Parent",
|
||||
"id": 2,
|
||||
"key": null,
|
||||
"parentID": 1,
|
||||
"treeBaseDuration": 14,
|
||||
},
|
||||
3 => Object {
|
||||
"children": Array [],
|
||||
"displayName": "Memo(Child)",
|
||||
"id": 3,
|
||||
"key": "first",
|
||||
"parentID": 2,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
4 => Object {
|
||||
"children": Array [],
|
||||
"displayName": "Memo(Child)",
|
||||
"id": 4,
|
||||
"key": "second",
|
||||
"parentID": 2,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
},
|
||||
"rootID": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`profiling charts flamegraph chart should contain valid data: 1: FlamegraphChartData 1`] = `
|
||||
Object {
|
||||
"baseDuration": 14,
|
||||
"depth": 2,
|
||||
"idToDepthMap": Map {
|
||||
2 => 1,
|
||||
4 => 2,
|
||||
3 => 2,
|
||||
},
|
||||
"maxSelfDuration": 10,
|
||||
"rows": Array [
|
||||
Array [
|
||||
Object {
|
||||
"actualDuration": 10,
|
||||
"didRender": true,
|
||||
"id": 2,
|
||||
"label": "Parent (10.0ms of 10.0ms)",
|
||||
"name": "Parent",
|
||||
"offset": 0,
|
||||
"selfDuration": 10,
|
||||
"treeBaseDuration": 14,
|
||||
},
|
||||
],
|
||||
Array [
|
||||
Object {
|
||||
"actualDuration": 0,
|
||||
"didRender": false,
|
||||
"id": 4,
|
||||
"label": "Memo(Child) key=\\"second\\"",
|
||||
"name": "Memo(Child)",
|
||||
"offset": 12,
|
||||
"selfDuration": 0,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
Object {
|
||||
"actualDuration": 0,
|
||||
"didRender": false,
|
||||
"id": 3,
|
||||
"label": "Memo(Child) key=\\"first\\"",
|
||||
"name": "Memo(Child)",
|
||||
"offset": 10,
|
||||
"selfDuration": 0,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
],
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`profiling charts interactions should contain valid data: Interactions 1`] = `
|
||||
Object {
|
||||
"lastInteractionTime": 24,
|
||||
"maxCommitDuration": 14,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`profiling charts interactions should contain valid data: Interactions 2`] = `
|
||||
Object {
|
||||
"lastInteractionTime": 24,
|
||||
"maxCommitDuration": 14,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`profiling charts ranked chart should contain valid data: 0: CommitTree 1`] = `
|
||||
Object {
|
||||
"nodes": Map {
|
||||
1 => Object {
|
||||
"children": Array [
|
||||
2,
|
||||
],
|
||||
"displayName": null,
|
||||
"id": 1,
|
||||
"key": null,
|
||||
"parentID": 0,
|
||||
"treeBaseDuration": 14,
|
||||
},
|
||||
2 => Object {
|
||||
"children": Array [
|
||||
3,
|
||||
4,
|
||||
],
|
||||
"displayName": "Parent",
|
||||
"id": 2,
|
||||
"key": null,
|
||||
"parentID": 1,
|
||||
"treeBaseDuration": 14,
|
||||
},
|
||||
3 => Object {
|
||||
"children": Array [],
|
||||
"displayName": "Memo(Child)",
|
||||
"id": 3,
|
||||
"key": "first",
|
||||
"parentID": 2,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
4 => Object {
|
||||
"children": Array [],
|
||||
"displayName": "Memo(Child)",
|
||||
"id": 4,
|
||||
"key": "second",
|
||||
"parentID": 2,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
},
|
||||
"rootID": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`profiling charts ranked chart should contain valid data: 0: RankedChartData 1`] = `
|
||||
Object {
|
||||
"maxValue": 10,
|
||||
"nodes": Array [
|
||||
Object {
|
||||
"id": 2,
|
||||
"label": "Parent (10.0ms)",
|
||||
"name": "Parent",
|
||||
"value": 10,
|
||||
},
|
||||
Object {
|
||||
"id": 3,
|
||||
"label": "Memo(Child) key=\\"first\\" (2.0ms)",
|
||||
"name": "Memo(Child)",
|
||||
"value": 2,
|
||||
},
|
||||
Object {
|
||||
"id": 4,
|
||||
"label": "Memo(Child) key=\\"second\\" (2.0ms)",
|
||||
"name": "Memo(Child)",
|
||||
"value": 2,
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`profiling charts ranked chart should contain valid data: 1: CommitTree 1`] = `
|
||||
Object {
|
||||
"nodes": Map {
|
||||
1 => Object {
|
||||
"children": Array [
|
||||
2,
|
||||
],
|
||||
"displayName": null,
|
||||
"id": 1,
|
||||
"key": null,
|
||||
"parentID": 0,
|
||||
"treeBaseDuration": 14,
|
||||
},
|
||||
2 => Object {
|
||||
"children": Array [
|
||||
3,
|
||||
4,
|
||||
],
|
||||
"displayName": "Parent",
|
||||
"id": 2,
|
||||
"key": null,
|
||||
"parentID": 1,
|
||||
"treeBaseDuration": 14,
|
||||
},
|
||||
3 => Object {
|
||||
"children": Array [],
|
||||
"displayName": "Memo(Child)",
|
||||
"id": 3,
|
||||
"key": "first",
|
||||
"parentID": 2,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
4 => Object {
|
||||
"children": Array [],
|
||||
"displayName": "Memo(Child)",
|
||||
"id": 4,
|
||||
"key": "second",
|
||||
"parentID": 2,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
},
|
||||
"rootID": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`profiling charts ranked chart should contain valid data: 1: RankedChartData 1`] = `
|
||||
Object {
|
||||
"maxValue": 10,
|
||||
"nodes": Array [
|
||||
Object {
|
||||
"id": 2,
|
||||
"label": "Parent (10.0ms)",
|
||||
"name": "Parent",
|
||||
"value": 10,
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -1,13 +1,11 @@
|
||||
// @flow
|
||||
|
||||
import type React from 'react';
|
||||
import type ReactDOM from 'react-dom';
|
||||
import typeof ReactTestRenderer from 'react-test-renderer';
|
||||
import type Store from 'src/devtools/store';
|
||||
|
||||
describe('profiling', () => {
|
||||
let React: React;
|
||||
let ReactDOM: ReactDOM;
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let Scheduler;
|
||||
let SchedulerTracing;
|
||||
let TestRenderer: ReactTestRenderer;
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
// @flow
|
||||
|
||||
import typeof TestRendererType from 'react-test-renderer';
|
||||
import type Store from 'src/devtools/store';
|
||||
|
||||
describe('profiling charts', () => {
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let Scheduler;
|
||||
let SchedulerTracing;
|
||||
let TestRenderer: TestRendererType;
|
||||
let store: Store;
|
||||
let utils;
|
||||
|
||||
beforeEach(() => {
|
||||
utils = require('./utils');
|
||||
utils.beforeEachProfiling();
|
||||
|
||||
store = global.store;
|
||||
store.collapseNodesByDefault = false;
|
||||
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
Scheduler = require('scheduler');
|
||||
SchedulerTracing = require('scheduler/tracing');
|
||||
TestRenderer = utils.requireTestRenderer();
|
||||
});
|
||||
|
||||
describe('flamegraph chart', () => {
|
||||
it('should contain valid data', async done => {
|
||||
const Parent = ({ count }) => {
|
||||
Scheduler.advanceTime(10);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Child key="first" />
|
||||
<Child key="second" />
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
// Memoize children to verify that chart doesn't include in the update.
|
||||
const Child = React.memo(function Child() {
|
||||
Scheduler.advanceTime(2);
|
||||
return null;
|
||||
});
|
||||
|
||||
const container = document.createElement('div');
|
||||
|
||||
utils.act(() => store.startProfiling());
|
||||
utils.act(() =>
|
||||
SchedulerTracing.unstable_trace('mount', Scheduler.unstable_now(), () =>
|
||||
ReactDOM.render(<Parent />, container)
|
||||
)
|
||||
);
|
||||
utils.act(() =>
|
||||
SchedulerTracing.unstable_trace(
|
||||
'update',
|
||||
Scheduler.unstable_now(),
|
||||
() => ReactDOM.render(<Parent />, container)
|
||||
)
|
||||
);
|
||||
utils.act(() => store.stopProfiling());
|
||||
|
||||
let suspenseResolved = false;
|
||||
|
||||
function Suspender({ commitIndex, rendererID, rootID }) {
|
||||
const profilingSummary = store.profilingCache.ProfilingSummary.read({
|
||||
rendererID,
|
||||
rootID,
|
||||
});
|
||||
const commitDetails = store.profilingCache.CommitDetails.read({
|
||||
commitIndex,
|
||||
rendererID,
|
||||
rootID,
|
||||
});
|
||||
suspenseResolved = true;
|
||||
const commitTree = store.profilingCache.getCommitTree({
|
||||
commitIndex,
|
||||
profilingSummary,
|
||||
});
|
||||
const chartData = store.profilingCache.getFlamegraphChartData({
|
||||
commitDetails,
|
||||
commitIndex,
|
||||
commitTree,
|
||||
});
|
||||
expect(commitTree).toMatchSnapshot(`${commitIndex}: CommitTree`);
|
||||
expect(chartData).toMatchSnapshot(
|
||||
`${commitIndex}: FlamegraphChartData`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const rendererID = utils.getRendererID();
|
||||
const rootID = store.roots[0];
|
||||
|
||||
for (let commitIndex = 0; commitIndex < 2; commitIndex++) {
|
||||
suspenseResolved = false;
|
||||
|
||||
await utils.actSuspense(
|
||||
() =>
|
||||
TestRenderer.create(
|
||||
<React.Suspense fallback={null}>
|
||||
<Suspender
|
||||
commitIndex={commitIndex}
|
||||
rendererID={rendererID}
|
||||
rootID={rootID}
|
||||
/>
|
||||
</React.Suspense>
|
||||
),
|
||||
3
|
||||
);
|
||||
|
||||
expect(suspenseResolved).toBe(true);
|
||||
}
|
||||
|
||||
expect(suspenseResolved).toBe(true);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ranked chart', () => {
|
||||
it('should contain valid data', async done => {
|
||||
const Parent = ({ count }) => {
|
||||
Scheduler.advanceTime(10);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Child key="first" />
|
||||
<Child key="second" />
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
// Memoize children to verify that chart doesn't include in the update.
|
||||
const Child = React.memo(function Child() {
|
||||
Scheduler.advanceTime(2);
|
||||
return null;
|
||||
});
|
||||
|
||||
const container = document.createElement('div');
|
||||
|
||||
utils.act(() => store.startProfiling());
|
||||
utils.act(() =>
|
||||
SchedulerTracing.unstable_trace('mount', Scheduler.unstable_now(), () =>
|
||||
ReactDOM.render(<Parent />, container)
|
||||
)
|
||||
);
|
||||
utils.act(() =>
|
||||
SchedulerTracing.unstable_trace(
|
||||
'update',
|
||||
Scheduler.unstable_now(),
|
||||
() => ReactDOM.render(<Parent />, container)
|
||||
)
|
||||
);
|
||||
utils.act(() => store.stopProfiling());
|
||||
|
||||
let suspenseResolved = false;
|
||||
|
||||
function Suspender({ commitIndex, rendererID, rootID }) {
|
||||
const profilingSummary = store.profilingCache.ProfilingSummary.read({
|
||||
rendererID,
|
||||
rootID,
|
||||
});
|
||||
const commitDetails = store.profilingCache.CommitDetails.read({
|
||||
commitIndex,
|
||||
rendererID,
|
||||
rootID,
|
||||
});
|
||||
suspenseResolved = true;
|
||||
const commitTree = store.profilingCache.getCommitTree({
|
||||
commitIndex,
|
||||
profilingSummary,
|
||||
});
|
||||
const chartData = store.profilingCache.getRankedChartData({
|
||||
commitDetails,
|
||||
commitIndex,
|
||||
commitTree,
|
||||
});
|
||||
expect(commitTree).toMatchSnapshot(`${commitIndex}: CommitTree`);
|
||||
expect(chartData).toMatchSnapshot(`${commitIndex}: RankedChartData`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const rendererID = utils.getRendererID();
|
||||
const rootID = store.roots[0];
|
||||
|
||||
for (let commitIndex = 0; commitIndex < 2; commitIndex++) {
|
||||
suspenseResolved = false;
|
||||
|
||||
await utils.actSuspense(
|
||||
() =>
|
||||
TestRenderer.create(
|
||||
<React.Suspense fallback={null}>
|
||||
<Suspender
|
||||
commitIndex={commitIndex}
|
||||
rendererID={rendererID}
|
||||
rootID={rootID}
|
||||
/>
|
||||
</React.Suspense>
|
||||
),
|
||||
3
|
||||
);
|
||||
|
||||
expect(suspenseResolved).toBe(true);
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('interactions', () => {
|
||||
it('should contain valid data', async done => {
|
||||
const Parent = ({ count }) => {
|
||||
Scheduler.advanceTime(10);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Child key="first" />
|
||||
<Child key="second" />
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
// Memoize children to verify that chart doesn't include in the update.
|
||||
const Child = React.memo(function Child() {
|
||||
Scheduler.advanceTime(2);
|
||||
return null;
|
||||
});
|
||||
|
||||
const container = document.createElement('div');
|
||||
|
||||
utils.act(() => store.startProfiling());
|
||||
utils.act(() =>
|
||||
SchedulerTracing.unstable_trace('mount', Scheduler.unstable_now(), () =>
|
||||
ReactDOM.render(<Parent />, container)
|
||||
)
|
||||
);
|
||||
utils.act(() =>
|
||||
SchedulerTracing.unstable_trace(
|
||||
'update',
|
||||
Scheduler.unstable_now(),
|
||||
() => ReactDOM.render(<Parent />, container)
|
||||
)
|
||||
);
|
||||
utils.act(() => store.stopProfiling());
|
||||
|
||||
let suspenseResolved = false;
|
||||
|
||||
function Suspender({ commitIndex, rendererID, rootID }) {
|
||||
const profilingSummary = store.profilingCache.ProfilingSummary.read({
|
||||
rendererID,
|
||||
rootID,
|
||||
});
|
||||
const interactions = store.profilingCache.Interactions.read({
|
||||
rendererID,
|
||||
rootID,
|
||||
});
|
||||
suspenseResolved = true;
|
||||
const chartData = store.profilingCache.getInteractionsChartData({
|
||||
interactions,
|
||||
profilingSummary,
|
||||
});
|
||||
expect(chartData).toMatchSnapshot('Interactions');
|
||||
return null;
|
||||
}
|
||||
|
||||
const rendererID = utils.getRendererID();
|
||||
const rootID = store.roots[0];
|
||||
|
||||
for (let commitIndex = 0; commitIndex < 2; commitIndex++) {
|
||||
suspenseResolved = false;
|
||||
|
||||
await utils.actSuspense(
|
||||
() =>
|
||||
TestRenderer.create(
|
||||
<React.Suspense fallback={null}>
|
||||
<Suspender
|
||||
commitIndex={commitIndex}
|
||||
rendererID={rendererID}
|
||||
rootID={rootID}
|
||||
/>
|
||||
</React.Suspense>
|
||||
),
|
||||
3
|
||||
);
|
||||
|
||||
expect(suspenseResolved).toBe(true);
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
+14
-3
@@ -12,7 +12,10 @@ export function act(callback: Function): void {
|
||||
jest.runAllTimers();
|
||||
}
|
||||
|
||||
export async function actSuspense(callback: Function): Promise<void> {
|
||||
export async function actSuspense(
|
||||
callback: Function,
|
||||
numTimesToFlush: number = 1
|
||||
): Promise<void> {
|
||||
const TestUtils = require('react-dom/test-utils');
|
||||
const Scheduler = require('scheduler');
|
||||
|
||||
@@ -24,8 +27,16 @@ export async function actSuspense(callback: Function): Promise<void> {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
// Re-render after resolved promises
|
||||
Scheduler.flushAll();
|
||||
// Run cascading microtasks and flush scheduled React work.
|
||||
// Components that suspend multiple times will need to do this once per suspend operation.
|
||||
// HACK Ideally the mock scheduler would provide an API to ask if there was outstanding work.
|
||||
while (--numTimesToFlush >= 0) {
|
||||
// $FlowFixMe Flow doens't know about "await act()" yet
|
||||
await TestUtils.act(async () => {
|
||||
jest.runAllTimers();
|
||||
Scheduler.flushAll();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function beforeEachProfiling(): void {
|
||||
|
||||
Reference in New Issue
Block a user