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
});