mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Experimenting with profiler tests
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
"<rootDir>/src/__tests__/setupTests"
|
||||
],
|
||||
"snapshotSerializers": [
|
||||
"<rootDir>/src/__tests__/profilingSummarySerializer",
|
||||
"<rootDir>/src/__tests__/storeSerializer"
|
||||
],
|
||||
"testMatch": [
|
||||
@@ -134,6 +135,7 @@
|
||||
"react-color": "^2.11.7",
|
||||
"react-dom": "0.0.0-fb28e9048",
|
||||
"react-is": "0.0.0-fb28e9048",
|
||||
"react-test-renderer": "0.0.0-fb28e9048",
|
||||
"react-virtualized-auto-sizer": "^1.0.2",
|
||||
"react-window": "^1.8.0",
|
||||
"request-promise": "^4.2.4",
|
||||
|
||||
@@ -1,31 +1,110 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Profiler should start and stop profiling, handle root unmounting: 1: mount 1`] = `
|
||||
exports[`Profiler should clean up after a root has been unmounted: 1: mount 1`] = `
|
||||
[root]
|
||||
▸ <Parent key="A">
|
||||
▾ <Parent key="A">
|
||||
<Child key="0">
|
||||
<Child key="1">
|
||||
<Child key="2">
|
||||
[root]
|
||||
▸ <Parent key="B">
|
||||
▾ <Parent key="B">
|
||||
<Child key="0">
|
||||
<Child key="1">
|
||||
`;
|
||||
|
||||
exports[`Profiler should start and stop profiling, handle root unmounting: 2: profiling started 1`] = `
|
||||
exports[`Profiler should clean up after a root has been unmounted: 2: profiling started 1`] = `
|
||||
[root]
|
||||
▸ <Parent key="A">
|
||||
▾ <Parent key="A">
|
||||
<Child key="0">
|
||||
<Child key="1">
|
||||
<Child key="2">
|
||||
[root]
|
||||
▸ <Parent key="B">
|
||||
▾ <Parent key="B">
|
||||
<Child key="0">
|
||||
<Child key="1">
|
||||
`;
|
||||
|
||||
exports[`Profiler should start and stop profiling, handle root unmounting: 3: update 1`] = `
|
||||
exports[`Profiler should clean up after a root has been unmounted: 3: update 1`] = `
|
||||
[root]
|
||||
▸ <Parent key="A">
|
||||
▾ <Parent key="A">
|
||||
<Child key="0">
|
||||
<Child key="1">
|
||||
<Child key="2">
|
||||
<Child key="3">
|
||||
[root]
|
||||
▸ <Parent key="B">
|
||||
▾ <Parent key="B">
|
||||
<Child key="0">
|
||||
`;
|
||||
|
||||
exports[`Profiler should start and stop profiling, handle root unmounting: 4: unmount B 1`] = `
|
||||
exports[`Profiler should clean up after a root has been unmounted: 4: unmount B 1`] = `
|
||||
[root]
|
||||
▸ <Parent key="A">
|
||||
▾ <Parent key="A">
|
||||
<Child key="0">
|
||||
<Child key="1">
|
||||
<Child key="2">
|
||||
<Child key="3">
|
||||
`;
|
||||
|
||||
exports[`Profiler should start and stop profiling, handle root unmounting: 5: unmount A 1`] = ``;
|
||||
exports[`Profiler should clean up after a root has been unmounted: 5: unmount A 1`] = ``;
|
||||
|
||||
exports[`Profiler should start and stop profiling, handle root unmounting: 6: profiling stopped 1`] = ``;
|
||||
exports[`Profiler should clean up after a root has been unmounted: 6: profiling stopped 1`] = ``;
|
||||
|
||||
exports[`Profiler should collect basic profiling metrics: 1: mount 1`] = `
|
||||
[root]
|
||||
▾ <Parent key="A">
|
||||
<Child key="0">
|
||||
<Child key="1">
|
||||
`;
|
||||
|
||||
exports[`Profiler should collect basic profiling metrics: 2: add child 1`] = `
|
||||
[root]
|
||||
▾ <Parent key="A">
|
||||
<Child key="0">
|
||||
<Child key="1">
|
||||
<Child key="2">
|
||||
`;
|
||||
|
||||
exports[`Profiler should collect basic profiling metrics: 3: remove children 1`] = `
|
||||
[root]
|
||||
▾ <Parent key="A">
|
||||
<Child key="0">
|
||||
`;
|
||||
|
||||
exports[`Profiler should collect basic profiling metrics: 4: profiling stopped 1`] = `
|
||||
[root]
|
||||
▾ <Parent key="A">
|
||||
<Child key="0">
|
||||
`;
|
||||
|
||||
exports[`Profiler should collect basic profiling metrics: ProfilingSummary 1`] = `
|
||||
{
|
||||
"rootID": 1,
|
||||
"commitDurations": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"commitTimes": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"initialTreeBaseDurations": [
|
||||
[
|
||||
1,
|
||||
0
|
||||
],
|
||||
[
|
||||
2,
|
||||
1
|
||||
],
|
||||
[
|
||||
3,
|
||||
2
|
||||
],
|
||||
[
|
||||
4,
|
||||
3
|
||||
]
|
||||
],
|
||||
"interactionCount": 0
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
describe('Profiler', () => {
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let TestRenderer;
|
||||
let TestUtils;
|
||||
let agent;
|
||||
let store;
|
||||
|
||||
const act = (callback: Function) => {
|
||||
@@ -13,15 +15,88 @@ describe('Profiler', () => {
|
||||
jest.runAllTimers(); // Flush Bridge operations
|
||||
};
|
||||
|
||||
const renderAndResolve = async (root, element) => {
|
||||
// $FlowFixMe Flow doens't know about "await act()" yet
|
||||
await TestUtils.act(async () => {
|
||||
root.update(element);
|
||||
|
||||
// Resolve pending suspense promises
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
// Re-render after resolved promises
|
||||
jest.runAllTimers();
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
agent = global.agent;
|
||||
store = global.store;
|
||||
store.collapseNodesByDefault = false;
|
||||
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
TestUtils = require('react-dom/test-utils');
|
||||
|
||||
// Hide the hook before requiring TestRenderer, so we don't end up with a loop.
|
||||
const hook = global.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
delete global.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
TestRenderer = require('react-test-renderer');
|
||||
global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = hook;
|
||||
});
|
||||
|
||||
it('should start and stop profiling, handle root unmounting', async () => {
|
||||
it('should collect basic profiling metrics', async done => {
|
||||
const Parent = ({ count }) =>
|
||||
new Array(count).fill(true).map((_, index) => <Child key={index} />);
|
||||
const Child = () => {
|
||||
jest.advanceTimersByTime(1);
|
||||
return null;
|
||||
};
|
||||
|
||||
const container = document.createElement('div');
|
||||
|
||||
act(() => ReactDOM.render(<Parent key="A" count={2} />, container));
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
|
||||
act(() => store.startProfiling());
|
||||
|
||||
act(() => ReactDOM.render(<Parent key="A" count={3} />, container));
|
||||
expect(store).toMatchSnapshot('2: add child');
|
||||
|
||||
act(() => ReactDOM.render(<Parent key="A" count={1} />, container));
|
||||
expect(store).toMatchSnapshot('3: remove children');
|
||||
|
||||
act(() => store.stopProfiling());
|
||||
expect(store).toMatchSnapshot('4: profiling stopped');
|
||||
|
||||
let profilingSummary;
|
||||
function Suspender({ rendererID, rootID }) {
|
||||
profilingSummary = store.profilingCache.ProfilingSummary.read({
|
||||
rendererID,
|
||||
rootID,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
// HACK There's only one renderer for this test
|
||||
const rendererID = Object.keys(agent._rendererInterfaces)[0];
|
||||
const rootID = store.roots[0];
|
||||
|
||||
let root = TestRenderer.create();
|
||||
await renderAndResolve(
|
||||
root,
|
||||
<React.Suspense fallback={null}>
|
||||
<Suspender rendererID={rendererID} rootID={rootID} />
|
||||
</React.Suspense>
|
||||
);
|
||||
|
||||
// HACK root.toTree() doesn't handle Suspense yet
|
||||
// but Jest serializer wouldn't work with a JSON string
|
||||
expect(profilingSummary).toMatchSnapshot('ProfilingSummary');
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should clean up after a root has been unmounted', async () => {
|
||||
const Parent = ({ count }) =>
|
||||
new Array(count).fill(true).map((_, index) => <Child key={index} />);
|
||||
const Child = () => <div>Hi!</div>;
|
||||
@@ -35,9 +110,7 @@ describe('Profiler', () => {
|
||||
});
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
|
||||
act(() => {
|
||||
store.startProfiling();
|
||||
});
|
||||
act(() => store.startProfiling());
|
||||
expect(store).toMatchSnapshot('2: profiling started');
|
||||
|
||||
act(() => {
|
||||
@@ -52,9 +125,7 @@ describe('Profiler', () => {
|
||||
act(() => ReactDOM.unmountComponentAtNode(containerA));
|
||||
expect(store).toMatchSnapshot('5: unmount A');
|
||||
|
||||
act(() => {
|
||||
store.stopProfiling();
|
||||
});
|
||||
act(() => store.stopProfiling());
|
||||
expect(store).toMatchSnapshot('6: profiling stopped');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// test() is part of Jest's serializer API
|
||||
export function test(maybeProfilingSummary) {
|
||||
return (
|
||||
typeof maybeProfilingSummary === 'object' &&
|
||||
maybeProfilingSummary !== null &&
|
||||
typeof maybeProfilingSummary.rootID === 'number' &&
|
||||
Array.isArray(maybeProfilingSummary.commitDurations) &&
|
||||
Array.isArray(maybeProfilingSummary.commitTimes) &&
|
||||
typeof maybeProfilingSummary.initialTreeBaseDurations === 'object' &&
|
||||
maybeProfilingSummary.initialTreeBaseDurations !== null &&
|
||||
typeof maybeProfilingSummary.interactionCount === 'number'
|
||||
);
|
||||
}
|
||||
|
||||
// print() is part of Jest's serializer API
|
||||
export function print(profilingSummary, serialize, indent) {
|
||||
return JSON.stringify(
|
||||
{
|
||||
...profilingSummary,
|
||||
commitDurations: profilingSummary.commitDurations.map((_, i) => i),
|
||||
commitTimes: profilingSummary.commitTimes.map((_, i) => i),
|
||||
initialTreeBaseDurations: [
|
||||
...profilingSummary.initialTreeBaseDurations,
|
||||
].map(([id, _], index) => [id, index]),
|
||||
},
|
||||
null,
|
||||
2
|
||||
);
|
||||
}
|
||||
@@ -8,10 +8,6 @@ import { installHook } from 'src/hook';
|
||||
|
||||
const env = jasmine.getEnv();
|
||||
env.beforeEach(() => {
|
||||
// It's important to reset modules between test runs;
|
||||
// Without this, ReactDOM won't re-inject itself into the new hook.
|
||||
jest.resetModules();
|
||||
|
||||
// Fake timers let us flush Bridge operations between setup and assertions.
|
||||
jest.useFakeTimers();
|
||||
|
||||
@@ -55,4 +51,10 @@ env.beforeEach(() => {
|
||||
});
|
||||
env.afterEach(() => {
|
||||
delete global.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
|
||||
// It's important to reset modules between test runs;
|
||||
// Without this, ReactDOM won't re-inject itself into the new hook.
|
||||
// It's also important to reset after tests, rather than before,
|
||||
// so that we don't disconnect the ReactCurrentDispatcher ref.
|
||||
jest.resetModules();
|
||||
});
|
||||
|
||||
@@ -9837,6 +9837,16 @@ react-lifecycles-compat@^3.0.4:
|
||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
||||
|
||||
react-test-renderer@0.0.0-fb28e9048:
|
||||
version "0.0.0-fb28e9048"
|
||||
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-0.0.0-fb28e9048.tgz#1a94c8d19cbb1ac98ab37c66c0b294e5be280c52"
|
||||
integrity sha512-WK/wQOh0v6+8Gbkurgb3he9hKoOKWueqQY+RFs2vM3u3vn7PMyYhzm/KkU75VvTG/GVciojNQBHBpekuvU5dYw==
|
||||
dependencies:
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
react-is "0.0.0-fb28e9048"
|
||||
scheduler "0.0.0-fb28e9048"
|
||||
|
||||
react-virtualized-auto-sizer@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.2.tgz#a61dd4f756458bbf63bd895a92379f9b70f803bd"
|
||||
|
||||
Reference in New Issue
Block a user