@samitouri / QOS-React / commits / 9f848f8ebe

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

Brian Vaughn committed Jan 3, 2018 at 13:55 UTC 9f848f8ebec30b3aaa4844ecaef83b014359c5e3
20 files changed +463 -731
packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js
-2
@@ -423,8 +423,6 @@ describe('ReactCompositeComponent-state', () => {
423 });
424
425 it('should treat assigning to this.state inside cWM as a replaceState, with a warning', () => {
426 - spyOnDev(console, 'error');
427 -
426 let ops = [];
427 class Test extends React.Component {
428 state = {step: 1, extra: true};
packages/react-dom/src/__tests__/ReactErrorBoundaries-test.js
+10 -4
@@ -1909,8 +1909,6 @@ describe('ReactErrorBoundaries', () => {
1909 });
1910
1911 it('discards a bad root if the root component fails', () => {
1912 - spyOnDev(console, 'error');
1913 -
1912 const X = null;
1913 const Y = undefined;
1914 let err1;
@@ -1918,13 +1916,21 @@ describe('ReactErrorBoundaries', () => {
1916
1917 try {
1918 let container = document.createElement('div');
1921 - ReactDOM.render(<X />, container);
1919 + expect(() => ReactDOM.render(<X />, container)).toWarnDev(
1920 + 'React.createElement: type is invalid -- expected a string ' +
1921 + '(for built-in components) or a class/function ' +
1922 + '(for composite components) but got: null.',
1923 + );
1924 } catch (err) {
1925 err1 = err;
1926 }
1927 try {
1928 let container = document.createElement('div');
1927 - ReactDOM.render(<Y />, container);
1929 + expect(() => ReactDOM.render(<Y />, container)).toWarnDev(
1930 + 'React.createElement: type is invalid -- expected a string ' +
1931 + '(for built-in components) or a class/function ' +
1932 + '(for composite components) but got: undefined.',
1933 + );
1934 } catch (err) {
1935 err2 = err;
1936 }
packages/react-dom/src/__tests__/ReactMultiChildText-test.js
+91 -97
@@ -73,104 +73,98 @@ const expectChildren = function(container, children) {
73 */
74 describe('ReactMultiChildText', () => {
75 it('should correctly handle all possible children for render and update', () => {
76 - spyOnDev(console, 'error');
77 - // prettier-ignore
78 - testAllPermutations([
79 - // basic values
80 - undefined, [],
81 - null, [],
82 - false, [],
83 - true, [],
84 - 0, '0',
85 - 1.2, '1.2',
86 - '', '',
87 - 'foo', 'foo',
88 -
89 - [], [],
90 - [undefined], [],
91 - [null], [],
92 - [false], [],
93 - [true], [],
94 - [0], ['0'],
95 - [1.2], ['1.2'],
96 - [''], [''],
97 - ['foo'], ['foo'],
98 - [<div />], [<div />],
99 -
100 - // two adjacent values
101 - [true, 0], ['0'],
102 - [0, 0], ['0', '0'],
103 - [1.2, 0], ['1.2', '0'],
104 - [0, ''], ['0', ''],
105 - ['foo', 0], ['foo', '0'],
106 - [0, <div />], ['0', <div />],
107 -
108 - [true, 1.2], ['1.2'],
109 - [1.2, 0], ['1.2', '0'],
110 - [1.2, 1.2], ['1.2', '1.2'],
111 - [1.2, ''], ['1.2', ''],
112 - ['foo', 1.2], ['foo', '1.2'],
113 - [1.2, <div />], ['1.2', <div />],
114 -
115 - [true, ''], [''],
116 - ['', 0], ['', '0'],
117 - [1.2, ''], ['1.2', ''],
118 - ['', ''], ['', ''],
119 - ['foo', ''], ['foo', ''],
120 - ['', <div />], ['', <div />],
121 -
122 - [true, 'foo'], ['foo'],
123 - ['foo', 0], ['foo', '0'],
124 - [1.2, 'foo'], ['1.2', 'foo'],
125 - ['foo', ''], ['foo', ''],
126 - ['foo', 'foo'], ['foo', 'foo'],
127 - ['foo', <div />], ['foo', <div />],
128 -
129 - // values separated by an element
130 - [true, <div />, true], [<div />],
131 - [1.2, <div />, 1.2], ['1.2', <div />, '1.2'],
132 - ['', <div />, ''], ['', <div />, ''],
133 - ['foo', <div />, 'foo'], ['foo', <div />, 'foo'],
134 -
135 - [true, 1.2, <div />, '', 'foo'], ['1.2', <div />, '', 'foo'],
136 - [1.2, '', <div />, 'foo', true], ['1.2', '', <div />, 'foo'],
137 - ['', 'foo', <div />, true, 1.2], ['', 'foo', <div />, '1.2'],
138 -
139 - [true, 1.2, '', <div />, 'foo', true, 1.2], ['1.2', '', <div />, 'foo', '1.2'],
140 - ['', 'foo', true, <div />, 1.2, '', 'foo'], ['', 'foo', <div />, '1.2', '', 'foo'],
141 -
142 - // values inside arrays
143 - [[true], [true]], [],
144 - [[1.2], [1.2]], ['1.2', '1.2'],
145 - [[''], ['']], ['', ''],
146 - [['foo'], ['foo']], ['foo', 'foo'],
147 - [[<div />], [<div />]], [<div />, <div />],
148 -
149 - [[true, 1.2, <div />], '', 'foo'], ['1.2', <div />, '', 'foo'],
150 - [1.2, '', [<div />, 'foo', true]], ['1.2', '', <div />, 'foo'],
151 - ['', ['foo', <div />, true], 1.2], ['', 'foo', <div />, '1.2'],
152 -
153 - [true, [1.2, '', <div />, 'foo'], true, 1.2], ['1.2', '', <div />, 'foo', '1.2'],
154 - ['', 'foo', [true, <div />, 1.2, ''], 'foo'], ['', 'foo', <div />, '1.2', '', 'foo'],
155 -
156 - // values inside elements
157 - [<div>{true}{1.2}{<div />}</div>, '', 'foo'], [<div />, '', 'foo'],
158 - [1.2, '', <div>{<div />}{'foo'}{true}</div>], ['1.2', '', <div />],
159 - ['', <div>{'foo'}{<div />}{true}</div>, 1.2], ['', <div />, '1.2'],
160 -
161 - [true, <div>{1.2}{''}{<div />}{'foo'}</div>, true, 1.2], [<div />, '1.2'],
162 - ['', 'foo', <div>{true}{<div />}{1.2}{''}</div>, 'foo'], ['', 'foo', <div />, 'foo'],
76 + expect(() => {
77 + // prettier-ignore
78 + testAllPermutations([
79 + // basic values
80 + undefined, [],
81 + null, [],
82 + false, [],
83 + true, [],
84 + 0, '0',
85 + 1.2, '1.2',
86 + '', '',
87 + 'foo', 'foo',
88 +
89 + [], [],
90 + [undefined], [],
91 + [null], [],
92 + [false], [],
93 + [true], [],
94 + [0], ['0'],
95 + [1.2], ['1.2'],
96 + [''], [''],
97 + ['foo'], ['foo'],
98 + [<div />], [<div />],
99 +
100 + // two adjacent values
101 + [true, 0], ['0'],
102 + [0, 0], ['0', '0'],
103 + [1.2, 0], ['1.2', '0'],
104 + [0, ''], ['0', ''],
105 + ['foo', 0], ['foo', '0'],
106 + [0, <div />], ['0', <div />],
107 +
108 + [true, 1.2], ['1.2'],
109 + [1.2, 0], ['1.2', '0'],
110 + [1.2, 1.2], ['1.2', '1.2'],
111 + [1.2, ''], ['1.2', ''],
112 + ['foo', 1.2], ['foo', '1.2'],
113 + [1.2, <div />], ['1.2', <div />],
114 +
115 + [true, ''], [''],
116 + ['', 0], ['', '0'],
117 + [1.2, ''], ['1.2', ''],
118 + ['', ''], ['', ''],
119 + ['foo', ''], ['foo', ''],
120 + ['', <div />], ['', <div />],
121 +
122 + [true, 'foo'], ['foo'],
123 + ['foo', 0], ['foo', '0'],
124 + [1.2, 'foo'], ['1.2', 'foo'],
125 + ['foo', ''], ['foo', ''],
126 + ['foo', 'foo'], ['foo', 'foo'],
127 + ['foo', <div />], ['foo', <div />],
128 +
129 + // values separated by an element
130 + [true, <div />, true], [<div />],
131 + [1.2, <div />, 1.2], ['1.2', <div />, '1.2'],
132 + ['', <div />, ''], ['', <div />, ''],
133 + ['foo', <div />, 'foo'], ['foo', <div />, 'foo'],
134 +
135 + [true, 1.2, <div />, '', 'foo'], ['1.2', <div />, '', 'foo'],
136 + [1.2, '', <div />, 'foo', true], ['1.2', '', <div />, 'foo'],
137 + ['', 'foo', <div />, true, 1.2], ['', 'foo', <div />, '1.2'],
138 +
139 + [true, 1.2, '', <div />, 'foo', true, 1.2], ['1.2', '', <div />, 'foo', '1.2'],
140 + ['', 'foo', true, <div />, 1.2, '', 'foo'], ['', 'foo', <div />, '1.2', '', 'foo'],
141 +
142 + // values inside arrays
143 + [[true], [true]], [],
144 + [[1.2], [1.2]], ['1.2', '1.2'],
145 + [[''], ['']], ['', ''],
146 + [['foo'], ['foo']], ['foo', 'foo'],
147 + [[<div />], [<div />]], [<div />, <div />],
148 +
149 + [[true, 1.2, <div />], '', 'foo'], ['1.2', <div />, '', 'foo'],
150 + [1.2, '', [<div />, 'foo', true]], ['1.2', '', <div />, 'foo'],
151 + ['', ['foo', <div />, true], 1.2], ['', 'foo', <div />, '1.2'],
152 +
153 + [true, [1.2, '', <div />, 'foo'], true, 1.2], ['1.2', '', <div />, 'foo', '1.2'],
154 + ['', 'foo', [true, <div />, 1.2, ''], 'foo'], ['', 'foo', <div />, '1.2', '', 'foo'],
155 +
156 + // values inside elements
157 + [<div>{true}{1.2}{<div />}</div>, '', 'foo'], [<div />, '', 'foo'],
158 + [1.2, '', <div>{<div />}{'foo'}{true}</div>], ['1.2', '', <div />],
159 + ['', <div>{'foo'}{<div />}{true}</div>, 1.2], ['', <div />, '1.2'],
160 +
161 + [true, <div>{1.2}{''}{<div />}{'foo'}</div>, true, 1.2], [<div />, '1.2'],
162 + ['', 'foo', <div>{true}{<div />}{1.2}{''}</div>, 'foo'], ['', 'foo', <div />, 'foo'],
163 + ]);
164 + }).toWarnDev([
165 + 'Warning: Each child in an array or iterator should have a unique "key" prop.',
166 + 'Warning: Each child in an array or iterator should have a unique "key" prop.',
167 ]);
164 -
165 - if (__DEV__) {
166 - expect(console.error.calls.count()).toBe(2);
167 - expect(console.error.calls.argsFor(0)[0]).toContain(
168 - 'Warning: Each child in an array or iterator should have a unique "key" prop.',
169 - );
170 - expect(console.error.calls.argsFor(1)[0]).toContain(
171 - 'Warning: Each child in an array or iterator should have a unique "key" prop.',
172 - );
173 - }
168 });
169
170 it('should throw if rendering both HTML and children', () => {
packages/react-dom/src/__tests__/ReactRenderDocument-test.js
+37 -62
@@ -34,19 +34,15 @@ describe('rendering React components at document', () => {
34 });
35
36 describe('with old implicit hydration API', () => {
37 - function expectDeprecationWarningWithFiber() {
38 - if (__DEV__) {
39 - expect(console.warn.calls.count()).toBe(1);
40 - expect(console.warn.calls.argsFor(0)[0]).toContain(
41 - 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
42 - 'will stop working in React v17. Replace the ReactDOM.render() call ' +
43 - 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
44 - );
45 - }
37 + function expectDeprecationWarningWithFiber(callback) {
38 + expect(callback).toLowPriorityWarnDev(
39 + 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
40 + 'will stop working in React v17. Replace the ReactDOM.render() call ' +
41 + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
42 + );
43 }
44
45 it('should be able to adopt server markup', () => {
49 - spyOnDev(console, 'warn');
46 class Root extends React.Component {
47 render() {
48 return (
@@ -64,18 +60,18 @@ describe('rendering React components at document', () => {
60 const testDocument = getTestDocument(markup);
61 const body = testDocument.body;
62
67 - ReactDOM.render(<Root hello="world" />, testDocument);
63 + expectDeprecationWarningWithFiber(() =>
64 + ReactDOM.render(<Root hello="world" />, testDocument),
65 + );
66 expect(testDocument.body.innerHTML).toBe('Hello world');
67
68 ReactDOM.render(<Root hello="moon" />, testDocument);
69 expect(testDocument.body.innerHTML).toBe('Hello moon');
70
71 expect(body === testDocument.body).toBe(true);
74 - expectDeprecationWarningWithFiber();
72 });
73
74 it('should not be able to unmount component from document node', () => {
78 - spyOnDev(console, 'warn');
75 class Root extends React.Component {
76 render() {
77 return (
@@ -91,18 +87,17 @@ describe('rendering React components at document', () => {
87
88 const markup = ReactDOMServer.renderToString(<Root />);
89 const testDocument = getTestDocument(markup);
94 - ReactDOM.render(<Root />, testDocument);
90 + expectDeprecationWarningWithFiber(() =>
91 + ReactDOM.render(<Root />, testDocument),
92 + );
93 expect(testDocument.body.innerHTML).toBe('Hello world');
94
95 // In Fiber this actually works. It might not be a good idea though.
96 ReactDOM.unmountComponentAtNode(testDocument);
97 expect(testDocument.firstChild).toBe(null);
100 -
101 - expectDeprecationWarningWithFiber();
98 });
99
100 it('should not be able to switch root constructors', () => {
105 - spyOnDev(console, 'warn');
101 class Component extends React.Component {
102 render() {
103 return (
@@ -132,18 +127,18 @@ describe('rendering React components at document', () => {
127 const markup = ReactDOMServer.renderToString(<Component />);
128 const testDocument = getTestDocument(markup);
129
135 - ReactDOM.render(<Component />, testDocument);
130 + expectDeprecationWarningWithFiber(() =>
131 + ReactDOM.render(<Component />, testDocument),
132 + );
133 expect(testDocument.body.innerHTML).toBe('Hello world');
134
135 // This works but is probably a bad idea.
136 ReactDOM.render(<Component2 />, testDocument);
137
138 expect(testDocument.body.innerHTML).toBe('Goodbye world');
142 - expectDeprecationWarningWithFiber();
139 });
140
141 it('should be able to mount into document', () => {
146 - spyOnDev(console, 'warn');
142 class Component extends React.Component {
143 render() {
144 return (
@@ -162,10 +157,11 @@ describe('rendering React components at document', () => {
157 );
158 const testDocument = getTestDocument(markup);
159
165 - ReactDOM.render(<Component text="Hello world" />, testDocument);
160 + expectDeprecationWarningWithFiber(() =>
161 + ReactDOM.render(<Component text="Hello world" />, testDocument),
162 + );
163
164 expect(testDocument.body.innerHTML).toBe('Hello world');
168 - expectDeprecationWarningWithFiber();
165 });
166
167 it('renders over an existing text child without throwing', () => {
@@ -197,22 +193,15 @@ describe('rendering React components at document', () => {
193 );
194 const testDocument = getTestDocument(markup);
195
200 - spyOnDev(console, 'warn');
201 - spyOnDev(console, 'error');
202 - ReactDOM.render(<Component text="Hello world" />, testDocument);
203 - expect(testDocument.body.innerHTML).toBe('Hello world');
204 - if (__DEV__) {
205 - expect(console.warn.calls.count()).toBe(1);
206 - expect(console.warn.calls.argsFor(0)[0]).toContain(
196 + expect(() => {
197 + expect(() =>
198 + ReactDOM.render(<Component text="Hello world" />, testDocument),
199 + ).toLowPriorityWarnDev(
200 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
201 'will stop working in React v17. Replace the ReactDOM.render() call ' +
202 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
203 );
211 - expect(console.error.calls.count()).toBe(1);
212 - expect(console.error.calls.argsFor(0)[0]).toContain(
213 - 'Warning: Text content did not match.',
214 - );
215 - }
204 + }).toWarnDev('Warning: Text content did not match.');
205 });
206
207 it('should throw on full document render w/ no markup', () => {
@@ -239,7 +228,6 @@ describe('rendering React components at document', () => {
228 });
229
230 it('supports findDOMNode on full-page components', () => {
242 - spyOnDev(console, 'warn');
231 const tree = (
232 <html>
233 <head>
@@ -251,10 +239,12 @@ describe('rendering React components at document', () => {
239
240 const markup = ReactDOMServer.renderToString(tree);
241 const testDocument = getTestDocument(markup);
254 - const component = ReactDOM.render(tree, testDocument);
242 + let component;
243 + expectDeprecationWarningWithFiber(() => {
244 + component = ReactDOM.render(tree, testDocument);
245 + });
246 expect(testDocument.body.innerHTML).toBe('Hello world');
247 expect(ReactDOM.findDOMNode(component).tagName).toBe('HTML');
257 - expectDeprecationWarningWithFiber();
248 });
249 });
250
@@ -375,17 +365,12 @@ describe('rendering React components at document', () => {
365 });
366
367 it('renders over an existing text child without throwing', () => {
378 - spyOnDev(console, 'error');
368 const container = document.createElement('div');
369 container.textContent = 'potato';
381 - ReactDOM.hydrate(<div>parsnip</div>, container);
370 + expect(() => ReactDOM.hydrate(<div>parsnip</div>, container)).toWarnDev(
371 + 'Expected server HTML to contain a matching <div> in <div>.',
372 + );
373 expect(container.textContent).toBe('parsnip');
383 - if (__DEV__) {
384 - expect(console.error.calls.count()).toBe(1);
385 - expect(console.error.calls.argsFor(0)[0]).toContain(
386 - 'Expected server HTML to contain a matching <div> in <div>.',
387 - );
388 - }
374 });
375
376 it('should give helpful errors on state desync', () => {
@@ -407,19 +392,13 @@ describe('rendering React components at document', () => {
392 );
393 const testDocument = getTestDocument(markup);
394
410 - spyOnDev(console, 'error');
411 - ReactDOM.hydrate(<Component text="Hello world" />, testDocument);
395 + expect(() =>
396 + ReactDOM.hydrate(<Component text="Hello world" />, testDocument),
397 + ).toWarnDev('Warning: Text content did not match.');
398 expect(testDocument.body.innerHTML).toBe('Hello world');
413 - if (__DEV__) {
414 - expect(console.error.calls.count()).toBe(1);
415 - expect(console.error.calls.argsFor(0)[0]).toContain(
416 - 'Warning: Text content did not match.',
417 - );
418 - }
399 });
400
401 it('should render w/ no markup to full document', () => {
422 - spyOnDev(console, 'error');
402 const testDocument = getTestDocument();
403
404 class Component extends React.Component {
@@ -435,15 +414,11 @@ describe('rendering React components at document', () => {
414 }
415 }
416
438 - ReactDOM.hydrate(<Component text="Hello world" />, testDocument);
417 + // getTestDocument() has an extra <meta> that we didn't render.
418 + expect(() =>
419 + ReactDOM.hydrate(<Component text="Hello world" />, testDocument),
420 + ).toWarnDev('Did not expect server HTML to contain a <meta> in <head>.');
421 expect(testDocument.body.innerHTML).toBe('Hello world');
440 - if (__DEV__) {
441 - expect(console.error.calls.count()).toBe(1);
442 - expect(console.error.calls.argsFor(0)[0]).toContain(
443 - // getTestDocument() has an extra <meta> that we didn't render.
444 - 'Did not expect server HTML to contain a <meta> in <head>.',
445 - );
446 - }
422 });
423
424 it('supports findDOMNode on full-page components', () => {
packages/react-dom/src/__tests__/ReactServerRendering-test.js
+40 -61
@@ -431,24 +431,17 @@ describe('ReactDOMServer', () => {
431 }
432 }
433
434 - spyOnDev(console, 'error');
434 ReactDOMServer.renderToString(<Foo />);
436 - jest.runOnlyPendingTimers();
437 - if (__DEV__) {
438 - expect(console.error.calls.count()).toBe(1);
439 - expect(console.error.calls.mostRecent().args[0]).toBe(
440 - 'Warning: setState(...): Can only update a mounting component.' +
441 - ' This usually means you called setState() outside componentWillMount() on the server.' +
442 - ' This is a no-op.\n\nPlease check the code for the Foo component.',
443 - );
444 - }
435 + expect(() => jest.runOnlyPendingTimers()).toWarnDev(
436 + 'Warning: setState(...): Can only update a mounting component.' +
437 + ' This usually means you called setState() outside componentWillMount() on the server.' +
438 + ' This is a no-op.\n\nPlease check the code for the Foo component.',
439 + );
440
441 const markup = ReactDOMServer.renderToStaticMarkup(<Foo />);
442 expect(markup).toBe('<div>hello</div>');
443 + // No additional warnings are expected
444 jest.runOnlyPendingTimers();
449 - if (__DEV__) {
450 - expect(console.error.calls.count()).toBe(1);
451 - }
445 });
446
447 it('warns with a no-op when an async forceUpdate is triggered', () => {
@@ -465,23 +458,17 @@ describe('ReactDOMServer', () => {
458 }
459 }
460
468 - spyOnDev(console, 'error');
461 ReactDOMServer.renderToString(<Baz />);
470 - jest.runOnlyPendingTimers();
471 - if (__DEV__) {
472 - expect(console.error.calls.count()).toBe(1);
473 - expect(console.error.calls.mostRecent().args[0]).toBe(
474 - 'Warning: forceUpdate(...): Can only update a mounting component. ' +
475 - 'This usually means you called forceUpdate() outside componentWillMount() on the server. ' +
476 - 'This is a no-op.\n\nPlease check the code for the Baz component.',
477 - );
478 - }
462 + expect(() => jest.runOnlyPendingTimers()).toWarnDev(
463 + 'Warning: forceUpdate(...): Can only update a mounting component. ' +
464 + 'This usually means you called forceUpdate() outside componentWillMount() on the server. ' +
465 + 'This is a no-op.\n\nPlease check the code for the Baz component.',
466 + );
467 const markup = ReactDOMServer.renderToStaticMarkup(<Baz />);
468 expect(markup).toBe('<div></div>');
469 });
470
471 it('should throw (in dev) when children are mutated during render', () => {
484 - spyOnDev(console, 'error');
472 function Wrapper(props) {
473 props.children[1] = <p key={1} />; // Mutation is illegal
474 return <div>{props.children}</div>;
@@ -510,51 +497,43 @@ describe('ReactDOMServer', () => {
497 });
498
499 it('warns about lowercase html but not in svg tags', () => {
513 - spyOnDev(console, 'error');
500 function CompositeG(props) {
501 // Make sure namespace passes through composites
502 return <g>{props.children}</g>;
503 }
518 - ReactDOMServer.renderToStaticMarkup(
519 - <div>
520 - <inPUT />
521 - <svg>
522 - <CompositeG>
523 - <linearGradient />
524 - <foreignObject>
525 - {/* back to HTML */}
526 - <iFrame />
527 - </foreignObject>
528 - </CompositeG>
529 - </svg>
530 - </div>,
531 - );
532 - if (__DEV__) {
533 - expect(console.error.calls.count()).toBe(2);
534 - expect(console.error.calls.argsFor(0)[0]).toBe(
535 - 'Warning: <inPUT /> is using uppercase HTML. Always use lowercase ' +
536 - 'HTML tags in React.',
537 - );
504 + expect(() =>
505 + ReactDOMServer.renderToStaticMarkup(
506 + <div>
507 + <inPUT />
508 + <svg>
509 + <CompositeG>
510 + <linearGradient />
511 + <foreignObject>
512 + {/* back to HTML */}
513 + <iFrame />
514 + </foreignObject>
515 + </CompositeG>
516 + </svg>
517 + </div>,
518 + ),
519 + ).toWarnDev([
520 + 'Warning: <inPUT /> is using uppercase HTML. Always use lowercase ' +
521 + 'HTML tags in React.',
522 // linearGradient doesn't warn
539 - expect(console.error.calls.argsFor(1)[0]).toBe(
540 - 'Warning: <iFrame /> is using uppercase HTML. Always use lowercase ' +
541 - 'HTML tags in React.',
542 - );
543 - }
523 + 'Warning: <iFrame /> is using uppercase HTML. Always use lowercase ' +
524 + 'HTML tags in React.',
525 + ]);
526 });
527
528 it('should warn about contentEditable and children', () => {
547 - spyOnDev(console, 'error');
548 - ReactDOMServer.renderToString(<div contentEditable={true} children="" />);
549 - if (__DEV__) {
550 - expect(console.error.calls.count()).toBe(1);
551 - expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
552 - 'Warning: A component is `contentEditable` and contains `children` ' +
553 - 'managed by React. It is now your responsibility to guarantee that ' +
554 - 'none of those nodes are unexpectedly modified or duplicated. This ' +
555 - 'is probably not intentional.\n in div (at **)',
556 - );
557 - }
529 + expect(() =>
530 + ReactDOMServer.renderToString(<div contentEditable={true} children="" />),
531 + ).toWarnDev(
532 + 'Warning: A component is `contentEditable` and contains `children` ' +
533 + 'managed by React. It is now your responsibility to guarantee that ' +
534 + 'none of those nodes are unexpectedly modified or duplicated. This ' +
535 + 'is probably not intentional.\n in div (at **)',
536 + );
537 });
538
539 it('should throw rendering call/return on the server', () => {
packages/react-dom/src/__tests__/ReactServerRenderingHydration.js
+21 -40
@@ -26,9 +26,6 @@ describe('ReactDOMServerHydration', () => {
26 });
27
28 it('should have the correct mounting behavior (old hydrate API)', () => {
29 - spyOnDev(console, 'warn');
30 - spyOnDev(console, 'error');
31 -
29 let mountCount = 0;
30 let numClicks = 0;
31
@@ -78,17 +75,16 @@ describe('ReactDOMServerHydration', () => {
75 lastMarkup = ReactDOMServer.renderToString(<TestComponent name="x" />);
76 element.innerHTML = lastMarkup;
77
81 - let instance = ReactDOM.render(<TestComponent name="x" />, element);
78 + let instance;
79 +
80 + expect(() => {
81 + instance = ReactDOM.render(<TestComponent name="x" />, element);
82 + }).toLowPriorityWarnDev(
83 + 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
84 + 'will stop working in React v17. Replace the ReactDOM.render() call ' +
85 + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
86 + );
87 expect(mountCount).toEqual(3);
83 - if (__DEV__) {
84 - expect(console.warn.calls.count()).toBe(1);
85 - expect(console.warn.calls.argsFor(0)[0]).toContain(
86 - 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
87 - 'will stop working in React v17. Replace the ReactDOM.render() call ' +
88 - 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
89 - );
90 - console.warn.calls.reset();
91 - }
88 expect(element.innerHTML).toBe(lastMarkup);
89
90 // Ensure the events system works after mount into server markup
@@ -102,15 +98,10 @@ describe('ReactDOMServerHydration', () => {
98 // Now simulate a situation where the app is not idempotent. React should
99 // warn but do the right thing.
100 element.innerHTML = lastMarkup;
105 - instance = ReactDOM.render(<TestComponent name="y" />, element);
101 + expect(() => {
102 + instance = ReactDOM.render(<TestComponent name="y" />, element);
103 + }).toWarnDev('Text content did not match. Server: "x" Client: "y"');
104 expect(mountCount).toEqual(4);
107 - if (__DEV__) {
108 - expect(console.error.calls.count()).toBe(1);
109 - expect(console.error.calls.argsFor(0)[0]).toContain(
110 - 'Text content did not match. Server: "x" Client: "y"',
111 - );
112 - console.error.calls.reset();
113 - }
105 expect(element.innerHTML.length > 0).toBe(true);
106 expect(element.innerHTML).not.toEqual(lastMarkup);
107
@@ -118,15 +109,9 @@ describe('ReactDOMServerHydration', () => {
109 expect(numClicks).toEqual(1);
110 ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
111 expect(numClicks).toEqual(2);
121 - if (__DEV__) {
122 - expect(console.warn.calls.count()).toBe(0);
123 - expect(console.error.calls.count()).toBe(0);
124 - }
112 });
113
114 it('should have the correct mounting behavior (new hydrate API)', () => {
128 - spyOnDev(console, 'error');
129 -
115 let mountCount = 0;
116 let numClicks = 0;
117
@@ -191,11 +176,10 @@ describe('ReactDOMServerHydration', () => {
176 // Now simulate a situation where the app is not idempotent. React should
177 // warn but do the right thing.
178 element.innerHTML = lastMarkup;
194 - instance = ReactDOM.hydrate(<TestComponent name="y" />, element);
179 + expect(() => {
180 + instance = ReactDOM.hydrate(<TestComponent name="y" />, element);
181 + }).toWarnDev('Text content did not match. Server: "x" Client: "y"');
182 expect(mountCount).toEqual(4);
196 - if (__DEV__) {
197 - expect(console.error.calls.count()).toBe(1);
198 - }
183 expect(element.innerHTML.length > 0).toBe(true);
184 expect(element.innerHTML).not.toEqual(lastMarkup);
185
@@ -242,8 +226,6 @@ describe('ReactDOMServerHydration', () => {
226
227 // Regression test for https://github.com/facebook/react/issues/11726
228 it('should not focus on either server or client with autofocus={false} even if there is a markup mismatch', () => {
245 - spyOnDev(console, 'error');
246 -
229 const element = document.createElement('div');
230 element.innerHTML = ReactDOMServer.renderToString(
231 <button autoFocus={false}>server</button>,
@@ -251,15 +233,14 @@ describe('ReactDOMServerHydration', () => {
233 expect(element.firstChild.autofocus).toBe(false);
234
235 element.firstChild.focus = jest.fn();
254 - ReactDOM.hydrate(<button autoFocus={false}>client</button>, element);
236 +
237 + expect(() =>
238 + ReactDOM.hydrate(<button autoFocus={false}>client</button>, element),
239 + ).toWarnDev(
240 + 'Warning: Text content did not match. Server: "server" Client: "client"',
241 + );
242
243 expect(element.firstChild.focus).not.toHaveBeenCalled();
257 - if (__DEV__) {
258 - expect(console.error.calls.count()).toBe(1);
259 - expect(console.error.calls.argsFor(0)[0]).toBe(
260 - 'Warning: Text content did not match. Server: "server" Client: "client"',
261 - );
262 - }
244 });
245
246 it('should throw rendering portals on the server', () => {
packages/react-dom/src/__tests__/ReactStatelessComponent-test.js
+75 -135
@@ -19,10 +19,6 @@ function StatelessComponent(props) {
19 }
20
21 describe('ReactStatelessComponent', () => {
22 - function normalizeCodeLocInfo(str) {
23 - return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
24 - }
25 -
22 beforeEach(() => {
23 jest.resetModuleRegistry();
24 PropTypes = require('prop-types');
@@ -103,7 +99,6 @@ describe('ReactStatelessComponent', () => {
99 });
100
101 it('should warn for childContextTypes on a functional component', () => {
106 - spyOnDev(console, 'error');
102 function StatelessComponentWithChildContext(props) {
103 return <div>{props.name}</div>;
104 }
@@ -114,15 +109,15 @@ describe('ReactStatelessComponent', () => {
109
110 const container = document.createElement('div');
111
117 - ReactDOM.render(<StatelessComponentWithChildContext name="A" />, container);
118 -
119 - if (__DEV__) {
120 - expect(console.error.calls.count()).toBe(1);
121 - expect(console.error.calls.argsFor(0)[0]).toContain(
122 - 'StatelessComponentWithChildContext(...): childContextTypes cannot ' +
123 - 'be defined on a functional component.',
124 - );
125 - }
112 + expect(() =>
113 + ReactDOM.render(
114 + <StatelessComponentWithChildContext name="A" />,
115 + container,
116 + ),
117 + ).toWarnDev(
118 + 'StatelessComponentWithChildContext(...): childContextTypes cannot ' +
119 + 'be defined on a functional component.',
120 + );
121 });
122
123 it('should throw when stateless component returns undefined', () => {
@@ -157,8 +152,6 @@ describe('ReactStatelessComponent', () => {
152 });
153
154 it('should warn when given a string ref', () => {
160 - spyOnDev(console, 'error');
161 -
155 function Indirection(props) {
156 return <div>{props.children}</div>;
157 }
@@ -173,29 +166,23 @@ describe('ReactStatelessComponent', () => {
166 }
167 }
168
176 - ReactTestUtils.renderIntoDocument(<ParentUsingStringRef />);
177 - if (__DEV__) {
178 - expect(console.error.calls.count()).toBe(1);
179 - expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
180 - 'Warning: Stateless function components cannot be given refs. ' +
181 - 'Attempts to access this ref will fail.\n\nCheck the render method ' +
182 - 'of `ParentUsingStringRef`.\n' +
183 - ' in StatelessComponent (at **)\n' +
184 - ' in div (at **)\n' +
185 - ' in Indirection (at **)\n' +
186 - ' in ParentUsingStringRef (at **)',
187 - );
188 - }
169 + expect(() =>
170 + ReactTestUtils.renderIntoDocument(<ParentUsingStringRef />),
171 + ).toWarnDev(
172 + 'Warning: Stateless function components cannot be given refs. ' +
173 + 'Attempts to access this ref will fail.\n\nCheck the render method ' +
174 + 'of `ParentUsingStringRef`.\n' +
175 + ' in StatelessComponent (at **)\n' +
176 + ' in div (at **)\n' +
177 + ' in Indirection (at **)\n' +
178 + ' in ParentUsingStringRef (at **)',
179 + );
180
181 + // No additional warnings should be logged
182 ReactTestUtils.renderIntoDocument(<ParentUsingStringRef />);
191 - if (__DEV__) {
192 - expect(console.error.calls.count()).toBe(1);
193 - }
183 });
184
185 it('should warn when given a function ref', () => {
197 - spyOnDev(console, 'error');
198 -
186 function Indirection(props) {
187 return <div>{props.children}</div>;
188 }
@@ -215,29 +202,23 @@ describe('ReactStatelessComponent', () => {
202 }
203 }
204
218 - ReactTestUtils.renderIntoDocument(<ParentUsingFunctionRef />);
219 - if (__DEV__) {
220 - expect(console.error.calls.count()).toBe(1);
221 - expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
222 - 'Warning: Stateless function components cannot be given refs. ' +
223 - 'Attempts to access this ref will fail.\n\nCheck the render method ' +
224 - 'of `ParentUsingFunctionRef`.\n' +
225 - ' in StatelessComponent (at **)\n' +
226 - ' in div (at **)\n' +
227 - ' in Indirection (at **)\n' +
228 - ' in ParentUsingFunctionRef (at **)',
229 - );
230 - }
205 + expect(() =>
206 + ReactTestUtils.renderIntoDocument(<ParentUsingFunctionRef />),
207 + ).toWarnDev(
208 + 'Warning: Stateless function components cannot be given refs. ' +
209 + 'Attempts to access this ref will fail.\n\nCheck the render method ' +
210 + 'of `ParentUsingFunctionRef`.\n' +
211 + ' in StatelessComponent (at **)\n' +
212 + ' in div (at **)\n' +
213 + ' in Indirection (at **)\n' +
214 + ' in ParentUsingFunctionRef (at **)',
215 + );
216
217 + // No additional warnings should be logged
218 ReactTestUtils.renderIntoDocument(<ParentUsingFunctionRef />);
233 - if (__DEV__) {
234 - expect(console.error.calls.count()).toBe(1);
235 - }
219 });
220
221 it('deduplicates ref warnings based on element or owner', () => {
239 - spyOnDev(console, 'error');
240 -
222 // When owner uses JSX, we can use exact line location to dedupe warnings
223 class AnonymousParentUsingJSX extends React.Component {
224 render() {
@@ -246,23 +227,19 @@ describe('ReactStatelessComponent', () => {
227 }
228 Object.defineProperty(AnonymousParentUsingJSX, 'name', {value: undefined});
229
249 - const instance1 = ReactTestUtils.renderIntoDocument(
250 - <AnonymousParentUsingJSX />,
251 - );
252 - if (__DEV__) {
253 - expect(console.error.calls.count()).toBe(1);
254 - expect(console.error.calls.argsFor(0)[0]).toContain(
255 - 'Warning: Stateless function components cannot be given refs.',
230 + let instance1;
231 +
232 + expect(() => {
233 + instance1 = ReactTestUtils.renderIntoDocument(
234 + <AnonymousParentUsingJSX />,
235 );
257 - }
236 + }).toWarnDev(
237 + 'Warning: Stateless function components cannot be given refs.',
238 + );
239 // Should be deduped (offending element is on the same line):
240 instance1.forceUpdate();
241 // Should also be deduped (offending element is on the same line):
242 ReactTestUtils.renderIntoDocument(<AnonymousParentUsingJSX />);
262 - if (__DEV__) {
263 - expect(console.error.calls.count()).toBe(1);
264 - console.error.calls.reset();
265 - }
243
244 // When owner doesn't use JSX, and is anonymous, we warn once per internal instance.
245 class AnonymousParentNotUsingJSX extends React.Component {
@@ -277,29 +254,20 @@ describe('ReactStatelessComponent', () => {
254 value: undefined,
255 });
256
280 - const instance2 = ReactTestUtils.renderIntoDocument(
281 - <AnonymousParentNotUsingJSX />,
282 - );
283 - if (__DEV__) {
284 - expect(console.error.calls.count()).toBe(1);
285 - expect(console.error.calls.argsFor(0)[0]).toContain(
286 - 'Warning: Stateless function components cannot be given refs.',
257 + let instance2;
258 + expect(() => {
259 + instance2 = ReactTestUtils.renderIntoDocument(
260 + <AnonymousParentNotUsingJSX />,
261 );
288 - }
289 - // Should be deduped (same internal instance):
262 + }).toWarnDev(
263 + 'Warning: Stateless function components cannot be given refs.',
264 + );
265 + // Should be deduped (same internal instance, no additional warnings)
266 instance2.forceUpdate();
291 - if (__DEV__) {
292 - expect(console.error.calls.count()).toBe(1);
293 - }
267 // Could not be deduped (different internal instance):
295 - ReactTestUtils.renderIntoDocument(<AnonymousParentNotUsingJSX />);
296 - if (__DEV__) {
297 - expect(console.error.calls.count()).toBe(2);
298 - expect(console.error.calls.argsFor(1)[0]).toContain(
299 - 'Warning: Stateless function components cannot be given refs.',
300 - );
301 - console.error.calls.reset();
302 - }
268 + expect(() =>
269 + ReactTestUtils.renderIntoDocument(<AnonymousParentNotUsingJSX />),
270 + ).toWarnDev('Warning: Stateless function components cannot be given refs.');
271
272 // When owner doesn't use JSX, but is named, we warn once per owner name
273 class NamedParentNotUsingJSX extends React.Component {
@@ -310,33 +278,21 @@ describe('ReactStatelessComponent', () => {
278 });
279 }
280 }
313 - const instance3 = ReactTestUtils.renderIntoDocument(
314 - <NamedParentNotUsingJSX />,
281 + let instance3;
282 + expect(() => {
283 + instance3 = ReactTestUtils.renderIntoDocument(<NamedParentNotUsingJSX />);
284 + }).toWarnDev(
285 + 'Warning: Stateless function components cannot be given refs.',
286 );
316 - if (__DEV__) {
317 - expect(console.error.calls.count()).toBe(1);
318 - expect(console.error.calls.argsFor(0)[0]).toContain(
319 - 'Warning: Stateless function components cannot be given refs.',
320 - );
321 - }
322 - // Should be deduped (same owner name):
287 + // Should be deduped (same owner name, no additional warnings):
288 instance3.forceUpdate();
324 - if (__DEV__) {
325 - expect(console.error.calls.count()).toBe(1);
326 - }
327 - // Should also be deduped (same owner name):
289 + // Should also be deduped (same owner name, no additional warnings):
290 ReactTestUtils.renderIntoDocument(<NamedParentNotUsingJSX />);
329 - if (__DEV__) {
330 - expect(console.error.calls.count()).toBe(1);
331 - console.error.calls.reset();
332 - }
291 });
292
293 // This guards against a regression caused by clearing the current debug fiber.
294 // https://github.com/facebook/react/issues/10831
295 it('should warn when giving a function ref with context', () => {
338 - spyOnDev(console, 'error');
339 -
296 function Child() {
297 return null;
298 }
@@ -358,17 +314,13 @@ describe('ReactStatelessComponent', () => {
314 }
315 }
316
361 - ReactTestUtils.renderIntoDocument(<Parent />);
362 - if (__DEV__) {
363 - expect(console.error.calls.count()).toBe(1);
364 - expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
365 - 'Warning: Stateless function components cannot be given refs. ' +
366 - 'Attempts to access this ref will fail.\n\nCheck the render method ' +
367 - 'of `Parent`.\n' +
368 - ' in Child (at **)\n' +
369 - ' in Parent (at **)',
370 - );
371 - }
317 + expect(() => ReactTestUtils.renderIntoDocument(<Parent />)).toWarnDev(
318 + 'Warning: Stateless function components cannot be given refs. ' +
319 + 'Attempts to access this ref will fail.\n\nCheck the render method ' +
320 + 'of `Parent`.\n' +
321 + ' in Child (at **)\n' +
322 + ' in Parent (at **)',
323 + );
324 });
325
326 it('should provide a null ref', () => {
@@ -385,15 +337,10 @@ describe('ReactStatelessComponent', () => {
337 return <div>{[<span />]}</div>;
338 }
339
388 - spyOnDev(console, 'error');
389 - ReactTestUtils.renderIntoDocument(<Child />);
390 - if (__DEV__) {
391 - expect(console.error.calls.count()).toBe(1);
392 - expect(console.error.calls.argsFor(0)[0]).toContain(
393 - 'a unique "key" prop',
394 - );
395 - expect(console.error.calls.argsFor(0)[0]).toContain('Child');
396 - }
340 + expect(() => ReactTestUtils.renderIntoDocument(<Child />)).toWarnDev(
341 + 'Each child in an array or iterator should have a unique "key" prop.\n\n' +
342 + 'Check the render method of `Child`.',
343 + );
344 });
345
346 it('should support default props and prop types', () => {
@@ -403,18 +350,11 @@ describe('ReactStatelessComponent', () => {
350 Child.defaultProps = {test: 2};
351 Child.propTypes = {test: PropTypes.string};
352
406 - spyOnDev(console, 'error');
407 - ReactTestUtils.renderIntoDocument(<Child />);
408 - if (__DEV__) {
409 - expect(console.error.calls.count()).toBe(1);
410 - expect(
411 - console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)'),
412 - ).toBe(
413 - 'Warning: Failed prop type: Invalid prop `test` of type `number` ' +
414 - 'supplied to `Child`, expected `string`.\n' +
415 - ' in Child (at **)',
416 - );
417 - }
353 + expect(() => ReactTestUtils.renderIntoDocument(<Child />)).toWarnDev(
354 + 'Warning: Failed prop type: Invalid prop `test` of type `number` ' +
355 + 'supplied to `Child`, expected `string`.\n' +
356 + ' in Child (at **)',
357 + );
358 });
359
360 it('should receive context', () => {
packages/react-dom/src/__tests__/ReactUpdates-test.js
+33 -51
@@ -842,8 +842,6 @@ describe('ReactUpdates', () => {
842 });
843
844 it('throws in setState if the update callback is not a function', () => {
845 - spyOnDev(console, 'error');
846 -
845 function Foo() {
846 this.a = 1;
847 this.b = 2;
@@ -859,44 +857,38 @@ describe('ReactUpdates', () => {
857
858 let component = ReactTestUtils.renderIntoDocument(<A />);
859
862 - expect(() => component.setState({}, 'no')).toThrowError(
863 - 'Invalid argument passed as callback. Expected a function. Instead ' +
864 - 'received: no',
865 - );
866 - if (__DEV__) {
867 - expect(console.error.calls.argsFor(0)[0]).toContain(
860 + expect(() => {
861 + expect(() => component.setState({}, 'no')).toWarnDev(
862 'setState(...): Expected the last optional `callback` argument to be ' +
863 'a function. Instead received: no.',
864 );
871 - }
872 - component = ReactTestUtils.renderIntoDocument(<A />);
873 - expect(() => component.setState({}, {foo: 'bar'})).toThrowError(
865 + }).toThrowError(
866 'Invalid argument passed as callback. Expected a function. Instead ' +
875 - 'received: [object Object]',
867 + 'received: no',
868 );
877 - if (__DEV__) {
878 - expect(console.error.calls.argsFor(1)[0]).toContain(
869 + component = ReactTestUtils.renderIntoDocument(<A />);
870 + expect(() => {
871 + expect(() => component.setState({}, {foo: 'bar'})).toWarnDev(
872 'setState(...): Expected the last optional `callback` argument to be ' +
873 'a function. Instead received: [object Object].',
874 );
882 - }
883 - component = ReactTestUtils.renderIntoDocument(<A />);
884 - expect(() => component.setState({}, new Foo())).toThrowError(
875 + }).toThrowError(
876 'Invalid argument passed as callback. Expected a function. Instead ' +
877 'received: [object Object]',
878 );
888 - if (__DEV__) {
889 - expect(console.error.calls.argsFor(2)[0]).toContain(
879 + component = ReactTestUtils.renderIntoDocument(<A />);
880 + expect(() => {
881 + expect(() => component.setState({}, new Foo())).toWarnDev(
882 'setState(...): Expected the last optional `callback` argument to be ' +
883 'a function. Instead received: [object Object].',
884 );
893 - expect(console.error.calls.count()).toBe(3);
894 - }
885 + }).toThrowError(
886 + 'Invalid argument passed as callback. Expected a function. Instead ' +
887 + 'received: [object Object]',
888 + );
889 });
890
891 it('throws in forceUpdate if the update callback is not a function', () => {
898 - spyOnDev(console, 'error');
899 -
892 function Foo() {
893 this.a = 1;
894 this.b = 2;
@@ -912,39 +904,35 @@ describe('ReactUpdates', () => {
904
905 let component = ReactTestUtils.renderIntoDocument(<A />);
906
915 - expect(() => component.forceUpdate('no')).toThrowError(
916 - 'Invalid argument passed as callback. Expected a function. Instead ' +
917 - 'received: no',
918 - );
919 - if (__DEV__) {
920 - expect(console.error.calls.argsFor(0)[0]).toContain(
907 + expect(() => {
908 + expect(() => component.forceUpdate('no')).toWarnDev(
909 'forceUpdate(...): Expected the last optional `callback` argument to be ' +
910 'a function. Instead received: no.',
911 );
924 - }
925 - component = ReactTestUtils.renderIntoDocument(<A />);
926 - expect(() => component.forceUpdate({foo: 'bar'})).toThrowError(
912 + }).toThrowError(
913 'Invalid argument passed as callback. Expected a function. Instead ' +
928 - 'received: [object Object]',
914 + 'received: no',
915 );
930 - if (__DEV__) {
931 - expect(console.error.calls.argsFor(1)[0]).toContain(
916 + component = ReactTestUtils.renderIntoDocument(<A />);
917 + expect(() => {
918 + expect(() => component.forceUpdate({foo: 'bar'})).toWarnDev(
919 'forceUpdate(...): Expected the last optional `callback` argument to be ' +
920 'a function. Instead received: [object Object].',
921 );
935 - }
936 - component = ReactTestUtils.renderIntoDocument(<A />);
937 - expect(() => component.forceUpdate(new Foo())).toThrowError(
922 + }).toThrowError(
923 'Invalid argument passed as callback. Expected a function. Instead ' +
924 'received: [object Object]',
925 );
941 - if (__DEV__) {
942 - expect(console.error.calls.argsFor(2)[0]).toContain(
926 + component = ReactTestUtils.renderIntoDocument(<A />);
927 + expect(() => {
928 + expect(() => component.forceUpdate(new Foo())).toWarnDev(
929 'forceUpdate(...): Expected the last optional `callback` argument to be ' +
930 'a function. Instead received: [object Object].',
931 );
946 - expect(console.error.calls.count()).toBe(3);
947 - }
932 + }).toThrowError(
933 + 'Invalid argument passed as callback. Expected a function. Instead ' +
934 + 'received: [object Object]',
935 + );
936 });
937
938 it('does not update one component twice in a batch (#2410)', () => {
@@ -1228,8 +1216,6 @@ describe('ReactUpdates', () => {
1216 );
1217
1218 it('uses correct base state for setState inside render phase', () => {
1231 - spyOnDev(console, 'error');
1232 -
1219 let ops = [];
1220
1221 class Foo extends React.Component {
@@ -1246,14 +1232,10 @@ describe('ReactUpdates', () => {
1232 }
1233
1234 const container = document.createElement('div');
1249 - ReactDOM.render(<Foo />, container);
1235 + expect(() => ReactDOM.render(<Foo />, container)).toWarnDev(
1236 + 'Cannot update during an existing state transition',
1237 + );
1238 expect(ops).toEqual(['base: 0, memoized: 0', 'base: 1, memoized: 1']);
1251 - if (__DEV__) {
1252 - expect(console.error.calls.count()).toBe(1);
1253 - expect(console.error.calls.argsFor(0)[0]).toContain(
1254 - 'Cannot update during an existing state transition',
1255 - );
1256 - }
1239 });
1240
1241 it('does not re-render if state update is null', () => {
packages/react-dom/src/__tests__/TapEventPlugin-test.internal.js
+7 -13
@@ -86,19 +86,13 @@ describe('TapEventPlugin', () => {
86
87 idCallOrder = [];
88 tapMoveThreshold = TapEventPlugin.tapMoveThreshold;
89 - spyOnDev(console, 'warn');
90 - EventPluginHub.injection.injectEventPluginsByName({
91 - TapEventPlugin: TapEventPlugin,
92 - });
93 - });
94 -
95 - afterEach(() => {
96 - if (__DEV__) {
97 - expect(console.warn.calls.count()).toBe(1);
98 - expect(console.warn.calls.argsFor(0)[0]).toContain(
99 - 'Injecting custom event plugins (TapEventPlugin) is deprecated',
100 - );
101 - }
89 + expect(() =>
90 + EventPluginHub.injection.injectEventPluginsByName({
91 + TapEventPlugin: TapEventPlugin,
92 + }),
93 + ).toLowPriorityWarnDev(
94 + 'Injecting custom event plugins (TapEventPlugin) is deprecated',
95 + );
96 });
97
98 /**
packages/react-dom/src/__tests__/validateDOMNesting-test.js
+1 -20
@@ -12,40 +12,22 @@
12 const React = require('react');
13 const ReactDOM = require('react-dom');
14
15 -function normalizeCodeLocInfo(str) {
16 - return str && str.replace(/at .+?:\d+/g, 'at **');
17 -}
18 -
15 function expectWarnings(tags, warnings = []) {
16 tags = [...tags];
17 warnings = [...warnings];
18
19 let element = null;
24 - if (__DEV__) {
25 - console.error.calls.reset();
26 - }
20 const container = document.createElement(tags.splice(0, 1));
21 while (tags.length) {
22 const Tag = tags.pop();
23 element = <Tag>{element}</Tag>;
24 }
32 - ReactDOM.render(element, container);
25
34 - if (__DEV__) {
35 - expect(console.error.calls.count()).toEqual(warnings.length);
36 - while (warnings.length) {
37 - expect(
38 - normalizeCodeLocInfo(
39 - console.error.calls.argsFor(warnings.length - 1)[0],
40 - ),
41 - ).toContain(warnings.pop());
42 - }
43 - }
26 + expect(() => ReactDOM.render(element, container)).toWarnDev(warnings);
27 }
28
29 describe('validateDOMNesting', () => {
30 it('allows valid nestings', () => {
48 - spyOnDev(console, 'error');
31 expectWarnings(['table', 'tbody', 'tr', 'td', 'b']);
32 expectWarnings(
33 ['body', 'datalist', 'option'],
@@ -66,7 +48,6 @@ describe('validateDOMNesting', () => {
48 });
49
50 it('prevents problematic nestings', () => {
69 - spyOnDev(console, 'error');
51 expectWarnings(
52 ['a', 'a'],
53 [
packages/react-dom/src/events/__tests__/SyntheticEvent-test.js
+64 -83
@@ -157,7 +157,6 @@ describe('SyntheticEvent', () => {
157 });
158
159 it('should be nullified and log warnings if the synthetic event has not been persisted', () => {
160 - spyOnDev(console, 'error');
160 let node;
161 let expectedCount = 0;
162 let syntheticEvent;
@@ -173,26 +172,30 @@ describe('SyntheticEvent', () => {
172 event.initEvent('click', true, true);
173 node.dispatchEvent(event);
174
176 - expect(syntheticEvent.type).toBe(null);
177 - expect(syntheticEvent.nativeEvent).toBe(null);
178 - expect(syntheticEvent.target).toBe(null);
179 - if (__DEV__) {
180 - // once for each property accessed
181 - expect(console.error.calls.count()).toBe(3);
182 - // assert the first warning for accessing `type`
183 - expect(console.error.calls.argsFor(0)[0]).toBe(
184 - 'Warning: This synthetic event is reused for performance reasons. If ' +
185 - "you're seeing this, you're accessing the property `type` on a " +
186 - 'released/nullified synthetic event. This is set to null. If you must ' +
187 - 'keep the original synthetic event around, use event.persist(). ' +
188 - 'See https://fb.me/react-event-pooling for more information.',
189 - );
190 - }
175 + const getExpectedWarning = property =>
176 + 'Warning: This synthetic event is reused for performance reasons. If ' +
177 + `you're seeing this, you're accessing the property \`${
178 + property
179 + }\` on a ` +
180 + 'released/nullified synthetic event. This is set to null. If you must ' +
181 + 'keep the original synthetic event around, use event.persist(). ' +
182 + 'See https://fb.me/react-event-pooling for more information.';
183 +
184 + // once for each property accessed
185 + expect(() => expect(syntheticEvent.type).toBe(null)).toWarnDev(
186 + getExpectedWarning('type'),
187 + );
188 + expect(() => expect(syntheticEvent.nativeEvent).toBe(null)).toWarnDev(
189 + getExpectedWarning('nativeEvent'),
190 + );
191 + expect(() => expect(syntheticEvent.target).toBe(null)).toWarnDev(
192 + getExpectedWarning('target'),
193 + );
194 +
195 expect(expectedCount).toBe(1);
196 });
197
198 it('should warn when setting properties of a synthetic event that has not been persisted', () => {
195 - spyOnDev(console, 'error');
199 let node;
200 let expectedCount = 0;
201 let syntheticEvent;
@@ -208,22 +211,19 @@ describe('SyntheticEvent', () => {
211 event.initEvent('click', true, true);
212 node.dispatchEvent(event);
213
211 - syntheticEvent.type = 'MouseEvent';
212 - if (__DEV__) {
213 - expect(console.error.calls.count()).toBe(1);
214 - expect(console.error.calls.argsFor(0)[0]).toBe(
215 - 'Warning: This synthetic event is reused for performance reasons. If ' +
216 - "you're seeing this, you're setting the property `type` on a " +
217 - 'released/nullified synthetic event. This is effectively a no-op. If you must ' +
218 - 'keep the original synthetic event around, use event.persist(). ' +
219 - 'See https://fb.me/react-event-pooling for more information.',
220 - );
221 - }
214 + expect(() => {
215 + syntheticEvent.type = 'MouseEvent';
216 + }).toWarnDev(
217 + 'Warning: This synthetic event is reused for performance reasons. If ' +
218 + "you're seeing this, you're setting the property `type` on a " +
219 + 'released/nullified synthetic event. This is effectively a no-op. If you must ' +
220 + 'keep the original synthetic event around, use event.persist(). ' +
221 + 'See https://fb.me/react-event-pooling for more information.',
222 + );
223 expect(expectedCount).toBe(1);
224 });
225
226 it('should warn when calling `preventDefault` if the synthetic event has not been persisted', () => {
226 - spyOnDev(console, 'error');
227 let node;
228 let expectedCount = 0;
229 let syntheticEvent;
@@ -238,22 +238,17 @@ describe('SyntheticEvent', () => {
238 event.initEvent('click', true, true);
239 node.dispatchEvent(event);
240
241 - syntheticEvent.preventDefault();
242 - if (__DEV__) {
243 - expect(console.error.calls.count()).toBe(1);
244 - expect(console.error.calls.argsFor(0)[0]).toBe(
245 - 'Warning: This synthetic event is reused for performance reasons. If ' +
246 - "you're seeing this, you're accessing the method `preventDefault` on a " +
247 - 'released/nullified synthetic event. This is a no-op function. If you must ' +
248 - 'keep the original synthetic event around, use event.persist(). ' +
249 - 'See https://fb.me/react-event-pooling for more information.',
250 - );
251 - }
241 + expect(() => syntheticEvent.preventDefault()).toWarnDev(
242 + 'Warning: This synthetic event is reused for performance reasons. If ' +
243 + "you're seeing this, you're accessing the method `preventDefault` on a " +
244 + 'released/nullified synthetic event. This is a no-op function. If you must ' +
245 + 'keep the original synthetic event around, use event.persist(). ' +
246 + 'See https://fb.me/react-event-pooling for more information.',
247 + );
248 expect(expectedCount).toBe(1);
249 });
250
251 it('should warn when calling `stopPropagation` if the synthetic event has not been persisted', () => {
256 - spyOnDev(console, 'error');
252 let node;
253 let expectedCount = 0;
254 let syntheticEvent;
@@ -269,17 +264,13 @@ describe('SyntheticEvent', () => {
264
265 node.dispatchEvent(event);
266
272 - syntheticEvent.stopPropagation();
273 - if (__DEV__) {
274 - expect(console.error.calls.count()).toBe(1);
275 - expect(console.error.calls.argsFor(0)[0]).toBe(
276 - 'Warning: This synthetic event is reused for performance reasons. If ' +
277 - "you're seeing this, you're accessing the method `stopPropagation` on a " +
278 - 'released/nullified synthetic event. This is a no-op function. If you must ' +
279 - 'keep the original synthetic event around, use event.persist(). ' +
280 - 'See https://fb.me/react-event-pooling for more information.',
281 - );
282 - }
267 + expect(() => syntheticEvent.stopPropagation()).toWarnDev(
268 + 'Warning: This synthetic event is reused for performance reasons. If ' +
269 + "you're seeing this, you're accessing the method `stopPropagation` on a " +
270 + 'released/nullified synthetic event. This is a no-op function. If you must ' +
271 + 'keep the original synthetic event around, use event.persist(). ' +
272 + 'See https://fb.me/react-event-pooling for more information.',
273 + );
274 expect(expectedCount).toBe(1);
275 });
276
@@ -287,7 +278,6 @@ describe('SyntheticEvent', () => {
278 // using TestUtils.Simulate to avoid spurious warnings that result from the
279 // way we simulate events.
280 xit('should properly log warnings when events simulated with rendered components', () => {
290 - spyOnDev(console, 'error');
281 let event;
282 const element = document.createElement('div');
283 function assignEvent(e) {
@@ -295,33 +285,37 @@ describe('SyntheticEvent', () => {
285 }
286 const node = ReactDOM.render(<div onClick={assignEvent} />, element);
287 ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(node));
298 - if (__DEV__) {
299 - expect(console.error.calls.count()).toBe(0);
300 - }
288
289 // access a property to cause the warning
303 - event.nativeEvent; // eslint-disable-line no-unused-expressions
304 -
305 - if (__DEV__) {
306 - expect(console.error.calls.count()).toBe(1);
307 - expect(console.error.calls.argsFor(0)[0]).toBe(
308 - 'Warning: This synthetic event is reused for performance reasons. If ' +
309 - "you're seeing this, you're accessing the property `nativeEvent` on a " +
310 - 'released/nullified synthetic event. This is set to null. If you must ' +
311 - 'keep the original synthetic event around, use event.persist(). ' +
312 - 'See https://fb.me/react-event-pooling for more information.',
313 - );
314 - }
290 + expect(() => {
291 + event.nativeEvent; // eslint-disable-line no-unused-expressions
292 + }).toWarnDev(
293 + 'Warning: This synthetic event is reused for performance reasons. If ' +
294 + "you're seeing this, you're accessing the property `nativeEvent` on a " +
295 + 'released/nullified synthetic event. This is set to null. If you must ' +
296 + 'keep the original synthetic event around, use event.persist(). ' +
297 + 'See https://fb.me/react-event-pooling for more information.',
298 + );
299 });
300
301 it('should warn if Proxy is supported and the synthetic event is added a property', () => {
318 - spyOnDev(console, 'error');
302 let node;
303 let expectedCount = 0;
304 let syntheticEvent;
305
306 const eventHandler = e => {
324 - e.foo = 'bar';
307 + if (typeof Proxy === 'function') {
308 + expect(() => {
309 + e.foo = 'bar';
310 + }).toWarnDev(
311 + 'Warning: This synthetic event is reused for performance reasons. If ' +
312 + "you're seeing this, you're adding a new property in the synthetic " +
313 + 'event object. The property is never released. ' +
314 + 'See https://fb.me/react-event-pooling for more information.',
315 + );
316 + } else {
317 + e.foo = 'bar';
318 + }
319 syntheticEvent = e;
320 expectedCount++;
321 };
@@ -333,19 +327,6 @@ describe('SyntheticEvent', () => {
327 node.dispatchEvent(event);
328
329 expect(syntheticEvent.foo).toBe('bar');
336 - if (__DEV__) {
337 - if (typeof Proxy === 'function') {
338 - expect(console.error.calls.count()).toBe(1);
339 - expect(console.error.calls.argsFor(0)[0]).toBe(
340 - 'Warning: This synthetic event is reused for performance reasons. If ' +
341 - "you're seeing this, you're adding a new property in the synthetic " +
342 - 'event object. The property is never released. ' +
343 - 'See https://fb.me/react-event-pooling for more information.',
344 - );
345 - } else {
346 - expect(console.error.calls.count()).toBe(0);
347 - }
348 - }
330 expect(expectedCount).toBe(1);
331 });
332 });
packages/react-reconciler/src/__tests__/ReactFragment-test.js
+12 -20
@@ -672,7 +672,6 @@ describe('ReactFragment', () => {
672 });
673
674 it('should not preserve state when switching to a keyed fragment to an array', function() {
675 - spyOnDev(console, 'error');
675 const ops = [];
676
677 class Stateful extends React.Component {
@@ -707,7 +706,9 @@ describe('ReactFragment', () => {
706 ReactNoop.flush();
707
708 ReactNoop.render(<Foo condition={false} />);
710 - ReactNoop.flush();
709 + expect(ReactNoop.flush).toWarnDev(
710 + 'Each child in an array or iterator should have a unique "key" prop.',
711 + );
712
713 expect(ops).toEqual([]);
714 expect(ReactNoop.getChildren()).toEqual([div(div(), span())]);
@@ -717,16 +718,9 @@ describe('ReactFragment', () => {
718
719 expect(ops).toEqual([]);
720 expect(ReactNoop.getChildren()).toEqual([div(div(), span())]);
720 - if (__DEV__) {
721 - expect(console.error.calls.count()).toBe(1);
722 - expect(console.error.calls.argsFor(0)[0]).toContain(
723 - 'Each child in an array or iterator should have a unique "key" prop.',
724 - );
725 - }
721 });
722
723 it('should preserve state when it does not change positions', function() {
729 - spyOnDev(console, 'error');
724 const ops = [];
725
726 class Stateful extends React.Component {
@@ -756,26 +750,24 @@ describe('ReactFragment', () => {
750 }
751
752 ReactNoop.render(<Foo condition={true} />);
759 - ReactNoop.flush();
753 + expect(ReactNoop.flush).toWarnDev(
754 + 'Each child in an array or iterator should have a unique "key" prop.',
755 + );
756
757 ReactNoop.render(<Foo condition={false} />);
762 - ReactNoop.flush();
758 + expect(ReactNoop.flush).toWarnDev(
759 + 'Each child in an array or iterator should have a unique "key" prop.',
760 + );
761
762 expect(ops).toEqual(['Update Stateful']);
763 expect(ReactNoop.getChildren()).toEqual([span(), div()]);
764
765 ReactNoop.render(<Foo condition={true} />);
768 - ReactNoop.flush();
766 + expect(ReactNoop.flush).toWarnDev(
767 + 'Each child in an array or iterator should have a unique "key" prop.',
768 + );
769
770 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
771 expect(ReactNoop.getChildren()).toEqual([span(), div()]);
772 - if (__DEV__) {
773 - expect(console.error.calls.count()).toBe(3);
774 - for (let errorIndex = 0; errorIndex < 3; ++errorIndex) {
775 - expect(console.error.calls.argsFor(errorIndex)[0]).toContain(
776 - 'Each child in an array or iterator should have a unique "key" prop.',
777 - );
778 - }
779 - }
772 });
773 });
packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.js
+10 -29
@@ -720,8 +720,6 @@ describe('ReactIncrementalErrorHandling', () => {
720 });
721
722 it('catches reconciler errors in a boundary during mounting', () => {
723 - spyOnDev(console, 'error');
724 -
723 class ErrorBoundary extends React.Component {
724 state = {error: null};
725 componentDidCatch(error) {
@@ -744,7 +742,9 @@ describe('ReactIncrementalErrorHandling', () => {
742 <BrokenRender />
743 </ErrorBoundary>,
744 );
747 - ReactNoop.flush();
745 + expect(ReactNoop.flush).toWarnDev(
746 + 'Warning: React.createElement: type is invalid -- expected a string',
747 + );
748 expect(ReactNoop.getChildren()).toEqual([
749 span(
750 'Element type is invalid: expected a string (for built-in components) or ' +
@@ -756,17 +756,9 @@ describe('ReactIncrementalErrorHandling', () => {
756 : ''),
757 ),
758 ]);
759 - if (__DEV__) {
760 - expect(console.error.calls.count()).toBe(1);
761 - expect(console.error.calls.argsFor(0)[0]).toContain(
762 - 'Warning: React.createElement: type is invalid -- expected a string',
763 - );
764 - }
759 });
760
761 it('catches reconciler errors in a boundary during update', () => {
768 - spyOnDev(console, 'error');
769 -
762 class ErrorBoundary extends React.Component {
763 state = {error: null};
764 componentDidCatch(error) {
@@ -797,7 +789,9 @@ describe('ReactIncrementalErrorHandling', () => {
789 <BrokenRender fail={true} />
790 </ErrorBoundary>,
791 );
800 - ReactNoop.flush();
792 + expect(ReactNoop.flush).toWarnDev(
793 + 'Warning: React.createElement: type is invalid -- expected a string',
794 + );
795 expect(ReactNoop.getChildren()).toEqual([
796 span(
797 'Element type is invalid: expected a string (for built-in components) or ' +
@@ -809,21 +803,14 @@ describe('ReactIncrementalErrorHandling', () => {
803 : ''),
804 ),
805 ]);
812 - if (__DEV__) {
813 - expect(console.error.calls.count()).toBe(1);
814 - expect(console.error.calls.argsFor(0)[0]).toContain(
815 - 'Warning: React.createElement: type is invalid -- expected a string',
816 - );
817 - }
806 });
807
808 it('recovers from uncaught reconciler errors', () => {
821 - spyOnDev(console, 'error');
809 const InvalidType = undefined;
823 - ReactNoop.render(<InvalidType />);
824 - expect(() => {
825 - ReactNoop.flush();
826 - }).toThrowError(
810 + expect(() => ReactNoop.render(<InvalidType />)).toWarnDev(
811 + 'Warning: React.createElement: type is invalid -- expected a string',
812 + );
813 + expect(ReactNoop.flush).toThrowError(
814 'Element type is invalid: expected a string (for built-in components) or ' +
815 'a class/function (for composite components) but got: undefined.' +
816 (__DEV__
@@ -835,12 +822,6 @@ describe('ReactIncrementalErrorHandling', () => {
822 ReactNoop.render(<span prop="hi" />);
823 ReactNoop.flush();
824 expect(ReactNoop.getChildren()).toEqual([span('hi')]);
838 - if (__DEV__) {
839 - expect(console.error.calls.count()).toBe(1);
840 - expect(console.error.calls.argsFor(0)[0]).toContain(
841 - 'Warning: React.createElement: type is invalid -- expected a string',
842 - );
843 - }
825 });
826
827 it('unmounts components with uncaught errors', () => {
packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js
+8 -17
@@ -20,10 +20,6 @@ describe('ReactIncrementalSideEffects', () => {
20 ReactNoop = require('react-noop-renderer');
21 });
22
23 - function normalizeCodeLocInfo(str) {
24 - return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
25 - }
26 -
23 function div(...children) {
24 children = children.map(c => (typeof c === 'string' ? {text: c} : c));
25 return {type: 'div', children, prop: undefined};
@@ -987,7 +983,6 @@ describe('ReactIncrementalSideEffects', () => {
983 });
984
985 it('invokes ref callbacks after insertion/update/unmount', () => {
990 - spyOnDev(console, 'error');
986 let classInstance = null;
987
988 let ops = [];
@@ -1014,7 +1009,14 @@ describe('ReactIncrementalSideEffects', () => {
1009 }
1010
1011 ReactNoop.render(<Foo show={true} />);
1017 - ReactNoop.flush();
1012 + expect(ReactNoop.flush).toWarnDev(
1013 + 'Warning: Stateless function components cannot be given refs. ' +
1014 + 'Attempts to access this ref will fail.\n\nCheck the render method ' +
1015 + 'of `Foo`.\n' +
1016 + ' in FunctionalComponent (at **)\n' +
1017 + ' in div (at **)\n' +
1018 + ' in Foo (at **)',
1019 + );
1020 expect(ops).toEqual([
1021 classInstance,
1022 // no call for functional components
@@ -1044,17 +1046,6 @@ describe('ReactIncrementalSideEffects', () => {
1046 null,
1047 null,
1048 ]);
1047 -
1048 - if (__DEV__) {
1049 - expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1050 - 'Warning: Stateless function components cannot be given refs. ' +
1051 - 'Attempts to access this ref will fail.\n\nCheck the render method ' +
1052 - 'of `Foo`.\n' +
1053 - ' in FunctionalComponent (at **)\n' +
1054 - ' in div (at **)\n' +
1055 - ' in Foo (at **)',
1056 - );
1057 - }
1049 });
1050
1051 // TODO: Test that mounts, updates, refs, unmounts and deletions happen in the
packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js
+7 -16
@@ -321,7 +321,6 @@ describe('ReactIncrementalUpdates', () => {
321 });
322
323 it('enqueues setState inside an updater function as if the in-progress update is progressed (and warns)', () => {
324 - spyOnDev(console, 'error');
324 let instance;
325 let ops = [];
326 class Foo extends React.Component {
@@ -342,7 +341,12 @@ describe('ReactIncrementalUpdates', () => {
341 return {a: 'a'};
342 });
343
345 - ReactNoop.flush();
344 + expect(ReactNoop.flush).toWarnDev(
345 + 'An update (setState, replaceState, or forceUpdate) was scheduled ' +
346 + 'from inside an update function. Update functions should be pure, ' +
347 + 'with zero side-effects. Consider using componentDidUpdate or a ' +
348 + 'callback.',
349 + );
350 expect(ops).toEqual([
351 // Initial render
352 'render',
@@ -353,24 +357,11 @@ describe('ReactIncrementalUpdates', () => {
357 ]);
358 expect(instance.state).toEqual({a: 'a', b: 'b'});
359
356 - if (__DEV__) {
357 - expect(console.error.calls.count()).toBe(1);
358 - expect(console.error.calls.argsFor(0)[0]).toContain(
359 - 'An update (setState, replaceState, or forceUpdate) was scheduled ' +
360 - 'from inside an update function. Update functions should be pure, ' +
361 - 'with zero side-effects. Consider using componentDidUpdate or a ' +
362 - 'callback.',
363 - );
364 - }
365 -
366 - // Test deduplication
360 + // Test deduplication (no additional warnings expected)
361 instance.setState(function a() {
362 this.setState({a: 'a'});
363 return {b: 'b'};
364 });
365 ReactNoop.flush();
372 - if (__DEV__) {
373 - expect(console.error.calls.count()).toBe(1);
374 - }
366 });
367 });
packages/react-test-renderer/src/__tests__/ReactShallowRenderer-test.js
+28 -55
@@ -771,8 +771,6 @@ describe('ReactShallowRenderer', () => {
771 });
772
773 it('can fail context when shallowly rendering', () => {
774 - spyOnDev(console, 'error');
775 -
774 class SimpleComponent extends React.Component {
775 static contextTypes = {
776 name: PropTypes.string.isRequired,
@@ -784,22 +782,14 @@ describe('ReactShallowRenderer', () => {
782 }
783
784 const shallowRenderer = createRenderer();
787 - shallowRenderer.render(<SimpleComponent />);
788 - if (__DEV__) {
789 - expect(console.error.calls.count()).toBe(1);
790 - expect(
791 - console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)'),
792 - ).toBe(
793 - 'Warning: Failed context type: The context `name` is marked as ' +
794 - 'required in `SimpleComponent`, but its value is `undefined`.\n' +
795 - ' in SimpleComponent (at **)',
796 - );
797 - }
785 + expect(() => shallowRenderer.render(<SimpleComponent />)).toWarnDev(
786 + 'Warning: Failed context type: The context `name` is marked as ' +
787 + 'required in `SimpleComponent`, but its value is `undefined`.\n' +
788 + ' in SimpleComponent (at **)',
789 + );
790 });
791
792 it('should warn about propTypes (but only once)', () => {
801 - spyOnDev(console, 'error');
802 -
793 class SimpleComponent extends React.Component {
794 render() {
795 return React.createElement('div', null, this.props.name);
@@ -811,18 +801,13 @@ describe('ReactShallowRenderer', () => {
801 };
802
803 const shallowRenderer = createRenderer();
814 - shallowRenderer.render(React.createElement(SimpleComponent, {name: 123}));
815 -
816 - if (__DEV__) {
817 - expect(console.error.calls.count()).toBe(1);
818 - expect(
819 - console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)'),
820 - ).toBe(
821 - 'Warning: Failed prop type: Invalid prop `name` of type `number` ' +
822 - 'supplied to `SimpleComponent`, expected `string`.\n' +
823 - ' in SimpleComponent',
824 - );
825 - }
804 + expect(() =>
805 + shallowRenderer.render(React.createElement(SimpleComponent, {name: 123})),
806 + ).toWarnDev(
807 + 'Warning: Failed prop type: Invalid prop `name` of type `number` ' +
808 + 'supplied to `SimpleComponent`, expected `string`.\n' +
809 + ' in SimpleComponent',
810 + );
811 });
812
813 it('should enable rendering of cloned element', () => {
@@ -942,36 +927,24 @@ describe('ReactShallowRenderer', () => {
927 });
928
929 it('throws usefully when rendering badly-typed elements', () => {
945 - spyOnDev(console, 'error');
930 const shallowRenderer = createRenderer();
931
948 - const Undef = undefined;
949 - expect(() => shallowRenderer.render(<Undef />)).toThrowError(
950 - 'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
951 - 'components, but the provided element type was `undefined`.',
952 - );
953 -
954 - const Null = null;
955 - expect(() => shallowRenderer.render(<Null />)).toThrowError(
956 - 'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
957 - 'components, but the provided element type was `null`.',
958 - );
959 -
960 - const Arr = [];
961 - expect(() => shallowRenderer.render(<Arr />)).toThrowError(
962 - 'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
963 - 'components, but the provided element type was `array`.',
964 - );
965 -
966 - const Obj = {};
967 - expect(() => shallowRenderer.render(<Obj />)).toThrowError(
968 - 'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
969 - 'components, but the provided element type was `object`.',
970 - );
932 + const renderAndVerifyWarningAndError = (Component, typeString) => {
933 + expect(() => {
934 + expect(() => shallowRenderer.render(<Component />)).toWarnDev(
935 + 'React.createElement: type is invalid -- expected a string ' +
936 + '(for built-in components) or a class/function (for composite components) ' +
937 + `but got: ${typeString}.`,
938 + );
939 + }).toThrowError(
940 + 'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
941 + `components, but the provided element type was \`${typeString}\`.`,
942 + );
943 + };
944
972 - if (__DEV__) {
973 - // One warning for each element creation
974 - expect(console.error.calls.count()).toBe(4);
975 - }
945 + renderAndVerifyWarningAndError(undefined, 'undefined');
946 + renderAndVerifyWarningAndError(null, 'null');
947 + renderAndVerifyWarningAndError([], 'array');
948 + renderAndVerifyWarningAndError({}, 'object');
949 });
950 });
packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.js
+6 -15
@@ -42,10 +42,6 @@ function cleanNodeOrArray(node) {
42 }
43
44 describe('ReactTestRenderer', () => {
45 - function normalizeCodeLocInfo(str) {
46 - return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
47 - }
48 -
45 it('renders a simple component', () => {
46 function Link() {
47 return <a role="link" />;
@@ -264,7 +260,6 @@ describe('ReactTestRenderer', () => {
260 });
261
262 it('warns correctly for refs on SFCs', () => {
267 - spyOnDev(console, 'error');
263 function Bar() {
264 return <div>Hello, world</div>;
265 }
@@ -279,16 +274,12 @@ describe('ReactTestRenderer', () => {
274 }
275 }
276 ReactTestRenderer.create(<Baz />);
282 - ReactTestRenderer.create(<Foo />);
283 - if (__DEV__) {
284 - expect(console.error.calls.count()).toBe(1);
285 - expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
286 - 'Warning: Stateless function components cannot be given refs. Attempts ' +
287 - 'to access this ref will fail.\n\nCheck the render method of `Foo`.\n' +
288 - ' in Bar (at **)\n' +
289 - ' in Foo (at **)',
290 - );
291 - }
277 + expect(() => ReactTestRenderer.create(<Foo />)).toWarnDev(
278 + 'Warning: Stateless function components cannot be given refs. Attempts ' +
279 + 'to access this ref will fail.\n\nCheck the render method of `Foo`.\n' +
280 + ' in Bar (at **)\n' +
281 + ' in Foo (at **)',
282 + );
283 });
284
285 it('allows an optional createNodeMock function', () => {
packages/react/src/ReactElementValidator.js
+10 -1
@@ -308,12 +308,21 @@ export function createElementWithValidation(type, props, children) {
308
309 info += getStackAddendum() || '';
310
311 + let typeString;
312 + if (type === null) {
313 + typeString = 'null';
314 + } else if (Array.isArray(type)) {
315 + typeString = 'array';
316 + } else {
317 + typeString = typeof type;
318 + }
319 +
320 warning(
321 false,
322 'React.createElement: type is invalid -- expected a string (for ' +
323 'built-in components) or a class/function (for composite ' +
324 'components) but got: %s.%s',
316 - type == null ? type : typeof type,
325 + typeString,
326 info,
327 );
328 }
packages/react/src/__tests__/ReactElementValidator-test.js
-2
@@ -350,8 +350,6 @@ describe('ReactElementValidator', () => {
350 });
351
352 it('should warn if a PropType creator is used as a PropType', () => {
353 - spyOnDev(console, 'error');
354 -
353 class Component extends React.Component {
354 static propTypes = {
355 myProp: PropTypes.shape,
packages/shared/__tests__/ReactDOMFrameScheduling-test.js
+3 -8
@@ -15,14 +15,9 @@ describe('ReactDOMFrameScheduling', () => {
15 try {
16 global.requestAnimationFrame = undefined;
17 jest.resetModules();
18 - spyOnDev(console, 'error');
19 - require('react-dom');
20 - if (__DEV__) {
21 - expect(console.error.calls.count()).toBe(1);
22 - expect(console.error.calls.argsFor(0)[0]).toContain(
23 - 'React depends on requestAnimationFrame.',
24 - );
25 - }
18 + expect(() => require('react-dom')).toWarnDev(
19 + 'React depends on requestAnimationFrame.',
20 + );
21 } finally {
22 global.requestAnimationFrame = previousRAF;
23 }