mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Merge pull request #258 from bvaughn/profiler-test-experiments
Added profiler tests
This commit is contained in:
+81
@@ -0,0 +1,81 @@
|
||||
// flow-typed signature: b6bb53397d83d2d821e258cc73818d1b
|
||||
// flow-typed version: 9c71eca8ef/react-test-renderer_v16.x.x/flow_>=v0.47.x
|
||||
|
||||
// Type definitions for react-test-renderer 16.x.x
|
||||
// Ported from: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-test-renderer
|
||||
|
||||
type ReactComponentInstance = React$Component<any>;
|
||||
|
||||
type ReactTestRendererJSON = {
|
||||
type: string,
|
||||
props: { [propName: string]: any },
|
||||
children: null | ReactTestRendererJSON[],
|
||||
};
|
||||
|
||||
type ReactTestRendererTree = ReactTestRendererJSON & {
|
||||
nodeType: 'component' | 'host',
|
||||
instance: ?ReactComponentInstance,
|
||||
rendered: null | ReactTestRendererTree,
|
||||
};
|
||||
|
||||
type ReactTestInstance = {
|
||||
instance: ?ReactComponentInstance,
|
||||
type: string,
|
||||
props: { [propName: string]: any },
|
||||
parent: null | ReactTestInstance,
|
||||
children: Array<ReactTestInstance | string>,
|
||||
|
||||
find(predicate: (node: ReactTestInstance) => boolean): ReactTestInstance,
|
||||
findByType(type: React$ElementType): ReactTestInstance,
|
||||
findByProps(props: { [propName: string]: any }): ReactTestInstance,
|
||||
|
||||
findAll(
|
||||
predicate: (node: ReactTestInstance) => boolean,
|
||||
options?: { deep: boolean }
|
||||
): ReactTestInstance[],
|
||||
findAllByType(
|
||||
type: React$ElementType,
|
||||
options?: { deep: boolean }
|
||||
): ReactTestInstance[],
|
||||
findAllByProps(
|
||||
props: { [propName: string]: any },
|
||||
options?: { deep: boolean }
|
||||
): ReactTestInstance[],
|
||||
};
|
||||
|
||||
type TestRendererOptions = {
|
||||
createNodeMock(element: React$Element<any>): any,
|
||||
};
|
||||
|
||||
declare module 'react-test-renderer' {
|
||||
declare export type ReactTestRenderer = {
|
||||
toJSON(): null | ReactTestRendererJSON,
|
||||
toTree(): null | ReactTestRendererTree,
|
||||
unmount(nextElement?: React$Element<any>): void,
|
||||
update(nextElement: React$Element<any>): void,
|
||||
getInstance(): ?ReactComponentInstance,
|
||||
root: ReactTestInstance,
|
||||
};
|
||||
|
||||
declare type Thenable = {
|
||||
then(resolve: () => mixed, reject?: () => mixed): mixed,
|
||||
};
|
||||
|
||||
declare function create(
|
||||
nextElement: React$Element<any>,
|
||||
options?: TestRendererOptions
|
||||
): ReactTestRenderer;
|
||||
|
||||
declare function act(callback: () => void): Thenable;
|
||||
}
|
||||
|
||||
declare module 'react-test-renderer/shallow' {
|
||||
declare export default class ShallowRenderer {
|
||||
static createRenderer(): ShallowRenderer;
|
||||
getMountedInstance(): ReactTestInstance;
|
||||
getRenderOutput<E: React$Element<any>>(): E;
|
||||
getRenderOutput(): React$Element<any>;
|
||||
render(element: React$Element<any>, context?: any): void;
|
||||
unmount(): void;
|
||||
}
|
||||
}
|
||||
+5
-4
@@ -130,14 +130,15 @@
|
||||
"opener": "^1.5.1",
|
||||
"prettier": "^1.16.4",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "0.0.0-fb28e9048",
|
||||
"react": "^0.0.0-6da04b5d8",
|
||||
"react-color": "^2.11.7",
|
||||
"react-dom": "0.0.0-fb28e9048",
|
||||
"react-is": "0.0.0-fb28e9048",
|
||||
"react-dom": "^0.0.0-6da04b5d8",
|
||||
"react-is": "^0.0.0-6da04b5d8",
|
||||
"react-test-renderer": "^0.0.0-6da04b5d8",
|
||||
"react-virtualized-auto-sizer": "^1.0.2",
|
||||
"react-window": "^1.8.0",
|
||||
"request-promise": "^4.2.4",
|
||||
"scheduler": "0.0.0-fb28e9048",
|
||||
"scheduler": "^0.0.0-6da04b5d8",
|
||||
"semver": "^5.5.1",
|
||||
"style-loader": "^0.23.1",
|
||||
"web-ext": "^3.0.0",
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Profiler should start and stop profiling, handle root unmounting: 1: mount 1`] = `
|
||||
[root]
|
||||
▸ <Parent key="A">
|
||||
[root]
|
||||
▸ <Parent key="B">
|
||||
`;
|
||||
|
||||
exports[`Profiler should start and stop profiling, handle root unmounting: 2: profiling started 1`] = `
|
||||
[root]
|
||||
▸ <Parent key="A">
|
||||
[root]
|
||||
▸ <Parent key="B">
|
||||
`;
|
||||
|
||||
exports[`Profiler should start and stop profiling, handle root unmounting: 3: update 1`] = `
|
||||
[root]
|
||||
▸ <Parent key="A">
|
||||
[root]
|
||||
▸ <Parent key="B">
|
||||
`;
|
||||
|
||||
exports[`Profiler should start and stop profiling, handle root unmounting: 4: unmount B 1`] = `
|
||||
[root]
|
||||
▸ <Parent key="A">
|
||||
`;
|
||||
|
||||
exports[`Profiler should start and stop profiling, handle root unmounting: 5: unmount A 1`] = ``;
|
||||
|
||||
exports[`Profiler should start and stop profiling, handle root unmounting: 6: profiling stopped 1`] = ``;
|
||||
@@ -0,0 +1,155 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`profiling CommitDetails should be collected for each commit: CommitDetails commitIndex: 0 1`] = `
|
||||
Object {
|
||||
"actualDurations": Map {
|
||||
1 => 14,
|
||||
2 => 14,
|
||||
3 => 2,
|
||||
4 => 2,
|
||||
},
|
||||
"commitIndex": 0,
|
||||
"interactions": Array [],
|
||||
"rootID": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`profiling CommitDetails should be collected for each commit: CommitDetails commitIndex: 1 1`] = `
|
||||
Object {
|
||||
"actualDurations": Map {
|
||||
3 => 2,
|
||||
4 => 2,
|
||||
5 => 2,
|
||||
2 => 16,
|
||||
1 => 16,
|
||||
},
|
||||
"commitIndex": 1,
|
||||
"interactions": Array [],
|
||||
"rootID": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`profiling CommitDetails should be collected for each commit: CommitDetails commitIndex: 2 1`] = `
|
||||
Object {
|
||||
"actualDurations": Map {
|
||||
3 => 2,
|
||||
2 => 12,
|
||||
1 => 12,
|
||||
},
|
||||
"commitIndex": 2,
|
||||
"interactions": Array [],
|
||||
"rootID": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`profiling CommitDetails should be collected for each commit: CommitDetails commitIndex: 3 1`] = `
|
||||
Object {
|
||||
"actualDurations": Map {
|
||||
2 => 10,
|
||||
1 => 10,
|
||||
},
|
||||
"commitIndex": 3,
|
||||
"interactions": Array [],
|
||||
"rootID": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`profiling FiberCommits should be collected for each rendered fiber: FiberCommits: element 2 1`] = `
|
||||
Object {
|
||||
"commitDurations": Array [
|
||||
0,
|
||||
12,
|
||||
1,
|
||||
14,
|
||||
2,
|
||||
16,
|
||||
],
|
||||
"fiberID": 2,
|
||||
"rootID": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`profiling FiberCommits should be collected for each rendered fiber: FiberCommits: element 3 1`] = `
|
||||
Object {
|
||||
"commitDurations": Array [
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
],
|
||||
"fiberID": 3,
|
||||
"rootID": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`profiling FiberCommits should be collected for each rendered fiber: FiberCommits: element 4 1`] = `
|
||||
Object {
|
||||
"commitDurations": Array [
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
],
|
||||
"fiberID": 4,
|
||||
"rootID": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`profiling FiberCommits should be collected for each rendered fiber: FiberCommits: element 5 1`] = `
|
||||
Object {
|
||||
"commitDurations": Array [
|
||||
2,
|
||||
2,
|
||||
],
|
||||
"fiberID": 5,
|
||||
"rootID": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`profiling Interactions should be collected for every traced interaction: Interactions 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"__count": 1,
|
||||
"commits": Array [
|
||||
0,
|
||||
],
|
||||
"id": 0,
|
||||
"name": "mount: one child",
|
||||
"timestamp": 0,
|
||||
},
|
||||
Object {
|
||||
"__count": 0,
|
||||
"commits": Array [
|
||||
1,
|
||||
],
|
||||
"id": 1,
|
||||
"name": "update: two children",
|
||||
"timestamp": 12,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`profiling ProfilingSummary should be collected for each commit: ProfilingSummary 1`] = `
|
||||
Object {
|
||||
"commitDurations": Array [
|
||||
16,
|
||||
12,
|
||||
10,
|
||||
],
|
||||
"commitTimes": Array [
|
||||
16,
|
||||
28,
|
||||
38,
|
||||
],
|
||||
"initialTreeBaseDurations": Map {
|
||||
1 => 14,
|
||||
2 => 14,
|
||||
3 => 2,
|
||||
4 => 2,
|
||||
},
|
||||
"interactionCount": 0,
|
||||
"rootID": 1,
|
||||
}
|
||||
`;
|
||||
@@ -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,
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,162 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`commit tree should be able to rebuild the store tree for each commit: 0: CommitTree 1`] = `
|
||||
Object {
|
||||
"nodes": Map {
|
||||
1 => Object {
|
||||
"children": Array [
|
||||
2,
|
||||
],
|
||||
"displayName": null,
|
||||
"id": 1,
|
||||
"key": null,
|
||||
"parentID": 0,
|
||||
"treeBaseDuration": 12,
|
||||
},
|
||||
2 => Object {
|
||||
"children": Array [
|
||||
3,
|
||||
],
|
||||
"displayName": "Parent",
|
||||
"id": 2,
|
||||
"key": null,
|
||||
"parentID": 1,
|
||||
"treeBaseDuration": 12,
|
||||
},
|
||||
3 => Object {
|
||||
"children": Array [],
|
||||
"displayName": "Memo(Child)",
|
||||
"id": 3,
|
||||
"key": "0",
|
||||
"parentID": 2,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
},
|
||||
"rootID": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`commit tree should be able to rebuild the store tree for each commit: 1: CommitTree 1`] = `
|
||||
Object {
|
||||
"nodes": Map {
|
||||
1 => Object {
|
||||
"children": Array [
|
||||
2,
|
||||
],
|
||||
"displayName": null,
|
||||
"id": 1,
|
||||
"key": null,
|
||||
"parentID": 0,
|
||||
"treeBaseDuration": 16,
|
||||
},
|
||||
2 => Object {
|
||||
"children": Array [
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
],
|
||||
"displayName": "Parent",
|
||||
"id": 2,
|
||||
"key": null,
|
||||
"parentID": 1,
|
||||
"treeBaseDuration": 16,
|
||||
},
|
||||
3 => Object {
|
||||
"children": Array [],
|
||||
"displayName": "Memo(Child)",
|
||||
"id": 3,
|
||||
"key": "0",
|
||||
"parentID": 2,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
4 => Object {
|
||||
"children": Array [],
|
||||
"displayName": "Memo(Child)",
|
||||
"id": 4,
|
||||
"key": "1",
|
||||
"parentID": 2,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
5 => Object {
|
||||
"children": Array [],
|
||||
"displayName": "Memo(Child)",
|
||||
"id": 5,
|
||||
"key": "2",
|
||||
"parentID": 2,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
},
|
||||
"rootID": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`commit tree should be able to rebuild the store tree for each commit: 2: 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": "0",
|
||||
"parentID": 2,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
4 => Object {
|
||||
"children": Array [],
|
||||
"displayName": "Memo(Child)",
|
||||
"id": 4,
|
||||
"key": "1",
|
||||
"parentID": 2,
|
||||
"treeBaseDuration": 2,
|
||||
},
|
||||
},
|
||||
"rootID": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`commit tree should be able to rebuild the store tree for each commit: 3: CommitTree 1`] = `
|
||||
Object {
|
||||
"nodes": Map {
|
||||
1 => Object {
|
||||
"children": Array [
|
||||
2,
|
||||
],
|
||||
"displayName": null,
|
||||
"id": 1,
|
||||
"key": null,
|
||||
"parentID": 0,
|
||||
"treeBaseDuration": 10,
|
||||
},
|
||||
2 => Object {
|
||||
"children": Array [],
|
||||
"displayName": "Parent",
|
||||
"id": 2,
|
||||
"key": null,
|
||||
"parentID": 1,
|
||||
"treeBaseDuration": 10,
|
||||
},
|
||||
},
|
||||
"rootID": 1,
|
||||
}
|
||||
`;
|
||||
@@ -1,60 +0,0 @@
|
||||
// @flow
|
||||
|
||||
describe('Profiler', () => {
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let TestUtils;
|
||||
let store;
|
||||
|
||||
const act = (callback: Function) => {
|
||||
TestUtils.act(() => {
|
||||
callback();
|
||||
});
|
||||
jest.runAllTimers(); // Flush Bridge operations
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
store = global.store;
|
||||
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
TestUtils = require('react-dom/test-utils');
|
||||
});
|
||||
|
||||
it('should start and stop profiling, handle root unmounting', async () => {
|
||||
const Parent = ({ count }) =>
|
||||
new Array(count).fill(true).map((_, index) => <Child key={index} />);
|
||||
const Child = () => <div>Hi!</div>;
|
||||
|
||||
const containerA = document.createElement('div');
|
||||
const containerB = document.createElement('div');
|
||||
|
||||
act(() => {
|
||||
ReactDOM.render(<Parent key="A" count={3} />, containerA);
|
||||
ReactDOM.render(<Parent key="B" count={2} />, containerB);
|
||||
});
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
|
||||
act(() => {
|
||||
store.startProfiling();
|
||||
});
|
||||
expect(store).toMatchSnapshot('2: profiling started');
|
||||
|
||||
act(() => {
|
||||
ReactDOM.render(<Parent key="A" count={4} />, containerA);
|
||||
ReactDOM.render(<Parent key="B" count={1} />, containerB);
|
||||
});
|
||||
expect(store).toMatchSnapshot('3: update');
|
||||
|
||||
act(() => ReactDOM.unmountComponentAtNode(containerB));
|
||||
expect(store).toMatchSnapshot('4: unmount B');
|
||||
|
||||
act(() => ReactDOM.unmountComponentAtNode(containerA));
|
||||
expect(store).toMatchSnapshot('5: unmount A');
|
||||
|
||||
act(() => {
|
||||
store.stopProfiling();
|
||||
});
|
||||
expect(store).toMatchSnapshot('6: profiling stopped');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,293 @@
|
||||
// @flow
|
||||
|
||||
import typeof ReactTestRenderer from 'react-test-renderer';
|
||||
import type Store from 'src/devtools/store';
|
||||
|
||||
describe('profiling', () => {
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let Scheduler;
|
||||
let SchedulerTracing;
|
||||
let TestRenderer: ReactTestRenderer;
|
||||
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('ProfilingSummary', () => {
|
||||
it('should be collected for each commit', async done => {
|
||||
const Parent = ({ count }) => {
|
||||
Scheduler.advanceTime(10);
|
||||
return new Array(count)
|
||||
.fill(true)
|
||||
.map((_, index) => <Child key={index} />);
|
||||
};
|
||||
const Child = () => {
|
||||
Scheduler.advanceTime(2);
|
||||
return null;
|
||||
};
|
||||
|
||||
const container = document.createElement('div');
|
||||
|
||||
utils.act(() => ReactDOM.render(<Parent count={2} />, container));
|
||||
utils.act(() => store.startProfiling());
|
||||
utils.act(() => ReactDOM.render(<Parent count={3} />, container));
|
||||
utils.act(() => ReactDOM.render(<Parent count={1} />, container));
|
||||
utils.act(() => ReactDOM.render(<Parent count={0} />, container));
|
||||
utils.act(() => store.stopProfiling());
|
||||
|
||||
let suspenseResolved = false;
|
||||
|
||||
function Suspender({ rendererID, rootID }) {
|
||||
const profilingSummary = store.profilingCache.ProfilingSummary.read({
|
||||
rendererID,
|
||||
rootID,
|
||||
});
|
||||
suspenseResolved = true;
|
||||
expect(profilingSummary).toMatchSnapshot('ProfilingSummary');
|
||||
return null;
|
||||
}
|
||||
|
||||
const rendererID = utils.getRendererID();
|
||||
const rootID = store.roots[0];
|
||||
|
||||
await utils.actSuspense(() =>
|
||||
TestRenderer.create(
|
||||
<React.Suspense fallback={null}>
|
||||
<Suspender rendererID={rendererID} rootID={rootID} />
|
||||
</React.Suspense>
|
||||
)
|
||||
);
|
||||
|
||||
expect(suspenseResolved).toBe(true);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('CommitDetails', () => {
|
||||
it('should be collected for each commit', async done => {
|
||||
const Parent = ({ count }) => {
|
||||
Scheduler.advanceTime(10);
|
||||
return new Array(count)
|
||||
.fill(true)
|
||||
.map((_, index) => <Child key={index} />);
|
||||
};
|
||||
const Child = () => {
|
||||
Scheduler.advanceTime(2);
|
||||
return null;
|
||||
};
|
||||
|
||||
const container = document.createElement('div');
|
||||
|
||||
utils.act(() => store.startProfiling());
|
||||
utils.act(() => ReactDOM.render(<Parent count={2} />, container));
|
||||
utils.act(() => ReactDOM.render(<Parent count={3} />, container));
|
||||
utils.act(() => ReactDOM.render(<Parent count={1} />, container));
|
||||
utils.act(() => ReactDOM.render(<Parent count={0} />, container));
|
||||
utils.act(() => store.stopProfiling());
|
||||
|
||||
let suspenseResolved = false;
|
||||
|
||||
function Suspender({ commitIndex, rendererID, rootID }) {
|
||||
const commitDetails = store.profilingCache.CommitDetails.read({
|
||||
commitIndex,
|
||||
rendererID,
|
||||
rootID,
|
||||
});
|
||||
suspenseResolved = true;
|
||||
expect(commitDetails).toMatchSnapshot(
|
||||
`CommitDetails commitIndex: ${commitIndex}`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const rendererID = utils.getRendererID();
|
||||
const rootID = store.roots[0];
|
||||
|
||||
for (let commitIndex = 0; commitIndex <= 3; commitIndex++) {
|
||||
suspenseResolved = false;
|
||||
await utils.actSuspense(() => {
|
||||
TestRenderer.create(
|
||||
<React.Suspense fallback={null}>
|
||||
<Suspender
|
||||
commitIndex={commitIndex}
|
||||
rendererID={rendererID}
|
||||
rootID={rootID}
|
||||
/>
|
||||
</React.Suspense>
|
||||
);
|
||||
});
|
||||
expect(suspenseResolved).toBe(true);
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('FiberCommits', () => {
|
||||
it('should be collected for each rendered fiber', async done => {
|
||||
const Parent = ({ count }) => {
|
||||
Scheduler.advanceTime(10);
|
||||
return new Array(count)
|
||||
.fill(true)
|
||||
.map((_, index) => <Child key={index} />);
|
||||
};
|
||||
const Child = () => {
|
||||
Scheduler.advanceTime(2);
|
||||
return null;
|
||||
};
|
||||
|
||||
const container = document.createElement('div');
|
||||
|
||||
utils.act(() => store.startProfiling());
|
||||
utils.act(() => ReactDOM.render(<Parent count={1} />, container));
|
||||
utils.act(() => ReactDOM.render(<Parent count={2} />, container));
|
||||
utils.act(() => ReactDOM.render(<Parent count={3} />, container));
|
||||
utils.act(() => store.stopProfiling());
|
||||
|
||||
let suspenseResolved = false;
|
||||
|
||||
function Suspender({ fiberID, rendererID, rootID }) {
|
||||
const fiberCommits = store.profilingCache.FiberCommits.read({
|
||||
fiberID,
|
||||
rendererID,
|
||||
rootID,
|
||||
});
|
||||
suspenseResolved = true;
|
||||
expect(fiberCommits).toMatchSnapshot(
|
||||
`FiberCommits: element ${fiberID}`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const rendererID = utils.getRendererID();
|
||||
const rootID = store.roots[0];
|
||||
|
||||
for (let index = 0; index < store.numElements; index++) {
|
||||
suspenseResolved = false;
|
||||
await utils.actSuspense(() => {
|
||||
const fiberID = store.getElementIDAtIndex(index);
|
||||
if (fiberID == null) {
|
||||
throw Error(`Unexpected null ID for element at index ${index}`);
|
||||
}
|
||||
TestRenderer.create(
|
||||
<React.Suspense fallback={null}>
|
||||
<Suspender
|
||||
fiberID={fiberID}
|
||||
rendererID={rendererID}
|
||||
rootID={rootID}
|
||||
/>
|
||||
</React.Suspense>
|
||||
);
|
||||
});
|
||||
expect(suspenseResolved).toBe(true);
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Interactions', () => {
|
||||
it('should be collected for every traced interaction', async done => {
|
||||
const Parent = ({ count }) => {
|
||||
Scheduler.advanceTime(10);
|
||||
return new Array(count)
|
||||
.fill(true)
|
||||
.map((_, index) => <Child key={index} />);
|
||||
};
|
||||
const Child = () => {
|
||||
Scheduler.advanceTime(2);
|
||||
return null;
|
||||
};
|
||||
|
||||
const container = document.createElement('div');
|
||||
|
||||
utils.act(() => store.startProfiling());
|
||||
utils.act(() =>
|
||||
SchedulerTracing.unstable_trace(
|
||||
'mount: one child',
|
||||
Scheduler.unstable_now(),
|
||||
() => ReactDOM.render(<Parent count={1} />, container)
|
||||
)
|
||||
);
|
||||
utils.act(() =>
|
||||
SchedulerTracing.unstable_trace(
|
||||
'update: two children',
|
||||
Scheduler.unstable_now(),
|
||||
() => ReactDOM.render(<Parent count={2} />, container)
|
||||
)
|
||||
);
|
||||
utils.act(() => store.stopProfiling());
|
||||
|
||||
let suspenseResolved = false;
|
||||
|
||||
function Suspender({ rendererID, rootID }) {
|
||||
const interactions = store.profilingCache.Interactions.read({
|
||||
rendererID,
|
||||
rootID,
|
||||
});
|
||||
suspenseResolved = true;
|
||||
expect(interactions).toMatchSnapshot('Interactions');
|
||||
return null;
|
||||
}
|
||||
|
||||
const rendererID = utils.getRendererID();
|
||||
const rootID = store.roots[0];
|
||||
|
||||
await utils.actSuspense(() =>
|
||||
TestRenderer.create(
|
||||
<React.Suspense fallback={null}>
|
||||
<Suspender rendererID={rendererID} rootID={rootID} />
|
||||
</React.Suspense>
|
||||
)
|
||||
);
|
||||
|
||||
expect(suspenseResolved).toBe(true);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove profiling data when roots are unmounted', async () => {
|
||||
const Parent = ({ count }) =>
|
||||
new Array(count).fill(true).map((_, index) => <Child key={index} />);
|
||||
const Child = () => <div>Hi!</div>;
|
||||
|
||||
const containerA = document.createElement('div');
|
||||
const containerB = document.createElement('div');
|
||||
|
||||
utils.act(() => {
|
||||
ReactDOM.render(<Parent key="A" count={3} />, containerA);
|
||||
ReactDOM.render(<Parent key="B" count={2} />, containerB);
|
||||
});
|
||||
|
||||
utils.act(() => store.startProfiling());
|
||||
|
||||
utils.act(() => {
|
||||
ReactDOM.render(<Parent key="A" count={4} />, containerA);
|
||||
ReactDOM.render(<Parent key="B" count={1} />, containerB);
|
||||
});
|
||||
|
||||
utils.act(() => ReactDOM.unmountComponentAtNode(containerB));
|
||||
|
||||
utils.act(() => ReactDOM.unmountComponentAtNode(containerA));
|
||||
|
||||
utils.act(() => store.stopProfiling());
|
||||
|
||||
// Assert all maps are empty
|
||||
store.assertExpectedRootMapSizes();
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,89 @@
|
||||
// @flow
|
||||
|
||||
import typeof TestRendererType from 'react-test-renderer';
|
||||
import type Store from 'src/devtools/store';
|
||||
|
||||
describe('commit tree', () => {
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let Scheduler;
|
||||
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');
|
||||
TestRenderer = utils.requireTestRenderer();
|
||||
});
|
||||
|
||||
it('should be able to rebuild the store tree for each commit', async done => {
|
||||
const Parent = ({ count }) => {
|
||||
Scheduler.advanceTime(10);
|
||||
return new Array(count)
|
||||
.fill(true)
|
||||
.map((_, index) => <Child key={index} />);
|
||||
};
|
||||
const Child = React.memo(function Child() {
|
||||
Scheduler.advanceTime(2);
|
||||
return null;
|
||||
});
|
||||
|
||||
const container = document.createElement('div');
|
||||
|
||||
utils.act(() => store.startProfiling());
|
||||
utils.act(() => ReactDOM.render(<Parent count={1} />, container));
|
||||
utils.act(() => ReactDOM.render(<Parent count={3} />, container));
|
||||
utils.act(() => ReactDOM.render(<Parent count={2} />, container));
|
||||
utils.act(() => ReactDOM.render(<Parent count={0} />, container));
|
||||
utils.act(() => store.stopProfiling());
|
||||
|
||||
let suspenseResolved = false;
|
||||
|
||||
function Suspender({ commitIndex, rendererID, rootID }) {
|
||||
const profilingSummary = store.profilingCache.ProfilingSummary.read({
|
||||
rendererID,
|
||||
rootID,
|
||||
});
|
||||
suspenseResolved = true;
|
||||
const commitTree = store.profilingCache.getCommitTree({
|
||||
commitIndex,
|
||||
profilingSummary,
|
||||
});
|
||||
expect(commitTree).toMatchSnapshot(`${commitIndex}: CommitTree`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const rendererID = utils.getRendererID();
|
||||
const rootID = store.roots[0];
|
||||
|
||||
for (let commitIndex = 0; commitIndex < 4; 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();
|
||||
});
|
||||
});
|
||||
@@ -1,16 +1,16 @@
|
||||
// @flow
|
||||
|
||||
import Agent from 'src/backend/agent';
|
||||
import { initBackend } from 'src/backend';
|
||||
import Bridge from 'src/bridge';
|
||||
import Store from 'src/devtools/store';
|
||||
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();
|
||||
// These files should be required (and re-reuired) before each test,
|
||||
// rather than imported at the head of the module.
|
||||
// That's because we reset modules between tests,
|
||||
// which disconnects the DevTool's cache from the current dispatcher ref.
|
||||
const Agent = require('src/backend/agent').default;
|
||||
const { initBackend } = require('src/backend');
|
||||
const Bridge = require('src/bridge').default;
|
||||
const Store = require('src/devtools/store').default;
|
||||
const { installHook } = require('src/hook');
|
||||
|
||||
// Fake timers let us flush Bridge operations between setup and assertions.
|
||||
jest.useFakeTimers();
|
||||
@@ -55,4 +55,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();
|
||||
});
|
||||
|
||||
@@ -3,24 +3,21 @@
|
||||
describe('Store', () => {
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let TestUtils;
|
||||
let agent;
|
||||
let act;
|
||||
let getRendererID;
|
||||
let store;
|
||||
|
||||
const act = (callback: Function) => {
|
||||
TestUtils.act(() => {
|
||||
callback();
|
||||
});
|
||||
jest.runAllTimers(); // Flush Bridge operations
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
agent = global.agent;
|
||||
store = global.store;
|
||||
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
TestUtils = require('react-dom/test-utils');
|
||||
|
||||
const utils = require('./utils');
|
||||
act = utils.act;
|
||||
getRendererID = utils.getRendererID;
|
||||
});
|
||||
|
||||
it('should not allow a root node to be collapsed', () => {
|
||||
@@ -281,8 +278,7 @@ describe('Store', () => {
|
||||
);
|
||||
expect(store).toMatchSnapshot('7: only third child is suspended');
|
||||
|
||||
// HACK There's only one renderer for this test
|
||||
const rendererID = Object.keys(agent._rendererInterfaces)[0];
|
||||
const rendererID = getRendererID();
|
||||
act(() =>
|
||||
agent.overrideSuspense({
|
||||
id: store.getElementIDAtIndex(4),
|
||||
@@ -673,8 +669,7 @@ describe('Store', () => {
|
||||
act(() => store.toggleIsCollapsed(store.getElementIDAtIndex(1), false));
|
||||
expect(store).toMatchSnapshot('2: expand tree');
|
||||
|
||||
// HACK There's only one renderer for this test
|
||||
const rendererID = Object.keys(agent._rendererInterfaces)[0];
|
||||
const rendererID = getRendererID();
|
||||
const suspenseID = store.getElementIDAtIndex(1);
|
||||
|
||||
act(() =>
|
||||
|
||||
@@ -5,23 +5,16 @@ const { printOwnersList } = require('./storeSerializer');
|
||||
describe('Store owners list', () => {
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let TestUtils;
|
||||
let act;
|
||||
let store;
|
||||
|
||||
const act = (callback: Function) => {
|
||||
TestUtils.act(() => {
|
||||
callback();
|
||||
});
|
||||
jest.runAllTimers(); // Flush Bridge operations
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
store = global.store;
|
||||
store.collapseNodesByDefault = false;
|
||||
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
TestUtils = require('react-dom/test-utils');
|
||||
act = require('./utils').act;
|
||||
});
|
||||
|
||||
it('should drill through intermediate components', () => {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import Store from 'src/devtools/store';
|
||||
|
||||
// test() is part of Jest's serializer API
|
||||
export function test(maybeStore) {
|
||||
return maybeStore instanceof Store;
|
||||
// It's important to lazy-require the Store rather than imported at the head of the module.
|
||||
// Because we reset modules between tests, different Store implementations will be used for each test.
|
||||
// Unfortunately Jest does not reset its own serializer modules.
|
||||
return maybeStore instanceof require('src/devtools/store').default;
|
||||
}
|
||||
|
||||
// print() is part of Jest's serializer API
|
||||
@@ -70,9 +71,9 @@ export function printStore(store, includeWeight = false) {
|
||||
);
|
||||
}
|
||||
|
||||
if (store.roots.length === 0) {
|
||||
store.assertEmptyMaps();
|
||||
}
|
||||
// If roots have been unmounted, verify that they've been removed from maps.
|
||||
// This helps ensure the Store doesn't leak memory.
|
||||
store.assertExpectedRootMapSizes();
|
||||
|
||||
return snapshotLines.join('\n');
|
||||
}
|
||||
|
||||
@@ -3,18 +3,11 @@
|
||||
describe('StoreStress (Sync Mode)', () => {
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let TestUtils;
|
||||
let act;
|
||||
let bridge;
|
||||
let store;
|
||||
let print;
|
||||
|
||||
const act = (callback: Function) => {
|
||||
TestUtils.act(() => {
|
||||
callback();
|
||||
});
|
||||
jest.runAllTimers(); // Flush Bridge operations
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
bridge = global.bridge;
|
||||
store = global.store;
|
||||
@@ -22,7 +15,7 @@ describe('StoreStress (Sync Mode)', () => {
|
||||
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
TestUtils = require('react-dom/test-utils');
|
||||
act = require('./utils').act;
|
||||
|
||||
print = require('./storeSerializer').print;
|
||||
});
|
||||
|
||||
@@ -3,19 +3,11 @@
|
||||
describe('StoreStressConcurrent', () => {
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let TestUtils;
|
||||
let act;
|
||||
let bridge;
|
||||
let store;
|
||||
let print;
|
||||
|
||||
const act = (callback: Function) => {
|
||||
TestUtils.act(() => {
|
||||
callback();
|
||||
});
|
||||
jest.advanceTimersByTime(1000); // Flush rendering and Suspense
|
||||
jest.runAllTimers(); // Flush Bridge operations
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
bridge = global.bridge;
|
||||
store = global.store;
|
||||
@@ -23,7 +15,7 @@ describe('StoreStressConcurrent', () => {
|
||||
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
TestUtils = require('react-dom/test-utils');
|
||||
act = require('./utils').act;
|
||||
|
||||
print = require('./storeSerializer').print;
|
||||
});
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
// @flow
|
||||
|
||||
import typeof ReactTestRenderer from 'react-test-renderer';
|
||||
|
||||
export function act(callback: Function): void {
|
||||
const TestUtils = require('react-dom/test-utils');
|
||||
TestUtils.act(() => {
|
||||
callback();
|
||||
});
|
||||
|
||||
// Flush Bridge operations
|
||||
jest.runAllTimers();
|
||||
}
|
||||
|
||||
export async function actSuspense(
|
||||
callback: Function,
|
||||
numTimesToFlush: number = 1
|
||||
): Promise<void> {
|
||||
const TestUtils = require('react-dom/test-utils');
|
||||
const Scheduler = require('scheduler');
|
||||
|
||||
// $FlowFixMe Flow doens't know about "await act()" yet
|
||||
await TestUtils.act(async () => {
|
||||
callback();
|
||||
|
||||
// Resolve pending suspense promises
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
// 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 {
|
||||
// Mock React's timing information so that test runs are predictable.
|
||||
jest.mock('scheduler', () =>
|
||||
// $FlowFixMe Flow does not konw about requireActual
|
||||
require.requireActual('scheduler/unstable_mock')
|
||||
);
|
||||
|
||||
// DevTools itself uses performance.now() to offset commit times
|
||||
// so they appear relative to when profiling was started in the UI.
|
||||
jest.spyOn(performance, 'now').mockImplementation(
|
||||
// $FlowFixMe Flow does not konw about requireActual
|
||||
require.requireActual('scheduler/unstable_mock').unstable_now
|
||||
);
|
||||
}
|
||||
|
||||
export function getRendererID(): number {
|
||||
if (global.agent == null) {
|
||||
throw Error('Agent unavailable.');
|
||||
}
|
||||
const ids = Object.keys(global.agent._rendererInterfaces);
|
||||
if (ids.length !== 1) {
|
||||
throw Error('Multiple renderers attached.');
|
||||
}
|
||||
return parseInt(ids[0], 10);
|
||||
}
|
||||
|
||||
export function requireTestRenderer(): ReactTestRenderer {
|
||||
let hook;
|
||||
try {
|
||||
// Hide the hook before requiring TestRenderer, so we don't end up with a loop.
|
||||
hook = global.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
delete global.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
|
||||
return require('react-test-renderer');
|
||||
} finally {
|
||||
global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = hook;
|
||||
}
|
||||
}
|
||||
+41
-18
@@ -192,30 +192,49 @@ export default class Store extends EventEmitter {
|
||||
}
|
||||
|
||||
// This is only used in tests to avoid memory leaks.
|
||||
assertEmptyMaps() {
|
||||
this.assertEmptyMap(this._idToElement, '_idToElement');
|
||||
this.assertEmptyMap(this._ownersMap, '_ownersMap');
|
||||
this.assertEmptyMap(
|
||||
this._profilingOperationsByRootID,
|
||||
'_profilingOperationsByRootID'
|
||||
assertExpectedRootMapSizes() {
|
||||
if (this.roots.length === 0) {
|
||||
// The only safe time to assert these maps are empty is when the store is empty.
|
||||
this.assertMapSizeMatchesRootCount(this._idToElement, '_idToElement');
|
||||
this.assertMapSizeMatchesRootCount(this._ownersMap, '_ownersMap');
|
||||
|
||||
// These maps will be empty unless profiling mode has been started.
|
||||
// After this, their size should always match the number of roots,
|
||||
// but unless we want to track additional metadata about profiling history,
|
||||
// the only safe time to assert this is when the store is empty.
|
||||
this.assertMapSizeMatchesRootCount(
|
||||
this._profilingOperationsByRootID,
|
||||
'_profilingOperationsByRootID'
|
||||
);
|
||||
this.assertMapSizeMatchesRootCount(
|
||||
this._profilingScreenshotsByRootID,
|
||||
'_profilingScreenshotsByRootID'
|
||||
);
|
||||
this.assertMapSizeMatchesRootCount(
|
||||
this._profilingSnapshotsByRootID,
|
||||
'_profilingSnapshotsByRootID'
|
||||
);
|
||||
}
|
||||
|
||||
// These maps should always be the same size as the number of roots
|
||||
this.assertMapSizeMatchesRootCount(
|
||||
this._rootIDToCapabilities,
|
||||
'_rootIDToCapabilities'
|
||||
);
|
||||
this.assertEmptyMap(
|
||||
this._profilingScreenshotsByRootID,
|
||||
'_profilingScreenshotsByRootID'
|
||||
this.assertMapSizeMatchesRootCount(
|
||||
this._rootIDToRendererID,
|
||||
'_rootIDToRendererID'
|
||||
);
|
||||
this.assertEmptyMap(
|
||||
this._profilingSnapshotsByRootID,
|
||||
'_profilingSnapshotsByRootID'
|
||||
);
|
||||
this.assertEmptyMap(this._rootIDToCapabilities, '_rootIDToCapabilities');
|
||||
this.assertEmptyMap(this._rootIDToRendererID, '_rootIDToRendererID');
|
||||
}
|
||||
|
||||
// This is only used in tests to avoid memory leaks.
|
||||
assertEmptyMap(map: Map<any, any>, mapName: string) {
|
||||
if (map.size !== 0) {
|
||||
assertMapSizeMatchesRootCount(map: Map<any, any>, mapName: string) {
|
||||
const expectedSize = this.roots.length;
|
||||
if (map.size !== expectedSize) {
|
||||
throw new Error(
|
||||
`Expected ${mapName} to be empty, got ${map.size}: ${inspect(map, {
|
||||
`Expected ${mapName} to contain ${expectedSize} items, but it contains ${
|
||||
map.size
|
||||
} items\n\n${inspect(map, {
|
||||
depth: 20,
|
||||
})}`
|
||||
);
|
||||
@@ -833,6 +852,10 @@ export default class Store extends EventEmitter {
|
||||
weight: 0,
|
||||
});
|
||||
|
||||
if (this._isProfiling) {
|
||||
this._profilingSnapshotsByRootID.set(id, new Map());
|
||||
}
|
||||
|
||||
haveRootsChanged = true;
|
||||
} else {
|
||||
parentID = ((operations[i]: any): number);
|
||||
|
||||
@@ -9807,20 +9807,20 @@ react-color@^2.11.7:
|
||||
reactcss "^1.2.0"
|
||||
tinycolor2 "^1.4.1"
|
||||
|
||||
react-dom@0.0.0-fb28e9048:
|
||||
version "0.0.0-fb28e9048"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-0.0.0-fb28e9048.tgz#d4276f11a16f74b2cef0700bb5a8eefaee3761d2"
|
||||
integrity sha512-C4bdIWINwV4pF1P0oN7fDAq+vTHOW2rxQLvGE5rDslGrK/DJ7bM5PrD81AcPkdgUnK3O/JvIkeGIvRcRgYemSw==
|
||||
react-dom@^0.0.0-6da04b5d8:
|
||||
version "0.0.0-6da04b5d8"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-0.0.0-6da04b5d8.tgz#ee78e45a40771560c756b8fc7f1fa213f2179ebe"
|
||||
integrity sha512-6oyfkucrweqCB5XyLsfEnPSWhvkFnttutkU9uUQovLAljuazgpAjvBy6MBGSewKptqch2OTNwopqvR3QUMD8AQ==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
scheduler "0.0.0-fb28e9048"
|
||||
scheduler "0.0.0-6da04b5d8"
|
||||
|
||||
react-is@0.0.0-fb28e9048:
|
||||
version "0.0.0-fb28e9048"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-0.0.0-fb28e9048.tgz#f2bdccfea1005ea5234719de192f25504ff4244e"
|
||||
integrity sha512-McCC7GvLXMcY29GIiJ56bvuXFVTIwBr7wcz93TcGvUN23a7l0h9KjCwUjGQfEpBRG3pbeBtMxDc6GiXuVbK6Lg==
|
||||
react-is@0.0.0-6da04b5d8, react-is@^0.0.0-6da04b5d8:
|
||||
version "0.0.0-6da04b5d8"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-0.0.0-6da04b5d8.tgz#ded1f02e9f1e2b8456812d0d45c341d70b7bf7db"
|
||||
integrity sha512-+Df3meqx+XUir+3dCqiHNAHruwmOAgXVp3TYmlUvgtPNBu+LF0OdchTMZ/xMUok1gYVoe4l/xhfs9CTEPkWt3g==
|
||||
|
||||
react-is@^16.8.1:
|
||||
version "16.8.3"
|
||||
@@ -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-6da04b5d8:
|
||||
version "0.0.0-6da04b5d8"
|
||||
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-0.0.0-6da04b5d8.tgz#01bed04c5a4cf22339f0ae3b23f89bb45e9a8f2a"
|
||||
integrity sha512-yDt5RPDXLZXTqlWS0jXMJ1IyS1e/UZXSr0L8bG0UCsna4T7A3HIYR2zChlydGXsxjGMZntKqEfm27hUhFgC06Q==
|
||||
dependencies:
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
react-is "0.0.0-6da04b5d8"
|
||||
scheduler "0.0.0-6da04b5d8"
|
||||
|
||||
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"
|
||||
@@ -9850,15 +9860,15 @@ react-window@^1.8.0:
|
||||
"@babel/runtime" "^7.0.0"
|
||||
memoize-one ">=3.1.1 <6"
|
||||
|
||||
react@0.0.0-fb28e9048:
|
||||
version "0.0.0-fb28e9048"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-0.0.0-fb28e9048.tgz#f3488c3e5cac772b2731d6d4bb3484410821254c"
|
||||
integrity sha512-6zsjgsy9hcKC41T6X6SMbyf3fJ2VadmUxDgEXUVAJ2qV9q4SP0mnK4ttDfOwDvlFNbisqtS5HbS0hnWO/8iKxQ==
|
||||
react@^0.0.0-6da04b5d8:
|
||||
version "0.0.0-6da04b5d8"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-0.0.0-6da04b5d8.tgz#583d81f73b26771da41170a5042a5ab0bdcfe37a"
|
||||
integrity sha512-8hXBHwDCKxSVFqj5Kb4OskZz7//2fx2IpUnYyukYV8qyAHlXr0qUl3GxwuryhdPzJHlzi776WjN0YEmEEANhYA==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
scheduler "0.0.0-fb28e9048"
|
||||
scheduler "0.0.0-6da04b5d8"
|
||||
|
||||
reactcss@^1.2.0:
|
||||
version "1.2.3"
|
||||
@@ -10514,10 +10524,10 @@ sax@>=0.6.0, sax@^1.2.4:
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
||||
|
||||
scheduler@0.0.0-fb28e9048:
|
||||
version "0.0.0-fb28e9048"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.0.0-fb28e9048.tgz#5e7e37a44eb69204936a3ae67f187947980438a8"
|
||||
integrity sha512-2s3rauGawjL8fGNLWHv5NJkUn8y1IOwAtptE6en0IRUcz33IZQyKlBlFpwdAKyarGScRL6p9FpcLYorXZCaDuw==
|
||||
scheduler@0.0.0-6da04b5d8, scheduler@^0.0.0-6da04b5d8:
|
||||
version "0.0.0-6da04b5d8"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.0.0-6da04b5d8.tgz#5e0ec65c2b0a7f05ffdc5522fc3a6d95b693e5c9"
|
||||
integrity sha512-upTrWBZvk4IjMsC/AcRpgCwjnSQl8i78+07KmcndqWOnWp7s4wauowWXhyswP9vucLtZaN5ussFdM3d7dXcTkw==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
Reference in New Issue
Block a user