Added a second Profiling test (for CommitDetails) and fixed some module reset prolems
Brian Vaughn committed
May 4, 2019 at 13:11 UTC
da1e5776b1491177c59d68e7723ad0e787e7fc49
7 files changed
+185
-60
package.json
-1
@@ -23,7 +23,6 @@
23
"<rootDir>/src/__tests__/setupTests"
24
],
25
"snapshotSerializers": [
26
- "<rootDir>/src/__tests__/profilingSummarySerializer",
26
"<rootDir>/src/__tests__/storeSerializer"
27
],
28
"testMatch": [
src/__tests__/__snapshots__/profiling-test.js.snap
+82
-25
@@ -1,5 +1,74 @@
1
// Jest Snapshot v1, https://goo.gl/fbAQLP
2
3
+exports[`profiling commitDetails should be collected for each commit: 1: mount 1`] = `
4
+[root]
5
+ ▾ <Parent key="A">
6
+ <Child key="0">
7
+ <Child key="1">
8
+`;
9
+
10
+exports[`profiling commitDetails should be collected for each commit: 2: add child 1`] = `
11
+[root]
12
+ ▾ <Parent key="A">
13
+ <Child key="0">
14
+ <Child key="1">
15
+ <Child key="2">
16
+`;
17
+
18
+exports[`profiling commitDetails should be collected for each commit: 3: remove children 1`] = `
19
+[root]
20
+ ▾ <Parent key="A">
21
+ <Child key="0">
22
+`;
23
+
24
+exports[`profiling commitDetails should be collected for each commit: 4: profiling stopped 1`] = `
25
+[root]
26
+ ▾ <Parent key="A">
27
+ <Child key="0">
28
+`;
29
+
30
+exports[`profiling commitDetails should be collected for each commit: 5: CommitDetails: mount 1`] = `
31
+Object {
32
+ "actualDurations": Map {
33
+ 1 => 14,
34
+ 2 => 14,
35
+ 3 => 2,
36
+ 4 => 2,
37
+ },
38
+ "commitIndex": 0,
39
+ "interactions": Array [],
40
+ "rootID": 1,
41
+}
42
+`;
43
+
44
+exports[`profiling commitDetails should be collected for each commit: 6: CommitDetails: add child 1`] = `
45
+Object {
46
+ "actualDurations": Map {
47
+ 3 => 2,
48
+ 4 => 2,
49
+ 5 => 2,
50
+ 2 => 16,
51
+ 1 => 16,
52
+ },
53
+ "commitIndex": 1,
54
+ "interactions": Array [],
55
+ "rootID": 1,
56
+}
57
+`;
58
+
59
+exports[`profiling commitDetails should be collected for each commit: 7: CommitDetails: remove children 1`] = `
60
+Object {
61
+ "actualDurations": Map {
62
+ 3 => 2,
63
+ 2 => 12,
64
+ 1 => 12,
65
+ },
66
+ "commitIndex": 2,
67
+ "interactions": Array [],
68
+ "rootID": 1,
69
+}
70
+`;
71
+
72
exports[`profiling profilingSummary should be collected for each commit: 1: mount 1`] = `
73
[root]
74
▾ <Parent key="A">
@@ -28,34 +97,22 @@ exports[`profiling profilingSummary should be collected for each commit: 4: prof
97
`;
98
99
exports[`profiling profilingSummary should be collected for each commit: ProfilingSummary 1`] = `
31
-{
32
- "rootID": 1,
33
- "commitDurations": [
100
+Object {
101
+ "commitDurations": Array [
102
16,
35
- 12
103
+ 12,
104
],
37
- "commitTimes": [
105
+ "commitTimes": Array [
106
16,
39
- 28
107
+ 28,
108
],
41
- "initialTreeBaseDurations": [
42
- [
43
- 1,
44
- 14
45
- ],
46
- [
47
- 2,
48
- 14
49
- ],
50
- [
51
- 3,
52
- 2
53
- ],
54
- [
55
- 4,
56
- 2
57
- ]
58
- ],
59
- "interactionCount": 0
109
+ "initialTreeBaseDurations": Map {
110
+ 1 => 14,
111
+ 2 => 14,
112
+ 3 => 2,
113
+ 4 => 2,
114
+ },
115
+ "interactionCount": 0,
116
+ "rootID": 1,
117
}
118
`;
src/__tests__/profiling-test.js
+87
@@ -76,6 +76,93 @@ describe('profiling', () => {
76
});
77
});
78
79
+ describe('commitDetails', () => {
80
+ it('should be collected for each commit', async done => {
81
+ const Parent = ({ count }) => {
82
+ Scheduler.advanceTime(10);
83
+ return new Array(count)
84
+ .fill(true)
85
+ .map((_, index) => <Child key={index} />);
86
+ };
87
+ const Child = () => {
88
+ Scheduler.advanceTime(2);
89
+ return null;
90
+ };
91
+
92
+ const container = document.createElement('div');
93
+
94
+ utils.act(() => store.startProfiling());
95
+
96
+ utils.act(() => ReactDOM.render(<Parent key="A" count={2} />, container));
97
+ expect(store).toMatchSnapshot('1: mount');
98
+
99
+ utils.act(() => ReactDOM.render(<Parent key="A" count={3} />, container));
100
+ expect(store).toMatchSnapshot('2: add child');
101
+
102
+ utils.act(() => ReactDOM.render(<Parent key="A" count={1} />, container));
103
+ expect(store).toMatchSnapshot('3: remove children');
104
+
105
+ utils.act(() => store.stopProfiling());
106
+ expect(store).toMatchSnapshot('4: profiling stopped');
107
+
108
+ let commitDetails;
109
+ function Suspender({ commitIndex, rendererID, rootID }) {
110
+ commitDetails = store.profilingCache.CommitDetails.read({
111
+ commitIndex,
112
+ rendererID,
113
+ rootID,
114
+ });
115
+ return null;
116
+ }
117
+
118
+ const rendererID = utils.getRendererID();
119
+ const rootID = store.roots[0];
120
+
121
+ await utils.actSuspense(() =>
122
+ TestRenderer.create(
123
+ <React.Suspense fallback={null}>
124
+ <Suspender
125
+ commitIndex={0}
126
+ rendererID={rendererID}
127
+ rootID={rootID}
128
+ />
129
+ </React.Suspense>
130
+ )
131
+ );
132
+ expect(commitDetails).toMatchSnapshot('5: CommitDetails: mount');
133
+
134
+ await utils.actSuspense(() =>
135
+ TestRenderer.create(
136
+ <React.Suspense fallback={null}>
137
+ <Suspender
138
+ commitIndex={1}
139
+ rendererID={rendererID}
140
+ rootID={rootID}
141
+ />
142
+ </React.Suspense>
143
+ )
144
+ );
145
+ expect(commitDetails).toMatchSnapshot('6: CommitDetails: add child');
146
+
147
+ await utils.actSuspense(() =>
148
+ TestRenderer.create(
149
+ <React.Suspense fallback={null}>
150
+ <Suspender
151
+ commitIndex={2}
152
+ rendererID={rendererID}
153
+ rootID={rootID}
154
+ />
155
+ </React.Suspense>
156
+ )
157
+ );
158
+ expect(commitDetails).toMatchSnapshot(
159
+ '7: CommitDetails: remove children'
160
+ );
161
+
162
+ done();
163
+ });
164
+ });
165
+
166
it('should remove profiling data when roots are unmounted', async () => {
167
const Parent = ({ count }) =>
168
new Array(count).fill(true).map((_, index) => <Child key={index} />);
src/__tests__/profilingSummarySerializer.js
deleted
-25
@@ -1,25 +0,0 @@
1
-// test() is part of Jest's serializer API
2
-export function test(maybeProfilingSummary) {
3
- return (
4
- typeof maybeProfilingSummary === 'object' &&
5
- maybeProfilingSummary !== null &&
6
- typeof maybeProfilingSummary.rootID === 'number' &&
7
- Array.isArray(maybeProfilingSummary.commitDurations) &&
8
- Array.isArray(maybeProfilingSummary.commitTimes) &&
9
- typeof maybeProfilingSummary.initialTreeBaseDurations === 'object' &&
10
- maybeProfilingSummary.initialTreeBaseDurations !== null &&
11
- typeof maybeProfilingSummary.interactionCount === 'number'
12
- );
13
-}
14
-
15
-// print() is part of Jest's serializer API
16
-export function print(profilingSummary, serialize, indent) {
17
- return JSON.stringify(
18
- {
19
- ...profilingSummary,
20
- initialTreeBaseDurations: [...profilingSummary.initialTreeBaseDurations],
21
- },
22
- null,
23
- 2
24
- );
25
-}
src/__tests__/setupTests.js
+10
-6
@@ -1,13 +1,17 @@
1
// @flow
2
3
-import Agent from 'src/backend/agent';
4
-import { initBackend } from 'src/backend';
5
-import Bridge from 'src/bridge';
6
-import Store from 'src/devtools/store';
7
-import { installHook } from 'src/hook';
8
-
3
const env = jasmine.getEnv();
4
env.beforeEach(() => {
5
+ // These files should be required (and re-reuired) before each test,
6
+ // rather than imported at the head of the module.
7
+ // That's because we reset modules between tests,
8
+ // which disconnects the DevTool's cache from the current dispatcher ref.
9
+ const Agent = require('src/backend/agent').default;
10
+ const { initBackend } = require('src/backend');
11
+ const Bridge = require('src/bridge').default;
12
+ const Store = require('src/devtools/store').default;
13
+ const { installHook } = require('src/hook');
14
+
15
// Fake timers let us flush Bridge operations between setup and assertions.
16
jest.useFakeTimers();
17
src/__tests__/storeSerializer.js
+4
-3
@@ -1,8 +1,9 @@
1
-import Store from 'src/devtools/store';
2
-
1
// test() is part of Jest's serializer API
2
export function test(maybeStore) {
5
- return maybeStore instanceof Store;
3
+ // It's important to lazy-require the Store rather than imported at the head of the module.
4
+ // Because we reset modules between tests, different Store implementations will be used for each test.
5
+ // Unfortunately Jest does not reset its own serializer modules.
6
+ return maybeStore instanceof require('src/devtools/store').default;
7
}
8
9
// print() is part of Jest's serializer API
src/devtools/cache.js
+2
@@ -49,6 +49,8 @@ const Pending = 0;
49
const Resolved = 1;
50
const Rejected = 2;
51
52
+// TODO This file isn't being re-imported between tests it seems, so it's getting disconnected
53
+
54
const ReactCurrentDispatcher = (React: any)
55
.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher;
56