@samitouri / QOS-React-2 / commits / fc33f12bde

Remove unstable scheduler/tracing API (#20037)

Brian Vaughn committed Apr 26, 2021 at 19:16 UTC fc33f12bdee1d0ffbcc83d25199cdf4d47252736
94 files changed +2428 -10871
.codesandbox/ci.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "packages": ["packages/react", "packages/react-dom", "packages/scheduler"],
3 - "buildCommand": "build --type=NODE react/index,react-dom/index,react-dom/server,react-dom/test-utils,scheduler/index,scheduler/unstable_no_dom,scheduler/tracing,react/jsx-runtime,react/jsx-dev-runtime",
3 + "buildCommand": "build --type=NODE react/index,react-dom/index,react-dom/server,react-dom/test-utils,scheduler/index,scheduler/unstable_no_dom,react/jsx-runtime,react/jsx-dev-runtime",
4 "publishDirectory": {
5 "react": "build/node_modules/react",
6 "react-dom": "build/node_modules/react-dom",
fixtures/tracing/index.html deleted
-83
@@ -1,83 +0,0 @@
1 -<!DOCTYPE html>
2 -<html style="width: 100%; height: 100%;">
3 - <head>
4 - <meta charset="utf-8">
5 - <title>Test tracing UMD</title>
6 - <style>
7 - body {
8 - font-family: sans-serif;
9 - }
10 - ol {
11 - display: inline-flex;
12 - flex-direction: column;
13 - align-items: flex-start;
14 - }
15 - li {
16 - background-color: #F7F7F7;
17 - border: solid #CCC 0.125rem;
18 - margin-bottom: 0.5rem;
19 - border-radius: 0.25rem;
20 - padding: 0.5rem;
21 - }
22 - li:after {
23 - content: attr(data-value);
24 - margin-left: 0.25rem;
25 - }
26 - .correct {
27 - border-color: #0C0;
28 - border-style: solid;
29 - background: #EFE;
30 - }
31 - .incorrect {
32 - border-color: #F00;
33 - border-style: dashed;
34 - background-color: #FEE;
35 - }
36 - </style>
37 - </head>
38 - <body>
39 - <h1>Test tracing UMD</h1>
40 - <p>
41 - This fixture tests that the new tracing API is accessible via UMD build using the UMD shim.
42 - It does not exhaustively test API functionality, only that the forwarded methods can be called.
43 - </p>
44 - <p>
45 - Before running the tests below, check the console to make sure there are no errors.
46 - </p>
47 - <h3>
48 - Tests
49 - <button id="run-test-button" onClick="runAllTests()">Run all tests</button>
50 - </h3>
51 - <ol>
52 - <li id="checkSchedulerAPI" data-value="...">
53 - <strong>Test scheduler API</strong>
54 - </li>
55 - <li id="checkSchedulerTracingAPI" data-value="...">
56 - <strong>Test tracing API</strong>
57 - </li>
58 - <li id="checkSchedulerTracingSubscriptionsAPI" data-value="...">
59 - <strong>Test tracing subscriptions API</strong>
60 - </li>
61 - <li id="checkEndToEndIntegration" data-value="...">
62 - <strong>Test end-to-end integration</strong>
63 - </li>
64 - </ol>
65 -
66 - <script>
67 - if (window.location.search.includes('puppeteer=true')) {
68 - // Collocated calls to performance.now() often yield different values in Puppeteer.
69 - // This causes the Scheduler API test to fail.
70 - // For the purposes of our automated release scripts,
71 - // Coerce tests to use Date.now() instead to reduce the chances of a false positive.
72 - window.performance = {now: Date.now};
73 - }
74 - </script>
75 -
76 - <!-- Load the tracing API before react to test that it's lazily evaluated -->
77 - <script src="../../build/node_modules/scheduler/umd/scheduler.development.js"></script>
78 - <script src="../../build/node_modules/scheduler/umd/scheduler-tracing.development.js"></script>
79 - <script src="../../build/node_modules/react/umd/react.development.js"></script>
80 - <script src="../../build/node_modules/react-dom/umd/react-dom.development.js"></script>
81 - <script src="./script.js"></script>
82 - </body>
83 -</html>
fixtures/tracing/script.js deleted
-206
@@ -1,206 +0,0 @@
1 -function runTest(listItem, callback) {
2 - try {
3 - callback();
4 - listItem.className = 'correct';
5 - listItem.setAttribute('data-value', 'All checks pass');
6 - } catch (error) {
7 - listItem.className = 'incorrect';
8 - listItem.setAttribute('data-value', error);
9 - }
10 -}
11 -
12 -function runAllTests() {
13 - try {
14 - checkSchedulerAPI();
15 - } finally {
16 - try {
17 - checkSchedulerTracingAPI();
18 - } finally {
19 - try {
20 - checkSchedulerTracingSubscriptionsAPI();
21 - } finally {
22 - checkEndToEndIntegration();
23 - }
24 - }
25 - }
26 -}
27 -
28 -function checkSchedulerAPI() {
29 - runTest(document.getElementById('checkSchedulerAPI'), () => {
30 - if (
31 - typeof Scheduler === 'undefined' ||
32 - typeof Scheduler.unstable_now !== 'function' ||
33 - typeof Scheduler.unstable_scheduleCallback !== 'function' ||
34 - typeof Scheduler.unstable_cancelCallback !== 'function'
35 - ) {
36 - throw 'API is not defined';
37 - }
38 -
39 - const abs = Math.abs(Scheduler.unstable_now() - performance.now());
40 - if (typeof abs !== 'number' || Number.isNaN(abs) || abs > 5) {
41 - throw 'API does not work';
42 - }
43 -
44 - // There is no real way to verify that the two APIs are connected.
45 - });
46 -}
47 -
48 -function checkSchedulerTracingAPI() {
49 - runTest(document.getElementById('checkSchedulerTracingAPI'), () => {
50 - if (
51 - typeof SchedulerTracing === 'undefined' ||
52 - typeof SchedulerTracing.unstable_clear !== 'function' ||
53 - typeof SchedulerTracing.unstable_getCurrent !== 'function' ||
54 - typeof SchedulerTracing.unstable_getThreadID !== 'function' ||
55 - typeof SchedulerTracing.unstable_trace !== 'function' ||
56 - typeof SchedulerTracing.unstable_wrap !== 'function'
57 - ) {
58 - throw 'API is not defined';
59 - }
60 -
61 - try {
62 - let interactionsSet;
63 - SchedulerTracing.unstable_trace('test', 123, () => {
64 - interactionsSet = SchedulerTracing.unstable_getCurrent();
65 - });
66 - if (interactionsSet.size !== 1) {
67 - throw null;
68 - }
69 - const interaction = Array.from(interactionsSet)[0];
70 - if (interaction.name !== 'test' || interaction.timestamp !== 123) {
71 - throw null;
72 - }
73 - } catch (error) {
74 - throw 'API does not work';
75 - }
76 -
77 - const ForwardedSchedulerTracing =
78 - React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing;
79 -
80 - if (
81 - SchedulerTracing.unstable_getThreadID() ===
82 - ForwardedSchedulerTracing.unstable_getThreadID()
83 - ) {
84 - throw 'API forwarding is broken';
85 - }
86 - });
87 -}
88 -
89 -function checkSchedulerTracingSubscriptionsAPI() {
90 - runTest(
91 - document.getElementById('checkSchedulerTracingSubscriptionsAPI'),
92 - () => {
93 - if (
94 - typeof SchedulerTracing === 'undefined' ||
95 - typeof SchedulerTracing.unstable_subscribe !== 'function' ||
96 - typeof SchedulerTracing.unstable_unsubscribe !== 'function'
97 - ) {
98 - throw 'API is not defined';
99 - }
100 -
101 - const onInteractionScheduledWorkCompletedCalls = [];
102 - const onInteractionTracedCalls = [];
103 - const onWorkCanceledCalls = [];
104 - const onWorkScheduledCalls = [];
105 - const onWorkStartedCalls = [];
106 - const onWorkStoppedCalls = [];
107 - const subscriber = {
108 - onInteractionScheduledWorkCompleted: (...args) =>
109 - onInteractionScheduledWorkCompletedCalls.push(args),
110 - onInteractionTraced: (...args) => onInteractionTracedCalls.push(args),
111 - onWorkCanceled: (...args) => onWorkCanceledCalls.push(args),
112 - onWorkScheduled: (...args) => onWorkScheduledCalls.push(args),
113 - onWorkStarted: (...args) => onWorkStartedCalls.push(args),
114 - onWorkStopped: (...args) => onWorkStoppedCalls.push(args),
115 - };
116 -
117 - try {
118 - SchedulerTracing.unstable_subscribe(subscriber);
119 - SchedulerTracing.unstable_trace('foo', 123, () => {});
120 - SchedulerTracing.unstable_unsubscribe(subscriber);
121 - if (onInteractionTracedCalls.length !== 1) {
122 - throw null;
123 - }
124 - const interaction = onInteractionTracedCalls[0][0];
125 - if (interaction.name !== 'foo' || interaction.timestamp !== 123) {
126 - throw null;
127 - }
128 - SchedulerTracing.unstable_trace('bar', 456, () => {});
129 - if (onInteractionTracedCalls.length !== 1) {
130 - throw null;
131 - }
132 - } catch (error) {
133 - throw 'API does not forward methods';
134 - }
135 -
136 - const ForwardedSchedulerTracing =
137 - React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
138 - .SchedulerTracing;
139 -
140 - try {
141 - ForwardedSchedulerTracing.unstable_subscribe(subscriber);
142 - SchedulerTracing.unstable_trace('foo', 123, () => {});
143 - ForwardedSchedulerTracing.unstable_trace('bar', 456, () => {});
144 - SchedulerTracing.unstable_unsubscribe(subscriber);
145 - if (onInteractionTracedCalls.length !== 3) {
146 - throw null;
147 - }
148 - const interactionFoo = onInteractionTracedCalls[1][0];
149 - const interactionBar = onInteractionTracedCalls[2][0];
150 - if (
151 - interactionFoo.name !== 'foo' ||
152 - interactionFoo.timestamp !== 123 ||
153 - interactionBar.name !== 'bar' ||
154 - interactionBar.timestamp !== 456
155 - ) {
156 - throw null;
157 - }
158 - ForwardedSchedulerTracing.unstable_trace('baz', 789, () => {});
159 - if (onInteractionTracedCalls.length !== 3) {
160 - throw null;
161 - }
162 - } catch (error) {
163 - throw 'API forwarding is broken';
164 - }
165 - }
166 - );
167 -}
168 -
169 -function checkEndToEndIntegration() {
170 - runTest(document.getElementById('checkEndToEndIntegration'), () => {
171 - try {
172 - const onRenderCalls = [];
173 - const onRender = (...args) => onRenderCalls.push(args);
174 - const container = document.createElement('div');
175 -
176 - SchedulerTracing.unstable_trace('render', 123, () => {
177 - ReactDOM.render(
178 - React.createElement(
179 - React.Profiler,
180 - {id: 'profiler', onRender},
181 - React.createElement('div', null, 'hi')
182 - ),
183 - container
184 - );
185 - });
186 -
187 - if (container.textContent !== 'hi') {
188 - throw null;
189 - }
190 -
191 - if (onRenderCalls.length !== 1) {
192 - throw null;
193 - }
194 - const call = onRenderCalls[0];
195 - if (call.length !== 7) {
196 - throw null;
197 - }
198 - const interaction = Array.from(call[6])[0];
199 - if (interaction.name !== 'render' || interaction.timestamp !== 123) {
200 - throw null;
201 - }
202 - } catch (error) {
203 - throw 'End to end integration is broken';
204 - }
205 - });
206 -}
fixtures/tracing/test.html deleted
-21
@@ -1,21 +0,0 @@
1 -<!DOCTYPE html>
2 -<html style="width: 100%; height: 100%;">
3 - <head>
4 - <meta charset="utf-8">
5 - <title>Test tracing UMD</title>
6 - <style>
7 - body {
8 - font-family: sans-serif;
9 - }
10 - </style>
11 - </head>
12 - <body>
13 - <div id="root"></div>
14 - <!-- Load the tracing API before react to test that it's lazily evaluated -->
15 - <script src="../../build/node_modules/scheduler/umd/scheduler.development.js"></script>
16 - <script src="../../build/node_modules/scheduler/umd/scheduler-tracing.development.js"></script>
17 - <script src="../../build/node_modules/react/umd/react.development.js"></script>
18 - <script src="../../build/node_modules/react-dom/umd/react-dom.development.js"></script>
19 - <script src="./test.js"></script>
20 - </body>
21 -</html>
fixtures/tracing/test.js deleted
-101
@@ -1,101 +0,0 @@
1 -const {createElement, Component, Suspense} = React;
2 -const {createRoot} = ReactDOM;
3 -const {
4 - unstable_subscribe: subscribe,
5 - unstable_trace: trace,
6 - unstable_wrap: wrap,
7 -} = SchedulerTracing;
8 -
9 -const createLogger = (backgroundColor, color, enabled) => (
10 - message,
11 - ...args
12 -) => {
13 - if (enabled === false) return;
14 - console.groupCollapsed(
15 - `%c${message}`,
16 - `background-color: ${backgroundColor}; color: ${color}; padding: 2px 4px;`,
17 - ...args
18 - );
19 - console.log(
20 - new Error('stack').stack
21 - .split('\n')
22 - .slice(2)
23 - .join('\n')
24 - );
25 - console.groupEnd();
26 -};
27 -
28 -window.log = {
29 - app: createLogger('#37474f', '#fff'),
30 - interaction: createLogger('#6a1b9a', '#fff'),
31 - react: createLogger('#ff5722', '#fff'),
32 - tracing: createLogger('#2962ff', '#fff'),
33 - work: createLogger('#e1bee7', '#000'),
34 -};
35 -
36 -// Fake suspense
37 -const resolvedValues = {};
38 -const read = key => {
39 - if (!resolvedValues[key]) {
40 - log.app(`Suspending for "${key}" ...`);
41 - throw new Promise(
42 - wrap(resolve => {
43 - setTimeout(
44 - wrap(() => {
45 - log.app(`Loaded "${key}" ...`);
46 - resolvedValues[key] = true;
47 - resolve(key);
48 - }),
49 - 1000
50 - );
51 - })
52 - );
53 - }
54 - return key;
55 -};
56 -
57 -const TestApp = () =>
58 - createElement(
59 - Suspense,
60 - {fallback: createElement(PlaceholderText)},
61 - createElement(SuspendingChild, {text: 'foo'}),
62 - createElement(SuspendingChild, {text: 'bar'}),
63 - createElement(SuspendingChild, {text: 'baz'})
64 - );
65 -
66 -const PlaceholderText = () => 'Loading ...';
67 -
68 -const SuspendingChild = ({text}) => {
69 - const resolvedValue = read(text);
70 - return resolvedValue;
71 -};
72 -
73 -subscribe({
74 - onInteractionScheduledWorkCompleted: interaction =>
75 - log.interaction(
76 - 'onInteractionScheduledWorkCompleted',
77 - JSON.stringify(interaction)
78 - ),
79 - onInteractionTraced: interaction =>
80 - log.interaction('onInteractionTraced', JSON.stringify(interaction)),
81 - onWorkCanceled: interactions =>
82 - log.work('onWorkCanceled', JSON.stringify(Array.from(interactions))),
83 - onWorkScheduled: interactions =>
84 - log.work('onWorkScheduled', JSON.stringify(Array.from(interactions))),
85 - onWorkStarted: interactions =>
86 - log.work('onWorkStarted', JSON.stringify(Array.from(interactions))),
87 - onWorkStopped: interactions =>
88 - log.work('onWorkStopped', JSON.stringify(Array.from(interactions))),
89 -});
90 -
91 -const element = document.getElementById('root');
92 -trace('initial_render', performance.now(), () => {
93 - const root = createRoot(element);
94 - log.app('render()');
95 - root.render(
96 - createElement(TestApp),
97 - wrap(() => {
98 - log.app('committed');
99 - })
100 - );
101 -});
packages/react-devtools-shared/src/__tests__/__snapshots__/profilingCache-test.js.snap
-405
@@ -39,7 +39,6 @@ Object {
39 3 => 1,
40 5 => 1,
41 },
42 - "interactionIDs": Array [],
42 "passiveEffectDuration": null,
43 "priorityLevel": "Normal",
44 "timestamp": 16,
@@ -87,7 +86,6 @@ Object {
86 3 => 3,
87 4 => 2,
88 },
90 - "interactionIDs": Array [],
89 "passiveEffectDuration": null,
90 "priorityLevel": "Normal",
91 "timestamp": 15,
@@ -124,7 +122,6 @@ Object {
122 5 => 3,
123 3 => 0,
124 },
127 - "interactionIDs": Array [],
125 "passiveEffectDuration": null,
126 "priorityLevel": "Immediate",
127 "timestamp": 18,
@@ -188,7 +185,6 @@ Object {
185 4 => 1,
186 5 => 1,
187 },
191 - "interactionIDs": Array [],
188 "passiveEffectDuration": null,
189 "priorityLevel": "Normal",
190 "timestamp": 12,
@@ -254,7 +250,6 @@ Object {
250 2 => 10,
251 1 => 0,
252 },
257 - "interactionIDs": Array [],
253 "passiveEffectDuration": null,
254 "priorityLevel": "Immediate",
255 "timestamp": 25,
@@ -302,7 +297,6 @@ Object {
297 2 => 10,
298 1 => 0,
299 },
305 - "interactionIDs": Array [],
300 "passiveEffectDuration": null,
301 "priorityLevel": "Immediate",
302 "timestamp": 35,
@@ -341,7 +335,6 @@ Object {
335 2 => 10,
336 1 => 0,
337 },
344 - "interactionIDs": Array [],
338 "passiveEffectDuration": null,
339 "priorityLevel": "Immediate",
340 "timestamp": 45,
@@ -451,7 +444,6 @@ Object {
444 1,
445 ],
446 ],
454 - "interactionIDs": Array [],
447 "passiveEffectDuration": null,
448 "priorityLevel": "Normal",
449 "timestamp": 12,
@@ -556,7 +548,6 @@ Object {
548 0,
549 ],
550 ],
559 - "interactionIDs": Array [],
551 "passiveEffectDuration": null,
552 "priorityLevel": "Immediate",
553 "timestamp": 25,
@@ -625,7 +616,6 @@ Object {
616 0,
617 ],
618 ],
628 - "interactionIDs": Array [],
619 "passiveEffectDuration": null,
620 "priorityLevel": "Immediate",
621 "timestamp": 35,
@@ -676,7 +666,6 @@ Object {
666 0,
667 ],
668 ],
679 - "interactionIDs": Array [],
669 "passiveEffectDuration": null,
670 "priorityLevel": "Immediate",
671 "timestamp": 45,
@@ -693,8 +682,6 @@ Object {
682 ],
683 "displayName": "Parent",
684 "initialTreeBaseDurations": Array [],
696 - "interactionCommits": Array [],
697 - "interactions": Array [],
685 "operations": Array [
686 Array [
687 1,
@@ -950,7 +937,6 @@ Object {
937 1,
938 ],
939 ],
953 - "interactionIDs": Array [],
940 "passiveEffectDuration": null,
941 "priorityLevel": "Normal",
942 "timestamp": 11,
@@ -1037,7 +1023,6 @@ Object {
1023 0,
1024 ],
1025 ],
1040 - "interactionIDs": Array [],
1026 "passiveEffectDuration": null,
1027 "priorityLevel": "Immediate",
1028 "timestamp": 22,
@@ -1142,7 +1127,6 @@ Object {
1127 0,
1128 ],
1129 ],
1145 - "interactionIDs": Array [],
1130 "passiveEffectDuration": null,
1131 "priorityLevel": "Immediate",
1132 "timestamp": 35,
@@ -1159,8 +1143,6 @@ Object {
1143 ],
1144 "displayName": "Parent",
1145 "initialTreeBaseDurations": Array [],
1162 - "interactionCommits": Array [],
1163 - "interactions": Array [],
1146 "operations": Array [
1147 Array [
1148 1,
@@ -1352,7 +1334,6 @@ Object {
1334 2 => 10,
1335 1 => 0,
1336 },
1355 - "interactionIDs": Array [],
1337 "passiveEffectDuration": null,
1338 "priorityLevel": "Immediate",
1339 "timestamp": 13,
@@ -1397,7 +1378,6 @@ Object {
1378 2 => 10,
1379 1 => 0,
1380 },
1400 - "interactionIDs": Array [],
1381 "passiveEffectDuration": null,
1382 "priorityLevel": "Immediate",
1383 "timestamp": 34,
@@ -1433,7 +1413,6 @@ Object {
1413 2 => 10,
1414 1 => 0,
1415 },
1436 - "interactionIDs": Array [],
1416 "passiveEffectDuration": null,
1417 "priorityLevel": "Immediate",
1418 "timestamp": 44,
@@ -1456,8 +1435,6 @@ Object {
1435 4 => 1,
1436 5 => 1,
1437 },
1459 - "interactionCommits": Map {},
1460 - "interactions": Map {},
1438 "operations": Array [
1439 Array [
1440 1,
@@ -1619,7 +1596,6 @@ Object {
1596 13 => 0,
1597 14 => 1,
1598 },
1622 - "interactionIDs": Array [],
1599 "passiveEffectDuration": null,
1600 "priorityLevel": "Normal",
1601 "timestamp": 24,
@@ -1636,8 +1612,6 @@ Object {
1612 ],
1613 "displayName": "Parent",
1614 "initialTreeBaseDurations": Map {},
1639 - "interactionCommits": Map {},
1640 - "interactions": Map {},
1615 "operations": Array [
1616 Array [
1617 1,
@@ -1712,7 +1686,6 @@ Object {
1686 "effectDuration": null,
1687 "fiberActualDurations": Map {},
1688 "fiberSelfDurations": Map {},
1715 - "interactionIDs": Array [],
1689 "passiveEffectDuration": null,
1690 "priorityLevel": "Normal",
1691 "timestamp": 34,
@@ -1734,8 +1707,6 @@ Object {
1707 8 => 0,
1708 9 => 1,
1709 },
1737 - "interactionCommits": Map {},
1738 - "interactions": Map {},
1710 "operations": Array [
1711 Array [
1712 1,
@@ -1890,7 +1861,6 @@ Object {
1861 0,
1862 ],
1863 ],
1893 - "interactionIDs": Array [],
1864 "passiveEffectDuration": null,
1865 "priorityLevel": "Immediate",
1866 "timestamp": 13,
@@ -1959,7 +1929,6 @@ Object {
1929 0,
1930 ],
1931 ],
1962 - "interactionIDs": Array [],
1932 "passiveEffectDuration": null,
1933 "priorityLevel": "Immediate",
1934 "timestamp": 34,
@@ -2010,7 +1979,6 @@ Object {
1979 0,
1980 ],
1981 ],
2013 - "interactionIDs": Array [],
1982 "passiveEffectDuration": null,
1983 "priorityLevel": "Immediate",
1984 "timestamp": 44,
@@ -2048,8 +2016,6 @@ Object {
2016 1,
2017 ],
2018 ],
2051 - "interactionCommits": Array [],
2052 - "interactions": Array [],
2019 "operations": Array [
2020 Array [
2021 1,
@@ -2256,7 +2222,6 @@ Object {
2222 1,
2223 ],
2224 ],
2259 - "interactionIDs": Array [],
2225 "passiveEffectDuration": null,
2226 "priorityLevel": "Normal",
2227 "timestamp": 24,
@@ -2273,8 +2238,6 @@ Object {
2238 ],
2239 "displayName": "Parent",
2240 "initialTreeBaseDurations": Array [],
2276 - "interactionCommits": Array [],
2277 - "interactions": Array [],
2241 "operations": Array [
2242 Array [
2243 1,
@@ -2346,7 +2309,6 @@ Object {
2309 "effectDuration": null,
2310 "fiberActualDurations": Array [],
2311 "fiberSelfDurations": Array [],
2349 - "interactionIDs": Array [],
2312 "passiveEffectDuration": null,
2313 "priorityLevel": "Normal",
2314 "timestamp": 34,
@@ -2380,8 +2342,6 @@ Object {
2342 1,
2343 ],
2344 ],
2383 - "interactionCommits": Array [],
2384 - "interactions": Array [],
2345 "operations": Array [
2346 Array [
2347 1,
@@ -2470,7 +2430,6 @@ Object {
2430 1 => 0,
2431 2 => 0,
2432 },
2473 - "interactionIDs": Array [],
2433 "passiveEffectDuration": null,
2434 "priorityLevel": "Normal",
2435 "timestamp": 0,
@@ -2487,8 +2446,6 @@ Object {
2446 ],
2447 "displayName": "Suspense",
2448 "initialTreeBaseDurations": Map {},
2490 - "interactionCommits": Map {},
2491 - "interactions": Map {},
2449 "operations": Array [
2450 Array [
2451 1,
@@ -2548,7 +2505,6 @@ Object {
2505 2 => 0,
2506 3 => 0,
2507 },
2551 - "interactionIDs": Array [],
2508 "passiveEffectDuration": null,
2509 "priorityLevel": "Normal",
2510 "timestamp": 0,
@@ -2589,7 +2545,6 @@ Object {
2545 2 => 0,
2546 1 => 0,
2547 },
2592 - "interactionIDs": Array [],
2548 "passiveEffectDuration": null,
2549 "priorityLevel": "Immediate",
2550 "timestamp": 0,
@@ -2624,7 +2579,6 @@ Object {
2579 "fiberSelfDurations": Map {
2580 3 => 0,
2581 },
2627 - "interactionIDs": Array [],
2582 "passiveEffectDuration": null,
2583 "priorityLevel": "Immediate",
2584 "timestamp": 0,
@@ -2659,7 +2613,6 @@ Object {
2613 "fiberSelfDurations": Map {
2614 3 => 0,
2615 },
2662 - "interactionIDs": Array [],
2616 "passiveEffectDuration": null,
2617 "priorityLevel": "Immediate",
2618 "timestamp": 0,
@@ -2698,7 +2651,6 @@ Object {
2651 2 => 0,
2652 1 => 0,
2653 },
2701 - "interactionIDs": Array [],
2654 "passiveEffectDuration": null,
2655 "priorityLevel": "Immediate",
2656 "timestamp": 0,
@@ -2762,7 +2714,6 @@ Object {
2714 0,
2715 ],
2716 ],
2765 - "interactionIDs": Array [],
2717 "passiveEffectDuration": null,
2718 "priorityLevel": "Normal",
2719 "timestamp": 0,
@@ -2821,7 +2772,6 @@ Object {
2772 0,
2773 ],
2774 ],
2824 - "interactionIDs": Array [],
2775 "passiveEffectDuration": null,
2776 "priorityLevel": "Immediate",
2777 "timestamp": 0,
@@ -2862,7 +2812,6 @@ Object {
2812 0,
2813 ],
2814 ],
2865 - "interactionIDs": Array [],
2815 "passiveEffectDuration": null,
2816 "priorityLevel": "Immediate",
2817 "timestamp": 0,
@@ -2903,7 +2852,6 @@ Object {
2852 0,
2853 ],
2854 ],
2906 - "interactionIDs": Array [],
2855 "passiveEffectDuration": null,
2856 "priorityLevel": "Immediate",
2857 "timestamp": 0,
@@ -2960,7 +2908,6 @@ Object {
2908 0,
2909 ],
2910 ],
2963 - "interactionIDs": Array [],
2911 "passiveEffectDuration": null,
2912 "priorityLevel": "Immediate",
2913 "timestamp": 0,
@@ -2977,8 +2924,6 @@ Object {
2924 ],
2925 "displayName": "Component",
2926 "initialTreeBaseDurations": Array [],
2980 - "interactionCommits": Array [],
2981 - "interactions": Array [],
2927 "operations": Array [
2928 Array [
2929 1,
@@ -3125,7 +3070,6 @@ Object {
3070 6 => 0,
3071 7 => 0,
3072 },
3128 - "interactionIDs": Array [],
3073 "passiveEffectDuration": null,
3074 "priorityLevel": "Normal",
3075 "timestamp": 0,
@@ -3206,7 +3150,6 @@ Object {
3150 3 => 0,
3151 2 => 0,
3152 },
3209 - "interactionIDs": Array [],
3153 "passiveEffectDuration": null,
3154 "priorityLevel": "Immediate",
3155 "timestamp": 0,
@@ -3283,7 +3226,6 @@ Object {
3226 2 => 0,
3227 1 => 0,
3228 },
3286 - "interactionIDs": Array [],
3229 "passiveEffectDuration": null,
3230 "priorityLevel": "Immediate",
3231 "timestamp": 0,
@@ -3361,7 +3303,6 @@ Object {
3303 2 => 0,
3304 1 => 0,
3305 },
3364 - "interactionIDs": Array [],
3306 "passiveEffectDuration": null,
3307 "priorityLevel": "Immediate",
3308 "timestamp": 0,
@@ -3438,7 +3379,6 @@ Object {
3379 2 => 0,
3380 1 => 0,
3381 },
3441 - "interactionIDs": Array [],
3382 "passiveEffectDuration": null,
3383 "priorityLevel": "Immediate",
3384 "timestamp": 0,
@@ -3574,7 +3514,6 @@ Object {
3514 0,
3515 ],
3516 ],
3577 - "interactionIDs": Array [],
3517 "passiveEffectDuration": null,
3518 "priorityLevel": "Normal",
3519 "timestamp": 0,
@@ -3703,7 +3642,6 @@ Object {
3642 0,
3643 ],
3644 ],
3706 - "interactionIDs": Array [],
3645 "passiveEffectDuration": null,
3646 "priorityLevel": "Immediate",
3647 "timestamp": 0,
@@ -3834,7 +3772,6 @@ Object {
3772 0,
3773 ],
3774 ],
3837 - "interactionIDs": Array [],
3775 "passiveEffectDuration": null,
3776 "priorityLevel": "Immediate",
3777 "timestamp": 0,
@@ -3966,7 +3903,6 @@ Object {
3903 0,
3904 ],
3905 ],
3969 - "interactionIDs": Array [],
3906 "passiveEffectDuration": null,
3907 "priorityLevel": "Immediate",
3908 "timestamp": 0,
@@ -4097,7 +4033,6 @@ Object {
4033 0,
4034 ],
4035 ],
4100 - "interactionIDs": Array [],
4036 "passiveEffectDuration": null,
4037 "priorityLevel": "Immediate",
4038 "timestamp": 0,
@@ -4114,8 +4049,6 @@ Object {
4049 ],
4050 "displayName": "LegacyContextProvider",
4051 "initialTreeBaseDurations": Array [],
4117 - "interactionCommits": Array [],
4118 - "interactions": Array [],
4052 "operations": Array [
4053 Array [
4054 1,
@@ -4325,341 +4258,3 @@ Object {
4258 "version": 5,
4259 }
4260 `;
4328 -
4329 -exports[`ProfilingCache should report every traced interaction: Interactions 1`] = `
4330 -Array [
4331 - Object {
4332 - "__count": 1,
4333 - "id": 0,
4334 - "name": "mount: one child",
4335 - "timestamp": 0,
4336 - },
4337 - Object {
4338 - "__count": 0,
4339 - "id": 1,
4340 - "name": "update: two children",
4341 - "timestamp": 11,
4342 - },
4343 -]
4344 -`;
4345 -
4346 -exports[`ProfilingCache should report every traced interaction: imported data 1`] = `
4347 -Object {
4348 - "dataForRoots": Array [
4349 - Object {
4350 - "commitData": Array [
4351 - Object {
4352 - "changeDescriptions": Array [
4353 - Array [
4354 - 2,
4355 - Object {
4356 - "context": null,
4357 - "didHooksChange": false,
4358 - "isFirstMount": true,
4359 - "props": null,
4360 - "state": null,
4361 - },
4362 - ],
4363 - Array [
4364 - 3,
4365 - Object {
4366 - "context": null,
4367 - "didHooksChange": false,
4368 - "isFirstMount": true,
4369 - "props": null,
4370 - "state": null,
4371 - },
4372 - ],
4373 - Array [
4374 - 4,
4375 - Object {
4376 - "context": null,
4377 - "didHooksChange": false,
4378 - "isFirstMount": true,
4379 - "props": null,
4380 - "state": null,
4381 - },
4382 - ],
4383 - ],
4384 - "duration": 11,
4385 - "effectDuration": null,
4386 - "fiberActualDurations": Array [
4387 - Array [
4388 - 1,
4389 - 11,
4390 - ],
4391 - Array [
4392 - 2,
4393 - 11,
4394 - ],
4395 - Array [
4396 - 3,
4397 - 0,
4398 - ],
4399 - Array [
4400 - 4,
4401 - 1,
4402 - ],
4403 - ],
4404 - "fiberSelfDurations": Array [
4405 - Array [
4406 - 1,
4407 - 0,
4408 - ],
4409 - Array [
4410 - 2,
4411 - 10,
4412 - ],
4413 - Array [
4414 - 3,
4415 - 0,
4416 - ],
4417 - Array [
4418 - 4,
4419 - 1,
4420 - ],
4421 - ],
4422 - "interactionIDs": Array [
4423 - 0,
4424 - ],
4425 - "passiveEffectDuration": null,
4426 - "priorityLevel": "Normal",
4427 - "timestamp": 11,
4428 - "updaters": Array [
4429 - Object {
4430 - "displayName": "Anonymous",
4431 - "hocDisplayNames": null,
4432 - "id": 1,
4433 - "key": null,
4434 - "type": 11,
4435 - },
4436 - ],
4437 - },
4438 - Object {
4439 - "changeDescriptions": Array [
4440 - Array [
4441 - 3,
4442 - Object {
4443 - "context": null,
4444 - "didHooksChange": false,
4445 - "isFirstMount": false,
4446 - "props": Array [],
4447 - "state": null,
4448 - },
4449 - ],
4450 - Array [
4451 - 5,
4452 - Object {
4453 - "context": null,
4454 - "didHooksChange": false,
4455 - "isFirstMount": true,
4456 - "props": null,
4457 - "state": null,
4458 - },
4459 - ],
4460 - Array [
4461 - 2,
4462 - Object {
4463 - "context": null,
4464 - "didHooksChange": false,
4465 - "isFirstMount": false,
4466 - "props": Array [
4467 - "count",
4468 - ],
4469 - "state": null,
4470 - },
4471 - ],
4472 - ],
4473 - "duration": 11,
4474 - "effectDuration": null,
4475 - "fiberActualDurations": Array [
4476 - Array [
4477 - 3,
4478 - 0,
4479 - ],
4480 - Array [
4481 - 5,
4482 - 1,
4483 - ],
4484 - Array [
4485 - 2,
4486 - 11,
4487 - ],
4488 - Array [
4489 - 1,
4490 - 11,
4491 - ],
4492 - ],
4493 - "fiberSelfDurations": Array [
4494 - Array [
4495 - 3,
4496 - 0,
4497 - ],
4498 - Array [
4499 - 5,
4500 - 1,
4501 - ],
4502 - Array [
4503 - 2,
4504 - 10,
4505 - ],
4506 - Array [
4507 - 1,
4508 - 0,
4509 - ],
4510 - ],
4511 - "interactionIDs": Array [
4512 - 1,
4513 - ],
4514 - "passiveEffectDuration": null,
4515 - "priorityLevel": "Immediate",
4516 - "timestamp": 22,
4517 - "updaters": Array [
4518 - Object {
4519 - "displayName": "Anonymous",
4520 - "hocDisplayNames": null,
4521 - "id": 1,
4522 - "key": null,
4523 - "type": 11,
4524 - },
4525 - ],
4526 - },
4527 - ],
4528 - "displayName": "Parent",
4529 - "initialTreeBaseDurations": Array [],
4530 - "interactionCommits": Array [
4531 - Array [
4532 - 0,
4533 - Array [
4534 - 0,
4535 - ],
4536 - ],
4537 - Array [
4538 - 1,
4539 - Array [
4540 - 1,
4541 - ],
4542 - ],
4543 - ],
4544 - "interactions": Array [
4545 - Array [
4546 - 0,
4547 - Object {
4548 - "__count": 1,
4549 - "id": 0,
4550 - "name": "mount: one child",
4551 - "timestamp": 0,
4552 - },
4553 - ],
4554 - Array [
4555 - 1,
4556 - Object {
4557 - "__count": 0,
4558 - "id": 1,
4559 - "name": "update: two children",
4560 - "timestamp": 11,
4561 - },
4562 - ],
4563 - ],
4564 - "operations": Array [
4565 - Array [
4566 - 1,
4567 - 1,
4568 - 15,
4569 - 6,
4570 - 80,
4571 - 97,
4572 - 114,
4573 - 101,
4574 - 110,
4575 - 116,
4576 - 5,
4577 - 67,
4578 - 104,
4579 - 105,
4580 - 108,
4581 - 100,
4582 - 1,
4583 - 48,
4584 - 1,
4585 - 1,
4586 - 11,
4587 - 1,
4588 - 1,
4589 - 4,
4590 - 1,
4591 - 11000,
4592 - 1,
4593 - 2,
4594 - 5,
4595 - 1,
4596 - 0,
4597 - 1,
4598 - 0,
4599 - 4,
4600 - 2,
4601 - 11000,
4602 - 1,
4603 - 3,
4604 - 5,
4605 - 2,
4606 - 2,
4607 - 2,
4608 - 3,
4609 - 4,
4610 - 3,
4611 - 0,
4612 - 1,
4613 - 4,
4614 - 8,
4615 - 2,
4616 - 2,
4617 - 2,
4618 - 0,
4619 - 4,
4620 - 4,
4621 - 1000,
4622 - ],
4623 - Array [
4624 - 1,
4625 - 1,
4626 - 8,
4627 - 5,
4628 - 67,
4629 - 104,
4630 - 105,
4631 - 108,
4632 - 100,
4633 - 1,
4634 - 49,
4635 - 1,
4636 - 5,
4637 - 5,
4638 - 2,
4639 - 2,
4640 - 1,
4641 - 2,
4642 - 4,
4643 - 5,
4644 - 1000,
4645 - 4,
4646 - 2,
4647 - 12000,
4648 - 3,
4649 - 2,
4650 - 3,
4651 - 3,
4652 - 5,
4653 - 4,
4654 - 4,
4655 - 1,
4656 - 12000,
4657 - ],
4658 - ],
4659 - "rootID": 1,
4660 - "snapshots": Array [],
4661 - },
4662 - ],
4663 - "version": 5,
4664 -}
4665 -`;
packages/react-devtools-shared/src/__tests__/__snapshots__/profilingCharts-test.js.snap
-42
@@ -255,48 +255,6 @@ Object {
255 }
256 `;
257
258 -exports[`profiling charts interactions should contain valid data: Interactions 1`] = `
259 -Object {
260 - "interactions": Array [
261 - Object {
262 - "__count": 1,
263 - "id": 0,
264 - "name": "mount",
265 - "timestamp": 0,
266 - },
267 - Object {
268 - "__count": 0,
269 - "id": 1,
270 - "name": "update",
271 - "timestamp": 15,
272 - },
273 - ],
274 - "lastInteractionTime": 25,
275 - "maxCommitDuration": 15,
276 -}
277 -`;
278 -
279 -exports[`profiling charts interactions should contain valid data: Interactions 2`] = `
280 -Object {
281 - "interactions": Array [
282 - Object {
283 - "__count": 1,
284 - "id": 0,
285 - "name": "mount",
286 - "timestamp": 0,
287 - },
288 - Object {
289 - "__count": 0,
290 - "id": 1,
291 - "name": "update",
292 - "timestamp": 15,
293 - },
294 - ],
295 - "lastInteractionTime": 25,
296 - "maxCommitDuration": 15,
297 -}
298 -`;
299 -
258 exports[`profiling charts ranked chart should contain valid data: 0: CommitTree 1`] = `
259 Object {
260 "nodes": Map {
packages/react-devtools-shared/src/__tests__/profilingCache-test.js
-75
@@ -16,7 +16,6 @@ describe('ProfilingCache', () => {
16 let React;
17 let ReactDOM;
18 let Scheduler;
19 - let SchedulerTracing;
19 let TestRenderer: ReactTestRenderer;
20 let bridge: FrontendBridge;
21 let store: Store;
@@ -35,7 +34,6 @@ describe('ProfilingCache', () => {
34 React = require('react');
35 ReactDOM = require('react-dom');
36 Scheduler = require('scheduler');
38 - SchedulerTracing = require('scheduler/tracing');
37 TestRenderer = utils.requireTestRenderer();
38 });
39
@@ -622,79 +620,6 @@ describe('ProfilingCache', () => {
620 }
621 });
622
625 - it('should report every traced interaction', () => {
626 - const Parent = ({count}) => {
627 - Scheduler.unstable_advanceTime(10);
628 - const children = new Array(count)
629 - .fill(true)
630 - .map((_, index) => <Child key={index} duration={index} />);
631 - return (
632 - <React.Fragment>
633 - {children}
634 - <MemoizedChild duration={1} />
635 - </React.Fragment>
636 - );
637 - };
638 - const Child = ({duration}) => {
639 - Scheduler.unstable_advanceTime(duration);
640 - return null;
641 - };
642 - const MemoizedChild = React.memo(Child);
643 -
644 - const container = document.createElement('div');
645 -
646 - utils.act(() => store.profilerStore.startProfiling());
647 - utils.act(() =>
648 - SchedulerTracing.unstable_trace(
649 - 'mount: one child',
650 - Scheduler.unstable_now(),
651 - () => ReactDOM.render(<Parent count={1} />, container),
652 - ),
653 - );
654 - utils.act(() =>
655 - SchedulerTracing.unstable_trace(
656 - 'update: two children',
657 - Scheduler.unstable_now(),
658 - () => ReactDOM.render(<Parent count={2} />, container),
659 - ),
660 - );
661 - utils.act(() => store.profilerStore.stopProfiling());
662 -
663 - let interactions = null;
664 -
665 - function Validator({previousInteractions, rootID}) {
666 - interactions = store.profilerStore.profilingCache.getInteractionsChartData(
667 - {
668 - rootID,
669 - },
670 - ).interactions;
671 - if (previousInteractions != null) {
672 - expect(interactions).toEqual(previousInteractions);
673 - } else {
674 - expect(interactions).toMatchSnapshot('Interactions');
675 - }
676 - return null;
677 - }
678 -
679 - const rootID = store.roots[0];
680 -
681 - utils.act(() =>
682 - TestRenderer.create(
683 - <Validator previousInteractions={null} rootID={rootID} />,
684 - ),
685 - );
686 -
687 - expect(interactions).not.toBeNull();
688 -
689 - utils.exportImportHelper(bridge, store);
690 -
691 - utils.act(() =>
692 - TestRenderer.create(
693 - <Validator previousInteractions={interactions} rootID={rootID} />,
694 - ),
695 - );
696 - });
697 -
623 it('should handle unexpectedly shallow suspense trees', () => {
624 const container = document.createElement('div');
625
packages/react-devtools-shared/src/__tests__/profilingCharts-test.js
+4 -91
@@ -14,7 +14,6 @@ describe('profiling charts', () => {
14 let React;
15 let ReactDOM;
16 let Scheduler;
17 - let SchedulerTracing;
17 let TestRenderer: TestRendererType;
18 let store: Store;
19 let utils;
@@ -30,7 +29,6 @@ describe('profiling charts', () => {
29 React = require('react');
30 ReactDOM = require('react-dom');
31 Scheduler = require('scheduler');
33 - SchedulerTracing = require('scheduler/tracing');
32 TestRenderer = utils.requireTestRenderer();
33 });
34
@@ -56,18 +54,8 @@ describe('profiling charts', () => {
54 const container = document.createElement('div');
55
56 utils.act(() => store.profilerStore.startProfiling());
59 - utils.act(() =>
60 - SchedulerTracing.unstable_trace('mount', Scheduler.unstable_now(), () =>
61 - ReactDOM.render(<Parent />, container),
62 - ),
63 - );
64 - utils.act(() =>
65 - SchedulerTracing.unstable_trace(
66 - 'update',
67 - Scheduler.unstable_now(),
68 - () => ReactDOM.render(<Parent />, container),
69 - ),
70 - );
57 + utils.act(() => ReactDOM.render(<Parent />, container));
58 + utils.act(() => ReactDOM.render(<Parent />, container));
59 utils.act(() => store.profilerStore.stopProfiling());
60
61 let renderFinished = false;
@@ -132,18 +120,8 @@ describe('profiling charts', () => {
120 const container = document.createElement('div');
121
122 utils.act(() => store.profilerStore.startProfiling());
135 - utils.act(() =>
136 - SchedulerTracing.unstable_trace('mount', Scheduler.unstable_now(), () =>
137 - ReactDOM.render(<Parent />, container),
138 - ),
139 - );
140 - utils.act(() =>
141 - SchedulerTracing.unstable_trace(
142 - 'update',
143 - Scheduler.unstable_now(),
144 - () => ReactDOM.render(<Parent />, container),
145 - ),
146 - );
123 + utils.act(() => ReactDOM.render(<Parent />, container));
124 + utils.act(() => ReactDOM.render(<Parent />, container));
125 utils.act(() => store.profilerStore.stopProfiling());
126
127 let renderFinished = false;
@@ -181,69 +159,4 @@ describe('profiling charts', () => {
159 }
160 });
161 });
184 -
185 - describe('interactions', () => {
186 - it('should contain valid data', () => {
187 - const Parent = (_: {||}) => {
188 - Scheduler.unstable_advanceTime(10);
189 - return (
190 - <React.Fragment>
191 - <Child key="first" duration={3} />
192 - <Child key="second" duration={2} />
193 - <Child key="third" duration={0} />
194 - </React.Fragment>
195 - );
196 - };
197 -
198 - // Memoize children to verify that chart doesn't include in the update.
199 - const Child = React.memo(function Child({duration}) {
200 - Scheduler.unstable_advanceTime(duration);
201 - return null;
202 - });
203 -
204 - const container = document.createElement('div');
205 -
206 - utils.act(() => store.profilerStore.startProfiling());
207 - utils.act(() =>
208 - SchedulerTracing.unstable_trace('mount', Scheduler.unstable_now(), () =>
209 - ReactDOM.render(<Parent />, container),
210 - ),
211 - );
212 - utils.act(() =>
213 - SchedulerTracing.unstable_trace(
214 - 'update',
215 - Scheduler.unstable_now(),
216 - () => ReactDOM.render(<Parent />, container),
217 - ),
218 - );
219 - utils.act(() => store.profilerStore.stopProfiling());
220 -
221 - let renderFinished = false;
222 -
223 - function Validator({commitIndex, rootID}) {
224 - const chartData = store.profilerStore.profilingCache.getInteractionsChartData(
225 - {
226 - rootID,
227 - },
228 - );
229 - expect(chartData).toMatchSnapshot('Interactions');
230 - renderFinished = true;
231 - return null;
232 - }
233 -
234 - const rootID = store.roots[0];
235 -
236 - for (let commitIndex = 0; commitIndex < 2; commitIndex++) {
237 - renderFinished = false;
238 -
239 - utils.act(() => {
240 - TestRenderer.create(
241 - <Validator commitIndex={commitIndex} rootID={rootID} />,
242 - );
243 - });
244 -
245 - expect(renderFinished).toBe(true);
246 - }
247 - });
248 - });
162 });
packages/react-devtools-shared/src/backend/renderer.js
+23 -47
@@ -102,7 +102,6 @@ import type {
102 SerializedElement,
103 WorkTagMap,
104 } from './types';
105 -import type {Interaction} from 'react-devtools-shared/src/devtools/views/Profiler/types';
105 import type {
106 ComponentFilter,
107 ElementType,
@@ -2136,6 +2135,22 @@ export function attach(
2135 // We don't patch any methods so there is no cleanup.
2136 }
2137
2138 + function rootSupportsProfiling(root) {
2139 + if (root.memoizedInteractions != null) {
2140 + // v16 builds include this field for the scheduler/tracing API.
2141 + return true;
2142 + } else if (
2143 + root.current != null &&
2144 + root.current.hasOwnProperty('treeBaseDuration')
2145 + ) {
2146 + // The scheduler/tracing API was removed in v17 though
2147 + // so we need to check a non-root Fiber.
2148 + return true;
2149 + } else {
2150 + return false;
2151 + }
2152 + }
2153 +
2154 function flushInitialOperations() {
2155 const localPendingOperationsQueue = pendingOperationsQueue;
2156
@@ -2161,21 +2176,14 @@ export function attach(
2176 currentRootID = getFiberID(getPrimaryFiber(root.current));
2177 setRootPseudoKey(currentRootID, root.current);
2178
2164 - // Checking root.memoizedInteractions handles multi-renderer edge-case-
2165 - // where some v16 renderers support profiling and others don't.
2166 - if (isProfiling && root.memoizedInteractions != null) {
2167 - // If profiling is active, store commit time and duration, and the current interactions.
2179 + // Handle multi-renderer edge-case where only some v16 renderers support profiling.
2180 + if (isProfiling && rootSupportsProfiling(root)) {
2181 + // If profiling is active, store commit time and duration.
2182 // The frontend may request this information after profiling has stopped.
2183 currentCommitProfilingMetadata = {
2184 changeDescriptions: recordChangeDescriptions ? new Map() : null,
2185 durations: [],
2186 commitTime: getCurrentTime() - profilingStartTime,
2173 - interactions: Array.from(root.memoizedInteractions).map(
2174 - (interaction: Interaction) => ({
2175 - ...interaction,
2176 - timestamp: interaction.timestamp - profilingStartTime,
2177 - }),
2178 - ),
2187 maxActualDuration: 0,
2188 priorityLevel: null,
2189 updaters: getUpdatersList(root),
@@ -2205,8 +2213,7 @@ export function attach(
2213 }
2214
2215 function handlePostCommitFiberRoot(root) {
2208 - const isProfilingSupported = root.memoizedInteractions != null;
2209 - if (isProfiling && isProfilingSupported) {
2216 + if (isProfiling && rootSupportsProfiling(root)) {
2217 if (currentCommitProfilingMetadata !== null) {
2218 const {effectDuration, passiveEffectDuration} = getEffectDurations(
2219 root,
@@ -2233,23 +2240,16 @@ export function attach(
2240 traceUpdatesForNodes.clear();
2241 }
2242
2236 - // Checking root.memoizedInteractions handles multi-renderer edge-case-
2237 - // where some v16 renderers support profiling and others don't.
2238 - const isProfilingSupported = root.memoizedInteractions != null;
2243 + // Handle multi-renderer edge-case where only some v16 renderers support profiling.
2244 + const isProfilingSupported = rootSupportsProfiling(root);
2245
2246 if (isProfiling && isProfilingSupported) {
2241 - // If profiling is active, store commit time and duration, and the current interactions.
2247 + // If profiling is active, store commit time and duration.
2248 // The frontend may request this information after profiling has stopped.
2249 currentCommitProfilingMetadata = {
2250 changeDescriptions: recordChangeDescriptions ? new Map() : null,
2251 durations: [],
2252 commitTime: getCurrentTime() - profilingStartTime,
2247 - interactions: Array.from(root.memoizedInteractions).map(
2248 - (interaction: Interaction) => ({
2249 - ...interaction,
2250 - timestamp: interaction.timestamp - profilingStartTime,
2251 - }),
2252 - ),
2253 maxActualDuration: 0,
2254 priorityLevel:
2255 priorityLevel == null ? null : formatPriorityLevel(priorityLevel),
@@ -3386,7 +3386,6 @@ export function attach(
3386 commitTime: number,
3387 durations: Array<number>,
3388 effectDuration: number | null,
3389 - interactions: Array<Interaction>,
3389 maxActualDuration: number,
3390 passiveEffectDuration: number | null,
3391 priorityLevel: string | null,
@@ -3419,8 +3418,6 @@ export function attach(
3418 (commitProfilingMetadata, rootID) => {
3419 const commitData: Array<CommitDataBackend> = [];
3420 const initialTreeBaseDurations: Array<[number, number]> = [];
3422 - const allInteractions: Map<number, Interaction> = new Map();
3423 - const interactionCommits: Map<number, Array<number>> = new Map();
3421
3422 const displayName =
3423 (displayNamesByRootID !== null && displayNamesByRootID.get(rootID)) ||
@@ -3444,7 +3441,6 @@ export function attach(
3441 changeDescriptions,
3442 durations,
3443 effectDuration,
3447 - interactions,
3444 maxActualDuration,
3445 passiveEffectDuration,
3446 priorityLevel,
@@ -3452,23 +3448,6 @@ export function attach(
3448 updaters,
3449 } = commitProfilingData;
3450
3455 - const interactionIDs: Array<number> = [];
3456 -
3457 - interactions.forEach(interaction => {
3458 - if (!allInteractions.has(interaction.id)) {
3459 - allInteractions.set(interaction.id, interaction);
3460 - }
3461 -
3462 - interactionIDs.push(interaction.id);
3463 -
3464 - const commitIndices = interactionCommits.get(interaction.id);
3465 - if (commitIndices != null) {
3466 - commitIndices.push(commitIndex);
3467 - } else {
3468 - interactionCommits.set(interaction.id, [commitIndex]);
3469 - }
3470 - });
3471 -
3451 const fiberActualDurations: Array<[number, number]> = [];
3452 const fiberSelfDurations: Array<[number, number]> = [];
3453 for (let i = 0; i < durations.length; i += 3) {
@@ -3486,7 +3465,6 @@ export function attach(
3465 effectDuration,
3466 fiberActualDurations,
3467 fiberSelfDurations,
3489 - interactionIDs,
3468 passiveEffectDuration,
3469 priorityLevel,
3470 timestamp: commitTime,
@@ -3498,8 +3476,6 @@ export function attach(
3476 commitData,
3477 displayName,
3478 initialTreeBaseDurations,
3501 - interactionCommits: Array.from(interactionCommits.entries()),
3502 - interactions: Array.from(allInteractions.entries()),
3479 rootID,
3480 });
3481 },
packages/react-devtools-shared/src/backend/types.js
-5
@@ -14,7 +14,6 @@ import type {
14 ComponentFilter,
15 ElementType,
16 } from 'react-devtools-shared/src/types';
17 -import type {Interaction} from 'react-devtools-shared/src/devtools/views/Profiler/types';
17 import type {ResolveNativeStyle} from 'react-devtools-shared/src/backend/NativeStyleEditor/setupNativeStyleEditor';
18
19 type BundleType =
@@ -165,7 +164,6 @@ export type CommitDataBackend = {|
164 fiberActualDurations: Array<[number, number]>,
165 // Tuple of fiber ID and computed "self" duration
166 fiberSelfDurations: Array<[number, number]>,
168 - interactionIDs: Array<number>,
167 // Only available in certain (newer) React builds,
168 passiveEffectDuration: number | null,
169 priorityLevel: string | null,
@@ -178,9 +176,6 @@ export type ProfilingDataForRootBackend = {|
176 displayName: string,
177 // Tuple of Fiber ID and base duration
178 initialTreeBaseDurations: Array<[number, number]>,
181 - // Tuple of Interaction ID and commit indices
182 - interactionCommits: Array<[number, Array<number>]>,
183 - interactions: Array<[number, Interaction]>,
179 rootID: number,
180 |};
181
packages/react-devtools-shared/src/devtools/ProfilingCache.js
-16
@@ -16,10 +16,6 @@ import {
16 getChartData as getFlamegraphChartData,
17 invalidateChartData as invalidateFlamegraphChartData,
18 } from 'react-devtools-shared/src/devtools/views/Profiler/FlamegraphChartBuilder';
19 -import {
20 - getChartData as getInteractionsChartData,
21 - invalidateChartData as invalidateInteractionsChartData,
22 -} from 'react-devtools-shared/src/devtools/views/Profiler/InteractionsChartBuilder';
19 import {
20 getChartData as getRankedChartData,
21 invalidateChartData as invalidateRankedChartData,
@@ -27,7 +23,6 @@ import {
23
24 import type {CommitTree} from 'react-devtools-shared/src/devtools/views/Profiler/types';
25 import type {ChartData as FlamegraphChartData} from 'react-devtools-shared/src/devtools/views/Profiler/FlamegraphChartBuilder';
30 -import type {ChartData as InteractionsChartData} from 'react-devtools-shared/src/devtools/views/Profiler/InteractionsChartBuilder';
26 import type {ChartData as RankedChartData} from 'react-devtools-shared/src/devtools/views/Profiler/RankedChartBuilder';
27
28 export default class ProfilingCache {
@@ -92,16 +87,6 @@ export default class ProfilingCache {
87 rootID,
88 });
89
95 - getInteractionsChartData = ({
96 - rootID,
97 - }: {|
98 - rootID: number,
99 - |}): InteractionsChartData =>
100 - getInteractionsChartData({
101 - profilerStore: this._profilerStore,
102 - rootID,
103 - });
104 -
90 getRankedChartData = ({
91 commitIndex,
92 commitTree,
@@ -123,7 +108,6 @@ export default class ProfilingCache {
108
109 invalidateCommitTrees();
110 invalidateFlamegraphChartData();
126 - invalidateInteractionsChartData();
111 invalidateRankedChartData();
112 }
113 }
packages/react-devtools-shared/src/devtools/views/Icon.js
-12
@@ -19,7 +19,6 @@ export type IconType =
19 | 'error'
20 | 'facebook'
21 | 'flame-chart'
22 - | 'interactions'
22 | 'profiler'
23 | 'ranked-chart'
24 | 'search'
@@ -59,9 +58,6 @@ export default function Icon({className = '', type}: Props) {
58 case 'flame-chart':
59 pathData = PATH_FLAME_CHART;
60 break;
62 - case 'interactions':
63 - pathData = PATH_INTERACTIONS;
64 - break;
61 case 'profiler':
62 pathData = PATH_PROFILER;
63 break;
@@ -138,14 +134,6 @@ const PATH_FLAME_CHART = `
134 13.3541667,19.4702042 C13.3541667,20.1226027 12.7851952,20.6514763 12.0833333,20.6514763 Z
135 `;
136
141 -const PATH_INTERACTIONS = `
142 - M23 8c0 1.1-.9 2-2 2-.18 0-.35-.02-.51-.07l-3.56 3.55c.05.16.07.34.07.52 0 1.1-.9 2-2
143 - 2s-2-.9-2-2c0-.18.02-.36.07-.52l-2.55-2.55c-.16.05-.34.07-.52.07s-.36-.02-.52-.07l-4.55
144 - 4.56c.05.16.07.33.07.51 0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2c.18 0 .35.02.51.07l4.56-4.55C8.02
145 - 9.36 8 9.18 8 9c0-1.1.9-2 2-2s2 .9 2 2c0 .18-.02.36-.07.52l2.55
146 - 2.55c.16-.05.34-.07.52-.07s.36.02.52.07l3.55-3.56C19.02 8.35 19 8.18 19 8c0-1.1.9-2 2-2s2 .9 2 2z
147 -`;
148 -
137 const PATH_PROFILER = 'M5 9.2h3V19H5zM10.6 5h2.8v14h-2.8zm5.6 8H19v6h-2.8z';
138
139 const PATH_SEARCH = `
packages/react-devtools-shared/src/devtools/views/Profiler/InteractionListItem.css deleted
-43
@@ -1,43 +0,0 @@
1 -.Interaction,
2 -.SelectedInteraction {
3 - display: flex;
4 - align-items: center;
5 - padding: 0 0.25rem;
6 - border-bottom: 1px solid var(--color-border);
7 -}
8 -
9 -.Interaction:hover {
10 - background-color: var(--color-background-hover);
11 -}
12 -
13 -.SelectedInteraction {
14 - background-color: var(--color-background-hover);
15 -}
16 -
17 -.Name {
18 - white-space: nowrap;
19 - overflow-x: hidden;
20 - text-overflow: ellipsis;
21 -}
22 -
23 -.Timeline {
24 - position: relative;
25 - height: 100%;
26 -}
27 -
28 -.InteractionLine {
29 - position: absolute;
30 - height: 3px;
31 - background-color: var(--color-commit-did-not-render-fill);
32 - color: var(--color-commit-did-not-render-fill-text);
33 - border-radius: 0.125rem;
34 -}
35 -
36 -.CommitBox {
37 - position: absolute;
38 - width: var(--interaction-commit-size);
39 - height: var(--interaction-commit-size);
40 - background-color: var(--color-commit-did-not-render-fill);
41 - color: var(--color-commit-did-not-render-fill-text);
42 - cursor: pointer;
43 -}
packages/react-devtools-shared/src/devtools/views/Profiler/InteractionListItem.js deleted
-105
@@ -1,105 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import * as React from 'react';
11 -import {memo, useCallback} from 'react';
12 -import {areEqual} from 'react-window';
13 -import {getGradientColor} from './utils';
14 -
15 -import styles from './InteractionListItem.css';
16 -
17 -import type {ItemData} from './Interactions';
18 -
19 -type Props = {
20 - data: ItemData,
21 - index: number,
22 - style: Object,
23 - ...
24 -};
25 -
26 -function InteractionListItem({data: itemData, index, style}: Props) {
27 - const {
28 - chartData,
29 - dataForRoot,
30 - labelWidth,
31 - scaleX,
32 - selectedInteractionID,
33 - selectCommitIndex,
34 - selectInteraction,
35 - selectTab,
36 - } = itemData;
37 -
38 - const {commitData, interactionCommits} = dataForRoot;
39 - const {interactions, lastInteractionTime, maxCommitDuration} = chartData;
40 -
41 - const interaction = interactions[index];
42 - if (interaction == null) {
43 - throw Error(`Could not find interaction #${index}`);
44 - }
45 -
46 - const handleClick = useCallback(() => {
47 - selectInteraction(interaction.id);
48 - }, [interaction, selectInteraction]);
49 -
50 - const commits = interactionCommits.get(interaction.id) || [];
51 -
52 - const startTime = interaction.timestamp;
53 - const stopTime = lastInteractionTime;
54 -
55 - const viewCommit = (commitIndex: number) => {
56 - selectTab('flame-chart');
57 - selectCommitIndex(commitIndex);
58 - };
59 -
60 - return (
61 - <div
62 - className={
63 - selectedInteractionID === interaction.id
64 - ? styles.SelectedInteraction
65 - : styles.Interaction
66 - }
67 - onClick={handleClick}
68 - style={style}>
69 - <div
70 - className={styles.Name}
71 - style={{maxWidth: labelWidth}}
72 - title={interaction.name}>
73 - {interaction.name}
74 - </div>
75 - <div
76 - className={styles.InteractionLine}
77 - style={{
78 - left: labelWidth + scaleX(startTime, 0),
79 - width: scaleX(stopTime - startTime, 0),
80 - }}
81 - />
82 - {commits.map(commitIndex => (
83 - <div
84 - className={styles.CommitBox}
85 - key={commitIndex}
86 - onClick={() => viewCommit(commitIndex)}
87 - style={{
88 - backgroundColor: getGradientColor(
89 - Math.min(
90 - 1,
91 - Math.max(
92 - 0,
93 - commitData[commitIndex].duration / maxCommitDuration,
94 - ),
95 - ) || 0,
96 - ),
97 - left: labelWidth + scaleX(commitData[commitIndex].timestamp, 0),
98 - }}
99 - />
100 - ))}
101 - </div>
102 - );
103 -}
104 -
105 -export default memo<Props>(InteractionListItem, areEqual);
packages/react-devtools-shared/src/devtools/views/Profiler/Interactions.css deleted
-9
@@ -1,9 +0,0 @@
1 -.Container {
2 - width: 100%;
3 - flex: 1;
4 - padding: 0.5rem;
5 -}
6 -
7 -.FocusTarget:focus {
8 - outline: none;
9 -}
packages/react-devtools-shared/src/devtools/views/Profiler/Interactions.js deleted
-146
@@ -1,146 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import * as React from 'react';
11 -import {useCallback, useContext, useMemo} from 'react';
12 -import AutoSizer from 'react-virtualized-auto-sizer';
13 -import {FixedSizeList} from 'react-window';
14 -import {ProfilerContext} from './ProfilerContext';
15 -import InteractionListItem from './InteractionListItem';
16 -import NoInteractions from './NoInteractions';
17 -import {StoreContext} from '../context';
18 -import {scale} from './utils';
19 -
20 -import styles from './Interactions.css';
21 -
22 -import type {ProfilingDataForRootFrontend} from './types';
23 -import type {ChartData} from './InteractionsChartBuilder';
24 -import type {TabID} from './ProfilerContext';
25 -
26 -export type ItemData = {|
27 - chartData: ChartData,
28 - dataForRoot: ProfilingDataForRootFrontend,
29 - labelWidth: number,
30 - scaleX: (value: number, fallbackValue: number) => number,
31 - selectedInteractionID: number | null,
32 - selectCommitIndex: (id: number | null) => void,
33 - selectInteraction: (id: number | null) => void,
34 - selectTab: (id: TabID) => void,
35 -|};
36 -
37 -export default function InteractionsAutoSizer(_: {||}) {
38 - return (
39 - <div className={styles.Container}>
40 - <AutoSizer>
41 - {({height, width}) => <Interactions height={height} width={width} />}
42 - </AutoSizer>
43 - </div>
44 - );
45 -}
46 -
47 -function Interactions({height, width}: {|height: number, width: number|}) {
48 - const {
49 - rootID,
50 - selectedInteractionID,
51 - selectInteraction,
52 - selectCommitIndex,
53 - selectTab,
54 - } = useContext(ProfilerContext);
55 - const {profilerStore} = useContext(StoreContext);
56 - const {profilingCache} = profilerStore;
57 -
58 - const dataForRoot = profilerStore.getDataForRoot(((rootID: any): number));
59 -
60 - const chartData = profilingCache.getInteractionsChartData({
61 - rootID: ((rootID: any): number),
62 - });
63 -
64 - const {interactions} = chartData;
65 -
66 - const handleKeyDown = useCallback(
67 - event => {
68 - let index;
69 - switch (event.key) {
70 - case 'ArrowDown':
71 - index = interactions.findIndex(
72 - interaction => interaction.id === selectedInteractionID,
73 - );
74 - selectInteraction(Math.min(interactions.length - 1, index + 1));
75 - event.stopPropagation();
76 - break;
77 - case 'ArrowUp':
78 - index = interactions.findIndex(
79 - interaction => interaction.id === selectedInteractionID,
80 - );
81 - selectInteraction(Math.max(0, index - 1));
82 - event.stopPropagation();
83 - break;
84 - default:
85 - break;
86 - }
87 - },
88 - [interactions, selectedInteractionID, selectInteraction],
89 - );
90 -
91 - const itemData = useMemo<ItemData>(() => {
92 - const interactionCommitSize = parseInt(
93 - getComputedStyle((document.body: any)).getPropertyValue(
94 - '--interaction-commit-size',
95 - ),
96 - 10,
97 - );
98 - const interactionLabelWidth = parseInt(
99 - getComputedStyle((document.body: any)).getPropertyValue(
100 - '--interaction-label-width',
101 - ),
102 - 10,
103 - );
104 -
105 - const labelWidth = Math.min(interactionLabelWidth, width / 5);
106 - const timelineWidth = width - labelWidth - interactionCommitSize;
107 -
108 - return {
109 - chartData,
110 - dataForRoot,
111 - labelWidth,
112 - scaleX: scale(0, chartData.lastInteractionTime, 0, timelineWidth),
113 - selectedInteractionID,
114 - selectCommitIndex,
115 - selectInteraction,
116 - selectTab,
117 - };
118 - }, [
119 - chartData,
120 - dataForRoot,
121 - selectedInteractionID,
122 - selectCommitIndex,
123 - selectInteraction,
124 - selectTab,
125 - width,
126 - ]);
127 -
128 - // If a commit contains no fibers with an actualDuration > 0,
129 - // Display a fallback message.
130 - if (interactions.length === 0) {
131 - return <NoInteractions height={height} width={width} />;
132 - }
133 -
134 - return (
135 - <div className={styles.FocusTarget} onKeyDown={handleKeyDown} tabIndex={0}>
136 - <FixedSizeList
137 - height={height}
138 - itemCount={interactions.length}
139 - itemData={itemData}
140 - itemSize={30}
141 - width={width}>
142 - {InteractionListItem}
143 - </FixedSizeList>
144 - </div>
145 - );
146 -}
packages/react-devtools-shared/src/devtools/views/Profiler/InteractionsChartBuilder.js deleted
-62
@@ -1,62 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import ProfilerStore from 'react-devtools-shared/src/devtools/ProfilerStore';
11 -
12 -import type {Interaction} from './types';
13 -
14 -export type ChartData = {|
15 - interactions: Array<Interaction>,
16 - lastInteractionTime: number,
17 - maxCommitDuration: number,
18 -|};
19 -
20 -const cachedChartData: Map<number, ChartData> = new Map();
21 -
22 -export function getChartData({
23 - profilerStore,
24 - rootID,
25 -}: {|
26 - profilerStore: ProfilerStore,
27 - rootID: number,
28 -|}): ChartData {
29 - if (cachedChartData.has(rootID)) {
30 - return ((cachedChartData.get(rootID): any): ChartData);
31 - }
32 -
33 - const dataForRoot = profilerStore.getDataForRoot(rootID);
34 - if (dataForRoot == null) {
35 - throw Error(`Could not find profiling data for root "${rootID}"`);
36 - }
37 -
38 - const {commitData, interactions} = dataForRoot;
39 -
40 - const lastInteractionTime =
41 - commitData.length > 0 ? commitData[commitData.length - 1].timestamp : 0;
42 -
43 - let maxCommitDuration = 0;
44 -
45 - commitData.forEach(commitDatum => {
46 - maxCommitDuration = Math.max(maxCommitDuration, commitDatum.duration);
47 - });
48 -
49 - const chartData = {
50 - interactions: Array.from(interactions.values()),
51 - lastInteractionTime,
52 - maxCommitDuration,
53 - };
54 -
55 - cachedChartData.set(rootID, chartData);
56 -
57 - return chartData;
58 -}
59 -
60 -export function invalidateChartData(): void {
61 - cachedChartData.clear();
62 -}
packages/react-devtools-shared/src/devtools/views/Profiler/NoInteractions.css deleted
-16
@@ -1,16 +0,0 @@
1 -.NoInteractions {
2 - width: 100%;
3 - height: 100%;
4 - display: flex;
5 - flex-direction: column;
6 - align-items: center;
7 - justify-content: center;
8 -}
9 -
10 -.Header {
11 - font-size: var(--font-size-sans-large);
12 -}
13 -
14 -.Link {
15 - color: var(--color-button);
16 -}
packages/react-devtools-shared/src/devtools/views/Profiler/NoInteractions.js deleted
-35
@@ -1,35 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import * as React from 'react';
11 -
12 -import styles from './NoInteractions.css';
13 -
14 -export default function NoInteractions({
15 - height,
16 - width,
17 -}: {|
18 - height: number,
19 - width: number,
20 -|}) {
21 - return (
22 - <div className={styles.NoInteractions} style={{height, width}}>
23 - <p className={styles.Header}>No interactions were recorded.</p>
24 - <p>
25 - <a
26 - className={styles.Link}
27 - href="https://reactjs.org/link/interaction-tracing"
28 - rel="noopener noreferrer"
29 - target="_blank">
30 - Learn more about the interaction tracing API here.
31 - </a>
32 - </p>
33 - </div>
34 - );
35 -}
packages/react-devtools-shared/src/devtools/views/Profiler/Profiler.js
-14
@@ -15,14 +15,12 @@ import TabBar from '../TabBar';
15 import ClearProfilingDataButton from './ClearProfilingDataButton';
16 import CommitFlamegraph from './CommitFlamegraph';
17 import CommitRanked from './CommitRanked';
18 -import Interactions from './Interactions';
18 import RootSelector from './RootSelector';
19 import RecordToggle from './RecordToggle';
20 import ReloadAndProfileButton from './ReloadAndProfileButton';
21 import ProfilingImportExportButtons from './ProfilingImportExportButtons';
22 import SnapshotSelector from './SnapshotSelector';
23 import SidebarCommitInfo from './SidebarCommitInfo';
25 -import SidebarInteractions from './SidebarInteractions';
24 import SidebarSelectedFiberInfo from './SidebarSelectedFiberInfo';
25 import SettingsModal from 'react-devtools-shared/src/devtools/views/Settings/SettingsModal';
26 import SettingsModalContextToggle from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContextToggle';
@@ -53,9 +51,6 @@ function Profiler(_: {||}) {
51 case 'ranked-chart':
52 view = <CommitRanked />;
53 break;
56 - case 'interactions':
57 - view = <Interactions />;
58 - break;
54 default:
55 break;
56 }
@@ -72,9 +67,6 @@ function Profiler(_: {||}) {
67 let sidebar = null;
68 if (!isProfiling && !isProcessingData && didRecordCommits) {
69 switch (selectedTabID) {
75 - case 'interactions':
76 - sidebar = <SidebarInteractions />;
77 - break;
70 case 'flame-chart':
71 case 'ranked-chart':
72 // TRICKY
@@ -148,12 +140,6 @@ const tabs = [
140 label: 'Ranked',
141 title: 'Ranked chart',
142 },
151 - {
152 - id: 'interactions',
153 - icon: 'interactions',
154 - label: 'Interactions',
155 - title: 'Profiled interactions',
156 - },
143 ];
144
145 const NoProfilingData = () => (
packages/react-devtools-shared/src/devtools/views/Profiler/ProfilerContext.js
+1 -17
@@ -19,7 +19,7 @@ import {StoreContext} from '../context';
19
20 import type {ProfilingDataFrontend} from './types';
21
22 -export type TabID = 'flame-chart' | 'ranked-chart' | 'interactions';
22 +export type TabID = 'flame-chart' | 'ranked-chart';
23
24 export type Context = {|
25 // Which tab is selected in the Profiler UI?
@@ -64,10 +64,6 @@ export type Context = {|
64 selectedFiberID: number | null,
65 selectedFiberName: string | null,
66 selectFiber: (id: number | null, name: string | null) => void,
67 -
68 - // Which interaction is currently selected in the Interactions graph?
69 - selectedInteractionID: number | null,
70 - selectInteraction: (id: number | null) => void,
67 |};
68
69 const ProfilerContext = createContext<Context>(((null: any): Context));
@@ -216,9 +212,6 @@ function ProfilerContextController({children}: Props) {
212 null,
213 );
214 const [selectedTabID, selectTab] = useState<TabID>('flame-chart');
219 - const [selectedInteractionID, selectInteraction] = useState<number | null>(
220 - null,
221 - );
215
216 if (isProfiling) {
217 batchedUpdates(() => {
@@ -229,9 +222,6 @@ function ProfilerContextController({children}: Props) {
222 selectFiberID(null);
223 selectFiberName(null);
224 }
232 - if (selectedInteractionID !== null) {
233 - selectInteraction(null);
234 - }
225 });
226 }
227
@@ -262,9 +252,6 @@ function ProfilerContextController({children}: Props) {
252 selectedFiberID,
253 selectedFiberName,
254 selectFiber,
265 -
266 - selectedInteractionID,
267 - selectInteraction,
255 }),
256 [
257 selectedTabID,
@@ -293,9 +280,6 @@ function ProfilerContextController({children}: Props) {
280 selectedFiberID,
281 selectedFiberName,
282 selectFiber,
296 -
297 - selectedInteractionID,
298 - selectInteraction,
283 ],
284 );
285
packages/react-devtools-shared/src/devtools/views/Profiler/SidebarCommitInfo.css
-27
@@ -23,33 +23,6 @@
23 margin: 0 0 0.5rem;
24 }
25
26 -.NoInteractions {
27 - color: var(--color-dim);
28 -}
29 -
30 -.Interactions {
31 - margin: 0 0 0.5rem;
32 -}
33 -.NoInteractions,
34 -.Interaction {
35 - display: block;
36 - width: 100%;
37 - text-align: left;
38 - background: none;
39 - border: none;
40 - padding: 0.25rem 0.5rem;
41 - color: var(--color-text);
42 -}
43 -.Interaction:focus,
44 -.Interaction:hover {
45 - outline: none;
46 - background-color: var(--color-background-hover);
47 -}
48 -
49 -.NoInteractions {
50 - color: var(--color-dim);
51 -}
52 -
26 .Label {
27 overflow: hidden;
28 text-overflow: ellipsis;
packages/react-devtools-shared/src/devtools/views/Profiler/SidebarCommitInfo.js
+1 -36
@@ -20,12 +20,7 @@ import styles from './SidebarCommitInfo.css';
20 export type Props = {||};
21
22 export default function SidebarCommitInfo(_: Props) {
23 - const {
24 - selectedCommitIndex,
25 - rootID,
26 - selectInteraction,
27 - selectTab,
28 - } = useContext(ProfilerContext);
23 + const {selectedCommitIndex, rootID} = useContext(ProfilerContext);
24
25 const {profilerStore} = useContext(StoreContext);
26
@@ -33,22 +28,15 @@ export default function SidebarCommitInfo(_: Props) {
28 return <div className={styles.NothingSelected}>Nothing selected</div>;
29 }
30
36 - const {interactions} = profilerStore.getDataForRoot(rootID);
31 const {
32 duration,
33 effectDuration,
40 - interactionIDs,
34 passiveEffectDuration,
35 priorityLevel,
36 timestamp,
37 updaters,
38 } = profilerStore.getCommitData(rootID, selectedCommitIndex);
39
47 - const viewInteraction = interactionID => {
48 - selectTab('interactions');
49 - selectInteraction(interactionID);
50 - };
51 -
40 const hasCommitPhaseDurations =
41 effectDuration !== null || passiveEffectDuration !== null;
42
@@ -120,29 +108,6 @@ export default function SidebarCommitInfo(_: Props) {
108 <Updaters commitTree={commitTree} updaters={updaters} />
109 </li>
110 )}
123 -
124 - <li className={styles.Interactions}>
125 - <label className={styles.Label}>Interactions</label>:
126 - <div className={styles.InteractionList}>
127 - {interactionIDs.length === 0 ? (
128 - <div className={styles.NoInteractions}>(none)</div>
129 - ) : null}
130 - {interactionIDs.map(interactionID => {
131 - const interaction = interactions.get(interactionID);
132 - if (interaction == null) {
133 - throw Error(`Invalid interaction "${interactionID}"`);
134 - }
135 - return (
136 - <button
137 - key={interactionID}
138 - className={styles.Interaction}
139 - onClick={() => viewInteraction(interactionID)}>
140 - {interaction.name}
141 - </button>
142 - );
143 - })}
144 - </div>
145 - </li>
111 </ul>
112 </div>
113 </Fragment>
packages/react-devtools-shared/src/devtools/views/Profiler/SidebarInteractions.css deleted
-55
@@ -1,55 +0,0 @@
1 -.Toolbar {
2 - height: 2.25rem;
3 - padding: 0 0.5rem;
4 - flex: 0 0 auto;
5 - display: flex;
6 - align-items: center;
7 -}
8 -
9 -.Content {
10 - padding: 0.5rem;
11 - user-select: none;
12 - border-top: 1px solid var(--color-border);
13 - overflow: auto;
14 -}
15 -
16 -.Name {
17 - font-size: var(--font-size-sans-large);
18 - white-space: nowrap;
19 - overflow-x: hidden;
20 - text-overflow: ellipsis;
21 -}
22 -
23 -.NothingSelected {
24 - display: flex;
25 - align-items: center;
26 - justify-content: center;
27 - height: 100%;
28 - color: var(--color-dim);
29 -}
30 -
31 -.Commits {
32 - font-weight: bold;
33 -}
34 -
35 -.List {
36 - list-style: none;
37 - margin: 0;
38 - padding: 0;
39 -}
40 -
41 -.ListItem {
42 - display: flex;
43 - flex-direction: row;
44 - align-items: center;
45 - padding: 0.25rem 0.5rem;
46 -}
47 -.ListItem:hover {
48 - background-color: var(--color-background-hover);
49 -}
50 -
51 -.CommitBox {
52 - width: 20px;
53 - height: 20px;
54 - margin-right: 0.5rem;
55 -}
packages/react-devtools-shared/src/devtools/views/Profiler/SidebarInteractions.js deleted
-100
@@ -1,100 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import * as React from 'react';
11 -import {Fragment, useContext} from 'react';
12 -import {ProfilerContext} from './ProfilerContext';
13 -import {formatDuration, formatTime} from './utils';
14 -import {StoreContext} from '../context';
15 -import {getGradientColor} from './utils';
16 -
17 -import styles from './SidebarInteractions.css';
18 -
19 -export type Props = {||};
20 -
21 -export default function SidebarInteractions(_: Props) {
22 - const {
23 - selectedInteractionID,
24 - rootID,
25 - selectCommitIndex,
26 - selectTab,
27 - } = useContext(ProfilerContext);
28 -
29 - const {profilerStore} = useContext(StoreContext);
30 - const {profilingCache} = profilerStore;
31 -
32 - if (selectedInteractionID === null) {
33 - return <div className={styles.NothingSelected}>Nothing selected</div>;
34 - }
35 -
36 - const {interactionCommits, interactions} = profilerStore.getDataForRoot(
37 - ((rootID: any): number),
38 - );
39 - const interaction = interactions.get(selectedInteractionID);
40 - if (interaction == null) {
41 - throw Error(
42 - `Could not find interaction by selected interaction id "${selectedInteractionID}"`,
43 - );
44 - }
45 -
46 - const {maxCommitDuration} = profilingCache.getInteractionsChartData({
47 - rootID: ((rootID: any): number),
48 - });
49 -
50 - const viewCommit = (commitIndex: number) => {
51 - selectTab('flame-chart');
52 - selectCommitIndex(commitIndex);
53 - };
54 -
55 - const listItems: Array<React$Node> = [];
56 - const commitIndices = interactionCommits.get(selectedInteractionID);
57 - if (commitIndices != null) {
58 - commitIndices.forEach(commitIndex => {
59 - const {duration, timestamp} = profilerStore.getCommitData(
60 - ((rootID: any): number),
61 - commitIndex,
62 - );
63 -
64 - listItems.push(
65 - <li
66 - key={commitIndex}
67 - className={styles.ListItem}
68 - onClick={() => viewCommit(commitIndex)}>
69 - <div
70 - className={styles.CommitBox}
71 - style={{
72 - backgroundColor: getGradientColor(
73 - Math.min(1, Math.max(0, duration / maxCommitDuration)) || 0,
74 - ),
75 - }}
76 - />
77 - <div>
78 - timestamp: {formatTime(timestamp)}s
79 - <br />
80 - duration: {formatDuration(duration)}ms
81 - </div>
82 - </li>,
83 - );
84 - });
85 - }
86 -
87 - return (
88 - <Fragment>
89 - <div className={styles.Toolbar}>
90 - <div className={styles.Name} title={interaction.name}>
91 - {interaction.name}
92 - </div>
93 - </div>
94 - <div className={styles.Content}>
95 - <div className={styles.Commits}>Commits:</div>
96 - <ul className={styles.List}>{listItems}</ul>
97 - </div>
98 - </Fragment>
99 - );
100 -}
packages/react-devtools-shared/src/devtools/views/Profiler/constants.js
-2
@@ -8,7 +8,5 @@
8 */
9
10 export const barWidthThreshold = 2;
11 -export const interactionCommitSize = 10;
12 -export const interactionLabelWidth = 200;
11 export const maxBarWidth = 30;
12 export const minBarWidth = 5;
packages/react-devtools-shared/src/devtools/views/Profiler/types.js
-19
@@ -26,12 +26,6 @@ export type CommitTree = {|
26 rootID: number,
27 |};
28
29 -export type Interaction = {|
30 - id: number,
31 - name: string,
32 - timestamp: number,
33 -|};
34 -
29 export type SnapshotNode = {|
30 id: number,
31 children: Array<number>,
@@ -69,9 +63,6 @@ export type CommitDataFrontend = {|
63 // Fibers that did not render will not have entries in this Map.
64 fiberSelfDurations: Map<number, number>,
65
72 - // Which interactions (IDs) were associated with this commit.
73 - interactionIDs: Array<number>,
74 -
66 // How long was the passive commit phase?
67 // Note that not all builds of React expose this property.
68 passiveEffectDuration: number | null,
@@ -98,12 +89,6 @@ export type ProfilingDataForRootFrontend = {|
89 // This info can be used along with commitOperations to reconstruct the tree for any commit.
90 initialTreeBaseDurations: Map<number, number>,
91
101 - // All interactions recorded (for this root) during the current session.
102 - interactionCommits: Map<number, Array<number>>,
103 -
104 - // All interactions recorded (for this root) during the current session.
105 - interactions: Map<number, Interaction>,
106 -
92 // List of tree mutation that occur during profiling.
93 // These mutations can be used along with initial snapshots to reconstruct the tree for any commit.
94 operations: Array<Array<number>>,
@@ -131,7 +116,6 @@ export type CommitDataExport = {|
116 fiberActualDurations: Array<[number, number]>,
117 // Tuple of fiber ID and computed "self" duration
118 fiberSelfDurations: Array<[number, number]>,
134 - interactionIDs: Array<number>,
119 passiveEffectDuration: number | null,
120 priorityLevel: string | null,
121 timestamp: number,
@@ -143,9 +127,6 @@ export type ProfilingDataForRootExport = {|
127 displayName: string,
128 // Tuple of Fiber ID and base duration
129 initialTreeBaseDurations: Array<[number, number]>,
146 - // Tuple of Interaction ID and commit indices
147 - interactionCommits: Array<[number, Array<number>]>,
148 - interactions: Array<[number, Interaction]>,
130 operations: Array<Array<number>>,
131 rootID: number,
132 snapshots: Array<[number, SnapshotNode]>,
packages/react-devtools-shared/src/devtools/views/Profiler/utils.js
+2 -29
@@ -43,14 +43,7 @@ export function prepareProfilingDataFrontendFromBackendAndStore(
43
44 dataBackends.forEach(dataBackend => {
45 dataBackend.dataForRoots.forEach(
46 - ({
47 - commitData,
48 - displayName,
49 - initialTreeBaseDurations,
50 - interactionCommits,
51 - interactions,
52 - rootID,
53 - }) => {
46 + ({commitData, displayName, initialTreeBaseDurations, rootID}) => {
47 const operations = operationsByRootID.get(rootID);
48 if (operations == null) {
49 throw Error(
@@ -66,12 +59,7 @@ export function prepareProfilingDataFrontendFromBackendAndStore(
59 }
60
61 // Do not filter empty commits from the profiler data!
69 - // We used to do this, but it was error prone (see #18798).
70 - // A commit may appear to be empty (no actual durations) because of component filters,
71 - // but filtering these empty commits causes interaction commit indices to be off by N.
72 - // This not only corrupts the resulting data, but also potentially causes runtime errors.
73 - //
74 - // For that matter, hiding "empty" commits might cause confusion too.
62 + // Hiding "empty" commits might cause confusion too.
63 // A commit *did happen* even if none of the components the Profiler is showing were involved.
64 const convertedCommitData = commitData.map(
65 (commitDataBackend, commitIndex) => ({
@@ -85,7 +73,6 @@ export function prepareProfilingDataFrontendFromBackendAndStore(
73 commitDataBackend.fiberActualDurations,
74 ),
75 fiberSelfDurations: new Map(commitDataBackend.fiberSelfDurations),
88 - interactionIDs: commitDataBackend.interactionIDs,
76 passiveEffectDuration: commitDataBackend.passiveEffectDuration,
77 priorityLevel: commitDataBackend.priorityLevel,
78 timestamp: commitDataBackend.timestamp,
@@ -113,8 +100,6 @@ export function prepareProfilingDataFrontendFromBackendAndStore(
100 commitData: convertedCommitData,
101 displayName,
102 initialTreeBaseDurations: new Map(initialTreeBaseDurations),
116 - interactionCommits: new Map(interactionCommits),
117 - interactions: new Map(interactions),
103 operations,
104 rootID,
105 snapshots,
@@ -144,8 +129,6 @@ export function prepareProfilingDataFrontendFromExport(
129 commitData,
130 displayName,
131 initialTreeBaseDurations,
147 - interactionCommits,
148 - interactions,
132 operations,
133 rootID,
134 snapshots,
@@ -158,7 +141,6 @@ export function prepareProfilingDataFrontendFromExport(
141 effectDuration,
142 fiberActualDurations,
143 fiberSelfDurations,
161 - interactionIDs,
144 passiveEffectDuration,
145 priorityLevel,
146 timestamp,
@@ -170,7 +152,6 @@ export function prepareProfilingDataFrontendFromExport(
152 effectDuration,
153 fiberActualDurations: new Map(fiberActualDurations),
154 fiberSelfDurations: new Map(fiberSelfDurations),
173 - interactionIDs,
155 passiveEffectDuration,
156 priorityLevel,
157 timestamp,
@@ -179,8 +160,6 @@ export function prepareProfilingDataFrontendFromExport(
160 ),
161 displayName,
162 initialTreeBaseDurations: new Map(initialTreeBaseDurations),
182 - interactionCommits: new Map(interactionCommits),
183 - interactions: new Map(interactions),
163 operations,
164 rootID,
165 snapshots: new Map(snapshots),
@@ -201,8 +180,6 @@ export function prepareProfilingDataExport(
180 commitData,
181 displayName,
182 initialTreeBaseDurations,
204 - interactionCommits,
205 - interactions,
183 operations,
184 rootID,
185 snapshots,
@@ -215,7 +192,6 @@ export function prepareProfilingDataExport(
192 effectDuration,
193 fiberActualDurations,
194 fiberSelfDurations,
218 - interactionIDs,
195 passiveEffectDuration,
196 priorityLevel,
197 timestamp,
@@ -229,7 +205,6 @@ export function prepareProfilingDataExport(
205 effectDuration,
206 fiberActualDurations: Array.from(fiberActualDurations.entries()),
207 fiberSelfDurations: Array.from(fiberSelfDurations.entries()),
232 - interactionIDs,
208 passiveEffectDuration,
209 priorityLevel,
210 timestamp,
@@ -240,8 +215,6 @@ export function prepareProfilingDataExport(
215 initialTreeBaseDurations: Array.from(
216 initialTreeBaseDurations.entries(),
217 ),
243 - interactionCommits: Array.from(interactionCommits.entries()),
244 - interactions: Array.from(interactions.entries()),
218 operations,
219 rootID,
220 snapshots: Array.from(snapshots.entries()),
packages/react-devtools-shared/src/devtools/views/root.css
-4
@@ -211,8 +211,4 @@
211 Courier, monospace;
212 --font-family-sans: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica,
213 Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
214 -
215 - /* Constant values shared between JS and CSS */
216 - --interaction-commit-size: 10px;
217 - --interaction-label-width: 200px;
214 }
packages/react-devtools-shell/src/app/InteractionTracing/index.js deleted
-103
@@ -1,103 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import * as React from 'react';
11 -import {
12 - Fragment,
13 - useCallback,
14 - useLayoutEffect,
15 - useEffect,
16 - useState,
17 -} from 'react';
18 -import {unstable_batchedUpdates as batchedUpdates} from 'react-dom';
19 -import {
20 - unstable_trace as trace,
21 - unstable_wrap as wrap,
22 -} from 'scheduler/tracing';
23 -
24 -function sleep(ms) {
25 - const start = performance.now();
26 - let now;
27 - do {
28 - now = performance.now();
29 - } while (now - ms < start);
30 -}
31 -
32 -export default function InteractionTracing() {
33 - const [count, setCount] = useState(0);
34 - const [shouldCascade, setShouldCascade] = useState(false);
35 -
36 - const handleUpdate = useCallback(() => {
37 - trace('count', performance.now(), () => {
38 - setTimeout(
39 - wrap(() => {
40 - setCount(count + 1);
41 - }),
42 - count * 100,
43 - );
44 - });
45 - }, [count]);
46 -
47 - const handleCascadingUpdate = useCallback(() => {
48 - trace('cascade', performance.now(), () => {
49 - setTimeout(
50 - wrap(() => {
51 - batchedUpdates(() => {
52 - setCount(count + 1);
53 - setShouldCascade(true);
54 - });
55 - }),
56 - count * 100,
57 - );
58 - });
59 - }, [count]);
60 -
61 - const handleMultiple = useCallback(() => {
62 - trace('first', performance.now(), () => {
63 - trace('second', performance.now(), () => {
64 - setTimeout(
65 - wrap(() => {
66 - setCount(count + 1);
67 - }),
68 - count * 100,
69 - );
70 - });
71 - });
72 - }, [count]);
73 -
74 - useEffect(() => {
75 - if (shouldCascade) {
76 - setTimeout(
77 - wrap(() => {
78 - setShouldCascade(false);
79 - }),
80 - count * 100,
81 - );
82 - }
83 - }, [count, shouldCascade]);
84 -
85 - useLayoutEffect(() => {
86 - sleep(150);
87 - });
88 -
89 - useEffect(() => {
90 - sleep(300);
91 - });
92 -
93 - return (
94 - <Fragment>
95 - <h1>Interaction Tracing</h1>
96 - <button onClick={handleUpdate}>Update ({count})</button>
97 - <button onClick={handleCascadingUpdate}>
98 - Cascading Update ({count}, {shouldCascade ? 'true' : 'false'})
99 - </button>
100 - <button onClick={handleMultiple}>Multiple</button>
101 - </Fragment>
102 - );
103 -}
packages/react-devtools-shell/src/app/index.js
-2
@@ -14,7 +14,6 @@ import ElementTypes from './ElementTypes';
14 import Hydration from './Hydration';
15 import InlineWarnings from './InlineWarnings';
16 import InspectableElements from './InspectableElements';
17 -import InteractionTracing from './InteractionTracing';
17 import ReactNativeWeb from './ReactNativeWeb';
18 import ToDoList from './ToDoList';
19 import Toggle from './Toggle';
@@ -48,7 +47,6 @@ function mountHelper(App) {
47
48 function mountTestApp() {
49 mountHelper(ToDoList);
51 - mountHelper(InteractionTracing);
50 mountHelper(InspectableElements);
51 mountHelper(Hydration);
52 mountHelper(ElementTypes);
packages/react-devtools/OVERVIEW.md
-1
@@ -286,7 +286,6 @@ When profiling begins, the frontend takes a snapshot/copy of each root. This sna
286 When profiling begins, the backend records the base durations of each fiber currently in the tree. While profiling is in progress, the backend also stores some information about each commit, including:
287 * Commit time and duration
288 * Which elements were rendered during that commit
289 -* Which interactions (if any) were part of the commit
289 * Which props and state changed (if enabled in profiler settings)
290
291 This information will eventually be required by the frontend in order to render its profiling graphs, but it will not be sent across the bridge until profiling has completed (to minimize the performance impact of profiling).
packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js
-83
@@ -10,7 +10,6 @@
10 let React;
11 let ReactDOM;
12 let ReactTestUtils;
13 -let SchedulerTracing;
13 let Scheduler;
14 let act;
15 let container;
@@ -119,7 +118,6 @@ function runActTests(label, render, unmount, rerender) {
118 React = require('react');
119 ReactDOM = require('react-dom');
120 ReactTestUtils = require('react-dom/test-utils');
122 - SchedulerTracing = require('scheduler/tracing');
121 Scheduler = require('scheduler');
122 act = ReactTestUtils.act;
123 container = document.createElement('div');
@@ -497,87 +495,6 @@ function runActTests(label, render, unmount, rerender) {
495 });
496 });
497
500 - describe('interaction tracing', () => {
501 - if (__DEV__) {
502 - it('should correctly trace interactions for sync roots', () => {
503 - let expectedInteraction;
504 -
505 - const Component = jest.fn(() => {
506 - expect(expectedInteraction).toBeDefined();
507 -
508 - const interactions = SchedulerTracing.unstable_getCurrent();
509 - expect(interactions.size).toBe(1);
510 - expect(interactions).toContain(expectedInteraction);
511 -
512 - return null;
513 - });
514 -
515 - act(() => {
516 - SchedulerTracing.unstable_trace(
517 - 'mount traced inside act',
518 - performance.now(),
519 - () => {
520 - const interactions = SchedulerTracing.unstable_getCurrent();
521 - expect(interactions.size).toBe(1);
522 - expectedInteraction = Array.from(interactions)[0];
523 -
524 - render(<Component />, container);
525 - },
526 - );
527 - });
528 -
529 - act(() => {
530 - SchedulerTracing.unstable_trace(
531 - 'update traced inside act',
532 - performance.now(),
533 - () => {
534 - const interactions = SchedulerTracing.unstable_getCurrent();
535 - expect(interactions.size).toBe(1);
536 - expectedInteraction = Array.from(interactions)[0];
537 -
538 - rerender(<Component />);
539 - },
540 - );
541 - });
542 -
543 - const secondContainer = document.createElement('div');
544 -
545 - SchedulerTracing.unstable_trace(
546 - 'mount traced outside act',
547 - performance.now(),
548 - () => {
549 - act(() => {
550 - const interactions = SchedulerTracing.unstable_getCurrent();
551 - expect(interactions.size).toBe(1);
552 - expectedInteraction = Array.from(interactions)[0];
553 -
554 - render(<Component />, secondContainer);
555 - });
556 - },
557 - );
558 -
559 - SchedulerTracing.unstable_trace(
560 - 'update traced outside act',
561 - performance.now(),
562 - () => {
563 - act(() => {
564 - const interactions = SchedulerTracing.unstable_getCurrent();
565 - expect(interactions.size).toBe(1);
566 - expectedInteraction = Array.from(interactions)[0];
567 -
568 - rerender(<Component />);
569 - });
570 - },
571 - );
572 -
573 - expect(Component).toHaveBeenCalledTimes(
574 - label === 'legacy mode' ? 4 : 8,
575 - );
576 - unmount(secondContainer);
577 - });
578 - }
579 - });
580 -
498 describe('error propagation', () => {
499 it('propagates errors - sync', () => {
500 let err;
packages/react-dom/src/__tests__/ReactUpdates-test.js
-80
@@ -1684,84 +1684,4 @@ describe('ReactUpdates', () => {
1684 expect(container.textContent).toBe('1000');
1685 });
1686 }
1687 -
1688 - if (__DEV__) {
1689 - it('should properly trace interactions within batched updates', () => {
1690 - const SchedulerTracing = require('scheduler/tracing');
1691 -
1692 - let expectedInteraction;
1693 -
1694 - const container = document.createElement('div');
1695 -
1696 - const Component = jest.fn(() => {
1697 - expect(expectedInteraction).toBeDefined();
1698 -
1699 - const interactions = SchedulerTracing.unstable_getCurrent();
1700 - expect(interactions.size).toBe(1);
1701 - expect(interactions).toContain(expectedInteraction);
1702 -
1703 - return null;
1704 - });
1705 -
1706 - ReactDOM.unstable_batchedUpdates(() => {
1707 - SchedulerTracing.unstable_trace(
1708 - 'mount traced inside a batched update',
1709 - 1,
1710 - () => {
1711 - const interactions = SchedulerTracing.unstable_getCurrent();
1712 - expect(interactions.size).toBe(1);
1713 - expectedInteraction = Array.from(interactions)[0];
1714 -
1715 - ReactDOM.render(<Component />, container);
1716 - },
1717 - );
1718 - });
1719 -
1720 - ReactDOM.unstable_batchedUpdates(() => {
1721 - SchedulerTracing.unstable_trace(
1722 - 'update traced inside a batched update',
1723 - 2,
1724 - () => {
1725 - const interactions = SchedulerTracing.unstable_getCurrent();
1726 - expect(interactions.size).toBe(1);
1727 - expectedInteraction = Array.from(interactions)[0];
1728 -
1729 - ReactDOM.render(<Component />, container);
1730 - },
1731 - );
1732 - });
1733 -
1734 - const secondContainer = document.createElement('div');
1735 -
1736 - SchedulerTracing.unstable_trace(
1737 - 'mount traced outside a batched update',
1738 - 3,
1739 - () => {
1740 - ReactDOM.unstable_batchedUpdates(() => {
1741 - const interactions = SchedulerTracing.unstable_getCurrent();
1742 - expect(interactions.size).toBe(1);
1743 - expectedInteraction = Array.from(interactions)[0];
1744 -
1745 - ReactDOM.render(<Component />, secondContainer);
1746 - });
1747 - },
1748 - );
1749 -
1750 - SchedulerTracing.unstable_trace(
1751 - 'update traced outside a batched update',
1752 - 4,
1753 - () => {
1754 - ReactDOM.unstable_batchedUpdates(() => {
1755 - const interactions = SchedulerTracing.unstable_getCurrent();
1756 - expect(interactions.size).toBe(1);
1757 - expectedInteraction = Array.from(interactions)[0];
1758 -
1759 - ReactDOM.render(<Component />, container);
1760 - });
1761 - },
1762 - );
1763 -
1764 - expect(Component).toHaveBeenCalledTimes(4);
1765 - });
1766 - }
1687 });
packages/react-reconciler/src/ReactFiberBeginWork.new.js
+1 -19
@@ -78,7 +78,6 @@ import {
78 disableModulePatternComponents,
79 enableProfilerCommitHooks,
80 enableProfilerTimer,
81 - enableSchedulerTracing,
81 enableSuspenseServerRenderer,
82 warnAboutDefaultPropsOnFunctionComponents,
83 enableScopeAPI,
@@ -200,7 +199,6 @@ import {
199 isSimpleFunctionComponent,
200 } from './ReactFiber.new';
201 import {
203 - markSpawnedWork,
202 retryDehydratedSuspenseBoundary,
203 scheduleUpdateOnFiber,
204 renderDidSuspendDelayIfPossible,
@@ -211,7 +209,6 @@ import {
209 RetryAfterError,
210 NoContext,
211 } from './ReactFiberWorkLoop.new';
214 -import {unstable_wrap as Schedule_tracing_wrap} from 'scheduler/tracing';
212 import {setWorkInProgressVersion} from './ReactMutableSource.new';
213 import {
214 requestCacheFromPool,
@@ -635,9 +632,6 @@ function updateOffscreenComponent(
632 }
633
634 // Schedule this fiber to re-render at offscreen priority. Then bailout.
638 - if (enableSchedulerTracing) {
639 - markSpawnedWork((OffscreenLane: Lane));
640 - }
635 workInProgress.lanes = workInProgress.childLanes = laneToLanes(
636 OffscreenLane,
637 );
@@ -1939,9 +1933,6 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) {
1933 // RetryLane even if it's the one currently rendering since we're leaving
1934 // it behind on this node.
1935 workInProgress.lanes = SomeRetryLane;
1942 - if (enableSchedulerTracing) {
1943 - markSpawnedWork(SomeRetryLane);
1944 - }
1936 return fallbackFragment;
1937 } else {
1938 return mountSuspensePrimaryChildren(
@@ -2397,17 +2388,11 @@ function mountDehydratedSuspenseComponent(
2388 // time. This will mean that Suspense timeouts are slightly shifted to later than
2389 // they should be.
2390 // Schedule a normal pri update to render this content.
2400 - if (enableSchedulerTracing) {
2401 - markSpawnedWork(DefaultHydrationLane);
2402 - }
2391 workInProgress.lanes = laneToLanes(DefaultHydrationLane);
2392 } else {
2393 // We'll continue hydrating the rest at offscreen priority since we'll already
2394 // be showing the right content coming from the server, it is no rush.
2395 workInProgress.lanes = laneToLanes(OffscreenLane);
2408 - if (enableSchedulerTracing) {
2409 - markSpawnedWork(OffscreenLane);
2410 - }
2396 }
2397 return null;
2398 }
@@ -2520,10 +2505,7 @@ function updateDehydratedSuspenseComponent(
2505 // Leave the child in place. I.e. the dehydrated fragment.
2506 workInProgress.child = current.child;
2507 // Register a callback to retry this boundary once the server has sent the result.
2523 - let retry = retryDehydratedSuspenseBoundary.bind(null, current);
2524 - if (enableSchedulerTracing) {
2525 - retry = Schedule_tracing_wrap(retry);
2526 - }
2508 + const retry = retryDehydratedSuspenseBoundary.bind(null, current);
2509 registerSuspenseInstanceRetry(suspenseInstance, retry);
2510 return null;
2511 } else {
packages/react-reconciler/src/ReactFiberBeginWork.old.js
+1 -19
@@ -78,7 +78,6 @@ import {
78 disableModulePatternComponents,
79 enableProfilerCommitHooks,
80 enableProfilerTimer,
81 - enableSchedulerTracing,
81 enableSuspenseServerRenderer,
82 warnAboutDefaultPropsOnFunctionComponents,
83 enableScopeAPI,
@@ -200,7 +199,6 @@ import {
199 isSimpleFunctionComponent,
200 } from './ReactFiber.old';
201 import {
203 - markSpawnedWork,
202 retryDehydratedSuspenseBoundary,
203 scheduleUpdateOnFiber,
204 renderDidSuspendDelayIfPossible,
@@ -211,7 +209,6 @@ import {
209 RetryAfterError,
210 NoContext,
211 } from './ReactFiberWorkLoop.old';
214 -import {unstable_wrap as Schedule_tracing_wrap} from 'scheduler/tracing';
212 import {setWorkInProgressVersion} from './ReactMutableSource.old';
213 import {
214 requestCacheFromPool,
@@ -635,9 +632,6 @@ function updateOffscreenComponent(
632 }
633
634 // Schedule this fiber to re-render at offscreen priority. Then bailout.
638 - if (enableSchedulerTracing) {
639 - markSpawnedWork((OffscreenLane: Lane));
640 - }
635 workInProgress.lanes = workInProgress.childLanes = laneToLanes(
636 OffscreenLane,
637 );
@@ -1939,9 +1933,6 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) {
1933 // RetryLane even if it's the one currently rendering since we're leaving
1934 // it behind on this node.
1935 workInProgress.lanes = SomeRetryLane;
1942 - if (enableSchedulerTracing) {
1943 - markSpawnedWork(SomeRetryLane);
1944 - }
1936 return fallbackFragment;
1937 } else {
1938 return mountSuspensePrimaryChildren(
@@ -2397,17 +2388,11 @@ function mountDehydratedSuspenseComponent(
2388 // time. This will mean that Suspense timeouts are slightly shifted to later than
2389 // they should be.
2390 // Schedule a normal pri update to render this content.
2400 - if (enableSchedulerTracing) {
2401 - markSpawnedWork(DefaultHydrationLane);
2402 - }
2391 workInProgress.lanes = laneToLanes(DefaultHydrationLane);
2392 } else {
2393 // We'll continue hydrating the rest at offscreen priority since we'll already
2394 // be showing the right content coming from the server, it is no rush.
2395 workInProgress.lanes = laneToLanes(OffscreenLane);
2408 - if (enableSchedulerTracing) {
2409 - markSpawnedWork(OffscreenLane);
2410 - }
2396 }
2397 return null;
2398 }
@@ -2520,10 +2505,7 @@ function updateDehydratedSuspenseComponent(
2505 // Leave the child in place. I.e. the dehydrated fragment.
2506 workInProgress.child = current.child;
2507 // Register a callback to retry this boundary once the server has sent the result.
2523 - let retry = retryDehydratedSuspenseBoundary.bind(null, current);
2524 - if (enableSchedulerTracing) {
2525 - retry = Schedule_tracing_wrap(retry);
2526 - }
2508 + const retry = retryDehydratedSuspenseBoundary.bind(null, current);
2509 registerSuspenseInstanceRetry(suspenseInstance, retry);
2510 return null;
2511 } else {
packages/react-reconciler/src/ReactFiberCommitWork.new.js
+16 -55
@@ -25,9 +25,7 @@ import type {Wakeable} from 'shared/ReactTypes';
25 import type {OffscreenState} from './ReactFiberOffscreenComponent';
26 import type {HookFlags} from './ReactHookEffectTags';
27
28 -import {unstable_wrap as Schedule_tracing_wrap} from 'scheduler/tracing';
28 import {
30 - enableSchedulerTracing,
29 enableProfilerTimer,
30 enableProfilerCommitHooks,
31 enableProfilerNestedUpdatePhase,
@@ -641,17 +639,7 @@ export function commitPassiveEffectDurations(
639 }
640
641 if (typeof onPostCommit === 'function') {
644 - if (enableSchedulerTracing) {
645 - onPostCommit(
646 - id,
647 - phase,
648 - passiveEffectDuration,
649 - commitTime,
650 - finishedRoot.memoizedInteractions,
651 - );
652 - } else {
653 - onPostCommit(id, phase, passiveEffectDuration, commitTime);
654 - }
642 + onPostCommit(id, phase, passiveEffectDuration, commitTime);
643 }
644
645 // Bubble times to the next nearest ancestor Profiler.
@@ -919,46 +907,24 @@ function commitLayoutEffectOnFiber(
907 }
908
909 if (typeof onRender === 'function') {
922 - if (enableSchedulerTracing) {
923 - onRender(
924 - finishedWork.memoizedProps.id,
925 - phase,
926 - finishedWork.actualDuration,
927 - finishedWork.treeBaseDuration,
928 - finishedWork.actualStartTime,
929 - commitTime,
930 - finishedRoot.memoizedInteractions,
931 - );
932 - } else {
933 - onRender(
934 - finishedWork.memoizedProps.id,
935 - phase,
936 - finishedWork.actualDuration,
937 - finishedWork.treeBaseDuration,
938 - finishedWork.actualStartTime,
939 - commitTime,
940 - );
941 - }
910 + onRender(
911 + finishedWork.memoizedProps.id,
912 + phase,
913 + finishedWork.actualDuration,
914 + finishedWork.treeBaseDuration,
915 + finishedWork.actualStartTime,
916 + commitTime,
917 + );
918 }
919
920 if (enableProfilerCommitHooks) {
921 if (typeof onCommit === 'function') {
946 - if (enableSchedulerTracing) {
947 - onCommit(
948 - finishedWork.memoizedProps.id,
949 - phase,
950 - effectDuration,
951 - commitTime,
952 - finishedRoot.memoizedInteractions,
953 - );
954 - } else {
955 - onCommit(
956 - finishedWork.memoizedProps.id,
957 - phase,
958 - effectDuration,
959 - commitTime,
960 - );
961 - }
922 + onCommit(
923 + finishedWork.memoizedProps.id,
924 + phase,
925 + effectDuration,
926 + commitTime,
927 + );
928 }
929
930 // Schedule a passive effect for this Profiler to call onPostCommit hooks.
@@ -2105,13 +2071,8 @@ function attachSuspenseRetryListeners(finishedWork: Fiber) {
2071 }
2072 wakeables.forEach(wakeable => {
2073 // Memoize using the boundary fiber to prevent redundant listeners.
2108 - let retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);
2074 + const retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);
2075 if (!retryCache.has(wakeable)) {
2110 - if (enableSchedulerTracing) {
2111 - if (wakeable.__reactDoNotTraceInteractions !== true) {
2112 - retry = Schedule_tracing_wrap(retry);
2113 - }
2114 - }
2076 retryCache.add(wakeable);
2077
2078 if (enableUpdaterTracking) {
packages/react-reconciler/src/ReactFiberCommitWork.old.js
+16 -55
@@ -25,9 +25,7 @@ import type {Wakeable} from 'shared/ReactTypes';
25 import type {OffscreenState} from './ReactFiberOffscreenComponent';
26 import type {HookFlags} from './ReactHookEffectTags';
27
28 -import {unstable_wrap as Schedule_tracing_wrap} from 'scheduler/tracing';
28 import {
30 - enableSchedulerTracing,
29 enableProfilerTimer,
30 enableProfilerCommitHooks,
31 enableProfilerNestedUpdatePhase,
@@ -641,17 +639,7 @@ export function commitPassiveEffectDurations(
639 }
640
641 if (typeof onPostCommit === 'function') {
644 - if (enableSchedulerTracing) {
645 - onPostCommit(
646 - id,
647 - phase,
648 - passiveEffectDuration,
649 - commitTime,
650 - finishedRoot.memoizedInteractions,
651 - );
652 - } else {
653 - onPostCommit(id, phase, passiveEffectDuration, commitTime);
654 - }
642 + onPostCommit(id, phase, passiveEffectDuration, commitTime);
643 }
644
645 // Bubble times to the next nearest ancestor Profiler.
@@ -919,46 +907,24 @@ function commitLayoutEffectOnFiber(
907 }
908
909 if (typeof onRender === 'function') {
922 - if (enableSchedulerTracing) {
923 - onRender(
924 - finishedWork.memoizedProps.id,
925 - phase,
926 - finishedWork.actualDuration,
927 - finishedWork.treeBaseDuration,
928 - finishedWork.actualStartTime,
929 - commitTime,
930 - finishedRoot.memoizedInteractions,
931 - );
932 - } else {
933 - onRender(
934 - finishedWork.memoizedProps.id,
935 - phase,
936 - finishedWork.actualDuration,
937 - finishedWork.treeBaseDuration,
938 - finishedWork.actualStartTime,
939 - commitTime,
940 - );
941 - }
910 + onRender(
911 + finishedWork.memoizedProps.id,
912 + phase,
913 + finishedWork.actualDuration,
914 + finishedWork.treeBaseDuration,
915 + finishedWork.actualStartTime,
916 + commitTime,
917 + );
918 }
919
920 if (enableProfilerCommitHooks) {
921 if (typeof onCommit === 'function') {
946 - if (enableSchedulerTracing) {
947 - onCommit(
948 - finishedWork.memoizedProps.id,
949 - phase,
950 - effectDuration,
951 - commitTime,
952 - finishedRoot.memoizedInteractions,
953 - );
954 - } else {
955 - onCommit(
956 - finishedWork.memoizedProps.id,
957 - phase,
958 - effectDuration,
959 - commitTime,
960 - );
961 - }
922 + onCommit(
923 + finishedWork.memoizedProps.id,
924 + phase,
925 + effectDuration,
926 + commitTime,
927 + );
928 }
929
930 // Schedule a passive effect for this Profiler to call onPostCommit hooks.
@@ -2105,13 +2071,8 @@ function attachSuspenseRetryListeners(finishedWork: Fiber) {
2071 }
2072 wakeables.forEach(wakeable => {
2073 // Memoize using the boundary fiber to prevent redundant listeners.
2108 - let retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);
2074 + const retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);
2075 if (!retryCache.has(wakeable)) {
2110 - if (enableSchedulerTracing) {
2111 - if (wakeable.__reactDoNotTraceInteractions !== true) {
2112 - retry = Schedule_tracing_wrap(retry);
2113 - }
2114 - }
2076 retryCache.add(wakeable);
2077
2078 if (enableUpdaterTracking) {
packages/react-reconciler/src/ReactFiberCompleteWork.new.js
-11
@@ -118,7 +118,6 @@ import {
118 getIsHydrating,
119 } from './ReactFiberHydrationContext.new';
120 import {
121 - enableSchedulerTracing,
121 enableSuspenseCallback,
122 enableSuspenseServerRenderer,
123 enableScopeAPI,
@@ -127,7 +126,6 @@ import {
126 enableSuspenseLayoutEffectSemantics,
127 } from 'shared/ReactFeatureFlags';
128 import {
130 - markSpawnedWork,
129 renderDidSuspend,
130 renderDidSuspendDelayIfPossible,
131 renderHasNotSuspendedYet,
@@ -981,9 +979,6 @@ function completeWork(
979 'This is probably a bug in React.',
980 );
981 prepareToHydrateHostSuspenseInstance(workInProgress);
984 - if (enableSchedulerTracing) {
985 - markSpawnedWork(OffscreenLane);
986 - }
982 bubbleProperties(workInProgress);
983 if (enableProfilerTimer) {
984 if ((workInProgress.mode & ProfileMode) !== NoMode) {
@@ -1259,9 +1254,6 @@ function completeWork(
1254 // We can use any RetryLane even if it's the one currently rendering
1255 // since we're leaving it behind on this node.
1256 workInProgress.lanes = SomeRetryLane;
1262 - if (enableSchedulerTracing) {
1263 - markSpawnedWork(SomeRetryLane);
1264 - }
1257 }
1258 } else {
1259 cutOffTailIfNeeded(renderState, false);
@@ -1320,9 +1312,6 @@ function completeWork(
1312 // We can use any RetryLane even if it's the one currently rendering
1313 // since we're leaving it behind on this node.
1314 workInProgress.lanes = SomeRetryLane;
1323 - if (enableSchedulerTracing) {
1324 - markSpawnedWork(SomeRetryLane);
1325 - }
1315 }
1316 }
1317 if (renderState.isBackwards) {
packages/react-reconciler/src/ReactFiberCompleteWork.old.js
-11
@@ -118,7 +118,6 @@ import {
118 getIsHydrating,
119 } from './ReactFiberHydrationContext.old';
120 import {
121 - enableSchedulerTracing,
121 enableSuspenseCallback,
122 enableSuspenseServerRenderer,
123 enableScopeAPI,
@@ -127,7 +126,6 @@ import {
126 enableSuspenseLayoutEffectSemantics,
127 } from 'shared/ReactFeatureFlags';
128 import {
130 - markSpawnedWork,
129 renderDidSuspend,
130 renderDidSuspendDelayIfPossible,
131 renderHasNotSuspendedYet,
@@ -981,9 +979,6 @@ function completeWork(
979 'This is probably a bug in React.',
980 );
981 prepareToHydrateHostSuspenseInstance(workInProgress);
984 - if (enableSchedulerTracing) {
985 - markSpawnedWork(OffscreenLane);
986 - }
982 bubbleProperties(workInProgress);
983 if (enableProfilerTimer) {
984 if ((workInProgress.mode & ProfileMode) !== NoMode) {
@@ -1259,9 +1254,6 @@ function completeWork(
1254 // We can use any RetryLane even if it's the one currently rendering
1255 // since we're leaving it behind on this node.
1256 workInProgress.lanes = SomeRetryLane;
1262 - if (enableSchedulerTracing) {
1263 - markSpawnedWork(SomeRetryLane);
1264 - }
1257 }
1258 } else {
1259 cutOffTailIfNeeded(renderState, false);
@@ -1320,9 +1312,6 @@ function completeWork(
1312 // We can use any RetryLane even if it's the one currently rendering
1313 // since we're leaving it behind on this node.
1314 workInProgress.lanes = SomeRetryLane;
1323 - if (enableSchedulerTracing) {
1324 - markSpawnedWork(SomeRetryLane);
1325 - }
1315 }
1316 }
1317 if (renderState.isBackwards) {
packages/react-reconciler/src/ReactFiberRoot.new.js
-7
@@ -20,14 +20,12 @@ import {
20 createLaneMap,
21 } from './ReactFiberLane.new';
22 import {
23 - enableSchedulerTracing,
23 enableSuspenseCallback,
24 enableCache,
25 enableProfilerCommitHooks,
26 enableProfilerTimer,
27 enableUpdaterTracking,
28 } from 'shared/ReactFeatureFlags';
30 -import {unstable_getThreadID} from 'scheduler/tracing';
29 import {initializeUpdateQueue} from './ReactUpdateQueue.new';
30 import {LegacyRoot, ConcurrentRoot} from './ReactRootTags';
31
@@ -66,11 +64,6 @@ function FiberRootNode(containerInfo, tag, hydrate) {
64 this.mutableSourceEagerHydrationData = null;
65 }
66
69 - if (enableSchedulerTracing) {
70 - this.interactionThreadID = unstable_getThreadID();
71 - this.memoizedInteractions = new Set();
72 - this.pendingInteractionMap = new Map();
73 - }
67 if (enableSuspenseCallback) {
68 this.hydrationCallbacks = null;
69 }
packages/react-reconciler/src/ReactFiberRoot.old.js
-7
@@ -20,14 +20,12 @@ import {
20 createLaneMap,
21 } from './ReactFiberLane.old';
22 import {
23 - enableSchedulerTracing,
23 enableSuspenseCallback,
24 enableCache,
25 enableProfilerCommitHooks,
26 enableProfilerTimer,
27 enableUpdaterTracking,
28 } from 'shared/ReactFeatureFlags';
30 -import {unstable_getThreadID} from 'scheduler/tracing';
29 import {initializeUpdateQueue} from './ReactUpdateQueue.old';
30 import {LegacyRoot, ConcurrentRoot} from './ReactRootTags';
31
@@ -66,11 +64,6 @@ function FiberRootNode(containerInfo, tag, hydrate) {
64 this.mutableSourceEagerHydrationData = null;
65 }
66
69 - if (enableSchedulerTracing) {
70 - this.interactionThreadID = unstable_getThreadID();
71 - this.memoizedInteractions = new Set();
72 - this.pendingInteractionMap = new Map();
73 - }
67 if (enableSuspenseCallback) {
68 this.hydrationCallbacks = null;
69 }
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+2 -253
@@ -10,7 +10,6 @@
10 import type {Thenable, Wakeable} from 'shared/ReactTypes';
11 import type {Fiber, FiberRoot} from './ReactInternalTypes';
12 import type {Lanes, Lane} from './ReactFiberLane.new';
13 -import type {Interaction} from 'scheduler/src/Tracing';
13 import type {SuspenseState} from './ReactFiberSuspenseComponent.new';
14 import type {StackCursor} from './ReactFiberStack.new';
15 import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.new';
@@ -24,7 +23,6 @@ import {
23 enableProfilerCommitHooks,
24 enableProfilerNestedUpdatePhase,
25 enableProfilerNestedUpdateScheduledHook,
27 - enableSchedulerTracing,
26 warnAboutUnmockedScheduler,
27 deferRenderPhaseUpdateToNextBatch,
28 enableDebugTracing,
@@ -83,8 +81,6 @@ import {
81 // The scheduler is imported here *only* to detect whether it's been mocked
82 import * as Scheduler from 'scheduler';
83
86 -import {__interactionsRef, __subscriberRef} from 'scheduler/tracing';
87 -
84 import {
85 resetAfterCommit,
86 scheduleTimeout,
@@ -349,13 +345,6 @@ let rootWithNestedUpdates: FiberRoot | null = null;
345 const NESTED_PASSIVE_UPDATE_LIMIT = 50;
346 let nestedPassiveUpdateCount: number = 0;
347
352 -// Marks the need to reschedule pending interactions at these lanes
353 -// during the commit phase. This enables them to be traced across components
354 -// that spawn new work during render. E.g. hidden boundaries, suspended SSR
355 -// hydration or SuspenseList.
356 -// TODO: Can use a bitmask instead of an array
357 -let spawnedWorkDuringRender: null | Array<Lane | Lanes> = null;
358 -
348 // If two updates are scheduled within the same event, we should treat their
349 // event times as simultaneous, even if the actual clock time has advanced
350 // between the first and second call.
@@ -496,11 +485,7 @@ export function scheduleUpdateOnFiber(
485 if (current.tag === Profiler) {
486 const {id, onNestedUpdateScheduled} = current.memoizedProps;
487 if (typeof onNestedUpdateScheduled === 'function') {
499 - if (enableSchedulerTracing) {
500 - onNestedUpdateScheduled(id, root.memoizedInteractions);
501 - } else {
502 - onNestedUpdateScheduled(id);
503 - }
488 + onNestedUpdateScheduled(id);
489 }
490 }
491 current = current.return;
@@ -543,16 +528,12 @@ export function scheduleUpdateOnFiber(
528 // Check if we're not already rendering
529 (executionContext & (RenderContext | CommitContext)) === NoContext
530 ) {
546 - // Register pending interactions on the root to avoid losing traced interaction data.
547 - schedulePendingInteractions(root, lane);
548 -
531 // This is a legacy edge case. The initial mount of a ReactDOM.render-ed
532 // root inside of batchedUpdates should be synchronous, but layout updates
533 // should be deferred until the end of the batch.
534 performSyncWorkOnRoot(root);
535 } else {
536 ensureRootIsScheduled(root, eventTime);
555 - schedulePendingInteractions(root, lane);
537 if (
538 executionContext === NoContext &&
539 (fiber.mode & ConcurrentMode) === NoMode
@@ -569,7 +550,6 @@ export function scheduleUpdateOnFiber(
550 } else {
551 // Schedule other updates after in case the callback is sync.
552 ensureRootIsScheduled(root, eventTime);
572 - schedulePendingInteractions(root, lane);
553 }
554
555 return root;
@@ -1269,10 +1249,6 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes) {
1249
1250 enqueueInterleavedUpdates();
1251
1272 - if (enableSchedulerTracing) {
1273 - spawnedWorkDuringRender = null;
1274 - }
1275 -
1252 if (__DEV__) {
1253 ReactStrictModeWarnings.discardPendingWarnings();
1254 }
@@ -1357,21 +1333,6 @@ function popDispatcher(prevDispatcher) {
1333 ReactCurrentDispatcher.current = prevDispatcher;
1334 }
1335
1360 -function pushInteractions(root) {
1361 - if (enableSchedulerTracing) {
1362 - const prevInteractions: Set<Interaction> | null = __interactionsRef.current;
1363 - __interactionsRef.current = root.memoizedInteractions;
1364 - return prevInteractions;
1365 - }
1366 - return null;
1367 -}
1368 -
1369 -function popInteractions(prevInteractions) {
1370 - if (enableSchedulerTracing) {
1371 - __interactionsRef.current = prevInteractions;
1372 - }
1373 -}
1374 -
1336 export function markCommitTimeOfFallback() {
1337 globalMostRecentFallbackTime = now();
1338 }
@@ -1454,11 +1415,8 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
1415 }
1416
1417 prepareFreshStack(root, lanes);
1457 - startWorkOnPendingInteractions(root, lanes);
1418 }
1419
1460 - const prevInteractions = pushInteractions(root);
1461 -
1420 if (__DEV__) {
1421 if (enableDebugTracing) {
1422 logRenderStarted(lanes);
@@ -1478,9 +1436,6 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
1436 }
1437 } while (true);
1438 resetContextDependencies();
1481 - if (enableSchedulerTracing) {
1482 - popInteractions(((prevInteractions: any): Set<Interaction>));
1483 - }
1439
1440 executionContext = prevExecutionContext;
1441 popDispatcher(prevDispatcher);
@@ -1546,11 +1501,8 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
1501
1502 resetRenderTimer();
1503 prepareFreshStack(root, lanes);
1549 - startWorkOnPendingInteractions(root, lanes);
1504 }
1505
1552 - const prevInteractions = pushInteractions(root);
1553 -
1506 if (__DEV__) {
1507 if (enableDebugTracing) {
1508 logRenderStarted(lanes);
@@ -1570,9 +1522,6 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
1522 }
1523 } while (true);
1524 resetContextDependencies();
1573 - if (enableSchedulerTracing) {
1574 - popInteractions(((prevInteractions: any): Set<Interaction>));
1575 - }
1525
1526 popDispatcher(prevDispatcher);
1527 executionContext = prevExecutionContext;
@@ -1874,7 +1823,6 @@ function commitRootImpl(root, renderPriorityLevel) {
1823
1824 const prevExecutionContext = executionContext;
1825 executionContext |= CommitContext;
1877 - const prevInteractions = pushInteractions(root);
1826
1827 // Reset this to null before calling lifecycles
1828 ReactCurrentOwner.current = null;
@@ -1947,9 +1895,6 @@ function commitRootImpl(root, renderPriorityLevel) {
1895 // opportunity to paint.
1896 requestPaint();
1897
1950 - if (enableSchedulerTracing) {
1951 - popInteractions(((prevInteractions: any): Set<Interaction>));
1952 - }
1898 executionContext = prevExecutionContext;
1899
1900 // Reset the priority to the previous non-sync value.
@@ -1980,22 +1925,7 @@ function commitRootImpl(root, renderPriorityLevel) {
1925 remainingLanes = root.pendingLanes;
1926
1927 // Check if there's remaining work on this root
1983 - if (remainingLanes !== NoLanes) {
1984 - if (enableSchedulerTracing) {
1985 - if (spawnedWorkDuringRender !== null) {
1986 - const expirationTimes = spawnedWorkDuringRender;
1987 - spawnedWorkDuringRender = null;
1988 - for (let i = 0; i < expirationTimes.length; i++) {
1989 - scheduleInteractions(
1990 - root,
1991 - expirationTimes[i],
1992 - root.memoizedInteractions,
1993 - );
1994 - }
1995 - }
1996 - schedulePendingInteractions(root, remainingLanes);
1997 - }
1998 - } else {
1928 + if (remainingLanes === NoLanes) {
1929 // If there's no remaining work, we can clear the set of already failed
1930 // error boundaries.
1931 legacyErrorBoundariesThatAlreadyFailed = null;
@@ -2007,16 +1937,6 @@ function commitRootImpl(root, renderPriorityLevel) {
1937 }
1938 }
1939
2010 - if (enableSchedulerTracing) {
2011 - if (!rootDidHavePassiveEffects) {
2012 - // If there are no passive effects, then we can complete the pending interactions.
2013 - // Otherwise, we'll wait until after the passive effects are flushed.
2014 - // Wait to do this until after remaining work has been scheduled,
2015 - // so that we don't prematurely signal complete for interactions when there's e.g. hidden work.
2016 - finishPendingInteractions(root, lanes);
2017 - }
2018 - }
2019 -
1940 if (includesSomeLane(remainingLanes, (SyncLane: Lane))) {
1941 if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {
1942 markNestedUpdateScheduled();
@@ -2177,7 +2097,6 @@ function flushPassiveEffectsImpl() {
2097
2098 const prevExecutionContext = executionContext;
2099 executionContext |= CommitContext;
2180 - const prevInteractions = pushInteractions(root);
2100
2101 commitPassiveUnmountEffects(root.current);
2102 commitPassiveMountEffects(root, root.current);
@@ -2192,11 +2111,6 @@ function flushPassiveEffectsImpl() {
2111 }
2112 }
2113
2195 - if (enableSchedulerTracing) {
2196 - popInteractions(((prevInteractions: any): Set<Interaction>));
2197 - finishPendingInteractions(root, lanes);
2198 - }
2199 -
2114 if (__DEV__) {
2115 isFlushingPassiveEffects = false;
2116 }
@@ -2271,7 +2185,6 @@ function captureCommitPhaseErrorOnRoot(
2185 if (root !== null) {
2186 markRootUpdated(root, SyncLane, eventTime);
2187 ensureRootIsScheduled(root, eventTime);
2274 - schedulePendingInteractions(root, SyncLane);
2188 }
2189 }
2190
@@ -2318,7 +2231,6 @@ export function captureCommitPhaseError(
2231 if (root !== null) {
2232 markRootUpdated(root, SyncLane, eventTime);
2233 ensureRootIsScheduled(root, eventTime);
2321 - schedulePendingInteractions(root, SyncLane);
2234 }
2235 return;
2236 }
@@ -2390,7 +2302,6 @@ export function pingSuspendedRoot(
2302 }
2303
2304 ensureRootIsScheduled(root, eventTime);
2393 - schedulePendingInteractions(root, pingedLanes);
2305 }
2306
2307 function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {
@@ -2409,7 +2320,6 @@ function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {
2320 if (root !== null) {
2321 markRootUpdated(root, retryLane, eventTime);
2322 ensureRootIsScheduled(root, eventTime);
2412 - schedulePendingInteractions(root, retryLane);
2323 }
2324 }
2325
@@ -3008,167 +2918,6 @@ export function warnIfUnmockedScheduler(fiber: Fiber) {
2918 }
2919 }
2920
3011 -function computeThreadID(root: FiberRoot, lane: Lane | Lanes) {
3012 - // Interaction threads are unique per root and expiration time.
3013 - // NOTE: Intentionally unsound cast. All that matters is that it's a number
3014 - // and it represents a batch of work. Could make a helper function instead,
3015 - // but meh this is fine for now.
3016 - return (lane: any) * 1000 + root.interactionThreadID;
3017 -}
3018 -
3019 -export function markSpawnedWork(lane: Lane | Lanes) {
3020 - if (!enableSchedulerTracing) {
3021 - return;
3022 - }
3023 - if (spawnedWorkDuringRender === null) {
3024 - spawnedWorkDuringRender = [lane];
3025 - } else {
3026 - spawnedWorkDuringRender.push(lane);
3027 - }
3028 -}
3029 -
3030 -function scheduleInteractions(
3031 - root: FiberRoot,
3032 - lane: Lane | Lanes,
3033 - interactions: Set<Interaction>,
3034 -) {
3035 - if (!enableSchedulerTracing) {
3036 - return;
3037 - }
3038 -
3039 - if (interactions.size > 0) {
3040 - const pendingInteractionMap = root.pendingInteractionMap;
3041 - const pendingInteractions = pendingInteractionMap.get(lane);
3042 - if (pendingInteractions != null) {
3043 - interactions.forEach(interaction => {
3044 - if (!pendingInteractions.has(interaction)) {
3045 - // Update the pending async work count for previously unscheduled interaction.
3046 - interaction.__count++;
3047 - }
3048 -
3049 - pendingInteractions.add(interaction);
3050 - });
3051 - } else {
3052 - pendingInteractionMap.set(lane, new Set(interactions));
3053 -
3054 - // Update the pending async work count for the current interactions.
3055 - interactions.forEach(interaction => {
3056 - interaction.__count++;
3057 - });
3058 - }
3059 -
3060 - const subscriber = __subscriberRef.current;
3061 - if (subscriber !== null) {
3062 - const threadID = computeThreadID(root, lane);
3063 - subscriber.onWorkScheduled(interactions, threadID);
3064 - }
3065 - }
3066 -}
3067 -
3068 -function schedulePendingInteractions(root: FiberRoot, lane: Lane | Lanes) {
3069 - // This is called when work is scheduled on a root.
3070 - // It associates the current interactions with the newly-scheduled expiration.
3071 - // They will be restored when that expiration is later committed.
3072 - if (!enableSchedulerTracing) {
3073 - return;
3074 - }
3075 -
3076 - scheduleInteractions(root, lane, __interactionsRef.current);
3077 -}
3078 -
3079 -function startWorkOnPendingInteractions(root: FiberRoot, lanes: Lanes) {
3080 - // This is called when new work is started on a root.
3081 - if (!enableSchedulerTracing) {
3082 - return;
3083 - }
3084 -
3085 - // Determine which interactions this batch of work currently includes, So that
3086 - // we can accurately attribute time spent working on it, And so that cascading
3087 - // work triggered during the render phase will be associated with it.
3088 - const interactions: Set<Interaction> = new Set();
3089 - root.pendingInteractionMap.forEach((scheduledInteractions, scheduledLane) => {
3090 - if (includesSomeLane(lanes, scheduledLane)) {
3091 - scheduledInteractions.forEach(interaction =>
3092 - interactions.add(interaction),
3093 - );
3094 - }
3095 - });
3096 -
3097 - // Store the current set of interactions on the FiberRoot for a few reasons:
3098 - // We can re-use it in hot functions like performConcurrentWorkOnRoot()
3099 - // without having to recalculate it. We will also use it in commitWork() to
3100 - // pass to any Profiler onRender() hooks. This also provides DevTools with a
3101 - // way to access it when the onCommitRoot() hook is called.
3102 - root.memoizedInteractions = interactions;
3103 -
3104 - if (interactions.size > 0) {
3105 - const subscriber = __subscriberRef.current;
3106 - if (subscriber !== null) {
3107 - const threadID = computeThreadID(root, lanes);
3108 - try {
3109 - subscriber.onWorkStarted(interactions, threadID);
3110 - } catch (error) {
3111 - // If the subscriber throws, rethrow it in a separate task
3112 - scheduleCallback(ImmediateSchedulerPriority, () => {
3113 - throw error;
3114 - });
3115 - }
3116 - }
3117 - }
3118 -}
3119 -
3120 -function finishPendingInteractions(root, committedLanes) {
3121 - if (!enableSchedulerTracing) {
3122 - return;
3123 - }
3124 -
3125 - const remainingLanesAfterCommit = root.pendingLanes;
3126 -
3127 - let subscriber;
3128 -
3129 - try {
3130 - subscriber = __subscriberRef.current;
3131 - if (subscriber !== null && root.memoizedInteractions.size > 0) {
3132 - // FIXME: More than one lane can finish in a single commit.
3133 - const threadID = computeThreadID(root, committedLanes);
3134 - subscriber.onWorkStopped(root.memoizedInteractions, threadID);
3135 - }
3136 - } catch (error) {
3137 - // If the subscriber throws, rethrow it in a separate task
3138 - scheduleCallback(ImmediateSchedulerPriority, () => {
3139 - throw error;
3140 - });
3141 - } finally {
3142 - // Clear completed interactions from the pending Map.
3143 - // Unless the render was suspended or cascading work was scheduled,
3144 - // In which case– leave pending interactions until the subsequent render.
3145 - const pendingInteractionMap = root.pendingInteractionMap;
3146 - pendingInteractionMap.forEach((scheduledInteractions, lane) => {
3147 - // Only decrement the pending interaction count if we're done.
3148 - // If there's still work at the current priority,
3149 - // That indicates that we are waiting for suspense data.
3150 - if (!includesSomeLane(remainingLanesAfterCommit, lane)) {
3151 - pendingInteractionMap.delete(lane);
3152 -
3153 - scheduledInteractions.forEach(interaction => {
3154 - interaction.__count--;
3155 -
3156 - if (subscriber !== null && interaction.__count === 0) {
3157 - try {
3158 - subscriber.onInteractionScheduledWorkCompleted(interaction);
3159 - } catch (error) {
3160 - // If the subscriber throws, rethrow it in a separate task
3161 - scheduleCallback(ImmediateSchedulerPriority, () => {
3162 - throw error;
3163 - });
3164 - }
3165 - }
3166 - });
3167 - }
3168 - });
3169 - }
3170 -}
3171 -
2921 // `act` testing API
2922 //
2923 // TODO: This is mostly a copy-paste from the legacy `act`, which does not have
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+2 -253
@@ -10,7 +10,6 @@
10 import type {Thenable, Wakeable} from 'shared/ReactTypes';
11 import type {Fiber, FiberRoot} from './ReactInternalTypes';
12 import type {Lanes, Lane} from './ReactFiberLane.old';
13 -import type {Interaction} from 'scheduler/src/Tracing';
13 import type {SuspenseState} from './ReactFiberSuspenseComponent.old';
14 import type {StackCursor} from './ReactFiberStack.old';
15 import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.old';
@@ -24,7 +23,6 @@ import {
23 enableProfilerCommitHooks,
24 enableProfilerNestedUpdatePhase,
25 enableProfilerNestedUpdateScheduledHook,
27 - enableSchedulerTracing,
26 warnAboutUnmockedScheduler,
27 deferRenderPhaseUpdateToNextBatch,
28 enableDebugTracing,
@@ -83,8 +81,6 @@ import {
81 // The scheduler is imported here *only* to detect whether it's been mocked
82 import * as Scheduler from 'scheduler';
83
86 -import {__interactionsRef, __subscriberRef} from 'scheduler/tracing';
87 -
84 import {
85 resetAfterCommit,
86 scheduleTimeout,
@@ -349,13 +345,6 @@ let rootWithNestedUpdates: FiberRoot | null = null;
345 const NESTED_PASSIVE_UPDATE_LIMIT = 50;
346 let nestedPassiveUpdateCount: number = 0;
347
352 -// Marks the need to reschedule pending interactions at these lanes
353 -// during the commit phase. This enables them to be traced across components
354 -// that spawn new work during render. E.g. hidden boundaries, suspended SSR
355 -// hydration or SuspenseList.
356 -// TODO: Can use a bitmask instead of an array
357 -let spawnedWorkDuringRender: null | Array<Lane | Lanes> = null;
358 -
348 // If two updates are scheduled within the same event, we should treat their
349 // event times as simultaneous, even if the actual clock time has advanced
350 // between the first and second call.
@@ -496,11 +485,7 @@ export function scheduleUpdateOnFiber(
485 if (current.tag === Profiler) {
486 const {id, onNestedUpdateScheduled} = current.memoizedProps;
487 if (typeof onNestedUpdateScheduled === 'function') {
499 - if (enableSchedulerTracing) {
500 - onNestedUpdateScheduled(id, root.memoizedInteractions);
501 - } else {
502 - onNestedUpdateScheduled(id);
503 - }
488 + onNestedUpdateScheduled(id);
489 }
490 }
491 current = current.return;
@@ -543,16 +528,12 @@ export function scheduleUpdateOnFiber(
528 // Check if we're not already rendering
529 (executionContext & (RenderContext | CommitContext)) === NoContext
530 ) {
546 - // Register pending interactions on the root to avoid losing traced interaction data.
547 - schedulePendingInteractions(root, lane);
548 -
531 // This is a legacy edge case. The initial mount of a ReactDOM.render-ed
532 // root inside of batchedUpdates should be synchronous, but layout updates
533 // should be deferred until the end of the batch.
534 performSyncWorkOnRoot(root);
535 } else {
536 ensureRootIsScheduled(root, eventTime);
555 - schedulePendingInteractions(root, lane);
537 if (
538 executionContext === NoContext &&
539 (fiber.mode & ConcurrentMode) === NoMode
@@ -569,7 +550,6 @@ export function scheduleUpdateOnFiber(
550 } else {
551 // Schedule other updates after in case the callback is sync.
552 ensureRootIsScheduled(root, eventTime);
572 - schedulePendingInteractions(root, lane);
553 }
554
555 return root;
@@ -1269,10 +1249,6 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes) {
1249
1250 enqueueInterleavedUpdates();
1251
1272 - if (enableSchedulerTracing) {
1273 - spawnedWorkDuringRender = null;
1274 - }
1275 -
1252 if (__DEV__) {
1253 ReactStrictModeWarnings.discardPendingWarnings();
1254 }
@@ -1357,21 +1333,6 @@ function popDispatcher(prevDispatcher) {
1333 ReactCurrentDispatcher.current = prevDispatcher;
1334 }
1335
1360 -function pushInteractions(root) {
1361 - if (enableSchedulerTracing) {
1362 - const prevInteractions: Set<Interaction> | null = __interactionsRef.current;
1363 - __interactionsRef.current = root.memoizedInteractions;
1364 - return prevInteractions;
1365 - }
1366 - return null;
1367 -}
1368 -
1369 -function popInteractions(prevInteractions) {
1370 - if (enableSchedulerTracing) {
1371 - __interactionsRef.current = prevInteractions;
1372 - }
1373 -}
1374 -
1336 export function markCommitTimeOfFallback() {
1337 globalMostRecentFallbackTime = now();
1338 }
@@ -1454,11 +1415,8 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
1415 }
1416
1417 prepareFreshStack(root, lanes);
1457 - startWorkOnPendingInteractions(root, lanes);
1418 }
1419
1460 - const prevInteractions = pushInteractions(root);
1461 -
1420 if (__DEV__) {
1421 if (enableDebugTracing) {
1422 logRenderStarted(lanes);
@@ -1478,9 +1436,6 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
1436 }
1437 } while (true);
1438 resetContextDependencies();
1481 - if (enableSchedulerTracing) {
1482 - popInteractions(((prevInteractions: any): Set<Interaction>));
1483 - }
1439
1440 executionContext = prevExecutionContext;
1441 popDispatcher(prevDispatcher);
@@ -1546,11 +1501,8 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
1501
1502 resetRenderTimer();
1503 prepareFreshStack(root, lanes);
1549 - startWorkOnPendingInteractions(root, lanes);
1504 }
1505
1552 - const prevInteractions = pushInteractions(root);
1553 -
1506 if (__DEV__) {
1507 if (enableDebugTracing) {
1508 logRenderStarted(lanes);
@@ -1570,9 +1522,6 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
1522 }
1523 } while (true);
1524 resetContextDependencies();
1573 - if (enableSchedulerTracing) {
1574 - popInteractions(((prevInteractions: any): Set<Interaction>));
1575 - }
1525
1526 popDispatcher(prevDispatcher);
1527 executionContext = prevExecutionContext;
@@ -1874,7 +1823,6 @@ function commitRootImpl(root, renderPriorityLevel) {
1823
1824 const prevExecutionContext = executionContext;
1825 executionContext |= CommitContext;
1877 - const prevInteractions = pushInteractions(root);
1826
1827 // Reset this to null before calling lifecycles
1828 ReactCurrentOwner.current = null;
@@ -1947,9 +1895,6 @@ function commitRootImpl(root, renderPriorityLevel) {
1895 // opportunity to paint.
1896 requestPaint();
1897
1950 - if (enableSchedulerTracing) {
1951 - popInteractions(((prevInteractions: any): Set<Interaction>));
1952 - }
1898 executionContext = prevExecutionContext;
1899
1900 // Reset the priority to the previous non-sync value.
@@ -1980,22 +1925,7 @@ function commitRootImpl(root, renderPriorityLevel) {
1925 remainingLanes = root.pendingLanes;
1926
1927 // Check if there's remaining work on this root
1983 - if (remainingLanes !== NoLanes) {
1984 - if (enableSchedulerTracing) {
1985 - if (spawnedWorkDuringRender !== null) {
1986 - const expirationTimes = spawnedWorkDuringRender;
1987 - spawnedWorkDuringRender = null;
1988 - for (let i = 0; i < expirationTimes.length; i++) {
1989 - scheduleInteractions(
1990 - root,
1991 - expirationTimes[i],
1992 - root.memoizedInteractions,
1993 - );
1994 - }
1995 - }
1996 - schedulePendingInteractions(root, remainingLanes);
1997 - }
1998 - } else {
1928 + if (remainingLanes === NoLanes) {
1929 // If there's no remaining work, we can clear the set of already failed
1930 // error boundaries.
1931 legacyErrorBoundariesThatAlreadyFailed = null;
@@ -2007,16 +1937,6 @@ function commitRootImpl(root, renderPriorityLevel) {
1937 }
1938 }
1939
2010 - if (enableSchedulerTracing) {
2011 - if (!rootDidHavePassiveEffects) {
2012 - // If there are no passive effects, then we can complete the pending interactions.
2013 - // Otherwise, we'll wait until after the passive effects are flushed.
2014 - // Wait to do this until after remaining work has been scheduled,
2015 - // so that we don't prematurely signal complete for interactions when there's e.g. hidden work.
2016 - finishPendingInteractions(root, lanes);
2017 - }
2018 - }
2019 -
1940 if (includesSomeLane(remainingLanes, (SyncLane: Lane))) {
1941 if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {
1942 markNestedUpdateScheduled();
@@ -2177,7 +2097,6 @@ function flushPassiveEffectsImpl() {
2097
2098 const prevExecutionContext = executionContext;
2099 executionContext |= CommitContext;
2180 - const prevInteractions = pushInteractions(root);
2100
2101 commitPassiveUnmountEffects(root.current);
2102 commitPassiveMountEffects(root, root.current);
@@ -2192,11 +2111,6 @@ function flushPassiveEffectsImpl() {
2111 }
2112 }
2113
2195 - if (enableSchedulerTracing) {
2196 - popInteractions(((prevInteractions: any): Set<Interaction>));
2197 - finishPendingInteractions(root, lanes);
2198 - }
2199 -
2114 if (__DEV__) {
2115 isFlushingPassiveEffects = false;
2116 }
@@ -2271,7 +2185,6 @@ function captureCommitPhaseErrorOnRoot(
2185 if (root !== null) {
2186 markRootUpdated(root, SyncLane, eventTime);
2187 ensureRootIsScheduled(root, eventTime);
2274 - schedulePendingInteractions(root, SyncLane);
2188 }
2189 }
2190
@@ -2318,7 +2231,6 @@ export function captureCommitPhaseError(
2231 if (root !== null) {
2232 markRootUpdated(root, SyncLane, eventTime);
2233 ensureRootIsScheduled(root, eventTime);
2321 - schedulePendingInteractions(root, SyncLane);
2234 }
2235 return;
2236 }
@@ -2390,7 +2302,6 @@ export function pingSuspendedRoot(
2302 }
2303
2304 ensureRootIsScheduled(root, eventTime);
2393 - schedulePendingInteractions(root, pingedLanes);
2305 }
2306
2307 function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {
@@ -2409,7 +2320,6 @@ function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {
2320 if (root !== null) {
2321 markRootUpdated(root, retryLane, eventTime);
2322 ensureRootIsScheduled(root, eventTime);
2412 - schedulePendingInteractions(root, retryLane);
2323 }
2324 }
2325
@@ -3008,167 +2918,6 @@ export function warnIfUnmockedScheduler(fiber: Fiber) {
2918 }
2919 }
2920
3011 -function computeThreadID(root: FiberRoot, lane: Lane | Lanes) {
3012 - // Interaction threads are unique per root and expiration time.
3013 - // NOTE: Intentionally unsound cast. All that matters is that it's a number
3014 - // and it represents a batch of work. Could make a helper function instead,
3015 - // but meh this is fine for now.
3016 - return (lane: any) * 1000 + root.interactionThreadID;
3017 -}
3018 -
3019 -export function markSpawnedWork(lane: Lane | Lanes) {
3020 - if (!enableSchedulerTracing) {
3021 - return;
3022 - }
3023 - if (spawnedWorkDuringRender === null) {
3024 - spawnedWorkDuringRender = [lane];
3025 - } else {
3026 - spawnedWorkDuringRender.push(lane);
3027 - }
3028 -}
3029 -
3030 -function scheduleInteractions(
3031 - root: FiberRoot,
3032 - lane: Lane | Lanes,
3033 - interactions: Set<Interaction>,
3034 -) {
3035 - if (!enableSchedulerTracing) {
3036 - return;
3037 - }
3038 -
3039 - if (interactions.size > 0) {
3040 - const pendingInteractionMap = root.pendingInteractionMap;
3041 - const pendingInteractions = pendingInteractionMap.get(lane);
3042 - if (pendingInteractions != null) {
3043 - interactions.forEach(interaction => {
3044 - if (!pendingInteractions.has(interaction)) {
3045 - // Update the pending async work count for previously unscheduled interaction.
3046 - interaction.__count++;
3047 - }
3048 -
3049 - pendingInteractions.add(interaction);
3050 - });
3051 - } else {
3052 - pendingInteractionMap.set(lane, new Set(interactions));
3053 -
3054 - // Update the pending async work count for the current interactions.
3055 - interactions.forEach(interaction => {
3056 - interaction.__count++;
3057 - });
3058 - }
3059 -
3060 - const subscriber = __subscriberRef.current;
3061 - if (subscriber !== null) {
3062 - const threadID = computeThreadID(root, lane);
3063 - subscriber.onWorkScheduled(interactions, threadID);
3064 - }
3065 - }
3066 -}
3067 -
3068 -function schedulePendingInteractions(root: FiberRoot, lane: Lane | Lanes) {
3069 - // This is called when work is scheduled on a root.
3070 - // It associates the current interactions with the newly-scheduled expiration.
3071 - // They will be restored when that expiration is later committed.
3072 - if (!enableSchedulerTracing) {
3073 - return;
3074 - }
3075 -
3076 - scheduleInteractions(root, lane, __interactionsRef.current);
3077 -}
3078 -
3079 -function startWorkOnPendingInteractions(root: FiberRoot, lanes: Lanes) {
3080 - // This is called when new work is started on a root.
3081 - if (!enableSchedulerTracing) {
3082 - return;
3083 - }
3084 -
3085 - // Determine which interactions this batch of work currently includes, So that
3086 - // we can accurately attribute time spent working on it, And so that cascading
3087 - // work triggered during the render phase will be associated with it.
3088 - const interactions: Set<Interaction> = new Set();
3089 - root.pendingInteractionMap.forEach((scheduledInteractions, scheduledLane) => {
3090 - if (includesSomeLane(lanes, scheduledLane)) {
3091 - scheduledInteractions.forEach(interaction =>
3092 - interactions.add(interaction),
3093 - );
3094 - }
3095 - });
3096 -
3097 - // Store the current set of interactions on the FiberRoot for a few reasons:
3098 - // We can re-use it in hot functions like performConcurrentWorkOnRoot()
3099 - // without having to recalculate it. We will also use it in commitWork() to
3100 - // pass to any Profiler onRender() hooks. This also provides DevTools with a
3101 - // way to access it when the onCommitRoot() hook is called.
3102 - root.memoizedInteractions = interactions;
3103 -
3104 - if (interactions.size > 0) {
3105 - const subscriber = __subscriberRef.current;
3106 - if (subscriber !== null) {
3107 - const threadID = computeThreadID(root, lanes);
3108 - try {
3109 - subscriber.onWorkStarted(interactions, threadID);
3110 - } catch (error) {
3111 - // If the subscriber throws, rethrow it in a separate task
3112 - scheduleCallback(ImmediateSchedulerPriority, () => {
3113 - throw error;
3114 - });
3115 - }
3116 - }
3117 - }
3118 -}
3119 -
3120 -function finishPendingInteractions(root, committedLanes) {
3121 - if (!enableSchedulerTracing) {
3122 - return;
3123 - }
3124 -
3125 - const remainingLanesAfterCommit = root.pendingLanes;
3126 -
3127 - let subscriber;
3128 -
3129 - try {
3130 - subscriber = __subscriberRef.current;
3131 - if (subscriber !== null && root.memoizedInteractions.size > 0) {
3132 - // FIXME: More than one lane can finish in a single commit.
3133 - const threadID = computeThreadID(root, committedLanes);
3134 - subscriber.onWorkStopped(root.memoizedInteractions, threadID);
3135 - }
3136 - } catch (error) {
3137 - // If the subscriber throws, rethrow it in a separate task
3138 - scheduleCallback(ImmediateSchedulerPriority, () => {
3139 - throw error;
3140 - });
3141 - } finally {
3142 - // Clear completed interactions from the pending Map.
3143 - // Unless the render was suspended or cascading work was scheduled,
3144 - // In which case– leave pending interactions until the subsequent render.
3145 - const pendingInteractionMap = root.pendingInteractionMap;
3146 - pendingInteractionMap.forEach((scheduledInteractions, lane) => {
3147 - // Only decrement the pending interaction count if we're done.
3148 - // If there's still work at the current priority,
3149 - // That indicates that we are waiting for suspense data.
3150 - if (!includesSomeLane(remainingLanesAfterCommit, lane)) {
3151 - pendingInteractionMap.delete(lane);
3152 -
3153 - scheduledInteractions.forEach(interaction => {
3154 - interaction.__count--;
3155 -
3156 - if (subscriber !== null && interaction.__count === 0) {
3157 - try {
3158 - subscriber.onInteractionScheduledWorkCompleted(interaction);
3159 - } catch (error) {
3160 - // If the subscriber throws, rethrow it in a separate task
3161 - scheduleCallback(ImmediateSchedulerPriority, () => {
3162 - throw error;
3163 - });
3164 - }
3165 - }
3166 - });
3167 - }
3168 - });
3169 - }
3170 -}
3171 -
2921 // `act` testing API
2922 //
2923 // TODO: This is mostly a copy-paste from the legacy `act`, which does not have
packages/react-reconciler/src/ReactInternalTypes.js
-14
@@ -24,7 +24,6 @@ import type {Lane, Lanes, LaneMap} from './ReactFiberLane.old';
24 import type {RootTag} from './ReactRootTags';
25 import type {TimeoutHandle, NoTimeout} from './ReactFiberHostConfig';
26 import type {Wakeable} from 'shared/ReactTypes';
27 -import type {Interaction} from 'scheduler/src/Tracing';
27 import type {Cache} from './ReactFiberCacheComponent.old';
28
29 // Unwind Circular: moved from ReactFiberHooks.old
@@ -240,16 +239,6 @@ type BaseFiberRootProperties = {|
239 pooledCacheLanes: Lanes,
240 |};
241
243 -// The following attributes are only used by interaction tracing builds.
244 -// They enable interactions to be associated with their async work,
245 -// And expose interaction metadata to the React DevTools Profiler plugin.
246 -// Note that these attributes are only defined when the enableSchedulerTracing flag is enabled.
247 -type ProfilingOnlyFiberRootProperties = {|
248 - interactionThreadID: number,
249 - memoizedInteractions: Set<Interaction>,
250 - pendingInteractionMap: Map<Lane | Lanes, Set<Interaction>>,
251 -|};
252 -
242 // The following attributes are only used by DevTools and are only present in DEV builds.
243 // They enable DevTools Profiler UI to show which Fiber(s) scheduled a given commit.
244 type UpdaterTrackingOnlyFiberRootProperties = {|
@@ -270,12 +259,9 @@ type SuspenseCallbackOnlyFiberRootProperties = {|
259
260 // Exported FiberRoot type includes all properties,
261 // To avoid requiring potentially error-prone :any casts throughout the project.
273 -// Profiling properties are only safe to access in profiling builds (when enableSchedulerTracing is true).
262 // The types are defined separately within this file to ensure they stay in sync.
275 -// (We don't have to use an inline :any cast when enableSchedulerTracing is disabled.)
263 export type FiberRoot = {
264 ...BaseFiberRootProperties,
278 - ...ProfilingOnlyFiberRootProperties,
265 ...SuspenseCallbackOnlyFiberRootProperties,
266 ...UpdaterTrackingOnlyFiberRootProperties,
267 ...
packages/react-reconciler/src/Scheduler.js
-18
@@ -12,9 +12,6 @@
12 // because Rollup would use dynamic dispatch for CommonJS interop named imports.
13 // When we switch to ESM, we can delete this module.
14 import * as Scheduler from 'scheduler';
15 -import {__interactionsRef} from 'scheduler/tracing';
16 -import {enableSchedulerTracing} from 'shared/ReactFeatureFlags';
17 -import invariant from 'shared/invariant';
15
16 export const scheduleCallback = Scheduler.unstable_scheduleCallback;
17 export const cancelCallback = Scheduler.unstable_cancelCallback;
@@ -28,19 +25,4 @@ export const UserBlockingPriority = Scheduler.unstable_UserBlockingPriority;
25 export const NormalPriority = Scheduler.unstable_NormalPriority;
26 export const LowPriority = Scheduler.unstable_LowPriority;
27 export const IdlePriority = Scheduler.unstable_IdlePriority;
31 -
32 -if (enableSchedulerTracing) {
33 - // Provide explicit error message when production+profiling bundle of e.g.
34 - // react-dom is used with production (non-profiling) bundle of
35 - // scheduler/tracing
36 - invariant(
37 - __interactionsRef != null && __interactionsRef.current != null,
38 - 'It is not supported to run the profiling version of a renderer (for ' +
39 - 'example, `react-dom/profiling`) without also replacing the ' +
40 - '`scheduler/tracing` module with `scheduler/tracing-profiling`. Your ' +
41 - 'bundler might have a setting for aliasing both modules. Learn more at ' +
42 - 'https://reactjs.org/link/profiling',
43 - );
44 -}
45 -
28 export type SchedulerCallback = (isSync: boolean) => SchedulerCallback | null;
packages/react-reconciler/src/__mocks__/scheduler/tracing.js renamed
+2 -4
@@ -7,7 +7,5 @@
7 * @flow
8 */
9
10 -'use strict';
11 -
12 -export * from './src/Tracing';
13 -export * from './src/TracingSubscriptions';
10 +// This placeholder is to support a legacy/regression test in ReactFresh-test.
11 +// Without this mock, jest.mock('scheduler/tracing') throws.
packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
-67
@@ -18,7 +18,6 @@ let readText;
18 let resolveText;
19 let ReactNoop;
20 let Scheduler;
21 -let SchedulerTracing;
21 let Suspense;
22 let useState;
23 let useReducer;
@@ -43,7 +42,6 @@ describe('ReactHooksWithNoopRenderer', () => {
42 React = require('react');
43 ReactNoop = require('react-noop-renderer');
44 Scheduler = require('scheduler');
46 - SchedulerTracing = require('scheduler/tracing');
45 useState = React.useState;
46 useReducer = React.useReducer;
47 useEffect = React.useEffect;
@@ -1797,71 +1795,6 @@ describe('ReactHooksWithNoopRenderer', () => {
1795 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1796 });
1797
1800 - // @gate enableSchedulerTracing
1801 - it('does not flush non-discrete passive effects when flushing sync (with tracing)', () => {
1802 - const onInteractionScheduledWorkCompleted = jest.fn();
1803 - const onWorkCanceled = jest.fn();
1804 - SchedulerTracing.unstable_subscribe({
1805 - onInteractionScheduledWorkCompleted,
1806 - onInteractionTraced: jest.fn(),
1807 - onWorkCanceled,
1808 - onWorkScheduled: jest.fn(),
1809 - onWorkStarted: jest.fn(),
1810 - onWorkStopped: jest.fn(),
1811 - });
1812 -
1813 - let _updateCount;
1814 - function Counter(props) {
1815 - const [count, updateCount] = useState(0);
1816 - _updateCount = updateCount;
1817 - useEffect(() => {
1818 - expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions([
1819 - tracingEvent,
1820 - ]);
1821 - Scheduler.unstable_yieldValue(`Will set count to 1`);
1822 - updateCount(1);
1823 - }, []);
1824 - return <Text text={'Count: ' + count} />;
1825 - }
1826 -
1827 - const tracingEvent = {id: 0, name: 'hello', timestamp: 0};
1828 - // we explicitly wait for missing act() warnings here since
1829 - // it's a lot harder to simulate this condition inside an act scope
1830 - expect(() => {
1831 - SchedulerTracing.unstable_trace(
1832 - tracingEvent.name,
1833 - tracingEvent.timestamp,
1834 - () => {
1835 - ReactNoop.render(<Counter count={0} />, () =>
1836 - Scheduler.unstable_yieldValue('Sync effect'),
1837 - );
1838 - },
1839 - );
1840 - expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1841 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1842 - }).toErrorDev(['An update to Counter ran an effect']);
1843 -
1844 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(0);
1845 -
1846 - // A flush sync doesn't cause the passive effects to fire.
1847 - act(() => {
1848 - ReactNoop.flushSync(() => {
1849 - _updateCount(2);
1850 - });
1851 - });
1852 -
1853 - expect(Scheduler).toHaveYielded([
1854 - 'Will set count to 1',
1855 - 'Count: 2',
1856 - 'Count: 1',
1857 - ]);
1858 -
1859 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1860 -
1861 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
1862 - expect(onWorkCanceled).toHaveBeenCalledTimes(0);
1863 - });
1864 -
1798 it(
1799 'in legacy mode, useEffect is deferred and updates finish synchronously ' +
1800 '(in a single batch)',
packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js
-73
@@ -2,7 +2,6 @@ let React;
2 let ReactTestRenderer;
3 let ReactFeatureFlags;
4 let Scheduler;
5 -let SchedulerTracing;
5 let ReactCache;
6 let Suspense;
7 let act;
@@ -18,12 +17,10 @@ describe('ReactSuspense', () => {
17 ReactFeatureFlags = require('shared/ReactFeatureFlags');
18
19 ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
21 - ReactFeatureFlags.enableSchedulerTracing = true;
20 React = require('react');
21 ReactTestRenderer = require('react-test-renderer');
22 act = ReactTestRenderer.unstable_concurrentAct;
23 Scheduler = require('scheduler');
26 - SchedulerTracing = require('scheduler/tracing');
24 ReactCache = require('react-cache');
25
26 Suspense = React.Suspense;
@@ -1271,76 +1268,6 @@ describe('ReactSuspense', () => {
1268 ]);
1269 });
1270
1274 - it('should call onInteractionScheduledWorkCompleted after suspending', () => {
1275 - const subscriber = {
1276 - onInteractionScheduledWorkCompleted: jest.fn(),
1277 - onInteractionTraced: jest.fn(),
1278 - onWorkCanceled: jest.fn(),
1279 - onWorkScheduled: jest.fn(),
1280 - onWorkStarted: jest.fn(),
1281 - onWorkStopped: jest.fn(),
1282 - };
1283 - SchedulerTracing.unstable_subscribe(subscriber);
1284 - SchedulerTracing.unstable_trace('test', performance.now(), () => {
1285 - function App() {
1286 - return (
1287 - <React.Suspense fallback={<Text text="Loading..." />}>
1288 - <AsyncText text="A" ms={1000} />
1289 - <AsyncText text="B" ms={2000} />
1290 - <AsyncText text="C" ms={3000} />
1291 - </React.Suspense>
1292 - );
1293 - }
1294 -
1295 - const root = ReactTestRenderer.create(null);
1296 - root.update(<App />);
1297 -
1298 - expect(Scheduler).toHaveYielded([
1299 - 'Suspend! [A]',
1300 - 'Suspend! [B]',
1301 - 'Suspend! [C]',
1302 - 'Loading...',
1303 - ]);
1304 -
1305 - // Resolve A
1306 - jest.advanceTimersByTime(1000);
1307 -
1308 - expect(Scheduler).toHaveYielded(['Promise resolved [A]']);
1309 - expect(Scheduler).toFlushUntilNextPaint([
1310 - 'A',
1311 - // The promises for B and C have now been thrown twice
1312 - 'Suspend! [B]',
1313 - 'Suspend! [C]',
1314 - ]);
1315 -
1316 - // Resolve B
1317 - jest.advanceTimersByTime(1000);
1318 -
1319 - expect(Scheduler).toHaveYielded(['Promise resolved [B]']);
1320 - expect(Scheduler).toFlushUntilNextPaint([
1321 - // Even though the promise for B was thrown twice, we should only
1322 - // re-render once.
1323 - 'B',
1324 - // The promise for C has now been thrown three times
1325 - 'Suspend! [C]',
1326 - ]);
1327 -
1328 - // Resolve C
1329 - jest.advanceTimersByTime(1000);
1330 -
1331 - expect(Scheduler).toHaveYielded(['Promise resolved [C]']);
1332 - expect(Scheduler).toFlushAndYield([
1333 - // Even though the promise for C was thrown three times, we should only
1334 - // re-render once.
1335 - 'C',
1336 - ]);
1337 - });
1338 -
1339 - expect(
1340 - subscriber.onInteractionScheduledWorkCompleted,
1341 - ).toHaveBeenCalledTimes(1);
1342 - });
1343 -
1271 it('#14162', () => {
1272 const {lazy} = React;
1273
packages/react-reconciler/src/__tests__/ReactTracing-test.internal.js deleted
-28
@@ -1,28 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @emails react-core
8 - * @jest-environment node
9 - */
10 -
11 -'use strict';
12 -
13 -describe('ReactTracing', () => {
14 - it('should error if profiling renderer and non-profiling scheduler/tracing bundles are combined', () => {
15 - jest.resetModules();
16 -
17 - const ReactFeatureFlags = require('shared/ReactFeatureFlags');
18 - ReactFeatureFlags.enableSchedulerTracing = false;
19 -
20 - require('scheduler/tracing');
21 -
22 - ReactFeatureFlags.enableSchedulerTracing = true;
23 -
24 - expect(() => require('react-dom')).toThrow(
25 - 'Learn more at https://reactjs.org/link/profiling',
26 - );
27 - });
28 -});
packages/react-reconciler/src/__tests__/ReactUpdaters-test.internal.js
-85
@@ -292,91 +292,6 @@ describe('updaters', () => {
292 done();
293 });
294
295 - // @gate experimental
296 - it('traces interaction through hidden subtree', async () => {
297 - const {
298 - FunctionComponent,
299 - HostRoot,
300 - } = require('react-reconciler/src/ReactWorkTags');
301 -
302 - // Note: This is based on a similar component we use in www. We can delete once
303 - // the extra div wrapper is no longer necessary.
304 - function LegacyHiddenDiv({children, mode}) {
305 - return (
306 - <div hidden={mode === 'hidden'}>
307 - <React.unstable_LegacyHidden
308 - mode={mode === 'hidden' ? 'unstable-defer-without-hiding' : mode}>
309 - {children}
310 - </React.unstable_LegacyHidden>
311 - </div>
312 - );
313 - }
314 -
315 - const Child = () => {
316 - const [didMount, setDidMount] = React.useState(false);
317 - Scheduler.unstable_yieldValue('Child');
318 - React.useEffect(() => {
319 - if (didMount) {
320 - Scheduler.unstable_yieldValue('Child:update');
321 - } else {
322 - Scheduler.unstable_yieldValue('Child:mount');
323 - setDidMount(true);
324 - }
325 - }, [didMount]);
326 - return <div />;
327 - };
328 -
329 - const App = () => {
330 - Scheduler.unstable_yieldValue('App');
331 - React.useEffect(() => {
332 - Scheduler.unstable_yieldValue('App:mount');
333 - }, []);
334 - return (
335 - <LegacyHiddenDiv mode="hidden">
336 - <Child />
337 - </LegacyHiddenDiv>
338 - );
339 - };
340 -
341 - const container = document.createElement('div');
342 - const root = ReactDOM.createRoot(container);
343 - await ReactTestUtils.act(async () => {
344 - root.render(<App />);
345 - });
346 -
347 - // TODO: There are 4 commits here instead of 3
348 - // because this update was scheduled at idle priority,
349 - // and idle updates are slightly higher priority than offscreen work.
350 - // So it takes two render passes to finish it.
351 - // The onCommit hook is called even after the no-op bailout update.
352 - expect(Scheduler).toHaveYielded([
353 - 'App',
354 - 'onCommitRoot',
355 - 'App:mount',
356 -
357 - 'Child',
358 - 'onCommitRoot',
359 - 'Child:mount',
360 -
361 - 'onCommitRoot',
362 -
363 - 'Child',
364 - 'onCommitRoot',
365 - 'Child:update',
366 - ]);
367 - expect(allSchedulerTypes).toEqual([
368 - // Initial render
369 - [null],
370 - // Offscreen update
371 - [],
372 - // Child passive effect
373 - [Child],
374 - // Offscreen update
375 - [],
376 - ]);
377 - expect(allSchedulerTags).toEqual([[HostRoot], [], [FunctionComponent], []]);
378 - });
379 -
295 // @gate experimental
296 it('should cover error handling', async () => {
297 let triggerError = null;
packages/react-reconciler/src/__tests__/useMutableSource-test.internal.js
-1
@@ -25,7 +25,6 @@ function loadModules() {
25 jest.useFakeTimers();
26
27 ReactFeatureFlags = require('shared/ReactFeatureFlags');
28 - ReactFeatureFlags.enableSchedulerTracing = true;
28 ReactFeatureFlags.enableProfilerTimer = true;
29
30 React = require('react');
packages/react-refresh/package.json
+2 -1
@@ -28,6 +28,7 @@
28 },
29 "devDependencies": {
30 "react-16-8": "npm:react@16.8.0",
31 - "react-dom-16-8": "npm:react-dom@16.8.0"
31 + "react-dom-16-8": "npm:react-dom@16.8.0",
32 + "scheduler-0-13": "npm:scheduler@0.13.0"
33 }
34 }
\ No newline at end of file
packages/react-refresh/src/__tests__/ReactFresh-test.js
+4
@@ -3844,6 +3844,10 @@ describe('ReactFresh', () => {
3844
3845 // Redirect all React/ReactDOM requires to v16.8.0
3846 // This version predates Fast Refresh support.
3847 + jest.mock('scheduler', () => jest.requireActual('scheduler-0-13'));
3848 + jest.mock('scheduler/tracing', () =>
3849 + jest.requireActual('scheduler-0-13/tracing'),
3850 + );
3851 jest.mock('react', () => jest.requireActual('react-16-8'));
3852 jest.mock('react-dom', () => jest.requireActual('react-dom-16-8'));
3853
packages/react/index.js
-5
@@ -28,11 +28,6 @@ export type ElementConfig<C> = React$ElementConfig<C>;
28 export type ElementRef<C> = React$ElementRef<C>;
29 export type Config<Props, DefaultProps> = React$Config<Props, DefaultProps>;
30 export type ChildrenArray<+T> = $ReadOnlyArray<ChildrenArray<T>> | T;
31 -export type Interaction = {
32 - name: string,
33 - timestamp: number,
34 - ...
35 -};
31
32 // Export all exports so that they're available in tests.
33 // We can't use export * from in Flow for some reason.
packages/react/src/__tests__/ReactDOMTracing-test.internal.js deleted
-922
@@ -1,922 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @emails react-core
8 - */
9 -
10 -'use strict';
11 -
12 -let React;
13 -let ReactDOM;
14 -let ReactDOMServer;
15 -let ReactFeatureFlags;
16 -let Scheduler;
17 -let SchedulerTracing;
18 -let TestUtils;
19 -let act;
20 -let onInteractionScheduledWorkCompleted;
21 -let onInteractionTraced;
22 -let onWorkCanceled;
23 -let onWorkScheduled;
24 -let onWorkStarted;
25 -let onWorkStopped;
26 -let IdleEventPriority;
27 -let ContinuousEventPriority;
28 -
29 -function loadModules() {
30 - ReactFeatureFlags = require('shared/ReactFeatureFlags');
31 -
32 - ReactFeatureFlags.enableProfilerTimer = true;
33 - ReactFeatureFlags.enableSchedulerTracing = true;
34 - ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
35 -
36 - React = require('react');
37 - ReactDOM = require('react-dom');
38 - ReactDOMServer = require('react-dom/server');
39 - Scheduler = require('scheduler');
40 - SchedulerTracing = require('scheduler/tracing');
41 - TestUtils = require('react-dom/test-utils');
42 -
43 - IdleEventPriority = require('react-reconciler/constants').IdleEventPriority;
44 - ContinuousEventPriority = require('react-reconciler/constants')
45 - .ContinuousEventPriority;
46 -
47 - act = TestUtils.unstable_concurrentAct;
48 -
49 - onInteractionScheduledWorkCompleted = jest.fn();
50 - onInteractionTraced = jest.fn();
51 - onWorkCanceled = jest.fn();
52 - onWorkScheduled = jest.fn();
53 - onWorkStarted = jest.fn();
54 - onWorkStopped = jest.fn();
55 -
56 - // Verify interaction subscriber methods are called as expected.
57 - SchedulerTracing.unstable_subscribe({
58 - onInteractionScheduledWorkCompleted,
59 - onInteractionTraced,
60 - onWorkCanceled,
61 - onWorkScheduled,
62 - onWorkStarted,
63 - onWorkStopped,
64 - });
65 -}
66 -
67 -// Note: This is based on a similar component we use in www. We can delete once
68 -// the extra div wrapper is no longer necessary.
69 -function LegacyHiddenDiv({children, mode}) {
70 - return (
71 - <div hidden={mode === 'hidden'}>
72 - <React.unstable_LegacyHidden
73 - mode={mode === 'hidden' ? 'unstable-defer-without-hiding' : mode}>
74 - {children}
75 - </React.unstable_LegacyHidden>
76 - </div>
77 - );
78 -}
79 -
80 -describe('ReactDOMTracing', () => {
81 - beforeEach(() => {
82 - jest.resetModules();
83 -
84 - loadModules();
85 - });
86 -
87 - describe('interaction tracing', () => {
88 - describe('hidden', () => {
89 - // @gate experimental
90 - it('traces interaction through hidden subtree', () => {
91 - const Child = () => {
92 - const [didMount, setDidMount] = React.useState(false);
93 - Scheduler.unstable_yieldValue('Child');
94 - React.useEffect(() => {
95 - if (didMount) {
96 - Scheduler.unstable_yieldValue('Child:update');
97 - } else {
98 - Scheduler.unstable_yieldValue('Child:mount');
99 - setDidMount(true);
100 - }
101 - }, [didMount]);
102 - return <div />;
103 - };
104 -
105 - const App = () => {
106 - Scheduler.unstable_yieldValue('App');
107 - React.useEffect(() => {
108 - Scheduler.unstable_yieldValue('App:mount');
109 - }, []);
110 - return (
111 - <LegacyHiddenDiv mode="hidden">
112 - <Child />
113 - </LegacyHiddenDiv>
114 - );
115 - };
116 -
117 - let interaction;
118 -
119 - const onRender = jest.fn();
120 -
121 - const container = document.createElement('div');
122 - const root = ReactDOM.createRoot(container);
123 - SchedulerTracing.unstable_trace('initialization', 0, () => {
124 - interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
125 - act(() => {
126 - if (gate(flags => flags.enableSyncDefaultUpdates)) {
127 - React.unstable_startTransition(() => {
128 - root.render(
129 - <React.Profiler id="test" onRender={onRender}>
130 - <App />
131 - </React.Profiler>,
132 - );
133 - });
134 - } else {
135 - root.render(
136 - <React.Profiler id="test" onRender={onRender}>
137 - <App />
138 - </React.Profiler>,
139 - );
140 - }
141 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
142 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
143 - interaction,
144 - );
145 - expect(Scheduler).toFlushAndYieldThrough(['App', 'App:mount']);
146 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
147 - expect(onRender).toHaveBeenCalledTimes(1);
148 - expect(onRender).toHaveLastRenderedWithInteractions(
149 - new Set([interaction]),
150 - );
151 - expect(Scheduler).toFlushAndYieldThrough(['Child', 'Child:mount']);
152 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
153 -
154 - expect(onRender).toHaveBeenCalledTimes(2);
155 - expect(onRender).toHaveLastRenderedWithInteractions(
156 - new Set([interaction]),
157 - );
158 -
159 - expect(Scheduler).toFlushAndYield(['Child', 'Child:update']);
160 - });
161 - });
162 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
163 - expect(
164 - onInteractionScheduledWorkCompleted,
165 - ).toHaveBeenLastNotifiedOfInteraction(interaction);
166 -
167 - if (gate(flags => flags.enableUseJSStackToTrackPassiveDurations)) {
168 - expect(onRender).toHaveBeenCalledTimes(3);
169 - } else {
170 - // TODO: This is 4 instead of 3 because this update was scheduled at
171 - // idle priority, and idle updates are slightly higher priority than
172 - // offscreen work. So it takes two render passes to finish it. Profiler
173 - // calls `onRender` for the first render even though everything
174 - // bails out.
175 - expect(onRender).toHaveBeenCalledTimes(4);
176 - }
177 - expect(onRender).toHaveLastRenderedWithInteractions(
178 - new Set([interaction]),
179 - );
180 - });
181 -
182 - // @gate experimental
183 - it('traces interaction through hidden subtree when there is other pending traced work', () => {
184 - const Child = () => {
185 - Scheduler.unstable_yieldValue('Child');
186 - return <div />;
187 - };
188 -
189 - let wrapped = null;
190 -
191 - const App = () => {
192 - Scheduler.unstable_yieldValue('App');
193 - React.useEffect(() => {
194 - wrapped = SchedulerTracing.unstable_wrap(() => {});
195 - Scheduler.unstable_yieldValue('App:mount');
196 - }, []);
197 - return (
198 - <LegacyHiddenDiv mode="hidden">
199 - <Child />
200 - </LegacyHiddenDiv>
201 - );
202 - };
203 -
204 - let interaction;
205 -
206 - const onRender = jest.fn();
207 -
208 - const container = document.createElement('div');
209 - const root = ReactDOM.createRoot(container);
210 - SchedulerTracing.unstable_trace('initialization', 0, () => {
211 - interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
212 -
213 - act(() => {
214 - root.render(
215 - <React.Profiler id="test" onRender={onRender}>
216 - <App />
217 - </React.Profiler>,
218 - );
219 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
220 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
221 - interaction,
222 - );
223 -
224 - expect(Scheduler).toFlushAndYieldThrough(['App', 'App:mount']);
225 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
226 - expect(onRender).toHaveBeenCalledTimes(1);
227 - expect(onRender).toHaveLastRenderedWithInteractions(
228 - new Set([interaction]),
229 - );
230 -
231 - expect(wrapped).not.toBeNull();
232 -
233 - expect(Scheduler).toFlushAndYield(['Child']);
234 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
235 - expect(onRender).toHaveBeenCalledTimes(2);
236 - expect(onRender).toHaveLastRenderedWithInteractions(
237 - new Set([interaction]),
238 - );
239 - });
240 - });
241 -
242 - wrapped();
243 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
244 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
245 - expect(
246 - onInteractionScheduledWorkCompleted,
247 - ).toHaveBeenLastNotifiedOfInteraction(interaction);
248 - });
249 -
250 - // @gate experimental
251 - it('traces interaction through hidden subtree that schedules more idle/never work', () => {
252 - const Child = () => {
253 - const [didMount, setDidMount] = React.useState(false);
254 - Scheduler.unstable_yieldValue('Child');
255 - React.useLayoutEffect(() => {
256 - if (didMount) {
257 - Scheduler.unstable_yieldValue('Child:update');
258 - } else {
259 - Scheduler.unstable_yieldValue('Child:mount');
260 - ReactDOM.unstable_runWithPriority(IdleEventPriority, () =>
261 - setDidMount(true),
262 - );
263 - }
264 - }, [didMount]);
265 - return <div />;
266 - };
267 -
268 - const App = () => {
269 - Scheduler.unstable_yieldValue('App');
270 - React.useEffect(() => {
271 - Scheduler.unstable_yieldValue('App:mount');
272 - }, []);
273 - return (
274 - <LegacyHiddenDiv mode="hidden">
275 - <Child />
276 - </LegacyHiddenDiv>
277 - );
278 - };
279 -
280 - let interaction;
281 -
282 - const onRender = jest.fn();
283 -
284 - const container = document.createElement('div');
285 - const root = ReactDOM.createRoot(container);
286 - SchedulerTracing.unstable_trace('initialization', 0, () => {
287 - interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
288 - act(() => {
289 - if (gate(flags => flags.enableSyncDefaultUpdates)) {
290 - React.unstable_startTransition(() => {
291 - root.render(
292 - <React.Profiler id="test" onRender={onRender}>
293 - <App />
294 - </React.Profiler>,
295 - );
296 - });
297 - } else {
298 - root.render(
299 - <React.Profiler id="test" onRender={onRender}>
300 - <App />
301 - </React.Profiler>,
302 - );
303 - }
304 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
305 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
306 - interaction,
307 - );
308 -
309 - expect(Scheduler).toFlushAndYieldThrough(['App', 'App:mount']);
310 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
311 - expect(onRender).toHaveBeenCalledTimes(1);
312 - expect(onRender).toHaveLastRenderedWithInteractions(
313 - new Set([interaction]),
314 - );
315 -
316 - expect(Scheduler).toFlushAndYieldThrough(['Child', 'Child:mount']);
317 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
318 - expect(onRender).toHaveBeenCalledTimes(2);
319 - expect(onRender).toHaveLastRenderedWithInteractions(
320 - new Set([interaction]),
321 - );
322 -
323 - expect(Scheduler).toFlushAndYield(['Child', 'Child:update']);
324 - });
325 - });
326 -
327 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
328 - expect(
329 - onInteractionScheduledWorkCompleted,
330 - ).toHaveBeenLastNotifiedOfInteraction(interaction);
331 -
332 - // TODO: This is 4 instead of 3 because this update was scheduled at
333 - // idle priority, and idle updates are slightly higher priority than
334 - // offscreen work. So it takes two render passes to finish it. Profiler
335 - // calls `onRender` for the first render even though everything
336 - // bails out.
337 - expect(onRender).toHaveBeenCalledTimes(4);
338 - expect(onRender).toHaveLastRenderedWithInteractions(
339 - new Set([interaction]),
340 - );
341 - });
342 -
343 - // @gate experimental
344 - it('does not continue interactions across pre-existing idle work', () => {
345 - const Child = () => {
346 - Scheduler.unstable_yieldValue('Child');
347 - return <div />;
348 - };
349 -
350 - let update = null;
351 -
352 - const WithHiddenWork = () => {
353 - Scheduler.unstable_yieldValue('WithHiddenWork');
354 - return (
355 - <LegacyHiddenDiv mode="hidden">
356 - <Child />
357 - </LegacyHiddenDiv>
358 - );
359 - };
360 -
361 - const Updater = () => {
362 - Scheduler.unstable_yieldValue('Updater');
363 - React.useEffect(() => {
364 - Scheduler.unstable_yieldValue('Updater:effect');
365 - });
366 -
367 - const setCount = React.useState(0)[1];
368 - update = () => {
369 - setCount(current => current + 1);
370 - };
371 -
372 - return <div />;
373 - };
374 -
375 - const App = () => {
376 - Scheduler.unstable_yieldValue('App');
377 - React.useEffect(() => {
378 - Scheduler.unstable_yieldValue('App:effect');
379 - });
380 -
381 - return (
382 - <>
383 - <WithHiddenWork />
384 - <Updater />
385 - </>
386 - );
387 - };
388 -
389 - const onRender = jest.fn();
390 - const container = document.createElement('div');
391 - const root = ReactDOM.createRoot(container);
392 -
393 - // Schedule some idle work without any interactions.
394 - act(() => {
395 - root.render(
396 - <React.Profiler id="test" onRender={onRender}>
397 - <App />
398 - </React.Profiler>,
399 - );
400 - expect(Scheduler).toFlushAndYieldThrough([
401 - 'App',
402 - 'WithHiddenWork',
403 - 'Updater',
404 - 'Updater:effect',
405 - 'App:effect',
406 - ]);
407 - expect(update).not.toBeNull();
408 -
409 - // Trace a higher-priority update.
410 - let interaction = null;
411 - SchedulerTracing.unstable_trace('update', 0, () => {
412 - interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
413 - update();
414 - });
415 - expect(interaction).not.toBeNull();
416 - expect(onRender).toHaveBeenCalledTimes(1);
417 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
418 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
419 - interaction,
420 - );
421 -
422 - // Ensure the traced interaction completes without being attributed to the pre-existing idle work.
423 - expect(Scheduler).toFlushAndYieldThrough([
424 - 'Updater',
425 - 'Updater:effect',
426 - ]);
427 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
428 - expect(
429 - onInteractionScheduledWorkCompleted,
430 - ).toHaveBeenLastNotifiedOfInteraction(interaction);
431 - expect(onRender).toHaveBeenCalledTimes(2);
432 - expect(onRender).toHaveLastRenderedWithInteractions(
433 - new Set([interaction]),
434 - );
435 -
436 - // Complete low-priority work and ensure no lingering interaction.
437 - expect(Scheduler).toFlushAndYield(['Child']);
438 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
439 - expect(onRender).toHaveBeenCalledTimes(3);
440 - expect(onRender).toHaveLastRenderedWithInteractions(new Set([]));
441 - });
442 - });
443 -
444 - // @gate experimental
445 - it('should properly trace interactions when there is work of interleaved priorities', () => {
446 - const Child = () => {
447 - Scheduler.unstable_yieldValue('Child');
448 - return <div />;
449 - };
450 -
451 - let scheduleUpdate = null;
452 - let scheduleUpdateWithHidden = null;
453 -
454 - const MaybeHiddenWork = () => {
455 - const [flag, setFlag] = React.useState(false);
456 - scheduleUpdateWithHidden = () => setFlag(true);
457 - Scheduler.unstable_yieldValue('MaybeHiddenWork');
458 - React.useEffect(() => {
459 - Scheduler.unstable_yieldValue('MaybeHiddenWork:effect');
460 - });
461 - return flag ? (
462 - <LegacyHiddenDiv mode="hidden">
463 - <Child />
464 - </LegacyHiddenDiv>
465 - ) : null;
466 - };
467 -
468 - const Updater = () => {
469 - Scheduler.unstable_yieldValue('Updater');
470 - React.useEffect(() => {
471 - Scheduler.unstable_yieldValue('Updater:effect');
472 - });
473 -
474 - const setCount = React.useState(0)[1];
475 - scheduleUpdate = () => setCount(current => current + 1);
476 -
477 - return <div />;
478 - };
479 -
480 - const App = () => {
481 - Scheduler.unstable_yieldValue('App');
482 - React.useEffect(() => {
483 - Scheduler.unstable_yieldValue('App:effect');
484 - });
485 -
486 - return (
487 - <>
488 - <MaybeHiddenWork />
489 - <Updater />
490 - </>
491 - );
492 - };
493 -
494 - const onRender = jest.fn();
495 - const container = document.createElement('div');
496 - const root = ReactDOM.createRoot(container);
497 -
498 - act(() => {
499 - root.render(
500 - <React.Profiler id="test" onRender={onRender}>
501 - <App />
502 - </React.Profiler>,
503 - );
504 - expect(Scheduler).toFlushAndYield([
505 - 'App',
506 - 'MaybeHiddenWork',
507 - 'Updater',
508 - 'MaybeHiddenWork:effect',
509 - 'Updater:effect',
510 - 'App:effect',
511 - ]);
512 - expect(scheduleUpdate).not.toBeNull();
513 - expect(scheduleUpdateWithHidden).not.toBeNull();
514 - expect(onRender).toHaveBeenCalledTimes(1);
515 -
516 - // schedule traced high-pri update and a (non-traced) low-pri update.
517 - let interaction = null;
518 - SchedulerTracing.unstable_trace('update', 0, () => {
519 - interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
520 - ReactDOM.unstable_runWithPriority(ContinuousEventPriority, () =>
521 - scheduleUpdateWithHidden(),
522 - );
523 - });
524 - if (gate(flags => flags.enableSyncDefaultUpdates)) {
525 - React.unstable_startTransition(() => {
526 - scheduleUpdate();
527 - });
528 - } else {
529 - scheduleUpdate();
530 - }
531 - expect(interaction).not.toBeNull();
532 - expect(onRender).toHaveBeenCalledTimes(1);
533 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
534 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
535 - interaction,
536 - );
537 -
538 - // high-pri update should leave behind idle work and should not complete the interaction
539 - expect(Scheduler).toFlushAndYieldThrough([
540 - 'MaybeHiddenWork',
541 - 'MaybeHiddenWork:effect',
542 - ]);
543 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
544 - expect(onRender).toHaveBeenCalledTimes(2);
545 - expect(onRender).toHaveLastRenderedWithInteractions(
546 - new Set([interaction]),
547 - );
548 -
549 - // low-pri update should not have the interaction
550 - expect(Scheduler).toFlushAndYieldThrough([
551 - 'Updater',
552 - 'Updater:effect',
553 - ]);
554 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
555 - expect(onRender).toHaveBeenCalledTimes(3);
556 - expect(onRender).toHaveLastRenderedWithInteractions(new Set([]));
557 -
558 - // idle work should complete the interaction
559 - expect(Scheduler).toFlushAndYield(['Child']);
560 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
561 - expect(
562 - onInteractionScheduledWorkCompleted,
563 - ).toHaveBeenLastNotifiedOfInteraction(interaction);
564 - expect(onRender).toHaveBeenCalledTimes(4);
565 - expect(onRender).toHaveLastRenderedWithInteractions(
566 - new Set([interaction]),
567 - );
568 - });
569 - });
570 -
571 - // @gate experimental
572 - it('should properly trace interactions through a multi-pass SuspenseList render', () => {
573 - const SuspenseList = React.SuspenseList;
574 - const Suspense = React.Suspense;
575 - function Text({text}) {
576 - Scheduler.unstable_yieldValue(text);
577 - React.useEffect(() => {
578 - Scheduler.unstable_yieldValue('Commit ' + text);
579 - });
580 - return <span>{text}</span>;
581 - }
582 - function App() {
583 - return (
584 - <SuspenseList revealOrder="forwards">
585 - <Suspense fallback={<Text text="Loading A" />}>
586 - <Text text="A" />
587 - </Suspense>
588 - <Suspense fallback={<Text text="Loading B" />}>
589 - <Text text="B" />
590 - </Suspense>
591 - <Suspense fallback={<Text text="Loading C" />}>
592 - <Text text="C" />
593 - </Suspense>
594 - </SuspenseList>
595 - );
596 - }
597 -
598 - const container = document.createElement('div');
599 - const root = ReactDOM.createRoot(container);
600 -
601 - let interaction;
602 -
603 - act(() => {
604 - SchedulerTracing.unstable_trace('initialization', 0, () => {
605 - interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
606 - // This render is only CPU bound. Nothing suspends.
607 - if (gate(flags => flags.enableSyncDefaultUpdates)) {
608 - React.unstable_startTransition(() => {
609 - root.render(<App />);
610 - });
611 - } else {
612 - root.render(<App />);
613 - }
614 - });
615 -
616 - expect(Scheduler).toFlushAndYieldThrough(['A']);
617 -
618 - Scheduler.unstable_advanceTime(200);
619 - jest.advanceTimersByTime(200);
620 -
621 - expect(Scheduler).toFlushAndYieldThrough(['B']);
622 -
623 - Scheduler.unstable_advanceTime(300);
624 - jest.advanceTimersByTime(300);
625 -
626 - // Time has now elapsed for so long that we're just going to give up
627 - // rendering the rest of the content. So that we can at least show
628 - // something.
629 - expect(Scheduler).toFlushAndYieldThrough([
630 - 'Loading C',
631 - 'Commit A',
632 - 'Commit B',
633 - 'Commit Loading C',
634 - ]);
635 -
636 - // Schedule an unrelated low priority update that shouldn't be included
637 - // in the previous interaction. This is meant to ensure that we don't
638 - // rely on the whole tree completing to cover up bugs.
639 - ReactDOM.unstable_runWithPriority(IdleEventPriority, () => {
640 - root.render(<App />);
641 - });
642 -
643 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
644 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
645 - interaction,
646 - );
647 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
648 -
649 - // Then we do a second pass to commit the last item.
650 - expect(Scheduler).toFlushAndYieldThrough(['C', 'Commit C']);
651 -
652 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
653 - expect(
654 - onInteractionScheduledWorkCompleted,
655 - ).toHaveBeenLastNotifiedOfInteraction(interaction);
656 - });
657 - });
658 - });
659 -
660 - describe('hydration', () => {
661 - // @gate experimental
662 - it('traces interaction across hydration', () => {
663 - const ref = React.createRef();
664 -
665 - function Child() {
666 - return 'Hello';
667 - }
668 -
669 - function App() {
670 - return (
671 - <div>
672 - <span ref={ref}>
673 - <Child />
674 - </span>
675 - </div>
676 - );
677 - }
678 -
679 - // Render the final HTML.
680 - const finalHTML = ReactDOMServer.renderToString(<App />);
681 -
682 - const container = document.createElement('div');
683 - container.innerHTML = finalHTML;
684 -
685 - let interaction;
686 -
687 - const root = ReactDOM.createRoot(container, {hydrate: true});
688 -
689 - // Hydrate it.
690 - SchedulerTracing.unstable_trace('initialization', 0, () => {
691 - interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
692 -
693 - root.render(<App />);
694 - });
695 - Scheduler.unstable_flushAll();
696 - jest.runAllTimers();
697 -
698 - expect(ref.current).not.toBe(null);
699 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
700 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
701 - interaction,
702 - );
703 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
704 - expect(
705 - onInteractionScheduledWorkCompleted,
706 - ).toHaveBeenLastNotifiedOfInteraction(interaction);
707 - });
708 -
709 - // @gate experimental
710 - it('traces interaction across suspended hydration', async () => {
711 - let suspend = false;
712 - let resolve;
713 - const promise = new Promise(
714 - resolvePromise => (resolve = resolvePromise),
715 - );
716 - const ref = React.createRef();
717 -
718 - function Child() {
719 - if (suspend) {
720 - throw promise;
721 - } else {
722 - return 'Hello';
723 - }
724 - }
725 -
726 - function App() {
727 - return (
728 - <div>
729 - <React.Suspense fallback="Loading...">
730 - <span ref={ref}>
731 - <Child />
732 - </span>
733 - </React.Suspense>
734 - </div>
735 - );
736 - }
737 -
738 - // Render the final HTML.
739 - // Don't suspend on the server.
740 - const finalHTML = ReactDOMServer.renderToString(<App />);
741 -
742 - const container = document.createElement('div');
743 - container.innerHTML = finalHTML;
744 -
745 - let interaction;
746 -
747 - const root = ReactDOM.createRoot(container, {hydrate: true});
748 -
749 - // Start hydrating but simulate blocking for suspense data.
750 - suspend = true;
751 - SchedulerTracing.unstable_trace('initialization', 0, () => {
752 - interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
753 -
754 - root.render(<App />);
755 - });
756 - Scheduler.unstable_flushAll();
757 - jest.runAllTimers();
758 -
759 - expect(ref.current).toBe(null);
760 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
761 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
762 - interaction,
763 - );
764 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
765 -
766 - // Resolving the promise should continue hydration
767 - suspend = false;
768 - resolve();
769 - await promise;
770 - Scheduler.unstable_flushAll();
771 - jest.runAllTimers();
772 -
773 - expect(ref.current).not.toBe(null);
774 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
775 - expect(
776 - onInteractionScheduledWorkCompleted,
777 - ).toHaveBeenLastNotifiedOfInteraction(interaction);
778 - });
779 -
780 - // @gate experimental
781 - it('traces interaction across suspended hydration from server', async () => {
782 - // Copied from ReactDOMHostConfig.js
783 - const SUSPENSE_START_DATA = '$';
784 - const SUSPENSE_PENDING_START_DATA = '$?';
785 -
786 - const ref = React.createRef();
787 -
788 - function App() {
789 - return (
790 - <React.Suspense fallback="Loading...">
791 - <span ref={ref}>Hello</span>
792 - </React.Suspense>
793 - );
794 - }
795 -
796 - const container = document.createElement('div');
797 -
798 - // Render the final HTML.
799 - const finalHTML = ReactDOMServer.renderToString(<App />);
800 -
801 - // Replace the marker with a pending state.
802 - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
803 - const escapedMarker = SUSPENSE_START_DATA.replace(
804 - /[.*+\-?^${}()|[\]\\]/g,
805 - '\\$&',
806 - );
807 - container.innerHTML = finalHTML.replace(
808 - new RegExp(escapedMarker, 'g'),
809 - SUSPENSE_PENDING_START_DATA,
810 - );
811 -
812 - let interaction;
813 -
814 - const root = ReactDOM.createRoot(container, {hydrate: true});
815 -
816 - // Start hydrating but simulate blocking for suspense data from the server.
817 - SchedulerTracing.unstable_trace('initialization', 0, () => {
818 - interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
819 -
820 - root.render(<App />);
821 - });
822 - Scheduler.unstable_flushAll();
823 - jest.runAllTimers();
824 -
825 - expect(ref.current).toBe(null);
826 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
827 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
828 - interaction,
829 - );
830 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
831 -
832 - // Unblock rendering, pretend the content is injected by the server.
833 - const startNode = container.childNodes[0];
834 - expect(startNode).not.toBe(null);
835 - expect(startNode.nodeType).toBe(Node.COMMENT_NODE);
836 -
837 - startNode.textContent = SUSPENSE_START_DATA;
838 - startNode._reactRetry();
839 -
840 - Scheduler.unstable_flushAll();
841 - jest.runAllTimers();
842 -
843 - expect(ref.current).not.toBe(null);
844 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
845 - expect(
846 - onInteractionScheduledWorkCompleted,
847 - ).toHaveBeenLastNotifiedOfInteraction(interaction);
848 - });
849 -
850 - // @gate experimental
851 - it('traces interaction across client-rendered hydration', () => {
852 - let suspend = false;
853 - const promise = new Promise(() => {});
854 - const ref = React.createRef();
855 -
856 - function Child() {
857 - if (suspend) {
858 - throw promise;
859 - } else {
860 - return 'Hello';
861 - }
862 - }
863 -
864 - function App() {
865 - return (
866 - <div>
867 - <React.Suspense fallback="Loading...">
868 - <span ref={ref}>
869 - <Child />
870 - </span>
871 - </React.Suspense>
872 - </div>
873 - );
874 - }
875 -
876 - // Render the final HTML.
877 - suspend = true;
878 - const finalHTML = ReactDOMServer.renderToString(<App />);
879 -
880 - const container = document.createElement('div');
881 - container.innerHTML = finalHTML;
882 -
883 - let interaction;
884 -
885 - const root = ReactDOM.createRoot(container, {hydrate: true});
886 -
887 - // Hydrate without suspending to fill in the client-rendered content.
888 - suspend = false;
889 - SchedulerTracing.unstable_trace('initialization', 0, () => {
890 - interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
891 -
892 - root.render(<App />);
893 - });
894 -
895 - expect(onWorkStopped).toHaveBeenCalledTimes(1);
896 -
897 - // Advance time a bit so that we get into a new expiration bucket.
898 - Scheduler.unstable_advanceTime(300);
899 - jest.advanceTimersByTime(300);
900 -
901 - Scheduler.unstable_flushAll();
902 - jest.runAllTimers();
903 -
904 - expect(ref.current).not.toBe(null);
905 -
906 - // We should've had two commits that was traced.
907 - // First one that hydrates the parent, and then one that hydrates
908 - // the boundary at higher than Never priority.
909 - expect(onWorkStopped).toHaveBeenCalledTimes(3);
910 -
911 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
912 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
913 - interaction,
914 - );
915 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
916 - expect(
917 - onInteractionScheduledWorkCompleted,
918 - ).toHaveBeenLastNotifiedOfInteraction(interaction);
919 - });
920 - });
921 - });
922 -});
packages/react/src/__tests__/ReactProfiler-test.internal.js
+2330 -4527
@@ -14,23 +14,15 @@ let React;
14 let ReactFeatureFlags;
15 let ReactNoop;
16 let Scheduler;
17 -let ReactCache;
17 let ReactTestRenderer;
18 let ReactTestRendererAct;
20 -let SchedulerTracing;
19 let AdvanceTime;
22 -let AsyncText;
23 -let ComponentWithPassiveEffect;
24 -let Text;
25 -let TextResource;
26 -let resourcePromise;
20
21 function loadModules({
22 enableProfilerTimer = true,
23 enableProfilerCommitHooks = true,
24 enableProfilerNestedUpdatePhase = true,
25 enableProfilerNestedUpdateScheduledHook = false,
33 - enableSchedulerTracing = true,
26 replayFailedUnitOfWorkWithInvokeGuardedCallback = false,
27 useNoopRenderer = false,
28 } = {}) {
@@ -40,13 +32,10 @@ function loadModules({
32 ReactFeatureFlags.enableProfilerCommitHooks = enableProfilerCommitHooks;
33 ReactFeatureFlags.enableProfilerNestedUpdatePhase = enableProfilerNestedUpdatePhase;
34 ReactFeatureFlags.enableProfilerNestedUpdateScheduledHook = enableProfilerNestedUpdateScheduledHook;
43 - ReactFeatureFlags.enableSchedulerTracing = enableSchedulerTracing;
35 ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = replayFailedUnitOfWorkWithInvokeGuardedCallback;
36
37 React = require('react');
38 Scheduler = require('scheduler');
48 - SchedulerTracing = require('scheduler/tracing');
49 - ReactCache = require('react-cache');
39
40 if (useNoopRenderer) {
41 ReactNoop = require('react-noop-renderer');
@@ -72,4960 +61,2774 @@ function loadModules({
61 return this.props.children || null;
62 }
63 };
75 -
76 - resourcePromise = null;
77 -
78 - TextResource = ReactCache.unstable_createResource(
79 - ([text, ms = 0]) => {
80 - resourcePromise = new Promise((resolve, reject) =>
81 - setTimeout(() => {
82 - Scheduler.unstable_yieldValue(`Promise resolved [${text}]`);
83 - resolve(text);
84 - }, ms),
85 - );
86 - return resourcePromise;
87 - },
88 - ([text, ms]) => text,
89 - );
90 -
91 - AsyncText = ({ms, text}) => {
92 - try {
93 - TextResource.read([text, ms]);
94 - Scheduler.unstable_yieldValue(`AsyncText [${text}]`);
95 - return text;
96 - } catch (promise) {
97 - if (typeof promise.then === 'function') {
98 - Scheduler.unstable_yieldValue(`Suspend [${text}]`);
99 - } else {
100 - Scheduler.unstable_yieldValue(`Error [${text}]`);
101 - }
102 - throw promise;
103 - }
104 - };
105 -
106 - Text = ({text}) => {
107 - Scheduler.unstable_yieldValue(`Text [${text}]`);
108 - return text;
109 - };
110 -
111 - ComponentWithPassiveEffect = () => {
112 - // Intentionally schedule a passive effect so the onPostCommit hook will be called.
113 - React.useEffect(() => {});
114 - return null;
115 - };
64 }
65
66 describe('Profiler', () => {
67 describe('works in profiling and non-profiling bundles', () => {
120 - [true, false].forEach(enableSchedulerTracing => {
121 - [true, false].forEach(enableProfilerTimer => {
122 - describe(`enableSchedulerTracing:${
123 - enableSchedulerTracing ? 'enabled' : 'disabled'
124 - } enableProfilerTimer:${
125 - enableProfilerTimer ? 'enabled' : 'disabled'
126 - }`, () => {
127 - beforeEach(() => {
128 - jest.resetModules();
129 -
130 - loadModules({enableSchedulerTracing, enableProfilerTimer});
131 - });
132 -
133 - // This will throw in production too,
134 - // But the test is only interested in verifying the DEV error message.
135 - if (__DEV__ && enableProfilerTimer) {
136 - it('should warn if required params are missing', () => {
137 - expect(() => {
138 - ReactTestRenderer.create(<React.Profiler />);
139 - }).toErrorDev(
140 - 'Profiler must specify an "id" of type `string` as a prop. Received the type `undefined` instead.',
141 - {
142 - withoutStack: true,
143 - },
144 - );
145 - });
146 - }
147 -
148 - it('should support an empty Profiler (with no children)', () => {
149 - // As root
150 - expect(
151 - ReactTestRenderer.create(
152 - <React.Profiler id="label" onRender={jest.fn()} />,
153 - ).toJSON(),
154 - ).toMatchSnapshot();
155 -
156 - // As non-root
157 - expect(
158 - ReactTestRenderer.create(
159 - <div>
160 - <React.Profiler id="label" onRender={jest.fn()} />
161 - </div>,
162 - ).toJSON(),
163 - ).toMatchSnapshot();
164 - });
165 -
166 - it('should render children', () => {
167 - const FunctionComponent = ({label}) => <span>{label}</span>;
168 - const renderer = ReactTestRenderer.create(
169 - <div>
170 - <span>outside span</span>
171 - <React.Profiler id="label" onRender={jest.fn()}>
172 - <span>inside span</span>
173 - <FunctionComponent label="function component" />
174 - </React.Profiler>
175 - </div>,
176 - );
177 - expect(renderer.toJSON()).toMatchSnapshot();
178 - });
68 + [true, false].forEach(enableProfilerTimer => {
69 + describe(`enableProfilerTimer:${
70 + enableProfilerTimer ? 'enabled' : 'disabled'
71 + }`, () => {
72 + beforeEach(() => {
73 + jest.resetModules();
74 +
75 + loadModules({enableProfilerTimer});
76 + });
77
180 - it('should support nested Profilers', () => {
181 - const FunctionComponent = ({label}) => <div>{label}</div>;
182 - class ClassComponent extends React.Component {
183 - render() {
184 - return <block>{this.props.label}</block>;
185 - }
186 - }
187 - const renderer = ReactTestRenderer.create(
188 - <React.Profiler id="outer" onRender={jest.fn()}>
189 - <FunctionComponent label="outer function component" />
190 - <React.Profiler id="inner" onRender={jest.fn()}>
191 - <ClassComponent label="inner class component" />
192 - <span>inner span</span>
193 - </React.Profiler>
194 - </React.Profiler>,
78 + // This will throw in production too,
79 + // But the test is only interested in verifying the DEV error message.
80 + if (__DEV__ && enableProfilerTimer) {
81 + it('should warn if required params are missing', () => {
82 + expect(() => {
83 + ReactTestRenderer.create(<React.Profiler />);
84 + }).toErrorDev(
85 + 'Profiler must specify an "id" of type `string` as a prop. Received the type `undefined` instead.',
86 + {
87 + withoutStack: true,
88 + },
89 );
196 - expect(renderer.toJSON()).toMatchSnapshot();
90 });
198 - });
199 - });
200 - });
201 - });
91 + }
92
203 - [true, false].forEach(enableSchedulerTracing => {
204 - describe(`onRender enableSchedulerTracing:${
205 - enableSchedulerTracing ? 'enabled' : 'disabled'
206 - }`, () => {
207 - beforeEach(() => {
208 - jest.resetModules();
93 + it('should support an empty Profiler (with no children)', () => {
94 + // As root
95 + expect(
96 + ReactTestRenderer.create(
97 + <React.Profiler id="label" onRender={jest.fn()} />,
98 + ).toJSON(),
99 + ).toMatchSnapshot();
100
210 - loadModules({
211 - enableSchedulerTracing,
101 + // As non-root
102 + expect(
103 + ReactTestRenderer.create(
104 + <div>
105 + <React.Profiler id="label" onRender={jest.fn()} />
106 + </div>,
107 + ).toJSON(),
108 + ).toMatchSnapshot();
109 });
213 - });
110
215 - it('should handle errors thrown', () => {
216 - const callback = jest.fn(id => {
217 - if (id === 'throw') {
218 - throw Error('expected');
219 - }
111 + it('should render children', () => {
112 + const FunctionComponent = ({label}) => <span>{label}</span>;
113 + const renderer = ReactTestRenderer.create(
114 + <div>
115 + <span>outside span</span>
116 + <React.Profiler id="label" onRender={jest.fn()}>
117 + <span>inside span</span>
118 + <FunctionComponent label="function component" />
119 + </React.Profiler>
120 + </div>,
121 + );
122 + expect(renderer.toJSON()).toMatchSnapshot();
123 });
124
222 - let didMount = false;
223 - class ClassComponent extends React.Component {
224 - componentDidMount() {
225 - didMount = true;
226 - }
227 - render() {
228 - return this.props.children;
125 + it('should support nested Profilers', () => {
126 + const FunctionComponent = ({label}) => <div>{label}</div>;
127 + class ClassComponent extends React.Component {
128 + render() {
129 + return <block>{this.props.label}</block>;
130 + }
131 }
230 - }
231 -
232 - // Errors thrown from onRender should not break the commit phase,
233 - // Or prevent other lifecycles from being called.
234 - expect(() =>
235 - ReactTestRenderer.create(
236 - <ClassComponent>
237 - <React.Profiler id="do-not-throw" onRender={callback}>
238 - <React.Profiler id="throw" onRender={callback}>
239 - <div />
240 - </React.Profiler>
132 + const renderer = ReactTestRenderer.create(
133 + <React.Profiler id="outer" onRender={jest.fn()}>
134 + <FunctionComponent label="outer function component" />
135 + <React.Profiler id="inner" onRender={jest.fn()}>
136 + <ClassComponent label="inner class component" />
137 + <span>inner span</span>
138 </React.Profiler>
242 - </ClassComponent>,
243 - ),
244 - ).toThrow('expected');
245 - expect(didMount).toBe(true);
246 - expect(callback).toHaveBeenCalledTimes(2);
247 - });
248 -
249 - // @gate experimental || !enableSyncDefaultUpdates
250 - it('is not invoked until the commit phase', () => {
251 - const callback = jest.fn();
252 -
253 - const Yield = ({value}) => {
254 - Scheduler.unstable_yieldValue(value);
255 - return null;
256 - };
257 -
258 - if (gate(flags => flags.enableSyncDefaultUpdates)) {
259 - React.unstable_startTransition(() => {
260 - ReactTestRenderer.create(
261 - <React.Profiler id="test" onRender={callback}>
262 - <Yield value="first" />
263 - <Yield value="last" />
264 - </React.Profiler>,
265 - {
266 - unstable_isConcurrent: true,
267 - },
268 - );
269 - });
270 - } else {
271 - ReactTestRenderer.create(
272 - <React.Profiler id="test" onRender={callback}>
273 - <Yield value="first" />
274 - <Yield value="last" />
139 </React.Profiler>,
276 - {
277 - unstable_isConcurrent: true,
278 - },
140 );
280 - }
281 -
282 - // Times are logged until a render is committed.
283 - expect(Scheduler).toFlushAndYieldThrough(['first']);
284 - expect(callback).toHaveBeenCalledTimes(0);
285 - expect(Scheduler).toFlushAndYield(['last']);
286 - expect(callback).toHaveBeenCalledTimes(1);
141 + expect(renderer.toJSON()).toMatchSnapshot();
142 + });
143 });
144 + });
145 + });
146 +});
147
289 - it('does not record times for components outside of Profiler tree', () => {
290 - // Mock the Scheduler module so we can track how many times the current
291 - // time is read
292 - jest.mock('scheduler', obj => {
293 - const ActualScheduler = require.requireActual(
294 - 'scheduler/unstable_mock',
295 - );
296 - return {
297 - ...ActualScheduler,
298 - unstable_now: function mockUnstableNow() {
299 - ActualScheduler.unstable_yieldValue('read current time');
300 - return ActualScheduler.unstable_now();
301 - },
302 - };
303 - });
148 +describe(`onRender`, () => {
149 + beforeEach(() => {
150 + jest.resetModules();
151
305 - jest.resetModules();
152 + loadModules();
153 + });
154
307 - loadModules({enableSchedulerTracing});
155 + it('should handle errors thrown', () => {
156 + const callback = jest.fn(id => {
157 + if (id === 'throw') {
158 + throw Error('expected');
159 + }
160 + });
161
309 - // Clear yields in case the current time is read during initialization.
310 - Scheduler.unstable_clearYields();
162 + let didMount = false;
163 + class ClassComponent extends React.Component {
164 + componentDidMount() {
165 + didMount = true;
166 + }
167 + render() {
168 + return this.props.children;
169 + }
170 + }
171
312 - ReactTestRenderer.create(
313 - <div>
314 - <AdvanceTime />
315 - <AdvanceTime />
316 - <AdvanceTime />
317 - <AdvanceTime />
318 - <AdvanceTime />
319 - </div>,
320 - );
172 + // Errors thrown from onRender should not break the commit phase,
173 + // Or prevent other lifecycles from being called.
174 + expect(() =>
175 + ReactTestRenderer.create(
176 + <ClassComponent>
177 + <React.Profiler id="do-not-throw" onRender={callback}>
178 + <React.Profiler id="throw" onRender={callback}>
179 + <div />
180 + </React.Profiler>
181 + </React.Profiler>
182 + </ClassComponent>,
183 + ),
184 + ).toThrow('expected');
185 + expect(didMount).toBe(true);
186 + expect(callback).toHaveBeenCalledTimes(2);
187 + });
188
322 - // TODO: unstable_now is called by more places than just the profiler.
323 - // Rewrite this test so it's less fragile.
324 - expect(Scheduler).toHaveYielded([
325 - 'read current time',
326 - 'read current time',
327 - 'read current time',
328 - 'read current time',
329 - 'read current time',
330 - ]);
331 -
332 - // Restore original mock
333 - jest.mock('scheduler', () =>
334 - require.requireActual('scheduler/unstable_mock'),
335 - );
336 - });
189 + // @gate experimental || !enableSyncDefaultUpdates
190 + it('is not invoked until the commit phase', () => {
191 + const callback = jest.fn();
192
338 - it('does not report work done on a sibling', () => {
339 - const callback = jest.fn();
193 + const Yield = ({value}) => {
194 + Scheduler.unstable_yieldValue(value);
195 + return null;
196 + };
197
341 - const DoesNotUpdate = React.memo(
342 - function DoesNotUpdateInner() {
343 - Scheduler.unstable_advanceTime(10);
344 - return null;
198 + if (gate(flags => flags.enableSyncDefaultUpdates)) {
199 + React.unstable_startTransition(() => {
200 + ReactTestRenderer.create(
201 + <React.Profiler id="test" onRender={callback}>
202 + <Yield value="first" />
203 + <Yield value="last" />
204 + </React.Profiler>,
205 + {
206 + unstable_isConcurrent: true,
207 },
346 - () => true,
208 );
209 + });
210 + } else {
211 + ReactTestRenderer.create(
212 + <React.Profiler id="test" onRender={callback}>
213 + <Yield value="first" />
214 + <Yield value="last" />
215 + </React.Profiler>,
216 + {
217 + unstable_isConcurrent: true,
218 + },
219 + );
220 + }
221
349 - let updateProfilerSibling;
222 + // Times are logged until a render is committed.
223 + expect(Scheduler).toFlushAndYieldThrough(['first']);
224 + expect(callback).toHaveBeenCalledTimes(0);
225 + expect(Scheduler).toFlushAndYield(['last']);
226 + expect(callback).toHaveBeenCalledTimes(1);
227 + });
228
351 - function ProfilerSibling() {
352 - const [count, setCount] = React.useState(0);
353 - updateProfilerSibling = () => setCount(count + 1);
354 - return null;
355 - }
229 + it('does not record times for components outside of Profiler tree', () => {
230 + // Mock the Scheduler module so we can track how many times the current
231 + // time is read
232 + jest.mock('scheduler', obj => {
233 + const ActualScheduler = require.requireActual('scheduler/unstable_mock');
234 + return {
235 + ...ActualScheduler,
236 + unstable_now: function mockUnstableNow() {
237 + ActualScheduler.unstable_yieldValue('read current time');
238 + return ActualScheduler.unstable_now();
239 + },
240 + };
241 + });
242
357 - function App() {
358 - return (
359 - <React.Fragment>
360 - <React.Profiler id="test" onRender={callback}>
361 - <DoesNotUpdate />
362 - </React.Profiler>
363 - <ProfilerSibling />
364 - </React.Fragment>
365 - );
366 - }
243 + jest.resetModules();
244 +
245 + loadModules();
246 +
247 + // Clear yields in case the current time is read during initialization.
248 + Scheduler.unstable_clearYields();
249 +
250 + ReactTestRenderer.create(
251 + <div>
252 + <AdvanceTime />
253 + <AdvanceTime />
254 + <AdvanceTime />
255 + <AdvanceTime />
256 + <AdvanceTime />
257 + </div>,
258 + );
259 +
260 + // TODO: unstable_now is called by more places than just the profiler.
261 + // Rewrite this test so it's less fragile.
262 + expect(Scheduler).toHaveYielded([
263 + 'read current time',
264 + 'read current time',
265 + 'read current time',
266 + 'read current time',
267 + 'read current time',
268 + ]);
269 +
270 + // Restore original mock
271 + jest.mock('scheduler', () =>
272 + require.requireActual('scheduler/unstable_mock'),
273 + );
274 + });
275
368 - const renderer = ReactTestRenderer.create(<App />);
276 + it('does not report work done on a sibling', () => {
277 + const callback = jest.fn();
278
370 - expect(callback).toHaveBeenCalledTimes(1);
279 + const DoesNotUpdate = React.memo(
280 + function DoesNotUpdateInner() {
281 + Scheduler.unstable_advanceTime(10);
282 + return null;
283 + },
284 + () => true,
285 + );
286
372 - let call = callback.mock.calls[0];
287 + let updateProfilerSibling;
288
374 - expect(call).toHaveLength(enableSchedulerTracing ? 7 : 6);
375 - expect(call[0]).toBe('test');
376 - expect(call[1]).toBe('mount');
377 - expect(call[2]).toBe(10); // actual time
378 - expect(call[3]).toBe(10); // base time
379 - expect(call[4]).toBe(0); // start time
380 - expect(call[5]).toBe(10); // commit time
381 - expect(call[6]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
289 + function ProfilerSibling() {
290 + const [count, setCount] = React.useState(0);
291 + updateProfilerSibling = () => setCount(count + 1);
292 + return null;
293 + }
294
383 - callback.mockReset();
295 + function App() {
296 + return (
297 + <React.Fragment>
298 + <React.Profiler id="test" onRender={callback}>
299 + <DoesNotUpdate />
300 + </React.Profiler>
301 + <ProfilerSibling />
302 + </React.Fragment>
303 + );
304 + }
305
385 - Scheduler.unstable_advanceTime(20); // 10 -> 30
306 + const renderer = ReactTestRenderer.create(<App />);
307
387 - renderer.update(<App />);
308 + expect(callback).toHaveBeenCalledTimes(1);
309
389 - if (gate(flags => flags.enableUseJSStackToTrackPassiveDurations)) {
390 - // None of the Profiler's subtree was rendered because App bailed out before the Profiler.
391 - // So we expect onRender not to be called.
392 - expect(callback).not.toHaveBeenCalled();
393 - } else {
394 - // Updating a parent reports a re-render,
395 - // since React technically did a little bit of work between the Profiler and the bailed out subtree.
396 - // This is not optimal but it's how the old reconciler fork works.
397 - expect(callback).toHaveBeenCalledTimes(1);
310 + let call = callback.mock.calls[0];
311
399 - call = callback.mock.calls[0];
312 + expect(call).toHaveLength(6);
313 + expect(call[0]).toBe('test');
314 + expect(call[1]).toBe('mount');
315 + expect(call[2]).toBe(10); // actual time
316 + expect(call[3]).toBe(10); // base time
317 + expect(call[4]).toBe(0); // start time
318 + expect(call[5]).toBe(10); // commit time
319
401 - expect(call).toHaveLength(enableSchedulerTracing ? 7 : 6);
402 - expect(call[0]).toBe('test');
403 - expect(call[1]).toBe('update');
404 - expect(call[2]).toBe(0); // actual time
405 - expect(call[3]).toBe(10); // base time
406 - expect(call[4]).toBe(30); // start time
407 - expect(call[5]).toBe(30); // commit time
408 - expect(call[6]).toEqual(
409 - enableSchedulerTracing ? new Set() : undefined,
410 - ); // interaction events
320 + callback.mockReset();
321
412 - callback.mockReset();
413 - }
322 + Scheduler.unstable_advanceTime(20); // 10 -> 30
323
415 - Scheduler.unstable_advanceTime(20); // 30 -> 50
324 + renderer.update(<App />);
325
417 - // Updating a sibling should not report a re-render.
418 - ReactTestRendererAct(updateProfilerSibling);
326 + if (gate(flags => flags.enableUseJSStackToTrackPassiveDurations)) {
327 + // None of the Profiler's subtree was rendered because App bailed out before the Profiler.
328 + // So we expect onRender not to be called.
329 + expect(callback).not.toHaveBeenCalled();
330 + } else {
331 + // Updating a parent reports a re-render,
332 + // since React technically did a little bit of work between the Profiler and the bailed out subtree.
333 + // This is not optimal but it's how the old reconciler fork works.
334 + expect(callback).toHaveBeenCalledTimes(1);
335
420 - expect(callback).not.toHaveBeenCalled();
421 - });
336 + call = callback.mock.calls[0];
337
423 - it('logs render times for both mount and update', () => {
424 - const callback = jest.fn();
338 + expect(call).toHaveLength(6);
339 + expect(call[0]).toBe('test');
340 + expect(call[1]).toBe('update');
341 + expect(call[2]).toBe(0); // actual time
342 + expect(call[3]).toBe(10); // base time
343 + expect(call[4]).toBe(30); // start time
344 + expect(call[5]).toBe(30); // commit time
345
426 - Scheduler.unstable_advanceTime(5); // 0 -> 5
346 + callback.mockReset();
347 + }
348
428 - const renderer = ReactTestRenderer.create(
429 - <React.Profiler id="test" onRender={callback}>
430 - <AdvanceTime />
431 - </React.Profiler>,
432 - );
349 + Scheduler.unstable_advanceTime(20); // 30 -> 50
350
434 - expect(callback).toHaveBeenCalledTimes(1);
351 + // Updating a sibling should not report a re-render.
352 + ReactTestRendererAct(updateProfilerSibling);
353
436 - let [call] = callback.mock.calls;
354 + expect(callback).not.toHaveBeenCalled();
355 + });
356
438 - expect(call).toHaveLength(enableSchedulerTracing ? 7 : 6);
439 - expect(call[0]).toBe('test');
440 - expect(call[1]).toBe('mount');
441 - expect(call[2]).toBe(10); // actual time
442 - expect(call[3]).toBe(10); // base time
443 - expect(call[4]).toBe(5); // start time
444 - expect(call[5]).toBe(15); // commit time
445 - expect(call[6]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
357 + it('logs render times for both mount and update', () => {
358 + const callback = jest.fn();
359
447 - callback.mockReset();
360 + Scheduler.unstable_advanceTime(5); // 0 -> 5
361
449 - Scheduler.unstable_advanceTime(20); // 15 -> 35
362 + const renderer = ReactTestRenderer.create(
363 + <React.Profiler id="test" onRender={callback}>
364 + <AdvanceTime />
365 + </React.Profiler>,
366 + );
367
451 - renderer.update(
452 - <React.Profiler id="test" onRender={callback}>
453 - <AdvanceTime />
454 - </React.Profiler>,
455 - );
368 + expect(callback).toHaveBeenCalledTimes(1);
369
457 - expect(callback).toHaveBeenCalledTimes(1);
370 + let [call] = callback.mock.calls;
371
459 - [call] = callback.mock.calls;
372 + expect(call).toHaveLength(6);
373 + expect(call[0]).toBe('test');
374 + expect(call[1]).toBe('mount');
375 + expect(call[2]).toBe(10); // actual time
376 + expect(call[3]).toBe(10); // base time
377 + expect(call[4]).toBe(5); // start time
378 + expect(call[5]).toBe(15); // commit time
379
461 - expect(call).toHaveLength(enableSchedulerTracing ? 7 : 6);
462 - expect(call[0]).toBe('test');
463 - expect(call[1]).toBe('update');
464 - expect(call[2]).toBe(10); // actual time
465 - expect(call[3]).toBe(10); // base time
466 - expect(call[4]).toBe(35); // start time
467 - expect(call[5]).toBe(45); // commit time
468 - expect(call[6]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
380 + callback.mockReset();
381
470 - callback.mockReset();
382 + Scheduler.unstable_advanceTime(20); // 15 -> 35
383
472 - Scheduler.unstable_advanceTime(20); // 45 -> 65
384 + renderer.update(
385 + <React.Profiler id="test" onRender={callback}>
386 + <AdvanceTime />
387 + </React.Profiler>,
388 + );
389
474 - renderer.update(
475 - <React.Profiler id="test" onRender={callback}>
476 - <AdvanceTime byAmount={4} />
477 - </React.Profiler>,
478 - );
390 + expect(callback).toHaveBeenCalledTimes(1);
391
480 - expect(callback).toHaveBeenCalledTimes(1);
392 + [call] = callback.mock.calls;
393
482 - [call] = callback.mock.calls;
394 + expect(call).toHaveLength(6);
395 + expect(call[0]).toBe('test');
396 + expect(call[1]).toBe('update');
397 + expect(call[2]).toBe(10); // actual time
398 + expect(call[3]).toBe(10); // base time
399 + expect(call[4]).toBe(35); // start time
400 + expect(call[5]).toBe(45); // commit time
401
484 - expect(call).toHaveLength(enableSchedulerTracing ? 7 : 6);
485 - expect(call[0]).toBe('test');
486 - expect(call[1]).toBe('update');
487 - expect(call[2]).toBe(4); // actual time
488 - expect(call[3]).toBe(4); // base time
489 - expect(call[4]).toBe(65); // start time
490 - expect(call[5]).toBe(69); // commit time
491 - expect(call[6]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
492 - });
402 + callback.mockReset();
403
494 - it('includes render times of nested Profilers in their parent times', () => {
495 - const callback = jest.fn();
404 + Scheduler.unstable_advanceTime(20); // 45 -> 65
405
497 - Scheduler.unstable_advanceTime(5); // 0 -> 5
406 + renderer.update(
407 + <React.Profiler id="test" onRender={callback}>
408 + <AdvanceTime byAmount={4} />
409 + </React.Profiler>,
410 + );
411
499 - ReactTestRenderer.create(
500 - <React.Fragment>
501 - <React.Profiler id="parent" onRender={callback}>
502 - <AdvanceTime byAmount={10}>
503 - <React.Profiler id="child" onRender={callback}>
504 - <AdvanceTime byAmount={20} />
505 - </React.Profiler>
506 - </AdvanceTime>
507 - </React.Profiler>
508 - </React.Fragment>,
509 - );
412 + expect(callback).toHaveBeenCalledTimes(1);
413
511 - expect(callback).toHaveBeenCalledTimes(2);
512 -
513 - // Callbacks bubble (reverse order).
514 - const [childCall, parentCall] = callback.mock.calls;
515 - expect(childCall[0]).toBe('child');
516 - expect(parentCall[0]).toBe('parent');
517 -
518 - // Parent times should include child times
519 - expect(childCall[2]).toBe(20); // actual time
520 - expect(childCall[3]).toBe(20); // base time
521 - expect(childCall[4]).toBe(15); // start time
522 - expect(childCall[5]).toBe(35); // commit time
523 - expect(parentCall[2]).toBe(30); // actual time
524 - expect(parentCall[3]).toBe(30); // base time
525 - expect(parentCall[4]).toBe(5); // start time
526 - expect(parentCall[5]).toBe(35); // commit time
527 - });
414 + [call] = callback.mock.calls;
415
529 - it('traces sibling Profilers separately', () => {
530 - const callback = jest.fn();
416 + expect(call).toHaveLength(6);
417 + expect(call[0]).toBe('test');
418 + expect(call[1]).toBe('update');
419 + expect(call[2]).toBe(4); // actual time
420 + expect(call[3]).toBe(4); // base time
421 + expect(call[4]).toBe(65); // start time
422 + expect(call[5]).toBe(69); // commit time
423 + });
424
532 - Scheduler.unstable_advanceTime(5); // 0 -> 5
425 + it('includes render times of nested Profilers in their parent times', () => {
426 + const callback = jest.fn();
427
534 - ReactTestRenderer.create(
535 - <React.Fragment>
536 - <React.Profiler id="first" onRender={callback}>
428 + Scheduler.unstable_advanceTime(5); // 0 -> 5
429 +
430 + ReactTestRenderer.create(
431 + <React.Fragment>
432 + <React.Profiler id="parent" onRender={callback}>
433 + <AdvanceTime byAmount={10}>
434 + <React.Profiler id="child" onRender={callback}>
435 <AdvanceTime byAmount={20} />
436 </React.Profiler>
539 - <React.Profiler id="second" onRender={callback}>
540 - <AdvanceTime byAmount={5} />
541 - </React.Profiler>
542 - </React.Fragment>,
543 - );
437 + </AdvanceTime>
438 + </React.Profiler>
439 + </React.Fragment>,
440 + );
441 +
442 + expect(callback).toHaveBeenCalledTimes(2);
443 +
444 + // Callbacks bubble (reverse order).
445 + const [childCall, parentCall] = callback.mock.calls;
446 + expect(childCall[0]).toBe('child');
447 + expect(parentCall[0]).toBe('parent');
448 +
449 + // Parent times should include child times
450 + expect(childCall[2]).toBe(20); // actual time
451 + expect(childCall[3]).toBe(20); // base time
452 + expect(childCall[4]).toBe(15); // start time
453 + expect(childCall[5]).toBe(35); // commit time
454 + expect(parentCall[2]).toBe(30); // actual time
455 + expect(parentCall[3]).toBe(30); // base time
456 + expect(parentCall[4]).toBe(5); // start time
457 + expect(parentCall[5]).toBe(35); // commit time
458 + });
459
545 - expect(callback).toHaveBeenCalledTimes(2);
546 -
547 - const [firstCall, secondCall] = callback.mock.calls;
548 - expect(firstCall[0]).toBe('first');
549 - expect(secondCall[0]).toBe('second');
550 -
551 - // Parent times should include child times
552 - expect(firstCall[2]).toBe(20); // actual time
553 - expect(firstCall[3]).toBe(20); // base time
554 - expect(firstCall[4]).toBe(5); // start time
555 - expect(firstCall[5]).toBe(30); // commit time
556 - expect(secondCall[2]).toBe(5); // actual time
557 - expect(secondCall[3]).toBe(5); // base time
558 - expect(secondCall[4]).toBe(25); // start time
559 - expect(secondCall[5]).toBe(30); // commit time
560 - });
460 + it('traces sibling Profilers separately', () => {
461 + const callback = jest.fn();
462 +
463 + Scheduler.unstable_advanceTime(5); // 0 -> 5
464 +
465 + ReactTestRenderer.create(
466 + <React.Fragment>
467 + <React.Profiler id="first" onRender={callback}>
468 + <AdvanceTime byAmount={20} />
469 + </React.Profiler>
470 + <React.Profiler id="second" onRender={callback}>
471 + <AdvanceTime byAmount={5} />
472 + </React.Profiler>
473 + </React.Fragment>,
474 + );
475 +
476 + expect(callback).toHaveBeenCalledTimes(2);
477 +
478 + const [firstCall, secondCall] = callback.mock.calls;
479 + expect(firstCall[0]).toBe('first');
480 + expect(secondCall[0]).toBe('second');
481 +
482 + // Parent times should include child times
483 + expect(firstCall[2]).toBe(20); // actual time
484 + expect(firstCall[3]).toBe(20); // base time
485 + expect(firstCall[4]).toBe(5); // start time
486 + expect(firstCall[5]).toBe(30); // commit time
487 + expect(secondCall[2]).toBe(5); // actual time
488 + expect(secondCall[3]).toBe(5); // base time
489 + expect(secondCall[4]).toBe(25); // start time
490 + expect(secondCall[5]).toBe(30); // commit time
491 + });
492
562 - it('does not include time spent outside of profile root', () => {
563 - const callback = jest.fn();
493 + it('does not include time spent outside of profile root', () => {
494 + const callback = jest.fn();
495 +
496 + Scheduler.unstable_advanceTime(5); // 0 -> 5
497 +
498 + ReactTestRenderer.create(
499 + <React.Fragment>
500 + <AdvanceTime byAmount={20} />
501 + <React.Profiler id="test" onRender={callback}>
502 + <AdvanceTime byAmount={5} />
503 + </React.Profiler>
504 + <AdvanceTime byAmount={20} />
505 + </React.Fragment>,
506 + );
507 +
508 + expect(callback).toHaveBeenCalledTimes(1);
509 +
510 + const [call] = callback.mock.calls;
511 + expect(call[0]).toBe('test');
512 + expect(call[2]).toBe(5); // actual time
513 + expect(call[3]).toBe(5); // base time
514 + expect(call[4]).toBe(25); // start time
515 + expect(call[5]).toBe(50); // commit time
516 + });
517
565 - Scheduler.unstable_advanceTime(5); // 0 -> 5
518 + it('is not called when blocked by sCU false', () => {
519 + const callback = jest.fn();
520
567 - ReactTestRenderer.create(
568 - <React.Fragment>
569 - <AdvanceTime byAmount={20} />
570 - <React.Profiler id="test" onRender={callback}>
571 - <AdvanceTime byAmount={5} />
572 - </React.Profiler>
573 - <AdvanceTime byAmount={20} />
574 - </React.Fragment>,
575 - );
521 + let instance;
522 + class Updater extends React.Component {
523 + state = {};
524 + render() {
525 + instance = this;
526 + return this.props.children;
527 + }
528 + }
529
577 - expect(callback).toHaveBeenCalledTimes(1);
530 + const renderer = ReactTestRenderer.create(
531 + <React.Profiler id="outer" onRender={callback}>
532 + <Updater>
533 + <React.Profiler id="inner" onRender={callback}>
534 + <div />
535 + </React.Profiler>
536 + </Updater>
537 + </React.Profiler>,
538 + );
539
579 - const [call] = callback.mock.calls;
580 - expect(call[0]).toBe('test');
581 - expect(call[2]).toBe(5); // actual time
582 - expect(call[3]).toBe(5); // base time
583 - expect(call[4]).toBe(25); // start time
584 - expect(call[5]).toBe(50); // commit time
585 - });
540 + // All profile callbacks are called for initial render
541 + expect(callback).toHaveBeenCalledTimes(2);
542
587 - it('is not called when blocked by sCU false', () => {
588 - const callback = jest.fn();
543 + callback.mockReset();
544
590 - let instance;
591 - class Updater extends React.Component {
592 - state = {};
593 - render() {
594 - instance = this;
595 - return this.props.children;
596 - }
597 - }
545 + renderer.unstable_flushSync(() => {
546 + instance.setState({
547 + count: 1,
548 + });
549 + });
550
599 - const renderer = ReactTestRenderer.create(
600 - <React.Profiler id="outer" onRender={callback}>
601 - <Updater>
602 - <React.Profiler id="inner" onRender={callback}>
603 - <div />
604 - </React.Profiler>
605 - </Updater>
606 - </React.Profiler>,
607 - );
551 + // Only call onRender for paths that have re-rendered.
552 + // Since the Updater's props didn't change,
553 + // React does not re-render its children.
554 + expect(callback).toHaveBeenCalledTimes(1);
555 + expect(callback.mock.calls[0][0]).toBe('outer');
556 + });
557
609 - // All profile callbacks are called for initial render
610 - expect(callback).toHaveBeenCalledTimes(2);
558 + it('decreases actual time but not base time when sCU prevents an update', () => {
559 + const callback = jest.fn();
560
612 - callback.mockReset();
561 + Scheduler.unstable_advanceTime(5); // 0 -> 5
562
614 - renderer.unstable_flushSync(() => {
615 - instance.setState({
616 - count: 1,
617 - });
618 - });
563 + const renderer = ReactTestRenderer.create(
564 + <React.Profiler id="test" onRender={callback}>
565 + <AdvanceTime byAmount={10}>
566 + <AdvanceTime byAmount={13} shouldComponentUpdate={false} />
567 + </AdvanceTime>
568 + </React.Profiler>,
569 + );
570
620 - // Only call onRender for paths that have re-rendered.
621 - // Since the Updater's props didn't change,
622 - // React does not re-render its children.
623 - expect(callback).toHaveBeenCalledTimes(1);
624 - expect(callback.mock.calls[0][0]).toBe('outer');
625 - });
571 + expect(callback).toHaveBeenCalledTimes(1);
572
627 - it('decreases actual time but not base time when sCU prevents an update', () => {
628 - const callback = jest.fn();
573 + Scheduler.unstable_advanceTime(30); // 28 -> 58
574
630 - Scheduler.unstable_advanceTime(5); // 0 -> 5
575 + renderer.update(
576 + <React.Profiler id="test" onRender={callback}>
577 + <AdvanceTime byAmount={4}>
578 + <AdvanceTime byAmount={7} shouldComponentUpdate={false} />
579 + </AdvanceTime>
580 + </React.Profiler>,
581 + );
582
632 - const renderer = ReactTestRenderer.create(
633 - <React.Profiler id="test" onRender={callback}>
634 - <AdvanceTime byAmount={10}>
635 - <AdvanceTime byAmount={13} shouldComponentUpdate={false} />
636 - </AdvanceTime>
637 - </React.Profiler>,
638 - );
583 + expect(callback).toHaveBeenCalledTimes(2);
584
640 - expect(callback).toHaveBeenCalledTimes(1);
585 + const [mountCall, updateCall] = callback.mock.calls;
586
642 - Scheduler.unstable_advanceTime(30); // 28 -> 58
587 + expect(mountCall[1]).toBe('mount');
588 + expect(mountCall[2]).toBe(23); // actual time
589 + expect(mountCall[3]).toBe(23); // base time
590 + expect(mountCall[4]).toBe(5); // start time
591 + expect(mountCall[5]).toBe(28); // commit time
592
644 - renderer.update(
645 - <React.Profiler id="test" onRender={callback}>
646 - <AdvanceTime byAmount={4}>
647 - <AdvanceTime byAmount={7} shouldComponentUpdate={false} />
648 - </AdvanceTime>
649 - </React.Profiler>,
650 - );
593 + expect(updateCall[1]).toBe('update');
594 + expect(updateCall[2]).toBe(4); // actual time
595 + expect(updateCall[3]).toBe(17); // base time
596 + expect(updateCall[4]).toBe(58); // start time
597 + expect(updateCall[5]).toBe(62); // commit time
598 + });
599
652 - expect(callback).toHaveBeenCalledTimes(2);
600 + it('includes time spent in render phase lifecycles', () => {
601 + class WithLifecycles extends React.Component {
602 + state = {};
603 + static getDerivedStateFromProps() {
604 + Scheduler.unstable_advanceTime(3);
605 + return null;
606 + }
607 + shouldComponentUpdate() {
608 + Scheduler.unstable_advanceTime(7);
609 + return true;
610 + }
611 + render() {
612 + Scheduler.unstable_advanceTime(5);
613 + return null;
614 + }
615 + }
616
654 - const [mountCall, updateCall] = callback.mock.calls;
617 + const callback = jest.fn();
618
656 - expect(mountCall[1]).toBe('mount');
657 - expect(mountCall[2]).toBe(23); // actual time
658 - expect(mountCall[3]).toBe(23); // base time
659 - expect(mountCall[4]).toBe(5); // start time
660 - expect(mountCall[5]).toBe(28); // commit time
619 + Scheduler.unstable_advanceTime(5); // 0 -> 5
620
662 - expect(updateCall[1]).toBe('update');
663 - expect(updateCall[2]).toBe(4); // actual time
664 - expect(updateCall[3]).toBe(17); // base time
665 - expect(updateCall[4]).toBe(58); // start time
666 - expect(updateCall[5]).toBe(62); // commit time
667 - });
621 + const renderer = ReactTestRenderer.create(
622 + <React.Profiler id="test" onRender={callback}>
623 + <WithLifecycles />
624 + </React.Profiler>,
625 + );
626
669 - it('includes time spent in render phase lifecycles', () => {
670 - class WithLifecycles extends React.Component {
671 - state = {};
672 - static getDerivedStateFromProps() {
673 - Scheduler.unstable_advanceTime(3);
674 - return null;
675 - }
676 - shouldComponentUpdate() {
677 - Scheduler.unstable_advanceTime(7);
678 - return true;
679 - }
680 - render() {
681 - Scheduler.unstable_advanceTime(5);
682 - return null;
683 - }
684 - }
627 + Scheduler.unstable_advanceTime(15); // 13 -> 28
628
686 - const callback = jest.fn();
629 + renderer.update(
630 + <React.Profiler id="test" onRender={callback}>
631 + <WithLifecycles />
632 + </React.Profiler>,
633 + );
634
688 - Scheduler.unstable_advanceTime(5); // 0 -> 5
635 + expect(callback).toHaveBeenCalledTimes(2);
636
690 - const renderer = ReactTestRenderer.create(
691 - <React.Profiler id="test" onRender={callback}>
692 - <WithLifecycles />
693 - </React.Profiler>,
694 - );
637 + const [mountCall, updateCall] = callback.mock.calls;
638
696 - Scheduler.unstable_advanceTime(15); // 13 -> 28
639 + expect(mountCall[1]).toBe('mount');
640 + expect(mountCall[2]).toBe(8); // actual time
641 + expect(mountCall[3]).toBe(8); // base time
642 + expect(mountCall[4]).toBe(5); // start time
643 + expect(mountCall[5]).toBe(13); // commit time
644
698 - renderer.update(
699 - <React.Profiler id="test" onRender={callback}>
700 - <WithLifecycles />
701 - </React.Profiler>,
702 - );
645 + expect(updateCall[1]).toBe('update');
646 + expect(updateCall[2]).toBe(15); // actual time
647 + expect(updateCall[3]).toBe(15); // base time
648 + expect(updateCall[4]).toBe(28); // start time
649 + expect(updateCall[5]).toBe(43); // commit time
650 + });
651
704 - expect(callback).toHaveBeenCalledTimes(2);
652 + it('should clear nested-update flag when multiple cascading renders are scheduled', () => {
653 + loadModules({
654 + useNoopRenderer: true,
655 + });
656
706 - const [mountCall, updateCall] = callback.mock.calls;
657 + function Component() {
658 + const [didMount, setDidMount] = React.useState(false);
659 + const [didMountAndUpdate, setDidMountAndUpdate] = React.useState(false);
660
708 - expect(mountCall[1]).toBe('mount');
709 - expect(mountCall[2]).toBe(8); // actual time
710 - expect(mountCall[3]).toBe(8); // base time
711 - expect(mountCall[4]).toBe(5); // start time
712 - expect(mountCall[5]).toBe(13); // commit time
661 + React.useLayoutEffect(() => {
662 + setDidMount(true);
663 + }, []);
664
714 - expect(updateCall[1]).toBe('update');
715 - expect(updateCall[2]).toBe(15); // actual time
716 - expect(updateCall[3]).toBe(15); // base time
717 - expect(updateCall[4]).toBe(28); // start time
718 - expect(updateCall[5]).toBe(43); // commit time
719 - });
665 + React.useEffect(() => {
666 + if (didMount && !didMountAndUpdate) {
667 + setDidMountAndUpdate(true);
668 + }
669 + }, [didMount, didMountAndUpdate]);
670
721 - it('should clear nested-update flag when multiple cascading renders are scheduled', () => {
722 - loadModules({
723 - enableSchedulerTracing,
724 - useNoopRenderer: true,
725 - });
671 + Scheduler.unstable_yieldValue(`${didMount}:${didMountAndUpdate}`);
672
727 - function Component() {
728 - const [didMount, setDidMount] = React.useState(false);
729 - const [didMountAndUpdate, setDidMountAndUpdate] = React.useState(
730 - false,
731 - );
673 + return null;
674 + }
675
733 - React.useLayoutEffect(() => {
734 - setDidMount(true);
735 - }, []);
676 + const onRender = jest.fn();
677
737 - React.useEffect(() => {
738 - if (didMount && !didMountAndUpdate) {
739 - setDidMountAndUpdate(true);
740 - }
741 - }, [didMount, didMountAndUpdate]);
678 + ReactNoop.act(() => {
679 + ReactNoop.render(
680 + <React.Profiler id="root" onRender={onRender}>
681 + <Component />
682 + </React.Profiler>,
683 + );
684 + });
685 + expect(Scheduler).toHaveYielded(['false:false', 'true:false', 'true:true']);
686
743 - Scheduler.unstable_yieldValue(`${didMount}:${didMountAndUpdate}`);
687 + expect(onRender).toHaveBeenCalledTimes(3);
688 + expect(onRender.mock.calls[0][1]).toBe('mount');
689 + expect(onRender.mock.calls[1][1]).toBe('nested-update');
690 + expect(onRender.mock.calls[2][1]).toBe('update');
691 + });
692
745 - return null;
746 - }
693 + // @gate experimental
694 + it('is properly distinguish updates and nested-updates when there is more than sync remaining work', () => {
695 + loadModules({
696 + useNoopRenderer: true,
697 + });
698
748 - const onRender = jest.fn();
699 + function Component() {
700 + const [didMount, setDidMount] = React.useState(false);
701
750 - ReactNoop.act(() => {
751 - ReactNoop.render(
752 - <React.Profiler id="root" onRender={onRender}>
753 - <Component />
754 - </React.Profiler>,
755 - );
756 - });
757 - expect(Scheduler).toHaveYielded([
758 - 'false:false',
759 - 'true:false',
760 - 'true:true',
761 - ]);
762 -
763 - expect(onRender).toHaveBeenCalledTimes(3);
764 - expect(onRender.mock.calls[0][1]).toBe('mount');
765 - expect(onRender.mock.calls[1][1]).toBe('nested-update');
766 - expect(onRender.mock.calls[2][1]).toBe('update');
767 - });
702 + React.useLayoutEffect(() => {
703 + setDidMount(true);
704 + }, []);
705 + Scheduler.unstable_yieldValue(didMount);
706 + return didMount;
707 + }
708
769 - // @gate experimental
770 - it('is properly distinguish updates and nested-updates when there is more than sync remaining work', () => {
771 - loadModules({
772 - enableSchedulerTracing,
773 - useNoopRenderer: true,
774 - });
709 + const onRender = jest.fn();
710 +
711 + // Schedule low-priority work.
712 + React.unstable_startTransition(() =>
713 + ReactNoop.render(
714 + <React.Profiler id="root" onRender={onRender}>
715 + <Component />
716 + </React.Profiler>,
717 + ),
718 + );
719 +
720 + // Flush sync work with a nested update
721 + ReactNoop.flushSync(() => {
722 + ReactNoop.render(
723 + <React.Profiler id="root" onRender={onRender}>
724 + <Component />
725 + </React.Profiler>,
726 + );
727 + });
728 + expect(Scheduler).toHaveYielded([false, true]);
729
776 - function Component() {
777 - const [didMount, setDidMount] = React.useState(false);
730 + // Verify that the nested update inside of the sync work is appropriately tagged.
731 + expect(onRender).toHaveBeenCalledTimes(2);
732 + expect(onRender.mock.calls[0][1]).toBe('mount');
733 + expect(onRender.mock.calls[1][1]).toBe('nested-update');
734 + });
735
779 - React.useLayoutEffect(() => {
780 - setDidMount(true);
781 - }, []);
782 - Scheduler.unstable_yieldValue(didMount);
783 - return didMount;
784 - }
736 + describe('with regard to interruptions', () => {
737 + // @gate experimental || !enableSyncDefaultUpdates
738 + it('should accumulate actual time after a scheduling interruptions', () => {
739 + const callback = jest.fn();
740
786 - const onRender = jest.fn();
741 + const Yield = ({renderTime}) => {
742 + Scheduler.unstable_advanceTime(renderTime);
743 + Scheduler.unstable_yieldValue('Yield:' + renderTime);
744 + return null;
745 + };
746
788 - // Schedule low-priority work.
789 - React.unstable_startTransition(() =>
790 - ReactNoop.render(
791 - <React.Profiler id="root" onRender={onRender}>
792 - <Component />
793 - </React.Profiler>,
794 - ),
795 - );
747 + Scheduler.unstable_advanceTime(5); // 0 -> 5
748
797 - // Flush sync work with a nested update
798 - ReactNoop.flushSync(() => {
799 - ReactNoop.render(
800 - <React.Profiler id="root" onRender={onRender}>
801 - <Component />
749 + // Render partially, but run out of time before completing.
750 + if (gate(flags => flags.enableSyncDefaultUpdates)) {
751 + React.unstable_startTransition(() => {
752 + ReactTestRenderer.create(
753 + <React.Profiler id="test" onRender={callback}>
754 + <Yield renderTime={2} />
755 + <Yield renderTime={3} />
756 </React.Profiler>,
757 + {unstable_isConcurrent: true},
758 );
759 });
805 - expect(Scheduler).toHaveYielded([false, true]);
806 -
807 - // Verify that the nested update inside of the sync work is appropriately tagged.
808 - expect(onRender).toHaveBeenCalledTimes(2);
809 - expect(onRender.mock.calls[0][1]).toBe('mount');
810 - expect(onRender.mock.calls[1][1]).toBe('nested-update');
811 - });
760 + } else {
761 + ReactTestRenderer.create(
762 + <React.Profiler id="test" onRender={callback}>
763 + <Yield renderTime={2} />
764 + <Yield renderTime={3} />
765 + </React.Profiler>,
766 + {unstable_isConcurrent: true},
767 + );
768 + }
769 + expect(Scheduler).toFlushAndYieldThrough(['Yield:2']);
770 + expect(callback).toHaveBeenCalledTimes(0);
771
813 - describe('with regard to interruptions', () => {
814 - // @gate experimental || !enableSyncDefaultUpdates
815 - it('should accumulate actual time after a scheduling interruptions', () => {
816 - const callback = jest.fn();
772 + // Resume render for remaining children.
773 + expect(Scheduler).toFlushAndYield(['Yield:3']);
774
818 - const Yield = ({renderTime}) => {
819 - Scheduler.unstable_advanceTime(renderTime);
820 - Scheduler.unstable_yieldValue('Yield:' + renderTime);
821 - return null;
822 - };
775 + // Verify that logged times include both durations above.
776 + expect(callback).toHaveBeenCalledTimes(1);
777 + const [call] = callback.mock.calls;
778 + expect(call[2]).toBe(5); // actual time
779 + expect(call[3]).toBe(5); // base time
780 + expect(call[4]).toBe(5); // start time
781 + expect(call[5]).toBe(10); // commit time
782 + });
783
824 - Scheduler.unstable_advanceTime(5); // 0 -> 5
784 + // @gate experimental || !enableSyncDefaultUpdates
785 + it('should not include time between frames', () => {
786 + const callback = jest.fn();
787
826 - // Render partially, but run out of time before completing.
827 - if (gate(flags => flags.enableSyncDefaultUpdates)) {
828 - React.unstable_startTransition(() => {
829 - ReactTestRenderer.create(
830 - <React.Profiler id="test" onRender={callback}>
831 - <Yield renderTime={2} />
832 - <Yield renderTime={3} />
833 - </React.Profiler>,
834 - {unstable_isConcurrent: true},
835 - );
836 - });
837 - } else {
838 - ReactTestRenderer.create(
839 - <React.Profiler id="test" onRender={callback}>
840 - <Yield renderTime={2} />
841 - <Yield renderTime={3} />
842 - </React.Profiler>,
843 - {unstable_isConcurrent: true},
844 - );
845 - }
846 - expect(Scheduler).toFlushAndYieldThrough(['Yield:2']);
847 - expect(callback).toHaveBeenCalledTimes(0);
788 + const Yield = ({renderTime}) => {
789 + Scheduler.unstable_advanceTime(renderTime);
790 + Scheduler.unstable_yieldValue('Yield:' + renderTime);
791 + return null;
792 + };
793
849 - // Resume render for remaining children.
850 - expect(Scheduler).toFlushAndYield(['Yield:3']);
794 + Scheduler.unstable_advanceTime(5); // 0 -> 5
795
852 - // Verify that logged times include both durations above.
853 - expect(callback).toHaveBeenCalledTimes(1);
854 - const [call] = callback.mock.calls;
855 - expect(call[2]).toBe(5); // actual time
856 - expect(call[3]).toBe(5); // base time
857 - expect(call[4]).toBe(5); // start time
858 - expect(call[5]).toBe(10); // commit time
796 + // Render partially, but don't finish.
797 + // This partial render should take 5ms of simulated time.
798 + if (gate(flags => flags.enableSyncDefaultUpdates)) {
799 + React.unstable_startTransition(() => {
800 + ReactTestRenderer.create(
801 + <React.Profiler id="outer" onRender={callback}>
802 + <Yield renderTime={5} />
803 + <Yield renderTime={10} />
804 + <React.Profiler id="inner" onRender={callback}>
805 + <Yield renderTime={17} />
806 + </React.Profiler>
807 + </React.Profiler>,
808 + {unstable_isConcurrent: true},
809 + );
810 });
811 + } else {
812 + ReactTestRenderer.create(
813 + <React.Profiler id="outer" onRender={callback}>
814 + <Yield renderTime={5} />
815 + <Yield renderTime={10} />
816 + <React.Profiler id="inner" onRender={callback}>
817 + <Yield renderTime={17} />
818 + </React.Profiler>
819 + </React.Profiler>,
820 + {unstable_isConcurrent: true},
821 + );
822 + }
823 + expect(Scheduler).toFlushAndYieldThrough(['Yield:5']);
824 + expect(callback).toHaveBeenCalledTimes(0);
825 +
826 + // Simulate time moving forward while frame is paused.
827 + Scheduler.unstable_advanceTime(50); // 10 -> 60
828 +
829 + // Flush the remaining work,
830 + // Which should take an additional 10ms of simulated time.
831 + expect(Scheduler).toFlushAndYield(['Yield:10', 'Yield:17']);
832 + expect(callback).toHaveBeenCalledTimes(2);
833 +
834 + const [innerCall, outerCall] = callback.mock.calls;
835 +
836 + // Verify that the actual time includes all work times,
837 + // But not the time that elapsed between frames.
838 + expect(innerCall[0]).toBe('inner');
839 + expect(innerCall[2]).toBe(17); // actual time
840 + expect(innerCall[3]).toBe(17); // base time
841 + expect(innerCall[4]).toBe(70); // start time
842 + expect(innerCall[5]).toBe(87); // commit time
843 + expect(outerCall[0]).toBe('outer');
844 + expect(outerCall[2]).toBe(32); // actual time
845 + expect(outerCall[3]).toBe(32); // base time
846 + expect(outerCall[4]).toBe(5); // start time
847 + expect(outerCall[5]).toBe(87); // commit time
848 + });
849
861 - // @gate experimental || !enableSyncDefaultUpdates
862 - it('should not include time between frames', () => {
863 - const callback = jest.fn();
864 -
865 - const Yield = ({renderTime}) => {
866 - Scheduler.unstable_advanceTime(renderTime);
867 - Scheduler.unstable_yieldValue('Yield:' + renderTime);
868 - return null;
869 - };
850 + // @gate experimental || !enableSyncDefaultUpdates
851 + it('should report the expected times when a high-pri update replaces a mount in-progress', () => {
852 + const callback = jest.fn();
853
871 - Scheduler.unstable_advanceTime(5); // 0 -> 5
854 + const Yield = ({renderTime}) => {
855 + Scheduler.unstable_advanceTime(renderTime);
856 + Scheduler.unstable_yieldValue('Yield:' + renderTime);
857 + return null;
858 + };
859
873 - // Render partially, but don't finish.
874 - // This partial render should take 5ms of simulated time.
875 - if (gate(flags => flags.enableSyncDefaultUpdates)) {
876 - React.unstable_startTransition(() => {
877 - ReactTestRenderer.create(
878 - <React.Profiler id="outer" onRender={callback}>
879 - <Yield renderTime={5} />
880 - <Yield renderTime={10} />
881 - <React.Profiler id="inner" onRender={callback}>
882 - <Yield renderTime={17} />
883 - </React.Profiler>
884 - </React.Profiler>,
885 - {unstable_isConcurrent: true},
886 - );
887 - });
888 - } else {
889 - ReactTestRenderer.create(
890 - <React.Profiler id="outer" onRender={callback}>
891 - <Yield renderTime={5} />
892 - <Yield renderTime={10} />
893 - <React.Profiler id="inner" onRender={callback}>
894 - <Yield renderTime={17} />
895 - </React.Profiler>
896 - </React.Profiler>,
897 - {unstable_isConcurrent: true},
898 - );
899 - }
900 - expect(Scheduler).toFlushAndYieldThrough(['Yield:5']);
901 - expect(callback).toHaveBeenCalledTimes(0);
860 + Scheduler.unstable_advanceTime(5); // 0 -> 5
861 +
862 + // Render a partially update, but don't finish.
863 + // This partial render should take 10ms of simulated time.
864 + let renderer;
865 + if (gate(flags => flags.enableSyncDefaultUpdates)) {
866 + React.unstable_startTransition(() => {
867 + renderer = ReactTestRenderer.create(
868 + <React.Profiler id="test" onRender={callback}>
869 + <Yield renderTime={10} />
870 + <Yield renderTime={20} />
871 + </React.Profiler>,
872 + {unstable_isConcurrent: true},
873 + );
874 + });
875 + } else {
876 + renderer = ReactTestRenderer.create(
877 + <React.Profiler id="test" onRender={callback}>
878 + <Yield renderTime={10} />
879 + <Yield renderTime={20} />
880 + </React.Profiler>,
881 + {unstable_isConcurrent: true},
882 + );
883 + }
884 + expect(Scheduler).toFlushAndYieldThrough(['Yield:10']);
885 + expect(callback).toHaveBeenCalledTimes(0);
886
903 - // Simulate time moving forward while frame is paused.
904 - Scheduler.unstable_advanceTime(50); // 10 -> 60
887 + // Simulate time moving forward while frame is paused.
888 + Scheduler.unstable_advanceTime(100); // 15 -> 115
889
906 - // Flush the remaining work,
907 - // Which should take an additional 10ms of simulated time.
908 - expect(Scheduler).toFlushAndYield(['Yield:10', 'Yield:17']);
909 - expect(callback).toHaveBeenCalledTimes(2);
890 + // Interrupt with higher priority work.
891 + // The interrupted work simulates an additional 5ms of time.
892 + renderer.unstable_flushSync(() => {
893 + renderer.update(
894 + <React.Profiler id="test" onRender={callback}>
895 + <Yield renderTime={5} />
896 + </React.Profiler>,
897 + );
898 + });
899 + expect(Scheduler).toHaveYielded(['Yield:5']);
900 +
901 + // The initial work was thrown away in this case,
902 + // So the actual and base times should only include the final rendered tree times.
903 + expect(callback).toHaveBeenCalledTimes(1);
904 + const call = callback.mock.calls[0];
905 + expect(call[2]).toBe(5); // actual time
906 + expect(call[3]).toBe(5); // base time
907 + expect(call[4]).toBe(115); // start time
908 + expect(call[5]).toBe(120); // commit time
909 +
910 + callback.mockReset();
911 +
912 + // Verify no more unexpected callbacks from low priority work
913 + expect(Scheduler).toFlushWithoutYielding();
914 + expect(callback).toHaveBeenCalledTimes(0);
915 + });
916 +
917 + // @gate experimental || !enableSyncDefaultUpdates
918 + it('should report the expected times when a high-priority update replaces a low-priority update', () => {
919 + const callback = jest.fn();
920 +
921 + const Yield = ({renderTime}) => {
922 + Scheduler.unstable_advanceTime(renderTime);
923 + Scheduler.unstable_yieldValue('Yield:' + renderTime);
924 + return null;
925 + };
926 +
927 + Scheduler.unstable_advanceTime(5); // 0 -> 5
928 +
929 + const renderer = ReactTestRenderer.create(
930 + <React.Profiler id="test" onRender={callback}>
931 + <Yield renderTime={6} />
932 + <Yield renderTime={15} />
933 + </React.Profiler>,
934 + {unstable_isConcurrent: true},
935 + );
936 +
937 + // Render everything initially.
938 + // This should take 21 seconds of actual and base time.
939 + expect(Scheduler).toFlushAndYield(['Yield:6', 'Yield:15']);
940 + expect(callback).toHaveBeenCalledTimes(1);
941 + let call = callback.mock.calls[0];
942 + expect(call[2]).toBe(21); // actual time
943 + expect(call[3]).toBe(21); // base time
944 + expect(call[4]).toBe(5); // start time
945 + expect(call[5]).toBe(26); // commit time
946 +
947 + callback.mockReset();
948
911 - const [innerCall, outerCall] = callback.mock.calls;
912 -
913 - // Verify that the actual time includes all work times,
914 - // But not the time that elapsed between frames.
915 - expect(innerCall[0]).toBe('inner');
916 - expect(innerCall[2]).toBe(17); // actual time
917 - expect(innerCall[3]).toBe(17); // base time
918 - expect(innerCall[4]).toBe(70); // start time
919 - expect(innerCall[5]).toBe(87); // commit time
920 - expect(outerCall[0]).toBe('outer');
921 - expect(outerCall[2]).toBe(32); // actual time
922 - expect(outerCall[3]).toBe(32); // base time
923 - expect(outerCall[4]).toBe(5); // start time
924 - expect(outerCall[5]).toBe(87); // commit time
949 + Scheduler.unstable_advanceTime(30); // 26 -> 56
950 +
951 + // Render a partially update, but don't finish.
952 + // This partial render should take 3ms of simulated time.
953 + if (gate(flags => flags.enableSyncDefaultUpdates)) {
954 + React.unstable_startTransition(() => {
955 + renderer.update(
956 + <React.Profiler id="test" onRender={callback}>
957 + <Yield renderTime={3} />
958 + <Yield renderTime={5} />
959 + <Yield renderTime={9} />
960 + </React.Profiler>,
961 + );
962 });
963 + } else {
964 + renderer.update(
965 + <React.Profiler id="test" onRender={callback}>
966 + <Yield renderTime={3} />
967 + <Yield renderTime={5} />
968 + <Yield renderTime={9} />
969 + </React.Profiler>,
970 + );
971 + }
972 + expect(Scheduler).toFlushAndYieldThrough(['Yield:3']);
973 + expect(callback).toHaveBeenCalledTimes(0);
974
927 - // @gate experimental || !enableSyncDefaultUpdates
928 - it('should report the expected times when a high-pri update replaces a mount in-progress', () => {
929 - const callback = jest.fn();
975 + // Simulate time moving forward while frame is paused.
976 + Scheduler.unstable_advanceTime(100); // 59 -> 159
977
931 - const Yield = ({renderTime}) => {
932 - Scheduler.unstable_advanceTime(renderTime);
933 - Scheduler.unstable_yieldValue('Yield:' + renderTime);
934 - return null;
935 - };
978 + // Render another 5ms of simulated time.
979 + expect(Scheduler).toFlushAndYieldThrough(['Yield:5']);
980 + expect(callback).toHaveBeenCalledTimes(0);
981
937 - Scheduler.unstable_advanceTime(5); // 0 -> 5
982 + // Simulate time moving forward while frame is paused.
983 + Scheduler.unstable_advanceTime(100); // 164 -> 264
984
939 - // Render a partially update, but don't finish.
940 - // This partial render should take 10ms of simulated time.
941 - let renderer;
942 - if (gate(flags => flags.enableSyncDefaultUpdates)) {
943 - React.unstable_startTransition(() => {
944 - renderer = ReactTestRenderer.create(
945 - <React.Profiler id="test" onRender={callback}>
946 - <Yield renderTime={10} />
947 - <Yield renderTime={20} />
948 - </React.Profiler>,
949 - {unstable_isConcurrent: true},
950 - );
951 - });
952 - } else {
953 - renderer = ReactTestRenderer.create(
954 - <React.Profiler id="test" onRender={callback}>
955 - <Yield renderTime={10} />
956 - <Yield renderTime={20} />
957 - </React.Profiler>,
958 - {unstable_isConcurrent: true},
959 - );
960 - }
961 - expect(Scheduler).toFlushAndYieldThrough(['Yield:10']);
962 - expect(callback).toHaveBeenCalledTimes(0);
963 -
964 - // Simulate time moving forward while frame is paused.
965 - Scheduler.unstable_advanceTime(100); // 15 -> 115
966 -
967 - // Interrupt with higher priority work.
968 - // The interrupted work simulates an additional 5ms of time.
969 - renderer.unstable_flushSync(() => {
970 - renderer.update(
971 - <React.Profiler id="test" onRender={callback}>
972 - <Yield renderTime={5} />
973 - </React.Profiler>,
974 - );
975 - });
976 - expect(Scheduler).toHaveYielded(['Yield:5']);
985 + // Interrupt with higher priority work.
986 + // The interrupted work simulates an additional 11ms of time.
987 + renderer.unstable_flushSync(() => {
988 + renderer.update(
989 + <React.Profiler id="test" onRender={callback}>
990 + <Yield renderTime={11} />
991 + </React.Profiler>,
992 + );
993 + });
994 + expect(Scheduler).toHaveYielded(['Yield:11']);
995
978 - // The initial work was thrown away in this case,
979 - // So the actual and base times should only include the final rendered tree times.
980 - expect(callback).toHaveBeenCalledTimes(1);
981 - const call = callback.mock.calls[0];
982 - expect(call[2]).toBe(5); // actual time
983 - expect(call[3]).toBe(5); // base time
984 - expect(call[4]).toBe(115); // start time
985 - expect(call[5]).toBe(120); // commit time
996 + // The actual time should include only the most recent render,
997 + // Because this lets us avoid a lot of commit phase reset complexity.
998 + // The base time includes only the final rendered tree times.
999 + expect(callback).toHaveBeenCalledTimes(1);
1000 + call = callback.mock.calls[0];
1001 + expect(call[2]).toBe(11); // actual time
1002 + expect(call[3]).toBe(11); // base time
1003 + expect(call[4]).toBe(264); // start time
1004 + expect(call[5]).toBe(275); // commit time
1005 +
1006 + // Verify no more unexpected callbacks from low priority work
1007 + expect(Scheduler).toFlushAndYield([]);
1008 + expect(callback).toHaveBeenCalledTimes(1);
1009 + });
1010
987 - callback.mockReset();
1011 + // @gate experimental || !enableSyncDefaultUpdates
1012 + it('should report the expected times when a high-priority update interrupts a low-priority update', () => {
1013 + const callback = jest.fn();
1014
989 - // Verify no more unexpected callbacks from low priority work
990 - expect(Scheduler).toFlushWithoutYielding();
991 - expect(callback).toHaveBeenCalledTimes(0);
1015 + const Yield = ({renderTime}) => {
1016 + Scheduler.unstable_advanceTime(renderTime);
1017 + Scheduler.unstable_yieldValue('Yield:' + renderTime);
1018 + return null;
1019 + };
1020 +
1021 + let first;
1022 + class FirstComponent extends React.Component {
1023 + state = {renderTime: 1};
1024 + render() {
1025 + first = this;
1026 + Scheduler.unstable_advanceTime(this.state.renderTime);
1027 + Scheduler.unstable_yieldValue(
1028 + 'FirstComponent:' + this.state.renderTime,
1029 + );
1030 + return <Yield renderTime={4} />;
1031 + }
1032 + }
1033 + let second;
1034 + class SecondComponent extends React.Component {
1035 + state = {renderTime: 2};
1036 + render() {
1037 + second = this;
1038 + Scheduler.unstable_advanceTime(this.state.renderTime);
1039 + Scheduler.unstable_yieldValue(
1040 + 'SecondComponent:' + this.state.renderTime,
1041 + );
1042 + return <Yield renderTime={7} />;
1043 + }
1044 + }
1045 +
1046 + Scheduler.unstable_advanceTime(5); // 0 -> 5
1047 +
1048 + const renderer = ReactTestRenderer.create(
1049 + <React.Profiler id="test" onRender={callback}>
1050 + <FirstComponent />
1051 + <SecondComponent />
1052 + </React.Profiler>,
1053 + {unstable_isConcurrent: true},
1054 + );
1055 +
1056 + // Render everything initially.
1057 + // This simulates a total of 14ms of actual render time.
1058 + // The base render time is also 14ms for the initial render.
1059 + expect(Scheduler).toFlushAndYield([
1060 + 'FirstComponent:1',
1061 + 'Yield:4',
1062 + 'SecondComponent:2',
1063 + 'Yield:7',
1064 + ]);
1065 + expect(callback).toHaveBeenCalledTimes(1);
1066 + let call = callback.mock.calls[0];
1067 + expect(call[2]).toBe(14); // actual time
1068 + expect(call[3]).toBe(14); // base time
1069 + expect(call[4]).toBe(5); // start time
1070 + expect(call[5]).toBe(19); // commit time
1071 +
1072 + callback.mockClear();
1073 +
1074 + Scheduler.unstable_advanceTime(100); // 19 -> 119
1075 +
1076 + // Render a partially update, but don't finish.
1077 + // This partial render will take 10ms of actual render time.
1078 + if (gate(flags => flags.enableSyncDefaultUpdates)) {
1079 + React.unstable_startTransition(() => {
1080 + first.setState({renderTime: 10});
1081 + });
1082 + } else {
1083 + first.setState({renderTime: 10});
1084 + }
1085 + expect(Scheduler).toFlushAndYieldThrough(['FirstComponent:10']);
1086 + expect(callback).toHaveBeenCalledTimes(0);
1087 +
1088 + // Simulate time moving forward while frame is paused.
1089 + Scheduler.unstable_advanceTime(100); // 129 -> 229
1090 +
1091 + // Interrupt with higher priority work.
1092 + // This simulates a total of 37ms of actual render time.
1093 + renderer.unstable_flushSync(() => second.setState({renderTime: 30}));
1094 + expect(Scheduler).toHaveYielded(['SecondComponent:30', 'Yield:7']);
1095 +
1096 + // The actual time should include only the most recent render (37ms),
1097 + // Because this greatly simplifies the commit phase logic.
1098 + // The base time should include the more recent times for the SecondComponent subtree,
1099 + // As well as the original times for the FirstComponent subtree.
1100 + expect(callback).toHaveBeenCalledTimes(1);
1101 + call = callback.mock.calls[0];
1102 + expect(call[2]).toBe(37); // actual time
1103 + expect(call[3]).toBe(42); // base time
1104 + expect(call[4]).toBe(229); // start time
1105 + expect(call[5]).toBe(266); // commit time
1106 +
1107 + callback.mockClear();
1108 +
1109 + // Simulate time moving forward while frame is paused.
1110 + Scheduler.unstable_advanceTime(100); // 266 -> 366
1111 +
1112 + // Resume the original low priority update, with rebased state.
1113 + // This simulates a total of 14ms of actual render time,
1114 + // And does not include the original (interrupted) 10ms.
1115 + // The tree contains 42ms of base render time at this point,
1116 + // Reflecting the most recent (longer) render durations.
1117 + // TODO: This actual time should decrease by 10ms once the scheduler supports resuming.
1118 + expect(Scheduler).toFlushAndYield(['FirstComponent:10', 'Yield:4']);
1119 + expect(callback).toHaveBeenCalledTimes(1);
1120 + call = callback.mock.calls[0];
1121 + expect(call[2]).toBe(14); // actual time
1122 + expect(call[3]).toBe(51); // base time
1123 + expect(call[4]).toBe(366); // start time
1124 + expect(call[5]).toBe(380); // commit time
1125 + });
1126 +
1127 + [true, false].forEach(replayFailedUnitOfWorkWithInvokeGuardedCallback => {
1128 + describe(`replayFailedUnitOfWorkWithInvokeGuardedCallback ${
1129 + replayFailedUnitOfWorkWithInvokeGuardedCallback ? 'enabled' : 'disabled'
1130 + }`, () => {
1131 + beforeEach(() => {
1132 + jest.resetModules();
1133 +
1134 + loadModules({
1135 + replayFailedUnitOfWorkWithInvokeGuardedCallback,
1136 + });
1137 });
1138
994 - // @gate experimental || !enableSyncDefaultUpdates
995 - it('should report the expected times when a high-priority update replaces a low-priority update', () => {
1139 + it('should accumulate actual time after an error handled by componentDidCatch()', () => {
1140 const callback = jest.fn();
1141
998 - const Yield = ({renderTime}) => {
999 - Scheduler.unstable_advanceTime(renderTime);
1000 - Scheduler.unstable_yieldValue('Yield:' + renderTime);
1001 - return null;
1142 + const ThrowsError = ({unused}) => {
1143 + Scheduler.unstable_advanceTime(3);
1144 + throw Error('expected error');
1145 };
1146
1147 + class ErrorBoundary extends React.Component {
1148 + state = {error: null};
1149 + componentDidCatch(error) {
1150 + this.setState({error});
1151 + }
1152 + render() {
1153 + Scheduler.unstable_advanceTime(2);
1154 + return this.state.error === null ? (
1155 + this.props.children
1156 + ) : (
1157 + <AdvanceTime byAmount={20} />
1158 + );
1159 + }
1160 + }
1161 +
1162 Scheduler.unstable_advanceTime(5); // 0 -> 5
1163
1006 - const renderer = ReactTestRenderer.create(
1164 + ReactTestRenderer.create(
1165 <React.Profiler id="test" onRender={callback}>
1008 - <Yield renderTime={6} />
1009 - <Yield renderTime={15} />
1166 + <ErrorBoundary>
1167 + <AdvanceTime byAmount={9} />
1168 + <ThrowsError />
1169 + </ErrorBoundary>
1170 </React.Profiler>,
1011 - {unstable_isConcurrent: true},
1171 );
1172
1014 - // Render everything initially.
1015 - // This should take 21 seconds of actual and base time.
1016 - expect(Scheduler).toFlushAndYield(['Yield:6', 'Yield:15']);
1017 - expect(callback).toHaveBeenCalledTimes(1);
1018 - let call = callback.mock.calls[0];
1019 - expect(call[2]).toBe(21); // actual time
1020 - expect(call[3]).toBe(21); // base time
1021 - expect(call[4]).toBe(5); // start time
1022 - expect(call[5]).toBe(26); // commit time
1023 -
1024 - callback.mockReset();
1025 -
1026 - Scheduler.unstable_advanceTime(30); // 26 -> 56
1027 -
1028 - // Render a partially update, but don't finish.
1029 - // This partial render should take 3ms of simulated time.
1030 - if (gate(flags => flags.enableSyncDefaultUpdates)) {
1031 - React.unstable_startTransition(() => {
1032 - renderer.update(
1033 - <React.Profiler id="test" onRender={callback}>
1034 - <Yield renderTime={3} />
1035 - <Yield renderTime={5} />
1036 - <Yield renderTime={9} />
1037 - </React.Profiler>,
1038 - );
1039 - });
1040 - } else {
1041 - renderer.update(
1042 - <React.Profiler id="test" onRender={callback}>
1043 - <Yield renderTime={3} />
1044 - <Yield renderTime={5} />
1045 - <Yield renderTime={9} />
1046 - </React.Profiler>,
1047 - );
1048 - }
1049 - expect(Scheduler).toFlushAndYieldThrough(['Yield:3']);
1050 - expect(callback).toHaveBeenCalledTimes(0);
1051 -
1052 - // Simulate time moving forward while frame is paused.
1053 - Scheduler.unstable_advanceTime(100); // 59 -> 159
1054 -
1055 - // Render another 5ms of simulated time.
1056 - expect(Scheduler).toFlushAndYieldThrough(['Yield:5']);
1057 - expect(callback).toHaveBeenCalledTimes(0);
1058 -
1059 - // Simulate time moving forward while frame is paused.
1060 - Scheduler.unstable_advanceTime(100); // 164 -> 264
1061 -
1062 - // Interrupt with higher priority work.
1063 - // The interrupted work simulates an additional 11ms of time.
1064 - renderer.unstable_flushSync(() => {
1065 - renderer.update(
1066 - <React.Profiler id="test" onRender={callback}>
1067 - <Yield renderTime={11} />
1068 - </React.Profiler>,
1069 - );
1070 - });
1071 - expect(Scheduler).toHaveYielded(['Yield:11']);
1173 + expect(callback).toHaveBeenCalledTimes(2);
1174
1073 - // The actual time should include only the most recent render,
1074 - // Because this lets us avoid a lot of commit phase reset complexity.
1075 - // The base time includes only the final rendered tree times.
1076 - expect(callback).toHaveBeenCalledTimes(1);
1077 - call = callback.mock.calls[0];
1078 - expect(call[2]).toBe(11); // actual time
1079 - expect(call[3]).toBe(11); // base time
1080 - expect(call[4]).toBe(264); // start time
1081 - expect(call[5]).toBe(275); // commit time
1082 -
1083 - // Verify no more unexpected callbacks from low priority work
1084 - expect(Scheduler).toFlushAndYield([]);
1085 - expect(callback).toHaveBeenCalledTimes(1);
1175 + // Callbacks bubble (reverse order).
1176 + const [mountCall, updateCall] = callback.mock.calls;
1177 +
1178 + // The initial mount only includes the ErrorBoundary (which takes 2)
1179 + // But it spends time rendering all of the failed subtree also.
1180 + expect(mountCall[1]).toBe('mount');
1181 + // actual time includes: 2 (ErrorBoundary) + 9 (AdvanceTime) + 3 (ThrowsError)
1182 + // We don't count the time spent in replaying the failed unit of work (ThrowsError)
1183 + expect(mountCall[2]).toBe(14);
1184 + // base time includes: 2 (ErrorBoundary)
1185 + // Since the tree is empty for the initial commit
1186 + expect(mountCall[3]).toBe(2);
1187 + // start time
1188 + expect(mountCall[4]).toBe(5);
1189 + // commit time: 5 initially + 14 of work
1190 + // Add an additional 3 (ThrowsError) if we replayed the failed work
1191 + expect(mountCall[5]).toBe(
1192 + __DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback
1193 + ? 22
1194 + : 19,
1195 + );
1196 +
1197 + // The update includes the ErrorBoundary and its fallback child
1198 + expect(updateCall[1]).toBe('nested-update');
1199 + // actual time includes: 2 (ErrorBoundary) + 20 (AdvanceTime)
1200 + expect(updateCall[2]).toBe(22);
1201 + // base time includes: 2 (ErrorBoundary) + 20 (AdvanceTime)
1202 + expect(updateCall[3]).toBe(22);
1203 + // start time
1204 + expect(updateCall[4]).toBe(
1205 + __DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback
1206 + ? 22
1207 + : 19,
1208 + );
1209 + // commit time: 19 (startTime) + 2 (ErrorBoundary) + 20 (AdvanceTime)
1210 + // Add an additional 3 (ThrowsError) if we replayed the failed work
1211 + expect(updateCall[5]).toBe(
1212 + __DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback
1213 + ? 44
1214 + : 41,
1215 + );
1216 });
1217
1088 - // @gate experimental || !enableSyncDefaultUpdates
1089 - it('should report the expected times when a high-priority update interrupts a low-priority update', () => {
1218 + it('should accumulate actual time after an error handled by getDerivedStateFromError()', () => {
1219 const callback = jest.fn();
1220
1092 - const Yield = ({renderTime}) => {
1093 - Scheduler.unstable_advanceTime(renderTime);
1094 - Scheduler.unstable_yieldValue('Yield:' + renderTime);
1095 - return null;
1221 + const ThrowsError = ({unused}) => {
1222 + Scheduler.unstable_advanceTime(10);
1223 + throw Error('expected error');
1224 };
1225
1098 - let first;
1099 - class FirstComponent extends React.Component {
1100 - state = {renderTime: 1};
1101 - render() {
1102 - first = this;
1103 - Scheduler.unstable_advanceTime(this.state.renderTime);
1104 - Scheduler.unstable_yieldValue(
1105 - 'FirstComponent:' + this.state.renderTime,
1106 - );
1107 - return <Yield renderTime={4} />;
1226 + class ErrorBoundary extends React.Component {
1227 + state = {error: null};
1228 + static getDerivedStateFromError(error) {
1229 + return {error};
1230 }
1109 - }
1110 - let second;
1111 - class SecondComponent extends React.Component {
1112 - state = {renderTime: 2};
1231 render() {
1114 - second = this;
1115 - Scheduler.unstable_advanceTime(this.state.renderTime);
1116 - Scheduler.unstable_yieldValue(
1117 - 'SecondComponent:' + this.state.renderTime,
1232 + Scheduler.unstable_advanceTime(2);
1233 + return this.state.error === null ? (
1234 + this.props.children
1235 + ) : (
1236 + <AdvanceTime byAmount={20} />
1237 );
1119 - return <Yield renderTime={7} />;
1238 }
1239 }
1240
1241 Scheduler.unstable_advanceTime(5); // 0 -> 5
1242
1125 - const renderer = ReactTestRenderer.create(
1243 + ReactTestRenderer.create(
1244 <React.Profiler id="test" onRender={callback}>
1127 - <FirstComponent />
1128 - <SecondComponent />
1245 + <ErrorBoundary>
1246 + <AdvanceTime byAmount={5} />
1247 + <ThrowsError />
1248 + </ErrorBoundary>
1249 </React.Profiler>,
1130 - {unstable_isConcurrent: true},
1250 );
1251
1133 - // Render everything initially.
1134 - // This simulates a total of 14ms of actual render time.
1135 - // The base render time is also 14ms for the initial render.
1136 - expect(Scheduler).toFlushAndYield([
1137 - 'FirstComponent:1',
1138 - 'Yield:4',
1139 - 'SecondComponent:2',
1140 - 'Yield:7',
1141 - ]);
1252 expect(callback).toHaveBeenCalledTimes(1);
1143 - let call = callback.mock.calls[0];
1144 - expect(call[2]).toBe(14); // actual time
1145 - expect(call[3]).toBe(14); // base time
1146 - expect(call[4]).toBe(5); // start time
1147 - expect(call[5]).toBe(19); // commit time
1148 -
1149 - callback.mockClear();
1150 -
1151 - Scheduler.unstable_advanceTime(100); // 19 -> 119
1152 -
1153 - // Render a partially update, but don't finish.
1154 - // This partial render will take 10ms of actual render time.
1155 - if (gate(flags => flags.enableSyncDefaultUpdates)) {
1156 - React.unstable_startTransition(() => {
1157 - first.setState({renderTime: 10});
1158 - });
1159 - } else {
1160 - first.setState({renderTime: 10});
1161 - }
1162 - expect(Scheduler).toFlushAndYieldThrough(['FirstComponent:10']);
1163 - expect(callback).toHaveBeenCalledTimes(0);
1253
1165 - // Simulate time moving forward while frame is paused.
1166 - Scheduler.unstable_advanceTime(100); // 129 -> 229
1254 + // Callbacks bubble (reverse order).
1255 + const [mountCall] = callback.mock.calls;
1256 +
1257 + // The initial mount includes the ErrorBoundary's error state,
1258 + // But it also spends actual time rendering UI that fails and isn't included.
1259 + expect(mountCall[1]).toBe('mount');
1260 + // actual time includes: 2 (ErrorBoundary) + 5 (AdvanceTime) + 10 (ThrowsError)
1261 + // Then the re-render: 2 (ErrorBoundary) + 20 (AdvanceTime)
1262 + // We don't count the time spent in replaying the failed unit of work (ThrowsError)
1263 + expect(mountCall[2]).toBe(39);
1264 + // base time includes: 2 (ErrorBoundary) + 20 (AdvanceTime)
1265 + expect(mountCall[3]).toBe(22);
1266 + // start time
1267 + expect(mountCall[4]).toBe(5);
1268 + // commit time
1269 + expect(mountCall[5]).toBe(
1270 + __DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback
1271 + ? 54
1272 + : 44,
1273 + );
1274 + });
1275
1168 - // Interrupt with higher priority work.
1169 - // This simulates a total of 37ms of actual render time.
1170 - renderer.unstable_flushSync(() => second.setState({renderTime: 30}));
1171 - expect(Scheduler).toHaveYielded(['SecondComponent:30', 'Yield:7']);
1276 + it('should reset the fiber stack correct after a "complete" phase error', () => {
1277 + jest.resetModules();
1278
1173 - // The actual time should include only the most recent render (37ms),
1174 - // Because this greatly simplifies the commit phase logic.
1175 - // The base time should include the more recent times for the SecondComponent subtree,
1176 - // As well as the original times for the FirstComponent subtree.
1177 - expect(callback).toHaveBeenCalledTimes(1);
1178 - call = callback.mock.calls[0];
1179 - expect(call[2]).toBe(37); // actual time
1180 - expect(call[3]).toBe(42); // base time
1181 - expect(call[4]).toBe(229); // start time
1182 - expect(call[5]).toBe(266); // commit time
1183 -
1184 - callback.mockClear();
1185 -
1186 - // Simulate time moving forward while frame is paused.
1187 - Scheduler.unstable_advanceTime(100); // 266 -> 366
1188 -
1189 - // Resume the original low priority update, with rebased state.
1190 - // This simulates a total of 14ms of actual render time,
1191 - // And does not include the original (interrupted) 10ms.
1192 - // The tree contains 42ms of base render time at this point,
1193 - // Reflecting the most recent (longer) render durations.
1194 - // TODO: This actual time should decrease by 10ms once the scheduler supports resuming.
1195 - expect(Scheduler).toFlushAndYield(['FirstComponent:10', 'Yield:4']);
1196 - expect(callback).toHaveBeenCalledTimes(1);
1197 - call = callback.mock.calls[0];
1198 - expect(call[2]).toBe(14); // actual time
1199 - expect(call[3]).toBe(51); // base time
1200 - expect(call[4]).toBe(366); // start time
1201 - expect(call[5]).toBe(380); // commit time
1202 - });
1279 + loadModules({
1280 + useNoopRenderer: true,
1281 + replayFailedUnitOfWorkWithInvokeGuardedCallback,
1282 + });
1283
1204 - [true, false].forEach(
1205 - replayFailedUnitOfWorkWithInvokeGuardedCallback => {
1206 - describe(`replayFailedUnitOfWorkWithInvokeGuardedCallback ${
1207 - replayFailedUnitOfWorkWithInvokeGuardedCallback
1208 - ? 'enabled'
1209 - : 'disabled'
1210 - }`, () => {
1211 - beforeEach(() => {
1212 - jest.resetModules();
1213 -
1214 - loadModules({
1215 - replayFailedUnitOfWorkWithInvokeGuardedCallback,
1216 - });
1217 - });
1218 -
1219 - it('should accumulate actual time after an error handled by componentDidCatch()', () => {
1220 - const callback = jest.fn();
1221 -
1222 - const ThrowsError = ({unused}) => {
1223 - Scheduler.unstable_advanceTime(3);
1224 - throw Error('expected error');
1225 - };
1226 -
1227 - class ErrorBoundary extends React.Component {
1228 - state = {error: null};
1229 - componentDidCatch(error) {
1230 - this.setState({error});
1231 - }
1232 - render() {
1233 - Scheduler.unstable_advanceTime(2);
1234 - return this.state.error === null ? (
1235 - this.props.children
1236 - ) : (
1237 - <AdvanceTime byAmount={20} />
1238 - );
1239 - }
1240 - }
1241 -
1242 - Scheduler.unstable_advanceTime(5); // 0 -> 5
1243 -
1244 - ReactTestRenderer.create(
1245 - <React.Profiler id="test" onRender={callback}>
1246 - <ErrorBoundary>
1247 - <AdvanceTime byAmount={9} />
1248 - <ThrowsError />
1249 - </ErrorBoundary>
1250 - </React.Profiler>,
1251 - );
1252 -
1253 - expect(callback).toHaveBeenCalledTimes(2);
1254 -
1255 - // Callbacks bubble (reverse order).
1256 - const [mountCall, updateCall] = callback.mock.calls;
1257 -
1258 - // The initial mount only includes the ErrorBoundary (which takes 2)
1259 - // But it spends time rendering all of the failed subtree also.
1260 - expect(mountCall[1]).toBe('mount');
1261 - // actual time includes: 2 (ErrorBoundary) + 9 (AdvanceTime) + 3 (ThrowsError)
1262 - // We don't count the time spent in replaying the failed unit of work (ThrowsError)
1263 - expect(mountCall[2]).toBe(14);
1264 - // base time includes: 2 (ErrorBoundary)
1265 - // Since the tree is empty for the initial commit
1266 - expect(mountCall[3]).toBe(2);
1267 - // start time
1268 - expect(mountCall[4]).toBe(5);
1269 - // commit time: 5 initially + 14 of work
1270 - // Add an additional 3 (ThrowsError) if we replayed the failed work
1271 - expect(mountCall[5]).toBe(
1272 - __DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback
1273 - ? 22
1274 - : 19,
1275 - );
1276 -
1277 - // The update includes the ErrorBoundary and its fallback child
1278 - expect(updateCall[1]).toBe('nested-update');
1279 - // actual time includes: 2 (ErrorBoundary) + 20 (AdvanceTime)
1280 - expect(updateCall[2]).toBe(22);
1281 - // base time includes: 2 (ErrorBoundary) + 20 (AdvanceTime)
1282 - expect(updateCall[3]).toBe(22);
1283 - // start time
1284 - expect(updateCall[4]).toBe(
1285 - __DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback
1286 - ? 22
1287 - : 19,
1288 - );
1289 - // commit time: 19 (startTime) + 2 (ErrorBoundary) + 20 (AdvanceTime)
1290 - // Add an additional 3 (ThrowsError) if we replayed the failed work
1291 - expect(updateCall[5]).toBe(
1292 - __DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback
1293 - ? 44
1294 - : 41,
1295 - );
1296 - });
1297 -
1298 - it('should accumulate actual time after an error handled by getDerivedStateFromError()', () => {
1299 - const callback = jest.fn();
1300 -
1301 - const ThrowsError = ({unused}) => {
1302 - Scheduler.unstable_advanceTime(10);
1303 - throw Error('expected error');
1304 - };
1305 -
1306 - class ErrorBoundary extends React.Component {
1307 - state = {error: null};
1308 - static getDerivedStateFromError(error) {
1309 - return {error};
1310 - }
1311 - render() {
1312 - Scheduler.unstable_advanceTime(2);
1313 - return this.state.error === null ? (
1314 - this.props.children
1315 - ) : (
1316 - <AdvanceTime byAmount={20} />
1317 - );
1318 - }
1319 - }
1320 -
1321 - Scheduler.unstable_advanceTime(5); // 0 -> 5
1322 -
1323 - ReactTestRenderer.create(
1324 - <React.Profiler id="test" onRender={callback}>
1325 - <ErrorBoundary>
1326 - <AdvanceTime byAmount={5} />
1327 - <ThrowsError />
1328 - </ErrorBoundary>
1329 - </React.Profiler>,
1330 - );
1331 -
1332 - expect(callback).toHaveBeenCalledTimes(1);
1333 -
1334 - // Callbacks bubble (reverse order).
1335 - const [mountCall] = callback.mock.calls;
1336 -
1337 - // The initial mount includes the ErrorBoundary's error state,
1338 - // But it also spends actual time rendering UI that fails and isn't included.
1339 - expect(mountCall[1]).toBe('mount');
1340 - // actual time includes: 2 (ErrorBoundary) + 5 (AdvanceTime) + 10 (ThrowsError)
1341 - // Then the re-render: 2 (ErrorBoundary) + 20 (AdvanceTime)
1342 - // We don't count the time spent in replaying the failed unit of work (ThrowsError)
1343 - expect(mountCall[2]).toBe(39);
1344 - // base time includes: 2 (ErrorBoundary) + 20 (AdvanceTime)
1345 - expect(mountCall[3]).toBe(22);
1346 - // start time
1347 - expect(mountCall[4]).toBe(5);
1348 - // commit time
1349 - expect(mountCall[5]).toBe(
1350 - __DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback
1351 - ? 54
1352 - : 44,
1353 - );
1354 - });
1355 -
1356 - it('should reset the fiber stack correct after a "complete" phase error', () => {
1357 - jest.resetModules();
1358 -
1359 - loadModules({
1360 - useNoopRenderer: true,
1361 - replayFailedUnitOfWorkWithInvokeGuardedCallback,
1362 - });
1363 -
1364 - // Simulate a renderer error during the "complete" phase.
1365 - // This mimics behavior like React Native's View/Text nesting validation.
1366 - ReactNoop.render(
1367 - <React.Profiler id="profiler" onRender={jest.fn()}>
1368 - <errorInCompletePhase>hi</errorInCompletePhase>
1369 - </React.Profiler>,
1370 - );
1371 - expect(Scheduler).toFlushAndThrow('Error in host config.');
1372 -
1373 - // A similar case we've seen caused by an invariant in ReactDOM.
1374 - // It didn't reproduce without a host component inside.
1375 - ReactNoop.render(
1376 - <React.Profiler id="profiler" onRender={jest.fn()}>
1377 - <errorInCompletePhase>
1378 - <span>hi</span>
1379 - </errorInCompletePhase>
1380 - </React.Profiler>,
1381 - );
1382 - expect(Scheduler).toFlushAndThrow('Error in host config.');
1383 -
1384 - // So long as the profiler timer's fiber stack is reset correctly,
1385 - // Subsequent renders should not error.
1386 - ReactNoop.render(
1387 - <React.Profiler id="profiler" onRender={jest.fn()}>
1388 - <span>hi</span>
1389 - </React.Profiler>,
1390 - );
1391 - expect(Scheduler).toFlushWithoutYielding();
1392 - });
1393 - });
1394 - },
1395 - );
1396 - });
1284 + // Simulate a renderer error during the "complete" phase.
1285 + // This mimics behavior like React Native's View/Text nesting validation.
1286 + ReactNoop.render(
1287 + <React.Profiler id="profiler" onRender={jest.fn()}>
1288 + <errorInCompletePhase>hi</errorInCompletePhase>
1289 + </React.Profiler>,
1290 + );
1291 + expect(Scheduler).toFlushAndThrow('Error in host config.');
1292
1398 - it('reflects the most recently rendered id value', () => {
1399 - const callback = jest.fn();
1293 + // A similar case we've seen caused by an invariant in ReactDOM.
1294 + // It didn't reproduce without a host component inside.
1295 + ReactNoop.render(
1296 + <React.Profiler id="profiler" onRender={jest.fn()}>
1297 + <errorInCompletePhase>
1298 + <span>hi</span>
1299 + </errorInCompletePhase>
1300 + </React.Profiler>,
1301 + );
1302 + expect(Scheduler).toFlushAndThrow('Error in host config.');
1303
1401 - Scheduler.unstable_advanceTime(5); // 0 -> 5
1304 + // So long as the profiler timer's fiber stack is reset correctly,
1305 + // Subsequent renders should not error.
1306 + ReactNoop.render(
1307 + <React.Profiler id="profiler" onRender={jest.fn()}>
1308 + <span>hi</span>
1309 + </React.Profiler>,
1310 + );
1311 + expect(Scheduler).toFlushWithoutYielding();
1312 + });
1313 + });
1314 + });
1315 + });
1316
1403 - const renderer = ReactTestRenderer.create(
1404 - <React.Profiler id="one" onRender={callback}>
1405 - <AdvanceTime byAmount={2} />
1406 - </React.Profiler>,
1407 - );
1317 + it('reflects the most recently rendered id value', () => {
1318 + const callback = jest.fn();
1319
1409 - expect(callback).toHaveBeenCalledTimes(1);
1320 + Scheduler.unstable_advanceTime(5); // 0 -> 5
1321
1411 - Scheduler.unstable_advanceTime(20); // 7 -> 27
1322 + const renderer = ReactTestRenderer.create(
1323 + <React.Profiler id="one" onRender={callback}>
1324 + <AdvanceTime byAmount={2} />
1325 + </React.Profiler>,
1326 + );
1327
1413 - renderer.update(
1414 - <React.Profiler id="two" onRender={callback}>
1415 - <AdvanceTime byAmount={1} />
1416 - </React.Profiler>,
1417 - );
1328 + expect(callback).toHaveBeenCalledTimes(1);
1329
1419 - expect(callback).toHaveBeenCalledTimes(2);
1330 + Scheduler.unstable_advanceTime(20); // 7 -> 27
1331
1421 - const [mountCall, updateCall] = callback.mock.calls;
1332 + renderer.update(
1333 + <React.Profiler id="two" onRender={callback}>
1334 + <AdvanceTime byAmount={1} />
1335 + </React.Profiler>,
1336 + );
1337
1423 - expect(mountCall[0]).toBe('one');
1424 - expect(mountCall[1]).toBe('mount');
1425 - expect(mountCall[2]).toBe(2); // actual time
1426 - expect(mountCall[3]).toBe(2); // base time
1427 - expect(mountCall[4]).toBe(5); // start time
1338 + expect(callback).toHaveBeenCalledTimes(2);
1339
1429 - expect(updateCall[0]).toBe('two');
1430 - expect(updateCall[1]).toBe('update');
1431 - expect(updateCall[2]).toBe(1); // actual time
1432 - expect(updateCall[3]).toBe(1); // base time
1433 - expect(updateCall[4]).toBe(27); // start time
1434 - });
1340 + const [mountCall, updateCall] = callback.mock.calls;
1341
1436 - it('should not be called until after mutations', () => {
1437 - let classComponentMounted = false;
1438 - const callback = jest.fn(
1439 - (id, phase, actualDuration, baseDuration, startTime, commitTime) => {
1440 - // Don't call this hook until after mutations
1441 - expect(classComponentMounted).toBe(true);
1442 - // But the commit time should reflect pre-mutation
1443 - expect(commitTime).toBe(2);
1444 - },
1445 - );
1342 + expect(mountCall[0]).toBe('one');
1343 + expect(mountCall[1]).toBe('mount');
1344 + expect(mountCall[2]).toBe(2); // actual time
1345 + expect(mountCall[3]).toBe(2); // base time
1346 + expect(mountCall[4]).toBe(5); // start time
1347
1447 - class ClassComponent extends React.Component {
1448 - componentDidMount() {
1449 - Scheduler.unstable_advanceTime(5);
1450 - classComponentMounted = true;
1451 - }
1452 - render() {
1453 - Scheduler.unstable_advanceTime(2);
1454 - return null;
1455 - }
1456 - }
1348 + expect(updateCall[0]).toBe('two');
1349 + expect(updateCall[1]).toBe('update');
1350 + expect(updateCall[2]).toBe(1); // actual time
1351 + expect(updateCall[3]).toBe(1); // base time
1352 + expect(updateCall[4]).toBe(27); // start time
1353 + });
1354
1458 - ReactTestRenderer.create(
1459 - <React.Profiler id="test" onRender={callback}>
1460 - <ClassComponent />
1461 - </React.Profiler>,
1462 - );
1355 + it('should not be called until after mutations', () => {
1356 + let classComponentMounted = false;
1357 + const callback = jest.fn(
1358 + (id, phase, actualDuration, baseDuration, startTime, commitTime) => {
1359 + // Don't call this hook until after mutations
1360 + expect(classComponentMounted).toBe(true);
1361 + // But the commit time should reflect pre-mutation
1362 + expect(commitTime).toBe(2);
1363 + },
1364 + );
1365 +
1366 + class ClassComponent extends React.Component {
1367 + componentDidMount() {
1368 + Scheduler.unstable_advanceTime(5);
1369 + classComponentMounted = true;
1370 + }
1371 + render() {
1372 + Scheduler.unstable_advanceTime(2);
1373 + return null;
1374 + }
1375 + }
1376
1464 - expect(callback).toHaveBeenCalledTimes(1);
1465 - });
1466 - });
1377 + ReactTestRenderer.create(
1378 + <React.Profiler id="test" onRender={callback}>
1379 + <ClassComponent />
1380 + </React.Profiler>,
1381 + );
1382 +
1383 + expect(callback).toHaveBeenCalledTimes(1);
1384 + });
1385 +});
1386
1468 - describe(`onCommit enableSchedulerTracing:${
1469 - enableSchedulerTracing ? 'enabled' : 'disabled'
1470 - }`, () => {
1471 - beforeEach(() => {
1472 - jest.resetModules();
1387 +describe(`onCommit`, () => {
1388 + beforeEach(() => {
1389 + jest.resetModules();
1390
1474 - loadModules({
1475 - enableSchedulerTracing,
1476 - });
1477 - });
1391 + loadModules();
1392 + });
1393
1479 - it('should report time spent in layout effects and commit lifecycles', () => {
1480 - const callback = jest.fn();
1394 + it('should report time spent in layout effects and commit lifecycles', () => {
1395 + const callback = jest.fn();
1396
1482 - const ComponentWithEffects = () => {
1483 - React.useLayoutEffect(() => {
1484 - Scheduler.unstable_advanceTime(10);
1485 - return () => {
1486 - Scheduler.unstable_advanceTime(100);
1487 - };
1488 - }, []);
1489 - React.useLayoutEffect(() => {
1490 - Scheduler.unstable_advanceTime(1000);
1491 - return () => {
1492 - Scheduler.unstable_advanceTime(10000);
1493 - };
1494 - });
1495 - React.useEffect(() => {
1496 - // This passive effect is here to verify that its time isn't reported.
1497 - Scheduler.unstable_advanceTime(5);
1498 - return () => {
1499 - Scheduler.unstable_advanceTime(7);
1500 - };
1501 - });
1502 - return null;
1397 + const ComponentWithEffects = () => {
1398 + React.useLayoutEffect(() => {
1399 + Scheduler.unstable_advanceTime(10);
1400 + return () => {
1401 + Scheduler.unstable_advanceTime(100);
1402 + };
1403 + }, []);
1404 + React.useLayoutEffect(() => {
1405 + Scheduler.unstable_advanceTime(1000);
1406 + return () => {
1407 + Scheduler.unstable_advanceTime(10000);
1408 };
1409 + });
1410 + React.useEffect(() => {
1411 + // This passive effect is here to verify that its time isn't reported.
1412 + Scheduler.unstable_advanceTime(5);
1413 + return () => {
1414 + Scheduler.unstable_advanceTime(7);
1415 + };
1416 + });
1417 + return null;
1418 + };
1419
1505 - class ComponentWithCommitHooks extends React.Component {
1506 - componentDidMount() {
1507 - Scheduler.unstable_advanceTime(100000);
1508 - }
1509 - componentDidUpdate() {
1510 - Scheduler.unstable_advanceTime(1000000);
1511 - }
1512 - render() {
1513 - return null;
1514 - }
1515 - }
1420 + class ComponentWithCommitHooks extends React.Component {
1421 + componentDidMount() {
1422 + Scheduler.unstable_advanceTime(100000);
1423 + }
1424 + componentDidUpdate() {
1425 + Scheduler.unstable_advanceTime(1000000);
1426 + }
1427 + render() {
1428 + return null;
1429 + }
1430 + }
1431
1517 - Scheduler.unstable_advanceTime(1);
1432 + Scheduler.unstable_advanceTime(1);
1433
1519 - const renderer = ReactTestRenderer.create(
1520 - <React.Profiler id="mount-test" onCommit={callback}>
1521 - <ComponentWithEffects />
1522 - <ComponentWithCommitHooks />
1523 - </React.Profiler>,
1524 - );
1434 + const renderer = ReactTestRenderer.create(
1435 + <React.Profiler id="mount-test" onCommit={callback}>
1436 + <ComponentWithEffects />
1437 + <ComponentWithCommitHooks />
1438 + </React.Profiler>,
1439 + );
1440
1526 - expect(callback).toHaveBeenCalledTimes(1);
1441 + expect(callback).toHaveBeenCalledTimes(1);
1442
1528 - let call = callback.mock.calls[0];
1443 + let call = callback.mock.calls[0];
1444
1530 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
1531 - expect(call[0]).toBe('mount-test');
1532 - expect(call[1]).toBe('mount');
1533 - expect(call[2]).toBe(101010); // durations
1534 - expect(call[3]).toBe(1); // commit start time (before mutations or effects)
1535 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
1445 + expect(call).toHaveLength(4);
1446 + expect(call[0]).toBe('mount-test');
1447 + expect(call[1]).toBe('mount');
1448 + expect(call[2]).toBe(101010); // durations
1449 + expect(call[3]).toBe(1); // commit start time (before mutations or effects)
1450
1537 - Scheduler.unstable_advanceTime(1);
1451 + Scheduler.unstable_advanceTime(1);
1452
1539 - renderer.update(
1540 - <React.Profiler id="update-test" onCommit={callback}>
1541 - <ComponentWithEffects />
1542 - <ComponentWithCommitHooks />
1543 - </React.Profiler>,
1544 - );
1453 + renderer.update(
1454 + <React.Profiler id="update-test" onCommit={callback}>
1455 + <ComponentWithEffects />
1456 + <ComponentWithCommitHooks />
1457 + </React.Profiler>,
1458 + );
1459
1546 - expect(callback).toHaveBeenCalledTimes(2);
1460 + expect(callback).toHaveBeenCalledTimes(2);
1461
1548 - call = callback.mock.calls[1];
1462 + call = callback.mock.calls[1];
1463
1550 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
1551 - expect(call[0]).toBe('update-test');
1552 - expect(call[1]).toBe('update');
1553 - expect(call[2]).toBe(1011000); // durations
1554 - expect(call[3]).toBe(101017); // commit start time (before mutations or effects)
1555 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
1464 + expect(call).toHaveLength(4);
1465 + expect(call[0]).toBe('update-test');
1466 + expect(call[1]).toBe('update');
1467 + expect(call[2]).toBe(1011000); // durations
1468 + expect(call[3]).toBe(101017); // commit start time (before mutations or effects)
1469
1557 - Scheduler.unstable_advanceTime(1);
1470 + Scheduler.unstable_advanceTime(1);
1471
1559 - renderer.update(
1560 - <React.Profiler id="unmount-test" onCommit={callback} />,
1561 - );
1472 + renderer.update(<React.Profiler id="unmount-test" onCommit={callback} />);
1473
1563 - expect(callback).toHaveBeenCalledTimes(3);
1474 + expect(callback).toHaveBeenCalledTimes(3);
1475
1565 - call = callback.mock.calls[2];
1476 + call = callback.mock.calls[2];
1477
1567 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
1568 - expect(call[0]).toBe('unmount-test');
1569 - expect(call[1]).toBe('update');
1570 - expect(call[2]).toBe(10100); // durations
1571 - expect(call[3]).toBe(1112030); // commit start time (before mutations or effects)
1572 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
1573 - });
1478 + expect(call).toHaveLength(4);
1479 + expect(call[0]).toBe('unmount-test');
1480 + expect(call[1]).toBe('update');
1481 + expect(call[2]).toBe(10100); // durations
1482 + expect(call[3]).toBe(1112030); // commit start time (before mutations or effects)
1483 + });
1484
1575 - it('should report time spent in layout effects and commit lifecycles with cascading renders', () => {
1576 - const callback = jest.fn();
1485 + it('should report time spent in layout effects and commit lifecycles with cascading renders', () => {
1486 + const callback = jest.fn();
1487
1578 - const ComponentWithEffects = ({shouldCascade}) => {
1579 - const [didCascade, setDidCascade] = React.useState(false);
1580 - Scheduler.unstable_advanceTime(100000000);
1581 - React.useLayoutEffect(() => {
1582 - if (shouldCascade && !didCascade) {
1583 - setDidCascade(true);
1584 - }
1585 - Scheduler.unstable_advanceTime(didCascade ? 30 : 10);
1586 - return () => {
1587 - Scheduler.unstable_advanceTime(100);
1588 - };
1589 - }, [didCascade, shouldCascade]);
1590 - return null;
1488 + const ComponentWithEffects = ({shouldCascade}) => {
1489 + const [didCascade, setDidCascade] = React.useState(false);
1490 + Scheduler.unstable_advanceTime(100000000);
1491 + React.useLayoutEffect(() => {
1492 + if (shouldCascade && !didCascade) {
1493 + setDidCascade(true);
1494 + }
1495 + Scheduler.unstable_advanceTime(didCascade ? 30 : 10);
1496 + return () => {
1497 + Scheduler.unstable_advanceTime(100);
1498 };
1499 + }, [didCascade, shouldCascade]);
1500 + return null;
1501 + };
1502
1593 - class ComponentWithCommitHooks extends React.Component {
1594 - state = {
1595 - didCascade: false,
1596 - };
1597 - componentDidMount() {
1598 - Scheduler.unstable_advanceTime(1000);
1599 - }
1600 - componentDidUpdate() {
1601 - Scheduler.unstable_advanceTime(10000);
1602 - if (this.props.shouldCascade && !this.state.didCascade) {
1603 - this.setState({didCascade: true});
1604 - }
1605 - }
1606 - render() {
1607 - Scheduler.unstable_advanceTime(1000000000);
1608 - return null;
1609 - }
1503 + class ComponentWithCommitHooks extends React.Component {
1504 + state = {
1505 + didCascade: false,
1506 + };
1507 + componentDidMount() {
1508 + Scheduler.unstable_advanceTime(1000);
1509 + }
1510 + componentDidUpdate() {
1511 + Scheduler.unstable_advanceTime(10000);
1512 + if (this.props.shouldCascade && !this.state.didCascade) {
1513 + this.setState({didCascade: true});
1514 }
1515 + }
1516 + render() {
1517 + Scheduler.unstable_advanceTime(1000000000);
1518 + return null;
1519 + }
1520 + }
1521
1612 - Scheduler.unstable_advanceTime(1);
1522 + Scheduler.unstable_advanceTime(1);
1523
1614 - const renderer = ReactTestRenderer.create(
1615 - <React.Profiler id="mount-test" onCommit={callback}>
1616 - <ComponentWithEffects shouldCascade={true} />
1617 - <ComponentWithCommitHooks />
1618 - </React.Profiler>,
1619 - );
1524 + const renderer = ReactTestRenderer.create(
1525 + <React.Profiler id="mount-test" onCommit={callback}>
1526 + <ComponentWithEffects shouldCascade={true} />
1527 + <ComponentWithCommitHooks />
1528 + </React.Profiler>,
1529 + );
1530
1621 - expect(callback).toHaveBeenCalledTimes(2);
1531 + expect(callback).toHaveBeenCalledTimes(2);
1532
1623 - let call = callback.mock.calls[0];
1533 + let call = callback.mock.calls[0];
1534
1625 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
1626 - expect(call[0]).toBe('mount-test');
1627 - expect(call[1]).toBe('mount');
1628 - expect(call[2]).toBe(1010); // durations
1629 - expect(call[3]).toBe(1100000001); // commit start time (before mutations or effects)
1630 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
1535 + expect(call).toHaveLength(4);
1536 + expect(call[0]).toBe('mount-test');
1537 + expect(call[1]).toBe('mount');
1538 + expect(call[2]).toBe(1010); // durations
1539 + expect(call[3]).toBe(1100000001); // commit start time (before mutations or effects)
1540
1632 - call = callback.mock.calls[1];
1541 + call = callback.mock.calls[1];
1542
1634 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
1635 - expect(call[0]).toBe('mount-test');
1636 - expect(call[1]).toBe('nested-update');
1637 - expect(call[2]).toBe(130); // durations
1638 - expect(call[3]).toBe(1200001011); // commit start time (before mutations or effects)
1639 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
1543 + expect(call).toHaveLength(4);
1544 + expect(call[0]).toBe('mount-test');
1545 + expect(call[1]).toBe('nested-update');
1546 + expect(call[2]).toBe(130); // durations
1547 + expect(call[3]).toBe(1200001011); // commit start time (before mutations or effects)
1548
1641 - Scheduler.unstable_advanceTime(1);
1549 + Scheduler.unstable_advanceTime(1);
1550
1643 - renderer.update(
1644 - <React.Profiler id="update-test" onCommit={callback}>
1645 - <ComponentWithEffects />
1646 - <ComponentWithCommitHooks shouldCascade={true} />
1647 - </React.Profiler>,
1648 - );
1551 + renderer.update(
1552 + <React.Profiler id="update-test" onCommit={callback}>
1553 + <ComponentWithEffects />
1554 + <ComponentWithCommitHooks shouldCascade={true} />
1555 + </React.Profiler>,
1556 + );
1557
1650 - expect(callback).toHaveBeenCalledTimes(4);
1558 + expect(callback).toHaveBeenCalledTimes(4);
1559
1652 - call = callback.mock.calls[2];
1560 + call = callback.mock.calls[2];
1561
1654 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
1655 - expect(call[0]).toBe('update-test');
1656 - expect(call[1]).toBe('update');
1657 - expect(call[2]).toBe(10130); // durations
1658 - expect(call[3]).toBe(2300001142); // commit start time (before mutations or effects)
1659 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
1562 + expect(call).toHaveLength(4);
1563 + expect(call[0]).toBe('update-test');
1564 + expect(call[1]).toBe('update');
1565 + expect(call[2]).toBe(10130); // durations
1566 + expect(call[3]).toBe(2300001142); // commit start time (before mutations or effects)
1567
1661 - call = callback.mock.calls[3];
1568 + call = callback.mock.calls[3];
1569
1663 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
1664 - expect(call[0]).toBe('update-test');
1665 - expect(call[1]).toBe('nested-update');
1666 - expect(call[2]).toBe(10000); // durations
1667 - expect(call[3]).toBe(3300011272); // commit start time (before mutations or effects)
1668 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
1669 - });
1570 + expect(call).toHaveLength(4);
1571 + expect(call[0]).toBe('update-test');
1572 + expect(call[1]).toBe('nested-update');
1573 + expect(call[2]).toBe(10000); // durations
1574 + expect(call[3]).toBe(3300011272); // commit start time (before mutations or effects)
1575 + });
1576
1671 - it('should include time spent in ref callbacks', () => {
1672 - const callback = jest.fn();
1577 + it('should include time spent in ref callbacks', () => {
1578 + const callback = jest.fn();
1579
1674 - const refSetter = ref => {
1675 - if (ref !== null) {
1676 - Scheduler.unstable_advanceTime(10);
1677 - } else {
1678 - Scheduler.unstable_advanceTime(100);
1679 - }
1680 - };
1580 + const refSetter = ref => {
1581 + if (ref !== null) {
1582 + Scheduler.unstable_advanceTime(10);
1583 + } else {
1584 + Scheduler.unstable_advanceTime(100);
1585 + }
1586 + };
1587
1682 - class ClassComponent extends React.Component {
1683 - render() {
1684 - return null;
1685 - }
1686 - }
1588 + class ClassComponent extends React.Component {
1589 + render() {
1590 + return null;
1591 + }
1592 + }
1593
1688 - const Component = () => {
1689 - Scheduler.unstable_advanceTime(1000);
1690 - return <ClassComponent ref={refSetter} />;
1691 - };
1594 + const Component = () => {
1595 + Scheduler.unstable_advanceTime(1000);
1596 + return <ClassComponent ref={refSetter} />;
1597 + };
1598
1693 - Scheduler.unstable_advanceTime(1);
1599 + Scheduler.unstable_advanceTime(1);
1600
1695 - const renderer = ReactTestRenderer.create(
1696 - <React.Profiler id="root" onCommit={callback}>
1697 - <Component />
1698 - </React.Profiler>,
1699 - );
1601 + const renderer = ReactTestRenderer.create(
1602 + <React.Profiler id="root" onCommit={callback}>
1603 + <Component />
1604 + </React.Profiler>,
1605 + );
1606
1701 - expect(callback).toHaveBeenCalledTimes(1);
1607 + expect(callback).toHaveBeenCalledTimes(1);
1608
1703 - let call = callback.mock.calls[0];
1609 + let call = callback.mock.calls[0];
1610
1705 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
1706 - expect(call[0]).toBe('root');
1707 - expect(call[1]).toBe('mount');
1708 - expect(call[2]).toBe(10); // durations
1709 - expect(call[3]).toBe(1001); // commit start time (before mutations or effects)
1611 + expect(call).toHaveLength(4);
1612 + expect(call[0]).toBe('root');
1613 + expect(call[1]).toBe('mount');
1614 + expect(call[2]).toBe(10); // durations
1615 + expect(call[3]).toBe(1001); // commit start time (before mutations or effects)
1616
1711 - callback.mockClear();
1617 + callback.mockClear();
1618
1713 - renderer.update(<React.Profiler id="root" onCommit={callback} />);
1619 + renderer.update(<React.Profiler id="root" onCommit={callback} />);
1620
1715 - expect(callback).toHaveBeenCalledTimes(1);
1621 + expect(callback).toHaveBeenCalledTimes(1);
1622
1717 - call = callback.mock.calls[0];
1623 + call = callback.mock.calls[0];
1624
1719 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
1720 - expect(call[0]).toBe('root');
1721 - expect(call[1]).toBe('update');
1722 - expect(call[2]).toBe(100); // durations
1723 - expect(call[3]).toBe(1011); // commit start time (before mutations or effects)
1724 - });
1625 + expect(call).toHaveLength(4);
1626 + expect(call[0]).toBe('root');
1627 + expect(call[1]).toBe('update');
1628 + expect(call[2]).toBe(100); // durations
1629 + expect(call[3]).toBe(1011); // commit start time (before mutations or effects)
1630 + });
1631
1726 - it('should bubble time spent in layout effects to higher profilers', () => {
1727 - const callback = jest.fn();
1728 -
1729 - const ComponentWithEffects = ({
1730 - cleanupDuration,
1731 - duration,
1732 - setCountRef,
1733 - }) => {
1734 - const setCount = React.useState(0)[1];
1735 - if (setCountRef != null) {
1736 - setCountRef.current = setCount;
1737 - }
1738 - React.useLayoutEffect(() => {
1739 - Scheduler.unstable_advanceTime(duration);
1740 - return () => {
1741 - Scheduler.unstable_advanceTime(cleanupDuration);
1742 - };
1743 - });
1744 - Scheduler.unstable_advanceTime(1);
1745 - return null;
1632 + it('should bubble time spent in layout effects to higher profilers', () => {
1633 + const callback = jest.fn();
1634 +
1635 + const ComponentWithEffects = ({cleanupDuration, duration, setCountRef}) => {
1636 + const setCount = React.useState(0)[1];
1637 + if (setCountRef != null) {
1638 + setCountRef.current = setCount;
1639 + }
1640 + React.useLayoutEffect(() => {
1641 + Scheduler.unstable_advanceTime(duration);
1642 + return () => {
1643 + Scheduler.unstable_advanceTime(cleanupDuration);
1644 };
1645 + });
1646 + Scheduler.unstable_advanceTime(1);
1647 + return null;
1648 + };
1649
1748 - const setCountRef = React.createRef(null);
1650 + const setCountRef = React.createRef(null);
1651 +
1652 + let renderer = null;
1653 + ReactTestRendererAct(() => {
1654 + renderer = ReactTestRenderer.create(
1655 + <React.Profiler id="root-mount" onCommit={callback}>
1656 + <React.Profiler id="a">
1657 + <ComponentWithEffects
1658 + duration={10}
1659 + cleanupDuration={100}
1660 + setCountRef={setCountRef}
1661 + />
1662 + </React.Profiler>
1663 + <React.Profiler id="b">
1664 + <ComponentWithEffects duration={1000} cleanupDuration={10000} />
1665 + </React.Profiler>
1666 + </React.Profiler>,
1667 + );
1668 + });
1669
1750 - let renderer = null;
1751 - ReactTestRendererAct(() => {
1752 - renderer = ReactTestRenderer.create(
1753 - <React.Profiler id="root-mount" onCommit={callback}>
1754 - <React.Profiler id="a">
1755 - <ComponentWithEffects
1756 - duration={10}
1757 - cleanupDuration={100}
1758 - setCountRef={setCountRef}
1759 - />
1760 - </React.Profiler>
1761 - <React.Profiler id="b">
1762 - <ComponentWithEffects duration={1000} cleanupDuration={10000} />
1763 - </React.Profiler>
1764 - </React.Profiler>,
1765 - );
1766 - });
1670 + expect(callback).toHaveBeenCalledTimes(1);
1671
1768 - expect(callback).toHaveBeenCalledTimes(1);
1672 + let call = callback.mock.calls[0];
1673
1770 - let call = callback.mock.calls[0];
1674 + expect(call).toHaveLength(4);
1675 + expect(call[0]).toBe('root-mount');
1676 + expect(call[1]).toBe('mount');
1677 + expect(call[2]).toBe(1010); // durations
1678 + expect(call[3]).toBe(2); // commit start time (before mutations or effects)
1679
1772 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
1773 - expect(call[0]).toBe('root-mount');
1774 - expect(call[1]).toBe('mount');
1775 - expect(call[2]).toBe(1010); // durations
1776 - expect(call[3]).toBe(2); // commit start time (before mutations or effects)
1777 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
1680 + ReactTestRendererAct(() => setCountRef.current(count => count + 1));
1681
1779 - ReactTestRendererAct(() => setCountRef.current(count => count + 1));
1682 + expect(callback).toHaveBeenCalledTimes(2);
1683
1781 - expect(callback).toHaveBeenCalledTimes(2);
1684 + call = callback.mock.calls[1];
1685
1783 - call = callback.mock.calls[1];
1686 + expect(call).toHaveLength(4);
1687 + expect(call[0]).toBe('root-mount');
1688 + expect(call[1]).toBe('update');
1689 + expect(call[2]).toBe(110); // durations
1690 + expect(call[3]).toBe(1013); // commit start time (before mutations or effects)
1691
1785 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
1786 - expect(call[0]).toBe('root-mount');
1787 - expect(call[1]).toBe('update');
1788 - expect(call[2]).toBe(110); // durations
1789 - expect(call[3]).toBe(1013); // commit start time (before mutations or effects)
1790 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
1692 + ReactTestRendererAct(() => {
1693 + renderer.update(
1694 + <React.Profiler id="root-update" onCommit={callback}>
1695 + <React.Profiler id="b">
1696 + <ComponentWithEffects duration={1000} cleanupDuration={10000} />
1697 + </React.Profiler>
1698 + </React.Profiler>,
1699 + );
1700 + });
1701
1792 - ReactTestRendererAct(() => {
1793 - renderer.update(
1794 - <React.Profiler id="root-update" onCommit={callback}>
1795 - <React.Profiler id="b">
1796 - <ComponentWithEffects duration={1000} cleanupDuration={10000} />
1797 - </React.Profiler>
1798 - </React.Profiler>,
1799 - );
1800 - });
1702 + expect(callback).toHaveBeenCalledTimes(3);
1703
1802 - expect(callback).toHaveBeenCalledTimes(3);
1704 + call = callback.mock.calls[2];
1705
1804 - call = callback.mock.calls[2];
1706 + expect(call).toHaveLength(4);
1707 + expect(call[0]).toBe('root-update');
1708 + expect(call[1]).toBe('update');
1709 + expect(call[2]).toBe(1100); // durations
1710 + expect(call[3]).toBe(1124); // commit start time (before mutations or effects)
1711 + });
1712
1806 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
1807 - expect(call[0]).toBe('root-update');
1808 - expect(call[1]).toBe('update');
1809 - expect(call[2]).toBe(1100); // durations
1810 - expect(call[3]).toBe(1124); // commit start time (before mutations or effects)
1811 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
1812 - });
1713 + it('should properly report time in layout effects even when there are errors', () => {
1714 + const callback = jest.fn();
1715
1814 - it('should properly report time in layout effects even when there are errors', () => {
1815 - const callback = jest.fn();
1716 + class ErrorBoundary extends React.Component {
1717 + state = {error: null};
1718 + static getDerivedStateFromError(error) {
1719 + return {error};
1720 + }
1721 + render() {
1722 + return this.state.error === null
1723 + ? this.props.children
1724 + : this.props.fallback;
1725 + }
1726 + }
1727
1817 - class ErrorBoundary extends React.Component {
1818 - state = {error: null};
1819 - static getDerivedStateFromError(error) {
1820 - return {error};
1821 - }
1822 - render() {
1823 - return this.state.error === null
1824 - ? this.props.children
1825 - : this.props.fallback;
1826 - }
1728 + const ComponentWithEffects = ({
1729 + cleanupDuration,
1730 + duration,
1731 + effectDuration,
1732 + shouldThrow,
1733 + }) => {
1734 + React.useLayoutEffect(() => {
1735 + Scheduler.unstable_advanceTime(effectDuration);
1736 + if (shouldThrow) {
1737 + throw Error('expected');
1738 }
1828 -
1829 - const ComponentWithEffects = ({
1830 - cleanupDuration,
1831 - duration,
1832 - effectDuration,
1833 - shouldThrow,
1834 - }) => {
1835 - React.useLayoutEffect(() => {
1836 - Scheduler.unstable_advanceTime(effectDuration);
1837 - if (shouldThrow) {
1838 - throw Error('expected');
1839 - }
1840 - return () => {
1841 - Scheduler.unstable_advanceTime(cleanupDuration);
1842 - };
1843 - });
1844 - Scheduler.unstable_advanceTime(duration);
1845 - return null;
1739 + return () => {
1740 + Scheduler.unstable_advanceTime(cleanupDuration);
1741 };
1742 + });
1743 + Scheduler.unstable_advanceTime(duration);
1744 + return null;
1745 + };
1746
1848 - Scheduler.unstable_advanceTime(1);
1747 + Scheduler.unstable_advanceTime(1);
1748
1850 - // Test an error that happens during an effect
1749 + // Test an error that happens during an effect
1750
1852 - ReactTestRendererAct(() => {
1853 - ReactTestRenderer.create(
1854 - <React.Profiler id="root" onCommit={callback}>
1855 - <ErrorBoundary
1856 - fallback={
1857 - <ComponentWithEffects
1858 - duration={10000000}
1859 - effectDuration={100000000}
1860 - cleanupDuration={1000000000}
1861 - />
1862 - }>
1863 - <ComponentWithEffects
1864 - duration={10}
1865 - effectDuration={100}
1866 - cleanupDuration={1000}
1867 - shouldThrow={true}
1868 - />
1869 - </ErrorBoundary>
1751 + ReactTestRendererAct(() => {
1752 + ReactTestRenderer.create(
1753 + <React.Profiler id="root" onCommit={callback}>
1754 + <ErrorBoundary
1755 + fallback={
1756 <ComponentWithEffects
1871 - duration={10000}
1872 - effectDuration={100000}
1873 - cleanupDuration={1000000}
1757 + duration={10000000}
1758 + effectDuration={100000000}
1759 + cleanupDuration={1000000000}
1760 />
1875 - </React.Profiler>,
1876 - );
1877 - });
1761 + }>
1762 + <ComponentWithEffects
1763 + duration={10}
1764 + effectDuration={100}
1765 + cleanupDuration={1000}
1766 + shouldThrow={true}
1767 + />
1768 + </ErrorBoundary>
1769 + <ComponentWithEffects
1770 + duration={10000}
1771 + effectDuration={100000}
1772 + cleanupDuration={1000000}
1773 + />
1774 + </React.Profiler>,
1775 + );
1776 + });
1777
1879 - expect(callback).toHaveBeenCalledTimes(2);
1778 + expect(callback).toHaveBeenCalledTimes(2);
1779
1881 - let call = callback.mock.calls[0];
1780 + let call = callback.mock.calls[0];
1781
1883 - // Initial render (with error)
1884 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
1885 - expect(call[0]).toBe('root');
1886 - expect(call[1]).toBe('mount');
1887 - expect(call[2]).toBe(100100); // durations
1888 - expect(call[3]).toBe(10011); // commit start time (before mutations or effects)
1889 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
1782 + // Initial render (with error)
1783 + expect(call).toHaveLength(4);
1784 + expect(call[0]).toBe('root');
1785 + expect(call[1]).toBe('mount');
1786 + expect(call[2]).toBe(100100); // durations
1787 + expect(call[3]).toBe(10011); // commit start time (before mutations or effects)
1788
1891 - call = callback.mock.calls[1];
1789 + call = callback.mock.calls[1];
1790
1893 - // Cleanup render from error boundary
1894 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
1895 - expect(call[0]).toBe('root');
1896 - expect(call[1]).toBe('nested-update');
1897 - expect(call[2]).toBe(100000000); // durations
1898 - expect(call[3]).toBe(10110111); // commit start time (before mutations or effects)
1899 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
1900 - });
1791 + // Cleanup render from error boundary
1792 + expect(call).toHaveLength(4);
1793 + expect(call[0]).toBe('root');
1794 + expect(call[1]).toBe('nested-update');
1795 + expect(call[2]).toBe(100000000); // durations
1796 + expect(call[3]).toBe(10110111); // commit start time (before mutations or effects)
1797 + });
1798
1902 - it('should properly report time in layout effect cleanup functions even when there are errors', () => {
1903 - const callback = jest.fn();
1799 + it('should properly report time in layout effect cleanup functions even when there are errors', () => {
1800 + const callback = jest.fn();
1801
1905 - class ErrorBoundary extends React.Component {
1906 - state = {error: null};
1907 - static getDerivedStateFromError(error) {
1908 - return {error};
1909 - }
1910 - render() {
1911 - return this.state.error === null
1912 - ? this.props.children
1913 - : this.props.fallback;
1914 - }
1915 - }
1802 + class ErrorBoundary extends React.Component {
1803 + state = {error: null};
1804 + static getDerivedStateFromError(error) {
1805 + return {error};
1806 + }
1807 + render() {
1808 + return this.state.error === null
1809 + ? this.props.children
1810 + : this.props.fallback;
1811 + }
1812 + }
1813
1917 - const ComponentWithEffects = ({
1918 - cleanupDuration,
1919 - duration,
1920 - effectDuration,
1921 - shouldThrow = false,
1922 - }) => {
1923 - React.useLayoutEffect(() => {
1924 - Scheduler.unstable_advanceTime(effectDuration);
1925 - return () => {
1926 - Scheduler.unstable_advanceTime(cleanupDuration);
1927 - if (shouldThrow) {
1928 - throw Error('expected');
1929 - }
1930 - };
1931 - });
1932 - Scheduler.unstable_advanceTime(duration);
1933 - return null;
1814 + const ComponentWithEffects = ({
1815 + cleanupDuration,
1816 + duration,
1817 + effectDuration,
1818 + shouldThrow = false,
1819 + }) => {
1820 + React.useLayoutEffect(() => {
1821 + Scheduler.unstable_advanceTime(effectDuration);
1822 + return () => {
1823 + Scheduler.unstable_advanceTime(cleanupDuration);
1824 + if (shouldThrow) {
1825 + throw Error('expected');
1826 + }
1827 };
1828 + });
1829 + Scheduler.unstable_advanceTime(duration);
1830 + return null;
1831 + };
1832
1936 - Scheduler.unstable_advanceTime(1);
1833 + Scheduler.unstable_advanceTime(1);
1834
1938 - let renderer = null;
1835 + let renderer = null;
1836
1940 - ReactTestRendererAct(() => {
1941 - renderer = ReactTestRenderer.create(
1942 - <React.Profiler id="root" onCommit={callback}>
1943 - <ErrorBoundary
1944 - fallback={
1945 - <ComponentWithEffects
1946 - duration={10000000}
1947 - effectDuration={100000000}
1948 - cleanupDuration={1000000000}
1949 - />
1950 - }>
1951 - <ComponentWithEffects
1952 - duration={10}
1953 - effectDuration={100}
1954 - cleanupDuration={1000}
1955 - shouldThrow={true}
1956 - />
1957 - </ErrorBoundary>
1837 + ReactTestRendererAct(() => {
1838 + renderer = ReactTestRenderer.create(
1839 + <React.Profiler id="root" onCommit={callback}>
1840 + <ErrorBoundary
1841 + fallback={
1842 <ComponentWithEffects
1959 - duration={10000}
1960 - effectDuration={100000}
1961 - cleanupDuration={1000000}
1843 + duration={10000000}
1844 + effectDuration={100000000}
1845 + cleanupDuration={1000000000}
1846 />
1963 - </React.Profiler>,
1964 - );
1965 - });
1847 + }>
1848 + <ComponentWithEffects
1849 + duration={10}
1850 + effectDuration={100}
1851 + cleanupDuration={1000}
1852 + shouldThrow={true}
1853 + />
1854 + </ErrorBoundary>
1855 + <ComponentWithEffects
1856 + duration={10000}
1857 + effectDuration={100000}
1858 + cleanupDuration={1000000}
1859 + />
1860 + </React.Profiler>,
1861 + );
1862 + });
1863
1967 - expect(callback).toHaveBeenCalledTimes(1);
1864 + expect(callback).toHaveBeenCalledTimes(1);
1865
1969 - let call = callback.mock.calls[0];
1866 + let call = callback.mock.calls[0];
1867
1971 - // Initial render
1972 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
1973 - expect(call[0]).toBe('root');
1974 - expect(call[1]).toBe('mount');
1975 - expect(call[2]).toBe(100100); // durations
1976 - expect(call[3]).toBe(10011); // commit start time (before mutations or effects)
1977 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
1868 + // Initial render
1869 + expect(call).toHaveLength(4);
1870 + expect(call[0]).toBe('root');
1871 + expect(call[1]).toBe('mount');
1872 + expect(call[2]).toBe(100100); // durations
1873 + expect(call[3]).toBe(10011); // commit start time (before mutations or effects)
1874
1979 - callback.mockClear();
1875 + callback.mockClear();
1876
1981 - // Test an error that happens during an cleanup function
1877 + // Test an error that happens during an cleanup function
1878
1983 - ReactTestRendererAct(() => {
1984 - renderer.update(
1985 - <React.Profiler id="root" onCommit={callback}>
1986 - <ErrorBoundary
1987 - fallback={
1988 - <ComponentWithEffects
1989 - duration={10000000}
1990 - effectDuration={100000000}
1991 - cleanupDuration={1000000000}
1992 - />
1993 - }>
1994 - <ComponentWithEffects
1995 - duration={10}
1996 - effectDuration={100}
1997 - cleanupDuration={1000}
1998 - shouldThrow={false}
1999 - />
2000 - </ErrorBoundary>
1879 + ReactTestRendererAct(() => {
1880 + renderer.update(
1881 + <React.Profiler id="root" onCommit={callback}>
1882 + <ErrorBoundary
1883 + fallback={
1884 <ComponentWithEffects
2002 - duration={10000}
2003 - effectDuration={100000}
2004 - cleanupDuration={1000000}
1885 + duration={10000000}
1886 + effectDuration={100000000}
1887 + cleanupDuration={1000000000}
1888 />
2006 - </React.Profiler>,
2007 - );
2008 - });
2009 -
2010 - expect(callback).toHaveBeenCalledTimes(2);
2011 -
2012 - call = callback.mock.calls[0];
2013 -
2014 - // Update (that throws)
2015 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2016 - expect(call[0]).toBe('root');
2017 - expect(call[1]).toBe('update');
2018 - expect(call[2]).toBe(1101100); // durations
2019 - expect(call[3]).toBe(120121); // commit start time (before mutations or effects)
2020 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
2021 -
2022 - call = callback.mock.calls[1];
1889 + }>
1890 + <ComponentWithEffects
1891 + duration={10}
1892 + effectDuration={100}
1893 + cleanupDuration={1000}
1894 + shouldThrow={false}
1895 + />
1896 + </ErrorBoundary>
1897 + <ComponentWithEffects
1898 + duration={10000}
1899 + effectDuration={100000}
1900 + cleanupDuration={1000000}
1901 + />
1902 + </React.Profiler>,
1903 + );
1904 + });
1905
2024 - // Cleanup render from error boundary
2025 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2026 - expect(call[0]).toBe('root');
2027 - expect(call[1]).toBe('nested-update');
2028 - expect(call[2]).toBe(100001000); // durations
2029 - expect(call[3]).toBe(11221221); // commit start time (before mutations or effects)
2030 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
2031 - });
1906 + expect(callback).toHaveBeenCalledTimes(2);
1907
2033 - if (enableSchedulerTracing) {
2034 - it('should report interactions that were active', () => {
2035 - const callback = jest.fn();
1908 + call = callback.mock.calls[0];
1909
2037 - const ComponentWithEffects = () => {
2038 - const [didMount, setDidMount] = React.useState(false);
2039 - React.useLayoutEffect(() => {
2040 - Scheduler.unstable_advanceTime(didMount ? 1000 : 100);
2041 - if (!didMount) {
2042 - setDidMount(true);
2043 - }
2044 - return () => {
2045 - Scheduler.unstable_advanceTime(10000);
2046 - };
2047 - }, [didMount]);
2048 - Scheduler.unstable_advanceTime(10);
2049 - return null;
2050 - };
1910 + // Update (that throws)
1911 + expect(call).toHaveLength(4);
1912 + expect(call[0]).toBe('root');
1913 + expect(call[1]).toBe('update');
1914 + expect(call[2]).toBe(1101100); // durations
1915 + expect(call[3]).toBe(120121); // commit start time (before mutations or effects)
1916
2052 - const interaction = {
2053 - id: 0,
2054 - name: 'mount',
2055 - timestamp: Scheduler.unstable_now(),
2056 - };
1917 + call = callback.mock.calls[1];
1918
2058 - Scheduler.unstable_advanceTime(1);
1919 + // Cleanup render from error boundary
1920 + expect(call).toHaveLength(4);
1921 + expect(call[0]).toBe('root');
1922 + expect(call[1]).toBe('nested-update');
1923 + expect(call[2]).toBe(100001000); // durations
1924 + expect(call[3]).toBe(11221221); // commit start time (before mutations or effects)
1925 + });
1926 +});
1927
2060 - SchedulerTracing.unstable_trace(
2061 - interaction.name,
2062 - interaction.timestamp,
2063 - () => {
2064 - ReactTestRenderer.create(
2065 - <React.Profiler id="root" onCommit={callback}>
2066 - <ComponentWithEffects />
2067 - </React.Profiler>,
2068 - );
2069 - },
2070 - );
1928 +describe(`onPostCommit`, () => {
1929 + beforeEach(() => {
1930 + jest.resetModules();
1931
2072 - expect(callback).toHaveBeenCalledTimes(2);
1932 + loadModules();
1933 + });
1934
2074 - let call = callback.mock.calls[0];
1935 + it('should report time spent in passive effects', () => {
1936 + const callback = jest.fn();
1937
2076 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2077 - expect(call[0]).toBe('root');
2078 - expect(call[1]).toBe('mount');
2079 - expect(call[4]).toMatchInteractions([interaction]);
1938 + const ComponentWithEffects = () => {
1939 + React.useLayoutEffect(() => {
1940 + // This layout effect is here to verify that its time isn't reported.
1941 + Scheduler.unstable_advanceTime(5);
1942 + return () => {
1943 + Scheduler.unstable_advanceTime(7);
1944 + };
1945 + });
1946 + React.useEffect(() => {
1947 + Scheduler.unstable_advanceTime(10);
1948 + return () => {
1949 + Scheduler.unstable_advanceTime(100);
1950 + };
1951 + }, []);
1952 + React.useEffect(() => {
1953 + Scheduler.unstable_advanceTime(1000);
1954 + return () => {
1955 + Scheduler.unstable_advanceTime(10000);
1956 + };
1957 + });
1958 + return null;
1959 + };
1960
2081 - call = callback.mock.calls[1];
1961 + Scheduler.unstable_advanceTime(1);
1962
2083 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2084 - expect(call[0]).toBe('root');
2085 - expect(call[1]).toBe('nested-update');
2086 - expect(call[4]).toMatchInteractions([interaction]);
2087 - });
2088 - }
1963 + let renderer;
1964 + ReactTestRendererAct(() => {
1965 + renderer = ReactTestRenderer.create(
1966 + <React.Profiler id="mount-test" onPostCommit={callback}>
1967 + <ComponentWithEffects />
1968 + </React.Profiler>,
1969 + );
1970 });
1971 + Scheduler.unstable_flushAll();
1972
2091 - describe(`onPostCommit enableSchedulerTracing:${
2092 - enableSchedulerTracing ? 'enabled' : 'disabled'
2093 - }`, () => {
2094 - beforeEach(() => {
2095 - jest.resetModules();
2096 -
2097 - loadModules({
2098 - enableSchedulerTracing,
2099 - });
2100 - });
2101 -
2102 - it('should report time spent in passive effects', () => {
2103 - const callback = jest.fn();
1973 + expect(callback).toHaveBeenCalledTimes(1);
1974
2105 - const ComponentWithEffects = () => {
2106 - React.useLayoutEffect(() => {
2107 - // This layout effect is here to verify that its time isn't reported.
2108 - Scheduler.unstable_advanceTime(5);
2109 - return () => {
2110 - Scheduler.unstable_advanceTime(7);
2111 - };
2112 - });
2113 - React.useEffect(() => {
2114 - Scheduler.unstable_advanceTime(10);
2115 - return () => {
2116 - Scheduler.unstable_advanceTime(100);
2117 - };
2118 - }, []);
2119 - React.useEffect(() => {
2120 - Scheduler.unstable_advanceTime(1000);
2121 - return () => {
2122 - Scheduler.unstable_advanceTime(10000);
2123 - };
2124 - });
2125 - return null;
2126 - };
1975 + let call = callback.mock.calls[0];
1976
2128 - Scheduler.unstable_advanceTime(1);
2129 -
2130 - let renderer;
2131 - ReactTestRendererAct(() => {
2132 - renderer = ReactTestRenderer.create(
2133 - <React.Profiler id="mount-test" onPostCommit={callback}>
2134 - <ComponentWithEffects />
2135 - </React.Profiler>,
2136 - );
2137 - });
2138 - Scheduler.unstable_flushAll();
1977 + expect(call).toHaveLength(4);
1978 + expect(call[0]).toBe('mount-test');
1979 + expect(call[1]).toBe('mount');
1980 + expect(call[2]).toBe(1010); // durations
1981 + expect(call[3]).toBe(1); // commit start time (before mutations or effects)
1982
2140 - expect(callback).toHaveBeenCalledTimes(1);
1983 + Scheduler.unstable_advanceTime(1);
1984
2142 - let call = callback.mock.calls[0];
1985 + ReactTestRendererAct(() => {
1986 + renderer.update(
1987 + <React.Profiler id="update-test" onPostCommit={callback}>
1988 + <ComponentWithEffects />
1989 + </React.Profiler>,
1990 + );
1991 + });
1992 + Scheduler.unstable_flushAll();
1993
2144 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2145 - expect(call[0]).toBe('mount-test');
2146 - expect(call[1]).toBe('mount');
2147 - expect(call[2]).toBe(1010); // durations
2148 - expect(call[3]).toBe(1); // commit start time (before mutations or effects)
2149 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
1994 + expect(callback).toHaveBeenCalledTimes(2);
1995
2151 - Scheduler.unstable_advanceTime(1);
1996 + call = callback.mock.calls[1];
1997
2153 - ReactTestRendererAct(() => {
2154 - renderer.update(
2155 - <React.Profiler id="update-test" onPostCommit={callback}>
2156 - <ComponentWithEffects />
2157 - </React.Profiler>,
2158 - );
2159 - });
2160 - Scheduler.unstable_flushAll();
1998 + expect(call).toHaveLength(4);
1999 + expect(call[0]).toBe('update-test');
2000 + expect(call[1]).toBe('update');
2001 + expect(call[2]).toBe(11000); // durations
2002 + expect(call[3]).toBe(1017); // commit start time (before mutations or effects)
2003
2162 - expect(callback).toHaveBeenCalledTimes(2);
2004 + Scheduler.unstable_advanceTime(1);
2005
2164 - call = callback.mock.calls[1];
2006 + ReactTestRendererAct(() => {
2007 + renderer.update(
2008 + <React.Profiler id="unmount-test" onPostCommit={callback} />,
2009 + );
2010 + });
2011 + Scheduler.unstable_flushAll();
2012
2166 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2167 - expect(call[0]).toBe('update-test');
2168 - expect(call[1]).toBe('update');
2169 - expect(call[2]).toBe(11000); // durations
2170 - expect(call[3]).toBe(1017); // commit start time (before mutations or effects)
2171 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
2013 + expect(callback).toHaveBeenCalledTimes(3);
2014
2173 - Scheduler.unstable_advanceTime(1);
2015 + call = callback.mock.calls[2];
2016
2175 - ReactTestRendererAct(() => {
2176 - renderer.update(
2177 - <React.Profiler id="unmount-test" onPostCommit={callback} />,
2178 - );
2179 - });
2180 - Scheduler.unstable_flushAll();
2181 -
2182 - expect(callback).toHaveBeenCalledTimes(3);
2183 -
2184 - call = callback.mock.calls[2];
2185 -
2186 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2187 - expect(call[0]).toBe('unmount-test');
2188 - expect(call[1]).toBe('update');
2189 - // TODO (bvaughn) The duration reported below should be 10100, but is 0
2190 - // by the time the passive effect is flushed its parent Fiber pointer is gone.
2191 - // If we refactor to preserve the unmounted Fiber tree we could fix this.
2192 - // The current implementation would require too much extra overhead to track this.
2193 - expect(call[2]).toBe(0); // durations
2194 - expect(call[3]).toBe(12030); // commit start time (before mutations or effects)
2195 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
2196 - });
2017 + expect(call).toHaveLength(4);
2018 + expect(call[0]).toBe('unmount-test');
2019 + expect(call[1]).toBe('update');
2020 + // TODO (bvaughn) The duration reported below should be 10100, but is 0
2021 + // by the time the passive effect is flushed its parent Fiber pointer is gone.
2022 + // If we refactor to preserve the unmounted Fiber tree we could fix this.
2023 + // The current implementation would require too much extra overhead to track this.
2024 + expect(call[2]).toBe(0); // durations
2025 + expect(call[3]).toBe(12030); // commit start time (before mutations or effects)
2026 + });
2027
2198 - it('should report time spent in passive effects with cascading renders', () => {
2199 - const callback = jest.fn();
2028 + it('should report time spent in passive effects with cascading renders', () => {
2029 + const callback = jest.fn();
2030
2201 - const ComponentWithEffects = () => {
2202 - const [didMount, setDidMount] = React.useState(false);
2203 - Scheduler.unstable_advanceTime(1000);
2204 - React.useEffect(() => {
2205 - if (!didMount) {
2206 - setDidMount(true);
2207 - }
2208 - Scheduler.unstable_advanceTime(didMount ? 30 : 10);
2209 - return () => {
2210 - Scheduler.unstable_advanceTime(100);
2211 - };
2212 - }, [didMount]);
2213 - return null;
2031 + const ComponentWithEffects = () => {
2032 + const [didMount, setDidMount] = React.useState(false);
2033 + Scheduler.unstable_advanceTime(1000);
2034 + React.useEffect(() => {
2035 + if (!didMount) {
2036 + setDidMount(true);
2037 + }
2038 + Scheduler.unstable_advanceTime(didMount ? 30 : 10);
2039 + return () => {
2040 + Scheduler.unstable_advanceTime(100);
2041 };
2042 + }, [didMount]);
2043 + return null;
2044 + };
2045
2216 - Scheduler.unstable_advanceTime(1);
2046 + Scheduler.unstable_advanceTime(1);
2047
2218 - ReactTestRendererAct(() => {
2219 - ReactTestRenderer.create(
2220 - <React.Profiler id="mount-test" onPostCommit={callback}>
2221 - <ComponentWithEffects />
2222 - </React.Profiler>,
2223 - );
2224 - });
2048 + ReactTestRendererAct(() => {
2049 + ReactTestRenderer.create(
2050 + <React.Profiler id="mount-test" onPostCommit={callback}>
2051 + <ComponentWithEffects />
2052 + </React.Profiler>,
2053 + );
2054 + });
2055
2226 - expect(callback).toHaveBeenCalledTimes(2);
2056 + expect(callback).toHaveBeenCalledTimes(2);
2057
2228 - let call = callback.mock.calls[0];
2058 + let call = callback.mock.calls[0];
2059
2230 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2231 - expect(call[0]).toBe('mount-test');
2232 - expect(call[1]).toBe('mount');
2233 - expect(call[2]).toBe(10); // durations
2234 - expect(call[3]).toBe(1001); // commit start time (before mutations or effects)
2235 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
2060 + expect(call).toHaveLength(4);
2061 + expect(call[0]).toBe('mount-test');
2062 + expect(call[1]).toBe('mount');
2063 + expect(call[2]).toBe(10); // durations
2064 + expect(call[3]).toBe(1001); // commit start time (before mutations or effects)
2065
2237 - call = callback.mock.calls[1];
2066 + call = callback.mock.calls[1];
2067
2239 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2240 - expect(call[0]).toBe('mount-test');
2241 - expect(call[1]).toBe('update');
2242 - expect(call[2]).toBe(130); // durations
2243 - expect(call[3]).toBe(2011); // commit start time (before mutations or effects)
2244 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
2245 - });
2068 + expect(call).toHaveLength(4);
2069 + expect(call[0]).toBe('mount-test');
2070 + expect(call[1]).toBe('update');
2071 + expect(call[2]).toBe(130); // durations
2072 + expect(call[3]).toBe(2011); // commit start time (before mutations or effects)
2073 + });
2074
2247 - it('should bubble time spent in effects to higher profilers', () => {
2248 - const callback = jest.fn();
2249 -
2250 - const ComponentWithEffects = ({
2251 - cleanupDuration,
2252 - duration,
2253 - setCountRef,
2254 - }) => {
2255 - const setCount = React.useState(0)[1];
2256 - if (setCountRef != null) {
2257 - setCountRef.current = setCount;
2258 - }
2259 - React.useEffect(() => {
2260 - Scheduler.unstable_advanceTime(duration);
2261 - return () => {
2262 - Scheduler.unstable_advanceTime(cleanupDuration);
2263 - };
2264 - });
2265 - Scheduler.unstable_advanceTime(1);
2266 - return null;
2075 + it('should bubble time spent in effects to higher profilers', () => {
2076 + const callback = jest.fn();
2077 +
2078 + const ComponentWithEffects = ({cleanupDuration, duration, setCountRef}) => {
2079 + const setCount = React.useState(0)[1];
2080 + if (setCountRef != null) {
2081 + setCountRef.current = setCount;
2082 + }
2083 + React.useEffect(() => {
2084 + Scheduler.unstable_advanceTime(duration);
2085 + return () => {
2086 + Scheduler.unstable_advanceTime(cleanupDuration);
2087 };
2088 + });
2089 + Scheduler.unstable_advanceTime(1);
2090 + return null;
2091 + };
2092
2269 - const setCountRef = React.createRef(null);
2093 + const setCountRef = React.createRef(null);
2094 +
2095 + let renderer = null;
2096 + ReactTestRendererAct(() => {
2097 + renderer = ReactTestRenderer.create(
2098 + <React.Profiler id="root-mount" onPostCommit={callback}>
2099 + <React.Profiler id="a">
2100 + <ComponentWithEffects
2101 + duration={10}
2102 + cleanupDuration={100}
2103 + setCountRef={setCountRef}
2104 + />
2105 + </React.Profiler>
2106 + <React.Profiler id="b">
2107 + <ComponentWithEffects duration={1000} cleanupDuration={10000} />
2108 + </React.Profiler>
2109 + </React.Profiler>,
2110 + );
2111 + });
2112
2271 - let renderer = null;
2272 - ReactTestRendererAct(() => {
2273 - renderer = ReactTestRenderer.create(
2274 - <React.Profiler id="root-mount" onPostCommit={callback}>
2275 - <React.Profiler id="a">
2276 - <ComponentWithEffects
2277 - duration={10}
2278 - cleanupDuration={100}
2279 - setCountRef={setCountRef}
2280 - />
2281 - </React.Profiler>
2282 - <React.Profiler id="b">
2283 - <ComponentWithEffects duration={1000} cleanupDuration={10000} />
2284 - </React.Profiler>
2285 - </React.Profiler>,
2286 - );
2287 - });
2113 + expect(callback).toHaveBeenCalledTimes(1);
2114
2289 - expect(callback).toHaveBeenCalledTimes(1);
2115 + let call = callback.mock.calls[0];
2116
2291 - let call = callback.mock.calls[0];
2117 + expect(call).toHaveLength(4);
2118 + expect(call[0]).toBe('root-mount');
2119 + expect(call[1]).toBe('mount');
2120 + expect(call[2]).toBe(1010); // durations
2121 + expect(call[3]).toBe(2); // commit start time (before mutations or effects)
2122
2293 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2294 - expect(call[0]).toBe('root-mount');
2295 - expect(call[1]).toBe('mount');
2296 - expect(call[2]).toBe(1010); // durations
2297 - expect(call[3]).toBe(2); // commit start time (before mutations or effects)
2298 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
2123 + ReactTestRendererAct(() => setCountRef.current(count => count + 1));
2124
2300 - ReactTestRendererAct(() => setCountRef.current(count => count + 1));
2125 + expect(callback).toHaveBeenCalledTimes(2);
2126
2302 - expect(callback).toHaveBeenCalledTimes(2);
2127 + call = callback.mock.calls[1];
2128
2304 - call = callback.mock.calls[1];
2129 + expect(call).toHaveLength(4);
2130 + expect(call[0]).toBe('root-mount');
2131 + expect(call[1]).toBe('update');
2132 + expect(call[2]).toBe(110); // durations
2133 + expect(call[3]).toBe(1013); // commit start time (before mutations or effects)
2134
2306 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2307 - expect(call[0]).toBe('root-mount');
2308 - expect(call[1]).toBe('update');
2309 - expect(call[2]).toBe(110); // durations
2310 - expect(call[3]).toBe(1013); // commit start time (before mutations or effects)
2311 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
2135 + ReactTestRendererAct(() => {
2136 + renderer.update(
2137 + <React.Profiler id="root-update" onPostCommit={callback}>
2138 + <React.Profiler id="b">
2139 + <ComponentWithEffects duration={1000} cleanupDuration={10000} />
2140 + </React.Profiler>
2141 + </React.Profiler>,
2142 + );
2143 + });
2144
2313 - ReactTestRendererAct(() => {
2314 - renderer.update(
2315 - <React.Profiler id="root-update" onPostCommit={callback}>
2316 - <React.Profiler id="b">
2317 - <ComponentWithEffects duration={1000} cleanupDuration={10000} />
2318 - </React.Profiler>
2319 - </React.Profiler>,
2320 - );
2321 - });
2145 + expect(callback).toHaveBeenCalledTimes(3);
2146
2323 - expect(callback).toHaveBeenCalledTimes(3);
2147 + call = callback.mock.calls[2];
2148
2325 - call = callback.mock.calls[2];
2149 + expect(call).toHaveLength(4);
2150 + expect(call[0]).toBe('root-update');
2151 + expect(call[1]).toBe('update');
2152 + expect(call[2]).toBe(1100); // durations
2153 + expect(call[3]).toBe(1124); // commit start time (before mutations or effects)
2154 + });
2155
2327 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2328 - expect(call[0]).toBe('root-update');
2329 - expect(call[1]).toBe('update');
2330 - expect(call[2]).toBe(1100); // durations
2331 - expect(call[3]).toBe(1124); // commit start time (before mutations or effects)
2332 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
2333 - });
2156 + it('should properly report time in passive effects even when there are errors', () => {
2157 + const callback = jest.fn();
2158
2335 - it('should properly report time in passive effects even when there are errors', () => {
2336 - const callback = jest.fn();
2159 + class ErrorBoundary extends React.Component {
2160 + state = {error: null};
2161 + static getDerivedStateFromError(error) {
2162 + return {error};
2163 + }
2164 + render() {
2165 + return this.state.error === null
2166 + ? this.props.children
2167 + : this.props.fallback;
2168 + }
2169 + }
2170
2338 - class ErrorBoundary extends React.Component {
2339 - state = {error: null};
2340 - static getDerivedStateFromError(error) {
2341 - return {error};
2342 - }
2343 - render() {
2344 - return this.state.error === null
2345 - ? this.props.children
2346 - : this.props.fallback;
2347 - }
2171 + const ComponentWithEffects = ({
2172 + cleanupDuration,
2173 + duration,
2174 + effectDuration,
2175 + shouldThrow,
2176 + }) => {
2177 + React.useEffect(() => {
2178 + Scheduler.unstable_advanceTime(effectDuration);
2179 + if (shouldThrow) {
2180 + throw Error('expected');
2181 }
2349 -
2350 - const ComponentWithEffects = ({
2351 - cleanupDuration,
2352 - duration,
2353 - effectDuration,
2354 - shouldThrow,
2355 - }) => {
2356 - React.useEffect(() => {
2357 - Scheduler.unstable_advanceTime(effectDuration);
2358 - if (shouldThrow) {
2359 - throw Error('expected');
2360 - }
2361 - return () => {
2362 - Scheduler.unstable_advanceTime(cleanupDuration);
2363 - };
2364 - });
2365 - Scheduler.unstable_advanceTime(duration);
2366 - return null;
2182 + return () => {
2183 + Scheduler.unstable_advanceTime(cleanupDuration);
2184 };
2185 + });
2186 + Scheduler.unstable_advanceTime(duration);
2187 + return null;
2188 + };
2189
2369 - Scheduler.unstable_advanceTime(1);
2190 + Scheduler.unstable_advanceTime(1);
2191
2371 - // Test an error that happens during an effect
2192 + // Test an error that happens during an effect
2193
2373 - ReactTestRendererAct(() => {
2374 - ReactTestRenderer.create(
2375 - <React.Profiler id="root" onPostCommit={callback}>
2376 - <ErrorBoundary
2377 - fallback={
2378 - <ComponentWithEffects
2379 - duration={10000000}
2380 - effectDuration={100000000}
2381 - cleanupDuration={1000000000}
2382 - />
2383 - }>
2384 - <ComponentWithEffects
2385 - duration={10}
2386 - effectDuration={100}
2387 - cleanupDuration={1000}
2388 - shouldThrow={true}
2389 - />
2390 - </ErrorBoundary>
2194 + ReactTestRendererAct(() => {
2195 + ReactTestRenderer.create(
2196 + <React.Profiler id="root" onPostCommit={callback}>
2197 + <ErrorBoundary
2198 + fallback={
2199 <ComponentWithEffects
2392 - duration={10000}
2393 - effectDuration={100000}
2394 - cleanupDuration={1000000}
2200 + duration={10000000}
2201 + effectDuration={100000000}
2202 + cleanupDuration={1000000000}
2203 />
2396 - </React.Profiler>,
2397 - );
2398 - });
2204 + }>
2205 + <ComponentWithEffects
2206 + duration={10}
2207 + effectDuration={100}
2208 + cleanupDuration={1000}
2209 + shouldThrow={true}
2210 + />
2211 + </ErrorBoundary>
2212 + <ComponentWithEffects
2213 + duration={10000}
2214 + effectDuration={100000}
2215 + cleanupDuration={1000000}
2216 + />
2217 + </React.Profiler>,
2218 + );
2219 + });
2220
2400 - expect(callback).toHaveBeenCalledTimes(2);
2221 + expect(callback).toHaveBeenCalledTimes(2);
2222
2402 - let call = callback.mock.calls[0];
2223 + let call = callback.mock.calls[0];
2224
2404 - // Initial render (with error)
2405 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2406 - expect(call[0]).toBe('root');
2407 - expect(call[1]).toBe('mount');
2408 - expect(call[2]).toBe(100100); // durations
2409 - expect(call[3]).toBe(10011); // commit start time (before mutations or effects)
2410 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
2225 + // Initial render (with error)
2226 + expect(call).toHaveLength(4);
2227 + expect(call[0]).toBe('root');
2228 + expect(call[1]).toBe('mount');
2229 + expect(call[2]).toBe(100100); // durations
2230 + expect(call[3]).toBe(10011); // commit start time (before mutations or effects)
2231
2412 - call = callback.mock.calls[1];
2232 + call = callback.mock.calls[1];
2233
2414 - // Cleanup render from error boundary
2415 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2416 - expect(call[0]).toBe('root');
2417 - expect(call[1]).toBe('update');
2418 - expect(call[2]).toBe(100000000); // durations
2419 - expect(call[3]).toBe(10110111); // commit start time (before mutations or effects)
2420 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
2421 - });
2234 + // Cleanup render from error boundary
2235 + expect(call).toHaveLength(4);
2236 + expect(call[0]).toBe('root');
2237 + expect(call[1]).toBe('update');
2238 + expect(call[2]).toBe(100000000); // durations
2239 + expect(call[3]).toBe(10110111); // commit start time (before mutations or effects)
2240 + });
2241
2423 - it('should properly report time in passive effect cleanup functions even when there are errors', () => {
2424 - const callback = jest.fn();
2242 + it('should properly report time in passive effect cleanup functions even when there are errors', () => {
2243 + const callback = jest.fn();
2244
2426 - class ErrorBoundary extends React.Component {
2427 - state = {error: null};
2428 - static getDerivedStateFromError(error) {
2429 - return {error};
2430 - }
2431 - render() {
2432 - return this.state.error === null
2433 - ? this.props.children
2434 - : this.props.fallback;
2435 - }
2436 - }
2245 + class ErrorBoundary extends React.Component {
2246 + state = {error: null};
2247 + static getDerivedStateFromError(error) {
2248 + return {error};
2249 + }
2250 + render() {
2251 + return this.state.error === null
2252 + ? this.props.children
2253 + : this.props.fallback;
2254 + }
2255 + }
2256
2438 - const ComponentWithEffects = ({
2439 - cleanupDuration,
2440 - duration,
2441 - effectDuration,
2442 - shouldThrow = false,
2443 - id,
2444 - }) => {
2445 - React.useEffect(() => {
2446 - Scheduler.unstable_advanceTime(effectDuration);
2447 - return () => {
2448 - Scheduler.unstable_advanceTime(cleanupDuration);
2449 - if (shouldThrow) {
2450 - throw Error('expected');
2451 - }
2452 - };
2453 - });
2454 - Scheduler.unstable_advanceTime(duration);
2455 - return null;
2257 + const ComponentWithEffects = ({
2258 + cleanupDuration,
2259 + duration,
2260 + effectDuration,
2261 + shouldThrow = false,
2262 + id,
2263 + }) => {
2264 + React.useEffect(() => {
2265 + Scheduler.unstable_advanceTime(effectDuration);
2266 + return () => {
2267 + Scheduler.unstable_advanceTime(cleanupDuration);
2268 + if (shouldThrow) {
2269 + throw Error('expected');
2270 + }
2271 };
2272 + });
2273 + Scheduler.unstable_advanceTime(duration);
2274 + return null;
2275 + };
2276
2458 - Scheduler.unstable_advanceTime(1);
2277 + Scheduler.unstable_advanceTime(1);
2278
2460 - let renderer = null;
2279 + let renderer = null;
2280
2462 - ReactTestRendererAct(() => {
2463 - renderer = ReactTestRenderer.create(
2464 - <React.Profiler id="root" onPostCommit={callback}>
2465 - <ErrorBoundary
2466 - fallback={
2467 - <ComponentWithEffects
2468 - duration={10000000}
2469 - effectDuration={100000000}
2470 - cleanupDuration={1000000000}
2471 - />
2472 - }>
2473 - <ComponentWithEffects
2474 - duration={10}
2475 - effectDuration={100}
2476 - cleanupDuration={1000}
2477 - shouldThrow={true}
2478 - />
2479 - </ErrorBoundary>
2281 + ReactTestRendererAct(() => {
2282 + renderer = ReactTestRenderer.create(
2283 + <React.Profiler id="root" onPostCommit={callback}>
2284 + <ErrorBoundary
2285 + fallback={
2286 <ComponentWithEffects
2481 - duration={10000}
2482 - effectDuration={100000}
2483 - cleanupDuration={1000000}
2287 + duration={10000000}
2288 + effectDuration={100000000}
2289 + cleanupDuration={1000000000}
2290 />
2485 - </React.Profiler>,
2486 - );
2487 - });
2291 + }>
2292 + <ComponentWithEffects
2293 + duration={10}
2294 + effectDuration={100}
2295 + cleanupDuration={1000}
2296 + shouldThrow={true}
2297 + />
2298 + </ErrorBoundary>
2299 + <ComponentWithEffects
2300 + duration={10000}
2301 + effectDuration={100000}
2302 + cleanupDuration={1000000}
2303 + />
2304 + </React.Profiler>,
2305 + );
2306 + });
2307
2489 - expect(callback).toHaveBeenCalledTimes(1);
2308 + expect(callback).toHaveBeenCalledTimes(1);
2309
2491 - let call = callback.mock.calls[0];
2310 + let call = callback.mock.calls[0];
2311
2493 - // Initial render
2494 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2495 - expect(call[0]).toBe('root');
2496 - expect(call[1]).toBe('mount');
2497 - expect(call[2]).toBe(100100); // durations
2498 - expect(call[3]).toBe(10011); // commit start time (before mutations or effects)
2499 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
2312 + // Initial render
2313 + expect(call).toHaveLength(4);
2314 + expect(call[0]).toBe('root');
2315 + expect(call[1]).toBe('mount');
2316 + expect(call[2]).toBe(100100); // durations
2317 + expect(call[3]).toBe(10011); // commit start time (before mutations or effects)
2318
2501 - callback.mockClear();
2319 + callback.mockClear();
2320
2503 - // Test an error that happens during an cleanup function
2321 + // Test an error that happens during an cleanup function
2322
2505 - ReactTestRendererAct(() => {
2506 - renderer.update(
2507 - <React.Profiler id="root" onPostCommit={callback}>
2508 - <ErrorBoundary
2509 - fallback={
2510 - <ComponentWithEffects
2511 - duration={10000000}
2512 - effectDuration={100000000}
2513 - cleanupDuration={1000000000}
2514 - />
2515 - }>
2516 - <ComponentWithEffects
2517 - duration={10}
2518 - effectDuration={100}
2519 - cleanupDuration={1000}
2520 - shouldThrow={false}
2521 - />
2522 - </ErrorBoundary>
2323 + ReactTestRendererAct(() => {
2324 + renderer.update(
2325 + <React.Profiler id="root" onPostCommit={callback}>
2326 + <ErrorBoundary
2327 + fallback={
2328 <ComponentWithEffects
2524 - duration={10000}
2525 - effectDuration={100000}
2526 - cleanupDuration={1000000}
2329 + duration={10000000}
2330 + effectDuration={100000000}
2331 + cleanupDuration={1000000000}
2332 />
2528 - </React.Profiler>,
2529 - );
2530 - });
2333 + }>
2334 + <ComponentWithEffects
2335 + duration={10}
2336 + effectDuration={100}
2337 + cleanupDuration={1000}
2338 + shouldThrow={false}
2339 + />
2340 + </ErrorBoundary>
2341 + <ComponentWithEffects
2342 + duration={10000}
2343 + effectDuration={100000}
2344 + cleanupDuration={1000000}
2345 + />
2346 + </React.Profiler>,
2347 + );
2348 + });
2349
2532 - expect(callback).toHaveBeenCalledTimes(2);
2533 -
2534 - call = callback.mock.calls[0];
2535 -
2536 - // Update (that throws)
2537 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2538 - expect(call[0]).toBe('root');
2539 - expect(call[1]).toBe('update');
2540 - // We continue flushing pending effects even if one throws.
2541 - expect(call[2]).toBe(1101100); // durations
2542 - expect(call[3]).toBe(120121); // commit start time (before mutations or effects)
2543 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
2544 -
2545 - call = callback.mock.calls[1];
2546 -
2547 - // Cleanup render from error boundary
2548 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2549 - expect(call[0]).toBe('root');
2550 - expect(call[1]).toBe('update');
2551 - expect(call[2]).toBe(100000000); // durations
2552 - // The commit time varies because the above duration time varies
2553 - expect(call[3]).toBe(11221221); // commit start time (before mutations or effects)
2554 - expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events
2555 - });
2350 + expect(callback).toHaveBeenCalledTimes(2);
2351
2557 - if (enableSchedulerTracing) {
2558 - it('should report interactions that were active', () => {
2559 - const callback = jest.fn();
2352 + call = callback.mock.calls[0];
2353
2561 - const ComponentWithEffects = () => {
2562 - const [didMount, setDidMount] = React.useState(false);
2563 - React.useEffect(() => {
2564 - Scheduler.unstable_advanceTime(didMount ? 1000 : 100);
2565 - if (!didMount) {
2566 - setDidMount(true);
2567 - }
2568 - return () => {
2569 - Scheduler.unstable_advanceTime(10000);
2570 - };
2571 - }, [didMount]);
2572 - Scheduler.unstable_advanceTime(10);
2573 - return null;
2574 - };
2354 + // Update (that throws)
2355 + expect(call).toHaveLength(4);
2356 + expect(call[0]).toBe('root');
2357 + expect(call[1]).toBe('update');
2358 + // We continue flushing pending effects even if one throws.
2359 + expect(call[2]).toBe(1101100); // durations
2360 + expect(call[3]).toBe(120121); // commit start time (before mutations or effects)
2361
2576 - const interaction = {
2577 - id: 0,
2578 - name: 'mount',
2579 - timestamp: Scheduler.unstable_now(),
2580 - };
2362 + call = callback.mock.calls[1];
2363
2582 - Scheduler.unstable_advanceTime(1);
2583 -
2584 - ReactTestRendererAct(() => {
2585 - SchedulerTracing.unstable_trace(
2586 - interaction.name,
2587 - interaction.timestamp,
2588 - () => {
2589 - ReactTestRenderer.create(
2590 - <React.Profiler id="root" onPostCommit={callback}>
2591 - <ComponentWithEffects />
2592 - </React.Profiler>,
2593 - );
2594 - },
2595 - );
2596 - });
2364 + // Cleanup render from error boundary
2365 + expect(call).toHaveLength(4);
2366 + expect(call[0]).toBe('root');
2367 + expect(call[1]).toBe('update');
2368 + expect(call[2]).toBe(100000000); // durations
2369 + // The commit time varies because the above duration time varies
2370 + expect(call[3]).toBe(11221221); // commit start time (before mutations or effects)
2371 + });
2372 +});
2373
2598 - expect(callback).toHaveBeenCalledTimes(2);
2374 +describe(`onNestedUpdateScheduled`, () => {
2375 + beforeEach(() => {
2376 + jest.resetModules();
2377
2600 - let call = callback.mock.calls[0];
2378 + loadModules({
2379 + enableProfilerNestedUpdateScheduledHook: true,
2380 + useNoopRenderer: true,
2381 + });
2382 + });
2383
2602 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2603 - expect(call[0]).toBe('root');
2604 - expect(call[1]).toBe('mount');
2605 - expect(call[4]).toMatchInteractions([interaction]);
2384 + it('is not called when the legacy render API is used to schedule an update', () => {
2385 + const onNestedUpdateScheduled = jest.fn();
2386 +
2387 + ReactNoop.renderLegacySyncRoot(
2388 + <React.Profiler
2389 + id="test"
2390 + onNestedUpdateScheduled={onNestedUpdateScheduled}>
2391 + <div>initial</div>
2392 + </React.Profiler>,
2393 + );
2394 +
2395 + ReactNoop.renderLegacySyncRoot(
2396 + <React.Profiler
2397 + id="test"
2398 + onNestedUpdateScheduled={onNestedUpdateScheduled}>
2399 + <div>update</div>
2400 + </React.Profiler>,
2401 + );
2402 +
2403 + expect(onNestedUpdateScheduled).not.toHaveBeenCalled();
2404 + });
2405
2607 - call = callback.mock.calls[1];
2406 + it('is not called when the root API is used to schedule an update', () => {
2407 + const onNestedUpdateScheduled = jest.fn();
2408 +
2409 + ReactNoop.render(
2410 + <React.Profiler
2411 + id="test"
2412 + onNestedUpdateScheduled={onNestedUpdateScheduled}>
2413 + <div>initial</div>
2414 + </React.Profiler>,
2415 + );
2416 +
2417 + ReactNoop.render(
2418 + <React.Profiler
2419 + id="test"
2420 + onNestedUpdateScheduled={onNestedUpdateScheduled}>
2421 + <div>update</div>
2422 + </React.Profiler>,
2423 + );
2424 +
2425 + expect(onNestedUpdateScheduled).not.toHaveBeenCalled();
2426 + });
2427
2609 - expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4);
2610 - expect(call[0]).toBe('root');
2611 - expect(call[1]).toBe('update');
2612 - expect(call[4]).toMatchInteractions([interaction]);
2613 - });
2614 - }
2428 + it('is called when a function component schedules an update during a layout effect', () => {
2429 + function Component() {
2430 + const [didMount, setDidMount] = React.useState(false);
2431 + React.useLayoutEffect(() => {
2432 + setDidMount(true);
2433 + }, []);
2434 + Scheduler.unstable_yieldValue(`Component:${didMount}`);
2435 + return didMount;
2436 + }
2437 +
2438 + const onNestedUpdateScheduled = jest.fn();
2439 +
2440 + ReactNoop.act(() => {
2441 + ReactNoop.render(
2442 + <React.Profiler
2443 + id="test"
2444 + onNestedUpdateScheduled={onNestedUpdateScheduled}>
2445 + <Component />
2446 + </React.Profiler>,
2447 + );
2448 });
2449
2617 - describe(`onNestedUpdateScheduled enableSchedulerTracing:${
2618 - enableSchedulerTracing ? 'enabled' : 'disabled'
2619 - }`, () => {
2620 - beforeEach(() => {
2621 - jest.resetModules();
2450 + expect(Scheduler).toHaveYielded(['Component:false', 'Component:true']);
2451 + expect(onNestedUpdateScheduled).toHaveBeenCalledTimes(1);
2452 + expect(onNestedUpdateScheduled.mock.calls[0][0]).toBe('test');
2453 + });
2454
2623 - loadModules({
2624 - enableProfilerNestedUpdateScheduledHook: true,
2625 - enableSchedulerTracing,
2626 - useNoopRenderer: true,
2455 + it('is called when a function component schedules a batched update during a layout effect', () => {
2456 + function Component() {
2457 + const [didMount, setDidMount] = React.useState(false);
2458 + React.useLayoutEffect(() => {
2459 + ReactNoop.batchedUpdates(() => {
2460 + setDidMount(true);
2461 });
2628 - });
2462 + }, []);
2463 + Scheduler.unstable_yieldValue(`Component:${didMount}`);
2464 + return didMount;
2465 + }
2466
2630 - it('is not called when the legacy render API is used to schedule an update', () => {
2631 - const onNestedUpdateScheduled = jest.fn();
2467 + const onNestedUpdateScheduled = jest.fn();
2468 + const onRender = jest.fn();
2469 +
2470 + ReactNoop.render(
2471 + <React.Profiler
2472 + id="root"
2473 + onNestedUpdateScheduled={onNestedUpdateScheduled}
2474 + onRender={onRender}>
2475 + <Component />
2476 + </React.Profiler>,
2477 + );
2478 + expect(Scheduler).toFlushAndYield(['Component:false', 'Component:true']);
2479 +
2480 + expect(onRender).toHaveBeenCalledTimes(2);
2481 + expect(onRender.mock.calls[0][1]).toBe('mount');
2482 + expect(onRender.mock.calls[1][1]).toBe('nested-update');
2483 +
2484 + expect(onNestedUpdateScheduled).toHaveBeenCalledTimes(1);
2485 + expect(onNestedUpdateScheduled.mock.calls[0][0]).toBe('root');
2486 + });
2487
2633 - ReactNoop.renderLegacySyncRoot(
2488 + it('bubbles up and calls all ancestor Profilers', () => {
2489 + function Component() {
2490 + const [didMount, setDidMount] = React.useState(false);
2491 + React.useLayoutEffect(() => {
2492 + setDidMount(true);
2493 + }, []);
2494 + Scheduler.unstable_yieldValue(`Component:${didMount}`);
2495 + return didMount;
2496 + }
2497 + const onNestedUpdateScheduledOne = jest.fn();
2498 + const onNestedUpdateScheduledTwo = jest.fn();
2499 + const onNestedUpdateScheduledThree = jest.fn();
2500 +
2501 + ReactNoop.act(() => {
2502 + ReactNoop.render(
2503 + <React.Profiler
2504 + id="one"
2505 + onNestedUpdateScheduled={onNestedUpdateScheduledOne}>
2506 <React.Profiler
2635 - id="test"
2636 - onNestedUpdateScheduled={onNestedUpdateScheduled}>
2637 - <div>initial</div>
2638 - </React.Profiler>,
2639 - );
2507 + id="two"
2508 + onNestedUpdateScheduled={onNestedUpdateScheduledTwo}>
2509 + <>
2510 + <Component />
2511 + <React.Profiler
2512 + id="three"
2513 + onNestedUpdateScheduled={onNestedUpdateScheduledThree}
2514 + />
2515 + </>
2516 + </React.Profiler>
2517 + </React.Profiler>,
2518 + );
2519 + });
2520
2641 - ReactNoop.renderLegacySyncRoot(
2642 - <React.Profiler
2643 - id="test"
2644 - onNestedUpdateScheduled={onNestedUpdateScheduled}>
2645 - <div>update</div>
2646 - </React.Profiler>,
2647 - );
2648 -
2649 - expect(onNestedUpdateScheduled).not.toHaveBeenCalled();
2650 - });
2651 -
2652 - it('is not called when the root API is used to schedule an update', () => {
2653 - const onNestedUpdateScheduled = jest.fn();
2654 -
2655 - ReactNoop.render(
2656 - <React.Profiler
2657 - id="test"
2658 - onNestedUpdateScheduled={onNestedUpdateScheduled}>
2659 - <div>initial</div>
2660 - </React.Profiler>,
2661 - );
2662 -
2663 - ReactNoop.render(
2664 - <React.Profiler
2665 - id="test"
2666 - onNestedUpdateScheduled={onNestedUpdateScheduled}>
2667 - <div>update</div>
2668 - </React.Profiler>,
2669 - );
2670 -
2671 - expect(onNestedUpdateScheduled).not.toHaveBeenCalled();
2672 - });
2673 -
2674 - it('is called when a function component schedules an update during a layout effect', () => {
2675 - function Component() {
2676 - const [didMount, setDidMount] = React.useState(false);
2677 - React.useLayoutEffect(() => {
2678 - setDidMount(true);
2679 - }, []);
2680 - Scheduler.unstable_yieldValue(`Component:${didMount}`);
2681 - return didMount;
2682 - }
2683 -
2684 - const interactionCreation = {
2685 - id: 0,
2686 - name: 'creation event',
2687 - timestamp: Scheduler.unstable_now(),
2688 - };
2689 -
2690 - const onNestedUpdateScheduled = jest.fn();
2691 -
2692 - SchedulerTracing.unstable_trace(
2693 - interactionCreation.name,
2694 - Scheduler.unstable_now(),
2695 - () => {
2696 - ReactNoop.act(() => {
2697 - ReactNoop.render(
2698 - <React.Profiler
2699 - id="test"
2700 - onNestedUpdateScheduled={onNestedUpdateScheduled}>
2701 - <Component />
2702 - </React.Profiler>,
2703 - );
2704 - });
2705 - },
2706 - );
2707 -
2708 - expect(Scheduler).toHaveYielded(['Component:false', 'Component:true']);
2709 - expect(onNestedUpdateScheduled).toHaveBeenCalledTimes(1);
2710 - expect(onNestedUpdateScheduled.mock.calls[0][0]).toBe('test');
2711 - if (ReactFeatureFlags.enableSchedulerTracing) {
2712 - expect(onNestedUpdateScheduled.mock.calls[0][1]).toMatchInteractions([
2713 - interactionCreation,
2714 - ]);
2715 - }
2716 - });
2717 -
2718 - it('is called when a function component schedules a batched update during a layout effect', () => {
2719 - function Component() {
2720 - const [didMount, setDidMount] = React.useState(false);
2721 - React.useLayoutEffect(() => {
2722 - ReactNoop.batchedUpdates(() => {
2723 - setDidMount(true);
2724 - });
2725 - }, []);
2726 - Scheduler.unstable_yieldValue(`Component:${didMount}`);
2727 - return didMount;
2728 - }
2729 -
2730 - const onNestedUpdateScheduled = jest.fn();
2731 - const onRender = jest.fn();
2732 -
2733 - ReactNoop.render(
2734 - <React.Profiler
2735 - id="root"
2736 - onNestedUpdateScheduled={onNestedUpdateScheduled}
2737 - onRender={onRender}>
2738 - <Component />
2739 - </React.Profiler>,
2740 - );
2741 - expect(Scheduler).toFlushAndYield([
2742 - 'Component:false',
2743 - 'Component:true',
2744 - ]);
2745 -
2746 - expect(onRender).toHaveBeenCalledTimes(2);
2747 - expect(onRender.mock.calls[0][1]).toBe('mount');
2748 - expect(onRender.mock.calls[1][1]).toBe('nested-update');
2749 -
2750 - expect(onNestedUpdateScheduled).toHaveBeenCalledTimes(1);
2751 - expect(onNestedUpdateScheduled.mock.calls[0][0]).toBe('root');
2752 - });
2753 -
2754 - it('bubbles up and calls all ancestor Profilers', () => {
2755 - function Component() {
2756 - const [didMount, setDidMount] = React.useState(false);
2757 - React.useLayoutEffect(() => {
2758 - setDidMount(true);
2759 - }, []);
2760 - Scheduler.unstable_yieldValue(`Component:${didMount}`);
2761 - return didMount;
2762 - }
2763 - const onNestedUpdateScheduledOne = jest.fn();
2764 - const onNestedUpdateScheduledTwo = jest.fn();
2765 - const onNestedUpdateScheduledThree = jest.fn();
2766 -
2767 - ReactNoop.act(() => {
2768 - ReactNoop.render(
2769 - <React.Profiler
2770 - id="one"
2771 - onNestedUpdateScheduled={onNestedUpdateScheduledOne}>
2772 - <React.Profiler
2773 - id="two"
2774 - onNestedUpdateScheduled={onNestedUpdateScheduledTwo}>
2775 - <>
2776 - <Component />
2777 - <React.Profiler
2778 - id="three"
2779 - onNestedUpdateScheduled={onNestedUpdateScheduledThree}
2780 - />
2781 - </>
2782 - </React.Profiler>
2783 - </React.Profiler>,
2784 - );
2785 - });
2786 -
2787 - expect(Scheduler).toHaveYielded(['Component:false', 'Component:true']);
2788 - expect(onNestedUpdateScheduledOne).toHaveBeenCalledTimes(1);
2789 - expect(onNestedUpdateScheduledOne.mock.calls[0][0]).toBe('one');
2790 - expect(onNestedUpdateScheduledTwo).toHaveBeenCalledTimes(1);
2791 - expect(onNestedUpdateScheduledTwo.mock.calls[0][0]).toBe('two');
2792 - expect(onNestedUpdateScheduledThree).not.toHaveBeenCalled();
2793 - });
2794 -
2795 - it('is not called when an update is scheduled for another doort during a layout effect', () => {
2796 - const setStateRef = React.createRef(null);
2797 -
2798 - function ComponentRootOne() {
2799 - const [state, setState] = React.useState(false);
2800 - setStateRef.current = setState;
2801 - Scheduler.unstable_yieldValue(`ComponentRootOne:${state}`);
2802 - return state;
2803 - }
2804 -
2805 - function ComponentRootTwo() {
2806 - React.useLayoutEffect(() => {
2807 - setStateRef.current(true);
2808 - }, []);
2809 - Scheduler.unstable_yieldValue('ComponentRootTwo');
2810 - return null;
2811 - }
2812 -
2813 - const interactionCreation = {
2814 - id: 0,
2815 - name: 'creation event',
2816 - timestamp: Scheduler.unstable_now(),
2817 - };
2818 -
2819 - const onNestedUpdateScheduled = jest.fn();
2820 -
2821 - SchedulerTracing.unstable_trace(
2822 - interactionCreation.name,
2823 - Scheduler.unstable_now(),
2824 - () => {
2825 - ReactNoop.act(() => {
2826 - ReactNoop.renderToRootWithID(
2827 - <React.Profiler
2828 - id="test"
2829 - onNestedUpdateScheduled={onNestedUpdateScheduled}>
2830 - <ComponentRootOne />
2831 - </React.Profiler>,
2832 - 1,
2833 - );
2834 -
2835 - ReactNoop.renderToRootWithID(
2836 - <React.Profiler
2837 - id="test"
2838 - onNestedUpdateScheduled={onNestedUpdateScheduled}>
2839 - <ComponentRootTwo />
2840 - </React.Profiler>,
2841 - 2,
2842 - );
2843 - });
2844 - },
2845 - );
2846 -
2847 - expect(Scheduler).toHaveYielded([
2848 - 'ComponentRootOne:false',
2849 - 'ComponentRootTwo',
2850 - 'ComponentRootOne:true',
2851 - ]);
2852 - expect(onNestedUpdateScheduled).not.toHaveBeenCalled();
2853 - });
2854 -
2855 - it('is not called when a function component schedules an update during a passive effect', () => {
2856 - function Component() {
2857 - const [didMount, setDidMount] = React.useState(false);
2858 - React.useEffect(() => {
2859 - setDidMount(true);
2860 - }, []);
2861 - Scheduler.unstable_yieldValue(`Component:${didMount}`);
2862 - return didMount;
2863 - }
2864 -
2865 - const onNestedUpdateScheduled = jest.fn();
2866 -
2867 - ReactNoop.act(() => {
2868 - ReactNoop.render(
2869 - <React.Profiler
2870 - id="test"
2871 - onNestedUpdateScheduled={onNestedUpdateScheduled}>
2872 - <Component />
2873 - </React.Profiler>,
2874 - );
2875 - });
2876 -
2877 - expect(Scheduler).toHaveYielded(['Component:false', 'Component:true']);
2878 - expect(onNestedUpdateScheduled).not.toHaveBeenCalled();
2879 - });
2880 -
2881 - it('is not called when a function component schedules an update outside of render', () => {
2882 - const updateFnRef = React.createRef(null);
2883 -
2884 - function Component() {
2885 - const [state, setState] = React.useState(false);
2886 - updateFnRef.current = () => setState(true);
2887 - Scheduler.unstable_yieldValue(`Component:${state}`);
2888 - return state;
2889 - }
2890 -
2891 - const onNestedUpdateScheduled = jest.fn();
2892 -
2893 - ReactNoop.act(() => {
2894 - ReactNoop.render(
2895 - <React.Profiler
2896 - id="test"
2897 - onNestedUpdateScheduled={onNestedUpdateScheduled}>
2898 - <Component />
2899 - </React.Profiler>,
2900 - );
2901 - });
2902 - expect(Scheduler).toHaveYielded(['Component:false']);
2903 -
2904 - ReactNoop.act(() => {
2905 - updateFnRef.current();
2906 - });
2907 - expect(Scheduler).toHaveYielded(['Component:true']);
2908 - expect(onNestedUpdateScheduled).not.toHaveBeenCalled();
2909 - });
2910 -
2911 - it('it is not called when a component schedules an update during render', () => {
2912 - function Component() {
2913 - const [state, setState] = React.useState(false);
2914 - if (state === false) {
2915 - setState(true);
2916 - }
2917 - Scheduler.unstable_yieldValue(`Component:${state}`);
2918 - return state;
2919 - }
2920 -
2921 - const onNestedUpdateScheduled = jest.fn();
2922 -
2923 - ReactNoop.act(() => {
2924 - ReactNoop.render(
2925 - <React.Profiler
2926 - id="test"
2927 - onNestedUpdateScheduled={onNestedUpdateScheduled}>
2928 - <Component />
2929 - </React.Profiler>,
2930 - );
2931 - });
2932 -
2933 - expect(Scheduler).toHaveYielded(['Component:false', 'Component:true']);
2934 - expect(onNestedUpdateScheduled).not.toHaveBeenCalled();
2935 - });
2936 -
2937 - it('it is called when a component schedules an update from a ref callback', () => {
2938 - function Component({mountChild}) {
2939 - const [refAttached, setRefAttached] = React.useState(false);
2940 - const [refDetached, setRefDetached] = React.useState(false);
2941 - const refSetter = React.useCallback(ref => {
2942 - if (ref !== null) {
2943 - setRefAttached(true);
2944 - } else {
2945 - setRefDetached(true);
2946 - }
2947 - }, []);
2948 - Scheduler.unstable_yieldValue(
2949 - `Component:${refAttached}:${refDetached}`,
2950 - );
2951 - return mountChild ? <div ref={refSetter} /> : null;
2952 - }
2953 -
2954 - const onNestedUpdateScheduled = jest.fn();
2955 -
2956 - const interactionCreation = {
2957 - id: 0,
2958 - name: 'creation event',
2959 - timestamp: Scheduler.unstable_now(),
2960 - };
2961 -
2962 - SchedulerTracing.unstable_trace(
2963 - interactionCreation.name,
2964 - Scheduler.unstable_now(),
2965 - () => {
2966 - ReactNoop.act(() => {
2967 - ReactNoop.render(

This file is too large to show in full.

packages/react/src/__tests__/ReactProfilerDOM-test.internal.js deleted
-129
@@ -1,129 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @emails react-core
8 - */
9 -
10 -'use strict';
11 -
12 -let React;
13 -let ReactFeatureFlags;
14 -let ReactDOM;
15 -let SchedulerTracing;
16 -let Scheduler;
17 -
18 -function loadModules() {
19 - ReactFeatureFlags = require('shared/ReactFeatureFlags');
20 -
21 - ReactFeatureFlags.enableProfilerTimer = true;
22 - ReactFeatureFlags.enableSchedulerTracing = true;
23 -
24 - React = require('react');
25 - SchedulerTracing = require('scheduler/tracing');
26 - ReactDOM = require('react-dom');
27 - Scheduler = require('scheduler');
28 -}
29 -
30 -describe('ProfilerDOM', () => {
31 - let onInteractionScheduledWorkCompleted;
32 - let onInteractionTraced;
33 -
34 - beforeEach(() => {
35 - loadModules();
36 -
37 - onInteractionScheduledWorkCompleted = jest.fn();
38 - onInteractionTraced = jest.fn();
39 -
40 - // Verify interaction subscriber methods are called as expected.
41 - SchedulerTracing.unstable_subscribe({
42 - onInteractionScheduledWorkCompleted,
43 - onInteractionTraced,
44 - onWorkCanceled: () => {},
45 - onWorkScheduled: () => {},
46 - onWorkStarted: () => {},
47 - onWorkStopped: () => {},
48 - });
49 - });
50 -
51 - function Text(props) {
52 - Scheduler.unstable_yieldValue(props.text);
53 - return props.text;
54 - }
55 -
56 - // @gate experimental
57 - it('should correctly trace interactions for async roots', async () => {
58 - let resolve;
59 - let thenable = {
60 - then(res) {
61 - resolve = () => {
62 - thenable = null;
63 - res();
64 - };
65 - },
66 - };
67 -
68 - function Async() {
69 - if (thenable !== null) {
70 - Scheduler.unstable_yieldValue('Suspend! [Async]');
71 - throw thenable;
72 - }
73 - Scheduler.unstable_yieldValue('Async');
74 - return 'Async';
75 - }
76 -
77 - const element = document.createElement('div');
78 - const root = ReactDOM.createRoot(element);
79 -
80 - let interaction;
81 - let wrappedResolve;
82 - SchedulerTracing.unstable_trace('initial_event', performance.now(), () => {
83 - const interactions = SchedulerTracing.unstable_getCurrent();
84 - expect(interactions.size).toBe(1);
85 - interaction = Array.from(interactions)[0];
86 -
87 - root.render(
88 - <React.Suspense fallback={<Text text="Loading..." />}>
89 - <Async />
90 - </React.Suspense>,
91 - );
92 -
93 - wrappedResolve = SchedulerTracing.unstable_wrap(() => resolve());
94 - });
95 -
96 - // Render, suspend, and commit fallback
97 - expect(Scheduler).toFlushAndYield(['Suspend! [Async]', 'Loading...']);
98 - expect(element.textContent).toEqual('Loading...');
99 -
100 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
101 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
102 - interaction,
103 - );
104 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
105 -
106 - // Ping React to try rendering again
107 - wrappedResolve();
108 -
109 - // Complete the tree without committing it
110 - expect(Scheduler).toFlushAndYieldThrough(['Async']);
111 - // Still showing the fallback
112 - expect(element.textContent).toEqual('Loading...');
113 -
114 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
115 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
116 - interaction,
117 - );
118 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
119 -
120 - expect(Scheduler).toFlushAndYield([]);
121 - expect(element.textContent).toEqual('Async');
122 -
123 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
124 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
125 - interaction,
126 - );
127 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
128 - });
129 -});
packages/react/src/__tests__/ReactProfilerDevToolsIntegration-test.internal.js
+2 -42
@@ -15,7 +15,6 @@ describe('ReactProfiler DevTools integration', () => {
15 let ReactFeatureFlags;
16 let ReactTestRenderer;
17 let Scheduler;
18 - let SchedulerTracing;
18 let AdvanceTime;
19 let hook;
20
@@ -31,9 +30,7 @@ describe('ReactProfiler DevTools integration', () => {
30
31 ReactFeatureFlags = require('shared/ReactFeatureFlags');
32 ReactFeatureFlags.enableProfilerTimer = true;
34 - ReactFeatureFlags.enableSchedulerTracing = true;
33 Scheduler = require('scheduler');
36 - SchedulerTracing = require('scheduler/tracing');
34 React = require('react');
35 ReactTestRenderer = require('react-test-renderer');
36
@@ -76,15 +73,7 @@ describe('ReactProfiler DevTools integration', () => {
73 // The time spent in App (above the Profiler) won't be included in the durations,
74 // But needs to be accounted for in the offset times.
75 expect(onRender).toHaveBeenCalledTimes(1);
79 - expect(onRender).toHaveBeenCalledWith(
80 - 'Profiler',
81 - 'mount',
82 - 10,
83 - 10,
84 - 2,
85 - 12,
86 - new Set(),
87 - );
76 + expect(onRender).toHaveBeenCalledWith('Profiler', 'mount', 10, 10, 2, 12);
77 onRender.mockClear();
78
79 // Measure unobservable timing required by the DevTools profiler.
@@ -101,15 +90,7 @@ describe('ReactProfiler DevTools integration', () => {
90 // The time spent in App (above the Profiler) won't be included in the durations,
91 // But needs to be accounted for in the offset times.
92 expect(onRender).toHaveBeenCalledTimes(1);
104 - expect(onRender).toHaveBeenCalledWith(
105 - 'Profiler',
106 - 'update',
107 - 6,
108 - 13,
109 - 14,
110 - 20,
111 - new Set(),
112 - );
93 + expect(onRender).toHaveBeenCalledWith('Profiler', 'update', 6, 13, 14, 20);
94
95 // Measure unobservable timing required by the DevTools profiler.
96 // At this point, the base time should include both:
@@ -157,27 +138,6 @@ describe('ReactProfiler DevTools integration', () => {
138 ).toBe(7);
139 });
140
160 - it('should store traced interactions on the HostNode so DevTools can access them', () => {
161 - // Render without an interaction
162 - const rendered = ReactTestRenderer.create(<div />);
163 -
164 - const root = rendered.root._currentFiber().return;
165 - expect(root.stateNode.memoizedInteractions).toContainNoInteractions();
166 -
167 - Scheduler.unstable_advanceTime(10);
168 -
169 - const eventTime = Scheduler.unstable_now();
170 -
171 - // Render with an interaction
172 - SchedulerTracing.unstable_trace('some event', eventTime, () => {
173 - rendered.update(<div />);
174 - });
175 -
176 - expect(root.stateNode.memoizedInteractions).toMatchInteractions([
177 - {name: 'some event', timestamp: eventTime},
178 - ]);
179 - });
180 -
141 // @gate experimental || !enableSyncDefaultUpdates
142 it('regression test: #17159', () => {
143 function Text({text}) {
packages/react/src/__tests__/__snapshots__/ReactProfiler-test.internal.js.snap
+8 -72
@@ -1,6 +1,6 @@
1 // Jest Snapshot v1, https://goo.gl/fbAQLP
2
3 -exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracing:disabled enableProfilerTimer:disabled should render children 1`] = `
3 +exports[`Profiler works in profiling and non-profiling bundles enableProfilerTimer:disabled should render children 1`] = `
4 <div>
5 <span>
6 outside span
@@ -14,11 +14,11 @@ exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTr
14 </div>
15 `;
16
17 -exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracing:disabled enableProfilerTimer:disabled should support an empty Profiler (with no children) 1`] = `null`;
17 +exports[`Profiler works in profiling and non-profiling bundles enableProfilerTimer:disabled should support an empty Profiler (with no children) 1`] = `null`;
18
19 -exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracing:disabled enableProfilerTimer:disabled should support an empty Profiler (with no children) 2`] = `<div />`;
19 +exports[`Profiler works in profiling and non-profiling bundles enableProfilerTimer:disabled should support an empty Profiler (with no children) 2`] = `<div />`;
20
21 -exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracing:disabled enableProfilerTimer:disabled should support nested Profilers 1`] = `
21 +exports[`Profiler works in profiling and non-profiling bundles enableProfilerTimer:disabled should support nested Profilers 1`] = `
22 Array [
23 <div>
24 outer function component
@@ -32,7 +32,7 @@ Array [
32 ]
33 `;
34
35 -exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracing:disabled enableProfilerTimer:enabled should render children 1`] = `
35 +exports[`Profiler works in profiling and non-profiling bundles enableProfilerTimer:enabled should render children 1`] = `
36 <div>
37 <span>
38 outside span
@@ -46,75 +46,11 @@ exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTr
46 </div>
47 `;
48
49 -exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracing:disabled enableProfilerTimer:enabled should support an empty Profiler (with no children) 1`] = `null`;
49 +exports[`Profiler works in profiling and non-profiling bundles enableProfilerTimer:enabled should support an empty Profiler (with no children) 1`] = `null`;
50
51 -exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracing:disabled enableProfilerTimer:enabled should support an empty Profiler (with no children) 2`] = `<div />`;
51 +exports[`Profiler works in profiling and non-profiling bundles enableProfilerTimer:enabled should support an empty Profiler (with no children) 2`] = `<div />`;
52
53 -exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracing:disabled enableProfilerTimer:enabled should support nested Profilers 1`] = `
54 -Array [
55 - <div>
56 - outer function component
57 - </div>,
58 - <block>
59 - inner class component
60 - </block>,
61 - <span>
62 - inner span
63 - </span>,
64 -]
65 -`;
66 -
67 -exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracing:enabled enableProfilerTimer:disabled should render children 1`] = `
68 -<div>
69 - <span>
70 - outside span
71 - </span>
72 - <span>
73 - inside span
74 - </span>
75 - <span>
76 - function component
77 - </span>
78 -</div>
79 -`;
80 -
81 -exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracing:enabled enableProfilerTimer:disabled should support an empty Profiler (with no children) 1`] = `null`;
82 -
83 -exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracing:enabled enableProfilerTimer:disabled should support an empty Profiler (with no children) 2`] = `<div />`;
84 -
85 -exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracing:enabled enableProfilerTimer:disabled should support nested Profilers 1`] = `
86 -Array [
87 - <div>
88 - outer function component
89 - </div>,
90 - <block>
91 - inner class component
92 - </block>,
93 - <span>
94 - inner span
95 - </span>,
96 -]
97 -`;
98 -
99 -exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracing:enabled enableProfilerTimer:enabled should render children 1`] = `
100 -<div>
101 - <span>
102 - outside span
103 - </span>
104 - <span>
105 - inside span
106 - </span>
107 - <span>
108 - function component
109 - </span>
110 -</div>
111 -`;
112 -
113 -exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracing:enabled enableProfilerTimer:enabled should support an empty Profiler (with no children) 1`] = `null`;
114 -
115 -exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracing:enabled enableProfilerTimer:enabled should support an empty Profiler (with no children) 2`] = `<div />`;
116 -
117 -exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracing:enabled enableProfilerTimer:enabled should support nested Profilers 1`] = `
53 +exports[`Profiler works in profiling and non-profiling bundles enableProfilerTimer:enabled should support nested Profilers 1`] = `
54 Array [
55 <div>
56 outer function component
packages/react/src/forks/ReactSharedInternals.umd.js
-2
@@ -7,7 +7,6 @@
7
8 import assign from 'object-assign';
9 import * as Scheduler from 'scheduler';
10 -import * as SchedulerTracing from 'scheduler/tracing';
10 import ReactCurrentDispatcher from '../ReactCurrentDispatcher';
11 import ReactCurrentOwner from '../ReactCurrentOwner';
12 import ReactDebugCurrentFrame from '../ReactDebugCurrentFrame';
@@ -28,7 +27,6 @@ const ReactSharedInternals = {
27 // This re-export is only required for UMD bundles;
28 // CJS bundles use the shared NPM package.
29 Scheduler,
31 - SchedulerTracing,
30 };
31
32 if (__DEV__) {
packages/scheduler/npm/tracing-profiling.js deleted
-7
@@ -1,7 +0,0 @@
1 -'use strict';
2 -
3 -if (process.env.NODE_ENV === 'production') {
4 - module.exports = require('./cjs/scheduler-tracing.profiling.min.js');
5 -} else {
6 - module.exports = require('./cjs/scheduler-tracing.development.js');
7 -}
packages/scheduler/npm/tracing.js deleted
-7
@@ -1,7 +0,0 @@
1 -'use strict';
2 -
3 -if (process.env.NODE_ENV === 'production') {
4 - module.exports = require('./cjs/scheduler-tracing.production.min.js');
5 -} else {
6 - module.exports = require('./cjs/scheduler-tracing.development.js');
7 -}
packages/scheduler/npm/umd/scheduler-tracing.development.js deleted
-80
@@ -1,80 +0,0 @@
1 -/**
2 - * @license React
3 - *
4 - * Copyright (c) Facebook, Inc. and its affiliates.
5 - *
6 - * This source code is licensed under the MIT license found in the
7 - * LICENSE file in the root directory of this source tree.
8 - */
9 -
10 -'use strict';
11 -
12 -(function(global, factory) {
13 - // eslint-disable-next-line no-unused-expressions
14 - typeof exports === 'object' && typeof module !== 'undefined'
15 - ? (module.exports = factory(require('react')))
16 - : typeof define === 'function' && define.amd // eslint-disable-line no-undef
17 - ? define(['react'], factory) // eslint-disable-line no-undef
18 - : (global.SchedulerTracing = factory(global));
19 -})(this, function(global) {
20 - function unstable_clear() {
21 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_clear.apply(
22 - this,
23 - arguments
24 - );
25 - }
26 -
27 - function unstable_getCurrent() {
28 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_getCurrent.apply(
29 - this,
30 - arguments
31 - );
32 - }
33 -
34 - function unstable_getThreadID() {
35 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_getThreadID.apply(
36 - this,
37 - arguments
38 - );
39 - }
40 -
41 - function unstable_subscribe() {
42 - // eslint-disable-next-line max-len
43 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_subscribe.apply(
44 - this,
45 - arguments
46 - );
47 - }
48 -
49 - function unstable_trace() {
50 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_trace.apply(
51 - this,
52 - arguments
53 - );
54 - }
55 -
56 - function unstable_unsubscribe() {
57 - // eslint-disable-next-line max-len
58 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_unsubscribe.apply(
59 - this,
60 - arguments
61 - );
62 - }
63 -
64 - function unstable_wrap() {
65 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_wrap.apply(
66 - this,
67 - arguments
68 - );
69 - }
70 -
71 - return Object.freeze({
72 - unstable_clear: unstable_clear,
73 - unstable_getCurrent: unstable_getCurrent,
74 - unstable_getThreadID: unstable_getThreadID,
75 - unstable_subscribe: unstable_subscribe,
76 - unstable_trace: unstable_trace,
77 - unstable_unsubscribe: unstable_unsubscribe,
78 - unstable_wrap: unstable_wrap,
79 - });
80 -});
packages/scheduler/npm/umd/scheduler-tracing.production.min.js deleted
-80
@@ -1,80 +0,0 @@
1 -/**
2 - * @license React
3 - *
4 - * Copyright (c) Facebook, Inc. and its affiliates.
5 - *
6 - * This source code is licensed under the MIT license found in the
7 - * LICENSE file in the root directory of this source tree.
8 - */
9 -
10 -'use strict';
11 -
12 -(function(global, factory) {
13 - // eslint-disable-next-line no-unused-expressions
14 - typeof exports === 'object' && typeof module !== 'undefined'
15 - ? (module.exports = factory(require('react')))
16 - : typeof define === 'function' && define.amd // eslint-disable-line no-undef
17 - ? define(['react'], factory) // eslint-disable-line no-undef
18 - : (global.SchedulerTracing = factory(global));
19 -})(this, function(global) {
20 - function unstable_clear() {
21 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_clear.apply(
22 - this,
23 - arguments
24 - );
25 - }
26 -
27 - function unstable_getCurrent() {
28 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_getCurrent.apply(
29 - this,
30 - arguments
31 - );
32 - }
33 -
34 - function unstable_getThreadID() {
35 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_getThreadID.apply(
36 - this,
37 - arguments
38 - );
39 - }
40 -
41 - function unstable_subscribe() {
42 - // eslint-disable-next-line max-len
43 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_subscribe.apply(
44 - this,
45 - arguments
46 - );
47 - }
48 -
49 - function unstable_trace() {
50 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_trace.apply(
51 - this,
52 - arguments
53 - );
54 - }
55 -
56 - function unstable_unsubscribe() {
57 - // eslint-disable-next-line max-len
58 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_unsubscribe.apply(
59 - this,
60 - arguments
61 - );
62 - }
63 -
64 - function unstable_wrap() {
65 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_wrap.apply(
66 - this,
67 - arguments
68 - );
69 - }
70 -
71 - return Object.freeze({
72 - unstable_clear: unstable_clear,
73 - unstable_getCurrent: unstable_getCurrent,
74 - unstable_getThreadID: unstable_getThreadID,
75 - unstable_subscribe: unstable_subscribe,
76 - unstable_trace: unstable_trace,
77 - unstable_unsubscribe: unstable_unsubscribe,
78 - unstable_wrap: unstable_wrap,
79 - });
80 -});
packages/scheduler/npm/umd/scheduler-tracing.profiling.min.js deleted
-80
@@ -1,80 +0,0 @@
1 -/**
2 - * @license React
3 - *
4 - * Copyright (c) Facebook, Inc. and its affiliates.
5 - *
6 - * This source code is licensed under the MIT license found in the
7 - * LICENSE file in the root directory of this source tree.
8 - */
9 -
10 -'use strict';
11 -
12 -(function(global, factory) {
13 - // eslint-disable-next-line no-unused-expressions
14 - typeof exports === 'object' && typeof module !== 'undefined'
15 - ? (module.exports = factory(require('react')))
16 - : typeof define === 'function' && define.amd // eslint-disable-line no-undef
17 - ? define(['react'], factory) // eslint-disable-line no-undef
18 - : (global.SchedulerTracing = factory(global));
19 -})(this, function(global) {
20 - function unstable_clear() {
21 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_clear.apply(
22 - this,
23 - arguments
24 - );
25 - }
26 -
27 - function unstable_getCurrent() {
28 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_getCurrent.apply(
29 - this,
30 - arguments
31 - );
32 - }
33 -
34 - function unstable_getThreadID() {
35 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_getThreadID.apply(
36 - this,
37 - arguments
38 - );
39 - }
40 -
41 - function unstable_subscribe() {
42 - // eslint-disable-next-line max-len
43 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_subscribe.apply(
44 - this,
45 - arguments
46 - );
47 - }
48 -
49 - function unstable_trace() {
50 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_trace.apply(
51 - this,
52 - arguments
53 - );
54 - }
55 -
56 - function unstable_unsubscribe() {
57 - // eslint-disable-next-line max-len
58 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_unsubscribe.apply(
59 - this,
60 - arguments
61 - );
62 - }
63 -
64 - function unstable_wrap() {
65 - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracing.unstable_wrap.apply(
66 - this,
67 - arguments
68 - );
69 - }
70 -
71 - return Object.freeze({
72 - unstable_clear: unstable_clear,
73 - unstable_getCurrent: unstable_getCurrent,
74 - unstable_getThreadID: unstable_getThreadID,
75 - unstable_subscribe: unstable_subscribe,
76 - unstable_trace: unstable_trace,
77 - unstable_unsubscribe: unstable_unsubscribe,
78 - unstable_wrap: unstable_wrap,
79 - });
80 -});
packages/scheduler/package.json
-2
@@ -25,8 +25,6 @@
25 "README.md",
26 "build-info.json",
27 "index.js",
28 - "tracing.js",
29 - "tracing-profiling.js",
28 "unstable_mock.js",
29 "unstable_no_dom.js",
30 "unstable_post_task.js",
packages/scheduler/src/Tracing.js deleted
-264
@@ -1,264 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import {enableSchedulerTracing} from 'shared/ReactFeatureFlags';
11 -
12 -export type Interaction = {|
13 - __count: number,
14 - id: number,
15 - name: string,
16 - timestamp: number,
17 -|};
18 -
19 -export type Subscriber = {
20 - // A new interaction has been created via the trace() method.
21 - onInteractionTraced: (interaction: Interaction) => void,
22 -
23 - // All scheduled async work for an interaction has finished.
24 - onInteractionScheduledWorkCompleted: (interaction: Interaction) => void,
25 -
26 - // New async work has been scheduled for a set of interactions.
27 - // When this work is later run, onWorkStarted/onWorkStopped will be called.
28 - // A batch of async/yieldy work may be scheduled multiple times before completing.
29 - // In that case, onWorkScheduled may be called more than once before onWorkStopped.
30 - // Work is scheduled by a "thread" which is identified by a unique ID.
31 - onWorkScheduled: (interactions: Set<Interaction>, threadID: number) => void,
32 -
33 - // A batch of scheduled work has been canceled.
34 - // Work is done by a "thread" which is identified by a unique ID.
35 - onWorkCanceled: (interactions: Set<Interaction>, threadID: number) => void,
36 -
37 - // A batch of work has started for a set of interactions.
38 - // When this work is complete, onWorkStopped will be called.
39 - // Work is not always completed synchronously; yielding may occur in between.
40 - // A batch of async/yieldy work may also be re-started before completing.
41 - // In that case, onWorkStarted may be called more than once before onWorkStopped.
42 - // Work is done by a "thread" which is identified by a unique ID.
43 - onWorkStarted: (interactions: Set<Interaction>, threadID: number) => void,
44 -
45 - // A batch of work has completed for a set of interactions.
46 - // Work is done by a "thread" which is identified by a unique ID.
47 - onWorkStopped: (interactions: Set<Interaction>, threadID: number) => void,
48 - ...
49 -};
50 -
51 -export type InteractionsRef = {|current: Set<Interaction>|};
52 -
53 -export type SubscriberRef = {|current: Subscriber | null|};
54 -
55 -const DEFAULT_THREAD_ID = 0;
56 -
57 -// Counters used to generate unique IDs.
58 -let interactionIDCounter: number = 0;
59 -let threadIDCounter: number = 0;
60 -
61 -// Set of currently traced interactions.
62 -// Interactions "stack"–
63 -// Meaning that newly traced interactions are appended to the previously active set.
64 -// When an interaction goes out of scope, the previous set (if any) is restored.
65 -let interactionsRef: InteractionsRef = (null: any);
66 -
67 -// Listener(s) to notify when interactions begin and end.
68 -let subscriberRef: SubscriberRef = (null: any);
69 -
70 -if (enableSchedulerTracing) {
71 - interactionsRef = {
72 - current: new Set(),
73 - };
74 - subscriberRef = {
75 - current: null,
76 - };
77 -}
78 -
79 -export {interactionsRef as __interactionsRef, subscriberRef as __subscriberRef};
80 -
81 -export function unstable_clear(callback: Function): any {
82 - if (!enableSchedulerTracing) {
83 - return callback();
84 - }
85 -
86 - const prevInteractions = interactionsRef.current;
87 - interactionsRef.current = new Set();
88 -
89 - try {
90 - return callback();
91 - } finally {
92 - interactionsRef.current = prevInteractions;
93 - }
94 -}
95 -
96 -export function unstable_getCurrent(): Set<Interaction> | null {
97 - if (!enableSchedulerTracing) {
98 - return null;
99 - } else {
100 - return interactionsRef.current;
101 - }
102 -}
103 -
104 -export function unstable_getThreadID(): number {
105 - return ++threadIDCounter;
106 -}
107 -
108 -export function unstable_trace(
109 - name: string,
110 - timestamp: number,
111 - callback: Function,
112 - threadID: number = DEFAULT_THREAD_ID,
113 -): any {
114 - if (!enableSchedulerTracing) {
115 - return callback();
116 - }
117 -
118 - const interaction: Interaction = {
119 - __count: 1,
120 - id: interactionIDCounter++,
121 - name,
122 - timestamp,
123 - };
124 -
125 - const prevInteractions = interactionsRef.current;
126 -
127 - // Traced interactions should stack/accumulate.
128 - // To do that, clone the current interactions.
129 - // The previous set will be restored upon completion.
130 - const interactions = new Set(prevInteractions);
131 - interactions.add(interaction);
132 - interactionsRef.current = interactions;
133 -
134 - const subscriber = subscriberRef.current;
135 - let returnValue;
136 -
137 - try {
138 - if (subscriber !== null) {
139 - subscriber.onInteractionTraced(interaction);
140 - }
141 - } finally {
142 - try {
143 - if (subscriber !== null) {
144 - subscriber.onWorkStarted(interactions, threadID);
145 - }
146 - } finally {
147 - try {
148 - returnValue = callback();
149 - } finally {
150 - interactionsRef.current = prevInteractions;
151 -
152 - try {
153 - if (subscriber !== null) {
154 - subscriber.onWorkStopped(interactions, threadID);
155 - }
156 - } finally {
157 - interaction.__count--;
158 -
159 - // If no async work was scheduled for this interaction,
160 - // Notify subscribers that it's completed.
161 - if (subscriber !== null && interaction.__count === 0) {
162 - subscriber.onInteractionScheduledWorkCompleted(interaction);
163 - }
164 - }
165 - }
166 - }
167 - }
168 -
169 - return returnValue;
170 -}
171 -
172 -export function unstable_wrap(
173 - callback: Function,
174 - threadID: number = DEFAULT_THREAD_ID,
175 -): Function {
176 - if (!enableSchedulerTracing) {
177 - return callback;
178 - }
179 -
180 - const wrappedInteractions = interactionsRef.current;
181 -
182 - let subscriber = subscriberRef.current;
183 - if (subscriber !== null) {
184 - subscriber.onWorkScheduled(wrappedInteractions, threadID);
185 - }
186 -
187 - // Update the pending async work count for the current interactions.
188 - // Update after calling subscribers in case of error.
189 - wrappedInteractions.forEach(interaction => {
190 - interaction.__count++;
191 - });
192 -
193 - let hasRun = false;
194 -
195 - function wrapped() {
196 - const prevInteractions = interactionsRef.current;
197 - interactionsRef.current = wrappedInteractions;
198 -
199 - subscriber = subscriberRef.current;
200 -
201 - try {
202 - let returnValue;
203 -
204 - try {
205 - if (subscriber !== null) {
206 - subscriber.onWorkStarted(wrappedInteractions, threadID);
207 - }
208 - } finally {
209 - try {
210 - returnValue = callback.apply(undefined, arguments);
211 - } finally {
212 - interactionsRef.current = prevInteractions;
213 -
214 - if (subscriber !== null) {
215 - subscriber.onWorkStopped(wrappedInteractions, threadID);
216 - }
217 - }
218 - }
219 -
220 - return returnValue;
221 - } finally {
222 - if (!hasRun) {
223 - // We only expect a wrapped function to be executed once,
224 - // But in the event that it's executed more than once–
225 - // Only decrement the outstanding interaction counts once.
226 - hasRun = true;
227 -
228 - // Update pending async counts for all wrapped interactions.
229 - // If this was the last scheduled async work for any of them,
230 - // Mark them as completed.
231 - wrappedInteractions.forEach(interaction => {
232 - interaction.__count--;
233 -
234 - if (subscriber !== null && interaction.__count === 0) {
235 - subscriber.onInteractionScheduledWorkCompleted(interaction);
236 - }
237 - });
238 - }
239 - }
240 - }
241 -
242 - wrapped.cancel = function cancel() {
243 - subscriber = subscriberRef.current;
244 -
245 - try {
246 - if (subscriber !== null) {
247 - subscriber.onWorkCanceled(wrappedInteractions, threadID);
248 - }
249 - } finally {
250 - // Update pending async counts for all wrapped interactions.
251 - // If this was the last scheduled async work for any of them,
252 - // Mark them as completed.
253 - wrappedInteractions.forEach(interaction => {
254 - interaction.__count--;
255 -
256 - if (subscriber && interaction.__count === 0) {
257 - subscriber.onInteractionScheduledWorkCompleted(interaction);
258 - }
259 - });
260 - }
261 - };
262 -
263 - return wrapped;
264 -}
packages/scheduler/src/TracingSubscriptions.js deleted
-171
@@ -1,171 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import type {Interaction, Subscriber} from './Tracing';
11 -
12 -import {enableSchedulerTracing} from 'shared/ReactFeatureFlags';
13 -import {__subscriberRef} from './Tracing';
14 -
15 -let subscribers: Set<Subscriber> = (null: any);
16 -if (enableSchedulerTracing) {
17 - subscribers = new Set();
18 -}
19 -
20 -export function unstable_subscribe(subscriber: Subscriber): void {
21 - if (enableSchedulerTracing) {
22 - subscribers.add(subscriber);
23 -
24 - if (subscribers.size === 1) {
25 - __subscriberRef.current = {
26 - onInteractionScheduledWorkCompleted,
27 - onInteractionTraced,
28 - onWorkCanceled,
29 - onWorkScheduled,
30 - onWorkStarted,
31 - onWorkStopped,
32 - };
33 - }
34 - }
35 -}
36 -
37 -export function unstable_unsubscribe(subscriber: Subscriber): void {
38 - if (enableSchedulerTracing) {
39 - subscribers.delete(subscriber);
40 -
41 - if (subscribers.size === 0) {
42 - __subscriberRef.current = null;
43 - }
44 - }
45 -}
46 -
47 -function onInteractionTraced(interaction: Interaction): void {
48 - let didCatchError = false;
49 - let caughtError = null;
50 -
51 - subscribers.forEach(subscriber => {
52 - try {
53 - subscriber.onInteractionTraced(interaction);
54 - } catch (error) {
55 - if (!didCatchError) {
56 - didCatchError = true;
57 - caughtError = error;
58 - }
59 - }
60 - });
61 -
62 - if (didCatchError) {
63 - throw caughtError;
64 - }
65 -}
66 -
67 -function onInteractionScheduledWorkCompleted(interaction: Interaction): void {
68 - let didCatchError = false;
69 - let caughtError = null;
70 -
71 - subscribers.forEach(subscriber => {
72 - try {
73 - subscriber.onInteractionScheduledWorkCompleted(interaction);
74 - } catch (error) {
75 - if (!didCatchError) {
76 - didCatchError = true;
77 - caughtError = error;
78 - }
79 - }
80 - });
81 -
82 - if (didCatchError) {
83 - throw caughtError;
84 - }
85 -}
86 -
87 -function onWorkScheduled(
88 - interactions: Set<Interaction>,
89 - threadID: number,
90 -): void {
91 - let didCatchError = false;
92 - let caughtError = null;
93 -
94 - subscribers.forEach(subscriber => {
95 - try {
96 - subscriber.onWorkScheduled(interactions, threadID);
97 - } catch (error) {
98 - if (!didCatchError) {
99 - didCatchError = true;
100 - caughtError = error;
101 - }
102 - }
103 - });
104 -
105 - if (didCatchError) {
106 - throw caughtError;
107 - }
108 -}
109 -
110 -function onWorkStarted(interactions: Set<Interaction>, threadID: number): void {
111 - let didCatchError = false;
112 - let caughtError = null;
113 -
114 - subscribers.forEach(subscriber => {
115 - try {
116 - subscriber.onWorkStarted(interactions, threadID);
117 - } catch (error) {
118 - if (!didCatchError) {
119 - didCatchError = true;
120 - caughtError = error;
121 - }
122 - }
123 - });
124 -
125 - if (didCatchError) {
126 - throw caughtError;
127 - }
128 -}
129 -
130 -function onWorkStopped(interactions: Set<Interaction>, threadID: number): void {
131 - let didCatchError = false;
132 - let caughtError = null;
133 -
134 - subscribers.forEach(subscriber => {
135 - try {
136 - subscriber.onWorkStopped(interactions, threadID);
137 - } catch (error) {
138 - if (!didCatchError) {
139 - didCatchError = true;
140 - caughtError = error;
141 - }
142 - }
143 - });
144 -
145 - if (didCatchError) {
146 - throw caughtError;
147 - }
148 -}
149 -
150 -function onWorkCanceled(
151 - interactions: Set<Interaction>,
152 - threadID: number,
153 -): void {
154 - let didCatchError = false;
155 - let caughtError = null;
156 -
157 - subscribers.forEach(subscriber => {
158 - try {
159 - subscriber.onWorkCanceled(interactions, threadID);
160 - } catch (error) {
161 - if (!didCatchError) {
162 - didCatchError = true;
163 - caughtError = error;
164 - }
165 - }
166 - });
167 -
168 - if (didCatchError) {
169 - throw caughtError;
170 - }
171 -}
packages/scheduler/src/__tests__/SchedulerUMDBundle-test.internal.js
+2 -37
@@ -37,30 +37,10 @@ describe('Scheduling UMD bundle', () => {
37 global.MessageChannel = undefined;
38 });
39
40 - function filterPrivateKeys(name) {
41 - // Be very careful adding things to this filter!
42 - // It's easy to introduce bugs by doing it:
43 - // https://github.com/facebook/react/issues/14904
44 - switch (name) {
45 - case '__interactionsRef':
46 - case '__subscriberRef':
47 - // Don't forward these. (TODO: why?)
48 - return false;
49 - default:
50 - return true;
51 - }
52 - }
53 -
40 function validateForwardedAPIs(api, forwardedAPIs) {
55 - const apiKeys = Object.keys(api)
56 - .filter(filterPrivateKeys)
57 - .sort();
41 + const apiKeys = Object.keys(api).sort();
42 forwardedAPIs.forEach(forwardedAPI => {
59 - expect(
60 - Object.keys(forwardedAPI)
61 - .filter(filterPrivateKeys)
62 - .sort(),
63 - ).toEqual(apiKeys);
43 + expect(Object.keys(forwardedAPI).sort()).toEqual(apiKeys);
44 });
45 }
46
@@ -78,19 +58,4 @@ describe('Scheduling UMD bundle', () => {
58 secretAPI.Scheduler,
59 ]);
60 });
81 -
82 - it('should define the same tracing API', () => {
83 - const api = require('../../tracing');
84 - const umdAPIDev = require('../../npm/umd/scheduler-tracing.development');
85 - const umdAPIProd = require('../../npm/umd/scheduler-tracing.production.min');
86 - const umdAPIProfiling = require('../../npm/umd/scheduler-tracing.profiling.min');
87 - const secretAPI = require('react/src/forks/ReactSharedInternals.umd')
88 - .default;
89 - validateForwardedAPIs(api, [
90 - umdAPIDev,
91 - umdAPIProd,
92 - umdAPIProfiling,
93 - secretAPI.SchedulerTracing,
94 - ]);
95 - });
61 });
packages/scheduler/src/__tests__/Tracing-test.internal.js deleted
-375
@@ -1,375 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @jest-environment node
8 - */
9 -'use strict';
10 -
11 -describe('Tracing', () => {
12 - let SchedulerTracing;
13 - let ReactFeatureFlags;
14 -
15 - let advanceTimeBy;
16 - let currentTime;
17 -
18 - function loadModules({enableSchedulerTracing}) {
19 - jest.resetModules();
20 - jest.useFakeTimers();
21 -
22 - currentTime = 0;
23 - Date.now = jest.fn().mockImplementation(() => currentTime);
24 -
25 - advanceTimeBy = amount => {
26 - currentTime += amount;
27 - };
28 -
29 - ReactFeatureFlags = require('shared/ReactFeatureFlags');
30 - ReactFeatureFlags.enableSchedulerTracing = enableSchedulerTracing;
31 -
32 - SchedulerTracing = require('scheduler/tracing');
33 - }
34 -
35 - describe('enableSchedulerTracing enabled', () => {
36 - beforeEach(() => loadModules({enableSchedulerTracing: true}));
37 -
38 - it('should return the value of a traced function', () => {
39 - expect(
40 - SchedulerTracing.unstable_trace('arbitrary', currentTime, () => 123),
41 - ).toBe(123);
42 - });
43 -
44 - it('should return the value of a clear function', () => {
45 - expect(SchedulerTracing.unstable_clear(() => 123)).toBe(123);
46 - });
47 -
48 - it('should return the value of a wrapped function', () => {
49 - let wrapped;
50 - SchedulerTracing.unstable_trace('arbitrary', currentTime, () => {
51 - wrapped = SchedulerTracing.unstable_wrap(() => 123);
52 - });
53 - expect(wrapped()).toBe(123);
54 - });
55 -
56 - it('should pass arguments through to a wrapped function', done => {
57 - let wrapped;
58 - SchedulerTracing.unstable_trace('arbitrary', currentTime, () => {
59 - wrapped = SchedulerTracing.unstable_wrap((param1, param2) => {
60 - expect(param1).toBe('foo');
61 - expect(param2).toBe('bar');
62 - done();
63 - });
64 - });
65 - wrapped('foo', 'bar');
66 - });
67 -
68 - it('should return an empty set when outside of a traced event', () => {
69 - expect(SchedulerTracing.unstable_getCurrent()).toContainNoInteractions();
70 - });
71 -
72 - it('should report the traced interaction from within the trace callback', done => {
73 - advanceTimeBy(100);
74 -
75 - SchedulerTracing.unstable_trace('some event', currentTime, () => {
76 - const interactions = SchedulerTracing.unstable_getCurrent();
77 - expect(interactions).toMatchInteractions([
78 - {name: 'some event', timestamp: 100},
79 - ]);
80 -
81 - done();
82 - });
83 - });
84 -
85 - it('should report the traced interaction from within wrapped callbacks', done => {
86 - let wrappedIndirection;
87 -
88 - function indirection() {
89 - const interactions = SchedulerTracing.unstable_getCurrent();
90 - expect(interactions).toMatchInteractions([
91 - {name: 'some event', timestamp: 100},
92 - ]);
93 -
94 - done();
95 - }
96 -
97 - advanceTimeBy(100);
98 -
99 - SchedulerTracing.unstable_trace('some event', currentTime, () => {
100 - wrappedIndirection = SchedulerTracing.unstable_wrap(indirection);
101 - });
102 -
103 - advanceTimeBy(50);
104 -
105 - wrappedIndirection();
106 - });
107 -
108 - it('should clear the interaction stack for traced callbacks', () => {
109 - let innerTestReached = false;
110 -
111 - SchedulerTracing.unstable_trace('outer event', currentTime, () => {
112 - expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions([
113 - {name: 'outer event'},
114 - ]);
115 -
116 - SchedulerTracing.unstable_clear(() => {
117 - expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions(
118 - [],
119 - );
120 -
121 - SchedulerTracing.unstable_trace('inner event', currentTime, () => {
122 - expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions([
123 - {name: 'inner event'},
124 - ]);
125 -
126 - innerTestReached = true;
127 - });
128 - });
129 -
130 - expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions([
131 - {name: 'outer event'},
132 - ]);
133 - });
134 -
135 - expect(innerTestReached).toBe(true);
136 - });
137 -
138 - it('should clear the interaction stack for wrapped callbacks', () => {
139 - let innerTestReached = false;
140 - let wrappedIndirection;
141 -
142 - const indirection = jest.fn(() => {
143 - expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions([
144 - {name: 'outer event'},
145 - ]);
146 -
147 - SchedulerTracing.unstable_clear(() => {
148 - expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions(
149 - [],
150 - );
151 -
152 - SchedulerTracing.unstable_trace('inner event', currentTime, () => {
153 - expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions([
154 - {name: 'inner event'},
155 - ]);
156 -
157 - innerTestReached = true;
158 - });
159 - });
160 -
161 - expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions([
162 - {name: 'outer event'},
163 - ]);
164 - });
165 -
166 - SchedulerTracing.unstable_trace('outer event', currentTime, () => {
167 - wrappedIndirection = SchedulerTracing.unstable_wrap(indirection);
168 - });
169 -
170 - wrappedIndirection();
171 -
172 - expect(innerTestReached).toBe(true);
173 - });
174 -
175 - it('should support nested traced events', done => {
176 - advanceTimeBy(100);
177 -
178 - let innerIndirectionTraced = false;
179 - let outerIndirectionTraced = false;
180 -
181 - function innerIndirection() {
182 - const interactions = SchedulerTracing.unstable_getCurrent();
183 - expect(interactions).toMatchInteractions([
184 - {name: 'outer event', timestamp: 100},
185 - {name: 'inner event', timestamp: 150},
186 - ]);
187 -
188 - innerIndirectionTraced = true;
189 - }
190 -
191 - function outerIndirection() {
192 - const interactions = SchedulerTracing.unstable_getCurrent();
193 - expect(interactions).toMatchInteractions([
194 - {name: 'outer event', timestamp: 100},
195 - ]);
196 -
197 - outerIndirectionTraced = true;
198 - }
199 -
200 - SchedulerTracing.unstable_trace('outer event', currentTime, () => {
201 - // Verify the current traced event
202 - let interactions = SchedulerTracing.unstable_getCurrent();
203 - expect(interactions).toMatchInteractions([
204 - {name: 'outer event', timestamp: 100},
205 - ]);
206 -
207 - advanceTimeBy(50);
208 -
209 - const wrapperOuterIndirection = SchedulerTracing.unstable_wrap(
210 - outerIndirection,
211 - );
212 -
213 - let wrapperInnerIndirection;
214 - let innerEventTraced = false;
215 -
216 - // Verify that a nested event is properly traced
217 - SchedulerTracing.unstable_trace('inner event', currentTime, () => {
218 - interactions = SchedulerTracing.unstable_getCurrent();
219 - expect(interactions).toMatchInteractions([
220 - {name: 'outer event', timestamp: 100},
221 - {name: 'inner event', timestamp: 150},
222 - ]);
223 -
224 - // Verify that a wrapped outer callback is properly traced
225 - wrapperOuterIndirection();
226 - expect(outerIndirectionTraced).toBe(true);
227 -
228 - wrapperInnerIndirection = SchedulerTracing.unstable_wrap(
229 - innerIndirection,
230 - );
231 -
232 - innerEventTraced = true;
233 - });
234 -
235 - expect(innerEventTraced).toBe(true);
236 -
237 - // Verify that the original event is restored
238 - interactions = SchedulerTracing.unstable_getCurrent();
239 - expect(interactions).toMatchInteractions([
240 - {name: 'outer event', timestamp: 100},
241 - ]);
242 -
243 - // Verify that a wrapped nested callback is properly traced
244 - wrapperInnerIndirection();
245 - expect(innerIndirectionTraced).toBe(true);
246 -
247 - done();
248 - });
249 - });
250 -
251 - describe('error handling', () => {
252 - it('should reset state appropriately when an error occurs in a trace callback', done => {
253 - advanceTimeBy(100);
254 -
255 - SchedulerTracing.unstable_trace('outer event', currentTime, () => {
256 - expect(() => {
257 - SchedulerTracing.unstable_trace('inner event', currentTime, () => {
258 - throw Error('intentional');
259 - });
260 - }).toThrow();
261 -
262 - expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions([
263 - {name: 'outer event', timestamp: 100},
264 - ]);
265 -
266 - done();
267 - });
268 - });
269 -
270 - it('should reset state appropriately when an error occurs in a wrapped callback', done => {
271 - advanceTimeBy(100);
272 -
273 - SchedulerTracing.unstable_trace('outer event', currentTime, () => {
274 - let wrappedCallback;
275 -
276 - SchedulerTracing.unstable_trace('inner event', currentTime, () => {
277 - wrappedCallback = SchedulerTracing.unstable_wrap(() => {
278 - throw Error('intentional');
279 - });
280 - });
281 -
282 - expect(wrappedCallback).toThrow();
283 -
284 - expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions([
285 - {name: 'outer event', timestamp: 100},
286 - ]);
287 -
288 - done();
289 - });
290 - });
291 - });
292 -
293 - describe('advanced integration', () => {
294 - it('should return a unique threadID per request', () => {
295 - expect(SchedulerTracing.unstable_getThreadID()).not.toBe(
296 - SchedulerTracing.unstable_getThreadID(),
297 - );
298 - });
299 -
300 - it('should expose the current set of interactions to be externally manipulated', () => {
301 - SchedulerTracing.unstable_trace('outer event', currentTime, () => {
302 - expect(SchedulerTracing.__interactionsRef.current).toBe(
303 - SchedulerTracing.unstable_getCurrent(),
304 - );
305 -
306 - SchedulerTracing.__interactionsRef.current = new Set([
307 - {name: 'override event'},
308 - ]);
309 -
310 - expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions([
311 - {name: 'override event'},
312 - ]);
313 - });
314 - });
315 -
316 - it('should expose a subscriber ref to be externally manipulated', () => {
317 - SchedulerTracing.unstable_trace('outer event', currentTime, () => {
318 - expect(SchedulerTracing.__subscriberRef).toEqual({
319 - current: null,
320 - });
321 - });
322 - });
323 - });
324 - });
325 -
326 - describe('enableSchedulerTracing disabled', () => {
327 - beforeEach(() => loadModules({enableSchedulerTracing: false}));
328 -
329 - it('should return the value of a traced function', () => {
330 - expect(
331 - SchedulerTracing.unstable_trace('arbitrary', currentTime, () => 123),
332 - ).toBe(123);
333 - });
334 -
335 - it('should return the value of a wrapped function', () => {
336 - let wrapped;
337 - SchedulerTracing.unstable_trace('arbitrary', currentTime, () => {
338 - wrapped = SchedulerTracing.unstable_wrap(() => 123);
339 - });
340 - expect(wrapped()).toBe(123);
341 - });
342 -
343 - it('should return null for traced interactions', () => {
344 - expect(SchedulerTracing.unstable_getCurrent()).toBe(null);
345 - });
346 -
347 - it('should execute traced callbacks', done => {
348 - SchedulerTracing.unstable_trace('some event', currentTime, () => {
349 - expect(SchedulerTracing.unstable_getCurrent()).toBe(null);
350 -
351 - done();
352 - });
353 - });
354 -
355 - it('should return the value of a clear function', () => {
356 - expect(SchedulerTracing.unstable_clear(() => 123)).toBe(123);
357 - });
358 -
359 - it('should execute wrapped callbacks', done => {
360 - const wrappedCallback = SchedulerTracing.unstable_wrap(() => {
361 - expect(SchedulerTracing.unstable_getCurrent()).toBe(null);
362 -
363 - done();
364 - });
365 -
366 - wrappedCallback();
367 - });
368 -
369 - describe('advanced integration', () => {
370 - it('should not create unnecessary objects', () => {
371 - expect(SchedulerTracing.__interactionsRef).toBe(null);
372 - });
373 - });
374 - });
375 -});
packages/scheduler/src/__tests__/Tracing-test.js deleted
-51
@@ -1,51 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @jest-environment node
8 - */
9 -'use strict';
10 -
11 -describe('Tracing', () => {
12 - let SchedulerTracing;
13 -
14 - beforeEach(() => {
15 - jest.resetModules();
16 -
17 - SchedulerTracing = require('scheduler/tracing');
18 - });
19 -
20 - it('should return the value of a traced function', () => {
21 - expect(SchedulerTracing.unstable_trace('arbitrary', 0, () => 123)).toBe(
22 - 123,
23 - );
24 - });
25 -
26 - it('should return the value of a wrapped function', () => {
27 - let wrapped;
28 - SchedulerTracing.unstable_trace('arbitrary', 0, () => {
29 - wrapped = SchedulerTracing.unstable_wrap(() => 123);
30 - });
31 - expect(wrapped()).toBe(123);
32 - });
33 -
34 - it('should execute traced callbacks', done => {
35 - SchedulerTracing.unstable_trace('some event', 0, () => {
36 - done();
37 - });
38 - });
39 -
40 - it('should return the value of a clear function', () => {
41 - expect(SchedulerTracing.unstable_clear(() => 123)).toBe(123);
42 - });
43 -
44 - it('should execute wrapped callbacks', done => {
45 - const wrappedCallback = SchedulerTracing.unstable_wrap(() => {
46 - done();
47 - });
48 -
49 - wrappedCallback();
50 - });
51 -});
packages/scheduler/src/__tests__/TracingSubscriptions-test.internal.js deleted
-621
@@ -1,621 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @jest-environment node
8 - */
9 -'use strict';
10 -
11 -describe('TracingSubscriptions', () => {
12 - let SchedulerTracing;
13 - let ReactFeatureFlags;
14 -
15 - let currentTime;
16 -
17 - let onInteractionScheduledWorkCompleted;
18 - let onInteractionTraced;
19 - let onWorkCanceled;
20 - let onWorkScheduled;
21 - let onWorkStarted;
22 - let onWorkStopped;
23 - let throwInOnInteractionScheduledWorkCompleted;
24 - let throwInOnInteractionTraced;
25 - let throwInOnWorkCanceled;
26 - let throwInOnWorkScheduled;
27 - let throwInOnWorkStarted;
28 - let throwInOnWorkStopped;
29 - let firstSubscriber;
30 - let secondSubscriber;
31 -
32 - const firstEvent = {id: 0, name: 'first', timestamp: 0};
33 - const secondEvent = {id: 1, name: 'second', timestamp: 0};
34 - const threadID = 123;
35 -
36 - function loadModules({enableSchedulerTracing, autoSubscribe = true}) {
37 - jest.resetModules();
38 - jest.useFakeTimers();
39 -
40 - currentTime = 0;
41 -
42 - ReactFeatureFlags = require('shared/ReactFeatureFlags');
43 - ReactFeatureFlags.enableSchedulerTracing = enableSchedulerTracing;
44 -
45 - SchedulerTracing = require('scheduler/tracing');
46 -
47 - throwInOnInteractionScheduledWorkCompleted = false;
48 - throwInOnInteractionTraced = false;
49 - throwInOnWorkCanceled = false;
50 - throwInOnWorkScheduled = false;
51 - throwInOnWorkStarted = false;
52 - throwInOnWorkStopped = false;
53 -
54 - onInteractionScheduledWorkCompleted = jest.fn(() => {
55 - if (throwInOnInteractionScheduledWorkCompleted) {
56 - throw Error('Expected error onInteractionScheduledWorkCompleted');
57 - }
58 - });
59 - onInteractionTraced = jest.fn(() => {
60 - if (throwInOnInteractionTraced) {
61 - throw Error('Expected error onInteractionTraced');
62 - }
63 - });
64 - onWorkCanceled = jest.fn(() => {
65 - if (throwInOnWorkCanceled) {
66 - throw Error('Expected error onWorkCanceled');
67 - }
68 - });
69 - onWorkScheduled = jest.fn(() => {
70 - if (throwInOnWorkScheduled) {
71 - throw Error('Expected error onWorkScheduled');
72 - }
73 - });
74 - onWorkStarted = jest.fn(() => {
75 - if (throwInOnWorkStarted) {
76 - throw Error('Expected error onWorkStarted');
77 - }
78 - });
79 - onWorkStopped = jest.fn(() => {
80 - if (throwInOnWorkStopped) {
81 - throw Error('Expected error onWorkStopped');
82 - }
83 - });
84 -
85 - firstSubscriber = {
86 - onInteractionScheduledWorkCompleted,
87 - onInteractionTraced,
88 - onWorkCanceled,
89 - onWorkScheduled,
90 - onWorkStarted,
91 - onWorkStopped,
92 - };
93 -
94 - secondSubscriber = {
95 - onInteractionScheduledWorkCompleted: jest.fn(),
96 - onInteractionTraced: jest.fn(),
97 - onWorkCanceled: jest.fn(),
98 - onWorkScheduled: jest.fn(),
99 - onWorkStarted: jest.fn(),
100 - onWorkStopped: jest.fn(),
101 - };
102 -
103 - if (autoSubscribe) {
104 - SchedulerTracing.unstable_subscribe(firstSubscriber);
105 - SchedulerTracing.unstable_subscribe(secondSubscriber);
106 - }
107 - }
108 -
109 - describe('enabled', () => {
110 - beforeEach(() => loadModules({enableSchedulerTracing: true}));
111 -
112 - it('should lazily subscribe to tracing and unsubscribe again if there are no external subscribers', () => {
113 - loadModules({enableSchedulerTracing: true, autoSubscribe: false});
114 -
115 - expect(SchedulerTracing.__subscriberRef.current).toBe(null);
116 - SchedulerTracing.unstable_subscribe(firstSubscriber);
117 - expect(SchedulerTracing.__subscriberRef.current).toBeDefined();
118 - SchedulerTracing.unstable_subscribe(secondSubscriber);
119 - expect(SchedulerTracing.__subscriberRef.current).toBeDefined();
120 - SchedulerTracing.unstable_unsubscribe(secondSubscriber);
121 - expect(SchedulerTracing.__subscriberRef.current).toBeDefined();
122 - SchedulerTracing.unstable_unsubscribe(firstSubscriber);
123 - expect(SchedulerTracing.__subscriberRef.current).toBe(null);
124 - });
125 -
126 - describe('error handling', () => {
127 - it('should cover onInteractionTraced/onWorkStarted within', done => {
128 - SchedulerTracing.unstable_trace(firstEvent.name, currentTime, () => {
129 - const mock = jest.fn();
130 -
131 - // It should call the callback before re-throwing
132 - throwInOnInteractionTraced = true;
133 - expect(() =>
134 - SchedulerTracing.unstable_trace(
135 - secondEvent.name,
136 - currentTime,
137 - mock,
138 - threadID,
139 - ),
140 - ).toThrow('Expected error onInteractionTraced');
141 - throwInOnInteractionTraced = false;
142 - expect(mock).toHaveBeenCalledTimes(1);
143 -
144 - throwInOnWorkStarted = true;
145 - expect(() =>
146 - SchedulerTracing.unstable_trace(
147 - secondEvent.name,
148 - currentTime,
149 - mock,
150 - threadID,
151 - ),
152 - ).toThrow('Expected error onWorkStarted');
153 - expect(mock).toHaveBeenCalledTimes(2);
154 -
155 - // It should restore the previous/outer interactions
156 - expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions([
157 - firstEvent,
158 - ]);
159 -
160 - // It should call other subscribers despite the earlier error
161 - expect(secondSubscriber.onInteractionTraced).toHaveBeenCalledTimes(3);
162 - expect(secondSubscriber.onWorkStarted).toHaveBeenCalledTimes(3);
163 -
164 - done();
165 - });
166 - });
167 -
168 - it('should cover onWorkStopped within trace', done => {
169 - SchedulerTracing.unstable_trace(firstEvent.name, currentTime, () => {
170 - let innerInteraction;
171 - const mock = jest.fn(() => {
172 - innerInteraction = Array.from(
173 - SchedulerTracing.unstable_getCurrent(),
174 - )[1];
175 - });
176 -
177 - throwInOnWorkStopped = true;
178 - expect(() =>
179 - SchedulerTracing.unstable_trace(
180 - secondEvent.name,
181 - currentTime,
182 - mock,
183 - ),
184 - ).toThrow('Expected error onWorkStopped');
185 - throwInOnWorkStopped = false;
186 -
187 - // It should restore the previous/outer interactions
188 - expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions([
189 - firstEvent,
190 - ]);
191 -
192 - // It should update the interaction count so as not to interfere with subsequent calls
193 - expect(innerInteraction.__count).toBe(0);
194 -
195 - // It should call other subscribers despite the earlier error
196 - expect(secondSubscriber.onWorkStopped).toHaveBeenCalledTimes(1);
197 -
198 - done();
199 - });
200 - });
201 -
202 - it('should cover onInteractionScheduledWorkCompleted within trace', done => {
203 - SchedulerTracing.unstable_trace(firstEvent.name, currentTime, () => {
204 - const mock = jest.fn();
205 -
206 - throwInOnInteractionScheduledWorkCompleted = true;
207 - expect(() =>
208 - SchedulerTracing.unstable_trace(
209 - secondEvent.name,
210 - currentTime,
211 - mock,
212 - ),
213 - ).toThrow('Expected error onInteractionScheduledWorkCompleted');
214 - throwInOnInteractionScheduledWorkCompleted = false;
215 -
216 - // It should restore the previous/outer interactions
217 - expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions([
218 - firstEvent,
219 - ]);
220 -
221 - // It should call other subscribers despite the earlier error
222 - expect(
223 - secondSubscriber.onInteractionScheduledWorkCompleted,
224 - ).toHaveBeenCalledTimes(1);
225 -
226 - done();
227 - });
228 - });
229 -
230 - it('should cover the callback within trace', done => {
231 - expect(onWorkStarted).not.toHaveBeenCalled();
232 - expect(onWorkStopped).not.toHaveBeenCalled();
233 -
234 - expect(() => {
235 - SchedulerTracing.unstable_trace(firstEvent.name, currentTime, () => {
236 - throw Error('Expected error callback');
237 - });
238 - }).toThrow('Expected error callback');
239 -
240 - expect(onWorkStarted).toHaveBeenCalledTimes(1);
241 - expect(onWorkStopped).toHaveBeenCalledTimes(1);
242 -
243 - done();
244 - });
245 -
246 - it('should cover onWorkScheduled within wrap', done => {
247 - SchedulerTracing.unstable_trace(firstEvent.name, currentTime, () => {
248 - const interaction = Array.from(
249 - SchedulerTracing.unstable_getCurrent(),
250 - )[0];
251 - const beforeCount = interaction.__count;
252 -
253 - throwInOnWorkScheduled = true;
254 - expect(() => SchedulerTracing.unstable_wrap(() => {})).toThrow(
255 - 'Expected error onWorkScheduled',
256 - );
257 -
258 - // It should not update the interaction count so as not to interfere with subsequent calls
259 - expect(interaction.__count).toBe(beforeCount);
260 -
261 - // It should call other subscribers despite the earlier error
262 - expect(secondSubscriber.onWorkScheduled).toHaveBeenCalledTimes(1);
263 -
264 - done();
265 - });
266 - });
267 -
268 - it('should cover onWorkStarted within wrap', () => {
269 - const mock = jest.fn();
270 - let interaction, wrapped;
271 - SchedulerTracing.unstable_trace(firstEvent.name, currentTime, () => {
272 - interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
273 - wrapped = SchedulerTracing.unstable_wrap(mock);
274 - });
275 - expect(interaction.__count).toBe(1);
276 -
277 - throwInOnWorkStarted = true;
278 - expect(wrapped).toThrow('Expected error onWorkStarted');
279 -
280 - // It should call the callback before re-throwing
281 - expect(mock).toHaveBeenCalledTimes(1);
282 -
283 - // It should update the interaction count so as not to interfere with subsequent calls
284 - expect(interaction.__count).toBe(0);
285 -
286 - // It should call other subscribers despite the earlier error
287 - expect(secondSubscriber.onWorkStarted).toHaveBeenCalledTimes(2);
288 - });
289 -
290 - it('should cover onWorkStopped within wrap', done => {
291 - SchedulerTracing.unstable_trace(firstEvent.name, currentTime, () => {
292 - const outerInteraction = Array.from(
293 - SchedulerTracing.unstable_getCurrent(),
294 - )[0];
295 - expect(outerInteraction.__count).toBe(1);
296 -
297 - let wrapped;
298 - let innerInteraction;
299 -
300 - SchedulerTracing.unstable_trace(secondEvent.name, currentTime, () => {
301 - innerInteraction = Array.from(
302 - SchedulerTracing.unstable_getCurrent(),
303 - )[1];
304 - expect(outerInteraction.__count).toBe(1);
305 - expect(innerInteraction.__count).toBe(1);
306 -
307 - wrapped = SchedulerTracing.unstable_wrap(jest.fn());
308 - expect(outerInteraction.__count).toBe(2);
309 - expect(innerInteraction.__count).toBe(2);
310 - });
311 -
312 - expect(outerInteraction.__count).toBe(2);
313 - expect(innerInteraction.__count).toBe(1);
314 -
315 - throwInOnWorkStopped = true;
316 - expect(wrapped).toThrow('Expected error onWorkStopped');
317 - throwInOnWorkStopped = false;
318 -
319 - // It should restore the previous interactions
320 - expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions([
321 - outerInteraction,
322 - ]);
323 -
324 - // It should update the interaction count so as not to interfere with subsequent calls
325 - expect(outerInteraction.__count).toBe(1);
326 - expect(innerInteraction.__count).toBe(0);
327 -
328 - expect(secondSubscriber.onWorkStopped).toHaveBeenCalledTimes(2);
329 -
330 - done();
331 - });
332 - });
333 -
334 - it('should cover the callback within wrap', done => {
335 - expect(onWorkStarted).not.toHaveBeenCalled();
336 - expect(onWorkStopped).not.toHaveBeenCalled();
337 -
338 - let wrapped;
339 - let interaction;
340 - SchedulerTracing.unstable_trace(firstEvent.name, currentTime, () => {
341 - interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
342 - wrapped = SchedulerTracing.unstable_wrap(() => {
343 - throw Error('Expected error wrap');
344 - });
345 - });
346 -
347 - expect(onWorkStarted).toHaveBeenCalledTimes(1);
348 - expect(onWorkStopped).toHaveBeenCalledTimes(1);
349 -
350 - expect(wrapped).toThrow('Expected error wrap');
351 -
352 - expect(onWorkStarted).toHaveBeenCalledTimes(2);
353 - expect(onWorkStopped).toHaveBeenCalledTimes(2);
354 - expect(onWorkStopped).toHaveBeenLastNotifiedOfWork([interaction]);
355 -
356 - done();
357 - });
358 -
359 - it('should cover onWorkCanceled within wrap', () => {
360 - let interaction, wrapped;
361 - SchedulerTracing.unstable_trace(firstEvent.name, currentTime, () => {
362 - interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
363 - wrapped = SchedulerTracing.unstable_wrap(jest.fn());
364 - });
365 - expect(interaction.__count).toBe(1);
366 -
367 - throwInOnWorkCanceled = true;
368 - expect(wrapped.cancel).toThrow('Expected error onWorkCanceled');
369 -
370 - expect(onWorkCanceled).toHaveBeenCalledTimes(1);
371 -
372 - // It should update the interaction count so as not to interfere with subsequent calls
373 - expect(interaction.__count).toBe(0);
374 - expect(
375 - onInteractionScheduledWorkCompleted,
376 - ).toHaveBeenLastNotifiedOfInteraction(firstEvent);
377 -
378 - // It should call other subscribers despite the earlier error
379 - expect(secondSubscriber.onWorkCanceled).toHaveBeenCalledTimes(1);
380 - });
381 - });
382 -
383 - it('calls lifecycle methods for trace', () => {
384 - expect(onInteractionTraced).not.toHaveBeenCalled();
385 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
386 -
387 - SchedulerTracing.unstable_trace(
388 - firstEvent.name,
389 - currentTime,
390 - () => {
391 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
392 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
393 - firstEvent,
394 - );
395 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
396 - expect(onWorkStarted).toHaveBeenCalledTimes(1);
397 - expect(onWorkStarted).toHaveBeenLastNotifiedOfWork(
398 - new Set([firstEvent]),
399 - threadID,
400 - );
401 - expect(onWorkStopped).not.toHaveBeenCalled();
402 -
403 - SchedulerTracing.unstable_trace(
404 - secondEvent.name,
405 - currentTime,
406 - () => {
407 - expect(onInteractionTraced).toHaveBeenCalledTimes(2);
408 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
409 - secondEvent,
410 - );
411 - expect(
412 - onInteractionScheduledWorkCompleted,
413 - ).not.toHaveBeenCalled();
414 - expect(onWorkStarted).toHaveBeenCalledTimes(2);
415 - expect(onWorkStarted).toHaveBeenLastNotifiedOfWork(
416 - new Set([firstEvent, secondEvent]),
417 - threadID,
418 - );
419 - expect(onWorkStopped).not.toHaveBeenCalled();
420 - },
421 - threadID,
422 - );
423 -
424 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
425 - expect(
426 - onInteractionScheduledWorkCompleted,
427 - ).toHaveBeenLastNotifiedOfInteraction(secondEvent);
428 - expect(onWorkStopped).toHaveBeenCalledTimes(1);
429 - expect(onWorkStopped).toHaveBeenLastNotifiedOfWork(
430 - new Set([firstEvent, secondEvent]),
431 - threadID,
432 - );
433 - },
434 - threadID,
435 - );
436 -
437 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2);
438 - expect(
439 - onInteractionScheduledWorkCompleted,
440 - ).toHaveBeenLastNotifiedOfInteraction(firstEvent);
441 - expect(onWorkScheduled).not.toHaveBeenCalled();
442 - expect(onWorkCanceled).not.toHaveBeenCalled();
443 - expect(onWorkStarted).toHaveBeenCalledTimes(2);
444 - expect(onWorkStopped).toHaveBeenCalledTimes(2);
445 - expect(onWorkStopped).toHaveBeenLastNotifiedOfWork(
446 - new Set([firstEvent]),
447 - threadID,
448 - );
449 - });
450 -
451 - it('calls lifecycle methods for wrap', () => {
452 - const unwrapped = jest.fn();
453 - let wrapped;
454 -
455 - SchedulerTracing.unstable_trace(firstEvent.name, currentTime, () => {
456 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
457 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
458 - firstEvent,
459 - );
460 -
461 - SchedulerTracing.unstable_trace(secondEvent.name, currentTime, () => {
462 - expect(onInteractionTraced).toHaveBeenCalledTimes(2);
463 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
464 - secondEvent,
465 - );
466 -
467 - wrapped = SchedulerTracing.unstable_wrap(unwrapped, threadID);
468 - expect(onWorkScheduled).toHaveBeenCalledTimes(1);
469 - expect(onWorkScheduled).toHaveBeenLastNotifiedOfWork(
470 - new Set([firstEvent, secondEvent]),
471 - threadID,
472 - );
473 - });
474 - });
475 -
476 - expect(onInteractionTraced).toHaveBeenCalledTimes(2);
477 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
478 -
479 - wrapped();
480 - expect(unwrapped).toHaveBeenCalled();
481 -
482 - expect(onWorkScheduled).toHaveBeenCalledTimes(1);
483 - expect(onWorkCanceled).not.toHaveBeenCalled();
484 - expect(onWorkStarted).toHaveBeenCalledTimes(3);
485 - expect(onWorkStarted).toHaveBeenLastNotifiedOfWork(
486 - new Set([firstEvent, secondEvent]),
487 - threadID,
488 - );
489 - expect(onWorkStopped).toHaveBeenCalledTimes(3);
490 - expect(onWorkStopped).toHaveBeenLastNotifiedOfWork(
491 - new Set([firstEvent, secondEvent]),
492 - threadID,
493 - );
494 -
495 - expect(
496 - onInteractionScheduledWorkCompleted.mock.calls[0][0],
497 - ).toMatchInteraction(firstEvent);
498 - expect(
499 - onInteractionScheduledWorkCompleted.mock.calls[1][0],
500 - ).toMatchInteraction(secondEvent);
501 - });
502 -
503 - it('should call the correct interaction subscriber methods when a wrapped callback is canceled', () => {
504 - const fnOne = jest.fn();
505 - const fnTwo = jest.fn();
506 - let wrappedOne, wrappedTwo;
507 - SchedulerTracing.unstable_trace(firstEvent.name, currentTime, () => {
508 - wrappedOne = SchedulerTracing.unstable_wrap(fnOne, threadID);
509 - SchedulerTracing.unstable_trace(secondEvent.name, currentTime, () => {
510 - wrappedTwo = SchedulerTracing.unstable_wrap(fnTwo, threadID);
511 - });
512 - });
513 -
514 - expect(onInteractionTraced).toHaveBeenCalledTimes(2);
515 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
516 - expect(onWorkCanceled).not.toHaveBeenCalled();
517 - expect(onWorkStarted).toHaveBeenCalledTimes(2);
518 - expect(onWorkStopped).toHaveBeenCalledTimes(2);
519 -
520 - wrappedTwo.cancel();
521 -
522 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
523 - expect(
524 - onInteractionScheduledWorkCompleted,
525 - ).toHaveBeenLastNotifiedOfInteraction(secondEvent);
526 - expect(onWorkCanceled).toHaveBeenCalledTimes(1);
527 - expect(onWorkCanceled).toHaveBeenLastNotifiedOfWork(
528 - new Set([firstEvent, secondEvent]),
529 - threadID,
530 - );
531 -
532 - wrappedOne.cancel();
533 -
534 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2);
535 - expect(
536 - onInteractionScheduledWorkCompleted,
537 - ).toHaveBeenLastNotifiedOfInteraction(firstEvent);
538 - expect(onWorkCanceled).toHaveBeenCalledTimes(2);
539 - expect(onWorkCanceled).toHaveBeenLastNotifiedOfWork(
540 - new Set([firstEvent]),
541 - threadID,
542 - );
543 -
544 - expect(fnOne).not.toHaveBeenCalled();
545 - expect(fnTwo).not.toHaveBeenCalled();
546 - });
547 -
548 - it('should not end an interaction twice if wrap is used to schedule follow up work within another wrap', () => {
549 - const fnOne = jest.fn(() => {
550 - wrappedTwo = SchedulerTracing.unstable_wrap(fnTwo, threadID);
551 - });
552 - const fnTwo = jest.fn();
553 - let wrappedOne, wrappedTwo;
554 - SchedulerTracing.unstable_trace(firstEvent.name, currentTime, () => {
555 - wrappedOne = SchedulerTracing.unstable_wrap(fnOne, threadID);
556 - });
557 -
558 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
559 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
560 -
561 - wrappedOne();
562 -
563 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
564 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
565 -
566 - wrappedTwo();
567 -
568 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
569 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
570 - expect(
571 - onInteractionScheduledWorkCompleted,
572 - ).toHaveBeenLastNotifiedOfInteraction(firstEvent);
573 - });
574 -
575 - it('should not decrement the interaction count twice if a wrapped function is run twice', () => {
576 - const unwrappedOne = jest.fn();
577 - const unwrappedTwo = jest.fn();
578 - let wrappedOne, wrappedTwo;
579 - SchedulerTracing.unstable_trace(firstEvent.name, currentTime, () => {
580 - wrappedOne = SchedulerTracing.unstable_wrap(unwrappedOne, threadID);
581 - wrappedTwo = SchedulerTracing.unstable_wrap(unwrappedTwo, threadID);
582 - });
583 -
584 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
585 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
586 -
587 - wrappedOne();
588 -
589 - expect(unwrappedOne).toHaveBeenCalledTimes(1);
590 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
591 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
592 -
593 - wrappedOne();
594 -
595 - expect(unwrappedOne).toHaveBeenCalledTimes(2);
596 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
597 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
598 -
599 - wrappedTwo();
600 -
601 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
602 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
603 - expect(
604 - onInteractionScheduledWorkCompleted,
605 - ).toHaveBeenLastNotifiedOfInteraction(firstEvent);
606 - });
607 -
608 - it('should unsubscribe', () => {
609 - SchedulerTracing.unstable_unsubscribe(firstSubscriber);
610 - SchedulerTracing.unstable_trace(firstEvent.name, currentTime, () => {});
611 -
612 - expect(onInteractionTraced).not.toHaveBeenCalled();
613 - });
614 - });
615 -
616 - describe('disabled', () => {
617 - beforeEach(() => loadModules({enableSchedulerTracing: false}));
618 -
619 - // TODO
620 - });
621 -});
packages/shared/ReactFeatureFlags.js
-3
@@ -51,9 +51,6 @@ export const enableProfilerNestedUpdatePhase = false;
51 // This callback accepts the component type (class instance or function) the update is scheduled for.
52 export const enableProfilerNestedUpdateScheduledHook = false;
53
54 -// Trace which interactions trigger each commit.
55 -export const enableSchedulerTracing = __PROFILE__;
56 -
54 // Track which Fiber(s) schedule render work.
55 export const enableUpdaterTracking = __PROFILE__;
56
packages/shared/ReactTypes.js
-2
@@ -155,8 +155,6 @@ export type MutableSource<Source: $NonMaybeType<mixed>> = {|
155 // This doesn't require a value to be passed to either handler.
156 export interface Wakeable {
157 then(onFulfill: () => mixed, onReject: () => mixed): void | Wakeable;
158 - // Special flag to opt out of tracing interactions across a Suspense boundary.
159 - __reactDoNotTraceInteractions?: boolean;
158 }
159
160 // The subset of a Promise that React APIs rely on. This resolves a value.
packages/shared/forks/ReactFeatureFlags.native-fb.js
-1
@@ -17,7 +17,6 @@ export const enableProfilerTimer = __PROFILE__;
17 export const enableProfilerCommitHooks = false;
18 export const enableProfilerNestedUpdatePhase = false;
19 export const enableProfilerNestedUpdateScheduledHook = false;
20 -export const enableSchedulerTracing = __PROFILE__;
20 export const enableUpdaterTracking = false;
21 export const enableSuspenseServerRenderer = false;
22 export const enableSelectiveHydration = false;
packages/shared/forks/ReactFeatureFlags.native-oss.js
-1
@@ -19,7 +19,6 @@ export const enableProfilerTimer = __PROFILE__;
19 export const enableProfilerCommitHooks = false;
20 export const enableProfilerNestedUpdatePhase = false;
21 export const enableProfilerNestedUpdateScheduledHook = false;
22 -export const enableSchedulerTracing = __PROFILE__;
22 export const enableUpdaterTracking = false;
23 export const enableSuspenseServerRenderer = false;
24 export const enableSelectiveHydration = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
-1
@@ -19,7 +19,6 @@ export const enableProfilerTimer = __PROFILE__;
19 export const enableProfilerCommitHooks = false;
20 export const enableProfilerNestedUpdatePhase = false;
21 export const enableProfilerNestedUpdateScheduledHook = false;
22 -export const enableSchedulerTracing = __PROFILE__;
22 export const enableUpdaterTracking = false;
23 export const enableSuspenseServerRenderer = false;
24 export const enableSelectiveHydration = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
-1
@@ -19,7 +19,6 @@ export const enableProfilerTimer = __PROFILE__;
19 export const enableProfilerCommitHooks = false;
20 export const enableProfilerNestedUpdatePhase = false;
21 export const enableProfilerNestedUpdateScheduledHook = false;
22 -export const enableSchedulerTracing = __PROFILE__;
22 export const enableUpdaterTracking = false;
23 export const enableSuspenseServerRenderer = false;
24 export const enableSelectiveHydration = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
-1
@@ -19,7 +19,6 @@ export const enableProfilerTimer = __PROFILE__;
19 export const enableProfilerCommitHooks = false;
20 export const enableProfilerNestedUpdatePhase = false;
21 export const enableProfilerNestedUpdateScheduledHook = false;
22 -export const enableSchedulerTracing = __PROFILE__;
22 export const enableUpdaterTracking = false;
23 export const enableSuspenseServerRenderer = false;
24 export const enableSelectiveHydration = false;
packages/shared/forks/ReactFeatureFlags.testing.js
-1
@@ -19,7 +19,6 @@ export const enableProfilerTimer = __PROFILE__;
19 export const enableProfilerCommitHooks = false;
20 export const enableProfilerNestedUpdatePhase = false;
21 export const enableProfilerNestedUpdateScheduledHook = false;
22 -export const enableSchedulerTracing = __PROFILE__;
22 export const enableUpdaterTracking = false;
23 export const enableSuspenseServerRenderer = false;
24 export const enableSelectiveHydration = false;
packages/shared/forks/ReactFeatureFlags.testing.www.js
-1
@@ -19,7 +19,6 @@ export const enableProfilerTimer = false;
19 export const enableProfilerCommitHooks = false;
20 export const enableProfilerNestedUpdatePhase = false;
21 export const enableProfilerNestedUpdateScheduledHook = false;
22 -export const enableSchedulerTracing = false;
22 export const enableUpdaterTracking = false;
23 export const enableSuspenseServerRenderer = true;
24 export const enableSelectiveHydration = true;
packages/shared/forks/ReactFeatureFlags.www.js
-1
@@ -55,7 +55,6 @@ export const enableSchedulingProfiler =
55 // Note: we'll want to remove this when we to userland implementation.
56 // For now, we'll turn it on for everyone because it's *already* on for everyone in practice.
57 // At least this will let us stop shipping <Profiler> implementation to all users.
58 -export const enableSchedulerTracing = true;
58 export const enableSchedulerDebugging = true;
59
60 export const warnAboutDeprecatedLifecycles = true;
packages/shared/forks/SchedulerTracing.umd.js deleted
-36
@@ -1,36 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import * as React from 'react';
11 -
12 -const ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
13 -
14 -const {
15 - __interactionsRef,
16 - __subscriberRef,
17 - unstable_clear,
18 - unstable_getCurrent,
19 - unstable_getThreadID,
20 - unstable_subscribe,
21 - unstable_trace,
22 - unstable_unsubscribe,
23 - unstable_wrap,
24 -} = ReactInternals.SchedulerTracing;
25 -
26 -export {
27 - __interactionsRef,
28 - __subscriberRef,
29 - unstable_clear,
30 - unstable_getCurrent,
31 - unstable_getThreadID,
32 - unstable_subscribe,
33 - unstable_trace,
34 - unstable_unsubscribe,
35 - unstable_wrap,
36 -};
scripts/jest/matchers/interactionTracingMatchers.js deleted
-101
@@ -1,101 +0,0 @@
1 -'use strict';
2 -
3 -const jestDiff = require('jest-diff').default;
4 -
5 -function toContainNoInteractions(actualSet) {
6 - return {
7 - message: () =>
8 - this.isNot
9 - ? `Expected interactions but there were none.`
10 - : `Expected no interactions but there were ${actualSet.size}.`,
11 - pass: actualSet.size === 0,
12 - };
13 -}
14 -
15 -function toHaveBeenLastNotifiedOfInteraction(
16 - mockFunction,
17 - expectedInteraction
18 -) {
19 - const calls = mockFunction.mock.calls;
20 - if (calls.length === 0) {
21 - return {
22 - message: () => 'Mock function was not called',
23 - pass: false,
24 - };
25 - }
26 -
27 - const [actualInteraction] = calls[calls.length - 1];
28 -
29 - return toMatchInteraction(actualInteraction, expectedInteraction);
30 -}
31 -
32 -function toHaveBeenLastNotifiedOfWork(
33 - mockFunction,
34 - expectedInteractions,
35 - expectedThreadID = undefined
36 -) {
37 - const calls = mockFunction.mock.calls;
38 - if (calls.length === 0) {
39 - return {
40 - message: () => 'Mock function was not called',
41 - pass: false,
42 - };
43 - }
44 -
45 - const [actualInteractions, actualThreadID] = calls[calls.length - 1];
46 -
47 - if (expectedThreadID !== undefined) {
48 - if (expectedThreadID !== actualThreadID) {
49 - return {
50 - message: () => jestDiff(expectedThreadID + '', actualThreadID + ''),
51 - pass: false,
52 - };
53 - }
54 - }
55 -
56 - return toMatchInteractions(actualInteractions, expectedInteractions);
57 -}
58 -
59 -function toMatchInteraction(actual, expected) {
60 - let attribute;
61 - for (attribute in expected) {
62 - if (actual[attribute] !== expected[attribute]) {
63 - return {
64 - message: () => jestDiff(expected, actual),
65 - pass: false,
66 - };
67 - }
68 - }
69 -
70 - return {pass: true};
71 -}
72 -
73 -function toMatchInteractions(actualSetOrArray, expectedSetOrArray) {
74 - const actualArray = Array.from(actualSetOrArray);
75 - const expectedArray = Array.from(expectedSetOrArray);
76 -
77 - if (actualArray.length !== expectedArray.length) {
78 - return {
79 - message: () =>
80 - `Expected ${expectedArray.length} interactions but there were ${actualArray.length}`,
81 - pass: false,
82 - };
83 - }
84 -
85 - for (let i = 0; i < actualArray.length; i++) {
86 - const result = toMatchInteraction(actualArray[i], expectedArray[i]);
87 - if (result.pass === false) {
88 - return result;
89 - }
90 - }
91 -
92 - return {pass: true};
93 -}
94 -
95 -module.exports = {
96 - toContainNoInteractions,
97 - toHaveBeenLastNotifiedOfInteraction,
98 - toHaveBeenLastNotifiedOfWork,
99 - toMatchInteraction,
100 - toMatchInteractions,
101 -};
scripts/jest/matchers/profilerMatchers.js
-72
@@ -1,72 +0,0 @@
1 -'use strict';
2 -
3 -const jestDiff = require('jest-diff').default;
4 -
5 -function toHaveLastRenderedWithNoInteractions(onRenderMockFn) {
6 - const calls = onRenderMockFn.mock.calls;
7 - if (calls.length === 0) {
8 - return {
9 - message: () => 'Mock onRender function was not called',
10 - pass: false,
11 - };
12 - }
13 -}
14 -
15 -function toHaveLastRenderedWithInteractions(
16 - onRenderMockFn,
17 - expectedInteractions
18 -) {
19 - const calls = onRenderMockFn.mock.calls;
20 - if (calls.length === 0) {
21 - return {
22 - message: () => 'Mock onRender function was not called',
23 - pass: false,
24 - };
25 - }
26 -
27 - const lastCall = calls[calls.length - 1];
28 - const actualInteractions = lastCall[6];
29 -
30 - return toMatchInteractions(actualInteractions, expectedInteractions);
31 -}
32 -
33 -function toMatchInteraction(actual, expected) {
34 - let attribute;
35 - for (attribute in expected) {
36 - if (actual[attribute] !== expected[attribute]) {
37 - return {
38 - message: () => jestDiff(expected, actual),
39 - pass: false,
40 - };
41 - }
42 - }
43 -
44 - return {pass: true};
45 -}
46 -
47 -function toMatchInteractions(actualSetOrArray, expectedSetOrArray) {
48 - const actualArray = Array.from(actualSetOrArray);
49 - const expectedArray = Array.from(expectedSetOrArray);
50 -
51 - if (actualArray.length !== expectedArray.length) {
52 - return {
53 - message: () =>
54 - `Expected ${expectedArray.length} interactions but there were ${actualArray.length}`,
55 - pass: false,
56 - };
57 - }
58 -
59 - for (let i = 0; i < actualArray.length; i++) {
60 - const result = toMatchInteraction(actualArray[i], expectedArray[i]);
61 - if (result.pass === false) {
62 - return result;
63 - }
64 - }
65 -
66 - return {pass: true};
67 -}
68 -
69 -module.exports = {
70 - toHaveLastRenderedWithInteractions,
71 - toHaveLastRenderedWithNoInteractions,
72 -};
scripts/jest/setupTests.js
-2
@@ -45,8 +45,6 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) {
45 }
46
47 expect.extend({
48 - ...require('./matchers/interactionTracingMatchers'),
49 - ...require('./matchers/profilerMatchers'),
48 ...require('./matchers/toWarnDev'),
49 ...require('./matchers/reactTestMatchers'),
50 });
scripts/jest/spec-equivalence-reporter/setupTests.js
-2
@@ -46,8 +46,6 @@ global.spyOnProd = function(...args) {
46 };
47
48 expect.extend({
49 - ...require('../matchers/interactionTracingMatchers'),
50 - ...require('../matchers/profilerMatchers'),
49 ...require('../matchers/toWarnDev'),
50 ...require('../matchers/reactTestMatchers'),
51 });
scripts/release/shared-commands/test-tracing-fixture.js deleted
-58
@@ -1,58 +0,0 @@
1 -#!/usr/bin/env node
2 -
3 -'use strict';
4 -
5 -const {join} = require('path');
6 -const puppeteer = require('puppeteer');
7 -const theme = require('../theme');
8 -const {logPromise} = require('../utils');
9 -
10 -const validate = async ({cwd}) => {
11 - const browser = await puppeteer.launch();
12 - const page = await browser.newPage();
13 -
14 - await page.goto(
15 - 'file://' + join(cwd, 'fixtures/tracing/index.html?puppeteer=true')
16 - );
17 -
18 - try {
19 - return await page.evaluate(() => {
20 - const button = document.getElementById('run-test-button');
21 - button.click();
22 -
23 - const items = document.querySelectorAll('[data-value]');
24 -
25 - if (items.length === 0) {
26 - return 'No results were found.';
27 - }
28 -
29 - for (let i = 0; i < items.length; i++) {
30 - const item = items[i];
31 - if (item.getAttribute('data-value') !== 'All checks pass') {
32 - return `Unexpected result, "${item.getAttribute('data-value')}"`;
33 - }
34 - }
35 -
36 - return null;
37 - });
38 - } finally {
39 - await browser.close();
40 - }
41 -};
42 -
43 -const run = async ({cwd}) => {
44 - const errorMessage = await logPromise(
45 - validate({cwd}),
46 - 'Verifying "scheduler/tracing" fixture'
47 - );
48 - if (errorMessage) {
49 - console.error(
50 - theme.error('✗'),
51 - 'Verifying "scheduler/tracing" fixture\n ',
52 - theme.error(errorMessage)
53 - );
54 - process.exit(1);
55 - }
56 -};
57 -
58 -module.exports = run;
scripts/rollup/bundles.js
-18
@@ -805,24 +805,6 @@ const bundles = [
805 global: 'ReactFreshRuntime',
806 externals: [],
807 },
808 -
809 - {
810 - bundleTypes: [
811 - FB_WWW_DEV,
812 - FB_WWW_PROD,
813 - FB_WWW_PROFILING,
814 - NODE_DEV,
815 - NODE_PROD,
816 - NODE_PROFILING,
817 - RN_FB_DEV,
818 - RN_FB_PROD,
819 - RN_FB_PROFILING,
820 - ],
821 - moduleType: ISOMORPHIC,
822 - entry: 'scheduler/tracing',
823 - global: 'SchedulerTracing',
824 - externals: [],
825 - },
808 ];
809
810 // Based on deep-freeze by substack (public domain)
scripts/rollup/forks.js
-19
@@ -171,25 +171,6 @@ const forks = Object.freeze({
171 }
172 },
173
174 - 'scheduler/tracing': (bundleType, entry, dependencies) => {
175 - switch (bundleType) {
176 - case UMD_DEV:
177 - case UMD_PROD:
178 - case UMD_PROFILING:
179 - if (dependencies.indexOf('react') === -1) {
180 - // It's only safe to use this fork for modules that depend on React,
181 - // because they read the re-exported API from the SECRET_INTERNALS object.
182 - return null;
183 - }
184 - // Optimization: for UMDs, use the API that is already a part of the React
185 - // package instead of requiring it to be loaded via a separate <script> tag
186 - return 'shared/forks/SchedulerTracing.umd.js';
187 - default:
188 - // For other bundles, use the shared NPM package.
189 - return null;
190 - }
191 - },
192 -
174 'scheduler/src/SchedulerFeatureFlags': (bundleType, entry, dependencies) => {
175 if (
176 bundleType === FB_WWW_DEV ||
scripts/rollup/modules.js
-2
@@ -15,7 +15,6 @@ const importSideEffects = Object.freeze({
15 'prop-types/checkPropTypes': HAS_NO_SIDE_EFFECTS_ON_IMPORT,
16 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface': HAS_NO_SIDE_EFFECTS_ON_IMPORT,
17 scheduler: HAS_NO_SIDE_EFFECTS_ON_IMPORT,
18 - 'scheduler/tracing': HAS_NO_SIDE_EFFECTS_ON_IMPORT,
18 react: HAS_NO_SIDE_EFFECTS_ON_IMPORT,
19 'react-dom/server': HAS_NO_SIDE_EFFECTS_ON_IMPORT,
20 'react/jsx-dev-runtime': HAS_NO_SIDE_EFFECTS_ON_IMPORT,
@@ -31,7 +30,6 @@ const knownGlobals = Object.freeze({
30 'react-dom/server': 'ReactDOMServer',
31 'react-interactions/events/tap': 'ReactEventsTap',
32 scheduler: 'Scheduler',
34 - 'scheduler/tracing': 'SchedulerTracing',
33 'scheduler/unstable_mock': 'SchedulerMock',
34 });
35
yarn.lock
+8
@@ -12319,6 +12319,14 @@ saxes@^5.0.0:
12319 dependencies:
12320 xmlchars "^2.2.0"
12321
12322 +"scheduler-0-13@npm:scheduler@0.13.0":
12323 + version "0.13.0"
12324 + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.0.tgz#e701f62e1b3e78d2bbb264046d4e7260f12184dd"
12325 + integrity sha512-w7aJnV30jc7OsiZQNPVmBc+HooZuvQZIZIShKutC3tnMFMkcwVN9CZRRSSNw03OnSCKmEkK8usmwcw6dqBaLzw==
12326 + dependencies:
12327 + loose-envify "^1.1.0"
12328 + object-assign "^4.1.1"
12329 +
12330 scheduler@^0.11.0:
12331 version "0.11.3"
12332 resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.11.3.tgz#b5769b90cf8b1464f3f3cfcafe8e3cd7555a2d6b"