@samitouri / QOS-React / commits / 44952dc984

Convert ReactComponent to createRoot (#28171)

Sebastian Silbermann committed Feb 2, 2024 at 00:50 UTC 44952dc9840d335fe76bf1501919ec36f54591e9
1 file changed +185 -82
packages/react-dom/src/__tests__/ReactComponent-test.js
+185 -82
@@ -11,8 +11,9 @@
11
12 let React;
13 let ReactDOM;
14 +let ReactDOMClient;
15 let ReactDOMServer;
15 -let ReactTestUtils;
16 +let act;
17
18 describe('ReactComponent', () => {
19 beforeEach(() => {
@@ -20,11 +21,12 @@ describe('ReactComponent', () => {
21
22 React = require('react');
23 ReactDOM = require('react-dom');
24 + ReactDOMClient = require('react-dom/client');
25 ReactDOMServer = require('react-dom/server');
24 - ReactTestUtils = require('react-dom/test-utils');
26 + act = require('internal-test-utils').act;
27 });
28
27 - it('should throw on invalid render targets', () => {
29 + it('should throw on invalid render targets in legacy roots', () => {
30 const container = document.createElement('div');
31 // jQuery objects are basically arrays; people often pass them in by mistake
32 expect(function () {
@@ -36,40 +38,52 @@ describe('ReactComponent', () => {
38 }).toThrowError(/Target container is not a DOM element./);
39 });
40
39 - it('should throw when supplying a string ref outside of render method', () => {
40 - let instance = <div ref="badDiv" />;
41 - expect(function () {
42 - instance = ReactTestUtils.renderIntoDocument(instance);
43 - }).toThrow();
41 + it('should throw when supplying a string ref outside of render method', async () => {
42 + const container = document.createElement('div');
43 + const root = ReactDOMClient.createRoot(container);
44 + await expect(
45 + act(() => {
46 + root.render(<div ref="badDiv" />);
47 + }),
48 + ).rejects.toThrow();
49 });
50
46 - it('should throw (in dev) when children are mutated during render', () => {
51 + it('should throw (in dev) when children are mutated during render', async () => {
52 function Wrapper(props) {
53 props.children[1] = <p key={1} />; // Mutation is illegal
54 return <div>{props.children}</div>;
55 }
56 if (__DEV__) {
52 - expect(() => {
53 - ReactTestUtils.renderIntoDocument(
57 + const container = document.createElement('div');
58 + const root = ReactDOMClient.createRoot(container);
59 + await expect(
60 + act(() => {
61 + root.render(
62 + <Wrapper>
63 + <span key={0} />
64 + <span key={1} />
65 + <span key={2} />
66 + </Wrapper>,
67 + );
68 + }),
69 + ).rejects.toThrowError(/Cannot assign to read only property.*/);
70 + } else {
71 + const container = document.createElement('div');
72 + const root = ReactDOMClient.createRoot(container);
73 +
74 + await act(() => {
75 + root.render(
76 <Wrapper>
77 <span key={0} />
78 <span key={1} />
79 <span key={2} />
80 </Wrapper>,
81 );
60 - }).toThrowError(/Cannot assign to read only property.*/);
61 - } else {
62 - ReactTestUtils.renderIntoDocument(
63 - <Wrapper>
64 - <span key={0} />
65 - <span key={1} />
66 - <span key={2} />
67 - </Wrapper>,
68 - );
82 + });
83 }
84 });
85
72 - it('should throw (in dev) when children are mutated during update', () => {
86 + it('should throw (in dev) when children are mutated during update', async () => {
87 class Wrapper extends React.Component {
88 componentDidMount() {
89 this.props.children[1] = <p key={1} />; // Mutation is illegal
@@ -82,27 +96,36 @@ describe('ReactComponent', () => {
96 }
97
98 if (__DEV__) {
85 - expect(() => {
86 - ReactTestUtils.renderIntoDocument(
99 + const container = document.createElement('div');
100 + const root = ReactDOMClient.createRoot(container);
101 + await expect(
102 + act(() => {
103 + root.render(
104 + <Wrapper>
105 + <span key={0} />
106 + <span key={1} />
107 + <span key={2} />
108 + </Wrapper>,
109 + );
110 + }),
111 + ).rejects.toThrowError(/Cannot assign to read only property.*/);
112 + } else {
113 + const container = document.createElement('div');
114 + const root = ReactDOMClient.createRoot(container);
115 +
116 + await act(() => {
117 + root.render(
118 <Wrapper>
119 <span key={0} />
120 <span key={1} />
121 <span key={2} />
122 </Wrapper>,
123 );
93 - }).toThrowError(/Cannot assign to read only property.*/);
94 - } else {
95 - ReactTestUtils.renderIntoDocument(
96 - <Wrapper>
97 - <span key={0} />
98 - <span key={1} />
99 - <span key={2} />
100 - </Wrapper>,
101 - );
124 + });
125 }
126 });
127
105 - it('should support string refs on owned components', () => {
128 + it('should support string refs on owned components', async () => {
129 const innerObj = {};
130 const outerObj = {};
131
@@ -133,8 +156,12 @@ describe('ReactComponent', () => {
156 }
157 }
158
136 - expect(() => {
137 - ReactTestUtils.renderIntoDocument(<Component />);
159 + const container = document.createElement('div');
160 + const root = ReactDOMClient.createRoot(container);
161 + await expect(async () => {
162 + await act(() => {
163 + root.render(<Component />);
164 + });
165 }).toErrorDev([
166 'Warning: Component "div" contains the string ref "inner". ' +
167 'Support for string refs will be removed in a future major release. ' +
@@ -151,7 +178,7 @@ describe('ReactComponent', () => {
178 ]);
179 });
180
154 - it('should not have string refs on unmounted components', () => {
181 + it('should not have string refs on unmounted components', async () => {
182 class Parent extends React.Component {
183 render() {
184 return (
@@ -172,10 +199,14 @@ describe('ReactComponent', () => {
199 }
200 }
201
175 - ReactTestUtils.renderIntoDocument(<Parent child={<span />} />);
202 + const container = document.createElement('div');
203 + const root = ReactDOMClient.createRoot(container);
204 + await act(() => {
205 + root.render(<Parent child={<span />} />);
206 + });
207 });
208
178 - it('should support callback-style refs', () => {
209 + it('should support callback-style refs', async () => {
210 const innerObj = {};
211 const outerObj = {};
212
@@ -211,11 +242,16 @@ describe('ReactComponent', () => {
242 }
243 }
244
214 - ReactTestUtils.renderIntoDocument(<Component />);
245 + const container = document.createElement('div');
246 + const root = ReactDOMClient.createRoot(container);
247 + await act(() => {
248 + root.render(<Component />);
249 + });
250 +
251 expect(mounted).toBe(true);
252 });
253
218 - it('should support object-style refs', () => {
254 + it('should support object-style refs', async () => {
255 const innerObj = {};
256 const outerObj = {};
257
@@ -254,11 +290,16 @@ describe('ReactComponent', () => {
290 }
291 }
292
257 - ReactTestUtils.renderIntoDocument(<Component />);
293 + const container = document.createElement('div');
294 + const root = ReactDOMClient.createRoot(container);
295 + await act(() => {
296 + root.render(<Component />);
297 + });
298 +
299 expect(mounted).toBe(true);
300 });
301
261 - it('should support new-style refs with mixed-up owners', () => {
302 + it('should support new-style refs with mixed-up owners', async () => {
303 class Wrapper extends React.Component {
304 getTitle = () => {
305 return this.props.title;
@@ -296,11 +337,17 @@ describe('ReactComponent', () => {
337 }
338 }
339
299 - ReactTestUtils.renderIntoDocument(<Component />);
340 + const container = document.createElement('div');
341 + const root = ReactDOMClient.createRoot(container);
342 +
343 + await act(() => {
344 + root.render(<Component />);
345 + });
346 +
347 expect(mounted).toBe(true);
348 });
349
303 - it('should call refs at the correct time', () => {
350 + it('should call refs at the correct time', async () => {
351 const log = [];
352
353 class Inner extends React.Component {
@@ -358,11 +405,18 @@ describe('ReactComponent', () => {
405 // mount, update, unmount
406 const el = document.createElement('div');
407 log.push('start mount');
361 - ReactDOM.render(<Outer />, el);
408 + const root = ReactDOMClient.createRoot(el);
409 + await act(() => {
410 + root.render(<Outer />);
411 + });
412 log.push('start update');
363 - ReactDOM.render(<Outer />, el);
413 + await act(() => {
414 + root.render(<Outer />);
415 + });
416 log.push('start unmount');
365 - ReactDOM.unmountComponentAtNode(el);
417 + await act(() => {
418 + root.unmount();
419 + });
420
421 /* eslint-disable indent */
422 expect(log).toEqual([
@@ -396,7 +450,7 @@ describe('ReactComponent', () => {
450 /* eslint-enable indent */
451 });
452
399 - it('fires the callback after a component is rendered', () => {
453 + it('fires the callback after a component is rendered in legacy roots', () => {
454 const callback = jest.fn();
455 const container = document.createElement('div');
456 ReactDOM.render(<div />, container, callback);
@@ -407,14 +461,20 @@ describe('ReactComponent', () => {
461 expect(callback).toHaveBeenCalledTimes(3);
462 });
463
410 - it('throws usefully when rendering badly-typed elements', () => {
464 + it('throws usefully when rendering badly-typed elements', async () => {
465 const X = undefined;
412 - expect(() => {
413 - expect(() => ReactTestUtils.renderIntoDocument(<X />)).toErrorDev(
466 + let container = document.createElement('div');
467 + let root = ReactDOMClient.createRoot(container);
468 + await expect(
469 + expect(async () => {
470 + await act(() => {
471 + root.render(<X />);
472 + });
473 + }).toErrorDev(
474 'React.createElement: type is invalid -- expected a string (for built-in components) ' +
475 'or a class/function (for composite components) but got: undefined.',
416 - );
417 - }).toThrowError(
476 + ),
477 + ).rejects.toThrowError(
478 'Element type is invalid: expected a string (for built-in components) ' +
479 'or a class/function (for composite components) but got: undefined.' +
480 (__DEV__
@@ -424,18 +484,24 @@ describe('ReactComponent', () => {
484 );
485
486 const Y = null;
427 - expect(() => {
428 - expect(() => ReactTestUtils.renderIntoDocument(<Y />)).toErrorDev(
487 + container = document.createElement('div');
488 + root = ReactDOMClient.createRoot(container);
489 + await expect(
490 + expect(async () => {
491 + await act(() => {
492 + root.render(<Y />);
493 + });
494 + }).toErrorDev(
495 'React.createElement: type is invalid -- expected a string (for built-in components) ' +
496 'or a class/function (for composite components) but got: null.',
431 - );
432 - }).toThrowError(
497 + ),
498 + ).rejects.toThrowError(
499 'Element type is invalid: expected a string (for built-in components) ' +
500 'or a class/function (for composite components) but got: null.',
501 );
502 });
503
438 - it('includes owner name in the error about badly-typed elements', () => {
504 + it('includes owner name in the error about badly-typed elements', async () => {
505 const X = undefined;
506
507 function Indirection(props) {
@@ -454,12 +520,18 @@ describe('ReactComponent', () => {
520 return <Bar />;
521 }
522
457 - expect(() => {
458 - expect(() => ReactTestUtils.renderIntoDocument(<Foo />)).toErrorDev(
523 + const container = document.createElement('div');
524 + const root = ReactDOMClient.createRoot(container);
525 + await expect(
526 + expect(async () => {
527 + await act(() => {
528 + root.render(<Foo />);
529 + });
530 + }).toErrorDev(
531 'React.createElement: type is invalid -- expected a string (for built-in components) ' +
532 'or a class/function (for composite components) but got: undefined.',
461 - );
462 - }).toThrowError(
533 + ),
534 + ).rejects.toThrowError(
535 'Element type is invalid: expected a string (for built-in components) ' +
536 'or a class/function (for composite components) but got: undefined.' +
537 (__DEV__
@@ -470,7 +542,7 @@ describe('ReactComponent', () => {
542 );
543 });
544
473 - it('throws if a plain object is used as a child', () => {
545 + it('throws if a plain object is used as a child', async () => {
546 const children = {
547 x: <span />,
548 y: <span />,
@@ -478,15 +550,18 @@ describe('ReactComponent', () => {
550 };
551 const element = <div>{[children]}</div>;
552 const container = document.createElement('div');
481 - expect(() => {
482 - ReactDOM.render(element, container);
483 - }).toThrowError(
553 + const root = ReactDOMClient.createRoot(container);
554 + await expect(
555 + act(() => {
556 + root.render(element);
557 + }),
558 + ).rejects.toThrowError(
559 'Objects are not valid as a React child (found: object with keys {x, y, z}). ' +
560 'If you meant to render a collection of children, use an array instead.',
561 );
562 });
563
489 - it('throws if a plain object even if it is in an owner', () => {
564 + it('throws if a plain object even if it is in an owner', async () => {
565 class Foo extends React.Component {
566 render() {
567 const children = {
@@ -498,9 +573,12 @@ describe('ReactComponent', () => {
573 }
574 }
575 const container = document.createElement('div');
501 - expect(() => {
502 - ReactDOM.render(<Foo />, container);
503 - }).toThrowError(
576 + const root = ReactDOMClient.createRoot(container);
577 + await expect(
578 + act(() => {
579 + root.render(<Foo />);
580 + }),
581 + ).rejects.toThrowError(
582 'Objects are not valid as a React child (found: object with keys {a, b, c}).' +
583 ' If you meant to render a collection of children, use an array ' +
584 'instead.',
@@ -545,12 +623,17 @@ describe('ReactComponent', () => {
623 });
624
625 describe('with new features', () => {
548 - it('warns on function as a return value from a function', () => {
626 + it('warns on function as a return value from a function', async () => {
627 function Foo() {
628 return Foo;
629 }
630 const container = document.createElement('div');
553 - expect(() => ReactDOM.render(<Foo />, container)).toErrorDev(
631 + const root = ReactDOMClient.createRoot(container);
632 + await expect(async () => {
633 + await act(() => {
634 + root.render(<Foo />);
635 + });
636 + }).toErrorDev(
637 'Warning: Functions are not valid as a React child. This may happen if ' +
638 'you return a Component instead of <Component /> from render. ' +
639 'Or maybe you meant to call this function rather than return it.\n' +
@@ -558,14 +641,20 @@ describe('ReactComponent', () => {
641 );
642 });
643
561 - it('warns on function as a return value from a class', () => {
644 + it('warns on function as a return value from a class', async () => {
645 class Foo extends React.Component {
646 render() {
647 return Foo;
648 }
649 }
650 const container = document.createElement('div');
568 - expect(() => ReactDOM.render(<Foo />, container)).toErrorDev(
651 + await expect(async () => {
652 + const root = ReactDOMClient.createRoot(container);
653 +
654 + await act(() => {
655 + root.render(<Foo />);
656 + });
657 + }).toErrorDev(
658 'Warning: Functions are not valid as a React child. This may happen if ' +
659 'you return a Component instead of <Component /> from render. ' +
660 'Or maybe you meant to call this function rather than return it.\n' +
@@ -573,7 +662,7 @@ describe('ReactComponent', () => {
662 );
663 });
664
576 - it('warns on function as a child to host component', () => {
665 + it('warns on function as a child to host component', async () => {
666 function Foo() {
667 return (
668 <div>
@@ -582,7 +671,12 @@ describe('ReactComponent', () => {
671 );
672 }
673 const container = document.createElement('div');
585 - expect(() => ReactDOM.render(<Foo />, container)).toErrorDev(
674 + const root = ReactDOMClient.createRoot(container);
675 + await expect(async () => {
676 + await act(() => {
677 + root.render(<Foo />);
678 + });
679 + }).toErrorDev(
680 'Warning: Functions are not valid as a React child. This may happen if ' +
681 'you return a Component instead of <Component /> from render. ' +
682 'Or maybe you meant to call this function rather than return it.\n' +
@@ -592,7 +686,7 @@ describe('ReactComponent', () => {
686 );
687 });
688
595 - it('does not warn for function-as-a-child that gets resolved', () => {
689 + it('does not warn for function-as-a-child that gets resolved', async () => {
690 function Bar(props) {
691 return props.children();
692 }
@@ -600,11 +694,15 @@ describe('ReactComponent', () => {
694 return <Bar>{() => 'Hello'}</Bar>;
695 }
696 const container = document.createElement('div');
603 - ReactDOM.render(<Foo />, container);
697 + const root = ReactDOMClient.createRoot(container);
698 + await act(() => {
699 + root.render(<Foo />);
700 + });
701 +
702 expect(container.innerHTML).toBe('Hello');
703 });
704
607 - it('deduplicates function type warnings based on component type', () => {
705 + it('deduplicates function type warnings based on component type', async () => {
706 class Foo extends React.PureComponent {
707 constructor() {
708 super();
@@ -624,9 +722,12 @@ describe('ReactComponent', () => {
722 }
723 }
724 const container = document.createElement('div');
725 + const root = ReactDOMClient.createRoot(container);
726 let component;
628 - expect(() => {
629 - component = ReactDOM.render(<Foo />, container);
727 + await expect(async () => {
728 + await act(() => {
729 + root.render(<Foo ref={current => (component = current)} />);
730 + });
731 }).toErrorDev([
732 'Warning: Functions are not valid as a React child. This may happen if ' +
733 'you return a Component instead of <Component /> from render. ' +
@@ -640,7 +741,9 @@ describe('ReactComponent', () => {
741 ' in div (at **)\n' +
742 ' in Foo (at **)',
743 ]);
643 - component.setState({type: 'portobello mushrooms'});
744 + await act(() => {
745 + component.setState({type: 'portobello mushrooms'});
746 + });
747 });
748 });
749 });