Add better test coverage for hooks in DevTools Profiler's change-detection (#17816)
This commit is contained in:
parent
2e4948a34d
commit
435259f189
|
@ -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 {
|
||||
|
|
|
@ -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$')];
|
||||
|
||||
|
|
Loading…
Reference in New Issue