Add better test coverage for hooks in DevTools Profiler's change-detection (#17816)

This commit is contained in:
Brian Vaughn 2020-01-09 13:15:19 -08:00 committed by GitHub
parent 2e4948a34d
commit 435259f189
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 555 additions and 0 deletions

View File

@ -2121,6 +2121,437 @@ Object {
}
`;
exports[`ProfilingCache should properly detect changed hooks: CommitDetails commitIndex: 0 1`] = `
Object {
"changeDescriptions": Map {
3 => Object {
"context": null,
"didHooksChange": false,
"isFirstMount": true,
"props": null,
"state": null,
},
},
"duration": 0,
"fiberActualDurations": Map {
1 => 0,
2 => 0,
3 => 0,
},
"fiberSelfDurations": Map {
1 => 0,
2 => 0,
3 => 0,
},
"interactionIDs": Array [],
"priorityLevel": "Immediate",
"timestamp": 0,
}
`;
exports[`ProfilingCache should properly detect changed hooks: CommitDetails commitIndex: 1 1`] = `
Object {
"changeDescriptions": Map {
3 => Object {
"context": null,
"didHooksChange": false,
"isFirstMount": false,
"props": Array [
"count",
],
"state": null,
},
},
"duration": 0,
"fiberActualDurations": Map {
3 => 0,
2 => 0,
1 => 0,
},
"fiberSelfDurations": Map {
3 => 0,
2 => 0,
1 => 0,
},
"interactionIDs": Array [],
"priorityLevel": "Immediate",
"timestamp": 0,
}
`;
exports[`ProfilingCache should properly detect changed hooks: CommitDetails commitIndex: 2 1`] = `
Object {
"changeDescriptions": Map {
3 => Object {
"context": null,
"didHooksChange": true,
"isFirstMount": false,
"props": Array [],
"state": null,
},
},
"duration": 0,
"fiberActualDurations": Map {
3 => 0,
},
"fiberSelfDurations": Map {
3 => 0,
},
"interactionIDs": Array [],
"priorityLevel": "Immediate",
"timestamp": 0,
}
`;
exports[`ProfilingCache should properly detect changed hooks: CommitDetails commitIndex: 3 1`] = `
Object {
"changeDescriptions": Map {
3 => Object {
"context": null,
"didHooksChange": true,
"isFirstMount": false,
"props": Array [],
"state": null,
},
},
"duration": 0,
"fiberActualDurations": Map {
3 => 0,
},
"fiberSelfDurations": Map {
3 => 0,
},
"interactionIDs": Array [],
"priorityLevel": "Immediate",
"timestamp": 0,
}
`;
exports[`ProfilingCache should properly detect changed hooks: CommitDetails commitIndex: 4 1`] = `
Object {
"changeDescriptions": Map {
3 => Object {
"context": null,
"didHooksChange": false,
"isFirstMount": false,
"props": Array [],
"state": null,
},
},
"duration": 0,
"fiberActualDurations": Map {
3 => 0,
2 => 0,
1 => 0,
},
"fiberSelfDurations": Map {
3 => 0,
2 => 0,
1 => 0,
},
"interactionIDs": Array [],
"priorityLevel": "Immediate",
"timestamp": 0,
}
`;
exports[`ProfilingCache should properly detect changed hooks: imported data 1`] = `
Object {
"dataForRoots": Array [
Object {
"commitData": Array [
Object {
"changeDescriptions": Array [
Array [
3,
Object {
"context": null,
"didHooksChange": false,
"isFirstMount": true,
"props": null,
"state": null,
},
],
],
"duration": 0,
"fiberActualDurations": Array [
Array [
1,
0,
],
Array [
2,
0,
],
Array [
3,
0,
],
],
"fiberSelfDurations": Array [
Array [
1,
0,
],
Array [
2,
0,
],
Array [
3,
0,
],
],
"interactionIDs": Array [],
"priorityLevel": "Immediate",
"timestamp": 0,
},
Object {
"changeDescriptions": Array [
Array [
3,
Object {
"context": null,
"didHooksChange": false,
"isFirstMount": false,
"props": Array [
"count",
],
"state": null,
},
],
],
"duration": 0,
"fiberActualDurations": Array [
Array [
3,
0,
],
Array [
2,
0,
],
Array [
1,
0,
],
],
"fiberSelfDurations": Array [
Array [
3,
0,
],
Array [
2,
0,
],
Array [
1,
0,
],
],
"interactionIDs": Array [],
"priorityLevel": "Immediate",
"timestamp": 0,
},
Object {
"changeDescriptions": Array [
Array [
3,
Object {
"context": null,
"didHooksChange": true,
"isFirstMount": false,
"props": Array [],
"state": null,
},
],
],
"duration": 0,
"fiberActualDurations": Array [
Array [
3,
0,
],
],
"fiberSelfDurations": Array [
Array [
3,
0,
],
],
"interactionIDs": Array [],
"priorityLevel": "Immediate",
"timestamp": 0,
},
Object {
"changeDescriptions": Array [
Array [
3,
Object {
"context": null,
"didHooksChange": true,
"isFirstMount": false,
"props": Array [],
"state": null,
},
],
],
"duration": 0,
"fiberActualDurations": Array [
Array [
3,
0,
],
],
"fiberSelfDurations": Array [
Array [
3,
0,
],
],
"interactionIDs": Array [],
"priorityLevel": "Immediate",
"timestamp": 0,
},
Object {
"changeDescriptions": Array [
Array [
3,
Object {
"context": null,
"didHooksChange": false,
"isFirstMount": false,
"props": Array [],
"state": null,
},
],
],
"duration": 0,
"fiberActualDurations": Array [
Array [
3,
0,
],
Array [
2,
0,
],
Array [
1,
0,
],
],
"fiberSelfDurations": Array [
Array [
3,
0,
],
Array [
2,
0,
],
Array [
1,
0,
],
],
"interactionIDs": Array [],
"priorityLevel": "Immediate",
"timestamp": 0,
},
],
"displayName": "Component",
"initialTreeBaseDurations": Array [],
"interactionCommits": Array [],
"interactions": Array [],
"operations": Array [
Array [
1,
1,
27,
16,
67,
111,
110,
116,
101,
120,
116,
46,
80,
114,
111,
118,
105,
100,
101,
114,
9,
67,
111,
109,
112,
111,
110,
101,
110,
116,
1,
1,
11,
1,
1,
1,
2,
2,
1,
0,
1,
0,
4,
2,
0,
1,
3,
5,
2,
0,
2,
0,
4,
3,
0,
],
Array [
1,
1,
0,
],
Array [
1,
1,
0,
],
Array [
1,
1,
0,
],
Array [
1,
1,
0,
],
],
"rootID": 1,
"snapshots": Array [],
},
],
"version": 4,
}
`;
exports[`ProfilingCache should record changed props/state/context/hooks: CommitDetails commitIndex: 0 1`] = `
Object {
"changeDescriptions": Map {

View File

@ -308,6 +308,130 @@ describe('ProfilingCache', () => {
}
});
it('should properly detect changed hooks', () => {
const Context = React.createContext(0);
function reducer(state, action) {
switch (action.type) {
case 'invert':
return {value: !state.value};
default:
throw new Error();
}
}
let dispatch = null;
let setState = null;
const Component = ({count, string}) => {
// These hooks may change and initiate re-renders.
setState = React.useState('abc')[1];
dispatch = React.useReducer(reducer, {value: true})[1];
// This hook's return value may change between renders,
// but the hook itself isn't stateful.
React.useContext(Context);
// These hooks and their dependencies may not change between renders.
// We're using them to ensure that they don't trigger false positives.
React.useCallback(() => () => {}, [string]);
React.useMemo(() => string, [string]);
// These hooks never "change".
React.useEffect(() => {}, [string]);
React.useLayoutEffect(() => {}, [string]);
return null;
};
const container = document.createElement('div');
utils.act(() => store.profilerStore.startProfiling());
utils.act(() =>
ReactDOM.render(
<Context.Provider value={true}>
<Component count={1} />
</Context.Provider>,
container,
),
);
// Second render has no changed hooks, only changed props.
utils.act(() =>
ReactDOM.render(
<Context.Provider value={true}>
<Component count={2} />
</Context.Provider>,
container,
),
);
// Third render has a changed reducer hook
utils.act(() => dispatch({type: 'invert'}));
// Fourth render has a changed state hook
utils.act(() => setState('def'));
// Fifth render has a changed context value, but no changed hook.
// Technically, DevTools will miss this "context" change since it only tracks legacy context.
utils.act(() =>
ReactDOM.render(
<Context.Provider value={false}>
<Component count={2} />
</Context.Provider>,
container,
),
);
utils.act(() => store.profilerStore.stopProfiling());
const allCommitData = [];
function Validator({commitIndex, previousCommitDetails, rootID}) {
const commitData = store.profilerStore.getCommitData(rootID, commitIndex);
if (previousCommitDetails != null) {
expect(commitData).toEqual(previousCommitDetails);
} else {
allCommitData.push(commitData);
expect(commitData).toMatchSnapshot(
`CommitDetails commitIndex: ${commitIndex}`,
);
}
return null;
}
const rootID = store.roots[0];
for (let commitIndex = 0; commitIndex < 5; commitIndex++) {
utils.act(() => {
TestRenderer.create(
<Validator
commitIndex={commitIndex}
previousCommitDetails={null}
rootID={rootID}
/>,
);
});
}
expect(allCommitData).toHaveLength(5);
// Export and re-import profile data and make sure it is retained.
utils.exportImportHelper(bridge, store);
for (let commitIndex = 0; commitIndex < 5; commitIndex++) {
utils.act(() => {
TestRenderer.create(
<Validator
commitIndex={commitIndex}
previousCommitDetails={allCommitData[commitIndex]}
rootID={rootID}
/>,
);
});
}
});
it('should calculate a self duration based on actual children (not filtered children)', () => {
store.componentFilters = [utils.createDisplayNameFilter('^Parent$')];