Repository: facebook/react
Pull request: Remove ReactTestUtils from ReactDOMComponent


Changed files:
filename: packages/react-dom/src/__tests__/ReactDOMComponent-test.js
patch: @@ -11,7 +11,6 @@
 
 describe('ReactDOMComponent', () => {
   let React;
-  let ReactTestUtils;
   let ReactDOM;
   let ReactDOMClient;
   let ReactDOMServer;
@@ -25,7 +24,6 @@ describe('ReactDOMComponent', () => {
     ReactDOM = require('react-dom');
     ReactDOMClient = require('react-dom/client');
     ReactDOMServer = require('react-dom/server');
-    ReactTestUtils = require('react-dom/test-utils');
     act = require('internal-test-utils').act;
   });
 
@@ -162,7 +160,7 @@ describe('ReactDOMComponent', () => {
       expect(stubStyle.lineHeight).toBe('');
     });
 
-    it('should throw when mutating style objects', () => {
+    it('should throw when mutating style objects', async () => {
       const style = {border: '1px solid black'};
 
       class App extends React.Component {
@@ -173,7 +171,12 @@ describe('ReactDOMComponent', () => {
         }
       }
 
-      ReactTestUtils.renderIntoDocument(<App />);
+      const container = document.createElement('div');
+      const root = ReactDOMClient.createRoot(container);
+      await act(() => {
+        root.render(<App />);
+      });
+
       if (__DEV__) {
         expect(() => (style.position = 'absolute')).toThrow();
       }
@@ -298,14 +301,18 @@ describe('ReactDOMComponent', () => {
       expect(container.firstChild.getAttribute('CHILDREN')).toBe('5');
     });
 
-    it('should not warn for "0" as a unitless style value', () => {
+    it('should not warn for "0" as a unitless style value', async () => {
       class Component extends React.Component {
         render() {
           return <div style={{margin: '0'}} />;
         }
       }
 
-      ReactTestUtils.renderIntoDocument(<Component />);
+      const container = document.createElement('div');
+      const root = ReactDOMClient.createRoot(container);
+      await act(() => {
+        root.render(<Component />);
+      });
     });
 
     it('should warn nicely about NaN in style', async () => {
@@ -906,28 +913,36 @@ describe('ReactDOMComponent', () => {
       ]);
     });
 
-    it('should reject attribute key injection attack on mount for regular DOM', () => {
-      expect(() => {
+    it('should reject attribute key injection attack on mount for regular DOM', async () => {
+      await expect(async () => {
         for (let i = 0; i < 3; i++) {
           const container = document.createElement('div');
-          ReactDOM.render(
-            React.createElement(
-              'div',
-              {'blah" onclick="beevil" noise="hi': 'selected'},
-              null,
-            ),
-            container,
-          );
+          let root = ReactDOMClient.createRoot(container);
+          await act(() => {
+            root.render(
+              React.createElement(
+                'div',
+                {'blah" onclick="beevil" noise="hi': 'selected'},
+                null,
+              ),
+            );
+          });
+
           expect(container.firstChild.attributes.length).toBe(0);
-          ReactDOM.unmountComponentAtNode(container);
-          ReactDOM.render(
-            React.createElement(
-              'div',
-              {'></div><script>alert("hi")</script>': 'selected'},
-              null,
-            ),
-            container,
-          );
+          await act(() => {
+            root.unmount();
+          });
+          root = ReactDOMClient.createRoot(container);
+          await act(() => {
+            root.render(
+              React.createElement(
+                'div',
+                {'></div><script>alert("hi")</script>': 'selected'},
+                null,
+              ),
+            );
+          });
+
           expect(container.firstChild.attributes.length).toBe(0);
         }
       }).toErrorDev([
@@ -936,28 +951,37 @@ describe('ReactDOMComponent', () => {
       ]);
     });
 
-    it('should reject attribute key injection attack on mount for custom elements', () => {
-      expect(() => {
+    it('should reject attribute key injection attack on mount for custom elements', async () => {
+      await expect(async () => {
         for (let i = 0; i < 3; i++) {
           const container = document.createElement('div');
-          ReactDOM.render(
-            React.createElement(
-              'x-foo-component',
-              {'blah" onclick="beevil" noise="hi': 'selected'},
-              null,
-            ),
-            container,
-          );
+          let root = ReactDOMClient.createRoot(container);
+
+          await act(() => {
+            root.render(
+              React.createElement(
+                'x-foo-component',
+                {'blah" onclick="beevil" noise="hi': 'selected'},
+                null,
+              ),
+            );
+          });
+
           expect(container.firstChild.attributes.length).toBe(0);
-          ReactDOM.unmountComponentAtNode(container);
-          ReactDOM.render(
-            React.createElement(
-              'x-foo-component',
-              {'></x-foo-component><script>alert("hi")</script>': 'selected'},
-              null,
-            ),
-            container,
-          );
+          await act(() => {
+            root.unmount();
+          });
+          root = ReactDOMClient.createRoot(container);
+          await act(() => {
+            root.render(
+              React.createElement(
+                'x-foo-component',
+                {'></x-foo-component><script>alert("hi")</script>': 'selected'},
+                null,
+              ),
+            );
+          });
+
           expect(container.firstChild.attributes.length).toBe(0);
         }
       }).toErrorDev([
@@ -966,29 +990,36 @@ describe('ReactDOMComponent', () => {
       ]);
     });
 
-    it('should reject attribute key injection attack on update for regular DOM', () => {
-      expect(() => {
+    it('should reject attribute key injection attack on update for regular DOM', async () => {
+      await expect(async () => {
         for (let i = 0; i < 3; i++) {
           const container = document.createElement('div');
           const beforeUpdate = React.createElement('div', {}, null);
-          ReactDOM.render(beforeUpdate, container);
-          ReactDOM.render(
-            React.createElement(
-              'div',
-              {'blah" onclick="beevil" noise="hi': 'selected'},
-              null,
-            ),
-            container,
-          );
+          const root = ReactDOMClient.createRoot(container);
+          await act(() => {
+            root.render(beforeUpdate);
+          });
+          await act(() => {
+            root.render(
+              React.createElement(
+                'div',
+                {'blah" onclick="beevil" noise="hi': 'selected'},
+                null,
+              ),
+            );
+          });
+
           expect(container.firstChild.attributes.length).toBe(0);
-          ReactDOM.render(
-            React.createElement(
-              'div',
-              {'></div><script>alert("hi")</script>': 'selected'},
-              null,
-            ),
-            container,
-          );
+          await act(() => {
+            root.render(
+              React.createElement(
+                'div',
+                {'></div><script>alert("hi")</script>': 'selected'},
+                null,
+              ),
+            );
+          });
+
           expect(container.firstChild.attributes.length).toBe(0);
         }
       }).toErrorDev([
@@ -997,29 +1028,36 @@ describe('ReactDOMComponent', () => {
       ]);
     });
 
-    it('should reject attribute key injection attack on update for custom elements', () => {
-      expect(() => {
+    it('should reject attribute key injection attack on update for custom elements', async () => {
+      await expect(async () => {
         for (let i = 0; i < 3; i++) {
           const container = document.createElement('div');
           const beforeUpdate = React.createElement('x-foo-component', {}, null);
-          ReactDOM.render(beforeUpdate, container);
-          ReactDOM.render(
-            React.createElement(
-              'x-foo-component',
-              {'blah" onclick="beevil" noise="hi': 'selected'},
-              null,
-            ),
-            container,
-          );
+          const root = ReactDOMClient.createRoot(container);
+          await act(() => {
+            root.render(beforeUpdate);
+          });
+          await act(() => {
+            root.render(
+              React.createElement(
+                'x-foo-component',
+                {'blah" onclick="beevil" noise="hi': 'selected'},
+                null,
+              ),
+            );
+          });
+
           expect(container.firstChild.attributes.length).toBe(0);
-          ReactDOM.render(
-            React.createElement(
-              'x-foo-component',
-              {'></x-foo-component><script>alert("hi")</script>': 'selected'},
-              null,
-            ),
-            container,
-          );
+          await act(() => {
+            root.render(
+              React.createElement(
+                'x-foo-component',
+                {'></x-foo-component><script>alert("hi")</script>': 'selected'},
+                null,
+              ),
+            );
+          });
+
           expect(container.firstChild.attributes.length).toBe(0);
         }
       }).toErrorDev([
@@ -1615,31 +1653,50 @@ describe('ReactDOMComponent', () => {
       expect(returnedValue).toContain('</BR>');
     });
 
-    it('should warn on upper case HTML tags, not SVG nor custom tags', () => {
-      ReactTestUtils.renderIntoDocument(
-        React.createElement('svg', null, React.createElement('PATH')),
-      );
-      ReactTestUtils.renderIntoDocument(React.createElement('CUSTOM-TAG'));
+    it('should warn on upper case HTML tags, not SVG nor custom tags', async () => {
+      let container = document.createElement('div');
+      let root = ReactDOMClient.createRoot(container);
+      await act(() => {
+        root.render(
+          React.createElement('svg', null, React.createElement('PATH')),
+        );
+      });
 
-      expect(() =>
-        ReactTestUtils.renderIntoDocument(React.createElement('IMG')),
-      ).toErrorDev(
+      container = document.createElement('div');
+      root = ReactDOMClient.createRoot(container);
+      await act(() => {
+        root.render(React.createElement('CUSTOM-TAG'));
+      });
+
+      await expect(async () => {
+        container = document.createElement('div');
+        root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(React.createElement('IMG'));
+        });
+      }).toErrorDev(
         '<IMG /> is using incorrect casing. ' +
           'Use PascalCase for React components, ' +
           'or lowercase for HTML elements.',
       );
     });
 
-    it('should warn on props reserved for future use', () => {
-      expect(() =>
-        ReactTestUtils.renderIntoDocument(<div aria="hello" />),
-      ).toErrorDev(
+    it('should warn on props reserved for future use', async () => {
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(<div aria="hello" />);
+        });
+      }).toErrorDev(
         'The `aria` attribute is reserved for future use in React. ' +
           'Pass individual `aria-` attributes instead.',
       );
     });
 
-    it('should warn if the tag is unrecognized', () => {
+    it('should warn if the tag is unrecognized', async () => {
       let realToString;
       try {
         realToString = Object.prototype.toString;
@@ -1652,19 +1709,44 @@ describe('ReactDOMComponent', () => {
         };
         Object.prototype.toString = wrappedToString; // eslint-disable-line no-extend-native
 
-        expect(() => ReactTestUtils.renderIntoDocument(<bar />)).toErrorDev(
-          'The tag <bar> is unrecognized in this browser',
-        );
+        await expect(async () => {
+          const container = document.createElement('div');
+          const root = ReactDOMClient.createRoot(container);
+
+          await act(() => {
+            root.render(<bar />);
+          });
+        }).toErrorDev('The tag <bar> is unrecognized in this browser');
         // Test deduplication
-        expect(() => ReactTestUtils.renderIntoDocument(<foo />)).toErrorDev(
-          'The tag <foo> is unrecognized in this browser',
-        );
-        ReactTestUtils.renderIntoDocument(<foo />);
-        ReactTestUtils.renderIntoDocument(<time />);
+        await expect(async () => {
+          const container = document.createElement('div');
+          const root = ReactDOMClient.createRoot(container);
+
+          await act(() => {
+            root.render(<foo />);
+          });
+        }).toErrorDev('The tag <foo> is unrecognized in this browser');
+        let container = document.createElement('div');
+        let root = ReactDOMClient.createRoot(container);
+        await act(() => {
+          root.render(<foo />);
+        });
+
+        container = document.createElement('div');
+        root = ReactDOMClient.createRoot(container);
+        await act(() => {
+          root.render(<time />);
+        });
+
         // Corner case. Make sure out deduplication logic doesn't break with weird tag.
-        expect(() =>
-          ReactTestUtils.renderIntoDocument(<hasOwnProperty />),
-        ).toErrorDev([
+        await expect(async () => {
+          container = document.createElement('div');
+          root = ReactDOMClient.createRoot(container);
+
+          await act(() => {
+            root.render(<hasOwnProperty />);
+          });
+        }).toErrorDev([
           '<hasOwnProperty /> is using incorrect casing. ' +
             'Use PascalCase for React components, ' +
             'or lowercase for HTML elements.',
@@ -2069,26 +2151,42 @@ describe('ReactDOMComponent', () => {
       );
     });
 
-    it('should throw when an invalid tag name is used', () => {
+    it('should throw when an invalid tag name is used', async () => {
       const hackzor = React.createElement('script tag');
-      expect(() => ReactTestUtils.renderIntoDocument(hackzor)).toThrow();
+      const container = document.createElement('div');
+      const root = ReactDOMClient.createRoot(container);
+      await expect(
+        act(() => {
+          root.render(hackzor);
+        }),
+      ).rejects.toThrow();
     });
 
-    it('should throw when an attack vector is used', () => {
+    it('should throw when an attack vector is used', async () => {
       const hackzor = React.createElement('div><img /><div');
-      expect(() => ReactTestUtils.renderIntoDocument(hackzor)).toThrow();
+      const container = document.createElement('div');
+      const root = ReactDOMClient.createRoot(container);
+      await expect(
+        act(() => {
+          root.render(hackzor);
+        }),
+      ).rejects.toThrow();
     });
   });
 
   describe('nesting validation', () => {
-    it('warns on invalid nesting', () => {
-      expect(() => {
-        ReactTestUtils.renderIntoDocument(
-          <div>
-            <tr />
-            <tr />
-          </div>,
-        );
+    it('warns on invalid nesting', async () => {
+      const container = document.createElement('div');
+      const root = ReactDOMClient.createRoot(container);
+      await expect(async () => {
+        await act(() => {
+          root.render(
+            <div>
+              <tr />
+              <tr />
+            </div>,
+          );
+        });
       }).toErrorDev([
         'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
           '<div>.' +
@@ -2118,7 +2216,7 @@ describe('ReactDOMComponent', () => {
       );
     });
 
-    it('warns nicely for table rows', () => {
+    it('warns nicely for table rows', async () => {
       class Row extends React.Component {
         render() {
           return <tr>x</tr>;
@@ -2135,7 +2233,14 @@ describe('ReactDOMComponent', () => {
         }
       }
 
-      expect(() => ReactTestUtils.renderIntoDocument(<Foo />)).toErrorDev([
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(<Foo />);
+        });
+      }).toErrorDev([
         'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
           '<table>. Add a <tbody>, <thead> or <tfoot> to your code to match the DOM tree generated ' +
           'by the browser.' +
@@ -2216,7 +2321,7 @@ describe('ReactDOMComponent', () => {
       ]);
     });
 
-    it('gives useful context in warnings', () => {
+    it('gives useful context in warnings', async () => {
       function Row() {
         return <tr />;
       }
@@ -2234,7 +2339,13 @@ describe('ReactDOMComponent', () => {
       function App1() {
         return <Viz1 />;
       }
-      expect(() => ReactTestUtils.renderIntoDocument(<App1 />)).toErrorDev(
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+        await act(() => {
+          root.render(<App1 />);
+        });
+      }).toErrorDev(
         '\n    in tr (at **)' +
           '\n    in Row (at **)' +
           '\n    in FancyRow (at **)' +
@@ -2243,7 +2354,7 @@ describe('ReactDOMComponent', () => {
       );
     });
 
-    it('gives useful context in warnings 2', () => {
+    it('gives useful context in warnings 2', async () => {
       function Row() {
         return <tr />;
       }
@@ -2273,7 +2384,14 @@ describe('ReactDOMComponent', () => {
       function App2() {
         return <Viz2 />;
       }
-      expect(() => ReactTestUtils.renderIntoDocument(<App2 />)).toErrorDev(
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(<App2 />);
+        });
+      }).toErrorDev(
         '\n    in tr (at **)' +
           '\n    in Row (at **)' +
           '\n    in FancyRow (at **)' +
@@ -2284,7 +2402,7 @@ describe('ReactDOMComponent', () => {
       );
     });
 
-    it('gives useful context in warnings 3', () => {
+    it('gives useful context in warnings 3', async () => {
       function Row() {
         return <tr />;
       }
@@ -2303,12 +2421,17 @@ describe('ReactDOMComponent', () => {
           return <Table>{this.props.children}</Table>;
         }
       }
-      expect(() => {
-        ReactTestUtils.renderIntoDocument(
-          <FancyTable>
-            <FancyRow />
-          </FancyTable>,
-        );
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(
+            <FancyTable>
+              <FancyRow />
+            </FancyTable>,
+          );
+        });
       }).toErrorDev(
         '\n    in tr (at **)' +
           '\n    in Row (at **)' +
@@ -2319,20 +2442,25 @@ describe('ReactDOMComponent', () => {
       );
     });
 
-    it('gives useful context in warnings 4', () => {
+    it('gives useful context in warnings 4', async () => {
       function Row() {
         return <tr />;
       }
       function FancyRow() {
         return <Row />;
       }
 
-      expect(() => {
-        ReactTestUtils.renderIntoDocument(
-          <table>
-            <FancyRow />
-          </table>,
-        );
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(
+            <table>
+              <FancyRow />
+            </table>,
+          );
+        });
       }).toErrorDev(
         '\n    in tr (at **)' +
           '\n    in Row (at **)' +
@@ -2341,7 +2469,7 @@ describe('ReactDOMComponent', () => {
       );
     });
 
-    it('gives useful context in warnings 5', () => {
+    it('gives useful context in warnings 5', async () => {
       class Table extends React.Component {
         render() {
           return <table>{this.props.children}</table>;
@@ -2354,12 +2482,16 @@ describe('ReactDOMComponent', () => {
         }
       }
 
-      expect(() => {
-        ReactTestUtils.renderIntoDocument(
-          <FancyTable>
-            <tr />
-          </FancyTable>,
-        );
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+        await act(() => {
+          root.render(
+            <FancyTable>
+              <tr />
+            </FancyTable>,
+          );
+        });
       }).toErrorDev(
         '\n    in tr (at **)' +
           '\n    in table (at **)' +
@@ -2373,14 +2505,18 @@ describe('ReactDOMComponent', () => {
         }
       }
 
-      expect(() => {
-        ReactTestUtils.renderIntoDocument(
-          <Link>
-            <div>
-              <Link />
-            </div>
-          </Link>,
-        );
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+        await act(() => {
+          root.render(
+            <Link>
+              <div>
+                <Link />
+              </div>
+            </Link>,
+          );
+        });
       }).toErrorDev(
         '\n    in a (at **)' +
           '\n    in Link (at **)' +
@@ -2420,32 +2556,48 @@ describe('ReactDOMComponent', () => {
       // without access to the event system (which we don't bundle).
     });
 
-    it('should warn about incorrect casing on properties', () => {
-      expect(() => {
-        ReactTestUtils.renderIntoDocument(
-          React.createElement('input', {type: 'text', tabindex: '1'}),
-        );
+    it('should warn about incorrect casing on properties', async () => {
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+        await act(() => {
+          root.render(
+            React.createElement('input', {type: 'text', tabindex: '1'}),
+          );
+        });
       }).toErrorDev('tabIndex');
     });
 
-    it('should warn about incorrect casing on event handlers', () => {
-      expect(() => {
-        ReactTestUtils.renderIntoDocument(
-          React.createElement('input', {type: 'text', oninput: '1'}),
-        );
+    it('should warn about incorrect casing on event handlers', async () => {
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(
+            React.createElement('input', {type: 'text', oninput: '1'}),
+          );
+        });
       }).toErrorDev('onInput');
-      expect(() => {
-        ReactTestUtils.renderIntoDocument(
-          React.createElement('input', {type: 'text', onKeydown: '1'}),
-        );
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(
+            React.createElement('input', {type: 'text', onKeydown: '1'}),
+          );
+        });
       }).toErrorDev('onKeyDown');
     });
 
-    it('should warn about class', () => {
-      expect(() => {
-        ReactTestUtils.renderIntoDocument(
-          React.createElement('div', {class: 'muffins'}),
-        );
+    it('should warn about class', async () => {
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+        await act(() => {
+          root.render(React.createElement('div', {class: 'muffins'}));
+        });
       }).toErrorDev('className');
     });
 
@@ -2457,31 +2609,57 @@ describe('ReactDOMComponent', () => {
       }).toErrorDev('className');
     });
 
-    it('should warn about props that are no longer supported', () => {
-      ReactTestUtils.renderIntoDocument(<div />);
+    it('should warn about props that are no longer supported', async () => {
+      let container = document.createElement('div');
+      let root = ReactDOMClient.createRoot(container);
+      await act(() => {
+        root.render(<div />);
+      });
 
-      expect(() =>
-        ReactTestUtils.renderIntoDocument(<div onFocusIn={() => {}} />),
-      ).toErrorDev(
-        'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
-      );
-      expect(() =>
-        ReactTestUtils.renderIntoDocument(<div onFocusOut={() => {}} />),
-      ).toErrorDev(
+      await expect(async () => {
+        container = document.createElement('div');
+        root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(<div onFocusIn={() => {}} />);
+        });
+      }).toErrorDev(
+        'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
+      );
+      await expect(async () => {
+        container = document.createElement('div');
+        root = ReactDOMClient.createRoot(container);
+        await act(() => {
+          root.render(<div onFocusOut={() => {}} />);
+        });
+      }).toErrorDev(
         'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
       );
     });
 
-    it('should warn about props that are no longer supported without case sensitivity', () => {
-      ReactTestUtils.renderIntoDocument(<div />);
-      expect(() =>
-        ReactTestUtils.renderIntoDocument(<div onfocusin={() => {}} />),
-      ).toErrorDev(
+    it('should warn about props that are no longer supported without case sensitivity', async () => {
+      let container = document.createElement('div');
+      let root = ReactDOMClient.createRoot(container);
+      await act(() => {
+        root.render(<div />);
+      });
+
+      await expect(async () => {
+        container = document.createElement('div');
+        root = ReactDOMClient.createRoot(container);
+        await act(() => {
+          root.render(<div onfocusin={() => {}} />);
+        });
+      }).toErrorDev(
         'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
       );
-      expect(() =>
-        ReactTestUtils.renderIntoDocument(<div onfocusout={() => {}} />),
-      ).toErrorDev(
+      await expect(async () => {
+        container = document.createElement('div');
+        root = ReactDOMClient.createRoot(container);
+        await act(() => {
+          root.render(<div onfocusout={() => {}} />);
+        });
+      }).toErrorDev(
         'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
       );
     });
@@ -2514,15 +2692,23 @@ describe('ReactDOMComponent', () => {
       );
     });
 
-    it('gives source code refs for unknown prop warning', () => {
-      expect(() =>
-        ReactTestUtils.renderIntoDocument(<div class="paladin" />),
-      ).toErrorDev(
+    it('gives source code refs for unknown prop warning', async () => {
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+        await act(() => {
+          root.render(<div class="paladin" />);
+        });
+      }).toErrorDev(
         'Warning: Invalid DOM property `class`. Did you mean `className`?\n    in div (at **)',
       );
-      expect(() =>
-        ReactTestUtils.renderIntoDocument(<input type="text" onclick="1" />),
-      ).toErrorDev(
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+        await act(() => {
+          root.render(<input type="text" onclick="1" />);
+        });
+      }).toErrorDev(
         'Warning: Invalid event handler property `onclick`. Did you mean ' +
           '`onClick`?\n    in input (at **)',
       );
@@ -2547,29 +2733,40 @@ describe('ReactDOMComponent', () => {
       );
     });
 
-    it('gives source code refs for unknown prop warning for update render', () => {
-      const container = document.createElement('div');
+    it('gives source code refs for unknown prop warning for update render', async () => {
+      let container = document.createElement('div');
+      let root = ReactDOMClient.createRoot(container);
+      await act(() => {
+        root.render(<div className="paladin" />);
+      });
 
-      ReactTestUtils.renderIntoDocument(<div className="paladin" />, container);
-      expect(() =>
-        ReactTestUtils.renderIntoDocument(<div class="paladin" />, container),
-      ).toErrorDev(
+      container = document.createElement('div');
+      root = ReactDOMClient.createRoot(container);
+      await expect(async () => {
+        await act(() => {
+          root.render(<div class="paladin" />);
+        });
+      }).toErrorDev(
         'Warning: Invalid DOM property `class`. Did you mean `className`?\n    in div (at **)',
       );
     });
 
-    it('gives source code refs for unknown prop warning for exact elements', () => {
-      expect(() =>
-        ReactTestUtils.renderIntoDocument(
-          <div className="foo1">
-            <span class="foo2" />
-            <div onClick={() => {}} />
-            <strong onclick={() => {}} />
-            <div className="foo5" />
-            <div className="foo6" />
-          </div>,
-        ),
-      ).toErrorDev([
+    it('gives source code refs for unknown prop warning for exact elements', async () => {
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+        await act(() => {
+          root.render(
+            <div className="foo1">
+              <span class="foo2" />
+              <div onClick={() => {}} />
+              <strong onclick={() => {}} />
+              <div className="foo5" />
+              <div className="foo6" />
+            </div>,
+          );
+        });
+      }).toErrorDev([
         'Invalid DOM property `class`. Did you mean `className`?\n    in span (at **)',
         'Invalid event handler property `onclick`. Did you mean `onClick`?\n    in strong (at **)',
       ]);
@@ -2594,9 +2791,7 @@ describe('ReactDOMComponent', () => {
       ]);
     });
 
-    it('gives source code refs for unknown prop warning for exact elements in composition', () => {
-      const container = document.createElement('div');
-
+    it('gives source code refs for unknown prop warning for exact elements in composition', async () => {
       class Parent extends React.Component {
         render() {
           return (
@@ -2634,9 +2829,13 @@ describe('ReactDOMComponent', () => {
         }
       }
 
-      expect(() =>
-        ReactTestUtils.renderIntoDocument(<Parent />, container),
-      ).toErrorDev([
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+        await act(() => {
+          root.render(<Parent />);
+        });
+      }).toErrorDev([
         'Invalid DOM property `class`. Did you mean `className`?\n    in span (at **)',
         'Invalid event handler property `onclick`. Did you mean `onClick`?\n    in strong (at **)',
       ]);
@@ -2692,20 +2891,26 @@ describe('ReactDOMComponent', () => {
       ]);
     });
 
-    it('should suggest property name if available', () => {
-      expect(() =>
-        ReactTestUtils.renderIntoDocument(
-          React.createElement('label', {for: 'test'}),
-        ),
-      ).toErrorDev(
+    it('should suggest property name if available', async () => {
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+        await act(() => {
+          root.render(React.createElement('label', {for: 'test'}));
+        });
+      }).toErrorDev(
         'Warning: Invalid DOM property `for`. Did you mean `htmlFor`?\n    in label',
       );
 
-      expect(() =>
-        ReactTestUtils.renderIntoDocument(
-          React.createElement('input', {type: 'text', autofocus: true}),
-        ),
-      ).toErrorDev(
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+        await act(() => {
+          root.render(
+            React.createElement('input', {type: 'text', autofocus: true}),
+          );
+        });
+      }).toErrorDev(
         'Warning: Invalid DOM property `autofocus`. Did you mean `autoFocus`?\n    in input',
       );
     });
@@ -2762,36 +2967,51 @@ describe('ReactDOMComponent', () => {
   });
 
   describe('Attributes with aliases', function () {
-    it('sets aliased attributes on HTML attributes', function () {
+    it('sets aliased attributes on HTML attributes', async function () {
       let el;
-      expect(() => {
-        el = ReactTestUtils.renderIntoDocument(<div class="test" />);
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(<div class="test" ref={current => (el = current)} />);
+        });
       }).toErrorDev(
         'Warning: Invalid DOM property `class`. Did you mean `className`?',
       );
 
       expect(el.className).toBe('test');
     });
 
-    it('sets incorrectly cased aliased attributes on HTML attributes with a warning', function () {
+    it('sets incorrectly cased aliased attributes on HTML attributes with a warning', async function () {
       let el;
-      expect(() => {
-        el = ReactTestUtils.renderIntoDocument(<div cLASS="test" />);
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(<div cLASS="test" ref={current => (el = current)} />);
+        });
       }).toErrorDev(
         'Warning: Invalid DOM property `cLASS`. Did you mean `className`?',
       );
 
       expect(el.className).toBe('test');
     });
 
-    it('sets aliased attributes on SVG elements with a warning', function () {
+    it('sets aliased attributes on SVG elements with a warning', async function () {
       let el;
-      expect(() => {
-        el = ReactTestUtils.renderIntoDocument(
-          <svg>
-            <text arabic-form="initial" />
-          </svg>,
-        );
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(
+            <svg ref={current => (el = current)}>
+              <text arabic-form="initial" />
+            </svg>,
+          );
+        });
       }).toErrorDev(
         'Warning: Invalid DOM property `arabic-form`. Did you mean `arabicForm`?',
       );
@@ -2800,18 +3020,26 @@ describe('ReactDOMComponent', () => {
       expect(text.hasAttribute('arabic-form')).toBe(true);
     });
 
-    it('sets aliased attributes on custom elements', function () {
-      const el = ReactTestUtils.renderIntoDocument(
-        <div is="custom-element" class="test" />,
-      );
+    it('sets aliased attributes on custom elements', async function () {
+      const container = document.createElement('div');
+      const root = ReactDOMClient.createRoot(container);
+      await act(() => {
+        root.render(<div is="custom-element" class="test" />);
+      });
 
+      const el = container.firstChild;
       expect(el.getAttribute('class')).toBe('test');
     });
 
-    it('aliased attributes on custom elements with bad casing', function () {
-      const el = ReactTestUtils.renderIntoDocument(
-        <div is="custom-element" claSS="test" />,
-      );
+    it('aliased attributes on custom elements with bad casing', async function () {
+      const container = document.createElement('div');
+      const root = ReactDOMClient.createRoot(container);
+
+      await act(() => {
+        root.render(<div is="custom-element" claSS="test" />);
+      });
+
+      const el = container.firstChild;
 
       expect(el.getAttribute('class')).toBe('test');
     });
@@ -2831,8 +3059,15 @@ describe('ReactDOMComponent', () => {
   });
 
   describe('Custom attributes', function () {
-    it('allows assignment of custom attributes with string values', () => {
-      const el = ReactTestUtils.renderIntoDocument(<div whatever="30" />);
+    it('allows assignment of custom attributes with string values', async () => {
+      const container = document.createElement('div');
+      const root = ReactDOMClient.createRoot(container);
+
+      await act(() => {
+        root.render(<div whatever="30" />);
+      });
+
+      const el = container.firstChild;
 
       expect(el.getAttribute('whatever')).toBe('30');
     });
@@ -2853,10 +3088,15 @@ describe('ReactDOMComponent', () => {
       expect(container.firstChild.hasAttribute('whatever')).toBe(false);
     });
 
-    it('does not assign a boolean custom attributes as a string', function () {
+    it('does not assign a boolean custom attributes as a string', async function () {
       let el;
-      expect(() => {
-        el = ReactTestUtils.renderIntoDocument(<div whatever={true} />);
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(<div whatever={true} ref={current => (el = current)} />);
+        });
       }).toErrorDev(
         'Received `true` for a non-boolean attribute `whatever`.\n\n' +
           'If you want to write it to the DOM, pass a string instead: ' +
@@ -2866,11 +3106,18 @@ describe('ReactDOMComponent', () => {
       expect(el.hasAttribute('whatever')).toBe(false);
     });
 
-    it('does not assign an implicit boolean custom attributes', function () {
+    it('does not assign an implicit boolean custom attributes', async function () {
       let el;
-      expect(() => {
-        // eslint-disable-next-line react/jsx-boolean-value
-        el = ReactTestUtils.renderIntoDocument(<div whatever />);
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(
+            // eslint-disable-next-line react/jsx-boolean-value
+            <div whatever ref={current => (el = current)} />,
+          );
+        });
       }).toErrorDev(
         'Received `true` for a non-boolean attribute `whatever`.\n\n' +
           'If you want to write it to the DOM, pass a string instead: ' +
@@ -2880,23 +3127,44 @@ describe('ReactDOMComponent', () => {
       expect(el.hasAttribute('whatever')).toBe(false);
     });
 
-    it('assigns a numeric custom attributes as a string', function () {
-      const el = ReactTestUtils.renderIntoDocument(<div whatever={3} />);
+    it('assigns a numeric custom attributes as a string', async function () {
+      const container = document.createElement('div');
+      const root = ReactDOMClient.createRoot(container);
+
+      await act(() => {
+        root.render(<div whatever={3} />);
+      });
+
+      const el = container.firstChild;
 
       expect(el.getAttribute('whatever')).toBe('3');
     });
 
-    it('will not assign a function custom attributes', function () {
+    it('will not assign a function custom attributes', async function () {
       let el;
-      expect(() => {
-        el = ReactTestUtils.renderIntoDocument(<div whatever={() => {}} />);
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(
+            <div whatever={() => {}} ref={current => (el = current)} />,
+          );
+        });
       }).toErrorDev('Warning: Invalid value for prop `whatever` on <div> tag');
 
       expect(el.hasAttribute('whatever')).toBe(false);
     });
 
-    it('will assign an object custom attributes', function () {
-      const el = ReactTestUtils.renderIntoDocument(<div whatever={{}} />);
+    it('will assign an object custom attributes', async function () {
+      const container = document.createElement('div');
+      const root = ReactDOMClient.createRoot(container);
+
+      await act(() => {
+        root.render(<div whatever={{}} />);
+      });
+
+      const el = container.firstChild;
       expect(el.getAttribute('whatever')).toBe('[object Object]');
     });
 
@@ -2924,10 +3192,17 @@ describe('ReactDOMComponent', () => {
       expect(container.firstChild.innerHTML).toEqual('2020-01-01');
     });
 
-    it('allows cased data attributes', () => {
+    it('allows cased data attributes', async () => {
       let el;
-      expect(() => {
-        el = ReactTestUtils.renderIntoDocument(<div data-fooBar="true" />);
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(
+            <div data-fooBar="true" ref={current => (el = current)} />,
+          );
+        });
       }).toErrorDev(
         'React does not recognize the `data-fooBar` prop on a DOM element. ' +
           'If you intentionally want it to appear in the DOM as a custom ' +
@@ -2939,10 +3214,15 @@ describe('ReactDOMComponent', () => {
       expect(el.getAttribute('data-foobar')).toBe('true');
     });
 
-    it('allows cased custom attributes', () => {
+    it('allows cased custom attributes', async () => {
       let el;
-      expect(() => {
-        el = ReactTestUtils.renderIntoDocument(<div fooBar="true" />);
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(<div fooBar="true" ref={current => (el = current)} />);
+        });
       }).toErrorDev(
         'React does not recognize the `fooBar` prop on a DOM element. ' +
           'If you intentionally want it to appear in the DOM as a custom ' +
@@ -2954,10 +3234,15 @@ describe('ReactDOMComponent', () => {
       expect(el.getAttribute('foobar')).toBe('true');
     });
 
-    it('warns on NaN attributes', () => {
+    it('warns on NaN attributes', async () => {
       let el;
-      expect(() => {
-        el = ReactTestUtils.renderIntoDocument(<div whatever={NaN} />);
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(<div whatever={NaN} ref={current => (el = current)} />);
+        });
       }).toErrorDev(
         'Warning: Received NaN for the `whatever` attribute. If this is ' +
           'expected, cast the value to a string.\n    in div',
@@ -2981,10 +3266,15 @@ describe('ReactDOMComponent', () => {
       expect(el.hasAttribute('whatever')).toBe(false);
     });
 
-    it('warns on bad casing of known HTML attributes', function () {
+    it('warns on bad casing of known HTML attributes', async function () {
       let el;
-      expect(() => {
-        el = ReactTestUtils.renderIntoDocument(<div SiZe="30" />);
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(<div SiZe="30" ref={current => (el = current)} />);
+        });
       }).toErrorDev(
         'Warning: Invalid DOM property `SiZe`. Did you mean `size`?',
       );
@@ -2994,8 +3284,15 @@ describe('ReactDOMComponent', () => {
   });
 
   describe('Object stringification', function () {
-    it('allows objects on known properties', function () {
-      const el = ReactTestUtils.renderIntoDocument(<div acceptCharset={{}} />);
+    it('allows objects on known properties', async function () {
+      const container = document.createElement('div');
+      const root = ReactDOMClient.createRoot(container);
+
+      await act(() => {
+        root.render(<div acceptCharset={{}} />);
+      });
+
+      const el = container.firstChild;
       expect(el.getAttribute('accept-charset')).toBe('[object Object]');
     });
 
@@ -3050,27 +3347,46 @@ describe('ReactDOMComponent', () => {
       );
     });
 
-    it('allows objects that inherit a custom toString method', function () {
+    it('allows objects that inherit a custom toString method', async function () {
       const parent = {toString: () => 'hello.jpg'};
       const child = Object.create(parent);
-      const el = ReactTestUtils.renderIntoDocument(<img src={child} />);
+      const container = document.createElement('div');
+      const root = ReactDOMClient.createRoot(container);
+
+      await act(() => {
+        root.render(<img src={child} />);
+      });
+
+      const el = container.firstChild;
 
       expect(el.src).toBe('http://localhost/hello.jpg');
     });
 
-    it('assigns ajaxify (an important internal FB attribute)', function () {
+    it('assigns ajaxify (an important internal FB attribute)', async function () {
       const options = {toString: () => 'ajaxy'};
-      const el = ReactTestUtils.renderIntoDocument(<div ajaxify={options} />);
+      const container = document.createElement('div');
+      const root = ReactDOMClient.createRoot(container);
+
+      await act(() => {
+        root.render(<div ajaxify={options} />);
+      });
+
+      const el = container.firstChild;
 
       expect(el.getAttribute('ajaxify')).toBe('ajaxy');
     });
   });
 
   describe('String boolean attributes', function () {
-    it('does not assign string boolean attributes for custom attributes', function () {
+    it('does not assign string boolean attributes for custom attributes', async function () {
       let el;
-      expect(() => {
-        el = ReactTestUtils.renderIntoDocument(<div whatever={true} />);
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(<div whatever={true} ref={current => (el = current)} />);
+        });
       }).toErrorDev(
         'Received `true` for a non-boolean attribute `whatever`.\n\n' +
           'If you want to write it to the DOM, pass a string instead: ' +
@@ -3080,31 +3396,57 @@ describe('ReactDOMComponent', () => {
       expect(el.hasAttribute('whatever')).toBe(false);
     });
 
-    it('stringifies the boolean true for allowed attributes', function () {
-      const el = ReactTestUtils.renderIntoDocument(<div spellCheck={true} />);
+    it('stringifies the boolean true for allowed attributes', async function () {
+      const container = document.createElement('div');
+      const root = ReactDOMClient.createRoot(container);
+
+      await act(() => {
+        root.render(<div spellCheck={true} />);
+      });
+
+      const el = container.firstChild;
 
       expect(el.getAttribute('spellCheck')).toBe('true');
     });
 
-    it('stringifies the boolean false for allowed attributes', function () {
-      const el = ReactTestUtils.renderIntoDocument(<div spellCheck={false} />);
+    it('stringifies the boolean false for allowed attributes', async function () {
+      const container = document.createElement('div');
+      const root = ReactDOMClient.createRoot(container);
+
+      await act(() => {
+        root.render(<div spellCheck={false} />);
+      });
+
+      const el = container.firstChild;
 
       expect(el.getAttribute('spellCheck')).toBe('false');
     });
 
-    it('stringifies implicit booleans for allowed attributes', function () {
-      // eslint-disable-next-line react/jsx-boolean-value
-      const el = ReactTestUtils.renderIntoDocument(<div spellCheck />);
+    it('stringifies implicit booleans for allowed attributes', async function () {
+      const container = document.createElement('div');
+      const root = ReactDOMClient.createRoot(container);
+
+      await act(() => {
+        // eslint-disable-next-line react/jsx-boolean-value
+        root.render(<div spellCheck />);
+      });
+
+      const el = container.firstChild;
 
       expect(el.getAttribute('spellCheck')).toBe('true');
     });
   });
 
   describe('Boolean attributes', function () {
-    it('warns on the ambiguous string value "false"', function () {
+    it('warns on the ambiguous string value "false"', async function () {
       let el;
-      expect(() => {
-        el = ReactTestUtils.renderIntoDocument(<div hidden="false" />);
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(<div hidden="false" ref={current => (el = current)} />);
+        });
       }).toErrorDev(
         'Received the string `false` for the boolean attribute `hidden`. ' +
           'The browser will interpret it as a truthy value. ' +
@@ -3114,10 +3456,15 @@ describe('ReactDOMComponent', () => {
       expect(el.getAttribute('hidden')).toBe('');
     });
 
-    it('warns on the potentially-ambiguous string value "true"', function () {
+    it('warns on the potentially-ambiguous string value "true"', async function () {
       let el;
-      expect(() => {
-        el = ReactTestUtils.renderIntoDocument(<div hidden="true" />);
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(<div hidden="true" ref={current => (el = current)} />);
+        });
       }).toErrorDev(
         'Received the string `true` for the boolean attribute `hidden`. ' +
           'Although this works, it will not work as expected if you pass the string "false". ' +
@@ -3129,14 +3476,19 @@ describe('ReactDOMComponent', () => {
   });
 
   describe('Hyphenated SVG elements', function () {
-    it('the font-face element is not a custom element', function () {
+    it('the font-face element is not a custom element', async function () {
       let el;
-      expect(() => {
-        el = ReactTestUtils.renderIntoDocument(
-          <svg>
-            <font-face x-height={false} />
-          </svg>,
-        );
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(
+            <svg ref={current => (el = current)}>
+              <font-face x-height={false} />
+            </svg>,
+          );
+        });
       }).toErrorDev(
         'Warning: Invalid DOM property `x-height`. Did you mean `xHeight`',
       );
@@ -3146,14 +3498,19 @@ describe('ReactDOMComponent', () => {
       );
     });
 
-    it('the font-face element does not allow unknown boolean values', function () {
+    it('the font-face element does not allow unknown boolean values', async function () {
       let el;
-      expect(() => {
-        el = ReactTestUtils.renderIntoDocument(
-          <svg>
-            <font-face whatever={false} />
-          </svg>,
-        );
+      await expect(async () => {
+        const container = document.createElement('div');
+        const root = ReactDOMClient.createRoot(container);
+
+        await act(() => {
+          root.render(
+            <svg ref={current => (el = current)}>
+              <font-face whatever={false} />
+            </svg>,
+          );
+        });
       }).toErrorDev(
         'Received `false` for a non-boolean attribute `whatever`.\n\n' +
           'If you want to write it to the DOM, pass a string instead: ' +
@@ -3324,7 +3681,7 @@ describe('ReactDOMComponent', () => {
       expect(typeof portalContainer.onclick).toBe('function');
     });
 
-    it('does not add onclick handler to the React root', () => {
+    it('does not add onclick handler to the React root in legacy mode', () => {
       const container = document.createElement('div');
 
       function Component() {