Skip to content

Commit

Permalink
convert ReactElementValidator-test to createRoot (#28223)
Browse files Browse the repository at this point in the history
## Summary

Converts `ReactTestUtils.renderIntoDocument` and `ReactDOM.findDOMNode`
to `ReactDOMClient.createRoot`

## How did you test this change?
`yarn test ReactElementValidator`
  • Loading branch information
noahlemen authored and gaearon committed Feb 3, 2024
1 parent b3509d4 commit 12e85b6
Showing 1 changed file with 89 additions and 63 deletions.
152 changes: 89 additions & 63 deletions packages/react/src/__tests__/ReactElementValidator-test.internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

let PropTypes;
let React;
let ReactDOM;
let ReactTestUtils;
let ReactDOMClient;
let act;

let ReactFeatureFlags = require('shared/ReactFeatureFlags');

Expand All @@ -29,8 +29,8 @@ describe('ReactElementValidator', () => {
ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
React = require('react');
ReactDOM = require('react-dom');
ReactTestUtils = require('react-dom/test-utils');
ReactDOMClient = require('react-dom/client');
act = require('internal-test-utils').act;
ComponentClass = class extends React.Component {
render() {
return React.createElement('div');
Expand All @@ -47,7 +47,7 @@ describe('ReactElementValidator', () => {
}).toErrorDev('Each child in a list should have a unique "key" prop.');
});

it('warns for keys for arrays of elements with owner info', () => {
it('warns for keys for arrays of elements with owner info', async () => {
class InnerClass extends React.Component {
render() {
return React.createElement(ComponentClass, null, this.props.childSet);
Expand All @@ -65,37 +65,41 @@ describe('ReactElementValidator', () => {
}
}

expect(() => {
ReactTestUtils.renderIntoDocument(React.createElement(ComponentWrapper));
await expect(async () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() => root.render(React.createElement(ComponentWrapper)));
}).toErrorDev(
'Each child in a list should have a unique "key" prop.' +
'\n\nCheck the render method of `InnerClass`. ' +
'It was passed a child from ComponentWrapper. ',
);
});

it('warns for keys for arrays with no owner or parent info', () => {
it('warns for keys for arrays with no owner or parent info', async () => {
function Anonymous() {
return <div />;
}
Object.defineProperty(Anonymous, 'name', {value: undefined});

const divs = [<div />, <div />];

expect(() => {
ReactTestUtils.renderIntoDocument(<Anonymous>{divs}</Anonymous>);
await expect(async () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() => root.render(<Anonymous>{divs}</Anonymous>));
}).toErrorDev(
'Warning: Each child in a list should have a unique ' +
'"key" prop. See https://reactjs.org/link/warning-keys for more information.\n' +
' in div (at **)',
);
});

it('warns for keys for arrays of elements with no owner info', () => {
it('warns for keys for arrays of elements with no owner info', async () => {
const divs = [<div />, <div />];

expect(() => {
ReactTestUtils.renderIntoDocument(<div>{divs}</div>);
await expect(async () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));

await act(() => root.render(<div>{divs}</div>));
}).toErrorDev(
'Warning: Each child in a list should have a unique ' +
'"key" prop.\n\nCheck the top-level render call using <div>. See ' +
Expand All @@ -104,7 +108,7 @@ describe('ReactElementValidator', () => {
);
});

it('warns for keys with component stack info', () => {
it('warns for keys with component stack info', async () => {
function Component() {
return <div>{[<div />, <div />]}</div>;
}
Expand All @@ -117,7 +121,10 @@ describe('ReactElementValidator', () => {
return <Parent child={<Component />} />;
}

expect(() => ReactTestUtils.renderIntoDocument(<GrandParent />)).toErrorDev(
await expect(async () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() => root.render(<GrandParent />));
}).toErrorDev(
'Warning: Each child in a list should have a unique ' +
'"key" prop.\n\nCheck the render method of `Component`. See ' +
'https://reactjs.org/link/warning-keys for more information.\n' +
Expand All @@ -128,7 +135,7 @@ describe('ReactElementValidator', () => {
);
});

it('does not warn for keys when passing children down', () => {
it('does not warn for keys when passing children down', async () => {
function Wrapper(props) {
return (
<div>
Expand All @@ -138,11 +145,14 @@ describe('ReactElementValidator', () => {
);
}

ReactTestUtils.renderIntoDocument(
<Wrapper>
<span />
<span />
</Wrapper>,
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() =>
root.render(
<Wrapper>
<span />
<span />
</Wrapper>,
),
);
});

Expand Down Expand Up @@ -208,7 +218,7 @@ describe('ReactElementValidator', () => {
React.createElement(ComponentClass, null, [{}, {}]);
});

it('should give context for PropType errors in nested components.', () => {
it('should give context for PropType errors in nested components.', async () => {
// In this test, we're making sure that if a proptype error is found in a
// component, we give a small hint as to which parent instantiated that
// component as per warnings about key usage in ReactElementValidator.
Expand All @@ -221,8 +231,9 @@ describe('ReactElementValidator', () => {
function ParentComp() {
return React.createElement(MyComp, {color: 123});
}
expect(() => {
ReactTestUtils.renderIntoDocument(React.createElement(ParentComp));
await expect(async () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() => root.render(React.createElement(ParentComp)));
}).toErrorDev(
'Warning: Failed prop type: ' +
'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
Expand Down Expand Up @@ -288,28 +299,33 @@ describe('ReactElementValidator', () => {
React.createElement('div');
});

it('includes the owner name when passing null, undefined, boolean, or number', () => {
it('includes the owner name when passing null, undefined, boolean, or number', async () => {
function ParentComp() {
return React.createElement(null);
}

expect(() => {
expect(() => {
ReactTestUtils.renderIntoDocument(React.createElement(ParentComp));
}).toThrowError(
await expect(async () => {
await expect(async () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() => root.render(React.createElement(ParentComp)));
}).rejects.toThrowError(
'Element type is invalid: expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: null.' +
(__DEV__ ? '\n\nCheck the render method of `ParentComp`.' : ''),
);
}).toErrorDev(
}).toErrorDev([
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: null.' +
'\n\nCheck the render method of `ParentComp`.\n in ParentComp',
);
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: null.' +
'\n\nCheck the render method of `ParentComp`.\n in ParentComp',
]);
});

it('should check default prop values', () => {
it('should check default prop values', async () => {
class Component extends React.Component {
static propTypes = {prop: PropTypes.string.isRequired};
static defaultProps = {prop: null};
Expand All @@ -318,16 +334,17 @@ describe('ReactElementValidator', () => {
}
}

expect(() =>
ReactTestUtils.renderIntoDocument(React.createElement(Component)),
).toErrorDev(
await expect(async () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() => root.render(React.createElement(Component)));
}).toErrorDev(
'Warning: Failed prop type: The prop `prop` is marked as required in ' +
'`Component`, but its value is `null`.\n' +
' in Component',
);
});

it('should not check the default for explicit null', () => {
it('should not check the default for explicit null', async () => {
class Component extends React.Component {
static propTypes = {prop: PropTypes.string.isRequired};
static defaultProps = {prop: 'text'};
Expand All @@ -336,9 +353,10 @@ describe('ReactElementValidator', () => {
}
}

expect(() => {
ReactTestUtils.renderIntoDocument(
React.createElement(Component, {prop: null}),
await expect(async () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() =>
root.render(React.createElement(Component, {prop: null})),
);
}).toErrorDev(
'Warning: Failed prop type: The prop `prop` is marked as required in ' +
Expand All @@ -347,7 +365,7 @@ describe('ReactElementValidator', () => {
);
});

it('should check declared prop types', () => {
it('should check declared prop types', async () => {
class Component extends React.Component {
static propTypes = {
prop: PropTypes.string.isRequired,
Expand All @@ -357,11 +375,10 @@ describe('ReactElementValidator', () => {
}
}

expect(() => {
ReactTestUtils.renderIntoDocument(React.createElement(Component));
ReactTestUtils.renderIntoDocument(
React.createElement(Component, {prop: 42}),
);
const root = ReactDOMClient.createRoot(document.createElement('div'));
await expect(async () => {
await act(() => root.render(React.createElement(Component)));
await act(() => root.render(React.createElement(Component, {prop: 42})));
}).toErrorDev([
'Warning: Failed prop type: ' +
'The prop `prop` is marked as required in `Component`, but its value ' +
Expand All @@ -374,12 +391,12 @@ describe('ReactElementValidator', () => {
]);

// Should not error for strings
ReactTestUtils.renderIntoDocument(
React.createElement(Component, {prop: 'string'}),
await act(() =>
root.render(React.createElement(Component, {prop: 'string'})),
);
});

it('should warn if a PropType creator is used as a PropType', () => {
it('should warn if a PropType creator is used as a PropType', async () => {
class Component extends React.Component {
static propTypes = {
myProp: PropTypes.shape,
Expand All @@ -389,9 +406,10 @@ describe('ReactElementValidator', () => {
}
}

expect(() => {
ReactTestUtils.renderIntoDocument(
React.createElement(Component, {myProp: {value: 'hi'}}),
await expect(async () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() =>
root.render(React.createElement(Component, {myProp: {value: 'hi'}})),
);
}).toErrorDev(
'Warning: Component: type specification of prop `myProp` is invalid; ' +
Expand All @@ -402,7 +420,7 @@ describe('ReactElementValidator', () => {
);
});

it('should warn if component declares PropTypes instead of propTypes', () => {
it('should warn if component declares PropTypes instead of propTypes', async () => {
class MisspelledPropTypesComponent extends React.Component {
static PropTypes = {
prop: PropTypes.string,
Expand All @@ -412,9 +430,12 @@ describe('ReactElementValidator', () => {
}
}

expect(() => {
ReactTestUtils.renderIntoDocument(
React.createElement(MisspelledPropTypesComponent, {prop: 'Hi'}),
await expect(async () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() =>
root.render(
React.createElement(MisspelledPropTypesComponent, {prop: 'Hi'}),
),
);
}).toErrorDev(
'Warning: Component MisspelledPropTypesComponent declared `PropTypes` ' +
Expand All @@ -423,15 +444,16 @@ describe('ReactElementValidator', () => {
);
});

it('warns for fragments with illegal attributes', () => {
it('warns for fragments with illegal attributes', async () => {
class Foo extends React.Component {
render() {
return React.createElement(React.Fragment, {a: 1}, '123');
}
}

expect(() => {
ReactTestUtils.renderIntoDocument(React.createElement(Foo));
await expect(async () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() => root.render(React.createElement(Foo)));
}).toErrorDev(
'Invalid prop `a` supplied to `React.Fragment`. React.Fragment ' +
'can only have `key` and `children` props.',
Expand Down Expand Up @@ -466,19 +488,23 @@ describe('ReactElementValidator', () => {
});
}

it('does not warn when using DOM node as children', () => {
it('does not warn when using DOM node as children', async () => {
class DOMContainer extends React.Component {
ref;
render() {
return <div />;
return <div ref={n => (this.ref = n)} />;
}
componentDidMount() {
ReactDOM.findDOMNode(this).appendChild(this.props.children);
this.ref.appendChild(this.props.children);
}
}

const node = document.createElement('div');
// This shouldn't cause a stack overflow or any other problems (#3883)
ReactTestUtils.renderIntoDocument(<DOMContainer>{node}</DOMContainer>);
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() => {
// This shouldn't cause a stack overflow or any other problems (#3883)
root.render(<DOMContainer>{node}</DOMContainer>);
});
});

it('should not enumerate enumerable numbers (#4776)', () => {
Expand Down

0 comments on commit 12e85b6

Please sign in to comment.