, 'foo'], ['', 'foo', , 'foo'],
+ ]);
+ }).toWarnDev([
+ 'Warning: Each child in an array or iterator should have a unique "key" prop.',
+ 'Warning: Each child in an array or iterator should have a unique "key" prop.',
]);
-
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(2);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'Warning: Each child in an array or iterator should have a unique "key" prop.',
- );
- expect(console.error.calls.argsFor(1)[0]).toContain(
- 'Warning: Each child in an array or iterator should have a unique "key" prop.',
- );
- }
});
it('should throw if rendering both HTML and children', () => {
diff --git a/packages/react-dom/src/__tests__/ReactRenderDocument-test.js b/packages/react-dom/src/__tests__/ReactRenderDocument-test.js
index 3ff4c0d7b2..c9b8cb89f3 100644
--- a/packages/react-dom/src/__tests__/ReactRenderDocument-test.js
+++ b/packages/react-dom/src/__tests__/ReactRenderDocument-test.js
@@ -34,19 +34,15 @@ describe('rendering React components at document', () => {
});
describe('with old implicit hydration API', () => {
- function expectDeprecationWarningWithFiber() {
- if (__DEV__) {
- expect(console.warn.calls.count()).toBe(1);
- expect(console.warn.calls.argsFor(0)[0]).toContain(
- 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
- 'will stop working in React v17. Replace the ReactDOM.render() call ' +
- 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
- );
- }
+ function expectDeprecationWarningWithFiber(callback) {
+ expect(callback).toLowPriorityWarnDev(
+ 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
+ 'will stop working in React v17. Replace the ReactDOM.render() call ' +
+ 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
+ );
}
it('should be able to adopt server markup', () => {
- spyOnDev(console, 'warn');
class Root extends React.Component {
render() {
return (
@@ -64,18 +60,18 @@ describe('rendering React components at document', () => {
const testDocument = getTestDocument(markup);
const body = testDocument.body;
- ReactDOM.render(, testDocument);
+ expectDeprecationWarningWithFiber(() =>
+ ReactDOM.render(, testDocument),
+ );
expect(testDocument.body.innerHTML).toBe('Hello world');
ReactDOM.render(, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello moon');
expect(body === testDocument.body).toBe(true);
- expectDeprecationWarningWithFiber();
});
it('should not be able to unmount component from document node', () => {
- spyOnDev(console, 'warn');
class Root extends React.Component {
render() {
return (
@@ -91,18 +87,17 @@ describe('rendering React components at document', () => {
const markup = ReactDOMServer.renderToString();
const testDocument = getTestDocument(markup);
- ReactDOM.render(, testDocument);
+ expectDeprecationWarningWithFiber(() =>
+ ReactDOM.render(, testDocument),
+ );
expect(testDocument.body.innerHTML).toBe('Hello world');
// In Fiber this actually works. It might not be a good idea though.
ReactDOM.unmountComponentAtNode(testDocument);
expect(testDocument.firstChild).toBe(null);
-
- expectDeprecationWarningWithFiber();
});
it('should not be able to switch root constructors', () => {
- spyOnDev(console, 'warn');
class Component extends React.Component {
render() {
return (
@@ -132,18 +127,18 @@ describe('rendering React components at document', () => {
const markup = ReactDOMServer.renderToString();
const testDocument = getTestDocument(markup);
- ReactDOM.render(, testDocument);
+ expectDeprecationWarningWithFiber(() =>
+ ReactDOM.render(, testDocument),
+ );
expect(testDocument.body.innerHTML).toBe('Hello world');
// This works but is probably a bad idea.
ReactDOM.render(, testDocument);
expect(testDocument.body.innerHTML).toBe('Goodbye world');
- expectDeprecationWarningWithFiber();
});
it('should be able to mount into document', () => {
- spyOnDev(console, 'warn');
class Component extends React.Component {
render() {
return (
@@ -162,10 +157,11 @@ describe('rendering React components at document', () => {
);
const testDocument = getTestDocument(markup);
- ReactDOM.render(, testDocument);
+ expectDeprecationWarningWithFiber(() =>
+ ReactDOM.render(, testDocument),
+ );
expect(testDocument.body.innerHTML).toBe('Hello world');
- expectDeprecationWarningWithFiber();
});
it('renders over an existing text child without throwing', () => {
@@ -197,22 +193,15 @@ describe('rendering React components at document', () => {
);
const testDocument = getTestDocument(markup);
- spyOnDev(console, 'warn');
- spyOnDev(console, 'error');
- ReactDOM.render(, testDocument);
- expect(testDocument.body.innerHTML).toBe('Hello world');
- if (__DEV__) {
- expect(console.warn.calls.count()).toBe(1);
- expect(console.warn.calls.argsFor(0)[0]).toContain(
+ expect(() => {
+ expect(() =>
+ ReactDOM.render(, testDocument),
+ ).toLowPriorityWarnDev(
'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
'will stop working in React v17. Replace the ReactDOM.render() call ' +
'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
);
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'Warning: Text content did not match.',
- );
- }
+ }).toWarnDev('Warning: Text content did not match.');
});
it('should throw on full document render w/ no markup', () => {
@@ -239,7 +228,6 @@ describe('rendering React components at document', () => {
});
it('supports findDOMNode on full-page components', () => {
- spyOnDev(console, 'warn');
const tree = (
@@ -251,10 +239,12 @@ describe('rendering React components at document', () => {
const markup = ReactDOMServer.renderToString(tree);
const testDocument = getTestDocument(markup);
- const component = ReactDOM.render(tree, testDocument);
+ let component;
+ expectDeprecationWarningWithFiber(() => {
+ component = ReactDOM.render(tree, testDocument);
+ });
expect(testDocument.body.innerHTML).toBe('Hello world');
expect(ReactDOM.findDOMNode(component).tagName).toBe('HTML');
- expectDeprecationWarningWithFiber();
});
});
@@ -375,17 +365,12 @@ describe('rendering React components at document', () => {
});
it('renders over an existing text child without throwing', () => {
- spyOnDev(console, 'error');
const container = document.createElement('div');
container.textContent = 'potato';
- ReactDOM.hydrate(
parsnip
, container);
+ expect(() => ReactDOM.hydrate(
parsnip
, container)).toWarnDev(
+ 'Expected server HTML to contain a matching
in
.',
+ );
expect(container.textContent).toBe('parsnip');
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'Expected server HTML to contain a matching
in
.',
- );
- }
});
it('should give helpful errors on state desync', () => {
@@ -407,19 +392,13 @@ describe('rendering React components at document', () => {
);
const testDocument = getTestDocument(markup);
- spyOnDev(console, 'error');
- ReactDOM.hydrate(, testDocument);
+ expect(() =>
+ ReactDOM.hydrate(, testDocument),
+ ).toWarnDev('Warning: Text content did not match.');
expect(testDocument.body.innerHTML).toBe('Hello world');
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'Warning: Text content did not match.',
- );
- }
});
it('should render w/ no markup to full document', () => {
- spyOnDev(console, 'error');
const testDocument = getTestDocument();
class Component extends React.Component {
@@ -435,15 +414,11 @@ describe('rendering React components at document', () => {
}
}
- ReactDOM.hydrate(, testDocument);
+ // getTestDocument() has an extra that we didn't render.
+ expect(() =>
+ ReactDOM.hydrate(, testDocument),
+ ).toWarnDev('Did not expect server HTML to contain a in .');
expect(testDocument.body.innerHTML).toBe('Hello world');
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- // getTestDocument() has an extra that we didn't render.
- 'Did not expect server HTML to contain a in .',
- );
- }
});
it('supports findDOMNode on full-page components', () => {
diff --git a/packages/react-dom/src/__tests__/ReactServerRendering-test.js b/packages/react-dom/src/__tests__/ReactServerRendering-test.js
index 642d950f22..cfba21e052 100644
--- a/packages/react-dom/src/__tests__/ReactServerRendering-test.js
+++ b/packages/react-dom/src/__tests__/ReactServerRendering-test.js
@@ -431,24 +431,17 @@ describe('ReactDOMServer', () => {
}
}
- spyOnDev(console, 'error');
ReactDOMServer.renderToString();
- jest.runOnlyPendingTimers();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.mostRecent().args[0]).toBe(
- 'Warning: setState(...): Can only update a mounting component.' +
- ' This usually means you called setState() outside componentWillMount() on the server.' +
- ' This is a no-op.\n\nPlease check the code for the Foo component.',
- );
- }
+ expect(() => jest.runOnlyPendingTimers()).toWarnDev(
+ 'Warning: setState(...): Can only update a mounting component.' +
+ ' This usually means you called setState() outside componentWillMount() on the server.' +
+ ' This is a no-op.\n\nPlease check the code for the Foo component.',
+ );
const markup = ReactDOMServer.renderToStaticMarkup();
expect(markup).toBe('
hello
');
+ // No additional warnings are expected
jest.runOnlyPendingTimers();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- }
});
it('warns with a no-op when an async forceUpdate is triggered', () => {
@@ -465,23 +458,17 @@ describe('ReactDOMServer', () => {
}
}
- spyOnDev(console, 'error');
ReactDOMServer.renderToString();
- jest.runOnlyPendingTimers();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.mostRecent().args[0]).toBe(
- 'Warning: forceUpdate(...): Can only update a mounting component. ' +
- 'This usually means you called forceUpdate() outside componentWillMount() on the server. ' +
- 'This is a no-op.\n\nPlease check the code for the Baz component.',
- );
- }
+ expect(() => jest.runOnlyPendingTimers()).toWarnDev(
+ 'Warning: forceUpdate(...): Can only update a mounting component. ' +
+ 'This usually means you called forceUpdate() outside componentWillMount() on the server. ' +
+ 'This is a no-op.\n\nPlease check the code for the Baz component.',
+ );
const markup = ReactDOMServer.renderToStaticMarkup();
expect(markup).toBe('');
});
it('should throw (in dev) when children are mutated during render', () => {
- spyOnDev(console, 'error');
function Wrapper(props) {
props.children[1] = ; // Mutation is illegal
return
{props.children}
;
@@ -510,51 +497,43 @@ describe('ReactDOMServer', () => {
});
it('warns about lowercase html but not in svg tags', () => {
- spyOnDev(console, 'error');
function CompositeG(props) {
// Make sure namespace passes through composites
return {props.children};
}
- ReactDOMServer.renderToStaticMarkup(
-
-
-
-
,
- );
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(2);
- expect(console.error.calls.argsFor(0)[0]).toBe(
- 'Warning: is using uppercase HTML. Always use lowercase ' +
- 'HTML tags in React.',
- );
+ expect(() =>
+ ReactDOMServer.renderToStaticMarkup(
+
+
+
+
,
+ ),
+ ).toWarnDev([
+ 'Warning: is using uppercase HTML. Always use lowercase ' +
+ 'HTML tags in React.',
// linearGradient doesn't warn
- expect(console.error.calls.argsFor(1)[0]).toBe(
- 'Warning: is using uppercase HTML. Always use lowercase ' +
- 'HTML tags in React.',
- );
- }
+ 'Warning: is using uppercase HTML. Always use lowercase ' +
+ 'HTML tags in React.',
+ ]);
});
it('should warn about contentEditable and children', () => {
- spyOnDev(console, 'error');
- ReactDOMServer.renderToString();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: A component is `contentEditable` and contains `children` ' +
- 'managed by React. It is now your responsibility to guarantee that ' +
- 'none of those nodes are unexpectedly modified or duplicated. This ' +
- 'is probably not intentional.\n in div (at **)',
- );
- }
+ expect(() =>
+ ReactDOMServer.renderToString(),
+ ).toWarnDev(
+ 'Warning: A component is `contentEditable` and contains `children` ' +
+ 'managed by React. It is now your responsibility to guarantee that ' +
+ 'none of those nodes are unexpectedly modified or duplicated. This ' +
+ 'is probably not intentional.\n in div (at **)',
+ );
});
it('should throw rendering call/return on the server', () => {
diff --git a/packages/react-dom/src/__tests__/ReactServerRenderingHydration.js b/packages/react-dom/src/__tests__/ReactServerRenderingHydration.js
index 928d7a98ef..3fa9a18eaa 100644
--- a/packages/react-dom/src/__tests__/ReactServerRenderingHydration.js
+++ b/packages/react-dom/src/__tests__/ReactServerRenderingHydration.js
@@ -26,9 +26,6 @@ describe('ReactDOMServerHydration', () => {
});
it('should have the correct mounting behavior (old hydrate API)', () => {
- spyOnDev(console, 'warn');
- spyOnDev(console, 'error');
-
let mountCount = 0;
let numClicks = 0;
@@ -78,17 +75,16 @@ describe('ReactDOMServerHydration', () => {
lastMarkup = ReactDOMServer.renderToString();
element.innerHTML = lastMarkup;
- let instance = ReactDOM.render(, element);
+ let instance;
+
+ expect(() => {
+ instance = ReactDOM.render(, element);
+ }).toLowPriorityWarnDev(
+ 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
+ 'will stop working in React v17. Replace the ReactDOM.render() call ' +
+ 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
+ );
expect(mountCount).toEqual(3);
- if (__DEV__) {
- expect(console.warn.calls.count()).toBe(1);
- expect(console.warn.calls.argsFor(0)[0]).toContain(
- 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
- 'will stop working in React v17. Replace the ReactDOM.render() call ' +
- 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
- );
- console.warn.calls.reset();
- }
expect(element.innerHTML).toBe(lastMarkup);
// Ensure the events system works after mount into server markup
@@ -102,15 +98,10 @@ describe('ReactDOMServerHydration', () => {
// Now simulate a situation where the app is not idempotent. React should
// warn but do the right thing.
element.innerHTML = lastMarkup;
- instance = ReactDOM.render(, element);
+ expect(() => {
+ instance = ReactDOM.render(, element);
+ }).toWarnDev('Text content did not match. Server: "x" Client: "y"');
expect(mountCount).toEqual(4);
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'Text content did not match. Server: "x" Client: "y"',
- );
- console.error.calls.reset();
- }
expect(element.innerHTML.length > 0).toBe(true);
expect(element.innerHTML).not.toEqual(lastMarkup);
@@ -118,15 +109,9 @@ describe('ReactDOMServerHydration', () => {
expect(numClicks).toEqual(1);
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
expect(numClicks).toEqual(2);
- if (__DEV__) {
- expect(console.warn.calls.count()).toBe(0);
- expect(console.error.calls.count()).toBe(0);
- }
});
it('should have the correct mounting behavior (new hydrate API)', () => {
- spyOnDev(console, 'error');
-
let mountCount = 0;
let numClicks = 0;
@@ -191,11 +176,10 @@ describe('ReactDOMServerHydration', () => {
// Now simulate a situation where the app is not idempotent. React should
// warn but do the right thing.
element.innerHTML = lastMarkup;
- instance = ReactDOM.hydrate(, element);
+ expect(() => {
+ instance = ReactDOM.hydrate(, element);
+ }).toWarnDev('Text content did not match. Server: "x" Client: "y"');
expect(mountCount).toEqual(4);
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- }
expect(element.innerHTML.length > 0).toBe(true);
expect(element.innerHTML).not.toEqual(lastMarkup);
@@ -242,8 +226,6 @@ describe('ReactDOMServerHydration', () => {
// Regression test for https://github.com/facebook/react/issues/11726
it('should not focus on either server or client with autofocus={false} even if there is a markup mismatch', () => {
- spyOnDev(console, 'error');
-
const element = document.createElement('div');
element.innerHTML = ReactDOMServer.renderToString(
,
@@ -251,15 +233,14 @@ describe('ReactDOMServerHydration', () => {
expect(element.firstChild.autofocus).toBe(false);
element.firstChild.focus = jest.fn();
- ReactDOM.hydrate(, element);
+
+ expect(() =>
+ ReactDOM.hydrate(, element),
+ ).toWarnDev(
+ 'Warning: Text content did not match. Server: "server" Client: "client"',
+ );
expect(element.firstChild.focus).not.toHaveBeenCalled();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toBe(
- 'Warning: Text content did not match. Server: "server" Client: "client"',
- );
- }
});
it('should throw rendering portals on the server', () => {
diff --git a/packages/react-dom/src/__tests__/ReactStatelessComponent-test.js b/packages/react-dom/src/__tests__/ReactStatelessComponent-test.js
index 9f4cce3e1f..3f82323591 100644
--- a/packages/react-dom/src/__tests__/ReactStatelessComponent-test.js
+++ b/packages/react-dom/src/__tests__/ReactStatelessComponent-test.js
@@ -19,10 +19,6 @@ function StatelessComponent(props) {
}
describe('ReactStatelessComponent', () => {
- function normalizeCodeLocInfo(str) {
- return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
- }
-
beforeEach(() => {
jest.resetModuleRegistry();
PropTypes = require('prop-types');
@@ -103,7 +99,6 @@ describe('ReactStatelessComponent', () => {
});
it('should warn for childContextTypes on a functional component', () => {
- spyOnDev(console, 'error');
function StatelessComponentWithChildContext(props) {
return
{props.name}
;
}
@@ -114,15 +109,15 @@ describe('ReactStatelessComponent', () => {
const container = document.createElement('div');
- ReactDOM.render(, container);
-
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'StatelessComponentWithChildContext(...): childContextTypes cannot ' +
- 'be defined on a functional component.',
- );
- }
+ expect(() =>
+ ReactDOM.render(
+ ,
+ container,
+ ),
+ ).toWarnDev(
+ 'StatelessComponentWithChildContext(...): childContextTypes cannot ' +
+ 'be defined on a functional component.',
+ );
});
it('should throw when stateless component returns undefined', () => {
@@ -157,8 +152,6 @@ describe('ReactStatelessComponent', () => {
});
it('should warn when given a string ref', () => {
- spyOnDev(console, 'error');
-
function Indirection(props) {
return
{props.children}
;
}
@@ -173,29 +166,23 @@ describe('ReactStatelessComponent', () => {
}
}
- ReactTestUtils.renderIntoDocument();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Stateless function components cannot be given refs. ' +
- 'Attempts to access this ref will fail.\n\nCheck the render method ' +
- 'of `ParentUsingStringRef`.\n' +
- ' in StatelessComponent (at **)\n' +
- ' in div (at **)\n' +
- ' in Indirection (at **)\n' +
- ' in ParentUsingStringRef (at **)',
- );
- }
+ expect(() =>
+ ReactTestUtils.renderIntoDocument(),
+ ).toWarnDev(
+ 'Warning: Stateless function components cannot be given refs. ' +
+ 'Attempts to access this ref will fail.\n\nCheck the render method ' +
+ 'of `ParentUsingStringRef`.\n' +
+ ' in StatelessComponent (at **)\n' +
+ ' in div (at **)\n' +
+ ' in Indirection (at **)\n' +
+ ' in ParentUsingStringRef (at **)',
+ );
+ // No additional warnings should be logged
ReactTestUtils.renderIntoDocument();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- }
});
it('should warn when given a function ref', () => {
- spyOnDev(console, 'error');
-
function Indirection(props) {
return
{props.children}
;
}
@@ -215,29 +202,23 @@ describe('ReactStatelessComponent', () => {
}
}
- ReactTestUtils.renderIntoDocument();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Stateless function components cannot be given refs. ' +
- 'Attempts to access this ref will fail.\n\nCheck the render method ' +
- 'of `ParentUsingFunctionRef`.\n' +
- ' in StatelessComponent (at **)\n' +
- ' in div (at **)\n' +
- ' in Indirection (at **)\n' +
- ' in ParentUsingFunctionRef (at **)',
- );
- }
+ expect(() =>
+ ReactTestUtils.renderIntoDocument(),
+ ).toWarnDev(
+ 'Warning: Stateless function components cannot be given refs. ' +
+ 'Attempts to access this ref will fail.\n\nCheck the render method ' +
+ 'of `ParentUsingFunctionRef`.\n' +
+ ' in StatelessComponent (at **)\n' +
+ ' in div (at **)\n' +
+ ' in Indirection (at **)\n' +
+ ' in ParentUsingFunctionRef (at **)',
+ );
+ // No additional warnings should be logged
ReactTestUtils.renderIntoDocument();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- }
});
it('deduplicates ref warnings based on element or owner', () => {
- spyOnDev(console, 'error');
-
// When owner uses JSX, we can use exact line location to dedupe warnings
class AnonymousParentUsingJSX extends React.Component {
render() {
@@ -246,23 +227,19 @@ describe('ReactStatelessComponent', () => {
}
Object.defineProperty(AnonymousParentUsingJSX, 'name', {value: undefined});
- const instance1 = ReactTestUtils.renderIntoDocument(
- ,
- );
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'Warning: Stateless function components cannot be given refs.',
+ let instance1;
+
+ expect(() => {
+ instance1 = ReactTestUtils.renderIntoDocument(
+ ,
);
- }
+ }).toWarnDev(
+ 'Warning: Stateless function components cannot be given refs.',
+ );
// Should be deduped (offending element is on the same line):
instance1.forceUpdate();
// Should also be deduped (offending element is on the same line):
ReactTestUtils.renderIntoDocument();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- console.error.calls.reset();
- }
// When owner doesn't use JSX, and is anonymous, we warn once per internal instance.
class AnonymousParentNotUsingJSX extends React.Component {
@@ -277,29 +254,20 @@ describe('ReactStatelessComponent', () => {
value: undefined,
});
- const instance2 = ReactTestUtils.renderIntoDocument(
- ,
+ let instance2;
+ expect(() => {
+ instance2 = ReactTestUtils.renderIntoDocument(
+ ,
+ );
+ }).toWarnDev(
+ 'Warning: Stateless function components cannot be given refs.',
);
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'Warning: Stateless function components cannot be given refs.',
- );
- }
- // Should be deduped (same internal instance):
+ // Should be deduped (same internal instance, no additional warnings)
instance2.forceUpdate();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- }
// Could not be deduped (different internal instance):
- ReactTestUtils.renderIntoDocument();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(2);
- expect(console.error.calls.argsFor(1)[0]).toContain(
- 'Warning: Stateless function components cannot be given refs.',
- );
- console.error.calls.reset();
- }
+ expect(() =>
+ ReactTestUtils.renderIntoDocument(),
+ ).toWarnDev('Warning: Stateless function components cannot be given refs.');
// When owner doesn't use JSX, but is named, we warn once per owner name
class NamedParentNotUsingJSX extends React.Component {
@@ -310,33 +278,21 @@ describe('ReactStatelessComponent', () => {
});
}
}
- const instance3 = ReactTestUtils.renderIntoDocument(
- ,
+ let instance3;
+ expect(() => {
+ instance3 = ReactTestUtils.renderIntoDocument();
+ }).toWarnDev(
+ 'Warning: Stateless function components cannot be given refs.',
);
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'Warning: Stateless function components cannot be given refs.',
- );
- }
- // Should be deduped (same owner name):
+ // Should be deduped (same owner name, no additional warnings):
instance3.forceUpdate();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- }
- // Should also be deduped (same owner name):
+ // Should also be deduped (same owner name, no additional warnings):
ReactTestUtils.renderIntoDocument();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- console.error.calls.reset();
- }
});
// This guards against a regression caused by clearing the current debug fiber.
// https://github.com/facebook/react/issues/10831
it('should warn when giving a function ref with context', () => {
- spyOnDev(console, 'error');
-
function Child() {
return null;
}
@@ -358,17 +314,13 @@ describe('ReactStatelessComponent', () => {
}
}
- ReactTestUtils.renderIntoDocument();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Stateless function components cannot be given refs. ' +
- 'Attempts to access this ref will fail.\n\nCheck the render method ' +
- 'of `Parent`.\n' +
- ' in Child (at **)\n' +
- ' in Parent (at **)',
- );
- }
+ expect(() => ReactTestUtils.renderIntoDocument()).toWarnDev(
+ 'Warning: Stateless function components cannot be given refs. ' +
+ 'Attempts to access this ref will fail.\n\nCheck the render method ' +
+ 'of `Parent`.\n' +
+ ' in Child (at **)\n' +
+ ' in Parent (at **)',
+ );
});
it('should provide a null ref', () => {
@@ -385,15 +337,10 @@ describe('ReactStatelessComponent', () => {
return
{[]}
;
}
- spyOnDev(console, 'error');
- ReactTestUtils.renderIntoDocument();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'a unique "key" prop',
- );
- expect(console.error.calls.argsFor(0)[0]).toContain('Child');
- }
+ expect(() => ReactTestUtils.renderIntoDocument()).toWarnDev(
+ 'Each child in an array or iterator should have a unique "key" prop.\n\n' +
+ 'Check the render method of `Child`.',
+ );
});
it('should support default props and prop types', () => {
@@ -403,18 +350,11 @@ describe('ReactStatelessComponent', () => {
Child.defaultProps = {test: 2};
Child.propTypes = {test: PropTypes.string};
- spyOnDev(console, 'error');
- ReactTestUtils.renderIntoDocument();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(
- console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)'),
- ).toBe(
- 'Warning: Failed prop type: Invalid prop `test` of type `number` ' +
- 'supplied to `Child`, expected `string`.\n' +
- ' in Child (at **)',
- );
- }
+ expect(() => ReactTestUtils.renderIntoDocument()).toWarnDev(
+ 'Warning: Failed prop type: Invalid prop `test` of type `number` ' +
+ 'supplied to `Child`, expected `string`.\n' +
+ ' in Child (at **)',
+ );
});
it('should receive context', () => {
diff --git a/packages/react-dom/src/__tests__/ReactUpdates-test.js b/packages/react-dom/src/__tests__/ReactUpdates-test.js
index b033550641..1b86a59db3 100644
--- a/packages/react-dom/src/__tests__/ReactUpdates-test.js
+++ b/packages/react-dom/src/__tests__/ReactUpdates-test.js
@@ -842,8 +842,6 @@ describe('ReactUpdates', () => {
});
it('throws in setState if the update callback is not a function', () => {
- spyOnDev(console, 'error');
-
function Foo() {
this.a = 1;
this.b = 2;
@@ -859,44 +857,38 @@ describe('ReactUpdates', () => {
let component = ReactTestUtils.renderIntoDocument();
- expect(() => component.setState({}, 'no')).toThrowError(
- 'Invalid argument passed as callback. Expected a function. Instead ' +
- 'received: no',
- );
- if (__DEV__) {
- expect(console.error.calls.argsFor(0)[0]).toContain(
+ expect(() => {
+ expect(() => component.setState({}, 'no')).toWarnDev(
'setState(...): Expected the last optional `callback` argument to be ' +
'a function. Instead received: no.',
);
- }
- component = ReactTestUtils.renderIntoDocument();
- expect(() => component.setState({}, {foo: 'bar'})).toThrowError(
+ }).toThrowError(
'Invalid argument passed as callback. Expected a function. Instead ' +
- 'received: [object Object]',
+ 'received: no',
);
- if (__DEV__) {
- expect(console.error.calls.argsFor(1)[0]).toContain(
+ component = ReactTestUtils.renderIntoDocument();
+ expect(() => {
+ expect(() => component.setState({}, {foo: 'bar'})).toWarnDev(
'setState(...): Expected the last optional `callback` argument to be ' +
'a function. Instead received: [object Object].',
);
- }
- component = ReactTestUtils.renderIntoDocument();
- expect(() => component.setState({}, new Foo())).toThrowError(
+ }).toThrowError(
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: [object Object]',
);
- if (__DEV__) {
- expect(console.error.calls.argsFor(2)[0]).toContain(
+ component = ReactTestUtils.renderIntoDocument();
+ expect(() => {
+ expect(() => component.setState({}, new Foo())).toWarnDev(
'setState(...): Expected the last optional `callback` argument to be ' +
'a function. Instead received: [object Object].',
);
- expect(console.error.calls.count()).toBe(3);
- }
+ }).toThrowError(
+ 'Invalid argument passed as callback. Expected a function. Instead ' +
+ 'received: [object Object]',
+ );
});
it('throws in forceUpdate if the update callback is not a function', () => {
- spyOnDev(console, 'error');
-
function Foo() {
this.a = 1;
this.b = 2;
@@ -912,39 +904,35 @@ describe('ReactUpdates', () => {
let component = ReactTestUtils.renderIntoDocument();
- expect(() => component.forceUpdate('no')).toThrowError(
- 'Invalid argument passed as callback. Expected a function. Instead ' +
- 'received: no',
- );
- if (__DEV__) {
- expect(console.error.calls.argsFor(0)[0]).toContain(
+ expect(() => {
+ expect(() => component.forceUpdate('no')).toWarnDev(
'forceUpdate(...): Expected the last optional `callback` argument to be ' +
'a function. Instead received: no.',
);
- }
- component = ReactTestUtils.renderIntoDocument();
- expect(() => component.forceUpdate({foo: 'bar'})).toThrowError(
+ }).toThrowError(
'Invalid argument passed as callback. Expected a function. Instead ' +
- 'received: [object Object]',
+ 'received: no',
);
- if (__DEV__) {
- expect(console.error.calls.argsFor(1)[0]).toContain(
+ component = ReactTestUtils.renderIntoDocument();
+ expect(() => {
+ expect(() => component.forceUpdate({foo: 'bar'})).toWarnDev(
'forceUpdate(...): Expected the last optional `callback` argument to be ' +
'a function. Instead received: [object Object].',
);
- }
- component = ReactTestUtils.renderIntoDocument();
- expect(() => component.forceUpdate(new Foo())).toThrowError(
+ }).toThrowError(
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: [object Object]',
);
- if (__DEV__) {
- expect(console.error.calls.argsFor(2)[0]).toContain(
+ component = ReactTestUtils.renderIntoDocument();
+ expect(() => {
+ expect(() => component.forceUpdate(new Foo())).toWarnDev(
'forceUpdate(...): Expected the last optional `callback` argument to be ' +
'a function. Instead received: [object Object].',
);
- expect(console.error.calls.count()).toBe(3);
- }
+ }).toThrowError(
+ 'Invalid argument passed as callback. Expected a function. Instead ' +
+ 'received: [object Object]',
+ );
});
it('does not update one component twice in a batch (#2410)', () => {
@@ -1228,8 +1216,6 @@ describe('ReactUpdates', () => {
);
it('uses correct base state for setState inside render phase', () => {
- spyOnDev(console, 'error');
-
let ops = [];
class Foo extends React.Component {
@@ -1246,14 +1232,10 @@ describe('ReactUpdates', () => {
}
const container = document.createElement('div');
- ReactDOM.render(, container);
+ expect(() => ReactDOM.render(, container)).toWarnDev(
+ 'Cannot update during an existing state transition',
+ );
expect(ops).toEqual(['base: 0, memoized: 0', 'base: 1, memoized: 1']);
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'Cannot update during an existing state transition',
- );
- }
});
it('does not re-render if state update is null', () => {
diff --git a/packages/react-dom/src/__tests__/TapEventPlugin-test.internal.js b/packages/react-dom/src/__tests__/TapEventPlugin-test.internal.js
index 3abf048e04..bd5b097915 100644
--- a/packages/react-dom/src/__tests__/TapEventPlugin-test.internal.js
+++ b/packages/react-dom/src/__tests__/TapEventPlugin-test.internal.js
@@ -86,19 +86,13 @@ describe('TapEventPlugin', () => {
idCallOrder = [];
tapMoveThreshold = TapEventPlugin.tapMoveThreshold;
- spyOnDev(console, 'warn');
- EventPluginHub.injection.injectEventPluginsByName({
- TapEventPlugin: TapEventPlugin,
- });
- });
-
- afterEach(() => {
- if (__DEV__) {
- expect(console.warn.calls.count()).toBe(1);
- expect(console.warn.calls.argsFor(0)[0]).toContain(
- 'Injecting custom event plugins (TapEventPlugin) is deprecated',
- );
- }
+ expect(() =>
+ EventPluginHub.injection.injectEventPluginsByName({
+ TapEventPlugin: TapEventPlugin,
+ }),
+ ).toLowPriorityWarnDev(
+ 'Injecting custom event plugins (TapEventPlugin) is deprecated',
+ );
});
/**
diff --git a/packages/react-dom/src/__tests__/validateDOMNesting-test.js b/packages/react-dom/src/__tests__/validateDOMNesting-test.js
index f2e272338b..6a37e5aa14 100644
--- a/packages/react-dom/src/__tests__/validateDOMNesting-test.js
+++ b/packages/react-dom/src/__tests__/validateDOMNesting-test.js
@@ -12,40 +12,22 @@
const React = require('react');
const ReactDOM = require('react-dom');
-function normalizeCodeLocInfo(str) {
- return str && str.replace(/at .+?:\d+/g, 'at **');
-}
-
function expectWarnings(tags, warnings = []) {
tags = [...tags];
warnings = [...warnings];
let element = null;
- if (__DEV__) {
- console.error.calls.reset();
- }
const container = document.createElement(tags.splice(0, 1));
while (tags.length) {
const Tag = tags.pop();
element = {element};
}
- ReactDOM.render(element, container);
- if (__DEV__) {
- expect(console.error.calls.count()).toEqual(warnings.length);
- while (warnings.length) {
- expect(
- normalizeCodeLocInfo(
- console.error.calls.argsFor(warnings.length - 1)[0],
- ),
- ).toContain(warnings.pop());
- }
- }
+ expect(() => ReactDOM.render(element, container)).toWarnDev(warnings);
}
describe('validateDOMNesting', () => {
it('allows valid nestings', () => {
- spyOnDev(console, 'error');
expectWarnings(['table', 'tbody', 'tr', 'td', 'b']);
expectWarnings(
['body', 'datalist', 'option'],
@@ -66,7 +48,6 @@ describe('validateDOMNesting', () => {
});
it('prevents problematic nestings', () => {
- spyOnDev(console, 'error');
expectWarnings(
['a', 'a'],
[
diff --git a/packages/react-dom/src/events/__tests__/SyntheticEvent-test.js b/packages/react-dom/src/events/__tests__/SyntheticEvent-test.js
index a159467eab..10420f1f9c 100644
--- a/packages/react-dom/src/events/__tests__/SyntheticEvent-test.js
+++ b/packages/react-dom/src/events/__tests__/SyntheticEvent-test.js
@@ -157,7 +157,6 @@ describe('SyntheticEvent', () => {
});
it('should be nullified and log warnings if the synthetic event has not been persisted', () => {
- spyOnDev(console, 'error');
let node;
let expectedCount = 0;
let syntheticEvent;
@@ -173,26 +172,30 @@ describe('SyntheticEvent', () => {
event.initEvent('click', true, true);
node.dispatchEvent(event);
- expect(syntheticEvent.type).toBe(null);
- expect(syntheticEvent.nativeEvent).toBe(null);
- expect(syntheticEvent.target).toBe(null);
- if (__DEV__) {
- // once for each property accessed
- expect(console.error.calls.count()).toBe(3);
- // assert the first warning for accessing `type`
- expect(console.error.calls.argsFor(0)[0]).toBe(
- 'Warning: This synthetic event is reused for performance reasons. If ' +
- "you're seeing this, you're accessing the property `type` on a " +
- 'released/nullified synthetic event. This is set to null. If you must ' +
- 'keep the original synthetic event around, use event.persist(). ' +
- 'See https://fb.me/react-event-pooling for more information.',
- );
- }
+ const getExpectedWarning = property =>
+ 'Warning: This synthetic event is reused for performance reasons. If ' +
+ `you're seeing this, you're accessing the property \`${
+ property
+ }\` on a ` +
+ 'released/nullified synthetic event. This is set to null. If you must ' +
+ 'keep the original synthetic event around, use event.persist(). ' +
+ 'See https://fb.me/react-event-pooling for more information.';
+
+ // once for each property accessed
+ expect(() => expect(syntheticEvent.type).toBe(null)).toWarnDev(
+ getExpectedWarning('type'),
+ );
+ expect(() => expect(syntheticEvent.nativeEvent).toBe(null)).toWarnDev(
+ getExpectedWarning('nativeEvent'),
+ );
+ expect(() => expect(syntheticEvent.target).toBe(null)).toWarnDev(
+ getExpectedWarning('target'),
+ );
+
expect(expectedCount).toBe(1);
});
it('should warn when setting properties of a synthetic event that has not been persisted', () => {
- spyOnDev(console, 'error');
let node;
let expectedCount = 0;
let syntheticEvent;
@@ -208,22 +211,19 @@ describe('SyntheticEvent', () => {
event.initEvent('click', true, true);
node.dispatchEvent(event);
- syntheticEvent.type = 'MouseEvent';
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toBe(
- 'Warning: This synthetic event is reused for performance reasons. If ' +
- "you're seeing this, you're setting the property `type` on a " +
- 'released/nullified synthetic event. This is effectively a no-op. If you must ' +
- 'keep the original synthetic event around, use event.persist(). ' +
- 'See https://fb.me/react-event-pooling for more information.',
- );
- }
+ expect(() => {
+ syntheticEvent.type = 'MouseEvent';
+ }).toWarnDev(
+ 'Warning: This synthetic event is reused for performance reasons. If ' +
+ "you're seeing this, you're setting the property `type` on a " +
+ 'released/nullified synthetic event. This is effectively a no-op. If you must ' +
+ 'keep the original synthetic event around, use event.persist(). ' +
+ 'See https://fb.me/react-event-pooling for more information.',
+ );
expect(expectedCount).toBe(1);
});
it('should warn when calling `preventDefault` if the synthetic event has not been persisted', () => {
- spyOnDev(console, 'error');
let node;
let expectedCount = 0;
let syntheticEvent;
@@ -238,22 +238,17 @@ describe('SyntheticEvent', () => {
event.initEvent('click', true, true);
node.dispatchEvent(event);
- syntheticEvent.preventDefault();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toBe(
- 'Warning: This synthetic event is reused for performance reasons. If ' +
- "you're seeing this, you're accessing the method `preventDefault` on a " +
- 'released/nullified synthetic event. This is a no-op function. If you must ' +
- 'keep the original synthetic event around, use event.persist(). ' +
- 'See https://fb.me/react-event-pooling for more information.',
- );
- }
+ expect(() => syntheticEvent.preventDefault()).toWarnDev(
+ 'Warning: This synthetic event is reused for performance reasons. If ' +
+ "you're seeing this, you're accessing the method `preventDefault` on a " +
+ 'released/nullified synthetic event. This is a no-op function. If you must ' +
+ 'keep the original synthetic event around, use event.persist(). ' +
+ 'See https://fb.me/react-event-pooling for more information.',
+ );
expect(expectedCount).toBe(1);
});
it('should warn when calling `stopPropagation` if the synthetic event has not been persisted', () => {
- spyOnDev(console, 'error');
let node;
let expectedCount = 0;
let syntheticEvent;
@@ -269,17 +264,13 @@ describe('SyntheticEvent', () => {
node.dispatchEvent(event);
- syntheticEvent.stopPropagation();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toBe(
- 'Warning: This synthetic event is reused for performance reasons. If ' +
- "you're seeing this, you're accessing the method `stopPropagation` on a " +
- 'released/nullified synthetic event. This is a no-op function. If you must ' +
- 'keep the original synthetic event around, use event.persist(). ' +
- 'See https://fb.me/react-event-pooling for more information.',
- );
- }
+ expect(() => syntheticEvent.stopPropagation()).toWarnDev(
+ 'Warning: This synthetic event is reused for performance reasons. If ' +
+ "you're seeing this, you're accessing the method `stopPropagation` on a " +
+ 'released/nullified synthetic event. This is a no-op function. If you must ' +
+ 'keep the original synthetic event around, use event.persist(). ' +
+ 'See https://fb.me/react-event-pooling for more information.',
+ );
expect(expectedCount).toBe(1);
});
@@ -287,7 +278,6 @@ describe('SyntheticEvent', () => {
// using TestUtils.Simulate to avoid spurious warnings that result from the
// way we simulate events.
xit('should properly log warnings when events simulated with rendered components', () => {
- spyOnDev(console, 'error');
let event;
const element = document.createElement('div');
function assignEvent(e) {
@@ -295,33 +285,37 @@ describe('SyntheticEvent', () => {
}
const node = ReactDOM.render(, element);
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(node));
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(0);
- }
// access a property to cause the warning
- event.nativeEvent; // eslint-disable-line no-unused-expressions
-
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toBe(
- 'Warning: This synthetic event is reused for performance reasons. If ' +
- "you're seeing this, you're accessing the property `nativeEvent` on a " +
- 'released/nullified synthetic event. This is set to null. If you must ' +
- 'keep the original synthetic event around, use event.persist(). ' +
- 'See https://fb.me/react-event-pooling for more information.',
- );
- }
+ expect(() => {
+ event.nativeEvent; // eslint-disable-line no-unused-expressions
+ }).toWarnDev(
+ 'Warning: This synthetic event is reused for performance reasons. If ' +
+ "you're seeing this, you're accessing the property `nativeEvent` on a " +
+ 'released/nullified synthetic event. This is set to null. If you must ' +
+ 'keep the original synthetic event around, use event.persist(). ' +
+ 'See https://fb.me/react-event-pooling for more information.',
+ );
});
it('should warn if Proxy is supported and the synthetic event is added a property', () => {
- spyOnDev(console, 'error');
let node;
let expectedCount = 0;
let syntheticEvent;
const eventHandler = e => {
- e.foo = 'bar';
+ if (typeof Proxy === 'function') {
+ expect(() => {
+ e.foo = 'bar';
+ }).toWarnDev(
+ 'Warning: This synthetic event is reused for performance reasons. If ' +
+ "you're seeing this, you're adding a new property in the synthetic " +
+ 'event object. The property is never released. ' +
+ 'See https://fb.me/react-event-pooling for more information.',
+ );
+ } else {
+ e.foo = 'bar';
+ }
syntheticEvent = e;
expectedCount++;
};
@@ -333,19 +327,6 @@ describe('SyntheticEvent', () => {
node.dispatchEvent(event);
expect(syntheticEvent.foo).toBe('bar');
- if (__DEV__) {
- if (typeof Proxy === 'function') {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toBe(
- 'Warning: This synthetic event is reused for performance reasons. If ' +
- "you're seeing this, you're adding a new property in the synthetic " +
- 'event object. The property is never released. ' +
- 'See https://fb.me/react-event-pooling for more information.',
- );
- } else {
- expect(console.error.calls.count()).toBe(0);
- }
- }
expect(expectedCount).toBe(1);
});
});
diff --git a/packages/react-reconciler/src/__tests__/ReactFragment-test.js b/packages/react-reconciler/src/__tests__/ReactFragment-test.js
index d8974c7efa..9aa38b80a4 100644
--- a/packages/react-reconciler/src/__tests__/ReactFragment-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactFragment-test.js
@@ -672,7 +672,6 @@ describe('ReactFragment', () => {
});
it('should not preserve state when switching to a keyed fragment to an array', function() {
- spyOnDev(console, 'error');
const ops = [];
class Stateful extends React.Component {
@@ -707,7 +706,9 @@ describe('ReactFragment', () => {
ReactNoop.flush();
ReactNoop.render();
- ReactNoop.flush();
+ expect(ReactNoop.flush).toWarnDev(
+ 'Each child in an array or iterator should have a unique "key" prop.',
+ );
expect(ops).toEqual([]);
expect(ReactNoop.getChildren()).toEqual([div(div(), span())]);
@@ -717,16 +718,9 @@ describe('ReactFragment', () => {
expect(ops).toEqual([]);
expect(ReactNoop.getChildren()).toEqual([div(div(), span())]);
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'Each child in an array or iterator should have a unique "key" prop.',
- );
- }
});
it('should preserve state when it does not change positions', function() {
- spyOnDev(console, 'error');
const ops = [];
class Stateful extends React.Component {
@@ -756,26 +750,24 @@ describe('ReactFragment', () => {
}
ReactNoop.render();
- ReactNoop.flush();
+ expect(ReactNoop.flush).toWarnDev(
+ 'Each child in an array or iterator should have a unique "key" prop.',
+ );
ReactNoop.render();
- ReactNoop.flush();
+ expect(ReactNoop.flush).toWarnDev(
+ 'Each child in an array or iterator should have a unique "key" prop.',
+ );
expect(ops).toEqual(['Update Stateful']);
expect(ReactNoop.getChildren()).toEqual([span(), div()]);
ReactNoop.render();
- ReactNoop.flush();
+ expect(ReactNoop.flush).toWarnDev(
+ 'Each child in an array or iterator should have a unique "key" prop.',
+ );
expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
expect(ReactNoop.getChildren()).toEqual([span(), div()]);
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(3);
- for (let errorIndex = 0; errorIndex < 3; ++errorIndex) {
- expect(console.error.calls.argsFor(errorIndex)[0]).toContain(
- 'Each child in an array or iterator should have a unique "key" prop.',
- );
- }
- }
});
});
diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.js b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.js
index 80210157e0..ae3bf926ae 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.js
@@ -720,8 +720,6 @@ describe('ReactIncrementalErrorHandling', () => {
});
it('catches reconciler errors in a boundary during mounting', () => {
- spyOnDev(console, 'error');
-
class ErrorBoundary extends React.Component {
state = {error: null};
componentDidCatch(error) {
@@ -744,7 +742,9 @@ describe('ReactIncrementalErrorHandling', () => {
,
);
- ReactNoop.flush();
+ expect(ReactNoop.flush).toWarnDev(
+ 'Warning: React.createElement: type is invalid -- expected a string',
+ );
expect(ReactNoop.getChildren()).toEqual([
span(
'Element type is invalid: expected a string (for built-in components) or ' +
@@ -756,17 +756,9 @@ describe('ReactIncrementalErrorHandling', () => {
: ''),
),
]);
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'Warning: React.createElement: type is invalid -- expected a string',
- );
- }
});
it('catches reconciler errors in a boundary during update', () => {
- spyOnDev(console, 'error');
-
class ErrorBoundary extends React.Component {
state = {error: null};
componentDidCatch(error) {
@@ -797,7 +789,9 @@ describe('ReactIncrementalErrorHandling', () => {
,
);
- ReactNoop.flush();
+ expect(ReactNoop.flush).toWarnDev(
+ 'Warning: React.createElement: type is invalid -- expected a string',
+ );
expect(ReactNoop.getChildren()).toEqual([
span(
'Element type is invalid: expected a string (for built-in components) or ' +
@@ -809,21 +803,14 @@ describe('ReactIncrementalErrorHandling', () => {
: ''),
),
]);
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'Warning: React.createElement: type is invalid -- expected a string',
- );
- }
});
it('recovers from uncaught reconciler errors', () => {
- spyOnDev(console, 'error');
const InvalidType = undefined;
- ReactNoop.render();
- expect(() => {
- ReactNoop.flush();
- }).toThrowError(
+ expect(() => ReactNoop.render()).toWarnDev(
+ 'Warning: React.createElement: type is invalid -- expected a string',
+ );
+ expect(ReactNoop.flush).toThrowError(
'Element type is invalid: expected a string (for built-in components) or ' +
'a class/function (for composite components) but got: undefined.' +
(__DEV__
@@ -835,12 +822,6 @@ describe('ReactIncrementalErrorHandling', () => {
ReactNoop.render();
ReactNoop.flush();
expect(ReactNoop.getChildren()).toEqual([span('hi')]);
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'Warning: React.createElement: type is invalid -- expected a string',
- );
- }
});
it('unmounts components with uncaught errors', () => {
diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js b/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js
index d31de9d2f9..ba405776a5 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js
@@ -20,10 +20,6 @@ describe('ReactIncrementalSideEffects', () => {
ReactNoop = require('react-noop-renderer');
});
- function normalizeCodeLocInfo(str) {
- return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
- }
-
function div(...children) {
children = children.map(c => (typeof c === 'string' ? {text: c} : c));
return {type: 'div', children, prop: undefined};
@@ -987,7 +983,6 @@ describe('ReactIncrementalSideEffects', () => {
});
it('invokes ref callbacks after insertion/update/unmount', () => {
- spyOnDev(console, 'error');
let classInstance = null;
let ops = [];
@@ -1014,7 +1009,14 @@ describe('ReactIncrementalSideEffects', () => {
}
ReactNoop.render();
- ReactNoop.flush();
+ expect(ReactNoop.flush).toWarnDev(
+ 'Warning: Stateless function components cannot be given refs. ' +
+ 'Attempts to access this ref will fail.\n\nCheck the render method ' +
+ 'of `Foo`.\n' +
+ ' in FunctionalComponent (at **)\n' +
+ ' in div (at **)\n' +
+ ' in Foo (at **)',
+ );
expect(ops).toEqual([
classInstance,
// no call for functional components
@@ -1044,17 +1046,6 @@ describe('ReactIncrementalSideEffects', () => {
null,
null,
]);
-
- if (__DEV__) {
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Stateless function components cannot be given refs. ' +
- 'Attempts to access this ref will fail.\n\nCheck the render method ' +
- 'of `Foo`.\n' +
- ' in FunctionalComponent (at **)\n' +
- ' in div (at **)\n' +
- ' in Foo (at **)',
- );
- }
});
// TODO: Test that mounts, updates, refs, unmounts and deletions happen in the
diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js b/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js
index 6f43d97535..037e4c43e1 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js
@@ -321,7 +321,6 @@ describe('ReactIncrementalUpdates', () => {
});
it('enqueues setState inside an updater function as if the in-progress update is progressed (and warns)', () => {
- spyOnDev(console, 'error');
let instance;
let ops = [];
class Foo extends React.Component {
@@ -342,7 +341,12 @@ describe('ReactIncrementalUpdates', () => {
return {a: 'a'};
});
- ReactNoop.flush();
+ expect(ReactNoop.flush).toWarnDev(
+ 'An update (setState, replaceState, or forceUpdate) was scheduled ' +
+ 'from inside an update function. Update functions should be pure, ' +
+ 'with zero side-effects. Consider using componentDidUpdate or a ' +
+ 'callback.',
+ );
expect(ops).toEqual([
// Initial render
'render',
@@ -353,24 +357,11 @@ describe('ReactIncrementalUpdates', () => {
]);
expect(instance.state).toEqual({a: 'a', b: 'b'});
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'An update (setState, replaceState, or forceUpdate) was scheduled ' +
- 'from inside an update function. Update functions should be pure, ' +
- 'with zero side-effects. Consider using componentDidUpdate or a ' +
- 'callback.',
- );
- }
-
- // Test deduplication
+ // Test deduplication (no additional warnings expected)
instance.setState(function a() {
this.setState({a: 'a'});
return {b: 'b'};
});
ReactNoop.flush();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- }
});
});
diff --git a/packages/react-test-renderer/src/__tests__/ReactShallowRenderer-test.js b/packages/react-test-renderer/src/__tests__/ReactShallowRenderer-test.js
index 21247968db..a2e572e7bd 100644
--- a/packages/react-test-renderer/src/__tests__/ReactShallowRenderer-test.js
+++ b/packages/react-test-renderer/src/__tests__/ReactShallowRenderer-test.js
@@ -771,8 +771,6 @@ describe('ReactShallowRenderer', () => {
});
it('can fail context when shallowly rendering', () => {
- spyOnDev(console, 'error');
-
class SimpleComponent extends React.Component {
static contextTypes = {
name: PropTypes.string.isRequired,
@@ -784,22 +782,14 @@ describe('ReactShallowRenderer', () => {
}
const shallowRenderer = createRenderer();
- shallowRenderer.render();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(
- console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)'),
- ).toBe(
- 'Warning: Failed context type: The context `name` is marked as ' +
- 'required in `SimpleComponent`, but its value is `undefined`.\n' +
- ' in SimpleComponent (at **)',
- );
- }
+ expect(() => shallowRenderer.render()).toWarnDev(
+ 'Warning: Failed context type: The context `name` is marked as ' +
+ 'required in `SimpleComponent`, but its value is `undefined`.\n' +
+ ' in SimpleComponent (at **)',
+ );
});
it('should warn about propTypes (but only once)', () => {
- spyOnDev(console, 'error');
-
class SimpleComponent extends React.Component {
render() {
return React.createElement('div', null, this.props.name);
@@ -811,18 +801,13 @@ describe('ReactShallowRenderer', () => {
};
const shallowRenderer = createRenderer();
- shallowRenderer.render(React.createElement(SimpleComponent, {name: 123}));
-
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(
- console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)'),
- ).toBe(
- 'Warning: Failed prop type: Invalid prop `name` of type `number` ' +
- 'supplied to `SimpleComponent`, expected `string`.\n' +
- ' in SimpleComponent',
- );
- }
+ expect(() =>
+ shallowRenderer.render(React.createElement(SimpleComponent, {name: 123})),
+ ).toWarnDev(
+ 'Warning: Failed prop type: Invalid prop `name` of type `number` ' +
+ 'supplied to `SimpleComponent`, expected `string`.\n' +
+ ' in SimpleComponent',
+ );
});
it('should enable rendering of cloned element', () => {
@@ -942,36 +927,24 @@ describe('ReactShallowRenderer', () => {
});
it('throws usefully when rendering badly-typed elements', () => {
- spyOnDev(console, 'error');
const shallowRenderer = createRenderer();
- const Undef = undefined;
- expect(() => shallowRenderer.render()).toThrowError(
- 'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
- 'components, but the provided element type was `undefined`.',
- );
+ const renderAndVerifyWarningAndError = (Component, typeString) => {
+ expect(() => {
+ expect(() => shallowRenderer.render()).toWarnDev(
+ 'React.createElement: type is invalid -- expected a string ' +
+ '(for built-in components) or a class/function (for composite components) ' +
+ `but got: ${typeString}.`,
+ );
+ }).toThrowError(
+ 'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
+ `components, but the provided element type was \`${typeString}\`.`,
+ );
+ };
- const Null = null;
- expect(() => shallowRenderer.render()).toThrowError(
- 'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
- 'components, but the provided element type was `null`.',
- );
-
- const Arr = [];
- expect(() => shallowRenderer.render()).toThrowError(
- 'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
- 'components, but the provided element type was `array`.',
- );
-
- const Obj = {};
- expect(() => shallowRenderer.render()).toThrowError(
- 'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
- 'components, but the provided element type was `object`.',
- );
-
- if (__DEV__) {
- // One warning for each element creation
- expect(console.error.calls.count()).toBe(4);
- }
+ renderAndVerifyWarningAndError(undefined, 'undefined');
+ renderAndVerifyWarningAndError(null, 'null');
+ renderAndVerifyWarningAndError([], 'array');
+ renderAndVerifyWarningAndError({}, 'object');
});
});
diff --git a/packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.js b/packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.js
index 3d461d293b..e077fd6e58 100644
--- a/packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.js
+++ b/packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.js
@@ -42,10 +42,6 @@ function cleanNodeOrArray(node) {
}
describe('ReactTestRenderer', () => {
- function normalizeCodeLocInfo(str) {
- return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
- }
-
it('renders a simple component', () => {
function Link() {
return ;
@@ -264,7 +260,6 @@ describe('ReactTestRenderer', () => {
});
it('warns correctly for refs on SFCs', () => {
- spyOnDev(console, 'error');
function Bar() {
return
Hello, world
;
}
@@ -279,16 +274,12 @@ describe('ReactTestRenderer', () => {
}
}
ReactTestRenderer.create();
- ReactTestRenderer.create();
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Stateless function components cannot be given refs. Attempts ' +
- 'to access this ref will fail.\n\nCheck the render method of `Foo`.\n' +
- ' in Bar (at **)\n' +
- ' in Foo (at **)',
- );
- }
+ expect(() => ReactTestRenderer.create()).toWarnDev(
+ 'Warning: Stateless function components cannot be given refs. Attempts ' +
+ 'to access this ref will fail.\n\nCheck the render method of `Foo`.\n' +
+ ' in Bar (at **)\n' +
+ ' in Foo (at **)',
+ );
});
it('allows an optional createNodeMock function', () => {
diff --git a/packages/react/src/ReactElementValidator.js b/packages/react/src/ReactElementValidator.js
index cfa2afa00f..3b547b9aff 100644
--- a/packages/react/src/ReactElementValidator.js
+++ b/packages/react/src/ReactElementValidator.js
@@ -308,12 +308,21 @@ export function createElementWithValidation(type, props, children) {
info += getStackAddendum() || '';
+ let typeString;
+ if (type === null) {
+ typeString = 'null';
+ } else if (Array.isArray(type)) {
+ typeString = 'array';
+ } else {
+ typeString = typeof type;
+ }
+
warning(
false,
'React.createElement: type is invalid -- expected a string (for ' +
'built-in components) or a class/function (for composite ' +
'components) but got: %s.%s',
- type == null ? type : typeof type,
+ typeString,
info,
);
}
diff --git a/packages/react/src/__tests__/ReactElementValidator-test.js b/packages/react/src/__tests__/ReactElementValidator-test.js
index 61efff91e7..f792ca66a5 100644
--- a/packages/react/src/__tests__/ReactElementValidator-test.js
+++ b/packages/react/src/__tests__/ReactElementValidator-test.js
@@ -350,8 +350,6 @@ describe('ReactElementValidator', () => {
});
it('should warn if a PropType creator is used as a PropType', () => {
- spyOnDev(console, 'error');
-
class Component extends React.Component {
static propTypes = {
myProp: PropTypes.shape,
diff --git a/packages/shared/__tests__/ReactDOMFrameScheduling-test.js b/packages/shared/__tests__/ReactDOMFrameScheduling-test.js
index db6bfa72e4..ff7a3d5a91 100644
--- a/packages/shared/__tests__/ReactDOMFrameScheduling-test.js
+++ b/packages/shared/__tests__/ReactDOMFrameScheduling-test.js
@@ -15,14 +15,9 @@ describe('ReactDOMFrameScheduling', () => {
try {
global.requestAnimationFrame = undefined;
jest.resetModules();
- spyOnDev(console, 'error');
- require('react-dom');
- if (__DEV__) {
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'React depends on requestAnimationFrame.',
- );
- }
+ expect(() => require('react-dom')).toWarnDev(
+ 'React depends on requestAnimationFrame.',
+ );
} finally {
global.requestAnimationFrame = previousRAF;
}