toWarnInDev matcher; throw on unexpected console.error (#11786)
* Added toWarnInDev matcher and connected to 1 test * Added .toLowPriorityWarnDev() matcher * Reply Jest spy with custom spy. Unregister spy after toWarnDev() so unexpected console.error/warn calls will fail tests. * console warn/error throws immediately in tests by default (if not spied on) * Pass-thru console message before erroring to make it easier to identify * More robustly handle unexpected warnings within try/catch * Error message includes remaining expected warnings in addition to unexpected warning
Brian Vaughn committed
Jan 2, 2018 at 11:06 UTC
b5334a44e99c32ca26f7d65ca8312e13f7b7f4b7
20 files changed
+978
-1381
packages/react-art/src/__tests__/ReactART-test.js
+28
-49
@@ -336,10 +336,6 @@ describe('ReactART', () => {
336
});
337
338
describe('ReactARTComponents', () => {
339
- function normalizeCodeLocInfo(str) {
340
- return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
341
- }
342
-
339
it('should generate a <Shape> with props for drawing the Circle', () => {
340
const circle = renderer.create(
341
<Circle radius={10} stroke="green" strokeWidth={3} fill="blue" />,
@@ -348,16 +344,13 @@ describe('ReactARTComponents', () => {
344
});
345
346
it('should warn if radius is missing on a Circle component', () => {
351
- spyOnDev(console, 'error');
352
- renderer.create(<Circle stroke="green" strokeWidth={3} fill="blue" />);
353
- if (__DEV__) {
354
- expect(console.error.calls.count()).toBe(1);
355
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
356
- 'Warning: Failed prop type: The prop `radius` is marked as required in `Circle`, ' +
357
- 'but its value is `undefined`.' +
358
- '\n in Circle (at **)',
359
- );
360
- }
347
+ expect(() =>
348
+ renderer.create(<Circle stroke="green" strokeWidth={3} fill="blue" />),
349
+ ).toWarnDev(
350
+ 'Warning: Failed prop type: The prop `radius` is marked as required in `Circle`, ' +
351
+ 'but its value is `undefined`.' +
352
+ '\n in Circle (at **)',
353
+ );
354
});
355
356
it('should generate a <Shape> with props for drawing the Rectangle', () => {
@@ -368,21 +361,16 @@ describe('ReactARTComponents', () => {
361
});
362
363
it('should warn if width/height is missing on a Rectangle component', () => {
371
- spyOnDev(console, 'error');
372
- renderer.create(<Rectangle stroke="green" fill="blue" />);
373
- if (__DEV__) {
374
- expect(console.error.calls.count()).toBe(2);
375
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
376
- 'Warning: Failed prop type: The prop `width` is marked as required in `Rectangle`, ' +
377
- 'but its value is `undefined`.' +
378
- '\n in Rectangle (at **)',
379
- );
380
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toEqual(
381
- 'Warning: Failed prop type: The prop `height` is marked as required in `Rectangle`, ' +
382
- 'but its value is `undefined`.' +
383
- '\n in Rectangle (at **)',
384
- );
385
- }
364
+ expect(() =>
365
+ renderer.create(<Rectangle stroke="green" fill="blue" />),
366
+ ).toWarnDev([
367
+ 'Warning: Failed prop type: The prop `width` is marked as required in `Rectangle`, ' +
368
+ 'but its value is `undefined`.' +
369
+ '\n in Rectangle (at **)',
370
+ 'Warning: Failed prop type: The prop `height` is marked as required in `Rectangle`, ' +
371
+ 'but its value is `undefined`.' +
372
+ '\n in Rectangle (at **)',
373
+ ]);
374
});
375
376
it('should generate a <Shape> with props for drawing the Wedge', () => {
@@ -400,25 +388,16 @@ describe('ReactARTComponents', () => {
388
});
389
390
it('should warn if outerRadius/startAngle/endAngle is missing on a Wedge component', () => {
403
- spyOnDev(console, 'error');
404
- renderer.create(<Wedge fill="blue" />);
405
- if (__DEV__) {
406
- expect(console.error.calls.count()).toBe(3);
407
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
408
- 'Warning: Failed prop type: The prop `outerRadius` is marked as required in `Wedge`, ' +
409
- 'but its value is `undefined`.' +
410
- '\n in Wedge (at **)',
411
- );
412
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toEqual(
413
- 'Warning: Failed prop type: The prop `startAngle` is marked as required in `Wedge`, ' +
414
- 'but its value is `undefined`.' +
415
- '\n in Wedge (at **)',
416
- );
417
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toEqual(
418
- 'Warning: Failed prop type: The prop `endAngle` is marked as required in `Wedge`, ' +
419
- 'but its value is `undefined`.' +
420
- '\n in Wedge (at **)',
421
- );
422
- }
391
+ expect(() => renderer.create(<Wedge fill="blue" />)).toWarnDev([
392
+ 'Warning: Failed prop type: The prop `outerRadius` is marked as required in `Wedge`, ' +
393
+ 'but its value is `undefined`.' +
394
+ '\n in Wedge (at **)',
395
+ 'Warning: Failed prop type: The prop `startAngle` is marked as required in `Wedge`, ' +
396
+ 'but its value is `undefined`.' +
397
+ '\n in Wedge (at **)',
398
+ 'Warning: Failed prop type: The prop `endAngle` is marked as required in `Wedge`, ' +
399
+ 'but its value is `undefined`.' +
400
+ '\n in Wedge (at **)',
401
+ ]);
402
});
403
});
packages/react-dom/src/__tests__/CSSPropertyOperations-test.js
+47
-84
@@ -13,10 +13,6 @@ const React = require('react');
13
const ReactDOM = require('react-dom');
14
const ReactDOMServer = require('react-dom/server');
15
16
-function normalizeCodeLocInfo(str) {
17
- return str && str.replace(/at .+?:\d+/g, 'at **');
18
-}
19
-
16
describe('CSSPropertyOperations', () => {
17
it('should automatically append `px` to relevant styles', () => {
18
const styles = {
@@ -91,17 +87,13 @@ describe('CSSPropertyOperations', () => {
87
}
88
}
89
94
- spyOnDev(console, 'error');
90
const root = document.createElement('div');
96
- ReactDOM.render(<Comp />, root);
97
- if (__DEV__) {
98
- expect(console.error.calls.count()).toBe(1);
99
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
100
- 'Warning: Unsupported style property background-color. Did you mean backgroundColor?' +
101
- '\n in div (at **)' +
102
- '\n in Comp (at **)',
103
- );
104
- }
91
+
92
+ expect(() => ReactDOM.render(<Comp />, root)).toWarnDev(
93
+ 'Warning: Unsupported style property background-color. Did you mean backgroundColor?' +
94
+ '\n in div (at **)' +
95
+ '\n in Comp (at **)',
96
+ );
97
});
98
99
it('should warn when updating hyphenated style names', () => {
@@ -113,28 +105,21 @@ describe('CSSPropertyOperations', () => {
105
}
106
}
107
116
- spyOnDev(console, 'error');
108
const styles = {
109
'-ms-transform': 'translate3d(0, 0, 0)',
110
'-webkit-transform': 'translate3d(0, 0, 0)',
111
};
112
const root = document.createElement('div');
113
ReactDOM.render(<Comp />, root);
123
- ReactDOM.render(<Comp style={styles} />, root);
124
-
125
- if (__DEV__) {
126
- expect(console.error.calls.count()).toBe(2);
127
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
128
- 'Warning: Unsupported style property -ms-transform. Did you mean msTransform?' +
129
- '\n in div (at **)' +
130
- '\n in Comp (at **)',
131
- );
132
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toEqual(
133
- 'Warning: Unsupported style property -webkit-transform. Did you mean WebkitTransform?' +
134
- '\n in div (at **)' +
135
- '\n in Comp (at **)',
136
- );
137
- }
114
+
115
+ expect(() => ReactDOM.render(<Comp style={styles} />, root)).toWarnDev([
116
+ 'Warning: Unsupported style property -ms-transform. Did you mean msTransform?' +
117
+ '\n in div (at **)' +
118
+ '\n in Comp (at **)',
119
+ 'Warning: Unsupported style property -webkit-transform. Did you mean WebkitTransform?' +
120
+ '\n in div (at **)' +
121
+ '\n in Comp (at **)',
122
+ ]);
123
});
124
125
it('warns when miscapitalizing vendored style names', () => {
@@ -154,25 +139,19 @@ describe('CSSPropertyOperations', () => {
139
}
140
}
141
157
- spyOnDev(console, 'error');
142
const root = document.createElement('div');
159
- ReactDOM.render(<Comp />, root);
160
- if (__DEV__) {
143
+
144
+ expect(() => ReactDOM.render(<Comp />, root)).toWarnDev([
145
// msTransform is correct already and shouldn't warn
162
- expect(console.error.calls.count()).toBe(2);
163
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
164
- 'Warning: Unsupported vendor-prefixed style property oTransform. ' +
165
- 'Did you mean OTransform?' +
166
- '\n in div (at **)' +
167
- '\n in Comp (at **)',
168
- );
169
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toEqual(
170
- 'Warning: Unsupported vendor-prefixed style property webkitTransform. ' +
171
- 'Did you mean WebkitTransform?' +
172
- '\n in div (at **)' +
173
- '\n in Comp (at **)',
174
- );
175
- }
146
+ 'Warning: Unsupported vendor-prefixed style property oTransform. ' +
147
+ 'Did you mean OTransform?' +
148
+ '\n in div (at **)' +
149
+ '\n in Comp (at **)',
150
+ 'Warning: Unsupported vendor-prefixed style property webkitTransform. ' +
151
+ 'Did you mean WebkitTransform?' +
152
+ '\n in div (at **)' +
153
+ '\n in Comp (at **)',
154
+ ]);
155
});
156
157
it('should warn about style having a trailing semicolon', () => {
@@ -193,24 +172,18 @@ describe('CSSPropertyOperations', () => {
172
}
173
}
174
196
- spyOnDev(console, 'error');
175
const root = document.createElement('div');
198
- ReactDOM.render(<Comp />, root);
199
- if (__DEV__) {
200
- expect(console.error.calls.count()).toBe(2);
201
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
202
- "Warning: Style property values shouldn't contain a semicolon. " +
203
- 'Try "backgroundColor: blue" instead.' +
204
- '\n in div (at **)' +
205
- '\n in Comp (at **)',
206
- );
207
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toEqual(
208
- "Warning: Style property values shouldn't contain a semicolon. " +
209
- 'Try "color: red" instead.' +
210
- '\n in div (at **)' +
211
- '\n in Comp (at **)',
212
- );
213
- }
176
+
177
+ expect(() => ReactDOM.render(<Comp />, root)).toWarnDev([
178
+ "Warning: Style property values shouldn't contain a semicolon. " +
179
+ 'Try "backgroundColor: blue" instead.' +
180
+ '\n in div (at **)' +
181
+ '\n in Comp (at **)',
182
+ "Warning: Style property values shouldn't contain a semicolon. " +
183
+ 'Try "color: red" instead.' +
184
+ '\n in div (at **)' +
185
+ '\n in Comp (at **)',
186
+ ]);
187
});
188
189
it('should warn about style containing a NaN value', () => {
@@ -222,18 +195,13 @@ describe('CSSPropertyOperations', () => {
195
}
196
}
197
225
- spyOnDev(console, 'error');
198
const root = document.createElement('div');
227
- ReactDOM.render(<Comp />, root);
199
229
- if (__DEV__) {
230
- expect(console.error.calls.count()).toBe(1);
231
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
232
- 'Warning: `NaN` is an invalid value for the `fontSize` css style property.' +
233
- '\n in div (at **)' +
234
- '\n in Comp (at **)',
235
- );
236
- }
200
+ expect(() => ReactDOM.render(<Comp />, root)).toWarnDev(
201
+ 'Warning: `NaN` is an invalid value for the `fontSize` css style property.' +
202
+ '\n in div (at **)' +
203
+ '\n in Comp (at **)',
204
+ );
205
});
206
207
it('should not warn when setting CSS custom properties', () => {
@@ -256,18 +224,13 @@ describe('CSSPropertyOperations', () => {
224
}
225
}
226
259
- spyOnDev(console, 'error');
227
const root = document.createElement('div');
261
- ReactDOM.render(<Comp />, root);
228
263
- if (__DEV__) {
264
- expect(console.error.calls.count()).toBe(1);
265
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
266
- 'Warning: `Infinity` is an invalid value for the `fontSize` css style property.' +
267
- '\n in div (at **)' +
268
- '\n in Comp (at **)',
269
- );
270
- }
229
+ expect(() => ReactDOM.render(<Comp />, root)).toWarnDev(
230
+ 'Warning: `Infinity` is an invalid value for the `fontSize` css style property.' +
231
+ '\n in div (at **)' +
232
+ '\n in Comp (at **)',
233
+ );
234
});
235
236
it('should not add units to CSS custom properties', () => {
packages/react-dom/src/__tests__/DOMPropertyOperations-test.js
+7
-10
@@ -151,25 +151,22 @@ describe('DOMPropertyOperations', () => {
151
152
it('should not remove attributes for special properties', () => {
153
const container = document.createElement('div');
154
- spyOnDev(console, 'error');
154
ReactDOM.render(
155
<input type="text" value="foo" onChange={function() {}} />,
156
container,
157
);
158
expect(container.firstChild.getAttribute('value')).toBe('foo');
159
expect(container.firstChild.value).toBe('foo');
161
- ReactDOM.render(
162
- <input type="text" onChange={function() {}} />,
163
- container,
160
+ expect(() =>
161
+ ReactDOM.render(
162
+ <input type="text" onChange={function() {}} />,
163
+ container,
164
+ ),
165
+ ).toWarnDev(
166
+ 'A component is changing a controlled input of type text to be uncontrolled',
167
);
168
expect(container.firstChild.getAttribute('value')).toBe('foo');
169
expect(container.firstChild.value).toBe('foo');
167
- if (__DEV__) {
168
- expect(console.error.calls.count()).toBe(1);
169
- expect(console.error.calls.argsFor(0)[0]).toContain(
170
- 'A component is changing a controlled input of type text to be uncontrolled',
171
- );
172
- }
170
});
171
});
172
});
packages/react-dom/src/__tests__/EventPluginHub-test.js
+8
-12
@@ -22,21 +22,17 @@ describe('EventPluginHub', () => {
22
});
23
24
it('should prevent non-function listeners, at dispatch', () => {
25
- spyOnDev(console, 'error');
26
- const node = ReactTestUtils.renderIntoDocument(
27
- <div onClick="not a function" />,
25
+ let node;
26
+ expect(() => {
27
+ node = ReactTestUtils.renderIntoDocument(
28
+ <div onClick="not a function" />,
29
+ );
30
+ }).toWarnDev(
31
+ 'Expected `onClick` listener to be a function, instead got a value of `string` type.',
32
);
29
- expect(function() {
30
- ReactTestUtils.SimulateNative.click(node);
31
- }).toThrowError(
33
+ expect(() => ReactTestUtils.SimulateNative.click(node)).toThrowError(
34
'Expected `onClick` listener to be a function, instead got a value of `string` type.',
35
);
34
- if (__DEV__) {
35
- expect(console.error.calls.count()).toBe(1);
36
- expect(console.error.calls.argsFor(0)[0]).toContain(
37
- 'Expected `onClick` listener to be a function, instead got a value of `string` type.',
38
- );
39
- }
36
});
37
38
it('should not prevent null listeners, at dispatch', () => {
packages/react-dom/src/__tests__/ReactChildReconciler-test.js
+34
-66
@@ -16,10 +16,6 @@ let React;
16
let ReactTestUtils;
17
18
describe('ReactChildReconciler', () => {
19
- function normalizeCodeLocInfo(str) {
20
- return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
21
- }
22
-
19
beforeEach(() => {
20
jest.resetModules();
21
@@ -46,30 +42,21 @@ describe('ReactChildReconciler', () => {
42
}
43
44
it('warns for duplicated array keys', () => {
49
- spyOnDev(console, 'error');
50
-
45
class Component extends React.Component {
46
render() {
47
return <div>{[<div key="1" />, <div key="1" />]}</div>;
48
}
49
}
50
57
- ReactTestUtils.renderIntoDocument(<Component />);
58
-
59
- if (__DEV__) {
60
- expect(console.error.calls.count()).toBe(1);
61
- expect(console.error.calls.argsFor(0)[0]).toContain(
62
- 'Keys should be unique so that components maintain their identity ' +
63
- 'across updates. Non-unique keys may cause children to be ' +
64
- 'duplicated and/or omitted — the behavior is unsupported and ' +
65
- 'could change in a future version.',
66
- );
67
- }
51
+ expect(() => ReactTestUtils.renderIntoDocument(<Component />)).toWarnDev(
52
+ 'Keys should be unique so that components maintain their identity ' +
53
+ 'across updates. Non-unique keys may cause children to be ' +
54
+ 'duplicated and/or omitted — the behavior is unsupported and ' +
55
+ 'could change in a future version.',
56
+ );
57
});
58
59
it('warns for duplicated array keys with component stack info', () => {
71
- spyOnDev(console, 'error');
72
-
60
class Component extends React.Component {
61
render() {
62
return <div>{[<div key="1" />, <div key="1" />]}</div>;
@@ -88,49 +75,35 @@ describe('ReactChildReconciler', () => {
75
}
76
}
77
91
- ReactTestUtils.renderIntoDocument(<GrandParent />);
92
-
93
- if (__DEV__) {
94
- expect(console.error.calls.count()).toBe(1);
95
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toContain(
96
- 'Encountered two children with the same key, `1`. ' +
97
- 'Keys should be unique so that components maintain their identity ' +
98
- 'across updates. Non-unique keys may cause children to be ' +
99
- 'duplicated and/or omitted — the behavior is unsupported and ' +
100
- 'could change in a future version.',
101
- ' in div (at **)\n' +
102
- ' in Component (at **)\n' +
103
- ' in Parent (at **)\n' +
104
- ' in GrandParent (at **)',
105
- );
106
- }
78
+ expect(() => ReactTestUtils.renderIntoDocument(<GrandParent />)).toWarnDev(
79
+ 'Encountered two children with the same key, `1`. ' +
80
+ 'Keys should be unique so that components maintain their identity ' +
81
+ 'across updates. Non-unique keys may cause children to be ' +
82
+ 'duplicated and/or omitted — the behavior is unsupported and ' +
83
+ 'could change in a future version.',
84
+ ' in div (at **)\n' +
85
+ ' in Component (at **)\n' +
86
+ ' in Parent (at **)\n' +
87
+ ' in GrandParent (at **)',
88
+ );
89
});
90
91
it('warns for duplicated iterable keys', () => {
110
- spyOnDev(console, 'error');
111
-
92
class Component extends React.Component {
93
render() {
94
return <div>{createIterable([<div key="1" />, <div key="1" />])}</div>;
95
}
96
}
97
118
- ReactTestUtils.renderIntoDocument(<Component />);
119
-
120
- if (__DEV__) {
121
- expect(console.error.calls.count()).toBe(1);
122
- expect(console.error.calls.argsFor(0)[0]).toContain(
123
- 'Keys should be unique so that components maintain their identity ' +
124
- 'across updates. Non-unique keys may cause children to be ' +
125
- 'duplicated and/or omitted — the behavior is unsupported and ' +
126
- 'could change in a future version.',
127
- );
128
- }
98
+ expect(() => ReactTestUtils.renderIntoDocument(<Component />)).toWarnDev(
99
+ 'Keys should be unique so that components maintain their identity ' +
100
+ 'across updates. Non-unique keys may cause children to be ' +
101
+ 'duplicated and/or omitted — the behavior is unsupported and ' +
102
+ 'could change in a future version.',
103
+ );
104
});
105
106
it('warns for duplicated iterable keys with component stack info', () => {
132
- spyOnDev(console, 'error');
133
-
107
class Component extends React.Component {
108
render() {
109
return <div>{createIterable([<div key="1" />, <div key="1" />])}</div>;
@@ -149,21 +122,16 @@ describe('ReactChildReconciler', () => {
122
}
123
}
124
152
- ReactTestUtils.renderIntoDocument(<GrandParent />);
153
-
154
- if (__DEV__) {
155
- expect(console.error.calls.count()).toBe(1);
156
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toContain(
157
- 'Encountered two children with the same key, `1`. ' +
158
- 'Keys should be unique so that components maintain their identity ' +
159
- 'across updates. Non-unique keys may cause children to be ' +
160
- 'duplicated and/or omitted — the behavior is unsupported and ' +
161
- 'could change in a future version.',
162
- ' in div (at **)\n' +
163
- ' in Component (at **)\n' +
164
- ' in Parent (at **)\n' +
165
- ' in GrandParent (at **)',
166
- );
167
- }
125
+ expect(() => ReactTestUtils.renderIntoDocument(<GrandParent />)).toWarnDev(
126
+ 'Encountered two children with the same key, `1`. ' +
127
+ 'Keys should be unique so that components maintain their identity ' +
128
+ 'across updates. Non-unique keys may cause children to be ' +
129
+ 'duplicated and/or omitted — the behavior is unsupported and ' +
130
+ 'could change in a future version.',
131
+ ' in div (at **)\n' +
132
+ ' in Component (at **)\n' +
133
+ ' in Parent (at **)\n' +
134
+ ' in GrandParent (at **)',
135
+ );
136
});
137
});
packages/react/src/__tests__/ReactChildren-test.js
+19
-45
@@ -13,10 +13,6 @@ describe('ReactChildren', () => {
13
let React;
14
let ReactTestUtils;
15
16
- function normalizeCodeLocInfo(str) {
17
- return str && str.replace(/at .+?:\d+/g, 'at **');
18
- }
19
-
16
beforeEach(() => {
17
jest.resetModules();
18
React = require('react');
@@ -352,7 +348,6 @@ describe('ReactChildren', () => {
348
});
349
350
it('should be called for each child in an iterable without keys', () => {
355
- spyOnDev(console, 'error');
351
const threeDivIterable = {
352
'@@iterator': function() {
353
let i = 0;
@@ -374,7 +369,10 @@ describe('ReactChildren', () => {
369
return kid;
370
});
371
377
- const instance = <div>{threeDivIterable}</div>;
372
+ let instance;
373
+ expect(() => (instance = <div>{threeDivIterable}</div>)).toWarnDev(
374
+ 'Warning: Each child in an array or iterator should have a unique "key" prop.',
375
+ );
376
377
function assertCalls() {
378
expect(callback.calls.count()).toBe(3);
@@ -386,13 +384,6 @@ describe('ReactChildren', () => {
384
385
React.Children.forEach(instance.props.children, callback, context);
386
assertCalls();
389
- if (__DEV__) {
390
- expect(console.error.calls.count()).toBe(1);
391
- expect(console.error.calls.argsFor(0)[0]).toContain(
392
- 'Warning: Each child in an array or iterator should have a unique "key" prop.',
393
- );
394
- console.error.calls.reset();
395
- }
387
388
const mappedChildren = React.Children.map(
389
instance.props.children,
@@ -400,9 +391,6 @@ describe('ReactChildren', () => {
391
context,
392
);
393
assertCalls();
403
- if (__DEV__) {
404
- expect(console.error.calls.count()).toBe(0);
405
- }
394
expect(mappedChildren).toEqual([
395
<div key=".0" />,
396
<div key=".1" />,
@@ -958,28 +946,23 @@ describe('ReactChildren', () => {
946
947
describe('with fragments enabled', () => {
948
it('warns for keys for arrays of elements in a fragment', () => {
961
- spyOnDev(console, 'error');
949
class ComponentReturningArray extends React.Component {
950
render() {
951
return [<div />, <div />];
952
}
953
}
954
968
- ReactTestUtils.renderIntoDocument(<ComponentReturningArray />);
969
-
970
- if (__DEV__) {
971
- expect(console.error.calls.count()).toBe(1);
972
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
973
- 'Warning: ' +
974
- 'Each child in an array or iterator should have a unique "key" prop.' +
975
- ' See https://fb.me/react-warning-keys for more information.' +
976
- '\n in ComponentReturningArray (at **)',
977
- );
978
- }
955
+ expect(() =>
956
+ ReactTestUtils.renderIntoDocument(<ComponentReturningArray />),
957
+ ).toWarnDev(
958
+ 'Warning: ' +
959
+ 'Each child in an array or iterator should have a unique "key" prop.' +
960
+ ' See https://fb.me/react-warning-keys for more information.' +
961
+ '\n in ComponentReturningArray (at **)',
962
+ );
963
});
964
965
it('does not warn when there are keys on elements in a fragment', () => {
982
- spyOnDev(console, 'error');
966
class ComponentReturningArray extends React.Component {
967
render() {
968
return [<div key="foo" />, <div key="bar" />];
@@ -987,25 +970,16 @@ describe('ReactChildren', () => {
970
}
971
972
ReactTestUtils.renderIntoDocument(<ComponentReturningArray />);
990
-
991
- if (__DEV__) {
992
- expect(console.error.calls.count()).toBe(0);
993
- }
973
});
974
975
it('warns for keys for arrays at the top level', () => {
997
- spyOnDev(console, 'error');
998
-
999
- ReactTestUtils.renderIntoDocument([<div />, <div />]);
1000
-
1001
- if (__DEV__) {
1002
- expect(console.error.calls.count()).toBe(1);
1003
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1004
- 'Warning: ' +
1005
- 'Each child in an array or iterator should have a unique "key" prop.' +
1006
- ' See https://fb.me/react-warning-keys for more information.',
1007
- );
1008
- }
976
+ expect(() =>
977
+ ReactTestUtils.renderIntoDocument([<div />, <div />]),
978
+ ).toWarnDev(
979
+ 'Warning: ' +
980
+ 'Each child in an array or iterator should have a unique "key" prop.' +
981
+ ' See https://fb.me/react-warning-keys for more information.',
982
+ );
983
});
984
});
985
});
packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee
+38
-63
@@ -46,14 +46,12 @@ describe 'ReactCoffeeScriptClass', ->
46
expect(Foo.name).toBe 'Foo'
47
48
it 'throws if no render function is defined', ->
49
- spyOnDev console, 'error'
49
class Foo extends React.Component
50
expect(->
52
- ReactDOM.render React.createElement(Foo), container
53
- ).toThrow()
54
- if __DEV__
55
- expect(console.error.calls.count()).toBe(1)
56
- expect(console.error.calls.argsFor(0)[0]).toContain('No `render` method found on the returned component instance')
51
+ expect(->
52
+ ReactDOM.render React.createElement(Foo), container
53
+ ).toThrow()
54
+ ).toWarnDev('No `render` method found on the returned component instance')
55
undefined
56
57
it 'renders a simple stateless component with prop', ->
@@ -150,8 +148,6 @@ describe 'ReactCoffeeScriptClass', ->
148
undefined
149
150
it 'should warn with non-object in the initial state property', ->
153
- spyOnDev console, 'error'
154
-
151
[['an array'], 'a string', 1234].forEach (state) ->
152
class Foo extends React.Component
153
constructor: ->
@@ -160,14 +156,9 @@ describe 'ReactCoffeeScriptClass', ->
156
render: ->
157
span()
158
163
- test React.createElement(Foo), 'SPAN', ''
164
- if __DEV__
165
- expect(console.error.calls.count()).toBe 1
166
- expect(console.error.calls.argsFor(0)[0]).toContain(
167
- 'Foo.state: must be set to an object or null'
168
- )
169
- console.error.calls.reset()
170
-
159
+ expect(->
160
+ test React.createElement(Foo), 'SPAN', ''
161
+ ).toWarnDev('Foo.state: must be set to an object or null')
162
undefined
163
164
it 'should render with null in the initial state property', ->
@@ -287,7 +278,6 @@ describe 'ReactCoffeeScriptClass', ->
278
279
it 'warns when classic properties are defined on the instance,
280
but does not invoke them.', ->
290
- spyOnDev console, 'error'
281
getInitialStateWasCalled = false
282
getDefaultPropsWasCalled = false
283
class Foo extends React.Component
@@ -307,28 +297,20 @@ describe 'ReactCoffeeScriptClass', ->
297
span
298
className: 'foo'
299
310
- test React.createElement(Foo), 'SPAN', 'foo'
300
+ expect(->
301
+ test React.createElement(Foo), 'SPAN', 'foo'
302
+ ).toWarnDev([
303
+ 'getInitialState was defined on Foo, a plain JavaScript class.',
304
+ 'getDefaultProps was defined on Foo, a plain JavaScript class.',
305
+ 'propTypes was defined as an instance property on Foo.',
306
+ 'contextTypes was defined as an instance property on Foo.',
307
+ ])
308
expect(getInitialStateWasCalled).toBe false
309
expect(getDefaultPropsWasCalled).toBe false
313
- if __DEV__
314
- expect(console.error.calls.count()).toBe 4
315
- expect(console.error.calls.argsFor(0)[0]).toContain(
316
- 'getInitialState was defined on Foo, a plain JavaScript class.'
317
- )
318
- expect(console.error.calls.argsFor(1)[0]).toContain(
319
- 'getDefaultProps was defined on Foo, a plain JavaScript class.'
320
- )
321
- expect(console.error.calls.argsFor(2)[0]).toContain(
322
- 'propTypes was defined as an instance property on Foo.'
323
- )
324
- expect(console.error.calls.argsFor(3)[0]).toContain(
325
- 'contextTypes was defined as an instance property on Foo.'
326
- )
310
undefined
311
312
it 'does not warn about getInitialState() on class components
313
if state is also defined.', ->
331
- spyOnDev console, 'error'
314
class Foo extends React.Component
315
constructor: (props) ->
316
super props
@@ -342,12 +324,9 @@ describe 'ReactCoffeeScriptClass', ->
324
className: 'foo'
325
326
test React.createElement(Foo), 'SPAN', 'foo'
345
- if __DEV__
346
- expect(console.error.calls.count()).toBe 0
327
undefined
328
329
it 'should warn when misspelling shouldComponentUpdate', ->
350
- spyOnDev console, 'error'
330
class NamedComponent extends React.Component
331
componentShouldUpdate: ->
332
false
@@ -356,18 +335,16 @@ describe 'ReactCoffeeScriptClass', ->
335
span
336
className: 'foo'
337
359
- test React.createElement(NamedComponent), 'SPAN', 'foo'
360
- if __DEV__
361
- expect(console.error.calls.count()).toBe 1
362
- expect(console.error.calls.argsFor(0)[0]).toBe(
363
- 'Warning: NamedComponent has a method called componentShouldUpdate().
364
- Did you mean shouldComponentUpdate()? The name is phrased as a
365
- question because the function is expected to return a value.'
366
- )
338
+ expect(->
339
+ test React.createElement(NamedComponent), 'SPAN', 'foo'
340
+ ).toWarnDev(
341
+ 'Warning: NamedComponent has a method called componentShouldUpdate().
342
+ Did you mean shouldComponentUpdate()? The name is phrased as a
343
+ question because the function is expected to return a value.'
344
+ )
345
undefined
346
347
it 'should warn when misspelling componentWillReceiveProps', ->
370
- spyOnDev console, 'error'
348
class NamedComponent extends React.Component
349
componentWillRecieveProps: ->
350
false
@@ -376,29 +353,27 @@ describe 'ReactCoffeeScriptClass', ->
353
span
354
className: 'foo'
355
379
- test React.createElement(NamedComponent), 'SPAN', 'foo'
380
- if __DEV__
381
- expect(console.error.calls.count()).toBe 1
382
- expect(console.error.calls.argsFor(0)[0]).toBe(
383
- 'Warning: NamedComponent has a method called componentWillRecieveProps().
384
- Did you mean componentWillReceiveProps()?'
385
- )
356
+ expect(->
357
+ test React.createElement(NamedComponent), 'SPAN', 'foo'
358
+ ).toWarnDev(
359
+ 'Warning: NamedComponent has a method called componentWillRecieveProps().
360
+ Did you mean componentWillReceiveProps()?'
361
+ )
362
undefined
363
364
it 'should throw AND warn when trying to access classic APIs', ->
389
- spyOnDev console, 'warn'
365
instance =
366
test Inner(name: 'foo'), 'DIV', 'foo'
392
- expect(-> instance.replaceState {}).toThrow()
393
- expect(-> instance.isMounted()).toThrow()
394
- if __DEV__
395
- expect(console.warn.calls.count()).toBe 2
396
- expect(console.warn.calls.argsFor(0)[0]).toContain(
397
- 'replaceState(...) is deprecated in plain JavaScript React classes'
398
- )
399
- expect(console.warn.calls.argsFor(1)[0]).toContain(
400
- 'isMounted(...) is deprecated in plain JavaScript React classes'
401
- )
367
+ expect(->
368
+ expect(-> instance.replaceState {}).toThrow()
369
+ ).toLowPriorityWarnDev(
370
+ 'replaceState(...) is deprecated in plain JavaScript React classes'
371
+ )
372
+ expect(->
373
+ expect(-> instance.isMounted()).toThrow()
374
+ ).toLowPriorityWarnDev(
375
+ 'isMounted(...) is deprecated in plain JavaScript React classes'
376
+ )
377
undefined
378
379
it 'supports this.context passed via getChildContext', ->
packages/react/src/__tests__/ReactContextValidator-test.js
+55
-90
@@ -21,10 +21,6 @@ let ReactDOM;
21
let ReactTestUtils;
22
23
describe('ReactContextValidator', () => {
24
- function normalizeCodeLocInfo(str) {
25
- return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
26
- }
27
-
24
beforeEach(() => {
25
jest.resetModules();
26
@@ -161,8 +157,6 @@ describe('ReactContextValidator', () => {
157
});
158
159
it('should check context types', () => {
164
- spyOnDev(console, 'error');
165
-
160
class Component extends React.Component {
161
render() {
162
return <div />;
@@ -172,17 +166,12 @@ describe('ReactContextValidator', () => {
166
foo: PropTypes.string.isRequired,
167
};
168
175
- ReactTestUtils.renderIntoDocument(<Component />);
176
-
177
- if (__DEV__) {
178
- expect(console.error.calls.count()).toBe(1);
179
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
180
- 'Warning: Failed context type: ' +
181
- 'The context `foo` is marked as required in `Component`, but its value ' +
182
- 'is `undefined`.\n' +
183
- ' in Component (at **)',
184
- );
185
- }
169
+ expect(() => ReactTestUtils.renderIntoDocument(<Component />)).toWarnDev(
170
+ 'Warning: Failed context type: ' +
171
+ 'The context `foo` is marked as required in `Component`, but its value ' +
172
+ 'is `undefined`.\n' +
173
+ ' in Component (at **)',
174
+ );
175
176
class ComponentInFooStringContext extends React.Component {
177
getChildContext() {
@@ -199,15 +188,11 @@ describe('ReactContextValidator', () => {
188
foo: PropTypes.string,
189
};
190
191
+ // No additional errors expected
192
ReactTestUtils.renderIntoDocument(
193
<ComponentInFooStringContext fooValue={'bar'} />,
194
);
195
206
- // Previous call should not error
207
- if (__DEV__) {
208
- expect(console.error.calls.count()).toBe(1);
209
- }
210
-
196
class ComponentInFooNumberContext extends React.Component {
197
getChildContext() {
198
return {
@@ -223,25 +208,20 @@ describe('ReactContextValidator', () => {
208
foo: PropTypes.number,
209
};
210
226
- ReactTestUtils.renderIntoDocument(
227
- <ComponentInFooNumberContext fooValue={123} />,
211
+ expect(() =>
212
+ ReactTestUtils.renderIntoDocument(
213
+ <ComponentInFooNumberContext fooValue={123} />,
214
+ ),
215
+ ).toWarnDev(
216
+ 'Warning: Failed context type: ' +
217
+ 'Invalid context `foo` of type `number` supplied ' +
218
+ 'to `Component`, expected `string`.\n' +
219
+ ' in Component (at **)\n' +
220
+ ' in ComponentInFooNumberContext (at **)',
221
);
229
-
230
- if (__DEV__) {
231
- expect(console.error.calls.count()).toBe(2);
232
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
233
- 'Warning: Failed context type: ' +
234
- 'Invalid context `foo` of type `number` supplied ' +
235
- 'to `Component`, expected `string`.\n' +
236
- ' in Component (at **)\n' +
237
- ' in ComponentInFooNumberContext (at **)',
238
- );
239
- }
222
});
223
224
it('should check child context types', () => {
243
- spyOnDev(console, 'error');
244
-
225
class Component extends React.Component {
226
getChildContext() {
227
return this.props.testContext;
@@ -256,46 +236,35 @@ describe('ReactContextValidator', () => {
236
bar: PropTypes.number,
237
};
238
259
- ReactTestUtils.renderIntoDocument(<Component testContext={{bar: 123}} />);
260
- if (__DEV__) {
261
- expect(console.error.calls.count()).toBe(1);
262
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
263
- 'Warning: Failed child context type: ' +
264
- 'The child context `foo` is marked as required in `Component`, but its ' +
265
- 'value is `undefined`.\n' +
266
- ' in Component (at **)',
267
- );
268
- }
269
-
270
- ReactTestUtils.renderIntoDocument(<Component testContext={{foo: 123}} />);
239
+ expect(() =>
240
+ ReactTestUtils.renderIntoDocument(<Component testContext={{bar: 123}} />),
241
+ ).toWarnDev(
242
+ 'Warning: Failed child context type: ' +
243
+ 'The child context `foo` is marked as required in `Component`, but its ' +
244
+ 'value is `undefined`.\n' +
245
+ ' in Component (at **)',
246
+ );
247
272
- if (__DEV__) {
273
- expect(console.error.calls.count()).toBe(2);
274
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
275
- 'Warning: Failed child context type: ' +
276
- 'Invalid child context `foo` of type `number` ' +
277
- 'supplied to `Component`, expected `string`.\n' +
278
- ' in Component (at **)',
279
- );
280
- }
248
+ expect(() =>
249
+ ReactTestUtils.renderIntoDocument(<Component testContext={{foo: 123}} />),
250
+ ).toWarnDev(
251
+ 'Warning: Failed child context type: ' +
252
+ 'Invalid child context `foo` of type `number` ' +
253
+ 'supplied to `Component`, expected `string`.\n' +
254
+ ' in Component (at **)',
255
+ );
256
257
+ // No additional errors expected
258
ReactTestUtils.renderIntoDocument(
259
<Component testContext={{foo: 'foo', bar: 123}} />,
260
);
261
262
ReactTestUtils.renderIntoDocument(<Component testContext={{foo: 'foo'}} />);
287
-
288
- if (__DEV__) {
289
- // Previous calls should not log errors
290
- expect(console.error.calls.count()).toBe(2);
291
- }
263
});
264
265
// TODO (bvaughn) Remove this test and the associated behavior in the future.
266
// It has only been added in Fiber to match the (unintentional) behavior in Stack.
267
it('should warn (but not error) if getChildContext method is missing', () => {
297
- spyOnDev(console, 'error');
298
-
268
class ComponentA extends React.Component {
269
static childContextTypes = {
270
foo: PropTypes.string.isRequired,
@@ -313,40 +282,28 @@ describe('ReactContextValidator', () => {
282
}
283
}
284
316
- ReactTestUtils.renderIntoDocument(<ComponentA />);
317
- if (__DEV__) {
318
- expect(console.error.calls.count()).toBe(1);
319
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
320
- 'Warning: ComponentA.childContextTypes is specified but there is no ' +
321
- 'getChildContext() method on the instance. You can either define ' +
322
- 'getChildContext() on ComponentA or remove childContextTypes from it.',
323
- );
324
- }
285
+ expect(() => ReactTestUtils.renderIntoDocument(<ComponentA />)).toWarnDev(
286
+ 'Warning: ComponentA.childContextTypes is specified but there is no ' +
287
+ 'getChildContext() method on the instance. You can either define ' +
288
+ 'getChildContext() on ComponentA or remove childContextTypes from it.',
289
+ );
290
291
// Warnings should be deduped by component type
292
ReactTestUtils.renderIntoDocument(<ComponentA />);
328
- if (__DEV__) {
329
- expect(console.error.calls.count()).toBe(1);
330
- }
331
- ReactTestUtils.renderIntoDocument(<ComponentB />);
332
- if (__DEV__) {
333
- expect(console.error.calls.count()).toBe(2);
334
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
335
- 'Warning: ComponentB.childContextTypes is specified but there is no ' +
336
- 'getChildContext() method on the instance. You can either define ' +
337
- 'getChildContext() on ComponentB or remove childContextTypes from it.',
338
- );
339
- }
293
+
294
+ expect(() => ReactTestUtils.renderIntoDocument(<ComponentB />)).toWarnDev(
295
+ 'Warning: ComponentB.childContextTypes is specified but there is no ' +
296
+ 'getChildContext() method on the instance. You can either define ' +
297
+ 'getChildContext() on ComponentB or remove childContextTypes from it.',
298
+ );
299
});
300
301
// TODO (bvaughn) Remove this test and the associated behavior in the future.
302
// It has only been added in Fiber to match the (unintentional) behavior in Stack.
303
it('should pass parent context if getChildContext method is missing', () => {
345
- spyOnDev(console, 'error');
346
-
304
class ParentContextProvider extends React.Component {
305
static childContextTypes = {
349
- foo: PropTypes.number,
306
+ foo: PropTypes.string,
307
};
308
getChildContext() {
309
return {
@@ -379,7 +336,15 @@ describe('ReactContextValidator', () => {
336
foo: PropTypes.string.isRequired,
337
};
338
382
- ReactTestUtils.renderIntoDocument(<ParentContextProvider />);
339
+ expect(() =>
340
+ ReactTestUtils.renderIntoDocument(<ParentContextProvider />),
341
+ ).toWarnDev([
342
+ 'Warning: MiddleMissingContext.childContextTypes is specified but there is no getChildContext() method on the ' +
343
+ 'instance. You can either define getChildContext() on MiddleMissingContext or remove childContextTypes from ' +
344
+ 'it.',
345
+ 'Warning: Failed context type: The context `bar` is marked as required in `ChildContextConsumer`, but its ' +
346
+ 'value is `undefined`.',
347
+ ]);
348
expect(childContext.bar).toBeUndefined();
349
expect(childContext.foo).toBe('FOO');
350
});
packages/react/src/__tests__/ReactES6Class-test.js
+37
-75
@@ -56,17 +56,13 @@ describe('ReactES6Class', () => {
56
});
57
58
it('throws if no render function is defined', () => {
59
- spyOnDev(console, 'error');
59
class Foo extends React.Component {}
61
- expect(() => ReactDOM.render(<Foo />, container)).toThrow();
62
-
63
- if (__DEV__) {
64
- expect(console.error.calls.count()).toBe(1);
65
- expect(console.error.calls.argsFor(0)[0]).toBe(
66
- 'Warning: Foo(...): No `render` method found on the returned component ' +
67
- 'instance: you may have forgotten to define `render`.',
68
- );
69
- }
60
+ expect(() =>
61
+ expect(() => ReactDOM.render(<Foo />, container)).toThrow(),
62
+ ).toWarnDev(
63
+ 'Warning: Foo(...): No `render` method found on the returned component ' +
64
+ 'instance: you may have forgotten to define `render`.',
65
+ );
66
});
67
68
it('renders a simple stateless component with prop', () => {
@@ -164,7 +160,6 @@ describe('ReactES6Class', () => {
160
});
161
162
it('should warn with non-object in the initial state property', () => {
167
- spyOnDev(console, 'error');
163
[['an array'], 'a string', 1234].forEach(function(state) {
164
class Foo extends React.Component {
165
constructor() {
@@ -175,14 +170,9 @@ describe('ReactES6Class', () => {
170
return <span />;
171
}
172
}
178
- test(<Foo />, 'SPAN', '');
179
- if (__DEV__) {
180
- expect(console.error.calls.count()).toBe(1);
181
- expect(console.error.calls.argsFor(0)[0]).toContain(
182
- 'Foo.state: must be set to an object or null',
183
- );
184
- console.error.calls.reset();
185
- }
173
+ expect(() => test(<Foo />, 'SPAN', '')).toWarnDev(
174
+ 'Foo.state: must be set to an object or null',
175
+ );
176
});
177
});
178
@@ -310,7 +300,6 @@ describe('ReactES6Class', () => {
300
});
301
302
it('warns when classic properties are defined on the instance, but does not invoke them.', () => {
313
- spyOnDev(console, 'error');
303
let getDefaultPropsWasCalled = false;
304
let getInitialStateWasCalled = false;
305
class Foo extends React.Component {
@@ -331,28 +320,18 @@ describe('ReactES6Class', () => {
320
return <span className="foo" />;
321
}
322
}
334
- test(<Foo />, 'SPAN', 'foo');
323
+
324
+ expect(() => test(<Foo />, 'SPAN', 'foo')).toWarnDev([
325
+ 'getInitialState was defined on Foo, a plain JavaScript class.',
326
+ 'getDefaultProps was defined on Foo, a plain JavaScript class.',
327
+ 'propTypes was defined as an instance property on Foo.',
328
+ 'contextTypes was defined as an instance property on Foo.',
329
+ ]);
330
expect(getInitialStateWasCalled).toBe(false);
331
expect(getDefaultPropsWasCalled).toBe(false);
337
- if (__DEV__) {
338
- expect(console.error.calls.count()).toBe(4);
339
- expect(console.error.calls.argsFor(0)[0]).toContain(
340
- 'getInitialState was defined on Foo, a plain JavaScript class.',
341
- );
342
- expect(console.error.calls.argsFor(1)[0]).toContain(
343
- 'getDefaultProps was defined on Foo, a plain JavaScript class.',
344
- );
345
- expect(console.error.calls.argsFor(2)[0]).toContain(
346
- 'propTypes was defined as an instance property on Foo.',
347
- );
348
- expect(console.error.calls.argsFor(3)[0]).toContain(
349
- 'contextTypes was defined as an instance property on Foo.',
350
- );
351
- }
332
});
333
334
it('does not warn about getInitialState() on class components if state is also defined.', () => {
355
- spyOnDev(console, 'error');
335
class Foo extends React.Component {
336
state = this.getInitialState();
337
getInitialState() {
@@ -363,14 +342,9 @@ describe('ReactES6Class', () => {
342
}
343
}
344
test(<Foo />, 'SPAN', 'foo');
366
- if (__DEV__) {
367
- expect(console.error.calls.count()).toBe(0);
368
- }
345
});
346
347
it('should warn when misspelling shouldComponentUpdate', () => {
372
- spyOnDev(console, 'error');
373
-
348
class NamedComponent extends React.Component {
349
componentShouldUpdate() {
350
return false;
@@ -379,22 +353,16 @@ describe('ReactES6Class', () => {
353
return <span className="foo" />;
354
}
355
}
382
- test(<NamedComponent />, 'SPAN', 'foo');
356
384
- if (__DEV__) {
385
- expect(console.error.calls.count()).toBe(1);
386
- expect(console.error.calls.argsFor(0)[0]).toBe(
387
- 'Warning: ' +
388
- 'NamedComponent has a method called componentShouldUpdate(). Did you ' +
389
- 'mean shouldComponentUpdate()? The name is phrased as a question ' +
390
- 'because the function is expected to return a value.',
391
- );
392
- }
357
+ expect(() => test(<NamedComponent />, 'SPAN', 'foo')).toWarnDev(
358
+ 'Warning: ' +
359
+ 'NamedComponent has a method called componentShouldUpdate(). Did you ' +
360
+ 'mean shouldComponentUpdate()? The name is phrased as a question ' +
361
+ 'because the function is expected to return a value.',
362
+ );
363
});
364
365
it('should warn when misspelling componentWillReceiveProps', () => {
396
- spyOnDev(console, 'error');
397
-
366
class NamedComponent extends React.Component {
367
componentWillRecieveProps() {
368
return false;
@@ -403,32 +371,26 @@ describe('ReactES6Class', () => {
371
return <span className="foo" />;
372
}
373
}
406
- test(<NamedComponent />, 'SPAN', 'foo');
374
408
- if (__DEV__) {
409
- expect(console.error.calls.count()).toBe(1);
410
- expect(console.error.calls.argsFor(0)[0]).toBe(
411
- 'Warning: ' +
412
- 'NamedComponent has a method called componentWillRecieveProps(). Did ' +
413
- 'you mean componentWillReceiveProps()?',
414
- );
415
- }
375
+ expect(() => test(<NamedComponent />, 'SPAN', 'foo')).toWarnDev(
376
+ 'Warning: ' +
377
+ 'NamedComponent has a method called componentWillRecieveProps(). Did ' +
378
+ 'you mean componentWillReceiveProps()?',
379
+ );
380
});
381
382
it('should throw AND warn when trying to access classic APIs', () => {
419
- spyOnDev(console, 'warn');
383
const instance = test(<Inner name="foo" />, 'DIV', 'foo');
421
- expect(() => instance.replaceState({})).toThrow();
422
- expect(() => instance.isMounted()).toThrow();
423
- if (__DEV__) {
424
- expect(console.warn.calls.count()).toBe(2);
425
- expect(console.warn.calls.argsFor(0)[0]).toContain(
426
- 'replaceState(...) is deprecated in plain JavaScript React classes',
427
- );
428
- expect(console.warn.calls.argsFor(1)[0]).toContain(
429
- 'isMounted(...) is deprecated in plain JavaScript React classes',
430
- );
431
- }
384
+ expect(() =>
385
+ expect(() => instance.replaceState({})).toThrow(),
386
+ ).toLowPriorityWarnDev(
387
+ 'replaceState(...) is deprecated in plain JavaScript React classes',
388
+ );
389
+ expect(() =>
390
+ expect(() => instance.isMounted()).toThrow(),
391
+ ).toLowPriorityWarnDev(
392
+ 'isMounted(...) is deprecated in plain JavaScript React classes',
393
+ );
394
});
395
396
it('supports this.context passed via getChildContext', () => {
packages/react/src/__tests__/ReactElement-test.js
+18
-42
@@ -58,7 +58,6 @@ describe('ReactElement', () => {
58
});
59
60
it('should warn when `key` is being accessed on composite element', () => {
61
- spyOnDev(console, 'error');
61
const container = document.createElement('div');
62
class Child extends React.Component {
63
render() {
@@ -76,41 +75,25 @@ describe('ReactElement', () => {
75
);
76
}
77
}
79
- if (__DEV__) {
80
- expect(console.error.calls.count()).toBe(0);
81
- }
82
- ReactDOM.render(<Parent />, container);
83
- if (__DEV__) {
84
- expect(console.error.calls.count()).toBe(1);
85
- expect(console.error.calls.argsFor(0)[0]).toContain(
86
- 'Child: `key` is not a prop. Trying to access it will result ' +
87
- 'in `undefined` being returned. If you need to access the same ' +
88
- 'value within the child component, you should pass it as a different ' +
89
- 'prop. (https://fb.me/react-special-props)',
90
- );
91
- }
78
+ expect(() => ReactDOM.render(<Parent />, container)).toWarnDev(
79
+ 'Child: `key` is not a prop. Trying to access it will result ' +
80
+ 'in `undefined` being returned. If you need to access the same ' +
81
+ 'value within the child component, you should pass it as a different ' +
82
+ 'prop. (https://fb.me/react-special-props)',
83
+ );
84
});
85
86
it('should warn when `key` is being accessed on a host element', () => {
95
- spyOnDev(console, 'error');
87
const element = <div key="3" />;
97
- if (__DEV__) {
98
- expect(console.error.calls.count()).toBe(0);
99
- }
100
- void element.props.key;
101
- if (__DEV__) {
102
- expect(console.error.calls.count()).toBe(1);
103
- expect(console.error.calls.argsFor(0)[0]).toContain(
104
- 'div: `key` is not a prop. Trying to access it will result ' +
105
- 'in `undefined` being returned. If you need to access the same ' +
106
- 'value within the child component, you should pass it as a different ' +
107
- 'prop. (https://fb.me/react-special-props)',
108
- );
109
- }
88
+ expect(() => void element.props.key).toWarnDev(
89
+ 'div: `key` is not a prop. Trying to access it will result ' +
90
+ 'in `undefined` being returned. If you need to access the same ' +
91
+ 'value within the child component, you should pass it as a different ' +
92
+ 'prop. (https://fb.me/react-special-props)',
93
+ );
94
});
95
96
it('should warn when `ref` is being accessed', () => {
113
- spyOnDev(console, 'error');
97
const container = document.createElement('div');
98
class Child extends React.Component {
99
render() {
@@ -126,19 +109,12 @@ describe('ReactElement', () => {
109
);
110
}
111
}
129
- if (__DEV__) {
130
- expect(console.error.calls.count()).toBe(0);
131
- }
132
- ReactDOM.render(<Parent />, container);
133
- if (__DEV__) {
134
- expect(console.error.calls.count()).toBe(1);
135
- expect(console.error.calls.argsFor(0)[0]).toContain(
136
- 'Child: `ref` is not a prop. Trying to access it will result ' +
137
- 'in `undefined` being returned. If you need to access the same ' +
138
- 'value within the child component, you should pass it as a different ' +
139
- 'prop. (https://fb.me/react-special-props)',
140
- );
141
- }
112
+ expect(() => ReactDOM.render(<Parent />, container)).toWarnDev(
113
+ 'Child: `ref` is not a prop. Trying to access it will result ' +
114
+ 'in `undefined` being returned. If you need to access the same ' +
115
+ 'value within the child component, you should pass it as a different ' +
116
+ 'prop. (https://fb.me/react-special-props)',
117
+ );
118
});
119
120
it('allows a string to be passed as the type', () => {
packages/react/src/__tests__/ReactElementClone-test.js
+15
-23
@@ -257,16 +257,11 @@ describe('ReactElementClone', () => {
257
});
258
259
it('warns for keys for arrays of elements in rest args', () => {
260
- spyOnDev(console, 'error');
261
-
262
- React.cloneElement(<div />, null, [<div />, <div />]);
263
-
264
- if (__DEV__) {
265
- expect(console.error.calls.count()).toBe(1);
266
- expect(console.error.calls.argsFor(0)[0]).toContain(
267
- 'Each child in an array or iterator should have a unique "key" prop.',
268
- );
269
- }
260
+ expect(() =>
261
+ React.cloneElement(<div />, null, [<div />, <div />]),
262
+ ).toWarnDev(
263
+ 'Each child in an array or iterator should have a unique "key" prop.',
264
+ );
265
});
266
267
it('does not warns for arrays of elements with keys', () => {
@@ -282,7 +277,6 @@ describe('ReactElementClone', () => {
277
});
278
279
it('should check declared prop types after clone', () => {
285
- spyOnDev(console, 'error');
280
class Component extends React.Component {
281
static propTypes = {
282
color: PropTypes.string.isRequired,
@@ -303,18 +297,16 @@ describe('ReactElementClone', () => {
297
});
298
}
299
}
306
- ReactTestUtils.renderIntoDocument(React.createElement(GrandParent));
307
- if (__DEV__) {
308
- expect(console.error.calls.count()).toBe(1);
309
- expect(console.error.calls.argsFor(0)[0]).toBe(
310
- 'Warning: Failed prop type: ' +
311
- 'Invalid prop `color` of type `number` supplied to `Component`, ' +
312
- 'expected `string`.\n' +
313
- ' in Component (created by GrandParent)\n' +
314
- ' in Parent (created by GrandParent)\n' +
315
- ' in GrandParent',
316
- );
317
- }
300
+ expect(() =>
301
+ ReactTestUtils.renderIntoDocument(React.createElement(GrandParent)),
302
+ ).toWarnDev(
303
+ 'Warning: Failed prop type: ' +
304
+ 'Invalid prop `color` of type `number` supplied to `Component`, ' +
305
+ 'expected `string`.\n' +
306
+ ' in Component (created by GrandParent)\n' +
307
+ ' in Parent (created by GrandParent)\n' +
308
+ ' in GrandParent',
309
+ );
310
});
311
312
it('should ignore key and ref warning getters', () => {
packages/react/src/__tests__/ReactElementValidator-test.js
+157
-243
@@ -18,10 +18,6 @@ let ReactDOM;
18
let ReactTestUtils;
19
20
describe('ReactElementValidator', () => {
21
- function normalizeCodeLocInfo(str) {
22
- return str && str.replace(/at .+?:\d+/g, 'at **');
23
- }
24
-
21
let ComponentClass;
22
23
beforeEach(() => {
@@ -39,21 +35,16 @@ describe('ReactElementValidator', () => {
35
});
36
37
it('warns for keys for arrays of elements in rest args', () => {
42
- spyOnDev(console, 'error');
38
const Component = React.createFactory(ComponentClass);
39
45
- Component(null, [Component(), Component()]);
46
-
47
- if (__DEV__) {
48
- expect(console.error.calls.count()).toBe(1);
49
- expect(console.error.calls.argsFor(0)[0]).toContain(
50
- 'Each child in an array or iterator should have a unique "key" prop.',
51
- );
52
- }
40
+ expect(() => {
41
+ Component(null, [Component(), Component()]);
42
+ }).toWarnDev(
43
+ 'Each child in an array or iterator should have a unique "key" prop.',
44
+ );
45
});
46
47
it('warns for keys for arrays of elements with owner info', () => {
56
- spyOnDev(console, 'error');
48
const Component = React.createFactory(ComponentClass);
49
50
class InnerClass extends React.Component {
@@ -70,59 +61,46 @@ describe('ReactElementValidator', () => {
61
}
62
}
63
73
- ReactTestUtils.renderIntoDocument(React.createElement(ComponentWrapper));
74
-
75
- if (__DEV__) {
76
- expect(console.error.calls.count()).toBe(1);
77
- expect(console.error.calls.argsFor(0)[0]).toContain(
78
- 'Each child in an array or iterator should have a unique "key" prop.' +
79
- '\n\nCheck the render method of `InnerClass`. ' +
80
- 'It was passed a child from ComponentWrapper. ',
81
- );
82
- }
64
+ expect(() => {
65
+ ReactTestUtils.renderIntoDocument(React.createElement(ComponentWrapper));
66
+ }).toWarnDev(
67
+ 'Each child in an array or iterator should have a unique "key" prop.' +
68
+ '\n\nCheck the render method of `InnerClass`. ' +
69
+ 'It was passed a child from ComponentWrapper. ',
70
+ );
71
});
72
73
it('warns for keys for arrays with no owner or parent info', () => {
86
- spyOnDev(console, 'error');
87
-
74
function Anonymous() {
75
return <div />;
76
}
77
Object.defineProperty(Anonymous, 'name', {value: undefined});
78
79
const divs = [<div />, <div />];
94
- ReactTestUtils.renderIntoDocument(<Anonymous>{divs}</Anonymous>);
95
-
96
- if (__DEV__) {
97
- expect(console.error.calls.count()).toBe(1);
98
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
99
- 'Warning: Each child in an array or iterator should have a unique ' +
100
- '"key" prop. See https://fb.me/react-warning-keys for more information.\n' +
101
- ' in div (at **)',
102
- );
103
- }
80
+
81
+ expect(() => {
82
+ ReactTestUtils.renderIntoDocument(<Anonymous>{divs}</Anonymous>);
83
+ }).toWarnDev(
84
+ 'Warning: Each child in an array or iterator should have a unique ' +
85
+ '"key" prop. See https://fb.me/react-warning-keys for more information.\n' +
86
+ ' in div (at **)',
87
+ );
88
});
89
90
it('warns for keys for arrays of elements with no owner info', () => {
107
- spyOnDev(console, 'error');
108
-
91
const divs = [<div />, <div />];
110
- ReactTestUtils.renderIntoDocument(<div>{divs}</div>);
111
-
112
- if (__DEV__) {
113
- expect(console.error.calls.count()).toBe(1);
114
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
115
- 'Warning: Each child in an array or iterator should have a unique ' +
116
- '"key" prop.\n\nCheck the top-level render call using <div>. See ' +
117
- 'https://fb.me/react-warning-keys for more information.\n' +
118
- ' in div (at **)',
119
- );
120
- }
92
+
93
+ expect(() => {
94
+ ReactTestUtils.renderIntoDocument(<div>{divs}</div>);
95
+ }).toWarnDev(
96
+ 'Warning: Each child in an array or iterator should have a unique ' +
97
+ '"key" prop.\n\nCheck the top-level render call using <div>. See ' +
98
+ 'https://fb.me/react-warning-keys for more information.\n' +
99
+ ' in div (at **)',
100
+ );
101
});
102
103
it('warns for keys with component stack info', () => {
124
- spyOnDev(console, 'error');
125
-
104
function Component() {
105
return <div>{[<div />, <div />]}</div>;
106
}
@@ -135,20 +113,15 @@ describe('ReactElementValidator', () => {
113
return <Parent child={<Component />} />;
114
}
115
138
- ReactTestUtils.renderIntoDocument(<GrandParent />);
139
-
140
- if (__DEV__) {
141
- expect(console.error.calls.count()).toBe(1);
142
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
143
- 'Warning: Each child in an array or iterator should have a unique ' +
144
- '"key" prop.\n\nCheck the render method of `Component`. See ' +
145
- 'https://fb.me/react-warning-keys for more information.\n' +
146
- ' in div (at **)\n' +
147
- ' in Component (at **)\n' +
148
- ' in Parent (at **)\n' +
149
- ' in GrandParent (at **)',
150
- );
151
- }
116
+ expect(() => ReactTestUtils.renderIntoDocument(<GrandParent />)).toWarnDev(
117
+ 'Warning: Each child in an array or iterator should have a unique ' +
118
+ '"key" prop.\n\nCheck the render method of `Component`. See ' +
119
+ 'https://fb.me/react-warning-keys for more information.\n' +
120
+ ' in div (at **)\n' +
121
+ ' in Component (at **)\n' +
122
+ ' in Parent (at **)\n' +
123
+ ' in GrandParent (at **)',
124
+ );
125
});
126
127
it('does not warn for keys when passing children down', () => {
@@ -170,7 +143,6 @@ describe('ReactElementValidator', () => {
143
});
144
145
it('warns for keys for iterables of elements in rest args', () => {
173
- spyOnDev(console, 'error');
146
const Component = React.createFactory(ComponentClass);
147
148
const iterable = {
@@ -185,14 +157,9 @@ describe('ReactElementValidator', () => {
157
},
158
};
159
188
- Component(null, iterable);
189
-
190
- if (__DEV__) {
191
- expect(console.error.calls.count()).toBe(1);
192
- expect(console.error.calls.argsFor(0)[0]).toContain(
193
- 'Each child in an array or iterator should have a unique "key" prop.',
194
- );
195
- }
160
+ expect(() => Component(null, iterable)).toWarnDev(
161
+ 'Each child in an array or iterator should have a unique "key" prop.',
162
+ );
163
});
164
165
it('does not warns for arrays of elements with keys', () => {
@@ -238,7 +205,6 @@ describe('ReactElementValidator', () => {
205
// In this test, we're making sure that if a proptype error is found in a
206
// component, we give a small hint as to which parent instantiated that
207
// component as per warnings about key usage in ReactElementValidator.
241
- spyOnDev(console, 'error');
208
function MyComp(props) {
209
return React.createElement('div', null, 'My color is ' + props.color);
210
}
@@ -248,89 +214,72 @@ describe('ReactElementValidator', () => {
214
function ParentComp() {
215
return React.createElement(MyComp, {color: 123});
216
}
251
- ReactTestUtils.renderIntoDocument(React.createElement(ParentComp));
252
- if (__DEV__) {
253
- expect(console.error.calls.argsFor(0)[0]).toBe(
254
- 'Warning: Failed prop type: ' +
255
- 'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
256
- 'expected `string`.\n' +
257
- ' in MyComp (created by ParentComp)\n' +
258
- ' in ParentComp',
259
- );
260
- }
217
+ expect(() => {
218
+ ReactTestUtils.renderIntoDocument(React.createElement(ParentComp));
219
+ }).toWarnDev(
220
+ 'Warning: Failed prop type: ' +
221
+ 'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
222
+ 'expected `string`.\n' +
223
+ ' in MyComp (created by ParentComp)\n' +
224
+ ' in ParentComp',
225
+ );
226
});
227
228
it('gives a helpful error when passing invalid types', () => {
264
- spyOnDev(console, 'error');
265
- React.createElement(undefined);
266
- React.createElement(null);
267
- React.createElement(true);
268
- React.createElement({x: 17});
269
- React.createElement({});
270
- if (__DEV__) {
271
- expect(console.error.calls.count()).toBe(5);
272
- expect(console.error.calls.argsFor(0)[0]).toBe(
273
- 'Warning: React.createElement: type is invalid -- expected a string ' +
274
- '(for built-in components) or a class/function (for composite ' +
275
- 'components) but got: undefined. You likely forgot to export your ' +
276
- "component from the file it's defined in, or you might have mixed up " +
277
- 'default and named imports.',
278
- );
279
- expect(console.error.calls.argsFor(1)[0]).toBe(
280
- 'Warning: React.createElement: type is invalid -- expected a string ' +
281
- '(for built-in components) or a class/function (for composite ' +
282
- 'components) but got: null.',
283
- );
284
- expect(console.error.calls.argsFor(2)[0]).toBe(
285
- 'Warning: React.createElement: type is invalid -- expected a string ' +
286
- '(for built-in components) or a class/function (for composite ' +
287
- 'components) but got: boolean.',
288
- );
289
- expect(console.error.calls.argsFor(3)[0]).toBe(
290
- 'Warning: React.createElement: type is invalid -- expected a string ' +
291
- '(for built-in components) or a class/function (for composite ' +
292
- 'components) but got: object.',
293
- );
294
- expect(console.error.calls.argsFor(4)[0]).toBe(
295
- 'Warning: React.createElement: type is invalid -- expected a string ' +
296
- '(for built-in components) or a class/function (for composite ' +
297
- 'components) but got: object. You likely forgot to export your ' +
298
- "component from the file it's defined in, or you might have mixed up " +
299
- 'default and named imports.',
300
- );
301
- }
229
+ expect(() => {
230
+ React.createElement(undefined);
231
+ React.createElement(null);
232
+ React.createElement(true);
233
+ React.createElement({x: 17});
234
+ React.createElement({});
235
+ }).toWarnDev([
236
+ 'Warning: React.createElement: type is invalid -- expected a string ' +
237
+ '(for built-in components) or a class/function (for composite ' +
238
+ 'components) but got: undefined. You likely forgot to export your ' +
239
+ "component from the file it's defined in, or you might have mixed up " +
240
+ 'default and named imports.',
241
+ 'Warning: React.createElement: type is invalid -- expected a string ' +
242
+ '(for built-in components) or a class/function (for composite ' +
243
+ 'components) but got: null.',
244
+ 'Warning: React.createElement: type is invalid -- expected a string ' +
245
+ '(for built-in components) or a class/function (for composite ' +
246
+ 'components) but got: boolean.',
247
+ 'Warning: React.createElement: type is invalid -- expected a string ' +
248
+ '(for built-in components) or a class/function (for composite ' +
249
+ 'components) but got: object.',
250
+ 'Warning: React.createElement: type is invalid -- expected a string ' +
251
+ '(for built-in components) or a class/function (for composite ' +
252
+ 'components) but got: object. You likely forgot to export your ' +
253
+ "component from the file it's defined in, or you might have mixed up " +
254
+ 'default and named imports.',
255
+ ]);
256
+
257
+ // Should not log any additional warnings
258
React.createElement('div');
303
- if (__DEV__) {
304
- expect(console.error.calls.count()).toBe(5);
305
- }
259
});
260
261
it('includes the owner name when passing null, undefined, boolean, or number', () => {
309
- spyOnDev(console, 'error');
262
function ParentComp() {
263
return React.createElement(null);
264
}
313
- expect(function() {
314
- ReactTestUtils.renderIntoDocument(React.createElement(ParentComp));
315
- }).toThrowError(
316
- 'Element type is invalid: expected a string (for built-in components) ' +
317
- 'or a class/function (for composite components) but got: null.' +
318
- (__DEV__ ? '\n\nCheck the render method of `ParentComp`.' : ''),
319
- );
320
- if (__DEV__) {
321
- expect(console.error.calls.count()).toBe(1);
322
- expect(console.error.calls.argsFor(0)[0]).toBe(
323
- 'Warning: React.createElement: type is invalid -- expected a string ' +
324
- '(for built-in components) or a class/function (for composite ' +
325
- 'components) but got: null.' +
326
- '\n\nCheck the render method of `ParentComp`.\n in ParentComp',
265
+
266
+ expect(() => {
267
+ expect(() => {
268
+ ReactTestUtils.renderIntoDocument(React.createElement(ParentComp));
269
+ }).toThrowError(
270
+ 'Element type is invalid: expected a string (for built-in components) ' +
271
+ 'or a class/function (for composite components) but got: null.' +
272
+ (__DEV__ ? '\n\nCheck the render method of `ParentComp`.' : ''),
273
);
328
- }
274
+ }).toWarnDev(
275
+ 'Warning: React.createElement: type is invalid -- expected a string ' +
276
+ '(for built-in components) or a class/function (for composite ' +
277
+ 'components) but got: null.' +
278
+ '\n\nCheck the render method of `ParentComp`.\n in ParentComp',
279
+ );
280
});
281
282
it('should check default prop values', () => {
332
- spyOnDev(console, 'error');
333
-
283
class Component extends React.Component {
284
static propTypes = {prop: PropTypes.string.isRequired};
285
static defaultProps = {prop: null};
@@ -339,21 +288,16 @@ describe('ReactElementValidator', () => {
288
}
289
}
290
342
- ReactTestUtils.renderIntoDocument(React.createElement(Component));
343
-
344
- if (__DEV__) {
345
- expect(console.error.calls.count()).toBe(1);
346
- expect(console.error.calls.argsFor(0)[0]).toBe(
347
- 'Warning: Failed prop type: The prop `prop` is marked as required in ' +
348
- '`Component`, but its value is `null`.\n' +
349
- ' in Component',
350
- );
351
- }
291
+ expect(() =>
292
+ ReactTestUtils.renderIntoDocument(React.createElement(Component)),
293
+ ).toWarnDev(
294
+ 'Warning: Failed prop type: The prop `prop` is marked as required in ' +
295
+ '`Component`, but its value is `null`.\n' +
296
+ ' in Component',
297
+ );
298
});
299
300
it('should not check the default for explicit null', () => {
355
- spyOnDev(console, 'error');
356
-
301
class Component extends React.Component {
302
static propTypes = {prop: PropTypes.string.isRequired};
303
static defaultProps = {prop: 'text'};
@@ -362,23 +306,18 @@ describe('ReactElementValidator', () => {
306
}
307
}
308
365
- ReactTestUtils.renderIntoDocument(
366
- React.createElement(Component, {prop: null}),
367
- );
368
-
369
- if (__DEV__) {
370
- expect(console.error.calls.count()).toBe(1);
371
- expect(console.error.calls.argsFor(0)[0]).toBe(
372
- 'Warning: Failed prop type: The prop `prop` is marked as required in ' +
373
- '`Component`, but its value is `null`.\n' +
374
- ' in Component',
309
+ expect(() => {
310
+ ReactTestUtils.renderIntoDocument(
311
+ React.createElement(Component, {prop: null}),
312
);
376
- }
313
+ }).toWarnDev(
314
+ 'Warning: Failed prop type: The prop `prop` is marked as required in ' +
315
+ '`Component`, but its value is `null`.\n' +
316
+ ' in Component',
317
+ );
318
});
319
320
it('should check declared prop types', () => {
380
- spyOnDev(console, 'error');
381
-
321
class Component extends React.Component {
322
static propTypes = {
323
prop: PropTypes.string.isRequired,
@@ -388,36 +327,26 @@ describe('ReactElementValidator', () => {
327
}
328
}
329
391
- ReactTestUtils.renderIntoDocument(React.createElement(Component));
392
- ReactTestUtils.renderIntoDocument(
393
- React.createElement(Component, {prop: 42}),
394
- );
395
-
396
- if (__DEV__) {
397
- expect(console.error.calls.count()).toBe(2);
398
- expect(console.error.calls.argsFor(0)[0]).toBe(
399
- 'Warning: Failed prop type: ' +
400
- 'The prop `prop` is marked as required in `Component`, but its value ' +
401
- 'is `undefined`.\n' +
402
- ' in Component',
403
- );
404
-
405
- expect(console.error.calls.argsFor(1)[0]).toBe(
406
- 'Warning: Failed prop type: ' +
407
- 'Invalid prop `prop` of type `number` supplied to ' +
408
- '`Component`, expected `string`.\n' +
409
- ' in Component',
330
+ expect(() => {
331
+ ReactTestUtils.renderIntoDocument(React.createElement(Component));
332
+ ReactTestUtils.renderIntoDocument(
333
+ React.createElement(Component, {prop: 42}),
334
);
411
- }
412
-
335
+ }).toWarnDev([
336
+ 'Warning: Failed prop type: ' +
337
+ 'The prop `prop` is marked as required in `Component`, but its value ' +
338
+ 'is `undefined`.\n' +
339
+ ' in Component',
340
+ 'Warning: Failed prop type: ' +
341
+ 'Invalid prop `prop` of type `number` supplied to ' +
342
+ '`Component`, expected `string`.\n' +
343
+ ' in Component',
344
+ ]);
345
+
346
+ // Should not error for strings
347
ReactTestUtils.renderIntoDocument(
348
React.createElement(Component, {prop: 'string'}),
349
);
416
-
417
- if (__DEV__) {
418
- // Should not error for strings
419
- expect(console.error.calls.count()).toBe(2);
420
- }
350
});
351
352
it('should warn if a PropType creator is used as a PropType', () => {
@@ -432,24 +361,20 @@ describe('ReactElementValidator', () => {
361
}
362
}
363
435
- ReactTestUtils.renderIntoDocument(
436
- React.createElement(Component, {myProp: {value: 'hi'}}),
437
- );
438
-
439
- if (__DEV__) {
440
- expect(console.error.calls.count()).toBe(1);
441
- expect(console.error.calls.argsFor(0)[0]).toBe(
442
- 'Warning: Component: type specification of prop `myProp` is invalid; ' +
443
- 'the type checker function must return `null` or an `Error` but ' +
444
- 'returned a function. You may have forgotten to pass an argument to ' +
445
- 'the type checker creator (arrayOf, instanceOf, objectOf, oneOf, ' +
446
- 'oneOfType, and shape all require an argument).',
364
+ expect(() => {
365
+ ReactTestUtils.renderIntoDocument(
366
+ React.createElement(Component, {myProp: {value: 'hi'}}),
367
);
448
- }
368
+ }).toWarnDev(
369
+ 'Warning: Component: type specification of prop `myProp` is invalid; ' +
370
+ 'the type checker function must return `null` or an `Error` but ' +
371
+ 'returned a function. You may have forgotten to pass an argument to ' +
372
+ 'the type checker creator (arrayOf, instanceOf, objectOf, oneOf, ' +
373
+ 'oneOfType, and shape all require an argument).',
374
+ );
375
});
376
377
it('should warn if component declares PropTypes instead of propTypes', () => {
452
- spyOnDevAndProd(console, 'error');
378
class MisspelledPropTypesComponent extends React.Component {
379
static PropTypes = {
380
prop: PropTypes.string,
@@ -459,37 +384,29 @@ describe('ReactElementValidator', () => {
384
}
385
}
386
462
- ReactTestUtils.renderIntoDocument(
463
- React.createElement(MisspelledPropTypesComponent, {prop: 'Hi'}),
464
- );
465
- if (__DEV__) {
466
- expect(console.error.calls.count()).toBe(1);
467
- expect(console.error.calls.argsFor(0)[0]).toBe(
468
- 'Warning: Component MisspelledPropTypesComponent declared `PropTypes` ' +
469
- 'instead of `propTypes`. Did you misspell the property assignment?',
387
+ expect(() => {
388
+ ReactTestUtils.renderIntoDocument(
389
+ React.createElement(MisspelledPropTypesComponent, {prop: 'Hi'}),
390
);
471
- }
391
+ }).toWarnDev(
392
+ 'Warning: Component MisspelledPropTypesComponent declared `PropTypes` ' +
393
+ 'instead of `propTypes`. Did you misspell the property assignment?',
394
+ );
395
});
396
397
it('should warn when accessing .type on an element factory', () => {
475
- spyOnDev(console, 'warn');
398
function TestComponent() {
399
return <div />;
400
}
479
- const TestFactory = React.createFactory(TestComponent);
480
- expect(TestFactory.type).toBe(TestComponent);
481
- if (__DEV__) {
482
- expect(console.warn.calls.count()).toBe(1);
483
- expect(console.warn.calls.argsFor(0)[0]).toBe(
484
- 'Warning: Factory.type is deprecated. Access the class directly before ' +
485
- 'passing it to createFactory.',
486
- );
487
- }
401
+
402
+ let TestFactory = React.createFactory(TestComponent);
403
+ expect(() => TestFactory.type).toLowPriorityWarnDev(
404
+ 'Warning: Factory.type is deprecated. Access the class directly before ' +
405
+ 'passing it to createFactory.',
406
+ );
407
+
408
// Warn once, not again
409
expect(TestFactory.type).toBe(TestComponent);
490
- if (__DEV__) {
491
- expect(console.warn.calls.count()).toBe(1);
492
- }
410
});
411
412
it('does not warn when using DOM node as children', () => {
@@ -544,18 +461,15 @@ describe('ReactElementValidator', () => {
461
});
462
463
it('does not blow up on key warning with undefined type', () => {
547
- spyOnDev(console, 'error');
464
const Foo = undefined;
549
- void <Foo>{[<div />]}</Foo>;
550
- if (__DEV__) {
551
- expect(console.error.calls.count()).toBe(1);
552
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
553
- 'Warning: React.createElement: type is invalid -- expected a string ' +
554
- '(for built-in components) or a class/function (for composite ' +
555
- 'components) but got: undefined. You likely forgot to export your ' +
556
- "component from the file it's defined in, or you might have mixed up " +
557
- 'default and named imports.\n\nCheck your code at **.',
558
- );
559
- }
465
+ expect(() => {
466
+ void <Foo>{[<div />]}</Foo>;
467
+ }).toWarnDev(
468
+ 'Warning: React.createElement: type is invalid -- expected a string ' +
469
+ '(for built-in components) or a class/function (for composite ' +
470
+ 'components) but got: undefined. You likely forgot to export your ' +
471
+ "component from the file it's defined in, or you might have mixed up " +
472
+ 'default and named imports.\n\nCheck your code at **.',
473
+ );
474
});
475
});
packages/react/src/__tests__/ReactJSXElementValidator-test.js
+129
-224
@@ -17,10 +17,6 @@ let ReactTestUtils;
17
let PropTypes;
18
19
describe('ReactJSXElementValidator', () => {
20
- function normalizeCodeLocInfo(str) {
21
- return str && str.replace(/at .+?:\d+/g, 'at **');
22
- }
23
-
20
let Component;
21
let RequiredPropComponent;
22
@@ -48,23 +44,16 @@ describe('ReactJSXElementValidator', () => {
44
});
45
46
it('warns for keys for arrays of elements in children position', () => {
51
- spyOnDev(console, 'error');
52
-
53
- ReactTestUtils.renderIntoDocument(
54
- <Component>{[<Component />, <Component />]}</Component>,
47
+ expect(() =>
48
+ ReactTestUtils.renderIntoDocument(
49
+ <Component>{[<Component />, <Component />]}</Component>,
50
+ ),
51
+ ).toWarnDev(
52
+ 'Each child in an array or iterator should have a unique "key" prop.',
53
);
56
-
57
- if (__DEV__) {
58
- expect(console.error.calls.count()).toBe(1);
59
- expect(console.error.calls.argsFor(0)[0]).toContain(
60
- 'Each child in an array or iterator should have a unique "key" prop.',
61
- );
62
- }
54
});
55
56
it('warns for keys for arrays of elements with owner info', () => {
66
- spyOnDev(console, 'error');
67
-
57
class InnerComponent extends React.Component {
58
render() {
59
return <Component>{this.props.childSet}</Component>;
@@ -77,21 +66,16 @@ describe('ReactJSXElementValidator', () => {
66
}
67
}
68
80
- ReactTestUtils.renderIntoDocument(<ComponentWrapper />);
81
-
82
- if (__DEV__) {
83
- expect(console.error.calls.count()).toBe(1);
84
- expect(console.error.calls.argsFor(0)[0]).toContain(
85
- 'Each child in an array or iterator should have a unique "key" prop.' +
86
- '\n\nCheck the render method of `InnerComponent`. ' +
87
- 'It was passed a child from ComponentWrapper. ',
88
- );
89
- }
69
+ expect(() =>
70
+ ReactTestUtils.renderIntoDocument(<ComponentWrapper />),
71
+ ).toWarnDev(
72
+ 'Each child in an array or iterator should have a unique "key" prop.' +
73
+ '\n\nCheck the render method of `InnerComponent`. ' +
74
+ 'It was passed a child from ComponentWrapper. ',
75
+ );
76
});
77
78
it('warns for keys for iterables of elements in rest args', () => {
93
- spyOnDev(console, 'error');
94
-
79
const iterable = {
80
'@@iterator': function() {
81
let i = 0;
@@ -104,14 +88,11 @@ describe('ReactJSXElementValidator', () => {
88
},
89
};
90
107
- ReactTestUtils.renderIntoDocument(<Component>{iterable}</Component>);
108
-
109
- if (__DEV__) {
110
- expect(console.error.calls.count()).toBe(1);
111
- expect(console.error.calls.argsFor(0)[0]).toContain(
112
- 'Each child in an array or iterator should have a unique "key" prop.',
113
- );
114
- }
91
+ expect(() =>
92
+ ReactTestUtils.renderIntoDocument(<Component>{iterable}</Component>),
93
+ ).toWarnDev(
94
+ 'Each child in an array or iterator should have a unique "key" prop.',
95
+ );
96
});
97
98
it('does not warn for arrays of elements with keys', () => {
@@ -173,7 +154,6 @@ describe('ReactJSXElementValidator', () => {
154
// In this test, we're making sure that if a proptype error is found in a
155
// component, we give a small hint as to which parent instantiated that
156
// component as per warnings about key usage in ReactElementValidator.
176
- spyOnDev(console, 'error');
157
class MyComp extends React.Component {
158
render() {
159
return <div>My color is {this.color}</div>;
@@ -187,20 +167,16 @@ describe('ReactJSXElementValidator', () => {
167
return <MyComp color={123} />;
168
}
169
}
190
- ReactTestUtils.renderIntoDocument(<ParentComp />);
191
- if (__DEV__) {
192
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
193
- 'Warning: Failed prop type: ' +
194
- 'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
195
- 'expected `string`.\n' +
196
- ' in MyComp (at **)\n' +
197
- ' in ParentComp (at **)',
198
- );
199
- }
170
+ expect(() => ReactTestUtils.renderIntoDocument(<ParentComp />)).toWarnDev(
171
+ 'Warning: Failed prop type: ' +
172
+ 'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
173
+ 'expected `string`.\n' +
174
+ ' in MyComp (at **)\n' +
175
+ ' in ParentComp (at **)',
176
+ );
177
});
178
179
it('should update component stack after receiving next element', () => {
203
- spyOnDev(console, 'error');
180
function MyComp() {
181
return null;
182
}
@@ -221,21 +197,16 @@ describe('ReactJSXElementValidator', () => {
197
198
const container = document.createElement('div');
199
ReactDOM.render(<ParentComp warn={false} />, container);
224
- ReactDOM.render(<ParentComp warn={true} />, container);
225
-
226
- if (__DEV__) {
227
- expect(console.error.calls.count()).toBe(1);
228
- // The warning should have the full stack with line numbers.
229
- // If it doesn't, it means we're using information from the old element.
230
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
231
- 'Warning: Failed prop type: ' +
232
- 'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
233
- 'expected `string`.\n' +
234
- ' in MyComp (at **)\n' +
235
- ' in MiddleComp (at **)\n' +
236
- ' in ParentComp (at **)',
237
- );
238
- }
200
+ expect(() =>
201
+ ReactDOM.render(<ParentComp warn={true} />, container),
202
+ ).toWarnDev(
203
+ 'Warning: Failed prop type: ' +
204
+ 'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
205
+ 'expected `string`.\n' +
206
+ ' in MyComp (at **)\n' +
207
+ ' in MiddleComp (at **)\n' +
208
+ ' in ParentComp (at **)',
209
+ );
210
});
211
212
it('gives a helpful error when passing null, undefined, or boolean', () => {
@@ -243,100 +214,72 @@ describe('ReactJSXElementValidator', () => {
214
const Null = null;
215
const True = true;
216
const Div = 'div';
246
- spyOnDev(console, 'error');
247
- void <Undefined />;
248
- void <Null />;
249
- void <True />;
250
- if (__DEV__) {
251
- expect(console.error.calls.count()).toBe(3);
252
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
253
- 'Warning: React.createElement: type is invalid -- expected a string ' +
254
- '(for built-in components) or a class/function (for composite ' +
255
- 'components) but got: undefined. You likely forgot to export your ' +
256
- "component from the file it's defined in, or you might have mixed up " +
257
- 'default and named imports.' +
258
- '\n\nCheck your code at **.',
259
- );
260
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
261
- 'Warning: React.createElement: type is invalid -- expected a string ' +
262
- '(for built-in components) or a class/function (for composite ' +
263
- 'components) but got: null.' +
264
- '\n\nCheck your code at **.',
265
- );
266
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
267
- 'Warning: React.createElement: type is invalid -- expected a string ' +
268
- '(for built-in components) or a class/function (for composite ' +
269
- 'components) but got: boolean.' +
270
- '\n\nCheck your code at **.',
271
- );
272
- }
217
+ expect(() => void <Undefined />).toWarnDev(
218
+ 'Warning: React.createElement: type is invalid -- expected a string ' +
219
+ '(for built-in components) or a class/function (for composite ' +
220
+ 'components) but got: undefined. You likely forgot to export your ' +
221
+ "component from the file it's defined in, or you might have mixed up " +
222
+ 'default and named imports.' +
223
+ '\n\nCheck your code at **.',
224
+ );
225
+ expect(() => void <Null />).toWarnDev(
226
+ 'Warning: React.createElement: type is invalid -- expected a string ' +
227
+ '(for built-in components) or a class/function (for composite ' +
228
+ 'components) but got: null.' +
229
+ '\n\nCheck your code at **.',
230
+ );
231
+ expect(() => void <True />).toWarnDev(
232
+ 'Warning: React.createElement: type is invalid -- expected a string ' +
233
+ '(for built-in components) or a class/function (for composite ' +
234
+ 'components) but got: boolean.' +
235
+ '\n\nCheck your code at **.',
236
+ );
237
+ // No error expected
238
void <Div />;
274
- if (__DEV__) {
275
- expect(console.error.calls.count()).toBe(3);
276
- }
239
});
240
241
it('should check default prop values', () => {
280
- spyOnDev(console, 'error');
281
-
242
RequiredPropComponent.defaultProps = {prop: null};
243
284
- ReactTestUtils.renderIntoDocument(<RequiredPropComponent />);
285
-
286
- if (__DEV__) {
287
- expect(console.error.calls.count()).toBe(1);
288
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
289
- 'Warning: Failed prop type: The prop `prop` is marked as required in ' +
290
- '`RequiredPropComponent`, but its value is `null`.\n' +
291
- ' in RequiredPropComponent (at **)',
292
- );
293
- }
244
+ expect(() =>
245
+ ReactTestUtils.renderIntoDocument(<RequiredPropComponent />),
246
+ ).toWarnDev(
247
+ 'Warning: Failed prop type: The prop `prop` is marked as required in ' +
248
+ '`RequiredPropComponent`, but its value is `null`.\n' +
249
+ ' in RequiredPropComponent (at **)',
250
+ );
251
});
252
253
it('should not check the default for explicit null', () => {
297
- spyOnDev(console, 'error');
298
-
299
- ReactTestUtils.renderIntoDocument(<RequiredPropComponent prop={null} />);
300
-
301
- if (__DEV__) {
302
- expect(console.error.calls.count()).toBe(1);
303
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
304
- 'Warning: Failed prop type: The prop `prop` is marked as required in ' +
305
- '`RequiredPropComponent`, but its value is `null`.\n' +
306
- ' in RequiredPropComponent (at **)',
307
- );
308
- }
254
+ expect(() =>
255
+ ReactTestUtils.renderIntoDocument(<RequiredPropComponent prop={null} />),
256
+ ).toWarnDev(
257
+ 'Warning: Failed prop type: The prop `prop` is marked as required in ' +
258
+ '`RequiredPropComponent`, but its value is `null`.\n' +
259
+ ' in RequiredPropComponent (at **)',
260
+ );
261
});
262
263
it('should check declared prop types', () => {
312
- spyOnDev(console, 'error');
313
-
314
- ReactTestUtils.renderIntoDocument(<RequiredPropComponent />);
315
- ReactTestUtils.renderIntoDocument(<RequiredPropComponent prop={42} />);
316
-
317
- if (__DEV__) {
318
- expect(console.error.calls.count()).toBe(2);
319
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
320
- 'Warning: Failed prop type: ' +
321
- 'The prop `prop` is marked as required in `RequiredPropComponent`, but ' +
322
- 'its value is `undefined`.\n' +
323
- ' in RequiredPropComponent (at **)',
324
- );
325
-
326
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
327
- 'Warning: Failed prop type: ' +
328
- 'Invalid prop `prop` of type `number` supplied to ' +
329
- '`RequiredPropComponent`, expected `string`.\n' +
330
- ' in RequiredPropComponent (at **)',
331
- );
332
- }
264
+ expect(() =>
265
+ ReactTestUtils.renderIntoDocument(<RequiredPropComponent />),
266
+ ).toWarnDev(
267
+ 'Warning: Failed prop type: ' +
268
+ 'The prop `prop` is marked as required in `RequiredPropComponent`, but ' +
269
+ 'its value is `undefined`.\n' +
270
+ ' in RequiredPropComponent (at **)',
271
+ );
272
+ expect(() =>
273
+ ReactTestUtils.renderIntoDocument(<RequiredPropComponent prop={42} />),
274
+ ).toWarnDev(
275
+ 'Warning: Failed prop type: ' +
276
+ 'Invalid prop `prop` of type `number` supplied to ' +
277
+ '`RequiredPropComponent`, expected `string`.\n' +
278
+ ' in RequiredPropComponent (at **)',
279
+ );
280
281
+ // Should not error for strings
282
ReactTestUtils.renderIntoDocument(<RequiredPropComponent prop="string" />);
335
-
336
- if (__DEV__) {
337
- // Should not error for strings
338
- expect(console.error.calls.count()).toBe(2);
339
- }
283
});
284
285
it('should warn on invalid prop types', () => {
@@ -344,7 +287,6 @@ describe('ReactJSXElementValidator', () => {
287
// for us to issue a warning earlier than element creation when the error
288
// actually occurs. Since this step is skipped in production, we should just
289
// warn instead of throwing for this case.
347
- spyOnDev(console, 'error');
290
class NullPropTypeComponent extends React.Component {
291
render() {
292
return <span>{this.props.prop}</span>;
@@ -353,18 +295,15 @@ describe('ReactJSXElementValidator', () => {
295
NullPropTypeComponent.propTypes = {
296
prop: null,
297
};
356
- ReactTestUtils.renderIntoDocument(<NullPropTypeComponent />);
357
- if (__DEV__) {
358
- expect(console.error.calls.count()).toBe(1);
359
- expect(console.error.calls.argsFor(0)[0]).toContain(
360
- 'NullPropTypeComponent: prop type `prop` is invalid; it must be a ' +
361
- 'function, usually from the `prop-types` package,',
362
- );
363
- }
298
+ expect(() =>
299
+ ReactTestUtils.renderIntoDocument(<NullPropTypeComponent />),
300
+ ).toWarnDev(
301
+ 'NullPropTypeComponent: prop type `prop` is invalid; it must be a ' +
302
+ 'function, usually from the `prop-types` package,',
303
+ );
304
});
305
306
it('should warn on invalid context types', () => {
367
- spyOnDev(console, 'error');
307
class NullContextTypeComponent extends React.Component {
308
render() {
309
return <span>{this.props.prop}</span>;
@@ -373,18 +312,15 @@ describe('ReactJSXElementValidator', () => {
312
NullContextTypeComponent.contextTypes = {
313
prop: null,
314
};
376
- ReactTestUtils.renderIntoDocument(<NullContextTypeComponent />);
377
- if (__DEV__) {
378
- expect(console.error.calls.count()).toBe(1);
379
- expect(console.error.calls.argsFor(0)[0]).toContain(
380
- 'NullContextTypeComponent: context type `prop` is invalid; it must ' +
381
- 'be a function, usually from the `prop-types` package,',
382
- );
383
- }
315
+ expect(() =>
316
+ ReactTestUtils.renderIntoDocument(<NullContextTypeComponent />),
317
+ ).toWarnDev(
318
+ 'NullContextTypeComponent: context type `prop` is invalid; it must ' +
319
+ 'be a function, usually from the `prop-types` package,',
320
+ );
321
});
322
323
it('should warn if getDefaultProps is specificed on the class', () => {
387
- spyOnDev(console, 'error');
324
class GetDefaultPropsComponent extends React.Component {
325
render() {
326
return <span>{this.props.prop}</span>;
@@ -393,18 +329,15 @@ describe('ReactJSXElementValidator', () => {
329
GetDefaultPropsComponent.getDefaultProps = () => ({
330
prop: 'foo',
331
});
396
- ReactTestUtils.renderIntoDocument(<GetDefaultPropsComponent />);
397
- if (__DEV__) {
398
- expect(console.error.calls.count()).toBe(1);
399
- expect(console.error.calls.argsFor(0)[0]).toContain(
400
- 'getDefaultProps is only used on classic React.createClass definitions.' +
401
- ' Use a static property named `defaultProps` instead.',
402
- );
403
- }
332
+ expect(() =>
333
+ ReactTestUtils.renderIntoDocument(<GetDefaultPropsComponent />),
334
+ ).toWarnDev(
335
+ 'getDefaultProps is only used on classic React.createClass definitions.' +
336
+ ' Use a static property named `defaultProps` instead.',
337
+ );
338
});
339
340
it('should warn if component declares PropTypes instead of propTypes', () => {
407
- spyOnDevAndProd(console, 'error');
341
class MisspelledPropTypesComponent extends React.Component {
342
render() {
343
return <span>{this.props.prop}</span>;
@@ -413,46 +346,30 @@ describe('ReactJSXElementValidator', () => {
346
MisspelledPropTypesComponent.PropTypes = {
347
prop: PropTypes.string,
348
};
416
- ReactTestUtils.renderIntoDocument(
417
- <MisspelledPropTypesComponent prop="hi" />,
349
+ expect(() =>
350
+ ReactTestUtils.renderIntoDocument(
351
+ <MisspelledPropTypesComponent prop="hi" />,
352
+ ),
353
+ ).toWarnDev(
354
+ 'Warning: Component MisspelledPropTypesComponent declared `PropTypes` ' +
355
+ 'instead of `propTypes`. Did you misspell the property assignment?',
356
);
419
- if (__DEV__) {
420
- expect(console.error.calls.count()).toBe(1);
421
- expect(console.error.calls.argsFor(0)[0]).toBe(
422
- 'Warning: Component MisspelledPropTypesComponent declared `PropTypes` ' +
423
- 'instead of `propTypes`. Did you misspell the property assignment?',
424
- );
425
- }
357
});
358
359
it('warns for fragments with illegal attributes', () => {
429
- spyOnDev(console, 'error');
430
-
360
class Foo extends React.Component {
361
render() {
433
- return (
434
- <React.Fragment a={1} b={2}>
435
- hello
436
- </React.Fragment>
437
- );
362
+ return <React.Fragment a={1}>hello</React.Fragment>;
363
}
364
}
365
441
- ReactTestUtils.renderIntoDocument(<Foo />);
442
-
443
- if (__DEV__) {
444
- expect(console.error.calls.count()).toBe(1);
445
- expect(console.error.calls.argsFor(0)[0]).toContain('Invalid prop `');
446
- expect(console.error.calls.argsFor(0)[0]).toContain(
447
- '` supplied to `React.Fragment`. React.Fragment ' +
448
- 'can only have `key` and `children` props.',
449
- );
450
- }
366
+ expect(() => ReactTestUtils.renderIntoDocument(<Foo />)).toWarnDev(
367
+ 'Invalid prop `a` supplied to `React.Fragment`. React.Fragment ' +
368
+ 'can only have `key` and `children` props.',
369
+ );
370
});
371
372
it('warns for fragments with refs', () => {
454
- spyOnDev(console, 'error');
455
-
373
class Foo extends React.Component {
374
render() {
375
return (
@@ -466,14 +383,9 @@ describe('ReactJSXElementValidator', () => {
383
}
384
}
385
469
- ReactTestUtils.renderIntoDocument(<Foo />);
470
-
471
- if (__DEV__) {
472
- expect(console.error.calls.count()).toBe(1);
473
- expect(console.error.calls.argsFor(0)[0]).toContain(
474
- 'Invalid attribute `ref` supplied to `React.Fragment`.',
475
- );
476
- }
386
+ expect(() => ReactTestUtils.renderIntoDocument(<Foo />)).toWarnDev(
387
+ 'Invalid attribute `ref` supplied to `React.Fragment`.',
388
+ );
389
});
390
391
it('does not warn for fragments of multiple elements without keys', () => {
@@ -486,21 +398,14 @@ describe('ReactJSXElementValidator', () => {
398
});
399
400
it('warns for fragments of multiple elements with same key', () => {
489
- spyOnDev(console, 'error');
490
-
491
- ReactTestUtils.renderIntoDocument(
492
- <React.Fragment>
493
- <span key="a">1</span>
494
- <span key="a">2</span>
495
- <span key="b">3</span>
496
- </React.Fragment>,
497
- );
498
-
499
- if (__DEV__) {
500
- expect(console.error.calls.count()).toBe(1);
501
- expect(console.error.calls.argsFor(0)[0]).toContain(
502
- 'Encountered two children with the same key, `a`.',
503
- );
504
- }
401
+ expect(() =>
402
+ ReactTestUtils.renderIntoDocument(
403
+ <React.Fragment>
404
+ <span key="a">1</span>
405
+ <span key="a">2</span>
406
+ <span key="b">3</span>
407
+ </React.Fragment>,
408
+ ),
409
+ ).toWarnDev('Encountered two children with the same key, `a`.');
410
});
411
});
packages/react/src/__tests__/ReactPureComponent-test.js
+13
-24
@@ -62,7 +62,6 @@ describe('ReactPureComponent', () => {
62
});
63
64
it('can override shouldComponentUpdate', () => {
65
- spyOnDev(console, 'error');
65
let renders = 0;
66
class Component extends React.PureComponent {
67
render() {
@@ -73,18 +72,15 @@ describe('ReactPureComponent', () => {
72
return true;
73
}
74
}
75
+
76
const container = document.createElement('div');
77
+ expect(() => ReactDOM.render(<Component />, container)).toWarnDev(
78
+ 'Warning: ' +
79
+ 'Component has a method called shouldComponentUpdate(). ' +
80
+ 'shouldComponentUpdate should not be used when extending React.PureComponent. ' +
81
+ 'Please extend React.Component if shouldComponentUpdate is used.',
82
+ );
83
ReactDOM.render(<Component />, container);
78
- ReactDOM.render(<Component />, container);
79
- if (__DEV__) {
80
- expect(console.error.calls.count()).toBe(1);
81
- expect(console.error.calls.argsFor(0)[0]).toBe(
82
- 'Warning: ' +
83
- 'Component has a method called shouldComponentUpdate(). ' +
84
- 'shouldComponentUpdate should not be used when extending React.PureComponent. ' +
85
- 'Please extend React.Component if shouldComponentUpdate is used.',
86
- );
87
- }
84
expect(renders).toBe(2);
85
});
86
@@ -103,8 +99,6 @@ describe('ReactPureComponent', () => {
99
});
100
101
it('should warn when shouldComponentUpdate is defined on React.PureComponent', () => {
106
- spyOnDev(console, 'error');
107
-
102
class PureComponent extends React.PureComponent {
103
shouldComponentUpdate() {
104
return true;
@@ -114,16 +108,11 @@ describe('ReactPureComponent', () => {
108
}
109
}
110
const container = document.createElement('div');
117
- ReactDOM.render(<PureComponent />, container);
118
-
119
- if (__DEV__) {
120
- expect(console.error.calls.count()).toBe(1);
121
- expect(console.error.calls.argsFor(0)[0]).toBe(
122
- 'Warning: ' +
123
- 'PureComponent has a method called shouldComponentUpdate(). ' +
124
- 'shouldComponentUpdate should not be used when extending React.PureComponent. ' +
125
- 'Please extend React.Component if shouldComponentUpdate is used.',
126
- );
127
- }
111
+ expect(() => ReactDOM.render(<PureComponent />, container)).toWarnDev(
112
+ 'Warning: ' +
113
+ 'PureComponent has a method called shouldComponentUpdate(). ' +
114
+ 'shouldComponentUpdate should not be used when extending React.PureComponent. ' +
115
+ 'Please extend React.Component if shouldComponentUpdate is used.',
116
+ );
117
});
118
});
packages/react/src/__tests__/ReactTypeScriptClass-test.ts
+117
-159
@@ -26,7 +26,7 @@ class Inner extends React.Component {
26
render() {
27
attachedListener = this.props.onClick;
28
renderedName = this.props.name;
29
- return React.createElement('div', { className: this.props.name });
29
+ return React.createElement('div', {className: this.props.name});
30
}
31
}
32
@@ -45,11 +45,11 @@ function test(element, expectedTag, expectedClassName) {
45
46
// it preserves the name of the class for use in error messages
47
// it throws if no render function is defined
48
-class Empty extends React.Component { }
48
+class Empty extends React.Component {}
49
50
// it renders a simple stateless component with prop
51
class SimpleStateless extends React.Component {
52
- props : any;
52
+ props: any;
53
render() {
54
return React.createElement(Inner, {name: this.props.bar});
55
}
@@ -58,7 +58,7 @@ class SimpleStateless extends React.Component {
58
// it renders based on state using initial values in this.props
59
class InitialState extends React.Component {
60
state = {
61
- bar: this.props.initialValue
61
+ bar: this.props.initialValue,
62
};
63
render() {
64
return React.createElement('span', {className: this.state.bar});
@@ -69,10 +69,10 @@ class InitialState extends React.Component {
69
class StateBasedOnProps extends React.Component {
70
constructor(props) {
71
super(props);
72
- this.state = { bar: props.initialValue };
72
+ this.state = {bar: props.initialValue};
73
}
74
changeState() {
75
- this.setState({ bar: 'bar' });
75
+ this.setState({bar: 'bar'});
76
}
77
render() {
78
if (this.state.bar === 'foo') {
@@ -86,11 +86,11 @@ class StateBasedOnProps extends React.Component {
86
class StateBasedOnContext extends React.Component {
87
static contextTypes = {
88
tag: PropTypes.string,
89
- className: PropTypes.string
89
+ className: PropTypes.string,
90
};
91
state = {
92
tag: this.context.tag,
93
- className: this.context.className
93
+ className: this.context.className,
94
};
95
render() {
96
const Tag = this.state.tag;
@@ -101,10 +101,10 @@ class StateBasedOnContext extends React.Component {
101
class ProvideChildContextTypes extends React.Component {
102
static childContextTypes = {
103
tag: PropTypes.string,
104
- className: PropTypes.string
104
+ className: PropTypes.string,
105
};
106
getChildContext() {
107
- return { tag: 'span', className: 'foo' };
107
+ return {tag: 'span', className: 'foo'};
108
}
109
render() {
110
return React.createElement(StateBasedOnContext);
@@ -115,10 +115,10 @@ class ProvideChildContextTypes extends React.Component {
115
let renderCount = 0;
116
class RenderOnce extends React.Component {
117
state = {
118
- bar: this.props.initialValue
118
+ bar: this.props.initialValue,
119
};
120
componentWillMount() {
121
- this.setState({ bar: 'bar' });
121
+ this.setState({bar: 'bar'});
122
}
123
render() {
124
renderCount++;
@@ -157,57 +157,54 @@ class NullState extends React.Component {
157
// it setState through an event handler
158
class BoundEventHandler extends React.Component {
159
state = {
160
- bar: this.props.initialValue
160
+ bar: this.props.initialValue,
161
};
162
handleClick = () => {
163
- this.setState({ bar: 'bar' });
163
+ this.setState({bar: 'bar'});
164
};
165
render() {
166
- return (
167
- React.createElement(Inner, {
168
- name: this.state.bar,
169
- onClick: this.handleClick
170
- })
171
- );
166
+ return React.createElement(Inner, {
167
+ name: this.state.bar,
168
+ onClick: this.handleClick,
169
+ });
170
}
171
}
172
173
// it should not implicitly bind event handlers
174
class UnboundEventHandler extends React.Component {
175
state = {
178
- bar: this.props.initialValue
176
+ bar: this.props.initialValue,
177
};
178
handleClick() {
181
- this.setState({ bar: 'bar' });
179
+ this.setState({bar: 'bar'});
180
}
181
render() {
184
- return React.createElement(
185
- Inner, { name: this.state.bar, onClick: this.handleClick }
186
- );
182
+ return React.createElement(Inner, {
183
+ name: this.state.bar,
184
+ onClick: this.handleClick,
185
+ });
186
}
187
}
188
189
// it renders using forceUpdate even when there is no state
190
class ForceUpdateWithNoState extends React.Component {
192
- mutativeValue : string = this.props.initialValue;
191
+ mutativeValue: string = this.props.initialValue;
192
handleClick() {
193
this.mutativeValue = 'bar';
194
this.forceUpdate();
195
}
196
render() {
198
- return (
199
- React.createElement(Inner, {
200
- name: this.mutativeValue,
201
- onClick: this.handleClick.bind(this)}
202
- )
203
- );
197
+ return React.createElement(Inner, {
198
+ name: this.mutativeValue,
199
+ onClick: this.handleClick.bind(this),
200
+ });
201
}
202
}
203
204
// it will call all the normal life cycle methods
205
let lifeCycles = [];
206
class NormalLifeCycles extends React.Component {
210
- props : any;
207
+ props: any;
208
state = {};
209
componentWillMount() {
210
lifeCycles.push('will-mount');
@@ -278,15 +275,15 @@ class MisspelledComponent2 extends React.Component {
275
276
// it supports this.context passed via getChildContext
277
class ReadContext extends React.Component {
281
- static contextTypes = { bar: PropTypes.string };
278
+ static contextTypes = {bar: PropTypes.string};
279
render() {
283
- return React.createElement('div', { className: this.context.bar });
280
+ return React.createElement('div', {className: this.context.bar});
281
}
282
}
283
class ProvideContext extends React.Component {
287
- static childContextTypes = { bar: PropTypes.string };
284
+ static childContextTypes = {bar: PropTypes.string};
285
getChildContext() {
289
- return { bar: 'bar-through-context' };
286
+ return {bar: 'bar-through-context'};
287
}
288
render() {
289
return React.createElement(ReadContext);
@@ -303,7 +300,6 @@ class ClassicRefs extends React.Component {
300
// Describe the actual test cases.
301
302
describe('ReactTypeScriptClass', function() {
306
-
303
beforeEach(function() {
304
container = document.createElement('div');
305
attachedListener = null;
@@ -315,17 +311,14 @@ describe('ReactTypeScriptClass', function() {
311
});
312
313
it('throws if no render function is defined', function() {
318
- spyOnDev(console, 'error');
319
-
320
- expect(() => ReactDOM.render(React.createElement(Empty), container)).toThrow();
321
-
322
- if (__DEV__) {
323
- expect((<any>console.error).calls.count()).toBe(1);
324
- expect((<any>console.error).calls.argsFor(0)[0]).toBe(
325
- 'Warning: Empty(...): No `render` method found on the returned ' +
314
+ expect(() =>
315
+ expect(() =>
316
+ ReactDOM.render(React.createElement(Empty), container)
317
+ ).toThrow()
318
+ ).toWarnDev(
319
+ 'Warning: Empty(...): No `render` method found on the returned ' +
320
'component instance: you may have forgotten to define `render`.'
327
- );
328
- }
321
+ );
322
});
323
324
it('renders a simple stateless component with prop', function() {
@@ -362,31 +355,15 @@ describe('ReactTypeScriptClass', function() {
355
});
356
357
it('should warn with non-object in the initial state property', function() {
365
- spyOnDev(console, 'error');
366
- test(React.createElement(ArrayState), 'SPAN', '');
367
- if (__DEV__) {
368
- expect((<any>console.error).calls.count()).toBe(1);
369
- expect((<any>console.error).calls.argsFor(0)[0]).toContain(
370
- 'ArrayState.state: must be set to an object or null'
371
- );
372
- (<any>console.error).calls.reset()
373
- }
374
- test(React.createElement(StringState), 'SPAN', '');
375
- if (__DEV__) {
376
- expect((<any>console.error).calls.count()).toBe(1);
377
- expect((<any>console.error).calls.argsFor(0)[0]).toContain(
378
- 'StringState.state: must be set to an object or null'
379
- );
380
- (<any>console.error).calls.reset()
381
- }
382
- test(React.createElement(NumberState), 'SPAN', '');
383
- if (__DEV__) {
384
- expect((<any>console.error).calls.count()).toBe(1);
385
- expect((<any>console.error).calls.argsFor(0)[0]).toContain(
386
- 'NumberState.state: must be set to an object or null'
387
- );
388
- (<any>console.error).calls.reset()
389
- }
358
+ expect(() => test(React.createElement(ArrayState), 'SPAN', '')).toWarnDev(
359
+ 'ArrayState.state: must be set to an object or null'
360
+ );
361
+ expect(() => test(React.createElement(StringState), 'SPAN', '')).toWarnDev(
362
+ 'StringState.state: must be set to an object or null'
363
+ );
364
+ expect(() => test(React.createElement(NumberState), 'SPAN', '')).toWarnDev(
365
+ 'NumberState.state: must be set to an object or null'
366
+ );
367
});
368
369
it('should render with null in the initial state property', function() {
@@ -425,121 +402,103 @@ describe('ReactTypeScriptClass', function() {
402
it('will call all the normal life cycle methods', function() {
403
lifeCycles = [];
404
test(React.createElement(NormalLifeCycles, {value: 'foo'}), 'SPAN', 'foo');
428
- expect(lifeCycles).toEqual([
429
- 'will-mount',
430
- 'did-mount'
431
- ]);
405
+ expect(lifeCycles).toEqual(['will-mount', 'did-mount']);
406
lifeCycles = []; // reset
407
test(React.createElement(NormalLifeCycles, {value: 'bar'}), 'SPAN', 'bar');
408
expect(lifeCycles).toEqual([
435
- 'receive-props', { value: 'bar' },
436
- 'should-update', { value: 'bar' }, {},
437
- 'will-update', { value: 'bar' }, {},
438
- 'did-update', { value: 'foo' }, {}
409
+ 'receive-props',
410
+ {value: 'bar'},
411
+ 'should-update',
412
+ {value: 'bar'},
413
+ {},
414
+ 'will-update',
415
+ {value: 'bar'},
416
+ {},
417
+ 'did-update',
418
+ {value: 'foo'},
419
+ {},
420
]);
421
lifeCycles = []; // reset
422
ReactDOM.unmountComponentAtNode(container);
442
- expect(lifeCycles).toEqual([
443
- 'will-unmount'
444
- ]);
423
+ expect(lifeCycles).toEqual(['will-unmount']);
424
});
425
447
- it('warns when classic properties are defined on the instance, ' +
448
- 'but does not invoke them.', function() {
449
- spyOnDev(console, 'error');
450
- getInitialStateWasCalled = false;
451
- getDefaultPropsWasCalled = false;
452
- test(React.createElement(ClassicProperties), 'SPAN', 'foo');
453
- expect(getInitialStateWasCalled).toBe(false);
454
- expect(getDefaultPropsWasCalled).toBe(false);
455
-
456
- if (__DEV__) {
457
- expect((<any>console.error).calls.count()).toBe(4);
458
- expect((<any>console.error).calls.argsFor(0)[0]).toContain(
426
+ it(
427
+ 'warns when classic properties are defined on the instance, ' +
428
+ 'but does not invoke them.',
429
+ function() {
430
+ getInitialStateWasCalled = false;
431
+ getDefaultPropsWasCalled = false;
432
+ expect(() =>
433
+ test(React.createElement(ClassicProperties), 'SPAN', 'foo')
434
+ ).toWarnDev([
435
'getInitialState was defined on ClassicProperties, ' +
460
- 'a plain JavaScript class.'
461
- );
462
- expect((<any>console.error).calls.argsFor(1)[0]).toContain(
436
+ 'a plain JavaScript class.',
437
'getDefaultProps was defined on ClassicProperties, ' +
464
- 'a plain JavaScript class.'
465
- );
466
- expect((<any>console.error).calls.argsFor(2)[0]).toContain(
467
- 'propTypes was defined as an instance property on ClassicProperties.'
468
- );
469
- expect((<any>console.error).calls.argsFor(3)[0]).toContain(
470
- 'contextTypes was defined as an instance property on ClassicProperties.'
471
- );
438
+ 'a plain JavaScript class.',
439
+ 'propTypes was defined as an instance property on ClassicProperties.',
440
+ 'contextTypes was defined as an instance property on ClassicProperties.',
441
+ ]);
442
+ expect(getInitialStateWasCalled).toBe(false);
443
+ expect(getDefaultPropsWasCalled).toBe(false);
444
}
473
- });
474
-
475
- it('does not warn about getInitialState() on class components ' +
476
- 'if state is also defined.', () => {
477
- spyOnDev(console, 'error');
478
-
479
- class Example extends React.Component {
480
- state = {};
481
- getInitialState() {
482
- return {};
483
- }
484
- render() {
485
- return React.createElement('span', {className: 'foo'});
445
+ );
446
+
447
+ it(
448
+ 'does not warn about getInitialState() on class components ' +
449
+ 'if state is also defined.',
450
+ () => {
451
+ class Example extends React.Component {
452
+ state = {};
453
+ getInitialState() {
454
+ return {};
455
+ }
456
+ render() {
457
+ return React.createElement('span', {className: 'foo'});
458
+ }
459
}
487
- }
460
489
- test(React.createElement(Example), 'SPAN', 'foo');
490
- if (__DEV__) {
491
- expect((<any>console.error).calls.count()).toBe(0);
461
+ test(React.createElement(Example), 'SPAN', 'foo');
462
}
493
- });
463
+ );
464
465
it('should warn when misspelling shouldComponentUpdate', function() {
496
- spyOnDev(console, 'error');
497
-
498
- test(React.createElement(MisspelledComponent1), 'SPAN', 'foo');
499
-
500
- if (__DEV__) {
501
- expect((<any>console.error).calls.count()).toBe(1);
502
- expect((<any>console.error).calls.argsFor(0)[0]).toBe(
503
- 'Warning: ' +
466
+ expect(() =>
467
+ test(React.createElement(MisspelledComponent1), 'SPAN', 'foo')
468
+ ).toWarnDev(
469
+ 'Warning: ' +
470
'MisspelledComponent1 has a method called componentShouldUpdate(). Did ' +
471
'you mean shouldComponentUpdate()? The name is phrased as a question ' +
472
'because the function is expected to return a value.'
507
- );
508
- }
473
+ );
474
});
475
476
it('should warn when misspelling componentWillReceiveProps', function() {
512
- spyOnDev(console, 'error');
513
-
514
- test(React.createElement(MisspelledComponent2), 'SPAN', 'foo');
515
-
516
- if (__DEV__) {
517
- expect((<any>console.error).calls.count()).toBe(1);
518
- expect((<any>console.error).calls.argsFor(0)[0]).toBe(
519
- 'Warning: ' +
477
+ expect(() =>
478
+ test(React.createElement(MisspelledComponent2), 'SPAN', 'foo')
479
+ ).toWarnDev(
480
+ 'Warning: ' +
481
'MisspelledComponent2 has a method called componentWillRecieveProps(). ' +
482
'Did you mean componentWillReceiveProps()?'
522
- );
523
- }
483
+ );
484
});
485
486
it('should throw AND warn when trying to access classic APIs', function() {
527
- spyOnDev(console, 'warn');
487
const instance = test(
488
React.createElement(Inner, {name: 'foo'}),
530
- 'DIV','foo'
489
+ 'DIV',
490
+ 'foo'
491
+ );
492
+ expect(() =>
493
+ expect(() => instance.replaceState({})).toThrow()
494
+ ).toLowPriorityWarnDev(
495
+ 'replaceState(...) is deprecated in plain JavaScript React classes'
496
+ );
497
+ expect(() =>
498
+ expect(() => instance.isMounted()).toThrow()
499
+ ).toLowPriorityWarnDev(
500
+ 'isMounted(...) is deprecated in plain JavaScript React classes'
501
);
532
- expect(() => instance.replaceState({})).toThrow();
533
- expect(() => instance.isMounted()).toThrow();
534
- if (__DEV__) {
535
- expect((<any>console.warn).calls.count()).toBe(2);
536
- expect((<any>console.warn).calls.argsFor(0)[0]).toContain(
537
- 'replaceState(...) is deprecated in plain JavaScript React classes'
538
- );
539
- expect((<any>console.warn).calls.argsFor(1)[0]).toContain(
540
- 'isMounted(...) is deprecated in plain JavaScript React classes'
541
- );
542
- }
502
});
503
504
it('supports this.context passed via getChildContext', function() {
@@ -560,5 +519,4 @@ describe('ReactTypeScriptClass', function() {
519
const node = ReactDOM.findDOMNode(instance);
520
expect(node).toBe(container.firstChild);
521
});
563
-
522
});
packages/react/src/__tests__/createReactClassIntegration-test.js
+121
-158
@@ -52,121 +52,103 @@ describe('create-react-class-integration', () => {
52
});
53
54
it('should warn on invalid prop types', () => {
55
- spyOnDev(console, 'error');
56
- createReactClass({
57
- displayName: 'Component',
58
- propTypes: {
59
- prop: null,
60
- },
61
- render: function() {
62
- return <span>{this.props.prop}</span>;
63
- },
64
- });
65
- if (__DEV__) {
66
- expect(console.error.calls.count()).toBe(1);
67
- expect(console.error.calls.argsFor(0)[0]).toBe(
68
- 'Warning: Component: prop type `prop` is invalid; ' +
69
- 'it must be a function, usually from React.PropTypes.',
70
- );
71
- }
55
+ expect(() =>
56
+ createReactClass({
57
+ displayName: 'Component',
58
+ propTypes: {
59
+ prop: null,
60
+ },
61
+ render: function() {
62
+ return <span>{this.props.prop}</span>;
63
+ },
64
+ }),
65
+ ).toWarnDev(
66
+ 'Warning: Component: prop type `prop` is invalid; ' +
67
+ 'it must be a function, usually from React.PropTypes.',
68
+ );
69
});
70
71
it('should warn on invalid context types', () => {
75
- spyOnDev(console, 'error');
76
- createReactClass({
77
- displayName: 'Component',
78
- contextTypes: {
79
- prop: null,
80
- },
81
- render: function() {
82
- return <span>{this.props.prop}</span>;
83
- },
84
- });
85
- if (__DEV__) {
86
- expect(console.error.calls.count()).toBe(1);
87
- expect(console.error.calls.argsFor(0)[0]).toBe(
88
- 'Warning: Component: context type `prop` is invalid; ' +
89
- 'it must be a function, usually from React.PropTypes.',
90
- );
91
- }
72
+ expect(() =>
73
+ createReactClass({
74
+ displayName: 'Component',
75
+ contextTypes: {
76
+ prop: null,
77
+ },
78
+ render: function() {
79
+ return <span>{this.props.prop}</span>;
80
+ },
81
+ }),
82
+ ).toWarnDev(
83
+ 'Warning: Component: context type `prop` is invalid; ' +
84
+ 'it must be a function, usually from React.PropTypes.',
85
+ );
86
});
87
88
it('should throw on invalid child context types', () => {
95
- spyOnDev(console, 'error');
96
- createReactClass({
97
- displayName: 'Component',
98
- childContextTypes: {
99
- prop: null,
100
- },
101
- render: function() {
102
- return <span>{this.props.prop}</span>;
103
- },
104
- });
105
- if (__DEV__) {
106
- expect(console.error.calls.count()).toBe(1);
107
- expect(console.error.calls.argsFor(0)[0]).toBe(
108
- 'Warning: Component: child context type `prop` is invalid; ' +
109
- 'it must be a function, usually from React.PropTypes.',
110
- );
111
- }
89
+ expect(() =>
90
+ createReactClass({
91
+ displayName: 'Component',
92
+ childContextTypes: {
93
+ prop: null,
94
+ },
95
+ render: function() {
96
+ return <span>{this.props.prop}</span>;
97
+ },
98
+ }),
99
+ ).toWarnDev(
100
+ 'Warning: Component: child context type `prop` is invalid; ' +
101
+ 'it must be a function, usually from React.PropTypes.',
102
+ );
103
});
104
105
it('should warn when misspelling shouldComponentUpdate', () => {
115
- spyOnDev(console, 'error');
116
-
117
- createReactClass({
118
- componentShouldUpdate: function() {
119
- return false;
120
- },
121
- render: function() {
122
- return <div />;
123
- },
124
- });
125
- if (__DEV__) {
126
- expect(console.error.calls.count()).toBe(1);
127
- expect(console.error.calls.argsFor(0)[0]).toBe(
128
- 'Warning: A component has a method called componentShouldUpdate(). Did you ' +
129
- 'mean shouldComponentUpdate()? The name is phrased as a question ' +
130
- 'because the function is expected to return a value.',
131
- );
132
- }
106
+ expect(() =>
107
+ createReactClass({
108
+ componentShouldUpdate: function() {
109
+ return false;
110
+ },
111
+ render: function() {
112
+ return <div />;
113
+ },
114
+ }),
115
+ ).toWarnDev(
116
+ 'Warning: A component has a method called componentShouldUpdate(). Did you ' +
117
+ 'mean shouldComponentUpdate()? The name is phrased as a question ' +
118
+ 'because the function is expected to return a value.',
119
+ );
120
134
- createReactClass({
135
- displayName: 'NamedComponent',
136
- componentShouldUpdate: function() {
137
- return false;
138
- },
139
- render: function() {
140
- return <div />;
141
- },
142
- });
143
- if (__DEV__) {
144
- expect(console.error.calls.count()).toBe(2);
145
- expect(console.error.calls.argsFor(1)[0]).toBe(
146
- 'Warning: NamedComponent has a method called componentShouldUpdate(). Did you ' +
147
- 'mean shouldComponentUpdate()? The name is phrased as a question ' +
148
- 'because the function is expected to return a value.',
149
- );
150
- }
121
+ expect(() =>
122
+ createReactClass({
123
+ displayName: 'NamedComponent',
124
+ componentShouldUpdate: function() {
125
+ return false;
126
+ },
127
+ render: function() {
128
+ return <div />;
129
+ },
130
+ }),
131
+ ).toWarnDev(
132
+ 'Warning: NamedComponent has a method called componentShouldUpdate(). Did you ' +
133
+ 'mean shouldComponentUpdate()? The name is phrased as a question ' +
134
+ 'because the function is expected to return a value.',
135
+ );
136
});
137
138
it('should warn when misspelling componentWillReceiveProps', () => {
154
- spyOnDev(console, 'error');
155
- createReactClass({
156
- componentWillRecieveProps: function() {
157
- return false;
158
- },
159
- render: function() {
160
- return <div />;
161
- },
162
- });
163
- if (__DEV__) {
164
- expect(console.error.calls.count()).toBe(1);
165
- expect(console.error.calls.argsFor(0)[0]).toBe(
166
- 'Warning: A component has a method called componentWillRecieveProps(). Did you ' +
167
- 'mean componentWillReceiveProps()?',
168
- );
169
- }
139
+ expect(() =>
140
+ createReactClass({
141
+ componentWillRecieveProps: function() {
142
+ return false;
143
+ },
144
+ render: function() {
145
+ return <div />;
146
+ },
147
+ }),
148
+ ).toWarnDev(
149
+ 'Warning: A component has a method called componentWillRecieveProps(). Did you ' +
150
+ 'mean componentWillReceiveProps()?',
151
+ );
152
});
153
154
it('should throw if a reserved property is in statics', () => {
@@ -195,41 +177,32 @@ describe('create-react-class-integration', () => {
177
// TODO: Consider actually moving these to statics or drop this unit test.
178
179
xit('should warn when using deprecated non-static spec keys', () => {
198
- spyOnDev(console, 'error');
199
- createReactClass({
200
- mixins: [{}],
201
- propTypes: {
202
- foo: PropTypes.string,
203
- },
204
- contextTypes: {
205
- foo: PropTypes.string,
206
- },
207
- childContextTypes: {
208
- foo: PropTypes.string,
209
- },
210
- render: function() {
211
- return <div />;
212
- },
213
- });
214
- if (__DEV__) {
215
- expect(console.error.calls.count()).toBe(4);
216
- expect(console.error.calls.argsFor(0)[0]).toBe(
217
- 'createClass(...): `mixins` is now a static property and should ' +
218
- 'be defined inside "statics".',
219
- );
220
- expect(console.error.calls.argsFor(1)[0]).toBe(
221
- 'createClass(...): `propTypes` is now a static property and should ' +
222
- 'be defined inside "statics".',
223
- );
224
- expect(console.error.calls.argsFor(2)[0]).toBe(
225
- 'createClass(...): `contextTypes` is now a static property and ' +
226
- 'should be defined inside "statics".',
227
- );
228
- expect(console.error.calls.argsFor(3)[0]).toBe(
229
- 'createClass(...): `childContextTypes` is now a static property and ' +
230
- 'should be defined inside "statics".',
231
- );
232
- }
180
+ expect(() =>
181
+ createReactClass({
182
+ mixins: [{}],
183
+ propTypes: {
184
+ foo: PropTypes.string,
185
+ },
186
+ contextTypes: {
187
+ foo: PropTypes.string,
188
+ },
189
+ childContextTypes: {
190
+ foo: PropTypes.string,
191
+ },
192
+ render: function() {
193
+ return <div />;
194
+ },
195
+ }),
196
+ ).toWarnDev([
197
+ 'createClass(...): `mixins` is now a static property and should ' +
198
+ 'be defined inside "statics".',
199
+ 'createClass(...): `propTypes` is now a static property and should ' +
200
+ 'be defined inside "statics".',
201
+ 'createClass(...): `contextTypes` is now a static property and ' +
202
+ 'should be defined inside "statics".',
203
+ 'createClass(...): `childContextTypes` is now a static property and ' +
204
+ 'should be defined inside "statics".',
205
+ ]);
206
});
207
208
it('should support statics', () => {
@@ -342,21 +315,16 @@ describe('create-react-class-integration', () => {
315
});
316
317
it('should throw when using legacy factories', () => {
345
- spyOnDev(console, 'error');
318
const Component = createReactClass({
319
render() {
320
return <div />;
321
},
322
});
323
352
- expect(() => Component()).toThrow();
353
- if (__DEV__) {
354
- expect(console.error.calls.count()).toBe(1);
355
- expect(console.error.calls.argsFor(0)[0]).toBe(
356
- 'Warning: Something is calling a React component directly. Use a ' +
357
- 'factory or JSX instead. See: https://fb.me/react-legacyfactory',
358
- );
359
- }
324
+ expect(() => expect(() => Component()).toThrow()).toWarnDev(
325
+ 'Warning: Something is calling a React component directly. Use a ' +
326
+ 'factory or JSX instead. See: https://fb.me/react-legacyfactory',
327
+ );
328
});
329
330
it('replaceState and callback works', () => {
@@ -379,8 +347,6 @@ describe('create-react-class-integration', () => {
347
});
348
349
it('isMounted works', () => {
382
- spyOnDev(console, 'error');
383
-
350
const ops = [];
351
let instance;
352
const Component = createReactClass({
@@ -434,7 +400,13 @@ describe('create-react-class-integration', () => {
400
});
401
402
const container = document.createElement('div');
437
- ReactDOM.render(<Component />, container);
403
+
404
+ expect(() => ReactDOM.render(<Component />, container)).toWarnDev(
405
+ 'Warning: MyComponent: isMounted is deprecated. Instead, make sure to ' +
406
+ 'clean up subscriptions and pending requests in componentWillUnmount ' +
407
+ 'to prevent memory leaks.',
408
+ );
409
+
410
ReactDOM.render(<Component />, container);
411
ReactDOM.unmountComponentAtNode(container);
412
instance.log('after unmount');
@@ -454,14 +426,5 @@ describe('create-react-class-integration', () => {
426
'componentWillUnmount: true',
427
'after unmount: false',
428
]);
457
-
458
- if (__DEV__) {
459
- expect(console.error.calls.count()).toBe(1);
460
- expect(console.error.calls.argsFor(0)[0]).toEqual(
461
- 'Warning: MyComponent: isMounted is deprecated. Instead, make sure to ' +
462
- 'clean up subscriptions and pending requests in componentWillUnmount ' +
463
- 'to prevent memory leaks.',
464
- );
465
- }
429
});
430
});
scripts/jest/matchers/toWarnDev.js
new
+86
@@ -0,0 +1,86 @@
1
+'use strict';
2
+
3
+function normalizeCodeLocInfo(str) {
4
+ return str && str.replace(/at .+?:\d+/g, 'at **');
5
+}
6
+
7
+const createMatcherFor = consoleMethod =>
8
+ function matcher(callback, expectedMessages) {
9
+ if (__DEV__) {
10
+ // Warn about incorrect usage of matcher.
11
+ if (typeof expectedMessages === 'string') {
12
+ expectedMessages = [expectedMessages];
13
+ } else if (!Array.isArray(expectedMessages)) {
14
+ throw Error(
15
+ `toWarnDev() requires a parameter of type string or an array of strings ` +
16
+ `but was given ${typeof expectedMessages}.`
17
+ );
18
+ }
19
+
20
+ const consoleSpy = message => {
21
+ const normalizedMessage = normalizeCodeLocInfo(message);
22
+
23
+ for (let index = 0; index < expectedMessages.length; index++) {
24
+ const expectedMessage = expectedMessages[index];
25
+ if (
26
+ normalizedMessage === expectedMessage ||
27
+ normalizedMessage.includes(expectedMessage)
28
+ ) {
29
+ expectedMessages.splice(index, 1);
30
+ return;
31
+ }
32
+ }
33
+
34
+ // Fail early for unexpected warnings to preserve the call stack.
35
+ throw Error(
36
+ `Unexpected warning recorded:\n ${this.utils.printReceived(
37
+ message
38
+ )}\n\nThe following expected warnings were not yet seen:\n ${this.utils.printExpected(
39
+ expectedMessages.join('\n')
40
+ )}`
41
+ );
42
+ };
43
+
44
+ // TODO Decide whether we need to support nested toWarn* expectations.
45
+ // If we don't need id, add a check here to see if this is already our spy,
46
+ // And throw an error.
47
+ const originalMethod = console[consoleMethod];
48
+
49
+ // Avoid using Jest's built-in spy since it can't be removed.
50
+ console[consoleMethod] = consoleSpy;
51
+
52
+ try {
53
+ callback();
54
+
55
+ // Any remaining messages indicate a failed expectations.
56
+ if (expectedMessages.length > 0) {
57
+ return {
58
+ message: () =>
59
+ `Expected warning was not recorded:\n ${this.utils.printReceived(
60
+ expectedMessages.join('\n')
61
+ )}`,
62
+ pass: false,
63
+ };
64
+ }
65
+
66
+ return {pass: true};
67
+ } catch (error) {
68
+ // TODO Flag this error so Jest doesn't override its stack
69
+ // See https://tinyurl.com/y9unakwb
70
+ throw error;
71
+ } finally {
72
+ // Restore the unspied method so that unexpected errors fail tests.
73
+ console[consoleMethod] = originalMethod;
74
+ }
75
+ } else {
76
+ // Any uncaught errors or warnings should fail tests in production mode.
77
+ callback();
78
+
79
+ return {pass: true};
80
+ }
81
+ };
82
+
83
+module.exports = {
84
+ toLowPriorityWarnDev: createMatcherFor('warn'),
85
+ toWarnDev: createMatcherFor('error'),
86
+};
scripts/jest/setupTests.js
+43
-14
@@ -1,5 +1,7 @@
1
'use strict';
2
3
+const chalk = require('chalk');
4
+
5
if (process.env.REACT_CLASS_EQUIVALENCE_TEST) {
6
// Inside the class equivalence tester, we have a custom environment, let's
7
// require that instead.
@@ -39,6 +41,10 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) {
41
global.spyOnDevAndProd = spyOn;
42
}
43
44
+ expect.extend({
45
+ ...require('./matchers/toWarnDev'),
46
+ });
47
+
48
// We have a Babel transform that inserts guards against infinite loops.
49
// If a loop runs for too many iterations, we throw an error and set this
50
// global variable. The global lets us detect an infinite loop even if
@@ -56,16 +62,22 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) {
62
});
63
64
['error', 'warn'].forEach(methodName => {
59
- const oldMethod = console[methodName];
60
- const newMethod = function() {
61
- newMethod.__callCount++;
62
- oldMethod.apply(this, arguments);
65
+ const unexpectedConsoleCallStacks = [];
66
+ const newMethod = function(message) {
67
+ // Capture the call stack now so we can warn about it later.
68
+ // The call stack has helpful information for the test author.
69
+ // Don't throw yet though b'c it might be accidentally caught and suppressed.
70
+ const stack = new Error().stack;
71
+ unexpectedConsoleCallStacks.push([
72
+ stack.substr(stack.indexOf('\n') + 1),
73
+ message,
74
+ ]);
75
};
64
- newMethod.__callCount = 0;
76
+
77
console[methodName] = newMethod;
78
79
env.beforeEach(() => {
68
- newMethod.__callCount = 0;
80
+ unexpectedConsoleCallStacks.length = 0;
81
});
82
83
env.afterEach(() => {
@@ -74,15 +86,32 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) {
86
`Test did not tear down console.${methodName} mock properly.`
87
);
88
}
77
- if (console[methodName].__callCount !== 0) {
78
- throw new Error(
79
- `Expected test not to call console.${methodName}(). ` +
80
- 'If the warning is expected, mock it out using ' +
81
- `spyOnDev(console, '${methodName}') or spyOnProd(console, '${
82
- methodName
83
- }'), ` +
84
- 'and test that the warning occurs.'
89
+
90
+ if (unexpectedConsoleCallStacks.length > 0) {
91
+ const messages = unexpectedConsoleCallStacks.map(
92
+ ([stack, message]) =>
93
+ `${chalk.red(message)}\n` +
94
+ `${stack
95
+ .split('\n')
96
+ .map(line => chalk.gray(line))
97
+ .join('\n')}`
98
);
99
+
100
+ const message =
101
+ `Expected test not to call ${chalk.bold(
102
+ `console.${methodName}()`
103
+ )}.\n\n` +
104
+ 'If the warning is expected, test for it explicitly by:\n' +
105
+ `1. Using the ${chalk.bold('.toWarnDev()')} / ${chalk.bold(
106
+ '.toLowPriorityWarnDev()'
107
+ )} matchers, or...\n` +
108
+ `2. Mock it out using ${chalk.bold('spyOnDev')}(console, '${
109
+ methodName
110
+ }') or ${chalk.bold('spyOnProd')}(console, '${
111
+ methodName
112
+ }'), and test that the warning occurs.`;
113
+
114
+ throw new Error(`${message}\n\n${messages.join('\n\n')}`);
115
}
116
});
117
});
scripts/jest/spec-equivalence-reporter/setupTests.js
+4
@@ -45,6 +45,10 @@ global.spyOnProd = function(...args) {
45
}
46
};
47
48
+expect.extend({
49
+ ...require('../matchers/toWarnDev'),
50
+});
51
+
52
beforeEach(() => (numExpectations = 0));
53
54
jasmine.currentEnv_.addReporter({
scripts/jest/typescript/jest.d.ts
+2
@@ -21,6 +21,8 @@ interface Expect {
21
not: Expect
22
toThrow(message?: string): void
23
toThrowError(message?: string): void
24
+ toWarnDev(message?: string | Array<string>): void
25
+ toLowPriorityWarnDev(message?: string | Array<string>): void
26
toBe(value: any): void
27
toEqual(value: any): void
28
toBeFalsy(): void