@samitouri / QOS-React / commits / da1e5776b1

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