Update additional tests to use .toWarnDev() matcher (#11957)
* Migrated several additional tests to use new .toWarnDev() matcher * Migrated ReactDOMComponent-test to use .toWarnDev() matcher Note this test previous had some hacky logic to verify errors were reported against unique line numbers. Since the new matcher doesn't suppor this, I replaced this check with an equivalent (I think) comparison of unique DOM elements (eg div -> span) * Updated several additional tests to use the new .toWarnDev() matcher * Updated many more tests to use .toWarnDev() * Updated several additional tests to use .toWarnDev() matcher * Updated ReactElementValidator to distinguish between Array and Object in its warning. Also updated its test to use .toWarnDev() matcher. * Updated a couple of additional tests * Removed unused normalizeCodeLocInfo() methods
This commit is contained in:
parent
a442d9bc08
commit
9f848f8ebe
|
@ -423,8 +423,6 @@ describe('ReactCompositeComponent-state', () => {
|
|||
});
|
||||
|
||||
it('should treat assigning to this.state inside cWM as a replaceState, with a warning', () => {
|
||||
spyOnDev(console, 'error');
|
||||
|
||||
let ops = [];
|
||||
class Test extends React.Component {
|
||||
state = {step: 1, extra: true};
|
||||
|
|
|
@ -1909,8 +1909,6 @@ describe('ReactErrorBoundaries', () => {
|
|||
});
|
||||
|
||||
it('discards a bad root if the root component fails', () => {
|
||||
spyOnDev(console, 'error');
|
||||
|
||||
const X = null;
|
||||
const Y = undefined;
|
||||
let err1;
|
||||
|
@ -1918,13 +1916,21 @@ describe('ReactErrorBoundaries', () => {
|
|||
|
||||
try {
|
||||
let container = document.createElement('div');
|
||||
ReactDOM.render(<X />, container);
|
||||
expect(() => ReactDOM.render(<X />, container)).toWarnDev(
|
||||
'React.createElement: type is invalid -- expected a string ' +
|
||||
'(for built-in components) or a class/function ' +
|
||||
'(for composite components) but got: null.',
|
||||
);
|
||||
} catch (err) {
|
||||
err1 = err;
|
||||
}
|
||||
try {
|
||||
let container = document.createElement('div');
|
||||
ReactDOM.render(<Y />, container);
|
||||
expect(() => ReactDOM.render(<Y />, container)).toWarnDev(
|
||||
'React.createElement: type is invalid -- expected a string ' +
|
||||
'(for built-in components) or a class/function ' +
|
||||
'(for composite components) but got: undefined.',
|
||||
);
|
||||
} catch (err) {
|
||||
err2 = err;
|
||||
}
|
||||
|
|
|
@ -73,104 +73,98 @@ const expectChildren = function(container, children) {
|
|||
*/
|
||||
describe('ReactMultiChildText', () => {
|
||||
it('should correctly handle all possible children for render and update', () => {
|
||||
spyOnDev(console, 'error');
|
||||
// prettier-ignore
|
||||
testAllPermutations([
|
||||
// basic values
|
||||
undefined, [],
|
||||
null, [],
|
||||
false, [],
|
||||
true, [],
|
||||
0, '0',
|
||||
1.2, '1.2',
|
||||
'', '',
|
||||
'foo', 'foo',
|
||||
expect(() => {
|
||||
// prettier-ignore
|
||||
testAllPermutations([
|
||||
// basic values
|
||||
undefined, [],
|
||||
null, [],
|
||||
false, [],
|
||||
true, [],
|
||||
0, '0',
|
||||
1.2, '1.2',
|
||||
'', '',
|
||||
'foo', 'foo',
|
||||
|
||||
[], [],
|
||||
[undefined], [],
|
||||
[null], [],
|
||||
[false], [],
|
||||
[true], [],
|
||||
[0], ['0'],
|
||||
[1.2], ['1.2'],
|
||||
[''], [''],
|
||||
['foo'], ['foo'],
|
||||
[<div />], [<div />],
|
||||
[], [],
|
||||
[undefined], [],
|
||||
[null], [],
|
||||
[false], [],
|
||||
[true], [],
|
||||
[0], ['0'],
|
||||
[1.2], ['1.2'],
|
||||
[''], [''],
|
||||
['foo'], ['foo'],
|
||||
[<div />], [<div />],
|
||||
|
||||
// two adjacent values
|
||||
[true, 0], ['0'],
|
||||
[0, 0], ['0', '0'],
|
||||
[1.2, 0], ['1.2', '0'],
|
||||
[0, ''], ['0', ''],
|
||||
['foo', 0], ['foo', '0'],
|
||||
[0, <div />], ['0', <div />],
|
||||
// two adjacent values
|
||||
[true, 0], ['0'],
|
||||
[0, 0], ['0', '0'],
|
||||
[1.2, 0], ['1.2', '0'],
|
||||
[0, ''], ['0', ''],
|
||||
['foo', 0], ['foo', '0'],
|
||||
[0, <div />], ['0', <div />],
|
||||
|
||||
[true, 1.2], ['1.2'],
|
||||
[1.2, 0], ['1.2', '0'],
|
||||
[1.2, 1.2], ['1.2', '1.2'],
|
||||
[1.2, ''], ['1.2', ''],
|
||||
['foo', 1.2], ['foo', '1.2'],
|
||||
[1.2, <div />], ['1.2', <div />],
|
||||
[true, 1.2], ['1.2'],
|
||||
[1.2, 0], ['1.2', '0'],
|
||||
[1.2, 1.2], ['1.2', '1.2'],
|
||||
[1.2, ''], ['1.2', ''],
|
||||
['foo', 1.2], ['foo', '1.2'],
|
||||
[1.2, <div />], ['1.2', <div />],
|
||||
|
||||
[true, ''], [''],
|
||||
['', 0], ['', '0'],
|
||||
[1.2, ''], ['1.2', ''],
|
||||
['', ''], ['', ''],
|
||||
['foo', ''], ['foo', ''],
|
||||
['', <div />], ['', <div />],
|
||||
[true, ''], [''],
|
||||
['', 0], ['', '0'],
|
||||
[1.2, ''], ['1.2', ''],
|
||||
['', ''], ['', ''],
|
||||
['foo', ''], ['foo', ''],
|
||||
['', <div />], ['', <div />],
|
||||
|
||||
[true, 'foo'], ['foo'],
|
||||
['foo', 0], ['foo', '0'],
|
||||
[1.2, 'foo'], ['1.2', 'foo'],
|
||||
['foo', ''], ['foo', ''],
|
||||
['foo', 'foo'], ['foo', 'foo'],
|
||||
['foo', <div />], ['foo', <div />],
|
||||
[true, 'foo'], ['foo'],
|
||||
['foo', 0], ['foo', '0'],
|
||||
[1.2, 'foo'], ['1.2', 'foo'],
|
||||
['foo', ''], ['foo', ''],
|
||||
['foo', 'foo'], ['foo', 'foo'],
|
||||
['foo', <div />], ['foo', <div />],
|
||||
|
||||
// values separated by an element
|
||||
[true, <div />, true], [<div />],
|
||||
[1.2, <div />, 1.2], ['1.2', <div />, '1.2'],
|
||||
['', <div />, ''], ['', <div />, ''],
|
||||
['foo', <div />, 'foo'], ['foo', <div />, 'foo'],
|
||||
// values separated by an element
|
||||
[true, <div />, true], [<div />],
|
||||
[1.2, <div />, 1.2], ['1.2', <div />, '1.2'],
|
||||
['', <div />, ''], ['', <div />, ''],
|
||||
['foo', <div />, 'foo'], ['foo', <div />, 'foo'],
|
||||
|
||||
[true, 1.2, <div />, '', 'foo'], ['1.2', <div />, '', 'foo'],
|
||||
[1.2, '', <div />, 'foo', true], ['1.2', '', <div />, 'foo'],
|
||||
['', 'foo', <div />, true, 1.2], ['', 'foo', <div />, '1.2'],
|
||||
[true, 1.2, <div />, '', 'foo'], ['1.2', <div />, '', 'foo'],
|
||||
[1.2, '', <div />, 'foo', true], ['1.2', '', <div />, 'foo'],
|
||||
['', 'foo', <div />, true, 1.2], ['', 'foo', <div />, '1.2'],
|
||||
|
||||
[true, 1.2, '', <div />, 'foo', true, 1.2], ['1.2', '', <div />, 'foo', '1.2'],
|
||||
['', 'foo', true, <div />, 1.2, '', 'foo'], ['', 'foo', <div />, '1.2', '', 'foo'],
|
||||
[true, 1.2, '', <div />, 'foo', true, 1.2], ['1.2', '', <div />, 'foo', '1.2'],
|
||||
['', 'foo', true, <div />, 1.2, '', 'foo'], ['', 'foo', <div />, '1.2', '', 'foo'],
|
||||
|
||||
// values inside arrays
|
||||
[[true], [true]], [],
|
||||
[[1.2], [1.2]], ['1.2', '1.2'],
|
||||
[[''], ['']], ['', ''],
|
||||
[['foo'], ['foo']], ['foo', 'foo'],
|
||||
[[<div />], [<div />]], [<div />, <div />],
|
||||
// values inside arrays
|
||||
[[true], [true]], [],
|
||||
[[1.2], [1.2]], ['1.2', '1.2'],
|
||||
[[''], ['']], ['', ''],
|
||||
[['foo'], ['foo']], ['foo', 'foo'],
|
||||
[[<div />], [<div />]], [<div />, <div />],
|
||||
|
||||
[[true, 1.2, <div />], '', 'foo'], ['1.2', <div />, '', 'foo'],
|
||||
[1.2, '', [<div />, 'foo', true]], ['1.2', '', <div />, 'foo'],
|
||||
['', ['foo', <div />, true], 1.2], ['', 'foo', <div />, '1.2'],
|
||||
[[true, 1.2, <div />], '', 'foo'], ['1.2', <div />, '', 'foo'],
|
||||
[1.2, '', [<div />, 'foo', true]], ['1.2', '', <div />, 'foo'],
|
||||
['', ['foo', <div />, true], 1.2], ['', 'foo', <div />, '1.2'],
|
||||
|
||||
[true, [1.2, '', <div />, 'foo'], true, 1.2], ['1.2', '', <div />, 'foo', '1.2'],
|
||||
['', 'foo', [true, <div />, 1.2, ''], 'foo'], ['', 'foo', <div />, '1.2', '', 'foo'],
|
||||
[true, [1.2, '', <div />, 'foo'], true, 1.2], ['1.2', '', <div />, 'foo', '1.2'],
|
||||
['', 'foo', [true, <div />, 1.2, ''], 'foo'], ['', 'foo', <div />, '1.2', '', 'foo'],
|
||||
|
||||
// values inside elements
|
||||
[<div>{true}{1.2}{<div />}</div>, '', 'foo'], [<div />, '', 'foo'],
|
||||
[1.2, '', <div>{<div />}{'foo'}{true}</div>], ['1.2', '', <div />],
|
||||
['', <div>{'foo'}{<div />}{true}</div>, 1.2], ['', <div />, '1.2'],
|
||||
// values inside elements
|
||||
[<div>{true}{1.2}{<div />}</div>, '', 'foo'], [<div />, '', 'foo'],
|
||||
[1.2, '', <div>{<div />}{'foo'}{true}</div>], ['1.2', '', <div />],
|
||||
['', <div>{'foo'}{<div />}{true}</div>, 1.2], ['', <div />, '1.2'],
|
||||
|
||||
[true, <div>{1.2}{''}{<div />}{'foo'}</div>, true, 1.2], [<div />, '1.2'],
|
||||
['', 'foo', <div>{true}{<div />}{1.2}{''}</div>, 'foo'], ['', 'foo', <div />, 'foo'],
|
||||
[true, <div>{1.2}{''}{<div />}{'foo'}</div>, true, 1.2], [<div />, '1.2'],
|
||||
['', 'foo', <div>{true}{<div />}{1.2}{''}</div>, 'foo'], ['', 'foo', <div />, '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', () => {
|
||||
|
|
|
@ -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(<Root hello="world" />, testDocument);
|
||||
expectDeprecationWarningWithFiber(() =>
|
||||
ReactDOM.render(<Root hello="world" />, testDocument),
|
||||
);
|
||||
expect(testDocument.body.innerHTML).toBe('Hello world');
|
||||
|
||||
ReactDOM.render(<Root hello="moon" />, 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(<Root />);
|
||||
const testDocument = getTestDocument(markup);
|
||||
ReactDOM.render(<Root />, testDocument);
|
||||
expectDeprecationWarningWithFiber(() =>
|
||||
ReactDOM.render(<Root />, 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(<Component />);
|
||||
const testDocument = getTestDocument(markup);
|
||||
|
||||
ReactDOM.render(<Component />, testDocument);
|
||||
expectDeprecationWarningWithFiber(() =>
|
||||
ReactDOM.render(<Component />, testDocument),
|
||||
);
|
||||
expect(testDocument.body.innerHTML).toBe('Hello world');
|
||||
|
||||
// This works but is probably a bad idea.
|
||||
ReactDOM.render(<Component2 />, 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(<Component text="Hello world" />, testDocument);
|
||||
expectDeprecationWarningWithFiber(() =>
|
||||
ReactDOM.render(<Component text="Hello world" />, 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(<Component text="Hello world" />, 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(<Component text="Hello world" />, 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 = (
|
||||
<html>
|
||||
<head>
|
||||
|
@ -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(<div>parsnip</div>, container);
|
||||
expect(() => ReactDOM.hydrate(<div>parsnip</div>, container)).toWarnDev(
|
||||
'Expected server HTML to contain a matching <div> in <div>.',
|
||||
);
|
||||
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 <div> in <div>.',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
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(<Component text="Hello world" />, testDocument);
|
||||
expect(() =>
|
||||
ReactDOM.hydrate(<Component text="Hello world" />, 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(<Component text="Hello world" />, testDocument);
|
||||
// getTestDocument() has an extra <meta> that we didn't render.
|
||||
expect(() =>
|
||||
ReactDOM.hydrate(<Component text="Hello world" />, testDocument),
|
||||
).toWarnDev('Did not expect server HTML to contain a <meta> in <head>.');
|
||||
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 <meta> that we didn't render.
|
||||
'Did not expect server HTML to contain a <meta> in <head>.',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('supports findDOMNode on full-page components', () => {
|
||||
|
|
|
@ -431,24 +431,17 @@ describe('ReactDOMServer', () => {
|
|||
}
|
||||
}
|
||||
|
||||
spyOnDev(console, 'error');
|
||||
ReactDOMServer.renderToString(<Foo />);
|
||||
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(<Foo />);
|
||||
expect(markup).toBe('<div>hello</div>');
|
||||
// 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(<Baz />);
|
||||
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(<Baz />);
|
||||
expect(markup).toBe('<div></div>');
|
||||
});
|
||||
|
||||
it('should throw (in dev) when children are mutated during render', () => {
|
||||
spyOnDev(console, 'error');
|
||||
function Wrapper(props) {
|
||||
props.children[1] = <p key={1} />; // Mutation is illegal
|
||||
return <div>{props.children}</div>;
|
||||
|
@ -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 <g>{props.children}</g>;
|
||||
}
|
||||
ReactDOMServer.renderToStaticMarkup(
|
||||
<div>
|
||||
<inPUT />
|
||||
<svg>
|
||||
<CompositeG>
|
||||
<linearGradient />
|
||||
<foreignObject>
|
||||
{/* back to HTML */}
|
||||
<iFrame />
|
||||
</foreignObject>
|
||||
</CompositeG>
|
||||
</svg>
|
||||
</div>,
|
||||
);
|
||||
if (__DEV__) {
|
||||
expect(console.error.calls.count()).toBe(2);
|
||||
expect(console.error.calls.argsFor(0)[0]).toBe(
|
||||
'Warning: <inPUT /> is using uppercase HTML. Always use lowercase ' +
|
||||
'HTML tags in React.',
|
||||
);
|
||||
expect(() =>
|
||||
ReactDOMServer.renderToStaticMarkup(
|
||||
<div>
|
||||
<inPUT />
|
||||
<svg>
|
||||
<CompositeG>
|
||||
<linearGradient />
|
||||
<foreignObject>
|
||||
{/* back to HTML */}
|
||||
<iFrame />
|
||||
</foreignObject>
|
||||
</CompositeG>
|
||||
</svg>
|
||||
</div>,
|
||||
),
|
||||
).toWarnDev([
|
||||
'Warning: <inPUT /> is using uppercase HTML. Always use lowercase ' +
|
||||
'HTML tags in React.',
|
||||
// linearGradient doesn't warn
|
||||
expect(console.error.calls.argsFor(1)[0]).toBe(
|
||||
'Warning: <iFrame /> is using uppercase HTML. Always use lowercase ' +
|
||||
'HTML tags in React.',
|
||||
);
|
||||
}
|
||||
'Warning: <iFrame /> is using uppercase HTML. Always use lowercase ' +
|
||||
'HTML tags in React.',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should warn about contentEditable and children', () => {
|
||||
spyOnDev(console, 'error');
|
||||
ReactDOMServer.renderToString(<div contentEditable={true} children="" />);
|
||||
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(<div contentEditable={true} children="" />),
|
||||
).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', () => {
|
||||
|
|
|
@ -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(<TestComponent name="x" />);
|
||||
element.innerHTML = lastMarkup;
|
||||
|
||||
let instance = ReactDOM.render(<TestComponent name="x" />, element);
|
||||
let instance;
|
||||
|
||||
expect(() => {
|
||||
instance = ReactDOM.render(<TestComponent name="x" />, 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(<TestComponent name="y" />, element);
|
||||
expect(() => {
|
||||
instance = ReactDOM.render(<TestComponent name="y" />, 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(<TestComponent name="y" />, element);
|
||||
expect(() => {
|
||||
instance = ReactDOM.hydrate(<TestComponent name="y" />, 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(
|
||||
<button autoFocus={false}>server</button>,
|
||||
|
@ -251,15 +233,14 @@ describe('ReactDOMServerHydration', () => {
|
|||
expect(element.firstChild.autofocus).toBe(false);
|
||||
|
||||
element.firstChild.focus = jest.fn();
|
||||
ReactDOM.hydrate(<button autoFocus={false}>client</button>, element);
|
||||
|
||||
expect(() =>
|
||||
ReactDOM.hydrate(<button autoFocus={false}>client</button>, 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', () => {
|
||||
|
|
|
@ -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 <div>{props.name}</div>;
|
||||
}
|
||||
|
@ -114,15 +109,15 @@ describe('ReactStatelessComponent', () => {
|
|||
|
||||
const container = document.createElement('div');
|
||||
|
||||
ReactDOM.render(<StatelessComponentWithChildContext name="A" />, 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(
|
||||
<StatelessComponentWithChildContext name="A" />,
|
||||
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 <div>{props.children}</div>;
|
||||
}
|
||||
|
@ -173,29 +166,23 @@ describe('ReactStatelessComponent', () => {
|
|||
}
|
||||
}
|
||||
|
||||
ReactTestUtils.renderIntoDocument(<ParentUsingStringRef />);
|
||||
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(<ParentUsingStringRef />),
|
||||
).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(<ParentUsingStringRef />);
|
||||
if (__DEV__) {
|
||||
expect(console.error.calls.count()).toBe(1);
|
||||
}
|
||||
});
|
||||
|
||||
it('should warn when given a function ref', () => {
|
||||
spyOnDev(console, 'error');
|
||||
|
||||
function Indirection(props) {
|
||||
return <div>{props.children}</div>;
|
||||
}
|
||||
|
@ -215,29 +202,23 @@ describe('ReactStatelessComponent', () => {
|
|||
}
|
||||
}
|
||||
|
||||
ReactTestUtils.renderIntoDocument(<ParentUsingFunctionRef />);
|
||||
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(<ParentUsingFunctionRef />),
|
||||
).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(<ParentUsingFunctionRef />);
|
||||
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(
|
||||
<AnonymousParentUsingJSX />,
|
||||
);
|
||||
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(
|
||||
<AnonymousParentUsingJSX />,
|
||||
);
|
||||
}
|
||||
}).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(<AnonymousParentUsingJSX />);
|
||||
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(
|
||||
<AnonymousParentNotUsingJSX />,
|
||||
let instance2;
|
||||
expect(() => {
|
||||
instance2 = ReactTestUtils.renderIntoDocument(
|
||||
<AnonymousParentNotUsingJSX />,
|
||||
);
|
||||
}).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(<AnonymousParentNotUsingJSX />);
|
||||
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(<AnonymousParentNotUsingJSX />),
|
||||
).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(
|
||||
<NamedParentNotUsingJSX />,
|
||||
let instance3;
|
||||
expect(() => {
|
||||
instance3 = ReactTestUtils.renderIntoDocument(<NamedParentNotUsingJSX />);
|
||||
}).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(<NamedParentNotUsingJSX />);
|
||||
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(<Parent />);
|
||||
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(<Parent />)).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 <div>{[<span />]}</div>;
|
||||
}
|
||||
|
||||
spyOnDev(console, 'error');
|
||||
ReactTestUtils.renderIntoDocument(<Child />);
|
||||
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(<Child />)).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(<Child />);
|
||||
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(<Child />)).toWarnDev(
|
||||
'Warning: Failed prop type: Invalid prop `test` of type `number` ' +
|
||||
'supplied to `Child`, expected `string`.\n' +
|
||||
' in Child (at **)',
|
||||
);
|
||||
});
|
||||
|
||||
it('should receive context', () => {
|
||||
|
|
|
@ -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(<A />);
|
||||
|
||||
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(<A />);
|
||||
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(<A />);
|
||||
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(<A />);
|
||||
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(<A />);
|
||||
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(<A />);
|
||||
|
||||
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(<A />);
|
||||
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(<A />);
|
||||
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(<A />);
|
||||
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(<A />);
|
||||
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(<Foo />, container);
|
||||
expect(() => ReactDOM.render(<Foo />, 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', () => {
|
||||
|
|
|
@ -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',
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 = <Tag>{element}</Tag>;
|
||||
}
|
||||
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'],
|
||||
[
|
||||
|
|
|
@ -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(<div onClick={assignEvent} />, 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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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(<Foo condition={false} />);
|
||||
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(<Foo condition={true} />);
|
||||
ReactNoop.flush();
|
||||
expect(ReactNoop.flush).toWarnDev(
|
||||
'Each child in an array or iterator should have a unique "key" prop.',
|
||||
);
|
||||
|
||||
ReactNoop.render(<Foo condition={false} />);
|
||||
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(<Foo condition={true} />);
|
||||
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.',
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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', () => {
|
|||
<BrokenRender />
|
||||
</ErrorBoundary>,
|
||||
);
|
||||
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', () => {
|
|||
<BrokenRender fail={true} />
|
||||
</ErrorBoundary>,
|
||||
);
|
||||
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(<InvalidType />);
|
||||
expect(() => {
|
||||
ReactNoop.flush();
|
||||
}).toThrowError(
|
||||
expect(() => ReactNoop.render(<InvalidType />)).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(<span prop="hi" />);
|
||||
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', () => {
|
||||
|
|
|
@ -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(<Foo show={true} />);
|
||||
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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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(<SimpleComponent />);
|
||||
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(<SimpleComponent />)).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(<Undef />)).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(<Component />)).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(<Null />)).toThrowError(
|
||||
'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
|
||||
'components, but the provided element type was `null`.',
|
||||
);
|
||||
|
||||
const Arr = [];
|
||||
expect(() => shallowRenderer.render(<Arr />)).toThrowError(
|
||||
'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
|
||||
'components, but the provided element type was `array`.',
|
||||
);
|
||||
|
||||
const Obj = {};
|
||||
expect(() => shallowRenderer.render(<Obj />)).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');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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 <a role="link" />;
|
||||
|
@ -264,7 +260,6 @@ describe('ReactTestRenderer', () => {
|
|||
});
|
||||
|
||||
it('warns correctly for refs on SFCs', () => {
|
||||
spyOnDev(console, 'error');
|
||||
function Bar() {
|
||||
return <div>Hello, world</div>;
|
||||
}
|
||||
|
@ -279,16 +274,12 @@ describe('ReactTestRenderer', () => {
|
|||
}
|
||||
}
|
||||
ReactTestRenderer.create(<Baz />);
|
||||
ReactTestRenderer.create(<Foo />);
|
||||
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(<Foo />)).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', () => {
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue