' +
- 'tag. For details, see https://react.dev/link/invalid-aria-props',
- );
+ 'tag. For details, see https://react.dev/link/invalid-aria-props\n' +
+ ' in div (at **)',
+ ]);
});
it('should warn for an improperly cased aria-* prop', async () => {
// The valid attribute name is aria-haspopup.
- await expect(() => mountComponent({'aria-hasPopup': 'true'})).toErrorDev(
+ await mountComponent({'aria-hasPopup': 'true'});
+ assertConsoleErrorDev([
'Unknown ARIA attribute `aria-hasPopup`. ' +
- 'Did you mean `aria-haspopup`?',
- );
+ 'Did you mean `aria-haspopup`?\n' +
+ ' in div (at **)',
+ ]);
});
it('should warn for use of recognized camel case aria attributes', async () => {
// The valid attribute name is aria-haspopup.
- await expect(() => mountComponent({ariaHasPopup: 'true'})).toErrorDev(
+ await mountComponent({ariaHasPopup: 'true'});
+ assertConsoleErrorDev([
'Invalid ARIA attribute `ariaHasPopup`. ' +
- 'Did you mean `aria-haspopup`?',
- );
+ 'Did you mean `aria-haspopup`?\n' +
+ ' in div (at **)',
+ ]);
});
it('should warn for use of unrecognized camel case aria attributes', async () => {
// The valid attribute name is aria-haspopup.
- await expect(() =>
- mountComponent({ariaSomethingInvalid: 'true'}),
- ).toErrorDev(
+ await mountComponent({ariaSomethingInvalid: 'true'});
+ assertConsoleErrorDev([
'Invalid ARIA attribute `ariaSomethingInvalid`. ARIA ' +
- 'attributes follow the pattern aria-* and must be lowercase.',
- );
+ 'attributes follow the pattern aria-* and must be lowercase.\n' +
+ ' in div (at **)',
+ ]);
});
});
});
diff --git a/packages/react-dom/src/__tests__/ReactDOMLegacyComponentTree-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMLegacyComponentTree-test.internal.js
index 8465e68703961..e1ba0415767d4 100644
--- a/packages/react-dom/src/__tests__/ReactDOMLegacyComponentTree-test.internal.js
+++ b/packages/react-dom/src/__tests__/ReactDOMLegacyComponentTree-test.internal.js
@@ -13,10 +13,13 @@ describe('ReactDOMComponentTree', () => {
let React;
let ReactDOM;
let container;
+ let assertConsoleErrorDev;
beforeEach(() => {
React = require('react');
ReactDOM = require('react-dom');
+ assertConsoleErrorDev =
+ require('internal-test-utils').assertConsoleErrorDev;
container = document.createElement('div');
document.body.appendChild(container);
@@ -31,11 +34,14 @@ describe('ReactDOMComponentTree', () => {
it('finds instance of node that is attempted to be unmounted', () => {
const component = ;
const node = ReactDOM.render(
{component}
, container);
- expect(() => ReactDOM.unmountComponentAtNode(node)).toErrorDev(
- "unmountComponentAtNode(): The node you're attempting to unmount " +
- 'was rendered by React and is not a top-level container. You may ' +
- 'have accidentally passed in a React root node instead of its ' +
- 'container.',
+ ReactDOM.unmountComponentAtNode(node);
+ assertConsoleErrorDev(
+ [
+ "unmountComponentAtNode(): The node you're attempting to unmount " +
+ 'was rendered by React and is not a top-level container. You may ' +
+ 'have accidentally passed in a React root node instead of its ' +
+ 'container.',
+ ],
{withoutStack: true},
);
});
@@ -49,11 +55,14 @@ describe('ReactDOMComponentTree', () => {
);
const anotherComponent = ;
const instance = ReactDOM.render(component, container);
- expect(() => ReactDOM.render(anotherComponent, instance)).toErrorDev(
- 'Replacing React-rendered children with a new root ' +
- 'component. If you intended to update the children of this node, ' +
- 'you should instead have the existing children update their state ' +
- 'and render the new components instead of calling ReactDOM.render.',
+ ReactDOM.render(anotherComponent, instance);
+ assertConsoleErrorDev(
+ [
+ 'Replacing React-rendered children with a new root ' +
+ 'component. If you intended to update the children of this node, ' +
+ 'you should instead have the existing children update their state ' +
+ 'and render the new components instead of calling ReactDOM.render.',
+ ],
{withoutStack: true},
);
});
diff --git a/packages/react-dom/src/__tests__/ReactDOMLegacyFiber-test.js b/packages/react-dom/src/__tests__/ReactDOMLegacyFiber-test.js
index c799e3b904b86..9c1e7f030e1fd 100644
--- a/packages/react-dom/src/__tests__/ReactDOMLegacyFiber-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMLegacyFiber-test.js
@@ -13,11 +13,14 @@ const React = require('react');
const ReactDOM = require('react-dom');
const PropTypes = require('prop-types');
let act;
+let assertConsoleErrorDev;
describe('ReactDOMLegacyFiber', () => {
let container;
beforeEach(() => {
act = require('internal-test-utils').act;
+ assertConsoleErrorDev =
+ require('internal-test-utils').assertConsoleErrorDev;
container = document.createElement('div');
document.body.appendChild(container);
});
@@ -786,9 +789,8 @@ describe('ReactDOMLegacyFiber', () => {
}
}
- expect(() => {
- ReactDOM.render(, container);
- }).toErrorDev([
+ ReactDOM.render(, container);
+ assertConsoleErrorDev([
'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
]);
@@ -834,10 +836,8 @@ describe('ReactDOMLegacyFiber', () => {
}
}
- let instance;
- expect(() => {
- instance = ReactDOM.render(, container);
- }).toErrorDev([
+ const instance = ReactDOM.render(, container);
+ assertConsoleErrorDev([
'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
]);
@@ -882,9 +882,8 @@ describe('ReactDOMLegacyFiber', () => {
}
}
- expect(() => {
- ReactDOM.render(, container);
- }).toErrorDev([
+ ReactDOM.render(, container);
+ assertConsoleErrorDev([
'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
]);
@@ -1117,11 +1116,12 @@ describe('ReactDOMLegacyFiber', () => {
return ;
}
}
- expect(() => ReactDOM.render(, container)).toErrorDev(
+ ReactDOM.render(, container);
+ assertConsoleErrorDev([
'Expected `onClick` listener to be a function, instead got a value of `string` type.\n' +
' in div (at **)\n' +
' in Example (at **)',
- );
+ ]);
});
// @gate !disableLegacyMode
@@ -1131,13 +1131,14 @@ describe('ReactDOMLegacyFiber', () => {
return ;
}
}
- expect(() => ReactDOM.render(, container)).toErrorDev(
+ ReactDOM.render(, container);
+ assertConsoleErrorDev([
'Expected `onClick` listener to be a function, instead got `false`.\n\n' +
'If you used to conditionally omit it with onClick={condition && value}, ' +
'pass onClick={condition ? value : undefined} instead.\n' +
' in div (at **)\n' +
' in Example (at **)',
- );
+ ]);
});
// @gate !disableLegacyMode
@@ -1270,17 +1271,18 @@ describe('ReactDOMLegacyFiber', () => {
container.innerHTML = '
, container);
- });
- }).rejects.toThrow('The node to be removed is not a child of this node.');
- }).toErrorDev(
- '' +
- 'It looks like the React-rendered content of this container was ' +
- 'removed without using React. This is not supported and will ' +
- 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
- 'to empty a container.',
+ await act(() => {
+ ReactDOM.render(
baz
, container);
+ });
+ }).rejects.toThrow('The node to be removed is not a child of this node.');
+ assertConsoleErrorDev(
+ [
+ '' +
+ 'It looks like the React-rendered content of this container was ' +
+ 'removed without using React. This is not supported and will ' +
+ 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
+ 'to empty a container.',
+ ],
{withoutStack: true},
);
});
@@ -1293,12 +1295,15 @@ describe('ReactDOMLegacyFiber', () => {
expect(container.innerHTML).toBe('
bar
');
// then we mess with the DOM before an update
container.innerHTML = '
MEOW.
';
- expect(() => ReactDOM.render(
baz
, container)).toErrorDev(
- '' +
- 'It looks like the React-rendered content of this container was ' +
- 'removed without using React. This is not supported and will ' +
- 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
- 'to empty a container.',
+ ReactDOM.render(
baz
, container);
+ assertConsoleErrorDev(
+ [
+ '' +
+ 'It looks like the React-rendered content of this container was ' +
+ 'removed without using React. This is not supported and will ' +
+ 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
+ 'to empty a container.',
+ ],
{withoutStack: true},
);
});
@@ -1311,12 +1316,15 @@ describe('ReactDOMLegacyFiber', () => {
expect(container.innerHTML).toBe('
bar
');
// then we mess with the DOM before an update
container.innerHTML = '';
- expect(() => ReactDOM.render(
baz
, container)).toErrorDev(
- '' +
- 'It looks like the React-rendered content of this container was ' +
- 'removed without using React. This is not supported and will ' +
- 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
- 'to empty a container.',
+ ReactDOM.render(
baz
, container);
+ assertConsoleErrorDev(
+ [
+ '' +
+ 'It looks like the React-rendered content of this container was ' +
+ 'removed without using React. This is not supported and will ' +
+ 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
+ 'to empty a container.',
+ ],
{withoutStack: true},
);
});
diff --git a/packages/react-dom/src/__tests__/ReactDOMOption-test.js b/packages/react-dom/src/__tests__/ReactDOMOption-test.js
index ce5e3c65bcfdb..cb4270cd1ea20 100644
--- a/packages/react-dom/src/__tests__/ReactDOMOption-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMOption-test.js
@@ -14,6 +14,7 @@ describe('ReactDOMOption', () => {
let ReactDOMClient;
let ReactDOMServer;
let act;
+ let assertConsoleErrorDev;
beforeEach(() => {
jest.resetModules();
@@ -21,6 +22,8 @@ describe('ReactDOMOption', () => {
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server');
act = require('internal-test-utils').act;
+ assertConsoleErrorDev =
+ require('internal-test-utils').assertConsoleErrorDev;
});
async function renderIntoDocument(children) {
@@ -47,10 +50,8 @@ describe('ReactDOMOption', () => {
{1} {2}
);
- let container;
- await expect(async () => {
- container = await renderIntoDocument(el);
- }).toErrorDev(
+ const container = await renderIntoDocument(el);
+ assertConsoleErrorDev([
'In HTML,
cannot be a child of
);
- let container;
- await expect(async () => {
- container = await renderIntoDocument(el);
- }).toErrorDev(
+ const container = await renderIntoDocument(el);
+ assertConsoleErrorDev([
'Cannot infer the option value of complex children. ' +
- 'Pass a `value` prop or use a plain string as children to ;
- let container;
- await expect(async () => {
- container = await renderIntoDocument(stub);
- }).toErrorDev(
+ const container = await renderIntoDocument(stub);
+ assertConsoleErrorDev([
'Pass a `value` prop if you set dangerouslyInnerHTML so React knows which value should be selected.\n' +
' in option (at **)',
- );
+ ]);
expect(container.firstChild.innerHTML).toBe('foobar');
});
@@ -267,13 +265,12 @@ describe('ReactDOMOption', () => {
expect(option.textContent).toBe('BarFooBaz');
expect(option.selected).toBe(true);
- await expect(async () => {
- await act(async () => {
- ReactDOMClient.hydrateRoot(container, children, {
- onRecoverableError: () => {},
- });
+ await act(async () => {
+ ReactDOMClient.hydrateRoot(container, children, {
+ onRecoverableError: () => {},
});
- }).toErrorDev(
+ });
+ assertConsoleErrorDev([
'In HTML,
cannot be a child of
,
);
- await expect(async () => await waitForAll([])).toErrorDev(
- "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties.",
+ await waitForAll([]);
+ assertConsoleErrorDev(
+ [
+ "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. " +
+ "This won't be patched up. This can happen if a SSR-ed Client Component used:\n" +
+ '\n' +
+ "- A server/client branch `if (typeof window !== 'undefined')`.\n" +
+ "- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" +
+ "- Date formatting in a user's locale which doesn't match the server.\n" +
+ '- External changing data without sending a snapshot of it along with the HTML.\n' +
+ '- Invalid HTML tag nesting.\n' +
+ '\n' +
+ 'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n' +
+ '\n' +
+ 'https://react.dev/link/hydration-mismatch\n' +
+ '\n' +
+ '
\n' +
+ ' \n',
+ ],
{withoutStack: true},
);
});
@@ -183,12 +217,13 @@ describe('ReactDOMRoot', () => {
it('warns when creating two roots managing the same container', () => {
ReactDOMClient.createRoot(container);
- expect(() => {
- ReactDOMClient.createRoot(container);
- }).toErrorDev(
- 'You are calling ReactDOMClient.createRoot() on a container that ' +
- 'has already been passed to createRoot() before. Instead, call ' +
- 'root.render() on the existing root instead if you want to update it.',
+ ReactDOMClient.createRoot(container);
+ assertConsoleErrorDev(
+ [
+ 'You are calling ReactDOMClient.createRoot() on a container that ' +
+ 'has already been passed to createRoot() before. Instead, call ' +
+ 'root.render() on the existing root instead if you want to update it.',
+ ],
{withoutStack: true},
);
});
@@ -352,14 +387,15 @@ describe('ReactDOMRoot', () => {
});
expect(container1.textContent).toEqual('Hi');
- expect(() => {
- ReactDOM.flushSync(() => {
- root1.render();
- });
- }).toErrorDev(
- 'Attempted to synchronously unmount a root while React was ' +
- 'already rendering.',
- );
+ ReactDOM.flushSync(() => {
+ root1.render();
+ });
+ assertConsoleErrorDev([
+ 'Attempted to synchronously unmount a root while React was already rendering. ' +
+ 'React cannot finish unmounting the root until the current render has completed, ' +
+ 'which may lead to a race condition.\n' +
+ ' in App (at **)',
+ ]);
});
// @gate disableCommentsAsDOMContainers
@@ -378,8 +414,12 @@ describe('ReactDOMRoot', () => {
});
it('warn if no children passed to hydrateRoot', async () => {
- expect(() => ReactDOMClient.hydrateRoot(container)).toErrorDev(
- 'Must provide initial children as second argument to hydrateRoot.',
+ ReactDOMClient.hydrateRoot(container);
+ assertConsoleErrorDev(
+ [
+ 'Must provide initial children as second argument to hydrateRoot. ' +
+ 'Example usage: hydrateRoot(domContainer, )',
+ ],
{withoutStack: true},
);
});
@@ -389,9 +429,15 @@ describe('ReactDOMRoot', () => {
return 'Child';
}
- expect(() => ReactDOMClient.createRoot(container, )).toErrorDev(
- 'You passed a JSX element to createRoot. You probably meant to call ' +
- 'root.render instead',
+ ReactDOMClient.createRoot(container, );
+ assertConsoleErrorDev(
+ [
+ 'You passed a JSX element to createRoot. You probably meant to call root.render instead. ' +
+ 'Example usage:\n' +
+ '\n' +
+ ' let root = createRoot(domContainer);\n' +
+ ' root.render();',
+ ],
{
withoutStack: true,
},
@@ -405,15 +451,16 @@ describe('ReactDOMRoot', () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
- expect(() => {
- ReactDOM.flushSync(() => {
- root.render(Component);
- });
- }).toErrorDev(
- 'Functions are not valid as a React child. ' +
- 'This may happen if you return Component instead of from render. ' +
- 'Or maybe you meant to call this function rather than return it.\n' +
- ' root.render(Component)',
+ ReactDOM.flushSync(() => {
+ root.render(Component);
+ });
+ assertConsoleErrorDev(
+ [
+ 'Functions are not valid as a React child. ' +
+ 'This may happen if you return Component instead of from render. ' +
+ 'Or maybe you meant to call this function rather than return it.\n' +
+ ' root.render(Component)',
+ ],
{withoutStack: true},
);
});
@@ -421,13 +468,14 @@ describe('ReactDOMRoot', () => {
it('warns when given a symbol', () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
- expect(() => {
- ReactDOM.flushSync(() => {
- root.render(Symbol('foo'));
- });
- }).toErrorDev(
- 'Symbols are not valid as a React child.\n' +
- ' root.render(Symbol(foo))',
+ ReactDOM.flushSync(() => {
+ root.render(Symbol('foo'));
+ });
+ assertConsoleErrorDev(
+ [
+ 'Symbols are not valid as a React child.\n' +
+ ' root.render(Symbol(foo))',
+ ],
{withoutStack: true},
);
});
diff --git a/packages/react-dom/src/__tests__/ReactDOMSelect-test.js b/packages/react-dom/src/__tests__/ReactDOMSelect-test.js
index e450cf9a8166e..d36700ff124cc 100644
--- a/packages/react-dom/src/__tests__/ReactDOMSelect-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMSelect-test.js
@@ -22,6 +22,7 @@ describe('ReactDOMSelect', () => {
let ReactDOMClient;
let ReactDOMServer;
let act;
+ let assertConsoleErrorDev;
const noop = function () {};
@@ -32,6 +33,8 @@ describe('ReactDOMSelect', () => {
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server');
act = require('internal-test-utils').act;
+ assertConsoleErrorDev =
+ require('internal-test-utils').assertConsoleErrorDev;
});
it('should allow setting `defaultValue`', async () => {
@@ -749,19 +752,19 @@ describe('ReactDOMSelect', () => {
it('should warn if value is null', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
- await expect(async () => {
- await act(() => {
- root.render(
- ,
- );
- });
- }).toErrorDev(
+ await act(() => {
+ root.render(
+ ,
+ );
+ });
+ assertConsoleErrorDev([
'`value` prop on `select` should not be null. ' +
'Consider using an empty string to clear the component or `undefined` ' +
- 'for uncontrolled components.',
- );
+ 'for uncontrolled components.\n' +
+ ' in select (at **)',
+ ]);
await act(() => {
root.render(
@@ -784,14 +787,16 @@ describe('ReactDOMSelect', () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
- await expect(async () => {
- await act(() => {
- root.render();
- });
- }).toErrorDev(
+ await act(() => {
+ root.render();
+ });
+ assertConsoleErrorDev([
'Use the `defaultValue` or `value` props on