@samitouri / QOS-React-1 / commits / d1829775ad

chore: use versioned render in TimelineProfiler test and gate some for legacy rendering (#28218)

Ruslan Lesiutin committed Feb 7, 2024 at 10:30 UTC d1829775adeee0f888ec7d464eb3041cfe23f1c3
1 file changed +1110 -1064
packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js
+1110 -1064
@@ -9,51 +9,25 @@
9
10 'use strict';
11
12 -import {normalizeCodeLocInfo} from './utils';
12 +import {
13 + getLegacyRenderImplementation,
14 + getModernRenderImplementation,
15 + normalizeCodeLocInfo,
16 +} from './utils';
17
18 describe('Timeline profiler', () => {
19 let React;
16 - let ReactDOMClient;
20 let Scheduler;
18 - let renderHelper;
19 - let renderRootHelper;
21 let store;
21 - let unmountFns;
22 let utils;
23 - let waitFor;
24 - let waitForAll;
25 - let waitForPaint;
26 - let assertLog;
23
24 beforeEach(() => {
25 utils = require('./utils');
26 utils.beforeEachProfiling();
27
32 - unmountFns = [];
33 - renderHelper = element => {
34 - const unmountFn = utils.legacyRender(element);
35 - unmountFns.push(unmountFn);
36 - return unmountFn;
37 - };
38 - renderRootHelper = element => {
39 - const container = document.createElement('div');
40 - const root = ReactDOMClient.createRoot(container);
41 - root.render(element);
42 - const unmountFn = () => root.unmount();
43 - unmountFns.push(unmountFn);
44 - return unmountFn;
45 - };
46 -
28 React = require('react');
48 - ReactDOMClient = require('react-dom/client');
29 Scheduler = require('scheduler');
30
51 - const InternalTestUtils = require('internal-test-utils');
52 - waitFor = InternalTestUtils.waitFor;
53 - waitForAll = InternalTestUtils.waitForAll;
54 - waitForPaint = InternalTestUtils.waitForPaint;
55 - assertLog = InternalTestUtils.assertLog;
56 -
31 store = global.store;
32 });
33
@@ -135,18 +109,57 @@ describe('Timeline profiler', () => {
109 // Verify all logged marks also get cleared.
110 expect(marks).toHaveLength(0);
111
138 - unmountFns.forEach(unmountFn => unmountFn());
139 -
112 setPerformanceMock(null);
113 });
114
143 - it('should mark sync render without suspends or state updates', () => {
144 - renderHelper(<div />);
115 + describe('with legacy render', () => {
116 + const {render: legacyRender} = getLegacyRenderImplementation();
117 +
118 + // @reactVersion <= 18.2
119 + // @reactVersion >= 18.0
120 + it('should mark sync render without suspends or state updates', () => {
121 + legacyRender(<div />);
122 +
123 + expect(clearedMarks).toMatchInlineSnapshot(`
124 + [
125 + "--schedule-render-1",
126 + "--render-start-1",
127 + "--render-stop",
128 + "--commit-start-1",
129 + "--react-version-<filtered-version>",
130 + "--profiler-version-1",
131 + "--react-internal-module-start- at filtered (<anonymous>:0:0)",
132 + "--react-internal-module-stop- at filtered (<anonymous>:1:1)",
133 + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
134 + "--layout-effects-start-1",
135 + "--layout-effects-stop",
136 + "--commit-stop",
137 + ]
138 + `);
139 + });
140 +
141 + // TODO(hoxyq): investigate why running this test with React 18 fails
142 + // @reactVersion <= 18.2
143 + // @reactVersion >= 18.0
144 + xit('should mark sync render with suspense that resolves', async () => {
145 + const fakeSuspensePromise = Promise.resolve(true);
146 + function Example() {
147 + throw fakeSuspensePromise;
148 + }
149 +
150 + legacyRender(
151 + <React.Suspense fallback={null}>
152 + <Example />
153 + </React.Suspense>,
154 + );
155
146 - expect(clearedMarks).toMatchInlineSnapshot(`
156 + expect(clearedMarks).toMatchInlineSnapshot(`
157 [
158 "--schedule-render-2",
159 "--render-start-2",
160 + "--component-render-start-Example",
161 + "--component-render-stop",
162 + "--suspense-suspend-0-Example-mount-2-",
163 "--render-stop",
164 "--commit-start-2",
165 "--react-version-<filtered-version>",
@@ -159,173 +172,224 @@ describe('Timeline profiler', () => {
172 "--commit-stop",
173 ]
174 `);
162 - });
175
164 - it('should mark concurrent render without suspends or state updates', async () => {
165 - renderRootHelper(<div />);
176 + clearPendingMarks();
177
167 - expect(clearedMarks).toMatchInlineSnapshot(`
168 - [
169 - "--schedule-render-32",
170 - ]
171 - `);
178 + await fakeSuspensePromise;
179 + expect(clearedMarks).toMatchInlineSnapshot(`
180 + [
181 + "--suspense-resolved-0-Example",
182 + ]
183 + `);
184 + });
185
173 - clearPendingMarks();
186 + // TODO(hoxyq): investigate why running this test with React 18 fails
187 + // @reactVersion <= 18.2
188 + // @reactVersion >= 18.0
189 + xit('should mark sync render with suspense that rejects', async () => {
190 + const fakeSuspensePromise = Promise.reject(new Error('error'));
191 + function Example() {
192 + throw fakeSuspensePromise;
193 + }
194
175 - await waitForPaint([]);
195 + legacyRender(
196 + <React.Suspense fallback={null}>
197 + <Example />
198 + </React.Suspense>,
199 + );
200
177 - expect(clearedMarks).toMatchInlineSnapshot(`
201 + expect(clearedMarks).toMatchInlineSnapshot(`
202 [
179 - "--render-start-32",
203 + "--schedule-render-2",
204 + "--render-start-2",
205 + "--component-render-start-Example",
206 + "--component-render-stop",
207 + "--suspense-suspend-0-Example-mount-2-",
208 "--render-stop",
181 - "--commit-start-32",
209 + "--commit-start-2",
210 "--react-version-<filtered-version>",
211 "--profiler-version-1",
212 "--react-internal-module-start- at filtered (<anonymous>:0:0)",
213 "--react-internal-module-stop- at filtered (<anonymous>:1:1)",
214 "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
187 - "--layout-effects-start-32",
215 + "--layout-effects-start-2",
216 "--layout-effects-stop",
217 "--commit-stop",
218 ]
219 `);
192 - });
193 -
194 - it('should mark render yields', async () => {
195 - function Bar() {
196 - Scheduler.log('Bar');
197 - return null;
198 - }
220
200 - function Foo() {
201 - Scheduler.log('Foo');
202 - return <Bar />;
203 - }
221 + clearPendingMarks();
222
205 - React.startTransition(() => {
206 - renderRootHelper(<Foo />);
223 + await expect(fakeSuspensePromise).rejects.toThrow();
224 + expect(clearedMarks).toContain(`--suspense-rejected-0-Example`);
225 });
226
209 - await waitFor(['Foo']);
227 + // @reactVersion <= 18.2
228 + // @reactVersion >= 18.0
229 + it('should mark sync render that throws', async () => {
230 + jest.spyOn(console, 'error').mockImplementation(() => {});
231
211 - expect(clearedMarks).toMatchInlineSnapshot(`
212 - [
213 - "--schedule-render-128",
214 - "--render-start-128",
215 - "--component-render-start-Foo",
216 - "--component-render-stop",
217 - "--render-yield",
218 - ]
219 - `);
220 - });
232 + class ErrorBoundary extends React.Component {
233 + state = {error: null};
234 + componentDidCatch(error) {
235 + this.setState({error});
236 + }
237 + render() {
238 + if (this.state.error) {
239 + return null;
240 + }
241 + return this.props.children;
242 + }
243 + }
244
222 - it('should mark sync render with suspense that resolves', async () => {
223 - const fakeSuspensePromise = Promise.resolve(true);
224 - function Example() {
225 - throw fakeSuspensePromise;
226 - }
245 + function ExampleThatThrows() {
246 + throw Error('Expected error');
247 + }
248
228 - renderHelper(
229 - <React.Suspense fallback={null}>
230 - <Example />
231 - </React.Suspense>,
232 - );
249 + legacyRender(
250 + <ErrorBoundary>
251 + <ExampleThatThrows />
252 + </ErrorBoundary>,
253 + );
254
234 - expect(clearedMarks).toMatchInlineSnapshot(`
255 + expect(clearedMarks).toMatchInlineSnapshot(`
256 [
236 - "--schedule-render-2",
237 - "--render-start-2",
238 - "--component-render-start-Example",
257 + "--schedule-render-1",
258 + "--render-start-1",
259 + "--component-render-start-ErrorBoundary",
260 "--component-render-stop",
240 - "--suspense-suspend-0-Example-mount-2-",
261 + "--component-render-start-ExampleThatThrows",
262 + "--component-render-start-ExampleThatThrows",
263 + "--component-render-stop",
264 + "--error-ExampleThatThrows-mount-Expected error",
265 "--render-stop",
242 - "--commit-start-2",
266 + "--commit-start-1",
267 "--react-version-<filtered-version>",
268 "--profiler-version-1",
269 "--react-internal-module-start- at filtered (<anonymous>:0:0)",
270 "--react-internal-module-stop- at filtered (<anonymous>:1:1)",
247 - "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
248 - "--layout-effects-start-2",
271 + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
272 + "--layout-effects-start-1",
273 + "--schedule-state-update-1-ErrorBoundary",
274 "--layout-effects-stop",
275 "--commit-stop",
276 + "--render-start-1",
277 + "--component-render-start-ErrorBoundary",
278 + "--component-render-stop",
279 + "--render-stop",
280 + "--commit-start-1",
281 + "--react-version-<filtered-version>",
282 + "--profiler-version-1",
283 + "--react-internal-module-start- at filtered (<anonymous>:0:0)",
284 + "--react-internal-module-stop- at filtered (<anonymous>:1:1)",
285 + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
286 + "--commit-stop",
287 ]
288 `);
289 + });
290 + });
291
254 - clearPendingMarks();
292 + describe('with createRoot', () => {
293 + let waitFor;
294 + let waitForAll;
295 + let waitForPaint;
296 + let assertLog;
297
256 - await fakeSuspensePromise;
257 - expect(clearedMarks).toMatchInlineSnapshot(`
258 - [
259 - "--suspense-resolved-0-Example",
260 - ]
261 - `);
262 - });
298 + beforeEach(() => {
299 + const InternalTestUtils = require('internal-test-utils');
300 + waitFor = InternalTestUtils.waitFor;
301 + waitForAll = InternalTestUtils.waitForAll;
302 + waitForPaint = InternalTestUtils.waitForPaint;
303 + assertLog = InternalTestUtils.assertLog;
304 + });
305
264 - it('should mark sync render with suspense that rejects', async () => {
265 - const fakeSuspensePromise = Promise.reject(new Error('error'));
266 - function Example() {
267 - throw fakeSuspensePromise;
268 - }
306 + const {render: modernRender} = getModernRenderImplementation();
307
270 - renderHelper(
271 - <React.Suspense fallback={null}>
272 - <Example />
273 - </React.Suspense>,
274 - );
308 + it('should mark concurrent render without suspends or state updates', async () => {
309 + modernRender(<div />);
310
276 - expect(clearedMarks).toMatchInlineSnapshot(`
311 + expect(clearedMarks).toMatchInlineSnapshot(`
312 [
278 - "--schedule-render-2",
279 - "--render-start-2",
280 - "--component-render-start-Example",
281 - "--component-render-stop",
282 - "--suspense-suspend-0-Example-mount-2-",
313 + "--schedule-render-32",
314 + ]
315 + `);
316 +
317 + clearPendingMarks();
318 +
319 + await waitForPaint([]);
320 +
321 + expect(clearedMarks).toMatchInlineSnapshot(`
322 + [
323 + "--render-start-32",
324 "--render-stop",
284 - "--commit-start-2",
325 + "--commit-start-32",
326 "--react-version-<filtered-version>",
327 "--profiler-version-1",
328 "--react-internal-module-start- at filtered (<anonymous>:0:0)",
329 "--react-internal-module-stop- at filtered (<anonymous>:1:1)",
330 "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
290 - "--layout-effects-start-2",
331 + "--layout-effects-start-32",
332 "--layout-effects-stop",
333 "--commit-stop",
334 ]
335 `);
336 + });
337
296 - clearPendingMarks();
338 + it('should mark render yields', async () => {
339 + function Bar() {
340 + Scheduler.log('Bar');
341 + return null;
342 + }
343
298 - await expect(fakeSuspensePromise).rejects.toThrow();
299 - expect(clearedMarks).toContain(`--suspense-rejected-0-Example`);
300 - });
344 + function Foo() {
345 + Scheduler.log('Foo');
346 + return <Bar />;
347 + }
348 +
349 + React.startTransition(() => {
350 + modernRender(<Foo />);
351 + });
352
302 - it('should mark concurrent render with suspense that resolves', async () => {
303 - let resolveFakePromise;
304 - const fakeSuspensePromise = new Promise(
305 - resolve => (resolveFakePromise = resolve),
306 - );
353 + await waitFor(['Foo']);
354
308 - function Example() {
309 - throw fakeSuspensePromise;
310 - }
355 + expect(clearedMarks).toMatchInlineSnapshot(`
356 + [
357 + "--schedule-render-128",
358 + "--render-start-128",
359 + "--component-render-start-Foo",
360 + "--component-render-stop",
361 + "--render-yield",
362 + ]
363 + `);
364 + });
365 +
366 + it('should mark concurrent render with suspense that resolves', async () => {
367 + let resolveFakePromise;
368 + const fakeSuspensePromise = new Promise(
369 + resolve => (resolveFakePromise = resolve),
370 + );
371 +
372 + function Example() {
373 + throw fakeSuspensePromise;
374 + }
375
312 - renderRootHelper(
313 - <React.Suspense fallback={null}>
314 - <Example />
315 - </React.Suspense>,
316 - );
376 + modernRender(
377 + <React.Suspense fallback={null}>
378 + <Example />
379 + </React.Suspense>,
380 + );
381
318 - expect(clearedMarks).toMatchInlineSnapshot(`
382 + expect(clearedMarks).toMatchInlineSnapshot(`
383 [
384 "--schedule-render-32",
385 ]
386 `);
387
324 - clearPendingMarks();
388 + clearPendingMarks();
389
326 - await waitForPaint([]);
390 + await waitForPaint([]);
391
328 - expect(clearedMarks).toMatchInlineSnapshot(`
392 + expect(clearedMarks).toMatchInlineSnapshot(`
393 [
394 "--render-start-32",
395 "--component-render-start-Example",
@@ -344,43 +408,43 @@ describe('Timeline profiler', () => {
408 ]
409 `);
410
347 - clearPendingMarks();
411 + clearPendingMarks();
412
349 - await resolveFakePromise();
350 - expect(clearedMarks).toMatchInlineSnapshot(`
413 + await resolveFakePromise();
414 + expect(clearedMarks).toMatchInlineSnapshot(`
415 [
416 "--suspense-resolved-0-Example",
417 ]
418 `);
355 - });
419 + });
420
357 - it('should mark concurrent render with suspense that rejects', async () => {
358 - let rejectFakePromise;
359 - const fakeSuspensePromise = new Promise(
360 - (_, reject) => (rejectFakePromise = reject),
361 - );
421 + it('should mark concurrent render with suspense that rejects', async () => {
422 + let rejectFakePromise;
423 + const fakeSuspensePromise = new Promise(
424 + (_, reject) => (rejectFakePromise = reject),
425 + );
426
363 - function Example() {
364 - throw fakeSuspensePromise;
365 - }
427 + function Example() {
428 + throw fakeSuspensePromise;
429 + }
430
367 - renderRootHelper(
368 - <React.Suspense fallback={null}>
369 - <Example />
370 - </React.Suspense>,
371 - );
431 + modernRender(
432 + <React.Suspense fallback={null}>
433 + <Example />
434 + </React.Suspense>,
435 + );
436
373 - expect(clearedMarks).toMatchInlineSnapshot(`
437 + expect(clearedMarks).toMatchInlineSnapshot(`
438 [
439 "--schedule-render-32",
440 ]
441 `);
442
379 - clearPendingMarks();
443 + clearPendingMarks();
444
381 - await waitForPaint([]);
445 + await waitForPaint([]);
446
383 - expect(clearedMarks).toMatchInlineSnapshot(`
447 + expect(clearedMarks).toMatchInlineSnapshot(`
448 [
449 "--render-start-32",
450 "--component-render-start-Example",
@@ -399,43 +463,43 @@ describe('Timeline profiler', () => {
463 ]
464 `);
465
402 - clearPendingMarks();
466 + clearPendingMarks();
467
404 - await expect(() => {
405 - rejectFakePromise(new Error('error'));
406 - return fakeSuspensePromise;
407 - }).rejects.toThrow();
408 - expect(clearedMarks).toMatchInlineSnapshot(`
468 + await expect(() => {
469 + rejectFakePromise(new Error('error'));
470 + return fakeSuspensePromise;
471 + }).rejects.toThrow();
472 + expect(clearedMarks).toMatchInlineSnapshot(`
473 [
474 "--suspense-rejected-0-Example",
475 ]
476 `);
413 - });
477 + });
478
415 - it('should mark cascading class component state updates', async () => {
416 - class Example extends React.Component {
417 - state = {didMount: false};
418 - componentDidMount() {
419 - this.setState({didMount: true});
420 - }
421 - render() {
422 - return null;
479 + it('should mark cascading class component state updates', async () => {
480 + class Example extends React.Component {
481 + state = {didMount: false};
482 + componentDidMount() {
483 + this.setState({didMount: true});
484 + }
485 + render() {
486 + return null;
487 + }
488 }
424 - }
489
426 - renderRootHelper(<Example />);
490 + modernRender(<Example />);
491
428 - expect(clearedMarks).toMatchInlineSnapshot(`
492 + expect(clearedMarks).toMatchInlineSnapshot(`
493 [
494 "--schedule-render-32",
495 ]
496 `);
497
434 - clearPendingMarks();
498 + clearPendingMarks();
499
436 - await waitForPaint([]);
500 + await waitForPaint([]);
501
438 - expect(clearedMarks).toMatchInlineSnapshot(`
502 + expect(clearedMarks).toMatchInlineSnapshot(`
503 [
504 "--render-start-32",
505 "--component-render-start-Example",
@@ -464,31 +528,31 @@ describe('Timeline profiler', () => {
528 "--commit-stop",
529 ]
530 `);
467 - });
531 + });
532
469 - it('should mark cascading class component force updates', async () => {
470 - class Example extends React.Component {
471 - componentDidMount() {
472 - this.forceUpdate();
473 - }
474 - render() {
475 - return null;
533 + it('should mark cascading class component force updates', async () => {
534 + class Example extends React.Component {
535 + componentDidMount() {
536 + this.forceUpdate();
537 + }
538 + render() {
539 + return null;
540 + }
541 }
477 - }
542
479 - renderRootHelper(<Example />);
543 + modernRender(<Example />);
544
481 - expect(clearedMarks).toMatchInlineSnapshot(`
545 + expect(clearedMarks).toMatchInlineSnapshot(`
546 [
547 "--schedule-render-32",
548 ]
549 `);
550
487 - clearPendingMarks();
551 + clearPendingMarks();
552
489 - await waitForPaint([]);
553 + await waitForPaint([]);
554
491 - expect(clearedMarks).toMatchInlineSnapshot(`
555 + expect(clearedMarks).toMatchInlineSnapshot(`
556 [
557 "--render-start-32",
558 "--component-render-start-Example",
@@ -517,42 +581,42 @@ describe('Timeline profiler', () => {
581 "--commit-stop",
582 ]
583 `);
520 - });
584 + });
585
522 - it('should mark render phase state updates for class component', async () => {
523 - class Example extends React.Component {
524 - state = {didRender: false};
525 - render() {
526 - if (this.state.didRender === false) {
527 - this.setState({didRender: true});
586 + it('should mark render phase state updates for class component', async () => {
587 + class Example extends React.Component {
588 + state = {didRender: false};
589 + render() {
590 + if (this.state.didRender === false) {
591 + this.setState({didRender: true});
592 + }
593 + return null;
594 }
529 - return null;
595 }
531 - }
596
533 - renderRootHelper(<Example />);
597 + modernRender(<Example />);
598
535 - expect(clearedMarks).toMatchInlineSnapshot(`
599 + expect(clearedMarks).toMatchInlineSnapshot(`
600 [
601 "--schedule-render-32",
602 ]
603 `);
604
541 - clearPendingMarks();
605 + clearPendingMarks();
606
543 - let errorMessage;
544 - jest.spyOn(console, 'error').mockImplementation(message => {
545 - errorMessage = message;
546 - });
607 + let errorMessage;
608 + jest.spyOn(console, 'error').mockImplementation(message => {
609 + errorMessage = message;
610 + });
611
548 - await waitForPaint([]);
612 + await waitForPaint([]);
613
550 - expect(console.error).toHaveBeenCalledTimes(1);
551 - expect(errorMessage).toContain(
552 - 'Cannot update during an existing state transition',
553 - );
614 + expect(console.error).toHaveBeenCalledTimes(1);
615 + expect(errorMessage).toContain(
616 + 'Cannot update during an existing state transition',
617 + );
618
555 - expect(clearedMarks).toMatchInlineSnapshot(`
619 + expect(clearedMarks).toMatchInlineSnapshot(`
620 [
621 "--render-start-32",
622 "--component-render-start-Example",
@@ -570,43 +634,43 @@ describe('Timeline profiler', () => {
634 "--commit-stop",
635 ]
636 `);
573 - });
637 + });
638
575 - it('should mark render phase force updates for class component', async () => {
576 - let forced = false;
577 - class Example extends React.Component {
578 - render() {
579 - if (!forced) {
580 - forced = true;
581 - this.forceUpdate();
582 - }
583 - return null;
639 + it('should mark render phase force updates for class component', async () => {
640 + let forced = false;
641 + class Example extends React.Component {
642 + render() {
643 + if (!forced) {
644 + forced = true;
645 + this.forceUpdate();
646 + }
647 + return null;
648 + }
649 }
585 - }
650
587 - renderRootHelper(<Example />);
651 + modernRender(<Example />);
652
589 - expect(clearedMarks).toMatchInlineSnapshot(`
653 + expect(clearedMarks).toMatchInlineSnapshot(`
654 [
655 "--schedule-render-32",
656 ]
657 `);
658
595 - clearPendingMarks();
659 + clearPendingMarks();
660
597 - let errorMessage;
598 - jest.spyOn(console, 'error').mockImplementation(message => {
599 - errorMessage = message;
600 - });
661 + let errorMessage;
662 + jest.spyOn(console, 'error').mockImplementation(message => {
663 + errorMessage = message;
664 + });
665
602 - await waitForPaint([]);
666 + await waitForPaint([]);
667
604 - expect(console.error).toHaveBeenCalledTimes(1);
605 - expect(errorMessage).toContain(
606 - 'Cannot update during an existing state transition',
607 - );
668 + expect(console.error).toHaveBeenCalledTimes(1);
669 + expect(errorMessage).toContain(
670 + 'Cannot update during an existing state transition',
671 + );
672
609 - expect(clearedMarks).toMatchInlineSnapshot(`
673 + expect(clearedMarks).toMatchInlineSnapshot(`
674 [
675 "--render-start-32",
676 "--component-render-start-Example",
@@ -624,30 +688,30 @@ describe('Timeline profiler', () => {
688 "--commit-stop",
689 ]
690 `);
627 - });
691 + });
692
629 - it('should mark cascading layout updates', async () => {
630 - function Example() {
631 - const [didMount, setDidMount] = React.useState(false);
632 - React.useLayoutEffect(() => {
633 - setDidMount(true);
634 - }, []);
635 - return didMount;
636 - }
693 + it('should mark cascading layout updates', async () => {
694 + function Example() {
695 + const [didMount, setDidMount] = React.useState(false);
696 + React.useLayoutEffect(() => {
697 + setDidMount(true);
698 + }, []);
699 + return didMount;
700 + }
701
638 - renderRootHelper(<Example />);
702 + modernRender(<Example />);
703
640 - expect(clearedMarks).toMatchInlineSnapshot(`
704 + expect(clearedMarks).toMatchInlineSnapshot(`
705 [
706 "--schedule-render-32",
707 ]
708 `);
709
646 - clearPendingMarks();
710 + clearPendingMarks();
711
648 - await waitForPaint([]);
712 + await waitForPaint([]);
713
650 - expect(clearedMarks).toMatchInlineSnapshot(`
714 + expect(clearedMarks).toMatchInlineSnapshot(`
715 [
716 "--render-start-32",
717 "--component-render-start-Example",
@@ -678,22 +742,22 @@ describe('Timeline profiler', () => {
742 "--commit-stop",
743 ]
744 `);
681 - });
745 + });
746
683 - it('should mark cascading passive updates', async () => {
684 - function Example() {
685 - const [didMount, setDidMount] = React.useState(false);
686 - React.useEffect(() => {
687 - setDidMount(true);
688 - }, []);
689 - return didMount;
690 - }
747 + it('should mark cascading passive updates', async () => {
748 + function Example() {
749 + const [didMount, setDidMount] = React.useState(false);
750 + React.useEffect(() => {
751 + setDidMount(true);
752 + }, []);
753 + return didMount;
754 + }
755
692 - renderRootHelper(<Example />);
756 + modernRender(<Example />);
757
694 - await waitForAll([]);
758 + await waitForAll([]);
759
696 - expect(clearedMarks).toMatchInlineSnapshot(`
760 + expect(clearedMarks).toMatchInlineSnapshot(`
761 [
762 "--schedule-render-32",
763 "--render-start-32",
@@ -727,22 +791,22 @@ describe('Timeline profiler', () => {
791 "--commit-stop",
792 ]
793 `);
730 - });
794 + });
795
732 - it('should mark render phase updates', async () => {
733 - function Example() {
734 - const [didRender, setDidRender] = React.useState(false);
735 - if (!didRender) {
736 - setDidRender(true);
796 + it('should mark render phase updates', async () => {
797 + function Example() {
798 + const [didRender, setDidRender] = React.useState(false);
799 + if (!didRender) {
800 + setDidRender(true);
801 + }
802 + return didRender;
803 }
738 - return didRender;
739 - }
804
741 - renderRootHelper(<Example />);
805 + modernRender(<Example />);
806
743 - await waitForAll([]);
807 + await waitForAll([]);
808
745 - expect(clearedMarks).toMatchInlineSnapshot(`
809 + expect(clearedMarks).toMatchInlineSnapshot(`
810 [
811 "--schedule-render-32",
812 "--render-start-32",
@@ -761,108 +825,46 @@ describe('Timeline profiler', () => {
825 "--commit-stop",
826 ]
827 `);
764 - });
828 + });
829
766 - it('should mark sync render that throws', async () => {
767 - jest.spyOn(console, 'error').mockImplementation(() => {});
830 + it('should mark concurrent render that throws', async () => {
831 + jest.spyOn(console, 'error').mockImplementation(() => {});
832
769 - class ErrorBoundary extends React.Component {
770 - state = {error: null};
771 - componentDidCatch(error) {
772 - this.setState({error});
773 - }
774 - render() {
775 - if (this.state.error) {
776 - return null;
833 + class ErrorBoundary extends React.Component {
834 + state = {error: null};
835 + componentDidCatch(error) {
836 + this.setState({error});
837 }
778 - return this.props.children;
779 - }
780 - }
781 -
782 - function ExampleThatThrows() {
783 - throw Error('Expected error');
784 - }
785 -
786 - renderHelper(
787 - <ErrorBoundary>
788 - <ExampleThatThrows />
789 - </ErrorBoundary>,
790 - );
791 -
792 - expect(clearedMarks).toMatchInlineSnapshot(`
793 - [
794 - "--schedule-render-2",
795 - "--render-start-2",
796 - "--component-render-start-ErrorBoundary",
797 - "--component-render-stop",
798 - "--component-render-start-ExampleThatThrows",
799 - "--component-render-start-ExampleThatThrows",
800 - "--component-render-stop",
801 - "--error-ExampleThatThrows-mount-Expected error",
802 - "--render-stop",
803 - "--commit-start-2",
804 - "--react-version-<filtered-version>",
805 - "--profiler-version-1",
806 - "--react-internal-module-start- at filtered (<anonymous>:0:0)",
807 - "--react-internal-module-stop- at filtered (<anonymous>:1:1)",
808 - "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
809 - "--layout-effects-start-2",
810 - "--schedule-state-update-2-ErrorBoundary",
811 - "--layout-effects-stop",
812 - "--commit-stop",
813 - "--render-start-2",
814 - "--component-render-start-ErrorBoundary",
815 - "--component-render-stop",
816 - "--render-stop",
817 - "--commit-start-2",
818 - "--react-version-<filtered-version>",
819 - "--profiler-version-1",
820 - "--react-internal-module-start- at filtered (<anonymous>:0:0)",
821 - "--react-internal-module-stop- at filtered (<anonymous>:1:1)",
822 - "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
823 - "--commit-stop",
824 - ]
825 - `);
826 - });
827 -
828 - it('should mark concurrent render that throws', async () => {
829 - jest.spyOn(console, 'error').mockImplementation(() => {});
830 -
831 - class ErrorBoundary extends React.Component {
832 - state = {error: null};
833 - componentDidCatch(error) {
834 - this.setState({error});
835 - }
836 - render() {
837 - if (this.state.error) {
838 - return null;
838 + render() {
839 + if (this.state.error) {
840 + return null;
841 + }
842 + return this.props.children;
843 }
840 - return this.props.children;
844 }
842 - }
845
844 - function ExampleThatThrows() {
845 - // eslint-disable-next-line no-throw-literal
846 - throw 'Expected error';
847 - }
846 + function ExampleThatThrows() {
847 + // eslint-disable-next-line no-throw-literal
848 + throw 'Expected error';
849 + }
850
849 - renderRootHelper(
850 - <ErrorBoundary>
851 - <ExampleThatThrows />
852 - </ErrorBoundary>,
853 - );
851 + modernRender(
852 + <ErrorBoundary>
853 + <ExampleThatThrows />
854 + </ErrorBoundary>,
855 + );
856
855 - expect(clearedMarks).toMatchInlineSnapshot(`
857 + expect(clearedMarks).toMatchInlineSnapshot(`
858 [
859 "--schedule-render-32",
860 ]
861 `);
862
861 - clearPendingMarks();
863 + clearPendingMarks();
864
863 - await waitForPaint([]);
865 + await waitForPaint([]);
866
865 - expect(clearedMarks).toMatchInlineSnapshot(`
867 + expect(clearedMarks).toMatchInlineSnapshot(`
868 [
869 "--render-start-32",
870 "--component-render-start-ErrorBoundary",
@@ -903,53 +905,53 @@ describe('Timeline profiler', () => {
905 "--commit-stop",
906 ]
907 `);
906 - });
908 + });
909
908 - it('should mark passive and layout effects', async () => {
909 - function ComponentWithEffects() {
910 - React.useLayoutEffect(() => {
911 - Scheduler.log('layout 1 mount');
912 - return () => {
913 - Scheduler.log('layout 1 unmount');
914 - };
915 - }, []);
916 -
917 - React.useEffect(() => {
918 - Scheduler.log('passive 1 mount');
919 - return () => {
920 - Scheduler.log('passive 1 unmount');
921 - };
922 - }, []);
923 -
924 - React.useLayoutEffect(() => {
925 - Scheduler.log('layout 2 mount');
926 - return () => {
927 - Scheduler.log('layout 2 unmount');
928 - };
929 - }, []);
930 -
931 - React.useEffect(() => {
932 - Scheduler.log('passive 2 mount');
933 - return () => {
934 - Scheduler.log('passive 2 unmount');
935 - };
936 - }, []);
937 -
938 - React.useEffect(() => {
939 - Scheduler.log('passive 3 mount');
940 - return () => {
941 - Scheduler.log('passive 3 unmount');
942 - };
943 - }, []);
910 + it('should mark passive and layout effects', async () => {
911 + function ComponentWithEffects() {
912 + React.useLayoutEffect(() => {
913 + Scheduler.log('layout 1 mount');
914 + return () => {
915 + Scheduler.log('layout 1 unmount');
916 + };
917 + }, []);
918
945 - return null;
946 - }
919 + React.useEffect(() => {
920 + Scheduler.log('passive 1 mount');
921 + return () => {
922 + Scheduler.log('passive 1 unmount');
923 + };
924 + }, []);
925 +
926 + React.useLayoutEffect(() => {
927 + Scheduler.log('layout 2 mount');
928 + return () => {
929 + Scheduler.log('layout 2 unmount');
930 + };
931 + }, []);
932 +
933 + React.useEffect(() => {
934 + Scheduler.log('passive 2 mount');
935 + return () => {
936 + Scheduler.log('passive 2 unmount');
937 + };
938 + }, []);
939 +
940 + React.useEffect(() => {
941 + Scheduler.log('passive 3 mount');
942 + return () => {
943 + Scheduler.log('passive 3 unmount');
944 + };
945 + }, []);
946 +
947 + return null;
948 + }
949
948 - const unmount = renderRootHelper(<ComponentWithEffects />);
950 + const unmount = modernRender(<ComponentWithEffects />);
951
950 - await waitForPaint(['layout 1 mount', 'layout 2 mount']);
952 + await waitForPaint(['layout 1 mount', 'layout 2 mount']);
953
952 - expect(clearedMarks).toMatchInlineSnapshot(`
954 + expect(clearedMarks).toMatchInlineSnapshot(`
955 [
956 "--schedule-render-32",
957 "--render-start-32",
@@ -972,15 +974,15 @@ describe('Timeline profiler', () => {
974 ]
975 `);
976
975 - clearPendingMarks();
977 + clearPendingMarks();
978
977 - await waitForAll([
978 - 'passive 1 mount',
979 - 'passive 2 mount',
980 - 'passive 3 mount',
981 - ]);
979 + await waitForAll([
980 + 'passive 1 mount',
981 + 'passive 2 mount',
982 + 'passive 3 mount',
983 + ]);
984
983 - expect(clearedMarks).toMatchInlineSnapshot(`
985 + expect(clearedMarks).toMatchInlineSnapshot(`
986 [
987 "--passive-effects-start-32",
988 "--component-passive-effect-mount-start-ComponentWithEffects",
@@ -993,21 +995,21 @@ describe('Timeline profiler', () => {
995 ]
996 `);
997
996 - clearPendingMarks();
998 + clearPendingMarks();
999
998 - await waitForAll([]);
1000 + await waitForAll([]);
1001
1000 - unmount();
1002 + unmount();
1003
1002 - assertLog([
1003 - 'layout 1 unmount',
1004 - 'layout 2 unmount',
1005 - 'passive 1 unmount',
1006 - 'passive 2 unmount',
1007 - 'passive 3 unmount',
1008 - ]);
1004 + assertLog([
1005 + 'layout 1 unmount',
1006 + 'layout 2 unmount',
1007 + 'passive 1 unmount',
1008 + 'passive 2 unmount',
1009 + 'passive 3 unmount',
1010 + ]);
1011
1010 - expect(clearedMarks).toMatchInlineSnapshot(`
1012 + expect(clearedMarks).toMatchInlineSnapshot(`
1013 [
1014 "--schedule-render-2",
1015 "--render-start-2",
@@ -1035,61 +1037,78 @@ describe('Timeline profiler', () => {
1037 "--commit-stop",
1038 ]
1039 `);
1040 + });
1041 });
1042
1043 describe('lane labels', () => {
1041 - it('regression test SyncLane', () => {
1042 - renderHelper(<div />);
1044 + describe('with legacy render', () => {
1045 + const {render: legacyRender} = getLegacyRenderImplementation();
1046
1044 - expect(clearedMarks).toMatchInlineSnapshot(`
1047 + // @reactVersion <= 18.2
1048 + // @reactVersion >= 18.0
1049 + it('regression test SyncLane', () => {
1050 + legacyRender(<div />);
1051 +
1052 + expect(clearedMarks).toMatchInlineSnapshot(`
1053 [
1046 - "--schedule-render-2",
1047 - "--render-start-2",
1054 + "--schedule-render-1",
1055 + "--render-start-1",
1056 "--render-stop",
1049 - "--commit-start-2",
1057 + "--commit-start-1",
1058 "--react-version-<filtered-version>",
1059 "--profiler-version-1",
1060 "--react-internal-module-start- at filtered (<anonymous>:0:0)",
1061 "--react-internal-module-stop- at filtered (<anonymous>:1:1)",
1054 - "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
1055 - "--layout-effects-start-2",
1062 + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
1063 + "--layout-effects-start-1",
1064 "--layout-effects-stop",
1065 "--commit-stop",
1066 ]
1067 `);
1068 + });
1069 });
1070
1062 - it('regression test DefaultLane', () => {
1063 - renderRootHelper(<div />);
1064 - expect(clearedMarks).toMatchInlineSnapshot(`
1071 + describe('with createRoot()', () => {
1072 + let waitForAll;
1073 +
1074 + beforeEach(() => {
1075 + const InternalTestUtils = require('internal-test-utils');
1076 + waitForAll = InternalTestUtils.waitForAll;
1077 + });
1078 +
1079 + const {render: modernRender} = getModernRenderImplementation();
1080 +
1081 + it('regression test DefaultLane', () => {
1082 + modernRender(<div />);
1083 + expect(clearedMarks).toMatchInlineSnapshot(`
1084 [
1085 "--schedule-render-32",
1086 ]
1087 `);
1069 - });
1088 + });
1089
1071 - it('regression test InputDiscreteLane', async () => {
1072 - const targetRef = React.createRef(null);
1090 + it('regression test InputDiscreteLane', async () => {
1091 + const targetRef = React.createRef(null);
1092
1074 - function App() {
1075 - const [count, setCount] = React.useState(0);
1076 - const handleClick = () => {
1077 - setCount(count + 1);
1078 - };
1079 - return <button ref={targetRef} onClick={handleClick} />;
1080 - }
1093 + function App() {
1094 + const [count, setCount] = React.useState(0);
1095 + const handleClick = () => {
1096 + setCount(count + 1);
1097 + };
1098 + return <button ref={targetRef} onClick={handleClick} />;
1099 + }
1100
1082 - renderRootHelper(<App />);
1083 - await waitForAll([]);
1101 + modernRender(<App />);
1102 + await waitForAll([]);
1103
1085 - clearedMarks.splice(0);
1104 + clearedMarks.splice(0);
1105
1087 - targetRef.current.click();
1106 + targetRef.current.click();
1107
1089 - // Wait a frame, for React to process the "click" update.
1090 - await Promise.resolve();
1108 + // Wait a frame, for React to process the "click" update.
1109 + await Promise.resolve();
1110
1092 - expect(clearedMarks).toMatchInlineSnapshot(`
1111 + expect(clearedMarks).toMatchInlineSnapshot(`
1112 [
1113 "--schedule-state-update-2-App",
1114 "--render-start-2",
@@ -1107,29 +1126,29 @@ describe('Timeline profiler', () => {
1126 "--commit-stop",
1127 ]
1128 `);
1110 - });
1129 + });
1130
1112 - it('regression test InputContinuousLane', async () => {
1113 - const targetRef = React.createRef(null);
1131 + it('regression test InputContinuousLane', async () => {
1132 + const targetRef = React.createRef(null);
1133
1115 - function App() {
1116 - const [count, setCount] = React.useState(0);
1117 - const handleMouseOver = () => setCount(count + 1);
1118 - return <div ref={targetRef} onMouseOver={handleMouseOver} />;
1119 - }
1134 + function App() {
1135 + const [count, setCount] = React.useState(0);
1136 + const handleMouseOver = () => setCount(count + 1);
1137 + return <div ref={targetRef} onMouseOver={handleMouseOver} />;
1138 + }
1139
1121 - renderRootHelper(<App />);
1122 - await waitForAll([]);
1140 + modernRender(<App />);
1141 + await waitForAll([]);
1142
1124 - clearedMarks.splice(0);
1143 + clearedMarks.splice(0);
1144
1126 - const event = document.createEvent('MouseEvents');
1127 - event.initEvent('mouseover', true, true);
1128 - dispatchAndSetCurrentEvent(targetRef.current, event);
1145 + const event = document.createEvent('MouseEvents');
1146 + event.initEvent('mouseover', true, true);
1147 + dispatchAndSetCurrentEvent(targetRef.current, event);
1148
1130 - await waitForAll([]);
1149 + await waitForAll([]);
1150
1132 - expect(clearedMarks).toMatchInlineSnapshot(`
1151 + expect(clearedMarks).toMatchInlineSnapshot(`
1152 [
1153 "--schedule-state-update-8-App",
1154 "--render-start-8",
@@ -1147,6 +1166,7 @@ describe('Timeline profiler', () => {
1166 "--commit-stop",
1167 ]
1168 `);
1169 + });
1170 });
1171 });
1172 });
@@ -1196,181 +1216,170 @@ describe('Timeline profiler', () => {
1216 };
1217 });
1218
1199 - afterEach(() => {
1200 - unmountFns.forEach(unmountFn => unmountFn());
1201 - });
1202 -
1219 describe('when profiling', () => {
1220 beforeEach(() => {
1221 utils.act(() => store.profilerStore.startProfiling());
1222 });
1223
1208 - it('should mark sync render without suspends or state updates', () => {
1209 - renderHelper(<div />);
1224 + describe('with legacy render', () => {
1225 + const {render: legacyRender} = getLegacyRenderImplementation();
1226
1211 - const timelineData = stopProfilingAndGetTimelineData();
1212 - expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
1227 + // @reactVersion <= 18.2
1228 + // @reactVersion >= 18.0
1229 + it('should mark sync render without suspends or state updates', () => {
1230 + legacyRender(<div />);
1231 +
1232 + const timelineData = stopProfilingAndGetTimelineData();
1233 + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
1234 [
1235 {
1215 - "lanes": "0b0000000000000000000000000000010",
1236 + "lanes": "0b0000000000000000000000000000001",
1237 "timestamp": 10,
1238 "type": "schedule-render",
1239 "warning": null,
1240 },
1241 ]
1242 `);
1222 - });
1243 + });
1244
1224 - it('should mark concurrent render without suspends or state updates', () => {
1225 - utils.act(() => renderRootHelper(<div />));
1245 + // @reactVersion <= 18.2
1246 + // @reactVersion >= 18.0
1247 + it('should mark sync render that throws', async () => {
1248 + jest.spyOn(console, 'error').mockImplementation(() => {});
1249
1227 - const timelineData = stopProfilingAndGetTimelineData();
1228 - expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
1250 + class ErrorBoundary extends React.Component {
1251 + state = {error: null};
1252 + componentDidCatch(error) {
1253 + this.setState({error});
1254 + }
1255 + render() {
1256 + Scheduler.unstable_advanceTime(10);
1257 + if (this.state.error) {
1258 + Scheduler.unstable_yieldValue('ErrorBoundary fallback');
1259 + return null;
1260 + }
1261 + Scheduler.unstable_yieldValue('ErrorBoundary render');
1262 + return this.props.children;
1263 + }
1264 + }
1265 +
1266 + function ExampleThatThrows() {
1267 + Scheduler.unstable_yieldValue('ExampleThatThrows');
1268 + throw Error('Expected error');
1269 + }
1270 +
1271 + legacyRender(
1272 + <ErrorBoundary>
1273 + <ExampleThatThrows />
1274 + </ErrorBoundary>,
1275 + );
1276 +
1277 + expect(Scheduler.unstable_clearYields()).toEqual([
1278 + 'ErrorBoundary render',
1279 + 'ExampleThatThrows',
1280 + 'ExampleThatThrows',
1281 + 'ErrorBoundary fallback',
1282 + ]);
1283 +
1284 + const timelineData = stopProfilingAndGetTimelineData();
1285 + expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
1286 [
1287 {
1231 - "lanes": "0b0000000000000000000000000100000",
1288 + "componentName": "ErrorBoundary",
1289 + "duration": 10,
1290 "timestamp": 10,
1233 - "type": "schedule-render",
1291 + "type": "render",
1292 + "warning": null,
1293 + },
1294 + {
1295 + "componentName": "ExampleThatThrows",
1296 + "duration": 0,
1297 + "timestamp": 20,
1298 + "type": "render",
1299 + "warning": null,
1300 + },
1301 + {
1302 + "componentName": "ErrorBoundary",
1303 + "duration": 10,
1304 + "timestamp": 20,
1305 + "type": "render",
1306 "warning": null,
1307 },
1308 ]
1309 `);
1238 - });
1239 -
1240 - it('should mark concurrent render without suspends or state updates', () => {
1241 - let updaterFn;
1242 -
1243 - function Example() {
1244 - const setHigh = React.useState(0)[1];
1245 - const setLow = React.useState(0)[1];
1246 -
1247 - updaterFn = () => {
1248 - React.startTransition(() => {
1249 - setLow(prevLow => prevLow + 1);
1250 - });
1251 - setHigh(prevHigh => prevHigh + 1);
1252 - };
1253 -
1254 - Scheduler.unstable_advanceTime(10);
1255 -
1256 - return null;
1257 - }
1258 -
1259 - utils.act(() => renderRootHelper(<Example />));
1260 - utils.act(() => store.profilerStore.stopProfiling());
1261 - utils.act(() => store.profilerStore.startProfiling());
1262 - utils.act(updaterFn);
1263 -
1264 - const timelineData = stopProfilingAndGetTimelineData();
1265 - expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
1310 + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
1311 [
1312 {
1268 - "componentName": "Example",
1269 - "componentStack": "
1270 - in Example (at **)",
1271 - "lanes": "0b0000000000000000000000010000000",
1313 + "lanes": "0b0000000000000000000000000000001",
1314 "timestamp": 10,
1273 - "type": "schedule-state-update",
1315 + "type": "schedule-render",
1316 "warning": null,
1317 },
1318 {
1277 - "componentName": "Example",
1319 + "componentName": "ErrorBoundary",
1320 "componentStack": "
1279 - in Example (at **)",
1280 - "lanes": "0b0000000000000000000000000100000",
1281 - "timestamp": 10,
1321 + in ErrorBoundary (at **)",
1322 + "lanes": "0b0000000000000000000000000000001",
1323 + "timestamp": 20,
1324 "type": "schedule-state-update",
1325 "warning": null,
1326 },
1327 ]
1328 `);
1287 - expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
1329 + expect(timelineData.thrownErrors).toMatchInlineSnapshot(`
1330 [
1331 {
1290 - "componentName": "Example",
1291 - "duration": 0,
1292 - "timestamp": 10,
1293 - "type": "render",
1294 - "warning": null,
1295 - },
1296 - {
1297 - "componentName": "Example",
1298 - "duration": 10,
1299 - "timestamp": 10,
1300 - "type": "render",
1301 - "warning": null,
1332 + "componentName": "ExampleThatThrows",
1333 + "message": "Expected error",
1334 + "phase": "mount",
1335 + "timestamp": 20,
1336 + "type": "thrown-error",
1337 },
1338 ]
1339 `);
1305 - expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1306 - });
1307 -
1308 - // @reactVersion >=18.2
1309 - it('should mark render yields', async () => {
1310 - function Bar() {
1311 - Scheduler.log('Bar');
1312 - return null;
1313 - }
1314 -
1315 - function Foo() {
1316 - Scheduler.log('Foo');
1317 - return <Bar />;
1318 - }
1319 -
1320 - React.startTransition(() => {
1321 - renderRootHelper(<Foo />);
1340 });
1341
1324 - // Do one step of work.
1325 - await waitFor(['Foo']);
1326 -
1327 - // Finish flushing so React commits;
1328 - // Unless we do this, the ProfilerStore won't collect Profiling data.
1329 - await waitForAll(['Bar']);
1330 -
1331 - // Since we yielded, the batch should report two separate "render" chunks.
1332 - const batch = getBatchOfWork(0);
1333 - expect(batch.filter(({type}) => type === 'render')).toHaveLength(2);
1334 - });
1335 -
1336 - it('should mark sync render with suspense that resolves', async () => {
1337 - let resolveFn;
1338 - let resolved = false;
1339 - const suspensePromise = new Promise(resolve => {
1340 - resolveFn = () => {
1341 - resolved = true;
1342 - resolve();
1343 - };
1344 - });
1342 + // @reactVersion <= 18.2
1343 + // @reactVersion >= 18.0
1344 + it('should mark sync render with suspense that resolves', async () => {
1345 + let resolveFn;
1346 + let resolved = false;
1347 + const suspensePromise = new Promise(resolve => {
1348 + resolveFn = () => {
1349 + resolved = true;
1350 + resolve();
1351 + };
1352 + });
1353
1346 - function Example() {
1347 - Scheduler.log(resolved ? 'resolved' : 'suspended');
1348 - if (!resolved) {
1349 - throw suspensePromise;
1354 + function Example() {
1355 + Scheduler.unstable_yieldValue(resolved ? 'resolved' : 'suspended');
1356 + if (!resolved) {
1357 + throw suspensePromise;
1358 + }
1359 + return null;
1360 }
1351 - return null;
1352 - }
1361
1354 - renderHelper(
1355 - <React.Suspense fallback={null}>
1356 - <Example />
1357 - </React.Suspense>,
1358 - );
1362 + legacyRender(
1363 + <React.Suspense fallback={null}>
1364 + <Example />
1365 + </React.Suspense>,
1366 + );
1367
1360 - assertLog(['suspended']);
1368 + expect(Scheduler.unstable_clearYields()).toEqual(['suspended']);
1369
1362 - Scheduler.unstable_advanceTime(10);
1363 - resolveFn();
1364 - await suspensePromise;
1370 + Scheduler.unstable_advanceTime(10);
1371 + resolveFn();
1372 + await suspensePromise;
1373
1366 - await waitForAll(['resolved']);
1374 + await Scheduler.unstable_flushAllWithoutAsserting();
1375 + expect(Scheduler.unstable_clearYields()).toEqual(['resolved']);
1376
1368 - const timelineData = stopProfilingAndGetTimelineData();
1377 + const timelineData = stopProfilingAndGetTimelineData();
1378
1370 - // Verify the Suspense event and duration was recorded.
1371 - expect(timelineData.suspenseEvents).toHaveLength(1);
1372 - const suspenseEvent = timelineData.suspenseEvents[0];
1373 - expect(suspenseEvent).toMatchInlineSnapshot(`
1379 + // Verify the Suspense event and duration was recorded.
1380 + expect(timelineData.suspenseEvents).toHaveLength(1);
1381 + const suspenseEvent = timelineData.suspenseEvents[0];
1382 + expect(suspenseEvent).toMatchInlineSnapshot(`
1383 {
1384 "componentName": "Example",
1385 "depth": 0,
@@ -1385,50 +1394,50 @@ describe('Timeline profiler', () => {
1394 }
1395 `);
1396
1388 - // There should be two batches of renders: Suspeneded and resolved.
1389 - expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1390 - expect(timelineData.componentMeasures).toHaveLength(2);
1391 - });
1392 -
1393 - // @reactVersion >=18.2
1394 - it('should mark sync render with suspense that rejects', async () => {
1395 - let rejectFn;
1396 - let rejected = false;
1397 - const suspensePromise = new Promise((resolve, reject) => {
1398 - rejectFn = () => {
1399 - rejected = true;
1400 - reject(new Error('error'));
1401 - };
1397 + // There should be two batches of renders: Suspeneded and resolved.
1398 + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1399 + expect(timelineData.componentMeasures).toHaveLength(2);
1400 });
1401
1404 - function Example() {
1405 - Scheduler.log(rejected ? 'rejected' : 'suspended');
1406 - if (!rejected) {
1407 - throw suspensePromise;
1402 + // @reactVersion = 18.2
1403 + it('should mark sync render with suspense that rejects', async () => {
1404 + let rejectFn;
1405 + let rejected = false;
1406 + const suspensePromise = new Promise((resolve, reject) => {
1407 + rejectFn = () => {
1408 + rejected = true;
1409 + reject(new Error('error'));
1410 + };
1411 + });
1412 +
1413 + function Example() {
1414 + Scheduler.unstable_yieldValue(rejected ? 'rejected' : 'suspended');
1415 + if (!rejected) {
1416 + throw suspensePromise;
1417 + }
1418 + return null;
1419 }
1409 - return null;
1410 - }
1420
1412 - renderHelper(
1413 - <React.Suspense fallback={null}>
1414 - <Example />
1415 - </React.Suspense>,
1416 - );
1421 + legacyRender(
1422 + <React.Suspense fallback={null}>
1423 + <Example />
1424 + </React.Suspense>,
1425 + );
1426
1418 - assertLog(['suspended']);
1427 + expect(Scheduler.unstable_clearYields()).toEqual(['suspended']);
1428
1420 - Scheduler.unstable_advanceTime(10);
1421 - rejectFn();
1422 - await expect(suspensePromise).rejects.toThrow();
1429 + Scheduler.unstable_advanceTime(10);
1430 + rejectFn();
1431 + await expect(suspensePromise).rejects.toThrow();
1432
1424 - assertLog(['rejected']);
1433 + expect(Scheduler.unstable_clearYields()).toEqual(['rejected']);
1434
1426 - const timelineData = stopProfilingAndGetTimelineData();
1435 + const timelineData = stopProfilingAndGetTimelineData();
1436
1428 - // Verify the Suspense event and duration was recorded.
1429 - expect(timelineData.suspenseEvents).toHaveLength(1);
1430 - const suspenseEvent = timelineData.suspenseEvents[0];
1431 - expect(suspenseEvent).toMatchInlineSnapshot(`
1437 + // Verify the Suspense event and duration was recorded.
1438 + expect(timelineData.suspenseEvents).toHaveLength(1);
1439 + const suspenseEvent = timelineData.suspenseEvents[0];
1440 + expect(suspenseEvent).toMatchInlineSnapshot(`
1441 {
1442 "componentName": "Example",
1443 "depth": 0,
@@ -1443,50 +1452,177 @@ describe('Timeline profiler', () => {
1452 }
1453 `);
1454
1446 - // There should be two batches of renders: Suspeneded and resolved.
1447 - expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1448 - expect(timelineData.componentMeasures).toHaveLength(2);
1455 + // There should be two batches of renders: Suspeneded and resolved.
1456 + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1457 + expect(timelineData.componentMeasures).toHaveLength(2);
1458 + });
1459 });
1460
1451 - // @reactVersion >=18.2
1452 - it('should mark concurrent render with suspense that resolves', async () => {
1453 - let resolveFn;
1454 - let resolved = false;
1455 - const suspensePromise = new Promise(resolve => {
1456 - resolveFn = () => {
1457 - resolved = true;
1458 - resolve();
1459 - };
1461 + describe('with createRoot()', () => {
1462 + let waitFor;
1463 + let waitForAll;
1464 + let waitForPaint;
1465 + let assertLog;
1466 +
1467 + beforeEach(() => {
1468 + const InternalTestUtils = require('internal-test-utils');
1469 + waitFor = InternalTestUtils.waitFor;
1470 + waitForAll = InternalTestUtils.waitForAll;
1471 + waitForPaint = InternalTestUtils.waitForPaint;
1472 + assertLog = InternalTestUtils.assertLog;
1473 });
1474
1462 - function Example() {
1463 - Scheduler.log(resolved ? 'resolved' : 'suspended');
1464 - if (!resolved) {
1465 - throw suspensePromise;
1475 + const {render: modernRender} = getModernRenderImplementation();
1476 +
1477 + it('should mark concurrent render without suspends or state updates', () => {
1478 + utils.act(() => modernRender(<div />));
1479 +
1480 + const timelineData = stopProfilingAndGetTimelineData();
1481 + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
1482 + [
1483 + {
1484 + "lanes": "0b0000000000000000000000000100000",
1485 + "timestamp": 10,
1486 + "type": "schedule-render",
1487 + "warning": null,
1488 + },
1489 + ]
1490 + `);
1491 + });
1492 +
1493 + it('should mark concurrent render without suspends or state updates', () => {
1494 + let updaterFn;
1495 +
1496 + function Example() {
1497 + const setHigh = React.useState(0)[1];
1498 + const setLow = React.useState(0)[1];
1499 +
1500 + updaterFn = () => {
1501 + React.startTransition(() => {
1502 + setLow(prevLow => prevLow + 1);
1503 + });
1504 + setHigh(prevHigh => prevHigh + 1);
1505 + };
1506 +
1507 + Scheduler.unstable_advanceTime(10);
1508 +
1509 + return null;
1510 }
1467 - return null;
1468 - }
1511
1470 - renderRootHelper(
1471 - <React.Suspense fallback={null}>
1472 - <Example />
1473 - </React.Suspense>,
1474 - );
1512 + utils.act(() => modernRender(<Example />));
1513 + utils.act(() => store.profilerStore.stopProfiling());
1514 + utils.act(() => store.profilerStore.startProfiling());
1515 + utils.act(updaterFn);
1516
1476 - await waitForAll(['suspended']);
1517 + const timelineData = stopProfilingAndGetTimelineData();
1518 + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
1519 + [
1520 + {
1521 + "componentName": "Example",
1522 + "componentStack": "
1523 + in Example (at **)",
1524 + "lanes": "0b0000000000000000000000010000000",
1525 + "timestamp": 10,
1526 + "type": "schedule-state-update",
1527 + "warning": null,
1528 + },
1529 + {
1530 + "componentName": "Example",
1531 + "componentStack": "
1532 + in Example (at **)",
1533 + "lanes": "0b0000000000000000000000000100000",
1534 + "timestamp": 10,
1535 + "type": "schedule-state-update",
1536 + "warning": null,
1537 + },
1538 + ]
1539 + `);
1540 + expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
1541 + [
1542 + {
1543 + "componentName": "Example",
1544 + "duration": 0,
1545 + "timestamp": 10,
1546 + "type": "render",
1547 + "warning": null,
1548 + },
1549 + {
1550 + "componentName": "Example",
1551 + "duration": 10,
1552 + "timestamp": 10,
1553 + "type": "render",
1554 + "warning": null,
1555 + },
1556 + ]
1557 + `);
1558 + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1559 + });
1560
1478 - Scheduler.unstable_advanceTime(10);
1479 - resolveFn();
1480 - await suspensePromise;
1561 + it('should mark render yields', async () => {
1562 + function Bar() {
1563 + Scheduler.log('Bar');
1564 + return null;
1565 + }
1566
1482 - await waitForAll(['resolved']);
1567 + function Foo() {
1568 + Scheduler.log('Foo');
1569 + return <Bar />;
1570 + }
1571
1484 - const timelineData = stopProfilingAndGetTimelineData();
1572 + React.startTransition(() => {
1573 + modernRender(<Foo />);
1574 + });
1575 +
1576 + // Do one step of work.
1577 + await waitFor(['Foo']);
1578 +
1579 + // Finish flushing so React commits;
1580 + // Unless we do this, the ProfilerStore won't collect Profiling data.
1581 + await waitForAll(['Bar']);
1582 +
1583 + // Since we yielded, the batch should report two separate "render" chunks.
1584 + const batch = getBatchOfWork(0);
1585 + expect(batch.filter(({type}) => type === 'render')).toHaveLength(2);
1586 + });
1587 +
1588 + it('should mark concurrent render with suspense that resolves', async () => {
1589 + let resolveFn;
1590 + let resolved = false;
1591 + const suspensePromise = new Promise(resolve => {
1592 + resolveFn = () => {
1593 + resolved = true;
1594 + resolve();
1595 + };
1596 + });
1597 +
1598 + function Example() {
1599 + Scheduler.log(resolved ? 'resolved' : 'suspended');
1600 + if (!resolved) {
1601 + throw suspensePromise;
1602 + }
1603 + return null;
1604 + }
1605 +
1606 + modernRender(
1607 + <React.Suspense fallback={null}>
1608 + <Example />
1609 + </React.Suspense>,
1610 + );
1611
1486 - // Verify the Suspense event and duration was recorded.
1487 - expect(timelineData.suspenseEvents).toHaveLength(1);
1488 - const suspenseEvent = timelineData.suspenseEvents[0];
1489 - expect(suspenseEvent).toMatchInlineSnapshot(`
1612 + await waitForAll(['suspended']);
1613 +
1614 + Scheduler.unstable_advanceTime(10);
1615 + resolveFn();
1616 + await suspensePromise;
1617 +
1618 + await waitForAll(['resolved']);
1619 +
1620 + const timelineData = stopProfilingAndGetTimelineData();
1621 +
1622 + // Verify the Suspense event and duration was recorded.
1623 + expect(timelineData.suspenseEvents).toHaveLength(1);
1624 + const suspenseEvent = timelineData.suspenseEvents[0];
1625 + expect(suspenseEvent).toMatchInlineSnapshot(`
1626 {
1627 "componentName": "Example",
1628 "depth": 0,
@@ -1501,50 +1637,49 @@ describe('Timeline profiler', () => {
1637 }
1638 `);
1639
1504 - // There should be two batches of renders: Suspeneded and resolved.
1505 - expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1506 - expect(timelineData.componentMeasures).toHaveLength(2);
1507 - });
1508 -
1509 - // @reactVersion >=18.2
1510 - it('should mark concurrent render with suspense that rejects', async () => {
1511 - let rejectFn;
1512 - let rejected = false;
1513 - const suspensePromise = new Promise((resolve, reject) => {
1514 - rejectFn = () => {
1515 - rejected = true;
1516 - reject(new Error('error'));
1517 - };
1640 + // There should be two batches of renders: Suspeneded and resolved.
1641 + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1642 + expect(timelineData.componentMeasures).toHaveLength(2);
1643 });
1644
1520 - function Example() {
1521 - Scheduler.log(rejected ? 'rejected' : 'suspended');
1522 - if (!rejected) {
1523 - throw suspensePromise;
1645 + it('should mark concurrent render with suspense that rejects', async () => {
1646 + let rejectFn;
1647 + let rejected = false;
1648 + const suspensePromise = new Promise((resolve, reject) => {
1649 + rejectFn = () => {
1650 + rejected = true;
1651 + reject(new Error('error'));
1652 + };
1653 + });
1654 +
1655 + function Example() {
1656 + Scheduler.log(rejected ? 'rejected' : 'suspended');
1657 + if (!rejected) {
1658 + throw suspensePromise;
1659 + }
1660 + return null;
1661 }
1525 - return null;
1526 - }
1662
1528 - renderRootHelper(
1529 - <React.Suspense fallback={null}>
1530 - <Example />
1531 - </React.Suspense>,
1532 - );
1663 + modernRender(
1664 + <React.Suspense fallback={null}>
1665 + <Example />
1666 + </React.Suspense>,
1667 + );
1668
1534 - await waitForAll(['suspended']);
1669 + await waitForAll(['suspended']);
1670
1536 - Scheduler.unstable_advanceTime(10);
1537 - rejectFn();
1538 - await expect(suspensePromise).rejects.toThrow();
1671 + Scheduler.unstable_advanceTime(10);
1672 + rejectFn();
1673 + await expect(suspensePromise).rejects.toThrow();
1674
1540 - await waitForAll(['rejected']);
1675 + await waitForAll(['rejected']);
1676
1542 - const timelineData = stopProfilingAndGetTimelineData();
1677 + const timelineData = stopProfilingAndGetTimelineData();
1678
1544 - // Verify the Suspense event and duration was recorded.
1545 - expect(timelineData.suspenseEvents).toHaveLength(1);
1546 - const suspenseEvent = timelineData.suspenseEvents[0];
1547 - expect(suspenseEvent).toMatchInlineSnapshot(`
1679 + // Verify the Suspense event and duration was recorded.
1680 + expect(timelineData.suspenseEvents).toHaveLength(1);
1681 + const suspenseEvent = timelineData.suspenseEvents[0];
1682 + expect(suspenseEvent).toMatchInlineSnapshot(`
1683 {
1684 "componentName": "Example",
1685 "depth": 0,
@@ -1559,31 +1694,31 @@ describe('Timeline profiler', () => {
1694 }
1695 `);
1696
1562 - // There should be two batches of renders: Suspeneded and resolved.
1563 - expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1564 - expect(timelineData.componentMeasures).toHaveLength(2);
1565 - });
1697 + // There should be two batches of renders: Suspeneded and resolved.
1698 + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1699 + expect(timelineData.componentMeasures).toHaveLength(2);
1700 + });
1701
1567 - it('should mark cascading class component state updates', async () => {
1568 - class Example extends React.Component {
1569 - state = {didMount: false};
1570 - componentDidMount() {
1571 - this.setState({didMount: true});
1572 - }
1573 - render() {
1574 - Scheduler.unstable_advanceTime(10);
1575 - Scheduler.log(this.state.didMount ? 'update' : 'mount');
1576 - return null;
1702 + it('should mark cascading class component state updates', async () => {
1703 + class Example extends React.Component {
1704 + state = {didMount: false};
1705 + componentDidMount() {
1706 + this.setState({didMount: true});
1707 + }
1708 + render() {
1709 + Scheduler.unstable_advanceTime(10);
1710 + Scheduler.log(this.state.didMount ? 'update' : 'mount');
1711 + return null;
1712 + }
1713 }
1578 - }
1714
1580 - renderRootHelper(<Example />);
1715 + modernRender(<Example />);
1716
1582 - await waitForPaint(['mount', 'update']);
1717 + await waitForPaint(['mount', 'update']);
1718
1584 - const timelineData = stopProfilingAndGetTimelineData();
1585 - expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1586 - expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
1719 + const timelineData = stopProfilingAndGetTimelineData();
1720 + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1721 + expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
1722 [
1723 {
1724 "componentName": "Example",
@@ -1601,7 +1736,7 @@ describe('Timeline profiler', () => {
1736 },
1737 ]
1738 `);
1604 - expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
1739 + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
1740 [
1741 {
1742 "lanes": "0b0000000000000000000000000100000",
@@ -1620,29 +1755,29 @@ describe('Timeline profiler', () => {
1755 },
1756 ]
1757 `);
1623 - });
1758 + });
1759
1625 - it('should mark cascading class component force updates', async () => {
1626 - let forced = false;
1627 - class Example extends React.Component {
1628 - componentDidMount() {
1629 - forced = true;
1630 - this.forceUpdate();
1631 - }
1632 - render() {
1633 - Scheduler.unstable_advanceTime(10);
1634 - Scheduler.log(forced ? 'force update' : 'mount');
1635 - return null;
1760 + it('should mark cascading class component force updates', async () => {
1761 + let forced = false;
1762 + class Example extends React.Component {
1763 + componentDidMount() {
1764 + forced = true;
1765 + this.forceUpdate();
1766 + }
1767 + render() {
1768 + Scheduler.unstable_advanceTime(10);
1769 + Scheduler.log(forced ? 'force update' : 'mount');
1770 + return null;
1771 + }
1772 }
1637 - }
1773
1639 - renderRootHelper(<Example />);
1774 + modernRender(<Example />);
1775
1641 - await waitForPaint(['mount', 'force update']);
1776 + await waitForPaint(['mount', 'force update']);
1777
1643 - const timelineData = stopProfilingAndGetTimelineData();
1644 - expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1645 - expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
1778 + const timelineData = stopProfilingAndGetTimelineData();
1779 + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1780 + expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
1781 [
1782 {
1783 "componentName": "Example",
@@ -1660,7 +1795,7 @@ describe('Timeline profiler', () => {
1795 },
1796 ]
1797 `);
1663 - expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
1798 + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
1799 [
1800 {
1801 "lanes": "0b0000000000000000000000000100000",
@@ -1677,40 +1812,40 @@ describe('Timeline profiler', () => {
1812 },
1813 ]
1814 `);
1680 - });
1815 + });
1816
1682 - it('should mark render phase state updates for class component', async () => {
1683 - class Example extends React.Component {
1684 - state = {didRender: false};
1685 - render() {
1686 - if (this.state.didRender === false) {
1687 - this.setState({didRender: true});
1817 + it('should mark render phase state updates for class component', async () => {
1818 + class Example extends React.Component {
1819 + state = {didRender: false};
1820 + render() {
1821 + if (this.state.didRender === false) {
1822 + this.setState({didRender: true});
1823 + }
1824 + Scheduler.unstable_advanceTime(10);
1825 + Scheduler.log(
1826 + this.state.didRender ? 'second render' : 'first render',
1827 + );
1828 + return null;
1829 }
1689 - Scheduler.unstable_advanceTime(10);
1690 - Scheduler.log(
1691 - this.state.didRender ? 'second render' : 'first render',
1692 - );
1693 - return null;
1830 }
1695 - }
1696 -
1697 - renderRootHelper(<Example />);
1698 -
1699 - let errorMessage;
1700 - jest.spyOn(console, 'error').mockImplementation(message => {
1701 - errorMessage = message;
1702 - });
1831
1704 - await waitForAll(['first render', 'second render']);
1832 + modernRender(<Example />);
1833
1706 - expect(console.error).toHaveBeenCalledTimes(1);
1707 - expect(errorMessage).toContain(
1708 - 'Cannot update during an existing state transition',
1709 - );
1834 + let errorMessage;
1835 + jest.spyOn(console, 'error').mockImplementation(message => {
1836 + errorMessage = message;
1837 + });
1838
1711 - const timelineData = stopProfilingAndGetTimelineData();
1712 - expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1713 - expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
1839 + await waitForAll(['first render', 'second render']);
1840 +
1841 + expect(console.error).toHaveBeenCalledTimes(1);
1842 + expect(errorMessage).toContain(
1843 + 'Cannot update during an existing state transition',
1844 + );
1845 +
1846 + const timelineData = stopProfilingAndGetTimelineData();
1847 + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1848 + expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
1849 [
1850 {
1851 "componentName": "Example",
@@ -1728,7 +1863,7 @@ describe('Timeline profiler', () => {
1863 },
1864 ]
1865 `);
1731 - expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
1866 + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
1867 [
1868 {
1869 "lanes": "0b0000000000000000000000000100000",
@@ -1747,39 +1882,39 @@ describe('Timeline profiler', () => {
1882 },
1883 ]
1884 `);
1750 - });
1885 + });
1886
1752 - it('should mark render phase force updates for class component', async () => {
1753 - let forced = false;
1754 - class Example extends React.Component {
1755 - render() {
1756 - Scheduler.unstable_advanceTime(10);
1757 - Scheduler.log(forced ? 'force update' : 'render');
1758 - if (!forced) {
1759 - forced = true;
1760 - this.forceUpdate();
1887 + it('should mark render phase force updates for class component', async () => {
1888 + let forced = false;
1889 + class Example extends React.Component {
1890 + render() {
1891 + Scheduler.unstable_advanceTime(10);
1892 + Scheduler.log(forced ? 'force update' : 'render');
1893 + if (!forced) {
1894 + forced = true;
1895 + this.forceUpdate();
1896 + }
1897 + return null;
1898 }
1762 - return null;
1899 }
1764 - }
1900
1766 - renderRootHelper(<Example />);
1901 + modernRender(<Example />);
1902
1768 - let errorMessage;
1769 - jest.spyOn(console, 'error').mockImplementation(message => {
1770 - errorMessage = message;
1771 - });
1903 + let errorMessage;
1904 + jest.spyOn(console, 'error').mockImplementation(message => {
1905 + errorMessage = message;
1906 + });
1907
1773 - await waitForAll(['render', 'force update']);
1908 + await waitForAll(['render', 'force update']);
1909
1775 - expect(console.error).toHaveBeenCalledTimes(1);
1776 - expect(errorMessage).toContain(
1777 - 'Cannot update during an existing state transition',
1778 - );
1910 + expect(console.error).toHaveBeenCalledTimes(1);
1911 + expect(errorMessage).toContain(
1912 + 'Cannot update during an existing state transition',
1913 + );
1914
1780 - const timelineData = stopProfilingAndGetTimelineData();
1781 - expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1782 - expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
1915 + const timelineData = stopProfilingAndGetTimelineData();
1916 + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1917 + expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
1918 [
1919 {
1920 "componentName": "Example",
@@ -1797,7 +1932,7 @@ describe('Timeline profiler', () => {
1932 },
1933 ]
1934 `);
1800 - expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
1935 + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
1936 [
1937 {
1938 "lanes": "0b0000000000000000000000000100000",
@@ -1814,27 +1949,27 @@ describe('Timeline profiler', () => {
1949 },
1950 ]
1951 `);
1817 - });
1952 + });
1953
1819 - it('should mark cascading layout updates', async () => {
1820 - function Example() {
1821 - const [didMount, setDidMount] = React.useState(false);
1822 - React.useLayoutEffect(() => {
1823 - Scheduler.unstable_advanceTime(1);
1824 - setDidMount(true);
1825 - }, []);
1826 - Scheduler.unstable_advanceTime(10);
1827 - Scheduler.log(didMount ? 'update' : 'mount');
1828 - return didMount;
1829 - }
1954 + it('should mark cascading layout updates', async () => {
1955 + function Example() {
1956 + const [didMount, setDidMount] = React.useState(false);
1957 + React.useLayoutEffect(() => {
1958 + Scheduler.unstable_advanceTime(1);
1959 + setDidMount(true);
1960 + }, []);
1961 + Scheduler.unstable_advanceTime(10);
1962 + Scheduler.log(didMount ? 'update' : 'mount');
1963 + return didMount;
1964 + }
1965
1831 - renderRootHelper(<Example />);
1966 + modernRender(<Example />);
1967
1833 - await waitForAll(['mount', 'update']);
1968 + await waitForAll(['mount', 'update']);
1969
1835 - const timelineData = stopProfilingAndGetTimelineData();
1836 - expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1837 - expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
1970 + const timelineData = stopProfilingAndGetTimelineData();
1971 + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1972 + expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
1973 [
1974 {
1975 "componentName": "Example",
@@ -1859,7 +1994,7 @@ describe('Timeline profiler', () => {
1994 },
1995 ]
1996 `);
1862 - expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
1997 + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
1998 [
1999 {
2000 "lanes": "0b0000000000000000000000000100000",
@@ -1878,26 +2013,26 @@ describe('Timeline profiler', () => {
2013 },
2014 ]
2015 `);
1881 - });
2016 + });
2017
1883 - it('should mark cascading passive updates', async () => {
1884 - function Example() {
1885 - const [didMount, setDidMount] = React.useState(false);
1886 - React.useEffect(() => {
1887 - Scheduler.unstable_advanceTime(1);
1888 - setDidMount(true);
1889 - }, []);
1890 - Scheduler.unstable_advanceTime(10);
1891 - Scheduler.log(didMount ? 'update' : 'mount');
1892 - return didMount;
1893 - }
2018 + it('should mark cascading passive updates', async () => {
2019 + function Example() {
2020 + const [didMount, setDidMount] = React.useState(false);
2021 + React.useEffect(() => {
2022 + Scheduler.unstable_advanceTime(1);
2023 + setDidMount(true);
2024 + }, []);
2025 + Scheduler.unstable_advanceTime(10);
2026 + Scheduler.log(didMount ? 'update' : 'mount');
2027 + return didMount;
2028 + }
2029
1895 - renderRootHelper(<Example />);
1896 - await waitForAll(['mount', 'update']);
2030 + modernRender(<Example />);
2031 + await waitForAll(['mount', 'update']);
2032
1898 - const timelineData = stopProfilingAndGetTimelineData();
1899 - expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
1900 - expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
2033 + const timelineData = stopProfilingAndGetTimelineData();
2034 + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
2035 + expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
2036 [
2037 {
2038 "componentName": "Example",
@@ -1922,7 +2057,7 @@ describe('Timeline profiler', () => {
2057 },
2058 ]
2059 `);
1925 - expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
2060 + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
2061 [
2062 {
2063 "lanes": "0b0000000000000000000000000100000",
@@ -1941,26 +2076,26 @@ describe('Timeline profiler', () => {
2076 },
2077 ]
2078 `);
1944 - });
2079 + });
2080
1946 - it('should mark render phase updates', async () => {
1947 - function Example() {
1948 - const [didRender, setDidRender] = React.useState(false);
1949 - Scheduler.unstable_advanceTime(10);
1950 - if (!didRender) {
1951 - setDidRender(true);
2081 + it('should mark render phase updates', async () => {
2082 + function Example() {
2083 + const [didRender, setDidRender] = React.useState(false);
2084 + Scheduler.unstable_advanceTime(10);
2085 + if (!didRender) {
2086 + setDidRender(true);
2087 + }
2088 + Scheduler.log(didRender ? 'update' : 'mount');
2089 + return didRender;
2090 }
1953 - Scheduler.log(didRender ? 'update' : 'mount');
1954 - return didRender;
1955 - }
2091
1957 - renderRootHelper(<Example />);
1958 - await waitForAll(['mount', 'update']);
2092 + modernRender(<Example />);
2093 + await waitForAll(['mount', 'update']);
2094
1960 - const timelineData = stopProfilingAndGetTimelineData();
1961 - // Render phase updates should be retried as part of the same batch.
1962 - expect(timelineData.batchUIDToMeasuresMap.size).toBe(1);
1963 - expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
2095 + const timelineData = stopProfilingAndGetTimelineData();
2096 + // Render phase updates should be retried as part of the same batch.
2097 + expect(timelineData.batchUIDToMeasuresMap.size).toBe(1);
2098 + expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
2099 [
2100 {
2101 "componentName": "Example",
@@ -1971,7 +2106,7 @@ describe('Timeline profiler', () => {
2106 },
2107 ]
2108 `);
1974 - expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
2109 + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
2110 [
2111 {
2112 "lanes": "0b0000000000000000000000000100000",
@@ -1990,146 +2125,51 @@ describe('Timeline profiler', () => {
2125 },
2126 ]
2127 `);
1993 - });
2128 + });
2129
1995 - it('should mark sync render that throws', async () => {
1996 - jest.spyOn(console, 'error').mockImplementation(() => {});
2130 + it('should mark concurrent render that throws', async () => {
2131 + jest.spyOn(console, 'error').mockImplementation(() => {});
2132
1998 - class ErrorBoundary extends React.Component {
1999 - state = {error: null};
2000 - componentDidCatch(error) {
2001 - this.setState({error});
2002 - }
2003 - render() {
2004 - Scheduler.unstable_advanceTime(10);
2005 - if (this.state.error) {
2006 - Scheduler.log('ErrorBoundary fallback');
2007 - return null;
2133 + class ErrorBoundary extends React.Component {
2134 + state = {error: null};
2135 + componentDidCatch(error) {
2136 + this.setState({error});
2137 }
2009 - Scheduler.log('ErrorBoundary render');
2010 - return this.props.children;
2011 - }
2012 - }
2013 -
2014 - function ExampleThatThrows() {
2015 - Scheduler.log('ExampleThatThrows');
2016 - throw Error('Expected error');
2017 - }
2018 -
2019 - renderHelper(
2020 - <ErrorBoundary>
2021 - <ExampleThatThrows />
2022 - </ErrorBoundary>,
2023 - );
2024 -
2025 - assertLog([
2026 - 'ErrorBoundary render',
2027 - 'ExampleThatThrows',
2028 - 'ExampleThatThrows',
2029 - 'ErrorBoundary fallback',
2030 - ]);
2031 -
2032 - const timelineData = stopProfilingAndGetTimelineData();
2033 - expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
2034 - [
2035 - {
2036 - "componentName": "ErrorBoundary",
2037 - "duration": 10,
2038 - "timestamp": 10,
2039 - "type": "render",
2040 - "warning": null,
2041 - },
2042 - {
2043 - "componentName": "ExampleThatThrows",
2044 - "duration": 0,
2045 - "timestamp": 20,
2046 - "type": "render",
2047 - "warning": null,
2048 - },
2049 - {
2050 - "componentName": "ErrorBoundary",
2051 - "duration": 10,
2052 - "timestamp": 20,
2053 - "type": "render",
2054 - "warning": null,
2055 - },
2056 - ]
2057 - `);
2058 - expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
2059 - [
2060 - {
2061 - "lanes": "0b0000000000000000000000000000010",
2062 - "timestamp": 10,
2063 - "type": "schedule-render",
2064 - "warning": null,
2065 - },
2066 - {
2067 - "componentName": "ErrorBoundary",
2068 - "componentStack": "
2069 - in ErrorBoundary (at **)",
2070 - "lanes": "0b0000000000000000000000000000010",
2071 - "timestamp": 20,
2072 - "type": "schedule-state-update",
2073 - "warning": null,
2074 - },
2075 - ]
2076 - `);
2077 - expect(timelineData.thrownErrors).toMatchInlineSnapshot(`
2078 - [
2079 - {
2080 - "componentName": "ExampleThatThrows",
2081 - "message": "Expected error",
2082 - "phase": "mount",
2083 - "timestamp": 20,
2084 - "type": "thrown-error",
2085 - },
2086 - ]
2087 - `);
2088 - });
2089 -
2090 - it('should mark concurrent render that throws', async () => {
2091 - jest.spyOn(console, 'error').mockImplementation(() => {});
2092 -
2093 - class ErrorBoundary extends React.Component {
2094 - state = {error: null};
2095 - componentDidCatch(error) {
2096 - this.setState({error});
2097 - }
2098 - render() {
2099 - Scheduler.unstable_advanceTime(10);
2100 - if (this.state.error) {
2101 - Scheduler.log('ErrorBoundary fallback');
2102 - return null;
2138 + render() {
2139 + Scheduler.unstable_advanceTime(10);
2140 + if (this.state.error) {
2141 + Scheduler.log('ErrorBoundary fallback');
2142 + return null;
2143 + }
2144 + Scheduler.log('ErrorBoundary render');
2145 + return this.props.children;
2146 }
2104 - Scheduler.log('ErrorBoundary render');
2105 - return this.props.children;
2147 }
2107 - }
2108 -
2109 - function ExampleThatThrows() {
2110 - Scheduler.log('ExampleThatThrows');
2111 - // eslint-disable-next-line no-throw-literal
2112 - throw 'Expected error';
2113 - }
2114 -
2115 - renderRootHelper(
2116 - <ErrorBoundary>
2117 - <ExampleThatThrows />
2118 - </ErrorBoundary>,
2119 - );
2148
2121 - await waitForAll([
2122 - 'ErrorBoundary render',
2123 - 'ExampleThatThrows',
2124 - 'ExampleThatThrows',
2125 - 'ErrorBoundary render',
2126 - 'ExampleThatThrows',
2127 - 'ExampleThatThrows',
2128 - 'ErrorBoundary fallback',
2129 - ]);
2149 + function ExampleThatThrows() {
2150 + Scheduler.log('ExampleThatThrows');
2151 + // eslint-disable-next-line no-throw-literal
2152 + throw 'Expected error';
2153 + }
2154
2131 - const timelineData = stopProfilingAndGetTimelineData();
2132 - expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
2155 + modernRender(
2156 + <ErrorBoundary>
2157 + <ExampleThatThrows />
2158 + </ErrorBoundary>,
2159 + );
2160 +
2161 + await waitForAll([
2162 + 'ErrorBoundary render',
2163 + 'ExampleThatThrows',
2164 + 'ExampleThatThrows',
2165 + 'ErrorBoundary render',
2166 + 'ExampleThatThrows',
2167 + 'ExampleThatThrows',
2168 + 'ErrorBoundary fallback',
2169 + ]);
2170 +
2171 + const timelineData = stopProfilingAndGetTimelineData();
2172 + expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
2173 [
2174 {
2175 "componentName": "ErrorBoundary",
@@ -2168,7 +2208,7 @@ describe('Timeline profiler', () => {
2208 },
2209 ]
2210 `);
2171 - expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
2211 + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
2212 [
2213 {
2214 "lanes": "0b0000000000000000000000000100000",
@@ -2187,7 +2227,7 @@ describe('Timeline profiler', () => {
2227 },
2228 ]
2229 `);
2190 - expect(timelineData.thrownErrors).toMatchInlineSnapshot(`
2230 + expect(timelineData.thrownErrors).toMatchInlineSnapshot(`
2231 [
2232 {
2233 "componentName": "ExampleThatThrows",
@@ -2205,72 +2245,72 @@ describe('Timeline profiler', () => {
2245 },
2246 ]
2247 `);
2208 - });
2209 -
2210 - it('should mark passive and layout effects', async () => {
2211 - function ComponentWithEffects() {
2212 - React.useLayoutEffect(() => {
2213 - Scheduler.log('layout 1 mount');
2214 - return () => {
2215 - Scheduler.log('layout 1 unmount');
2216 - };
2217 - }, []);
2218 -
2219 - React.useEffect(() => {
2220 - Scheduler.log('passive 1 mount');
2221 - return () => {
2222 - Scheduler.log('passive 1 unmount');
2223 - };
2224 - }, []);
2225 -
2226 - React.useLayoutEffect(() => {
2227 - Scheduler.log('layout 2 mount');
2228 - return () => {
2229 - Scheduler.log('layout 2 unmount');
2230 - };
2231 - }, []);
2232 -
2233 - React.useEffect(() => {
2234 - Scheduler.log('passive 2 mount');
2235 - return () => {
2236 - Scheduler.log('passive 2 unmount');
2237 - };
2238 - }, []);
2248 + });
2249
2240 - React.useEffect(() => {
2241 - Scheduler.log('passive 3 mount');
2242 - return () => {
2243 - Scheduler.log('passive 3 unmount');
2244 - };
2245 - }, []);
2250 + it('should mark passive and layout effects', async () => {
2251 + function ComponentWithEffects() {
2252 + React.useLayoutEffect(() => {
2253 + Scheduler.log('layout 1 mount');
2254 + return () => {
2255 + Scheduler.log('layout 1 unmount');
2256 + };
2257 + }, []);
2258 +
2259 + React.useEffect(() => {
2260 + Scheduler.log('passive 1 mount');
2261 + return () => {
2262 + Scheduler.log('passive 1 unmount');
2263 + };
2264 + }, []);
2265 +
2266 + React.useLayoutEffect(() => {
2267 + Scheduler.log('layout 2 mount');
2268 + return () => {
2269 + Scheduler.log('layout 2 unmount');
2270 + };
2271 + }, []);
2272 +
2273 + React.useEffect(() => {
2274 + Scheduler.log('passive 2 mount');
2275 + return () => {
2276 + Scheduler.log('passive 2 unmount');
2277 + };
2278 + }, []);
2279 +
2280 + React.useEffect(() => {
2281 + Scheduler.log('passive 3 mount');
2282 + return () => {
2283 + Scheduler.log('passive 3 unmount');
2284 + };
2285 + }, []);
2286
2247 - return null;
2248 - }
2287 + return null;
2288 + }
2289
2250 - const unmount = renderRootHelper(<ComponentWithEffects />);
2290 + const unmount = modernRender(<ComponentWithEffects />);
2291
2252 - await waitForPaint(['layout 1 mount', 'layout 2 mount']);
2292 + await waitForPaint(['layout 1 mount', 'layout 2 mount']);
2293
2254 - await waitForAll([
2255 - 'passive 1 mount',
2256 - 'passive 2 mount',
2257 - 'passive 3 mount',
2258 - ]);
2294 + await waitForAll([
2295 + 'passive 1 mount',
2296 + 'passive 2 mount',
2297 + 'passive 3 mount',
2298 + ]);
2299
2260 - await waitForAll([]);
2300 + await waitForAll([]);
2301
2262 - unmount();
2302 + unmount();
2303
2264 - assertLog([
2265 - 'layout 1 unmount',
2266 - 'layout 2 unmount',
2267 - 'passive 1 unmount',
2268 - 'passive 2 unmount',
2269 - 'passive 3 unmount',
2270 - ]);
2304 + assertLog([
2305 + 'layout 1 unmount',
2306 + 'layout 2 unmount',
2307 + 'passive 1 unmount',
2308 + 'passive 2 unmount',
2309 + 'passive 3 unmount',
2310 + ]);
2311
2272 - const timelineData = stopProfilingAndGetTimelineData();
2273 - expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
2312 + const timelineData = stopProfilingAndGetTimelineData();
2313 + expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
2314 [
2315 {
2316 "componentName": "ComponentWithEffects",
@@ -2351,7 +2391,7 @@ describe('Timeline profiler', () => {
2391 },
2392 ]
2393 `);
2354 - expect(timelineData.batchUIDToMeasuresMap).toMatchInlineSnapshot(`
2394 + expect(timelineData.batchUIDToMeasuresMap).toMatchInlineSnapshot(`
2395 Map {
2396 1 => [
2397 {
@@ -2439,33 +2479,33 @@ describe('Timeline profiler', () => {
2479 ],
2480 }
2481 `);
2442 - });
2482 + });
2483
2444 - it('should generate component stacks for state update', async () => {
2445 - function CommponentWithChildren({initialRender}) {
2446 - Scheduler.log('Render ComponentWithChildren');
2447 - return <Child initialRender={initialRender} />;
2448 - }
2484 + it('should generate component stacks for state update', async () => {
2485 + function CommponentWithChildren({initialRender}) {
2486 + Scheduler.log('Render ComponentWithChildren');
2487 + return <Child initialRender={initialRender} />;
2488 + }
2489
2450 - function Child({initialRender}) {
2451 - const [didRender, setDidRender] = React.useState(initialRender);
2452 - if (!didRender) {
2453 - setDidRender(true);
2490 + function Child({initialRender}) {
2491 + const [didRender, setDidRender] = React.useState(initialRender);
2492 + if (!didRender) {
2493 + setDidRender(true);
2494 + }
2495 + Scheduler.log('Render Child');
2496 + return null;
2497 }
2455 - Scheduler.log('Render Child');
2456 - return null;
2457 - }
2498
2459 - renderRootHelper(<CommponentWithChildren initialRender={false} />);
2499 + modernRender(<CommponentWithChildren initialRender={false} />);
2500
2461 - await waitForAll([
2462 - 'Render ComponentWithChildren',
2463 - 'Render Child',
2464 - 'Render Child',
2465 - ]);
2501 + await waitForAll([
2502 + 'Render ComponentWithChildren',
2503 + 'Render Child',
2504 + 'Render Child',
2505 + ]);
2506
2467 - const timelineData = stopProfilingAndGetTimelineData();
2468 - expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
2507 + const timelineData = stopProfilingAndGetTimelineData();
2508 + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
2509 [
2510 {
2511 "lanes": "0b0000000000000000000000000100000",
@@ -2485,16 +2525,22 @@ describe('Timeline profiler', () => {
2525 },
2526 ]
2527 `);
2528 + });
2529 });
2530 });
2531
2532 describe('when not profiling', () => {
2492 - // @reactVersion >=18.0
2493 - it('should not log any marks', () => {
2494 - renderHelper(<div />);
2533 + describe('with legacy render', () => {
2534 + const {render: legacyRender} = getLegacyRenderImplementation();
2535
2496 - const timelineData = stopProfilingAndGetTimelineData();
2497 - expect(timelineData).toBeNull();
2536 + // @reactVersion <= 18.2
2537 + // @reactVersion >= 18.0
2538 + it('should not log any marks', () => {
2539 + legacyRender(<div />);
2540 +
2541 + const timelineData = stopProfilingAndGetTimelineData();
2542 + expect(timelineData).toBeNull();
2543 + });
2544 });
2545 });
2546 });