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

Use concurrent root in RTR (#28498)

Based on - https://github.com/facebook/react/pull/28497 - https://github.com/facebook/react/pull/28419 Reusing the disableLegacyMode flag, we set ReactTestRenderer to always render with concurrent root where legacy APIs are no longer available. If disableLegacyMode is false, we continue to allow the unstable_isConcurrent option determine the root type. Also checking a global `IS_REACT_NATIVE_TEST_ENVIRONMENT` so we can maintain the existing behavior for RN until we remove legacy root support there.

Jack Pope committed Mar 26, 2024 at 18:53 UTC bb66aa3cef4e42aee790200d03cf7a82659da121
8 files changed +536 -274
packages/react-reconciler/src/__tests__/ErrorBoundaryReconciliation-test.internal.js
+26 -31
@@ -46,45 +46,40 @@ describe('ErrorBoundaryReconciliation', () => {
46 fail ? <InvalidType /> : <span prop="BrokenRender" />;
47 });
48
49 - [true, false].forEach(isConcurrent => {
50 - async function sharedTest(ErrorBoundary, fallbackTagName) {
51 - let renderer;
49 + async function sharedTest(ErrorBoundary, fallbackTagName) {
50 + let renderer;
51
52 + await act(() => {
53 + renderer = ReactTestRenderer.create(
54 + <ErrorBoundary fallbackTagName={fallbackTagName}>
55 + <BrokenRender fail={false} />
56 + </ErrorBoundary>,
57 + {unstable_isConcurrent: true},
58 + );
59 + });
60 + expect(renderer).toMatchRenderedOutput(<span prop="BrokenRender" />);
61 + await expect(async () => {
62 await act(() => {
54 - renderer = ReactTestRenderer.create(
63 + renderer.update(
64 <ErrorBoundary fallbackTagName={fallbackTagName}>
56 - <BrokenRender fail={false} />
65 + <BrokenRender fail={true} />
66 </ErrorBoundary>,
58 - {unstable_isConcurrent: isConcurrent},
67 );
68 });
61 - expect(renderer).toMatchRenderedOutput(<span prop="BrokenRender" />);
62 -
63 - await expect(async () => {
64 - await act(() => {
65 - renderer.update(
66 - <ErrorBoundary fallbackTagName={fallbackTagName}>
67 - <BrokenRender fail={true} />
68 - </ErrorBoundary>,
69 - );
70 - });
71 - }).toErrorDev(isConcurrent ? ['invalid', 'invalid'] : ['invalid']);
72 - const Fallback = fallbackTagName;
73 - expect(renderer).toMatchRenderedOutput(<Fallback prop="ErrorBoundary" />);
74 - }
69 + }).toErrorDev(['invalid', 'invalid']);
70 + const Fallback = fallbackTagName;
71 + expect(renderer).toMatchRenderedOutput(<Fallback prop="ErrorBoundary" />);
72 + }
73
76 - describe(isConcurrent ? 'concurrent' : 'sync', () => {
77 - it('componentDidCatch can recover by rendering an element of the same type', () =>
78 - sharedTest(DidCatchErrorBoundary, 'span'));
74 + it('componentDidCatch can recover by rendering an element of the same type', () =>
75 + sharedTest(DidCatchErrorBoundary, 'span'));
76
80 - it('componentDidCatch can recover by rendering an element of a different type', () =>
81 - sharedTest(DidCatchErrorBoundary, 'div'));
77 + it('componentDidCatch can recover by rendering an element of a different type', () =>
78 + sharedTest(DidCatchErrorBoundary, 'div'));
79
83 - it('getDerivedStateFromError can recover by rendering an element of the same type', () =>
84 - sharedTest(GetDerivedErrorBoundary, 'span'));
80 + it('getDerivedStateFromError can recover by rendering an element of the same type', () =>
81 + sharedTest(GetDerivedErrorBoundary, 'span'));
82
86 - it('getDerivedStateFromError can recover by rendering an element of a different type', () =>
87 - sharedTest(GetDerivedErrorBoundary, 'div'));
88 - });
89 - });
83 + it('getDerivedStateFromError can recover by rendering an element of a different type', () =>
84 + sharedTest(GetDerivedErrorBoundary, 'div'));
85 });
packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js
+2
@@ -1451,6 +1451,7 @@ describe('ReactLazy', () => {
1451 });
1452
1453 describe('legacy mode', () => {
1454 + // @gate !disableLegacyMode
1455 it('mount and reorder lazy elements (legacy mode)', async () => {
1456 class Child extends React.Component {
1457 componentDidMount() {
@@ -1520,6 +1521,7 @@ describe('ReactLazy', () => {
1521 expect(root).toMatchRenderedOutput('ba');
1522 });
1523
1524 + // @gate !disableLegacyMode
1525 it('mount and reorder lazy types (legacy mode)', async () => {
1526 class Child extends React.Component {
1527 componentDidMount() {
packages/react-test-renderer/src/ReactTestRenderer.js
+7 -3
@@ -55,6 +55,7 @@ import {ConcurrentRoot, LegacyRoot} from 'react-reconciler/src/ReactRootTags';
55 import {
56 allowConcurrentByDefault,
57 enableReactTestRendererWarning,
58 + disableLegacyMode,
59 } from 'shared/ReactFeatureFlags';
60
61 const act = React.act;
@@ -485,7 +486,10 @@ function create(
486 }
487
488 let createNodeMock = defaultTestOptions.createNodeMock;
488 - let isConcurrent = false;
489 + const isConcurrentOnly =
490 + disableLegacyMode === true &&
491 + global.IS_REACT_NATIVE_TEST_ENVIRONMENT !== true;
492 + let isConcurrent = isConcurrentOnly;
493 let isStrictMode = false;
494 let concurrentUpdatesByDefault = null;
495 if (typeof options === 'object' && options !== null) {
@@ -493,8 +497,8 @@ function create(
497 // $FlowFixMe[incompatible-type] found when upgrading Flow
498 createNodeMock = options.createNodeMock;
499 }
496 - if (options.unstable_isConcurrent === true) {
497 - isConcurrent = true;
500 + if (isConcurrentOnly === false) {
501 + isConcurrent = options.unstable_isConcurrent;
502 }
503 if (options.unstable_strictMode === true) {
504 isStrictMode = true;
packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.internal.js
+347 -153
@@ -14,14 +14,19 @@ const ReactFeatureFlags = require('shared/ReactFeatureFlags');
14 const React = require('react');
15 const ReactTestRenderer = require('react-test-renderer');
16 const {format: prettyFormat} = require('pretty-format');
17 +const InternalTestUtils = require('internal-test-utils');
18 +const waitForAll = InternalTestUtils.waitForAll;
19 +const act = InternalTestUtils.act;
20 +const Reconciler = require('react-reconciler/src/ReactFiberReconciler');
21 +const {
22 + ConcurrentRoot,
23 + LegacyRoot,
24 +} = require('react-reconciler/src/ReactRootTags');
25
26 // Isolate noop renderer
27 jest.resetModules();
28 const ReactNoop = require('react-noop-renderer');
29
22 -const InternalTestUtils = require('internal-test-utils');
23 -const waitForAll = InternalTestUtils.waitForAll;
24 -
30 // Kind of hacky, but we nullify all the instances to test the tree structure
31 // with jasmine's deep equality function, and test the instances separate. We
32 // also delete children props because testing them is more annoying and not
@@ -75,14 +80,63 @@ describe('ReactTestRenderer', () => {
80 'Warning: react-test-renderer is deprecated. See https://react.dev/warnings/react-test-renderer',
81 {withoutStack: true},
82 );
78 - global.IS_REACT_NATIVE_TEST_ENVIRONMENT = false;
83 });
84
81 - it('renders a simple component', () => {
85 + describe('root tags', () => {
86 + let createContainerSpy;
87 + beforeEach(() => {
88 + global.IS_REACT_NATIVE_TEST_ENVIRONMENT = false;
89 + createContainerSpy = jest.spyOn(Reconciler, 'createContainer');
90 + });
91 +
92 + function expectTag(tag) {
93 + expect(createContainerSpy).toHaveBeenCalledWith(
94 + expect.anything(),
95 + tag,
96 + null,
97 + expect.anything(),
98 + null,
99 + expect.anything(),
100 + expect.anything(),
101 + null,
102 + );
103 + }
104 +
105 + // @gate disableLegacyMode
106 + it('should render using concurrent root if disableLegacyMode', () => {
107 + ReactTestRenderer.create(<div />);
108 + expectTag(ConcurrentRoot);
109 + });
110 +
111 + // @gate !disableLegacyMode
112 + it('should default to legacy root if not disableLegacyMode', () => {
113 + ReactTestRenderer.create(<div />);
114 + expectTag(LegacyRoot);
115 + });
116 +
117 + it('should allow unstable_isConcurrent if not disableLegacyMode', async () => {
118 + ReactTestRenderer.create(<div />, {
119 + unstable_isConcurrent: true,
120 + });
121 + ReactTestRenderer.create(<div />);
122 + expectTag(ConcurrentRoot);
123 + });
124 +
125 + it('should render legacy root when RN test environment', async () => {
126 + global.IS_REACT_NATIVE_TEST_ENVIRONMENT = true;
127 + ReactTestRenderer.create(<div />);
128 + expectTag(LegacyRoot);
129 + });
130 + });
131 +
132 + it('renders a simple component', async () => {
133 function Link() {
134 return <a role="link" />;
135 }
85 - const renderer = ReactTestRenderer.create(<Link />);
136 + let renderer;
137 + await act(() => {
138 + renderer = ReactTestRenderer.create(<Link />);
139 + });
140 expect(renderer.toJSON()).toEqual({
141 type: 'a',
142 props: {role: 'link'},
@@ -90,19 +144,25 @@ describe('ReactTestRenderer', () => {
144 });
145 });
146
93 - it('renders a top-level empty component', () => {
147 + it('renders a top-level empty component', async () => {
148 function Empty() {
149 return null;
150 }
97 - const renderer = ReactTestRenderer.create(<Empty />);
151 + let renderer;
152 + await act(() => {
153 + renderer = ReactTestRenderer.create(<Empty />);
154 + });
155 expect(renderer.toJSON()).toEqual(null);
156 });
157
101 - it('exposes a type flag', () => {
158 + it('exposes a type flag', async () => {
159 function Link() {
160 return <a role="link" />;
161 }
105 - const renderer = ReactTestRenderer.create(<Link />);
162 + let renderer;
163 + await act(() => {
164 + renderer = ReactTestRenderer.create(<Link />);
165 + });
166 const object = renderer.toJSON();
167 expect(object.$$typeof).toBe(Symbol.for('react.test.json'));
168
@@ -114,7 +174,7 @@ describe('ReactTestRenderer', () => {
174 }
175 });
176
117 - it('can render a composite component', () => {
177 + it('can render a composite component', async () => {
178 class Component extends React.Component {
179 render() {
180 return (
@@ -129,7 +189,10 @@ describe('ReactTestRenderer', () => {
189 return <moo />;
190 };
191
132 - const renderer = ReactTestRenderer.create(<Component />);
192 + let renderer;
193 + await act(() => {
194 + renderer = ReactTestRenderer.create(<Component />);
195 + });
196 expect(renderer.toJSON()).toEqual({
197 type: 'div',
198 props: {className: 'purple'},
@@ -137,7 +200,7 @@ describe('ReactTestRenderer', () => {
200 });
201 });
202
140 - it('renders some basics with an update', () => {
203 + it('renders some basics with an update', async () => {
204 let renders = 0;
205
206 class Component extends React.Component {
@@ -169,7 +232,10 @@ describe('ReactTestRenderer', () => {
232 return null;
233 };
234
172 - const renderer = ReactTestRenderer.create(<Component />);
235 + let renderer;
236 + await act(() => {
237 + renderer = ReactTestRenderer.create(<Component />);
238 + });
239 expect(renderer.toJSON()).toEqual({
240 type: 'div',
241 props: {className: 'purple'},
@@ -178,7 +244,7 @@ describe('ReactTestRenderer', () => {
244 expect(renders).toBe(6);
245 });
246
181 - it('exposes the instance', () => {
247 + it('exposes the instance', async () => {
248 class Mouse extends React.Component {
249 constructor() {
250 super();
@@ -191,7 +257,10 @@ describe('ReactTestRenderer', () => {
257 return <div>{this.state.mouse}</div>;
258 }
259 }
194 - const renderer = ReactTestRenderer.create(<Mouse />);
260 + let renderer;
261 + await act(() => {
262 + renderer = ReactTestRenderer.create(<Mouse />);
263 + });
264
265 expect(renderer.toJSON()).toEqual({
266 type: 'div',
@@ -200,7 +269,9 @@ describe('ReactTestRenderer', () => {
269 });
270
271 const mouse = renderer.getInstance();
203 - mouse.handleMoose();
272 + await act(() => {
273 + mouse.handleMoose();
274 + });
275 expect(renderer.toJSON()).toEqual({
276 type: 'div',
277 children: ['moose'],
@@ -208,15 +279,20 @@ describe('ReactTestRenderer', () => {
279 });
280 });
281
211 - it('updates types', () => {
212 - const renderer = ReactTestRenderer.create(<div>mouse</div>);
282 + it('updates types', async () => {
283 + let renderer;
284 + await act(() => {
285 + renderer = ReactTestRenderer.create(<div>mouse</div>);
286 + });
287 +
288 expect(renderer.toJSON()).toEqual({
289 type: 'div',
290 props: {},
291 children: ['mouse'],
292 });
218 -
219 - renderer.update(<span>mice</span>);
293 + await act(() => {
294 + renderer.update(<span>mice</span>);
295 + });
296 expect(renderer.toJSON()).toEqual({
297 type: 'span',
298 props: {},
@@ -224,14 +300,18 @@ describe('ReactTestRenderer', () => {
300 });
301 });
302
227 - it('updates children', () => {
228 - const renderer = ReactTestRenderer.create(
229 - <div>
230 - <span key="a">A</span>
231 - <span key="b">B</span>
232 - <span key="c">C</span>
233 - </div>,
234 - );
303 + it('updates children', async () => {
304 + let renderer;
305 + await act(() => {
306 + renderer = ReactTestRenderer.create(
307 + <div>
308 + <span key="a">A</span>
309 + <span key="b">B</span>
310 + <span key="c">C</span>
311 + </div>,
312 + );
313 + });
314 +
315 expect(renderer.toJSON()).toEqual({
316 type: 'div',
317 props: {},
@@ -242,13 +322,15 @@ describe('ReactTestRenderer', () => {
322 ],
323 });
324
245 - renderer.update(
246 - <div>
247 - <span key="d">D</span>
248 - <span key="c">C</span>
249 - <span key="b">B</span>
250 - </div>,
251 - );
325 + await act(() => {
326 + renderer.update(
327 + <div>
328 + <span key="d">D</span>
329 + <span key="c">C</span>
330 + <span key="b">B</span>
331 + </div>,
332 + );
333 + });
334 expect(renderer.toJSON()).toEqual({
335 type: 'div',
336 props: {},
@@ -260,7 +342,7 @@ describe('ReactTestRenderer', () => {
342 });
343 });
344
263 - it('does the full lifecycle', () => {
345 + it('does the full lifecycle', async () => {
346 const log = [];
347 class Log extends React.Component {
348 render() {
@@ -275,9 +357,16 @@ describe('ReactTestRenderer', () => {
357 }
358 }
359
278 - const renderer = ReactTestRenderer.create(<Log key="foo" name="Foo" />);
279 - renderer.update(<Log key="bar" name="Bar" />);
280 - renderer.unmount();
360 + let renderer;
361 + await act(() => {
362 + renderer = ReactTestRenderer.create(<Log key="foo" name="Foo" />);
363 + });
364 + await act(() => {
365 + renderer.update(<Log key="bar" name="Bar" />);
366 + });
367 + await act(() => {
368 + renderer.unmount();
369 + });
370
371 expect(log).toEqual([
372 'render Foo',
@@ -289,14 +378,16 @@ describe('ReactTestRenderer', () => {
378 ]);
379 });
380
292 - it('gives a ref to native components', () => {
381 + it('gives a ref to native components', async () => {
382 const log = [];
294 - ReactTestRenderer.create(<div ref={r => log.push(r)} />);
383 + await act(() => {
384 + ReactTestRenderer.create(<div ref={r => log.push(r)} />);
385 + });
386 expect(log).toEqual([null]);
387 });
388
389 // @gate !enableRefAsProp || !__DEV__
299 - it('warns correctly for refs on SFCs', () => {
390 + it('warns correctly for refs on SFCs', async () => {
391 function Bar() {
392 return <div>Hello, world</div>;
393 }
@@ -312,8 +403,14 @@ describe('ReactTestRenderer', () => {
403 return <div ref={this.bazRef} />;
404 }
405 }
315 - ReactTestRenderer.create(<Baz />);
316 - expect(() => ReactTestRenderer.create(<Foo />)).toErrorDev(
406 + await act(() => {
407 + ReactTestRenderer.create(<Baz />);
408 + });
409 + await expect(async () => {
410 + await act(() => {
411 + ReactTestRenderer.create(<Foo />);
412 + });
413 + }).toErrorDev(
414 'Warning: Function components cannot be given refs. Attempts ' +
415 'to access this ref will fail. ' +
416 'Did you mean to use React.forwardRef()?\n' +
@@ -322,7 +419,7 @@ describe('ReactTestRenderer', () => {
419 );
420 });
421
325 - it('allows an optional createNodeMock function', () => {
422 + it('allows an optional createNodeMock function', async () => {
423 const mockDivInstance = {appendChild: () => {}};
424 const mockInputInstance = {focus: () => {}};
425 const mockListItemInstance = {click: () => {}};
@@ -351,27 +448,41 @@ describe('ReactTestRenderer', () => {
448 return {};
449 }
450 }
354 - ReactTestRenderer.create(<div ref={r => log.push(r)} />, {createNodeMock});
355 - ReactTestRenderer.create(<input ref={r => log.push(r)} />, {
356 - createNodeMock,
451 + await act(() => {
452 + ReactTestRenderer.create(<div ref={r => log.push(r)} />, {
453 + createNodeMock,
454 + });
455 + });
456 + await act(() => {
457 + ReactTestRenderer.create(<input ref={r => log.push(r)} />, {
458 + createNodeMock,
459 + });
460 + });
461 + await act(() => {
462 + ReactTestRenderer.create(
463 + <div>
464 + <span>
465 + <ul>
466 + <li ref={r => log.push(r)} />
467 + </ul>
468 + <ul>
469 + <li ref={r => log.push(r)} />
470 + <li ref={r => log.push(r)} />
471 + </ul>
472 + </span>
473 + </div>,
474 + {createNodeMock, foobar: true},
475 + );
476 + });
477 + await act(() => {
478 + ReactTestRenderer.create(<Foo />, {createNodeMock});
479 + });
480 + await act(() => {
481 + ReactTestRenderer.create(<div ref={r => log.push(r)} />);
482 + });
483 + await act(() => {
484 + ReactTestRenderer.create(<div ref={r => log.push(r)} />, {});
485 });
358 - ReactTestRenderer.create(
359 - <div>
360 - <span>
361 - <ul>
362 - <li ref={r => log.push(r)} />
363 - </ul>
364 - <ul>
365 - <li ref={r => log.push(r)} />
366 - <li ref={r => log.push(r)} />
367 - </ul>
368 - </span>
369 - </div>,
370 - {createNodeMock, foobar: true},
371 - );
372 - ReactTestRenderer.create(<Foo />, {createNodeMock});
373 - ReactTestRenderer.create(<div ref={r => log.push(r)} />);
374 - ReactTestRenderer.create(<div ref={r => log.push(r)} />, {});
486 expect(log).toEqual([
487 mockDivInstance,
488 mockInputInstance,
@@ -396,7 +507,7 @@ describe('ReactTestRenderer', () => {
507 expect(() => inst.unmount()).not.toThrow();
508 });
509
399 - it('supports unmounting inner instances', () => {
510 + it('supports unmounting inner instances', async () => {
511 let count = 0;
512 class Foo extends React.Component {
513 componentWillUnmount() {
@@ -406,19 +517,24 @@ describe('ReactTestRenderer', () => {
517 return <div />;
518 }
519 }
409 - const inst = ReactTestRenderer.create(
410 - <div>
411 - <Foo />
412 - </div>,
413 - {
414 - createNodeMock: () => 'foo',
415 - },
416 - );
417 - expect(() => inst.unmount()).not.toThrow();
520 + let inst;
521 + await act(() => {
522 + inst = ReactTestRenderer.create(
523 + <div>
524 + <Foo />
525 + </div>,
526 + {
527 + createNodeMock: () => 'foo',
528 + },
529 + );
530 + });
531 + await act(() => {
532 + inst.unmount();
533 + });
534 expect(count).toEqual(1);
535 });
536
421 - it('supports updates when using refs', () => {
537 + it('supports updates when using refs', async () => {
538 const log = [];
539 const createNodeMock = element => {
540 log.push(element.type);
@@ -433,14 +549,19 @@ describe('ReactTestRenderer', () => {
549 );
550 }
551 }
436 - const inst = ReactTestRenderer.create(<Foo useDiv={true} />, {
437 - createNodeMock,
552 + let inst;
553 + await act(() => {
554 + inst = ReactTestRenderer.create(<Foo useDiv={true} />, {
555 + createNodeMock,
556 + });
557 + });
558 + await act(() => {
559 + inst.update(<Foo useDiv={false} />);
560 });
439 - inst.update(<Foo useDiv={false} />);
561 expect(log).toEqual(['div', 'span']);
562 });
563
443 - it('supports error boundaries', () => {
564 + it('supports error boundaries', async () => {
565 const log = [];
566 class Angry extends React.Component {
567 render() {
@@ -489,13 +610,20 @@ describe('ReactTestRenderer', () => {
610 }
611 }
612
492 - const renderer = ReactTestRenderer.create(<Boundary />);
613 + let renderer;
614 + await act(() => {
615 + renderer = ReactTestRenderer.create(<Boundary />, {
616 + unstable_isConcurrent: true,
617 + });
618 + });
619 expect(renderer.toJSON()).toEqual({
620 type: 'div',
621 props: {},
622 children: ['Happy Birthday!'],
623 });
624 expect(log).toEqual([
625 + 'Boundary render',
626 + 'Angry render',
627 'Boundary render',
628 'Angry render',
629 'Boundary componentDidMount',
@@ -504,42 +632,53 @@ describe('ReactTestRenderer', () => {
632 ]);
633 });
634
507 - it('can update text nodes', () => {
635 + it('can update text nodes', async () => {
636 class Component extends React.Component {
637 render() {
638 return <div>{this.props.children}</div>;
639 }
640 }
641
514 - const renderer = ReactTestRenderer.create(<Component>Hi</Component>);
642 + let renderer;
643 + await act(() => {
644 + renderer = ReactTestRenderer.create(<Component>Hi</Component>);
645 + });
646 expect(renderer.toJSON()).toEqual({
647 type: 'div',
648 children: ['Hi'],
649 props: {},
650 });
520 - renderer.update(<Component>{['Hi', 'Bye']}</Component>);
651 + await act(() => {
652 + renderer.update(<Component>{['Hi', 'Bye']}</Component>);
653 + });
654 expect(renderer.toJSON()).toEqual({
655 type: 'div',
656 children: ['Hi', 'Bye'],
657 props: {},
658 });
526 - renderer.update(<Component>Bye</Component>);
659 + await act(() => {
660 + renderer.update(<Component>Bye</Component>);
661 + });
662 expect(renderer.toJSON()).toEqual({
663 type: 'div',
664 children: ['Bye'],
665 props: {},
666 });
532 - renderer.update(<Component>{42}</Component>);
667 + await act(() => {
668 + renderer.update(<Component>{42}</Component>);
669 + });
670 expect(renderer.toJSON()).toEqual({
671 type: 'div',
672 children: ['42'],
673 props: {},
674 });
538 - renderer.update(
539 - <Component>
540 - <div />
541 - </Component>,
542 - );
675 + await act(() => {
676 + renderer.update(
677 + <Component>
678 + <div />
679 + </Component>,
680 + );
681 + });
682 expect(renderer.toJSON()).toEqual({
683 type: 'div',
684 children: [
@@ -553,10 +692,13 @@ describe('ReactTestRenderer', () => {
692 });
693 });
694
556 - it('toTree() renders simple components returning host components', () => {
695 + it('toTree() renders simple components returning host components', async () => {
696 const Qoo = () => <span className="Qoo">Hello World!</span>;
697
559 - const renderer = ReactTestRenderer.create(<Qoo />);
698 + let renderer;
699 + await act(() => {
700 + renderer = ReactTestRenderer.create(<Qoo />);
701 + });
702 const tree = renderer.toTree();
703
704 cleanNodeOrArray(tree);
@@ -578,13 +720,16 @@ describe('ReactTestRenderer', () => {
720 );
721 });
722
581 - it('toTree() handles nested Fragments', () => {
723 + it('toTree() handles nested Fragments', async () => {
724 const Foo = () => (
725 <>
726 <>foo</>
727 </>
728 );
587 - const renderer = ReactTestRenderer.create(<Foo />);
729 + let renderer;
730 + await act(() => {
731 + renderer = ReactTestRenderer.create(<Foo />);
732 + });
733 const tree = renderer.toTree();
734
735 cleanNodeOrArray(tree);
@@ -600,14 +745,17 @@ describe('ReactTestRenderer', () => {
745 );
746 });
747
603 - it('toTree() handles null rendering components', () => {
748 + it('toTree() handles null rendering components', async () => {
749 class Foo extends React.Component {
750 render() {
751 return null;
752 }
753 }
754
610 - const renderer = ReactTestRenderer.create(<Foo />);
755 + let renderer;
756 + await act(() => {
757 + renderer = ReactTestRenderer.create(<Foo />);
758 + });
759 const tree = renderer.toTree();
760
761 expect(tree.instance).toBeInstanceOf(Foo);
@@ -623,15 +771,18 @@ describe('ReactTestRenderer', () => {
771 });
772 });
773
626 - it('toTree() handles simple components that return arrays', () => {
774 + it('toTree() handles simple components that return arrays', async () => {
775 const Foo = ({children}) => children;
776
629 - const renderer = ReactTestRenderer.create(
630 - <Foo>
631 - <div>One</div>
632 - <div>Two</div>
633 - </Foo>,
634 - );
777 + let renderer;
778 + await act(() => {
779 + renderer = ReactTestRenderer.create(
780 + <Foo>
781 + <div>One</div>
782 + <div>Two</div>
783 + </Foo>,
784 + );
785 + });
786
787 const tree = renderer.toTree();
788
@@ -663,25 +814,28 @@ describe('ReactTestRenderer', () => {
814 );
815 });
816
666 - it('toTree() handles complicated tree of arrays', () => {
817 + it('toTree() handles complicated tree of arrays', async () => {
818 class Foo extends React.Component {
819 render() {
820 return this.props.children;
821 }
822 }
823
673 - const renderer = ReactTestRenderer.create(
674 - <div>
675 - <Foo>
676 - <div>One</div>
677 - <div>Two</div>
824 + let renderer;
825 + await act(() => {
826 + renderer = ReactTestRenderer.create(
827 + <div>
828 <Foo>
679 - <div>Three</div>
829 + <div>One</div>
830 + <div>Two</div>
831 + <Foo>
832 + <div>Three</div>
833 + </Foo>
834 </Foo>
681 - </Foo>
682 - <div>Four</div>
683 - </div>,
684 - );
835 + <div>Four</div>
836 + </div>,
837 + );
838 + });
839
840 const tree = renderer.toTree();
841
@@ -741,19 +895,22 @@ describe('ReactTestRenderer', () => {
895 );
896 });
897
744 - it('toTree() handles complicated tree of fragments', () => {
745 - const renderer = ReactTestRenderer.create(
746 - <>
898 + it('toTree() handles complicated tree of fragments', async () => {
899 + let renderer;
900 + await act(() => {
901 + renderer = ReactTestRenderer.create(
902 <>
748 - <div>One</div>
749 - <div>Two</div>
903 <>
751 - <div>Three</div>
904 + <div>One</div>
905 + <div>Two</div>
906 + <>
907 + <div>Three</div>
908 + </>
909 </>
753 - </>
754 - <div>Four</div>
755 - </>,
756 - );
910 + <div>Four</div>
911 + </>,
912 + );
913 + });
914
915 const tree = renderer.toTree();
916
@@ -793,18 +950,22 @@ describe('ReactTestRenderer', () => {
950 );
951 });
952
796 - it('root instance and createNodeMock ref return the same value', () => {
953 + it('root instance and createNodeMock ref return the same value', async () => {
954 const createNodeMock = ref => ({node: ref});
955 let refInst = null;
799 - const renderer = ReactTestRenderer.create(
800 - <div ref={ref => (refInst = ref)} />,
801 - {createNodeMock},
802 - );
956 + let renderer;
957 + await act(() => {
958 + renderer = ReactTestRenderer.create(
959 + <div ref={ref => (refInst = ref)} />,
960 + {createNodeMock},
961 + );
962 + });
963 +
964 const root = renderer.getInstance();
965 expect(root).toEqual(refInst);
966 });
967
807 - it('toTree() renders complicated trees of composites and hosts', () => {
968 + it('toTree() renders complicated trees of composites and hosts', async () => {
969 // SFC returning host. no children props.
970 const Qoo = () => <span className="Qoo">Hello World!</span>;
971
@@ -835,7 +996,11 @@ describe('ReactTestRenderer', () => {
996 }
997 }
998
838 - const renderer = ReactTestRenderer.create(<Bam />);
999 + let renderer;
1000 + await act(() => {
1001 + renderer = ReactTestRenderer.create(<Bam />);
1002 + });
1003 +
1004 const tree = renderer.toTree();
1005
1006 // we test for the presence of instances before nulling them out
@@ -894,30 +1059,45 @@ describe('ReactTestRenderer', () => {
1059 );
1060 });
1061
897 - it('can update text nodes when rendered as root', () => {
898 - const renderer = ReactTestRenderer.create(['Hello', 'world']);
1062 + it('can update text nodes when rendered as root', async () => {
1063 + let renderer;
1064 + await act(() => {
1065 + renderer = ReactTestRenderer.create(['Hello', 'world']);
1066 + });
1067 expect(renderer.toJSON()).toEqual(['Hello', 'world']);
900 - renderer.update(42);
1068 + await act(() => {
1069 + renderer.update(42);
1070 + });
1071 expect(renderer.toJSON()).toEqual('42');
902 - renderer.update([42, 'world']);
1072 + await act(() => {
1073 + renderer.update([42, 'world']);
1074 + });
1075 expect(renderer.toJSON()).toEqual(['42', 'world']);
1076 });
1077
906 - it('can render and update root fragments', () => {
1078 + it('can render and update root fragments', async () => {
1079 const Component = props => props.children;
1080
909 - const renderer = ReactTestRenderer.create([
910 - <Component key="a">Hi</Component>,
911 - <Component key="b">Bye</Component>,
912 - ]);
1081 + let renderer;
1082 + await act(() => {
1083 + renderer = ReactTestRenderer.create([
1084 + <Component key="a">Hi</Component>,
1085 + <Component key="b">Bye</Component>,
1086 + ]);
1087 + });
1088 +
1089 expect(renderer.toJSON()).toEqual(['Hi', 'Bye']);
914 - renderer.update(<div />);
1090 + await act(() => {
1091 + renderer.update(<div />);
1092 + });
1093 expect(renderer.toJSON()).toEqual({
1094 type: 'div',
1095 children: null,
1096 props: {},
1097 });
920 - renderer.update([<div key="a">goodbye</div>, 'world']);
1098 + await act(() => {
1099 + renderer.update([<div key="a">goodbye</div>, 'world']);
1100 + });
1101 expect(renderer.toJSON()).toEqual([
1102 {
1103 type: 'div',
@@ -928,7 +1108,7 @@ describe('ReactTestRenderer', () => {
1108 ]);
1109 });
1110
931 - it('supports context providers and consumers', () => {
1111 + it('supports context providers and consumers', async () => {
1112 const {Consumer, Provider} = React.createContext('a');
1113
1114 function Child(props) {
@@ -943,7 +1123,10 @@ describe('ReactTestRenderer', () => {
1123 );
1124 }
1125
946 - const renderer = ReactTestRenderer.create(<App />);
1126 + let renderer;
1127 + await act(() => {
1128 + renderer = ReactTestRenderer.create(<App />);
1129 + });
1130 const child = renderer.root.findByType(Child);
1131 expect(child.children).toEqual(['b']);
1132 expect(prettyFormat(renderer.toTree())).toEqual(
@@ -965,7 +1148,7 @@ describe('ReactTestRenderer', () => {
1148 );
1149 });
1150
968 - it('supports modes', () => {
1151 + it('supports modes', async () => {
1152 function Child(props) {
1153 return props.value;
1154 }
@@ -978,7 +1161,10 @@ describe('ReactTestRenderer', () => {
1161 );
1162 }
1163
981 - const renderer = ReactTestRenderer.create(<App value="a" />);
1164 + let renderer;
1165 + await act(() => {
1166 + renderer = ReactTestRenderer.create(<App value="a" />);
1167 + });
1168 const child = renderer.root.findByType(Child);
1169 expect(child.children).toEqual(['a']);
1170 expect(prettyFormat(renderer.toTree())).toEqual(
@@ -1002,7 +1188,7 @@ describe('ReactTestRenderer', () => {
1188 );
1189 });
1190
1005 - it('supports forwardRef', () => {
1191 + it('supports forwardRef', async () => {
1192 const InnerRefed = React.forwardRef((props, ref) => (
1193 <div>
1194 <span ref={ref} />
@@ -1020,7 +1206,10 @@ describe('ReactTestRenderer', () => {
1206 }
1207 }
1208
1023 - const renderer = ReactTestRenderer.create(<App />);
1209 + let renderer;
1210 + await act(() => {
1211 + renderer = ReactTestRenderer.create(<App />);
1212 + });
1213 const tree = renderer.toTree();
1214 cleanNodeOrArray(tree);
1215
@@ -1065,12 +1254,17 @@ describe('ReactTestRenderer', () => {
1254 );
1255 ReactNoop.render(<App />);
1256 await waitForAll([]);
1068 - ReactTestRenderer.create(<App />);
1257 + await act(() => {
1258 + ReactTestRenderer.create(<App />);
1259 + });
1260 });
1261
1071 - it('calling findByType() with an invalid component will fall back to "Unknown" for component name', () => {
1262 + it('calling findByType() with an invalid component will fall back to "Unknown" for component name', async () => {
1263 const App = () => null;
1073 - const renderer = ReactTestRenderer.create(<App />);
1264 + let renderer;
1265 + await act(() => {
1266 + renderer = ReactTestRenderer.create(<App />);
1267 + });
1268 const NonComponent = {};
1269
1270 expect(() => {
packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.js
+42 -32
@@ -13,7 +13,7 @@ let ReactDOM;
13 let React;
14 let ReactCache;
15 let ReactTestRenderer;
16 -let waitForAll;
16 +let act;
17
18 describe('ReactTestRenderer', () => {
19 beforeEach(() => {
@@ -26,32 +26,28 @@ describe('ReactTestRenderer', () => {
26 ReactCache = require('react-cache');
27 ReactTestRenderer = require('react-test-renderer');
28 const InternalTestUtils = require('internal-test-utils');
29 - waitForAll = InternalTestUtils.waitForAll;
29 + act = InternalTestUtils.act;
30 });
31
32 - it('should warn if used to render a ReactDOM portal', () => {
32 + it('should warn if used to render a ReactDOM portal', async () => {
33 const container = document.createElement('div');
34 - expect(() => {
35 - let error;
36 - try {
34 + let error;
35 +
36 + await expect(async () => {
37 + await act(() => {
38 ReactTestRenderer.create(ReactDOM.createPortal('foo', container));
38 - } catch (e) {
39 - error = e;
40 - }
41 - // After the update throws, a subsequent render is scheduled to
42 - // unmount the whole tree. This update also causes an error, so React
43 - // throws an AggregateError.
44 - const errors = error.errors;
45 - expect(errors.length).toBe(2);
46 - expect(errors[0].message.includes('indexOf is not a function')).toBe(
47 - true,
48 - );
49 - expect(errors[1].message.includes('indexOf is not a function')).toBe(
50 - true,
51 - );
39 + }).catch(e => (error = e));
40 }).toErrorDev('An invalid container has been provided.', {
41 withoutStack: true,
42 });
43 +
44 + // After the update throws, a subsequent render is scheduled to
45 + // unmount the whole tree. This update also causes an error, so React
46 + // throws an AggregateError.
47 + const errors = error.errors;
48 + expect(errors.length).toBe(2);
49 + expect(errors[0].message.includes('indexOf is not a function')).toBe(true);
50 + expect(errors[1].message.includes('indexOf is not a function')).toBe(true);
51 });
52
53 describe('timed out Suspense hidden subtrees should not be observable via toJSON', () => {
@@ -84,16 +80,23 @@ describe('ReactTestRenderer', () => {
80 );
81 };
82
87 - const root = ReactTestRenderer.create(<App text="initial" />);
88 - PendingResources.initial('initial');
89 - await waitForAll([]);
83 + let root;
84 + await act(() => {
85 + root = ReactTestRenderer.create(<App text="initial" />);
86 + });
87 + await act(() => {
88 + PendingResources.initial('initial');
89 + });
90 expect(root.toJSON()).toEqual('initial');
91
92 - root.update(<App text="dynamic" />);
92 + await act(() => {
93 + root.update(<App text="dynamic" />);
94 + });
95 expect(root.toJSON()).toEqual('fallback');
96
95 - PendingResources.dynamic('dynamic');
96 - await waitForAll([]);
97 + await act(() => {
98 + PendingResources.dynamic('dynamic');
99 + });
100 expect(root.toJSON()).toEqual('dynamic');
101 });
102
@@ -108,16 +111,23 @@ describe('ReactTestRenderer', () => {
111 );
112 };
113
111 - const root = ReactTestRenderer.create(<App text="initial" />);
112 - PendingResources.initial('initial');
113 - await waitForAll([]);
114 + let root;
115 + await act(() => {
116 + root = ReactTestRenderer.create(<App text="initial" />);
117 + });
118 + await act(() => {
119 + PendingResources.initial('initial');
120 + });
121 expect(root.toJSON().children).toEqual(['initial']);
122
116 - root.update(<App text="dynamic" />);
123 + await act(() => {
124 + root.update(<App text="dynamic" />);
125 + });
126 expect(root.toJSON().children).toEqual(['fallback']);
127
119 - PendingResources.dynamic('dynamic');
120 - await waitForAll([]);
128 + await act(() => {
129 + PendingResources.dynamic('dynamic');
130 + });
131 expect(root.toJSON().children).toEqual(['dynamic']);
132 });
133 });
packages/react-test-renderer/src/__tests__/ReactTestRendererAct-test.js
+5 -1
@@ -16,6 +16,7 @@ describe('ReactTestRenderer.act()', () => {
16
17 const InternalTestUtils = require('internal-test-utils');
18 assertLog = InternalTestUtils.assertLog;
19 + global.IS_REACT_ACT_ENVIRONMENT = true;
20 });
21
22 // @gate __DEV__
@@ -91,7 +92,10 @@ describe('ReactTestRenderer.act()', () => {
92 });
93 return step;
94 }
94 - const root = ReactTestRenderer.create(null);
95 + let root;
96 + await act(() => {
97 + root = ReactTestRenderer.create(null);
98 + });
99 await act(async () => {
100 root.update(<App />);
101 });
packages/react-test-renderer/src/__tests__/ReactTestRendererTraversal-test.js
+64 -31
@@ -13,6 +13,7 @@
13 const React = require('react');
14 let ReactTestRenderer;
15 let Context;
16 +let act;
17
18 const RCTView = 'RCTView';
19 const View = props => <RCTView {...props} />;
@@ -21,6 +22,7 @@ describe('ReactTestRendererTraversal', () => {
22 beforeEach(() => {
23 jest.resetModules();
24 ReactTestRenderer = require('react-test-renderer');
25 + act = require('internal-test-utils').act;
26 Context = React.createContext(null);
27 });
28
@@ -68,8 +70,11 @@ describe('ReactTestRendererTraversal', () => {
70 <View {...props} ref={ref} />
71 ));
72
71 - it('initializes', () => {
72 - const render = ReactTestRenderer.create(<Example />);
73 + it('initializes', async () => {
74 + let render;
75 + await act(() => {
76 + render = ReactTestRenderer.create(<Example />);
77 + });
78 const hasFooProp = node => node.props.hasOwnProperty('foo');
79
80 // assert .props, .type and .parent attributes
@@ -80,8 +85,12 @@ describe('ReactTestRendererTraversal', () => {
85 expect(foo.children[0].parent).toBe(foo);
86 });
87
83 - it('searches via .find() / .findAll()', () => {
84 - const render = ReactTestRenderer.create(<Example />);
88 + it('searches via .find() / .findAll()', async () => {
89 + let render;
90 + await act(() => {
91 + render = ReactTestRenderer.create(<Example />);
92 + });
93 +
94 const hasFooProp = node => node.props.hasOwnProperty('foo');
95 const hasBarProp = node => node.props.hasOwnProperty('bar');
96 const hasBazProp = node => node.props.hasOwnProperty('baz');
@@ -135,8 +144,11 @@ describe('ReactTestRendererTraversal', () => {
144 expect(itself.findAll(hasBazProp)).toHaveLength(2);
145 });
146
138 - it('searches via .findByType() / .findAllByType()', () => {
139 - const render = ReactTestRenderer.create(<Example />);
147 + it('searches via .findByType() / .findAllByType()', async () => {
148 + let render;
149 + await act(() => {
150 + render = ReactTestRenderer.create(<Example />);
151 + });
152
153 expect(() => render.root.findByType(ExampleFn)).not.toThrow(); // 1 match
154 expect(() => render.root.findByType(View)).not.toThrow(); // 1 match
@@ -159,8 +171,11 @@ describe('ReactTestRendererTraversal', () => {
171 expect(fn[0].findAllByType(View)).toHaveLength(1);
172 });
173
162 - it('searches via .findByProps() / .findAllByProps()', () => {
163 - const render = ReactTestRenderer.create(<Example />);
174 + it('searches via .findByProps() / .findAllByProps()', async () => {
175 + let render;
176 + await act(() => {
177 + render = ReactTestRenderer.create(<Example />);
178 + });
179 const foo = 'foo';
180 const bar = 'bar';
181 const baz = 'baz';
@@ -182,8 +197,11 @@ describe('ReactTestRendererTraversal', () => {
197 expect(render.root.findAllByProps({qux})).toHaveLength(3);
198 });
199
185 - it('skips special nodes', () => {
186 - const render = ReactTestRenderer.create(<Example />);
200 + it('skips special nodes', async () => {
201 + let render;
202 + await act(() => {
203 + render = ReactTestRenderer.create(<Example />);
204 + });
205 expect(render.root.findAllByType(React.Fragment)).toHaveLength(0);
206 expect(render.root.findAllByType(Context.Consumer)).toHaveLength(0);
207 expect(render.root.findAllByType(Context.Provider)).toHaveLength(0);
@@ -200,47 +218,62 @@ describe('ReactTestRendererTraversal', () => {
218 expect(nestedViews[2].parent).toBe(expectedParent);
219 });
220
203 - it('can have special nodes as roots', () => {
221 + it('can have special nodes as roots', async () => {
222 const FR = React.forwardRef((props, ref) => <section {...props} />);
205 - expect(
206 - ReactTestRenderer.create(
223 +
224 + let render1;
225 + await act(() => {
226 + render1 = ReactTestRenderer.create(
227 <FR>
228 <div />
229 <div />
230 </FR>,
211 - ).root.findAllByType('div').length,
212 - ).toBe(2);
213 - expect(
214 - ReactTestRenderer.create(
231 + );
232 + });
233 + expect(render1.root.findAllByType('div').length).toBe(2);
234 +
235 + let render2;
236 + await act(() => {
237 + render2 = ReactTestRenderer.create(
238 <>
239 <div />
240 <div />
241 </>,
219 - ).root.findAllByType('div').length,
220 - ).toBe(2);
221 - expect(
222 - ReactTestRenderer.create(
242 + );
243 + });
244 + expect(render2.root.findAllByType('div').length).toBe(2);
245 +
246 + let render3;
247 + await act(() => {
248 + render3 = ReactTestRenderer.create(
249 <React.Fragment key="foo">
250 <div />
251 <div />
252 </React.Fragment>,
227 - ).root.findAllByType('div').length,
228 - ).toBe(2);
229 - expect(
230 - ReactTestRenderer.create(
253 + );
254 + });
255 + expect(render3.root.findAllByType('div').length).toBe(2);
256 +
257 + let render4;
258 + await act(() => {
259 + render4 = ReactTestRenderer.create(
260 <React.StrictMode>
261 <div />
262 <div />
263 </React.StrictMode>,
235 - ).root.findAllByType('div').length,
236 - ).toBe(2);
237 - expect(
238 - ReactTestRenderer.create(
264 + );
265 + });
266 + expect(render4.root.findAllByType('div').length).toBe(2);
267 +
268 + let render5;
269 + await act(() => {
270 + render5 = ReactTestRenderer.create(
271 <Context.Provider value={null}>
272 <div />
273 <div />
274 </Context.Provider>,
243 - ).root.findAllByType('div').length,
244 - ).toBe(2);
275 + );
276 + });
277 + expect(render5.root.findAllByType('div').length).toBe(2);
278 });
279 });
packages/react/src/__tests__/ReactProfilerDevToolsIntegration-test.internal.js
+43 -23
@@ -19,6 +19,7 @@ describe('ReactProfiler DevTools integration', () => {
19 let hook;
20 let waitForAll;
21 let waitFor;
22 + let act;
23
24 beforeEach(() => {
25 global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = hook = {
@@ -39,6 +40,7 @@ describe('ReactProfiler DevTools integration', () => {
40 const InternalTestUtils = require('internal-test-utils');
41 waitForAll = InternalTestUtils.waitForAll;
42 waitFor = InternalTestUtils.waitFor;
43 + act = InternalTestUtils.act;
44
45 AdvanceTime = class extends React.Component {
46 static defaultProps = {
@@ -56,7 +58,7 @@ describe('ReactProfiler DevTools integration', () => {
58 };
59 });
60
59 - it('should auto-Profile all fibers if the DevTools hook is detected', () => {
61 + it('should auto-Profile all fibers if the DevTools hook is detected', async () => {
62 const App = ({multiplier}) => {
63 Scheduler.unstable_advanceTime(2);
64 return (
@@ -71,7 +73,12 @@ describe('ReactProfiler DevTools integration', () => {
73 };
74
75 const onRender = jest.fn(() => {});
74 - const rendered = ReactTestRenderer.create(<App multiplier={1} />);
76 + let rendered;
77 + await act(() => {
78 + rendered = ReactTestRenderer.create(<App multiplier={1} />, {
79 + unstable_isConcurrent: true,
80 + });
81 + });
82
83 expect(hook.onCommitFiberRoot).toHaveBeenCalledTimes(1);
84
@@ -90,7 +97,9 @@ describe('ReactProfiler DevTools integration', () => {
97 12,
98 );
99
93 - rendered.update(<App multiplier={2} />);
100 + await act(() => {
101 + rendered.update(<App multiplier={2} />);
102 + });
103
104 // Measure observable timing using the Profiler component.
105 // The time spent in App (above the Profiler) won't be included in the durations,
@@ -107,14 +116,18 @@ describe('ReactProfiler DevTools integration', () => {
116 );
117 });
118
110 - it('should reset the fiber stack correctly after an error when profiling host roots', () => {
119 + it('should reset the fiber stack correctly after an error when profiling host roots', async () => {
120 Scheduler.unstable_advanceTime(20);
121
113 - const rendered = ReactTestRenderer.create(
114 - <div>
115 - <AdvanceTime byAmount={2} />
116 - </div>,
117 - );
122 + let rendered;
123 + await act(() => {
124 + rendered = ReactTestRenderer.create(
125 + <div>
126 + <AdvanceTime byAmount={2} />
127 + </div>,
128 + {unstable_isConcurrent: true},
129 + );
130 + });
131
132 Scheduler.unstable_advanceTime(20);
133
@@ -122,22 +135,26 @@ describe('ReactProfiler DevTools integration', () => {
135 throw new Error('Oops!');
136 }
137
125 - expect(() => {
126 - rendered.update(
127 - <Throws>
128 - <AdvanceTime byAmount={3} />
129 - </Throws>,
130 - );
131 - }).toThrow('Oops!');
138 + await expect(async () => {
139 + await act(() => {
140 + rendered.update(
141 + <Throws>
142 + <AdvanceTime byAmount={3} />
143 + </Throws>,
144 + );
145 + });
146 + }).rejects.toThrow('Oops!');
147
148 Scheduler.unstable_advanceTime(20);
149
135 - // But this should render correctly, if the profiler's fiber stack has been reset.
136 - rendered.update(
137 - <div>
138 - <AdvanceTime byAmount={7} />
139 - </div>,
140 - );
150 + await act(() => {
151 + // But this should render correctly, if the profiler's fiber stack has been reset.
152 + rendered.update(
153 + <div>
154 + <AdvanceTime byAmount={7} />
155 + </div>,
156 + );
157 + });
158
159 // Measure unobservable timing required by the DevTools profiler.
160 // At this point, the base time should include only the most recent (not failed) render.
@@ -154,7 +171,10 @@ describe('ReactProfiler DevTools integration', () => {
171 return text;
172 }
173
157 - const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
174 + let root;
175 + await act(() => {
176 + root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
177 + });
178
179 // Commit something
180 root.update(<Text text="A" />);