diff --git a/packages/react-dom/src/__tests__/ReactComponent-test.js b/packages/react-dom/src/__tests__/ReactComponent-test.js
index 2d467f82c0d7c..a66f115624e65 100644
--- a/packages/react-dom/src/__tests__/ReactComponent-test.js
+++ b/packages/react-dom/src/__tests__/ReactComponent-test.js
@@ -11,8 +11,9 @@
let React;
let ReactDOM;
+let ReactDOMClient;
let ReactDOMServer;
-let ReactTestUtils;
+let act;
describe('ReactComponent', () => {
beforeEach(() => {
@@ -20,11 +21,12 @@ describe('ReactComponent', () => {
React = require('react');
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;
});
- it('should throw on invalid render targets', () => {
+ it('should throw on invalid render targets in legacy roots', () => {
const container = document.createElement('div');
// jQuery objects are basically arrays; people often pass them in by mistake
expect(function () {
@@ -36,40 +38,52 @@ describe('ReactComponent', () => {
}).toThrowError(/Target container is not a DOM element./);
});
- it('should throw when supplying a string ref outside of render method', () => {
- let instance =
;
- expect(function () {
- instance = ReactTestUtils.renderIntoDocument(instance);
- }).toThrow();
+ it('should throw when supplying a string ref outside of render method', async () => {
+ const container = document.createElement('div');
+ const root = ReactDOMClient.createRoot(container);
+ await expect(
+ act(() => {
+ root.render();
+ }),
+ ).rejects.toThrow();
});
- it('should throw (in dev) when children are mutated during render', () => {
+ it('should throw (in dev) when children are mutated during render', async () => {
function Wrapper(props) {
props.children[1] = ; // Mutation is illegal
return {props.children}
;
}
if (__DEV__) {
- expect(() => {
- ReactTestUtils.renderIntoDocument(
+ const container = document.createElement('div');
+ const root = ReactDOMClient.createRoot(container);
+ await expect(
+ act(() => {
+ root.render(
+
+
+
+
+ ,
+ );
+ }),
+ ).rejects.toThrowError(/Cannot assign to read only property.*/);
+ } else {
+ const container = document.createElement('div');
+ const root = ReactDOMClient.createRoot(container);
+
+ await act(() => {
+ root.render(
,
);
- }).toThrowError(/Cannot assign to read only property.*/);
- } else {
- ReactTestUtils.renderIntoDocument(
-
-
-
-
- ,
- );
+ });
}
});
- it('should throw (in dev) when children are mutated during update', () => {
+ it('should throw (in dev) when children are mutated during update', async () => {
class Wrapper extends React.Component {
componentDidMount() {
this.props.children[1] = ; // Mutation is illegal
@@ -82,27 +96,36 @@ describe('ReactComponent', () => {
}
if (__DEV__) {
- expect(() => {
- ReactTestUtils.renderIntoDocument(
+ const container = document.createElement('div');
+ const root = ReactDOMClient.createRoot(container);
+ await expect(
+ act(() => {
+ root.render(
+
+
+
+
+ ,
+ );
+ }),
+ ).rejects.toThrowError(/Cannot assign to read only property.*/);
+ } else {
+ const container = document.createElement('div');
+ const root = ReactDOMClient.createRoot(container);
+
+ await act(() => {
+ root.render(
,
);
- }).toThrowError(/Cannot assign to read only property.*/);
- } else {
- ReactTestUtils.renderIntoDocument(
-
-
-
-
- ,
- );
+ });
}
});
- it('should support string refs on owned components', () => {
+ it('should support string refs on owned components', async () => {
const innerObj = {};
const outerObj = {};
@@ -133,8 +156,12 @@ describe('ReactComponent', () => {
}
}
- expect(() => {
- ReactTestUtils.renderIntoDocument();
+ const container = document.createElement('div');
+ const root = ReactDOMClient.createRoot(container);
+ await expect(async () => {
+ await act(() => {
+ root.render();
+ });
}).toErrorDev([
'Warning: Component "div" contains the string ref "inner". ' +
'Support for string refs will be removed in a future major release. ' +
@@ -151,7 +178,7 @@ describe('ReactComponent', () => {
]);
});
- it('should not have string refs on unmounted components', () => {
+ it('should not have string refs on unmounted components', async () => {
class Parent extends React.Component {
render() {
return (
@@ -172,10 +199,14 @@ describe('ReactComponent', () => {
}
}
- ReactTestUtils.renderIntoDocument(} />);
+ const container = document.createElement('div');
+ const root = ReactDOMClient.createRoot(container);
+ await act(() => {
+ root.render(} />);
+ });
});
- it('should support callback-style refs', () => {
+ it('should support callback-style refs', async () => {
const innerObj = {};
const outerObj = {};
@@ -211,11 +242,16 @@ describe('ReactComponent', () => {
}
}
- ReactTestUtils.renderIntoDocument();
+ const container = document.createElement('div');
+ const root = ReactDOMClient.createRoot(container);
+ await act(() => {
+ root.render();
+ });
+
expect(mounted).toBe(true);
});
- it('should support object-style refs', () => {
+ it('should support object-style refs', async () => {
const innerObj = {};
const outerObj = {};
@@ -254,11 +290,16 @@ describe('ReactComponent', () => {
}
}
- ReactTestUtils.renderIntoDocument();
+ const container = document.createElement('div');
+ const root = ReactDOMClient.createRoot(container);
+ await act(() => {
+ root.render();
+ });
+
expect(mounted).toBe(true);
});
- it('should support new-style refs with mixed-up owners', () => {
+ it('should support new-style refs with mixed-up owners', async () => {
class Wrapper extends React.Component {
getTitle = () => {
return this.props.title;
@@ -296,11 +337,17 @@ describe('ReactComponent', () => {
}
}
- ReactTestUtils.renderIntoDocument();
+ const container = document.createElement('div');
+ const root = ReactDOMClient.createRoot(container);
+
+ await act(() => {
+ root.render();
+ });
+
expect(mounted).toBe(true);
});
- it('should call refs at the correct time', () => {
+ it('should call refs at the correct time', async () => {
const log = [];
class Inner extends React.Component {
@@ -358,11 +405,18 @@ describe('ReactComponent', () => {
// mount, update, unmount
const el = document.createElement('div');
log.push('start mount');
- ReactDOM.render(, el);
+ const root = ReactDOMClient.createRoot(el);
+ await act(() => {
+ root.render();
+ });
log.push('start update');
- ReactDOM.render(, el);
+ await act(() => {
+ root.render();
+ });
log.push('start unmount');
- ReactDOM.unmountComponentAtNode(el);
+ await act(() => {
+ root.unmount();
+ });
/* eslint-disable indent */
expect(log).toEqual([
@@ -396,7 +450,7 @@ describe('ReactComponent', () => {
/* eslint-enable indent */
});
- it('fires the callback after a component is rendered', () => {
+ it('fires the callback after a component is rendered in legacy roots', () => {
const callback = jest.fn();
const container = document.createElement('div');
ReactDOM.render(, container, callback);
@@ -407,14 +461,20 @@ describe('ReactComponent', () => {
expect(callback).toHaveBeenCalledTimes(3);
});
- it('throws usefully when rendering badly-typed elements', () => {
+ it('throws usefully when rendering badly-typed elements', async () => {
const X = undefined;
- expect(() => {
- expect(() => ReactTestUtils.renderIntoDocument()).toErrorDev(
+ let container = document.createElement('div');
+ let root = ReactDOMClient.createRoot(container);
+ await expect(
+ expect(async () => {
+ await act(() => {
+ root.render();
+ });
+ }).toErrorDev(
'React.createElement: type is invalid -- expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: undefined.',
- );
- }).toThrowError(
+ ),
+ ).rejects.toThrowError(
'Element type is invalid: expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: undefined.' +
(__DEV__
@@ -424,18 +484,24 @@ describe('ReactComponent', () => {
);
const Y = null;
- expect(() => {
- expect(() => ReactTestUtils.renderIntoDocument()).toErrorDev(
+ container = document.createElement('div');
+ root = ReactDOMClient.createRoot(container);
+ await expect(
+ expect(async () => {
+ await act(() => {
+ root.render();
+ });
+ }).toErrorDev(
'React.createElement: type is invalid -- expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: null.',
- );
- }).toThrowError(
+ ),
+ ).rejects.toThrowError(
'Element type is invalid: expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: null.',
);
});
- it('includes owner name in the error about badly-typed elements', () => {
+ it('includes owner name in the error about badly-typed elements', async () => {
const X = undefined;
function Indirection(props) {
@@ -454,12 +520,18 @@ describe('ReactComponent', () => {
return ;
}
- expect(() => {
- expect(() => ReactTestUtils.renderIntoDocument()).toErrorDev(
+ const container = document.createElement('div');
+ const root = ReactDOMClient.createRoot(container);
+ await expect(
+ expect(async () => {
+ await act(() => {
+ root.render();
+ });
+ }).toErrorDev(
'React.createElement: type is invalid -- expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: undefined.',
- );
- }).toThrowError(
+ ),
+ ).rejects.toThrowError(
'Element type is invalid: expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: undefined.' +
(__DEV__
@@ -470,7 +542,7 @@ describe('ReactComponent', () => {
);
});
- it('throws if a plain object is used as a child', () => {
+ it('throws if a plain object is used as a child', async () => {
const children = {
x: ,
y: ,
@@ -478,15 +550,18 @@ describe('ReactComponent', () => {
};
const element = {[children]}
;
const container = document.createElement('div');
- expect(() => {
- ReactDOM.render(element, container);
- }).toThrowError(
+ const root = ReactDOMClient.createRoot(container);
+ await expect(
+ act(() => {
+ root.render(element);
+ }),
+ ).rejects.toThrowError(
'Objects are not valid as a React child (found: object with keys {x, y, z}). ' +
'If you meant to render a collection of children, use an array instead.',
);
});
- it('throws if a plain object even if it is in an owner', () => {
+ it('throws if a plain object even if it is in an owner', async () => {
class Foo extends React.Component {
render() {
const children = {
@@ -498,9 +573,12 @@ describe('ReactComponent', () => {
}
}
const container = document.createElement('div');
- expect(() => {
- ReactDOM.render(, container);
- }).toThrowError(
+ const root = ReactDOMClient.createRoot(container);
+ await expect(
+ act(() => {
+ root.render();
+ }),
+ ).rejects.toThrowError(
'Objects are not valid as a React child (found: object with keys {a, b, c}).' +
' If you meant to render a collection of children, use an array ' +
'instead.',
@@ -545,12 +623,17 @@ describe('ReactComponent', () => {
});
describe('with new features', () => {
- it('warns on function as a return value from a function', () => {
+ it('warns on function as a return value from a function', async () => {
function Foo() {
return Foo;
}
const container = document.createElement('div');
- expect(() => ReactDOM.render(, container)).toErrorDev(
+ const root = ReactDOMClient.createRoot(container);
+ await expect(async () => {
+ await act(() => {
+ root.render();
+ });
+ }).toErrorDev(
'Warning: Functions are not valid as a React child. This may happen if ' +
'you return a Component instead of from render. ' +
'Or maybe you meant to call this function rather than return it.\n' +
@@ -558,14 +641,20 @@ describe('ReactComponent', () => {
);
});
- it('warns on function as a return value from a class', () => {
+ it('warns on function as a return value from a class', async () => {
class Foo extends React.Component {
render() {
return Foo;
}
}
const container = document.createElement('div');
- expect(() => ReactDOM.render(, container)).toErrorDev(
+ await expect(async () => {
+ const root = ReactDOMClient.createRoot(container);
+
+ await act(() => {
+ root.render();
+ });
+ }).toErrorDev(
'Warning: Functions are not valid as a React child. This may happen if ' +
'you return a Component instead of from render. ' +
'Or maybe you meant to call this function rather than return it.\n' +
@@ -573,7 +662,7 @@ describe('ReactComponent', () => {
);
});
- it('warns on function as a child to host component', () => {
+ it('warns on function as a child to host component', async () => {
function Foo() {
return (
@@ -582,7 +671,12 @@ describe('ReactComponent', () => {
);
}
const container = document.createElement('div');
- expect(() => ReactDOM.render(, container)).toErrorDev(
+ const root = ReactDOMClient.createRoot(container);
+ await expect(async () => {
+ await act(() => {
+ root.render();
+ });
+ }).toErrorDev(
'Warning: Functions are not valid as a React child. This may happen if ' +
'you return a Component instead of from render. ' +
'Or maybe you meant to call this function rather than return it.\n' +
@@ -592,7 +686,7 @@ describe('ReactComponent', () => {
);
});
- it('does not warn for function-as-a-child that gets resolved', () => {
+ it('does not warn for function-as-a-child that gets resolved', async () => {
function Bar(props) {
return props.children();
}
@@ -600,11 +694,15 @@ describe('ReactComponent', () => {
return {() => 'Hello'};
}
const container = document.createElement('div');
- ReactDOM.render(, container);
+ const root = ReactDOMClient.createRoot(container);
+ await act(() => {
+ root.render();
+ });
+
expect(container.innerHTML).toBe('Hello');
});
- it('deduplicates function type warnings based on component type', () => {
+ it('deduplicates function type warnings based on component type', async () => {
class Foo extends React.PureComponent {
constructor() {
super();
@@ -624,9 +722,12 @@ describe('ReactComponent', () => {
}
}
const container = document.createElement('div');
+ const root = ReactDOMClient.createRoot(container);
let component;
- expect(() => {
- component = ReactDOM.render(, container);
+ await expect(async () => {
+ await act(() => {
+ root.render( (component = current)} />);
+ });
}).toErrorDev([
'Warning: Functions are not valid as a React child. This may happen if ' +
'you return a Component instead of from render. ' +
@@ -640,7 +741,9 @@ describe('ReactComponent', () => {
' in div (at **)\n' +
' in Foo (at **)',
]);
- component.setState({type: 'portobello mushrooms'});
+ await act(() => {
+ component.setState({type: 'portobello mushrooms'});
+ });
});
});
});