@samitouri / QOS-React / commits / 6041f481b7

Run Jest in production mode (#11616)

* Move Jest setup files to /dev/ subdirectory * Clone Jest /dev/ files into /prod/ * Move shared code into scripts/jest * Move Jest config into the scripts folder * Fix the equivalence test It fails because the config is now passed to Jest explicitly. But the test doesn't know about the config. To fix this, we just run it via `yarn test` (which includes the config). We already depend on Yarn for development anyway. * Add yarn test-prod to run Jest with production environment * Actually flip the production tests to run in prod environment This produces a bunch of errors: Test Suites: 64 failed, 58 passed, 122 total Tests: 740 failed, 26 skipped, 1809 passed, 2575 total Snapshots: 16 failed, 4 passed, 20 total * Ignore expectDev() calls in production Down from 740 to 175 failed. Test Suites: 44 failed, 78 passed, 122 total Tests: 175 failed, 26 skipped, 2374 passed, 2575 total Snapshots: 16 failed, 4 passed, 20 total * Decode errors so tests can assert on their messages Down from 175 to 129. Test Suites: 33 failed, 89 passed, 122 total Tests: 129 failed, 1029 skipped, 1417 passed, 2575 total Snapshots: 16 failed, 4 passed, 20 total * Remove ReactDOMProduction-test There is no need for it now. The only test that was special is moved into ReactDOM-test. * Remove production switches from ReactErrorUtils The tests now run in production in a separate pass. * Add and use spyOnDev() for warnings This ensures that by default we expect no warnings in production bundles. If the warning *is* expected, use the regular spyOn() method. This currently breaks all expectDev() assertions without __DEV__ blocks so we go back to: Test Suites: 56 failed, 65 passed, 121 total Tests: 379 failed, 1029 skipped, 1148 passed, 2556 total Snapshots: 16 failed, 4 passed, 20 total * Replace expectDev() with expect() in __DEV__ blocks We started using spyOnDev() for console warnings to ensure we don't *expect* them to occur in production. As a consequence, expectDev() assertions on console.error.calls fail because console.error.calls doesn't exist. This is actually good because it would help catch accidental warnings in production. To solve this, we are getting rid of expectDev() altogether, and instead introduce explicit expectation branches. We'd need them anyway for testing intentional behavior differences. This commit replaces all expectDev() calls with expect() calls in __DEV__ blocks. It also removes a few unnecessary expect() checks that no warnings were produced (by also removing the corresponding spyOnDev() calls). Some DEV-only assertions used plain expect(). Those were also moved into __DEV__ blocks. ReactFiberErrorLogger was special because it console.error()'s in production too. So in that case I intentionally used spyOn() instead of spyOnDev(), and added extra assertions. This gets us down to: Test Suites: 21 failed, 100 passed, 121 total Tests: 72 failed, 26 skipped, 2458 passed, 2556 total Snapshots: 16 failed, 4 passed, 20 total * Enable User Timing API for production testing We could've disabled it, but seems like a good idea to test since we use it at FB. * Test for explicit Object.freeze() differences between PROD and DEV This is one of the few places where DEV and PROD behavior differs for performance reasons. Now we explicitly test both branches. * Run Jest via "yarn test" on CI * Remove unused variable * Assert different error messages * Fix error handling tests This logic is really complicated because of the global ReactFiberErrorLogger mock. I understand it now, so I added TODOs for later. It can be much simpler if we change the rest of the tests that assert uncaught errors to also assert they are logged as warnings. Which mirrors what happens in practice anyway. * Fix more assertions * Change tests to document the DEV/PROD difference for state invariant It is very likely unintentional but I don't want to change behavior in this PR. Filed a follow up as https://github.com/facebook/react/issues/11618. * Remove unnecessary split between DEV/PROD ref tests * Fix more test message assertions * Make validateDOMNesting tests DEV-only * Fix error message assertions * Document existing DEV/PROD message difference (possible bug) * Change mocking assertions to be DEV-only * Fix the error code test * Fix more error message assertions * Fix the last failing test due to known issue * Run production tests on CI * Unify configuration * Fix coverage script * Remove expectDev from eslintrc * Run everything in band We used to before, too. I just forgot to add the arguments after deleting the script.

Dan Abramov committed Nov 22, 2017 at 13:02 UTC 6041f481b7851d75649630eea489628d399cc3cf
71 files changed +3707 -3223
.eslintrc.js
+1 -1
@@ -59,6 +59,6 @@ module.exports = {
59 },
60
61 globals: {
62 - expectDev: true,
62 + spyOnDev: true,
63 },
64 };
package.json
+3 -1
@@ -45,6 +45,7 @@
45 "core-js": "^2.2.1",
46 "coveralls": "^2.11.6",
47 "create-react-class": "^15.6.2",
48 + "cross-env": "^5.1.1",
49 "del": "^2.0.2",
50 "derequire": "^2.0.3",
51 "escape-string-regexp": "^1.0.5",
@@ -102,7 +103,8 @@
103 "linc": "node ./scripts/tasks/linc.js",
104 "lint": "node ./scripts/tasks/eslint.js",
105 "postinstall": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json",
105 - "test": "jest",
106 + "test": "cross-env NODE_ENV=development jest",
107 + "test-prod": "cross-env NODE_ENV=production jest",
108 "flow": "node ./scripts/tasks/flow.js",
109 "prettier": "node ./scripts/prettier/index.js write-changed",
110 "prettier-all": "node ./scripts/prettier/index.js write",
packages/react-dom/src/__tests__/CSSPropertyOperations-test.js
+74 -62
@@ -91,15 +91,17 @@ describe('CSSPropertyOperations', () => {
91 }
92 }
93
94 - spyOn(console, 'error');
94 + spyOnDev(console, 'error');
95 var root = document.createElement('div');
96 ReactDOM.render(<Comp />, root);
97 - expectDev(console.error.calls.count()).toBe(1);
98 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
99 - 'Warning: Unsupported style property background-color. Did you mean backgroundColor?' +
100 - '\n in div (at **)' +
101 - '\n in Comp (at **)',
102 - );
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 + }
105 });
106
107 it('should warn when updating hyphenated style names', () => {
@@ -111,7 +113,7 @@ describe('CSSPropertyOperations', () => {
113 }
114 }
115
114 - spyOn(console, 'error');
116 + spyOnDev(console, 'error');
117 var styles = {
118 '-ms-transform': 'translate3d(0, 0, 0)',
119 '-webkit-transform': 'translate3d(0, 0, 0)',
@@ -120,17 +122,19 @@ describe('CSSPropertyOperations', () => {
122 ReactDOM.render(<Comp />, root);
123 ReactDOM.render(<Comp style={styles} />, root);
124
123 - expectDev(console.error.calls.count()).toBe(2);
124 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
125 - 'Warning: Unsupported style property -ms-transform. Did you mean msTransform?' +
126 - '\n in div (at **)' +
127 - '\n in Comp (at **)',
128 - );
129 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toEqual(
130 - 'Warning: Unsupported style property -webkit-transform. Did you mean WebkitTransform?' +
131 - '\n in div (at **)' +
132 - '\n in Comp (at **)',
133 - );
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 + }
138 });
139
140 it('warns when miscapitalizing vendored style names', () => {
@@ -150,23 +154,25 @@ describe('CSSPropertyOperations', () => {
154 }
155 }
156
153 - spyOn(console, 'error');
157 + spyOnDev(console, 'error');
158 var root = document.createElement('div');
159 ReactDOM.render(<Comp />, root);
156 - // msTransform is correct already and shouldn't warn
157 - expectDev(console.error.calls.count()).toBe(2);
158 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
159 - 'Warning: Unsupported vendor-prefixed style property oTransform. ' +
160 - 'Did you mean OTransform?' +
161 - '\n in div (at **)' +
162 - '\n in Comp (at **)',
163 - );
164 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toEqual(
165 - 'Warning: Unsupported vendor-prefixed style property webkitTransform. ' +
166 - 'Did you mean WebkitTransform?' +
167 - '\n in div (at **)' +
168 - '\n in Comp (at **)',
169 - );
160 + if (__DEV__) {
161 + // 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 + }
176 });
177
178 it('should warn about style having a trailing semicolon', () => {
@@ -187,22 +193,24 @@ describe('CSSPropertyOperations', () => {
193 }
194 }
195
190 - spyOn(console, 'error');
196 + spyOnDev(console, 'error');
197 var root = document.createElement('div');
198 ReactDOM.render(<Comp />, root);
193 - expectDev(console.error.calls.count()).toBe(2);
194 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
195 - "Warning: Style property values shouldn't contain a semicolon. " +
196 - 'Try "backgroundColor: blue" instead.' +
197 - '\n in div (at **)' +
198 - '\n in Comp (at **)',
199 - );
200 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toEqual(
201 - "Warning: Style property values shouldn't contain a semicolon. " +
202 - 'Try "color: red" instead.' +
203 - '\n in div (at **)' +
204 - '\n in Comp (at **)',
205 - );
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 + }
214 });
215
216 it('should warn about style containing a NaN value', () => {
@@ -214,16 +222,18 @@ describe('CSSPropertyOperations', () => {
222 }
223 }
224
217 - spyOn(console, 'error');
225 + spyOnDev(console, 'error');
226 var root = document.createElement('div');
227 ReactDOM.render(<Comp />, root);
228
221 - expectDev(console.error.calls.count()).toBe(1);
222 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
223 - 'Warning: `NaN` is an invalid value for the `fontSize` css style property.' +
224 - '\n in div (at **)' +
225 - '\n in Comp (at **)',
226 - );
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 + }
237 });
238
239 it('should not warn when setting CSS custom properties', () => {
@@ -246,16 +256,18 @@ describe('CSSPropertyOperations', () => {
256 }
257 }
258
249 - spyOn(console, 'error');
259 + spyOnDev(console, 'error');
260 var root = document.createElement('div');
261 ReactDOM.render(<Comp />, root);
262
253 - expectDev(console.error.calls.count()).toBe(1);
254 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
255 - 'Warning: `Infinity` is an invalid value for the `fontSize` css style property.' +
256 - '\n in div (at **)' +
257 - '\n in Comp (at **)',
258 - );
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 + }
271 });
272
273 it('should not add units to CSS custom properties', () => {
packages/react-dom/src/__tests__/DOMPropertyOperations-test.js
+7 -5
@@ -151,7 +151,7 @@ describe('DOMPropertyOperations', () => {
151
152 it('should not remove attributes for special properties', () => {
153 var container = document.createElement('div');
154 - spyOn(console, 'error');
154 + spyOnDev(console, 'error');
155 ReactDOM.render(
156 <input type="text" value="foo" onChange={function() {}} />,
157 container,
@@ -164,10 +164,12 @@ describe('DOMPropertyOperations', () => {
164 );
165 expect(container.firstChild.getAttribute('value')).toBe('foo');
166 expect(container.firstChild.value).toBe('foo');
167 - expect(console.error.calls.count()).toBe(1);
168 - expect(console.error.calls.argsFor(0)[0]).toContain(
169 - 'A component is changing a controlled input of type text to be uncontrolled',
170 - );
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 + }
173 });
174 });
175 });
packages/react-dom/src/__tests__/EventPluginHub-test.js
+7 -5
@@ -22,7 +22,7 @@ describe('EventPluginHub', () => {
22 });
23
24 it('should prevent non-function listeners, at dispatch', () => {
25 - spyOn(console, 'error');
25 + spyOnDev(console, 'error');
26 var node = ReactTestUtils.renderIntoDocument(
27 <div onClick="not a function" />,
28 );
@@ -31,10 +31,12 @@ describe('EventPluginHub', () => {
31 }).toThrowError(
32 'Expected `onClick` listener to be a function, instead got a value of `string` type.',
33 );
34 - expectDev(console.error.calls.count()).toBe(1);
35 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
36 - 'Expected `onClick` listener to be a function, instead got a value of `string` type.',
37 - );
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 + }
40 });
41
42 it('should not prevent null listeners, at dispatch', () => {
packages/react-dom/src/__tests__/ReactBrowserEventEmitter-test.js
+4 -2
@@ -285,13 +285,15 @@ describe('ReactBrowserEventEmitter', () => {
285 putListener(CHILD, ON_CLICK_KEY, recordIDAndReturnFalse.bind(null, CHILD));
286 putListener(PARENT, ON_CLICK_KEY, recordID.bind(null, PARENT));
287 putListener(GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, GRANDPARENT));
288 - spyOn(console, 'error');
288 + spyOnDev(console, 'error');
289 ReactTestUtils.Simulate.click(CHILD);
290 expect(idCallOrder.length).toBe(3);
291 expect(idCallOrder[0]).toBe(CHILD);
292 expect(idCallOrder[1]).toBe(PARENT);
293 expect(idCallOrder[2]).toBe(GRANDPARENT);
294 - expectDev(console.error.calls.count()).toEqual(0);
294 + if (__DEV__) {
295 + expect(console.error.calls.count()).toEqual(0);
296 + }
297 });
298
299 /**
packages/react-dom/src/__tests__/ReactChildReconciler-test.js
+50 -46
@@ -46,7 +46,7 @@ describe('ReactChildReconciler', () => {
46 }
47
48 it('warns for duplicated array keys', () => {
49 - spyOn(console, 'error');
49 + spyOnDev(console, 'error');
50
51 class Component extends React.Component {
52 render() {
@@ -56,17 +56,19 @@ describe('ReactChildReconciler', () => {
56
57 ReactTestUtils.renderIntoDocument(<Component />);
58
59 - expectDev(console.error.calls.count()).toBe(1);
60 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
61 - 'Keys should be unique so that components maintain their identity ' +
62 - 'across updates. Non-unique keys may cause children to be ' +
63 - 'duplicated and/or omitted — the behavior is unsupported and ' +
64 - 'could change in a future version.',
65 - );
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 + }
68 });
69
70 it('warns for duplicated array keys with component stack info', () => {
69 - spyOn(console, 'error');
71 + spyOnDev(console, 'error');
72
73 class Component extends React.Component {
74 render() {
@@ -88,24 +90,24 @@ describe('ReactChildReconciler', () => {
90
91 ReactTestUtils.renderIntoDocument(<GrandParent />);
92
91 - expectDev(console.error.calls.count()).toBe(1);
92 - expectDev(
93 - normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
94 - ).toContain(
95 - 'Encountered two children with the same key, `1`. ' +
96 - 'Keys should be unique so that components maintain their identity ' +
97 - 'across updates. Non-unique keys may cause children to be ' +
98 - 'duplicated and/or omitted — the behavior is unsupported and ' +
99 - 'could change in a future version.',
100 - ' in div (at **)\n' +
101 - ' in Component (at **)\n' +
102 - ' in Parent (at **)\n' +
103 - ' in GrandParent (at **)',
104 - );
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 + }
107 });
108
109 it('warns for duplicated iterable keys', () => {
108 - spyOn(console, 'error');
110 + spyOnDev(console, 'error');
111
112 class Component extends React.Component {
113 render() {
@@ -115,17 +117,19 @@ describe('ReactChildReconciler', () => {
117
118 ReactTestUtils.renderIntoDocument(<Component />);
119
118 - expectDev(console.error.calls.count()).toBe(1);
119 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
120 - 'Keys should be unique so that components maintain their identity ' +
121 - 'across updates. Non-unique keys may cause children to be ' +
122 - 'duplicated and/or omitted — the behavior is unsupported and ' +
123 - 'could change in a future version.',
124 - );
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 + }
129 });
130
131 it('warns for duplicated iterable keys with component stack info', () => {
128 - spyOn(console, 'error');
132 + spyOnDev(console, 'error');
133
134 class Component extends React.Component {
135 render() {
@@ -147,19 +151,19 @@ describe('ReactChildReconciler', () => {
151
152 ReactTestUtils.renderIntoDocument(<GrandParent />);
153
150 - expectDev(console.error.calls.count()).toBe(1);
151 - expectDev(
152 - normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
153 - ).toContain(
154 - 'Encountered two children with the same key, `1`. ' +
155 - 'Keys should be unique so that components maintain their identity ' +
156 - 'across updates. Non-unique keys may cause children to be ' +
157 - 'duplicated and/or omitted — the behavior is unsupported and ' +
158 - 'could change in a future version.',
159 - ' in div (at **)\n' +
160 - ' in Component (at **)\n' +
161 - ' in Parent (at **)\n' +
162 - ' in GrandParent (at **)',
163 - );
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 + }
168 });
169 });
packages/react-dom/src/__tests__/ReactComponent-test.js
+126 -84
@@ -45,13 +45,23 @@ describe('ReactComponent', () => {
45 }).toThrow();
46 });
47
48 - it('should warn when children are mutated during render', () => {
49 - spyOn(console, 'error');
48 + it('should throw (in dev) when children are mutated during render', () => {
49 + spyOnDev(console, 'error');
50 function Wrapper(props) {
51 props.children[1] = <p key={1} />; // Mutation is illegal
52 return <div>{props.children}</div>;
53 }
54 - expect(() => {
54 + if (__DEV__) {
55 + expect(() => {
56 + ReactTestUtils.renderIntoDocument(
57 + <Wrapper>
58 + <span key={0} />
59 + <span key={1} />
60 + <span key={2} />
61 + </Wrapper>,
62 + );
63 + }).toThrowError(/Cannot assign to read only property.*/);
64 + } else {
65 ReactTestUtils.renderIntoDocument(
66 <Wrapper>
67 <span key={0} />
@@ -59,11 +69,11 @@ describe('ReactComponent', () => {
69 <span key={2} />
70 </Wrapper>,
71 );
62 - }).toThrowError(/Cannot assign to read only property.*/);
72 + }
73 });
74
65 - it('should warn when children are mutated during update', () => {
66 - spyOn(console, 'error');
75 + it('should throw (in dev) when children are mutated during update', () => {
76 + spyOnDev(console, 'error');
77
78 class Wrapper extends React.Component {
79 componentDidMount() {
@@ -76,7 +86,17 @@ describe('ReactComponent', () => {
86 }
87 }
88
79 - expect(() => {
89 + if (__DEV__) {
90 + expect(() => {
91 + ReactTestUtils.renderIntoDocument(
92 + <Wrapper>
93 + <span key={0} />
94 + <span key={1} />
95 + <span key={2} />
96 + </Wrapper>,
97 + );
98 + }).toThrowError(/Cannot assign to read only property.*/);
99 + } else {
100 ReactTestUtils.renderIntoDocument(
101 <Wrapper>
102 <span key={0} />
@@ -84,7 +104,7 @@ describe('ReactComponent', () => {
104 <span key={2} />
105 </Wrapper>,
106 );
87 - }).toThrowError(/Cannot assign to read only property.*/);
107 + }
108 });
109
110 it('should support refs on owned components', () => {
@@ -335,14 +355,16 @@ describe('ReactComponent', () => {
355 });
356
357 it('throws usefully when rendering badly-typed elements', () => {
338 - spyOn(console, 'error');
358 + spyOnDev(console, 'error');
359
360 var X = undefined;
361 expect(() => ReactTestUtils.renderIntoDocument(<X />)).toThrowError(
362 'Element type is invalid: expected a string (for built-in components) ' +
343 - 'or a class/function (for composite components) but got: undefined. ' +
344 - "You likely forgot to export your component from the file it's " +
345 - 'defined in, or you might have mixed up default and named imports.',
363 + 'or a class/function (for composite components) but got: undefined.' +
364 + (__DEV__
365 + ? " You likely forgot to export your component from the file it's " +
366 + 'defined in, or you might have mixed up default and named imports.'
367 + : ''),
368 );
369
370 var Y = null;
@@ -351,12 +373,14 @@ describe('ReactComponent', () => {
373 'or a class/function (for composite components) but got: null.',
374 );
375
354 - // One warning for each element creation
355 - expectDev(console.error.calls.count()).toBe(2);
376 + if (__DEV__) {
377 + // One warning for each element creation
378 + expect(console.error.calls.count()).toBe(2);
379 + }
380 });
381
382 it('includes owner name in the error about badly-typed elements', () => {
359 - spyOn(console, 'error');
383 + spyOnDev(console, 'error');
384
385 var X = undefined;
386
@@ -378,14 +402,18 @@ describe('ReactComponent', () => {
402
403 expect(() => ReactTestUtils.renderIntoDocument(<Foo />)).toThrowError(
404 'Element type is invalid: expected a string (for built-in components) ' +
381 - 'or a class/function (for composite components) but got: undefined. ' +
382 - "You likely forgot to export your component from the file it's " +
383 - 'defined in, or you might have mixed up default and named imports.' +
384 - '\n\nCheck the render method of `Bar`.',
405 + 'or a class/function (for composite components) but got: undefined.' +
406 + (__DEV__
407 + ? " You likely forgot to export your component from the file it's " +
408 + 'defined in, or you might have mixed up default and named imports.' +
409 + '\n\nCheck the render method of `Bar`.'
410 + : ''),
411 );
412
387 - // One warning for each element creation
388 - expectDev(console.error.calls.count()).toBe(1);
413 + if (__DEV__) {
414 + // One warning for each element creation
415 + expect(console.error.calls.count()).toBe(1);
416 + }
417 });
418
419 it('throws if a plain object is used as a child', () => {
@@ -404,10 +432,12 @@ describe('ReactComponent', () => {
432 }
433 expect(ex).toBeDefined();
434 expect(normalizeCodeLocInfo(ex.message)).toBe(
407 - 'Objects are not valid as a React child (found: object with keys ' +
408 - '{x, y, z}). If you meant to render a collection of children, use ' +
409 - 'an array instead.' +
410 - '\n in div (at **)',
435 + 'Objects are not valid as a React child (found: object with keys {x, y, z}).' +
436 + (__DEV__
437 + ? ' If you meant to render a collection of children, use ' +
438 + 'an array instead.' +
439 + '\n in div (at **)'
440 + : ''),
441 );
442 });
443
@@ -431,11 +461,13 @@ describe('ReactComponent', () => {
461 }
462 expect(ex).toBeDefined();
463 expect(normalizeCodeLocInfo(ex.message)).toBe(
434 - 'Objects are not valid as a React child (found: object with keys ' +
435 - '{a, b, c}). If you meant to render a collection of children, use ' +
436 - 'an array instead.\n' +
437 - ' in div (at **)\n' +
438 - ' in Foo (at **)',
464 + 'Objects are not valid as a React child (found: object with keys {a, b, c}).' +
465 + (__DEV__
466 + ? ' If you meant to render a collection of children, use ' +
467 + 'an array instead.\n' +
468 + ' in div (at **)\n' +
469 + ' in Foo (at **)'
470 + : ''),
471 );
472 });
473
@@ -454,10 +486,12 @@ describe('ReactComponent', () => {
486 }
487 expect(ex).toBeDefined();
488 expect(normalizeCodeLocInfo(ex.message)).toBe(
457 - 'Objects are not valid as a React child (found: object with keys ' +
458 - '{x, y, z}). If you meant to render a collection of children, use ' +
459 - 'an array instead.' +
460 - '\n in div (at **)',
489 + 'Objects are not valid as a React child (found: object with keys {x, y, z}).' +
490 + (__DEV__
491 + ? ' If you meant to render a collection of children, use ' +
492 + 'an array instead.' +
493 + '\n in div (at **)'
494 + : ''),
495 );
496 });
497
@@ -481,11 +515,13 @@ describe('ReactComponent', () => {
515 }
516 expect(ex).toBeDefined();
517 expect(normalizeCodeLocInfo(ex.message)).toBe(
484 - 'Objects are not valid as a React child (found: object with keys ' +
485 - '{a, b, c}). If you meant to render a collection of children, use ' +
486 - 'an array instead.\n' +
487 - ' in div (at **)\n' +
488 - ' in Foo (at **)',
518 + 'Objects are not valid as a React child (found: object with keys {a, b, c}).' +
519 + (__DEV__
520 + ? ' If you meant to render a collection of children, use ' +
521 + 'an array instead.\n' +
522 + ' in div (at **)\n' +
523 + ' in Foo (at **)'
524 + : ''),
525 );
526 });
527
@@ -494,16 +530,18 @@ describe('ReactComponent', () => {
530 function Foo() {
531 return Foo;
532 }
497 - spyOn(console, 'error');
533 + spyOnDev(console, 'error');
534 var container = document.createElement('div');
535 ReactDOM.render(<Foo />, container);
500 - expectDev(console.error.calls.count()).toBe(1);
501 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
502 - 'Warning: Functions are not valid as a React child. This may happen if ' +
503 - 'you return a Component instead of <Component /> from render. ' +
504 - 'Or maybe you meant to call this function rather than return it.\n' +
505 - ' in Foo (at **)',
506 - );
536 + if (__DEV__) {
537 + expect(console.error.calls.count()).toBe(1);
538 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
539 + 'Warning: Functions are not valid as a React child. This may happen if ' +
540 + 'you return a Component instead of <Component /> from render. ' +
541 + 'Or maybe you meant to call this function rather than return it.\n' +
542 + ' in Foo (at **)',
543 + );
544 + }
545 });
546
547 it('warns on function as a return value from a class', () => {
@@ -512,16 +550,18 @@ describe('ReactComponent', () => {
550 return Foo;
551 }
552 }
515 - spyOn(console, 'error');
553 + spyOnDev(console, 'error');
554 var container = document.createElement('div');
555 ReactDOM.render(<Foo />, container);
518 - expectDev(console.error.calls.count()).toBe(1);
519 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
520 - 'Warning: Functions are not valid as a React child. This may happen if ' +
521 - 'you return a Component instead of <Component /> from render. ' +
522 - 'Or maybe you meant to call this function rather than return it.\n' +
523 - ' in Foo (at **)',
524 - );
556 + if (__DEV__) {
557 + expect(console.error.calls.count()).toBe(1);
558 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
559 + 'Warning: Functions are not valid as a React child. This may happen if ' +
560 + 'you return a Component instead of <Component /> from render. ' +
561 + 'Or maybe you meant to call this function rather than return it.\n' +
562 + ' in Foo (at **)',
563 + );
564 + }
565 });
566
567 it('warns on function as a child to host component', () => {
@@ -532,18 +572,20 @@ describe('ReactComponent', () => {
572 </div>
573 );
574 }
535 - spyOn(console, 'error');
575 + spyOnDev(console, 'error');
576 var container = document.createElement('div');
577 ReactDOM.render(<Foo />, container);
538 - expectDev(console.error.calls.count()).toBe(1);
539 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
540 - 'Warning: Functions are not valid as a React child. This may happen if ' +
541 - 'you return a Component instead of <Component /> from render. ' +
542 - 'Or maybe you meant to call this function rather than return it.\n' +
543 - ' in span (at **)\n' +
544 - ' in div (at **)\n' +
545 - ' in Foo (at **)',
546 - );
578 + if (__DEV__) {
579 + expect(console.error.calls.count()).toBe(1);
580 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
581 + 'Warning: Functions are not valid as a React child. This may happen if ' +
582 + 'you return a Component instead of <Component /> from render. ' +
583 + 'Or maybe you meant to call this function rather than return it.\n' +
584 + ' in span (at **)\n' +
585 + ' in div (at **)\n' +
586 + ' in Foo (at **)',
587 + );
588 + }
589 });
590
591 it('does not warn for function-as-a-child that gets resolved', () => {
@@ -553,15 +595,13 @@ describe('ReactComponent', () => {
595 function Foo() {
596 return <Bar>{() => 'Hello'}</Bar>;
597 }
556 - spyOn(console, 'error');
598 var container = document.createElement('div');
599 ReactDOM.render(<Foo />, container);
600 expect(container.innerHTML).toBe('Hello');
560 - expectDev(console.error.calls.count()).toBe(0);
601 });
602
603 it('deduplicates function type warnings based on component type', () => {
564 - spyOn(console, 'error');
604 + spyOnDev(console, 'error');
605 class Foo extends React.PureComponent {
606 constructor() {
607 super();
@@ -583,22 +623,24 @@ describe('ReactComponent', () => {
623 var container = document.createElement('div');
624 var component = ReactDOM.render(<Foo />, container);
625 component.setState({type: 'portobello mushrooms'});
586 - expectDev(console.error.calls.count()).toBe(2);
587 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
588 - 'Warning: Functions are not valid as a React child. This may happen if ' +
589 - 'you return a Component instead of <Component /> from render. ' +
590 - 'Or maybe you meant to call this function rather than return it.\n' +
591 - ' in div (at **)\n' +
592 - ' in Foo (at **)',
593 - );
594 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
595 - 'Warning: Functions are not valid as a React child. This may happen if ' +
596 - 'you return a Component instead of <Component /> from render. ' +
597 - 'Or maybe you meant to call this function rather than return it.\n' +
598 - ' in span (at **)\n' +
599 - ' in div (at **)\n' +
600 - ' in Foo (at **)',
601 - );
626 + if (__DEV__) {
627 + expect(console.error.calls.count()).toBe(2);
628 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
629 + 'Warning: Functions are not valid as a React child. This may happen if ' +
630 + 'you return a Component instead of <Component /> from render. ' +
631 + 'Or maybe you meant to call this function rather than return it.\n' +
632 + ' in div (at **)\n' +
633 + ' in Foo (at **)',
634 + );
635 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
636 + 'Warning: Functions are not valid as a React child. This may happen if ' +
637 + 'you return a Component instead of <Component /> from render. ' +
638 + 'Or maybe you meant to call this function rather than return it.\n' +
639 + ' in span (at **)\n' +
640 + ' in div (at **)\n' +
641 + ' in Foo (at **)',
642 + );
643 + }
644 });
645 });
646 });
packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
+41 -29
@@ -198,7 +198,7 @@ describe('ReactComponentLifeCycle', () => {
198 });
199
200 it('should not allow update state inside of getInitialState', () => {
201 - spyOn(console, 'error');
201 + spyOnDev(console, 'error');
202
203 class StatefulComponent extends React.Component {
204 constructor(props, context) {
@@ -214,21 +214,25 @@ describe('ReactComponentLifeCycle', () => {
214 }
215
216 ReactTestUtils.renderIntoDocument(<StatefulComponent />);
217 - expectDev(console.error.calls.count()).toBe(1);
218 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
219 - 'Warning: setState(...): Can only update a mounted or ' +
220 - 'mounting component. This usually means you called setState() on an ' +
221 - 'unmounted component. This is a no-op.\n\nPlease check the code for the ' +
222 - 'StatefulComponent component.',
223 - );
217 + if (__DEV__) {
218 + expect(console.error.calls.count()).toBe(1);
219 + expect(console.error.calls.argsFor(0)[0]).toBe(
220 + 'Warning: setState(...): Can only update a mounted or ' +
221 + 'mounting component. This usually means you called setState() on an ' +
222 + 'unmounted component. This is a no-op.\n\nPlease check the code for the ' +
223 + 'StatefulComponent component.',
224 + );
225 + }
226
227 // Check deduplication
228 ReactTestUtils.renderIntoDocument(<StatefulComponent />);
227 - expectDev(console.error.calls.count()).toBe(1);
229 + if (__DEV__) {
230 + expect(console.error.calls.count()).toBe(1);
231 + }
232 });
233
234 it('should correctly determine if a component is mounted', () => {
231 - spyOn(console, 'error');
235 + spyOnDev(console, 'error');
236 class Component extends React.Component {
237 _isMounted() {
238 // No longer a public API, but we can test that it works internally by
@@ -252,14 +256,16 @@ describe('ReactComponentLifeCycle', () => {
256 var instance = ReactTestUtils.renderIntoDocument(element);
257 expect(instance._isMounted()).toBeTruthy();
258
255 - expectDev(console.error.calls.count()).toBe(1);
256 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
257 - 'Component is accessing isMounted inside its render()',
258 - );
259 + if (__DEV__) {
260 + expect(console.error.calls.count()).toBe(1);
261 + expect(console.error.calls.argsFor(0)[0]).toContain(
262 + 'Component is accessing isMounted inside its render()',
263 + );
264 + }
265 });
266
267 it('should correctly determine if a null component is mounted', () => {
262 - spyOn(console, 'error');
268 + spyOnDev(console, 'error');
269 class Component extends React.Component {
270 _isMounted() {
271 // No longer a public API, but we can test that it works internally by
@@ -283,10 +289,12 @@ describe('ReactComponentLifeCycle', () => {
289 var instance = ReactTestUtils.renderIntoDocument(element);
290 expect(instance._isMounted()).toBeTruthy();
291
286 - expectDev(console.error.calls.count()).toBe(1);
287 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
288 - 'Component is accessing isMounted inside its render()',
289 - );
292 + if (__DEV__) {
293 + expect(console.error.calls.count()).toBe(1);
294 + expect(console.error.calls.argsFor(0)[0]).toContain(
295 + 'Component is accessing isMounted inside its render()',
296 + );
297 + }
298 });
299
300 it('isMounted should return false when unmounted', () => {
@@ -309,7 +317,7 @@ describe('ReactComponentLifeCycle', () => {
317 });
318
319 it('warns if findDOMNode is used inside render', () => {
312 - spyOn(console, 'error');
320 + spyOnDev(console, 'error');
321 class Component extends React.Component {
322 state = {isMounted: false};
323 componentDidMount() {
@@ -324,14 +332,16 @@ describe('ReactComponentLifeCycle', () => {
332 }
333
334 ReactTestUtils.renderIntoDocument(<Component />);
327 - expectDev(console.error.calls.count()).toBe(1);
328 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
329 - 'Component is accessing findDOMNode inside its render()',
330 - );
335 + if (__DEV__) {
336 + expect(console.error.calls.count()).toBe(1);
337 + expect(console.error.calls.argsFor(0)[0]).toContain(
338 + 'Component is accessing findDOMNode inside its render()',
339 + );
340 + }
341 });
342
343 it('should carry through each of the phases of setup', () => {
334 - spyOn(console, 'error');
344 + spyOnDev(console, 'error');
345
346 class LifeCycleComponent extends React.Component {
347 constructor(props, context) {
@@ -440,10 +450,12 @@ describe('ReactComponentLifeCycle', () => {
450 expect(getLifeCycleState(instance)).toBe('UNMOUNTED');
451 expect(instance.state).toEqual(POST_WILL_UNMOUNT_STATE);
452
443 - expectDev(console.error.calls.count()).toBe(1);
444 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
445 - 'LifeCycleComponent is accessing isMounted inside its render() function',
446 - );
453 + if (__DEV__) {
454 + expect(console.error.calls.count()).toBe(1);
455 + expect(console.error.calls.argsFor(0)[0]).toContain(
456 + 'LifeCycleComponent is accessing isMounted inside its render() function',
457 + );
458 + }
459 });
460
461 it('should not throw when updating an auxiliary component', () => {
packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
+156 -109
@@ -122,26 +122,29 @@ describe('ReactCompositeComponent', () => {
122 }
123 }
124
125 - spyOn(console, 'warn');
125 + spyOnDev(console, 'warn');
126 var markup = ReactDOMServer.renderToString(<Parent />);
127
128 // Old API based on heuristic
129 var container = document.createElement('div');
130 container.innerHTML = markup;
131 ReactDOM.render(<Parent />, container);
132 - expectDev(console.warn.calls.count()).toBe(1);
133 - expectDev(console.warn.calls.argsFor(0)[0]).toContain(
134 - 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
135 - 'will stop working in React v17. Replace the ReactDOM.render() call ' +
136 - 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
137 - );
138 -
132 + if (__DEV__) {
133 + expect(console.warn.calls.count()).toBe(1);
134 + expect(console.warn.calls.argsFor(0)[0]).toContain(
135 + 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
136 + 'will stop working in React v17. Replace the ReactDOM.render() call ' +
137 + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
138 + );
139 + console.warn.calls.reset();
140 + }
141 // New explicit API
140 - console.warn.calls.reset();
142 container = document.createElement('div');
143 container.innerHTML = markup;
144 ReactDOM.hydrate(<Parent />, container);
144 - expectDev(console.warn.calls.count()).toBe(0);
145 + if (__DEV__) {
146 + expect(console.warn.calls.count()).toBe(0);
147 + }
148 });
149
150 it('should react to state changes from callbacks', () => {
@@ -231,7 +234,7 @@ describe('ReactCompositeComponent', () => {
234 });
235
236 it('should warn about `forceUpdate` on unmounted components', () => {
234 - spyOn(console, 'error');
237 + spyOnDev(console, 'error');
238
239 var container = document.createElement('div');
240 document.body.appendChild(container);
@@ -248,25 +251,31 @@ describe('ReactCompositeComponent', () => {
251 instance = ReactDOM.render(instance, container);
252 instance.forceUpdate();
253
251 - expectDev(console.error.calls.count()).toBe(0);
254 + if (__DEV__) {
255 + expect(console.error.calls.count()).toBe(0);
256 + }
257
258 ReactDOM.unmountComponentAtNode(container);
259
260 instance.forceUpdate();
256 - expectDev(console.error.calls.count()).toBe(1);
257 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
258 - 'Can only update a mounted or mounting component. This usually means ' +
259 - 'you called setState, replaceState, or forceUpdate on an unmounted ' +
260 - 'component. This is a no-op.\n\nPlease check the code for the ' +
261 - 'Component component.',
262 - );
261 + if (__DEV__) {
262 + expect(console.error.calls.count()).toBe(1);
263 + expect(console.error.calls.argsFor(0)[0]).toContain(
264 + 'Can only update a mounted or mounting component. This usually means ' +
265 + 'you called setState, replaceState, or forceUpdate on an unmounted ' +
266 + 'component. This is a no-op.\n\nPlease check the code for the ' +
267 + 'Component component.',
268 + );
269 + }
270
271 instance.forceUpdate();
265 - expectDev(console.error.calls.count()).toBe(1);
272 + if (__DEV__) {
273 + expect(console.error.calls.count()).toBe(1);
274 + }
275 });
276
277 it('should warn about `setState` on unmounted components', () => {
269 - spyOn(console, 'error');
278 + spyOnDev(console, 'error');
279
280 var container = document.createElement('div');
281 document.body.appendChild(container);
@@ -291,7 +300,9 @@ describe('ReactCompositeComponent', () => {
300
301 instance.setState({value: 1});
302
294 - expectDev(console.error.calls.count()).toBe(0);
303 + if (__DEV__) {
304 + expect(console.error.calls.count()).toBe(0);
305 + }
306
307 expect(renders).toBe(2);
308
@@ -300,13 +311,15 @@ describe('ReactCompositeComponent', () => {
311
312 expect(renders).toBe(2);
313
303 - expectDev(console.error.calls.count()).toBe(1);
304 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
305 - 'Can only update a mounted or mounting component. This usually means ' +
306 - 'you called setState, replaceState, or forceUpdate on an unmounted ' +
307 - 'component. This is a no-op.\n\nPlease check the code for the ' +
308 - 'Component component.',
309 - );
314 + if (__DEV__) {
315 + expect(console.error.calls.count()).toBe(1);
316 + expect(console.error.calls.argsFor(0)[0]).toContain(
317 + 'Can only update a mounted or mounting component. This usually means ' +
318 + 'you called setState, replaceState, or forceUpdate on an unmounted ' +
319 + 'component. This is a no-op.\n\nPlease check the code for the ' +
320 + 'Component component.',
321 + );
322 + }
323 });
324
325 it('should silently allow `setState`, not call cb on unmounting components', () => {
@@ -338,27 +351,31 @@ describe('ReactCompositeComponent', () => {
351 });
352
353 it('should warn when rendering a class with a render method that does not extend React.Component', () => {
341 - spyOn(console, 'error');
354 + spyOnDev(console, 'error');
355 var container = document.createElement('div');
356 class ClassWithRenderNotExtended {
357 render() {
358 return <div />;
359 }
360 }
348 - expectDev(console.error.calls.count()).toBe(0);
361 + if (__DEV__) {
362 + expect(console.error.calls.count()).toBe(0);
363 + }
364 expect(() => {
365 ReactDOM.render(<ClassWithRenderNotExtended />, container);
366 }).toThrow(TypeError);
352 - expectDev(console.error.calls.count()).toBe(1);
353 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
354 - 'Warning: The <ClassWithRenderNotExtended /> component appears to have a render method, ' +
355 - "but doesn't extend React.Component. This is likely to cause errors. " +
356 - 'Change ClassWithRenderNotExtended to extend React.Component instead.',
357 - );
367 + if (__DEV__) {
368 + expect(console.error.calls.count()).toBe(1);
369 + expect(console.error.calls.argsFor(0)[0]).toContain(
370 + 'Warning: The <ClassWithRenderNotExtended /> component appears to have a render method, ' +
371 + "but doesn't extend React.Component. This is likely to cause errors. " +
372 + 'Change ClassWithRenderNotExtended to extend React.Component instead.',
373 + );
374 + }
375 });
376
377 it('should warn about `setState` in render', () => {
361 - spyOn(console, 'error');
378 + spyOnDev(console, 'error');
379
380 var container = document.createElement('div');
381
@@ -378,17 +395,21 @@ describe('ReactCompositeComponent', () => {
395 }
396 }
397
381 - expectDev(console.error.calls.count()).toBe(0);
398 + if (__DEV__) {
399 + expect(console.error.calls.count()).toBe(0);
400 + }
401
402 var instance = ReactDOM.render(<Component />, container);
403
385 - expectDev(console.error.calls.count()).toBe(1);
386 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
387 - 'Cannot update during an existing state transition (such as within ' +
388 - "`render` or another component's constructor). Render methods should " +
389 - 'be a pure function of props and state; constructor side-effects are ' +
390 - 'an anti-pattern, but can be moved to `componentWillMount`.',
391 - );
404 + if (__DEV__) {
405 + expect(console.error.calls.count()).toBe(1);
406 + expect(console.error.calls.argsFor(0)[0]).toContain(
407 + 'Cannot update during an existing state transition (such as within ' +
408 + "`render` or another component's constructor). Render methods should " +
409 + 'be a pure function of props and state; constructor side-effects are ' +
410 + 'an anti-pattern, but can be moved to `componentWillMount`.',
411 + );
412 + }
413
414 // The setState call is queued and then executed as a second pass. This
415 // behavior is undefined though so we're free to change it to suit the
@@ -406,11 +427,13 @@ describe('ReactCompositeComponent', () => {
427 // Test deduplication
428 ReactDOM.unmountComponentAtNode(container);
429 ReactDOM.render(<Component prop={123} />, container);
409 - expectDev(console.error.calls.count()).toBe(1);
430 + if (__DEV__) {
431 + expect(console.error.calls.count()).toBe(1);
432 + }
433 });
434
435 it('should warn about `setState` in getChildContext', () => {
413 - spyOn(console, 'error');
436 + spyOnDev(console, 'error');
437
438 var container = document.createElement('div');
439
@@ -432,19 +455,25 @@ describe('ReactCompositeComponent', () => {
455 }
456 Component.childContextTypes = {};
457
435 - expectDev(console.error.calls.count()).toBe(0);
458 + if (__DEV__) {
459 + expect(console.error.calls.count()).toBe(0);
460 + }
461 var instance = ReactDOM.render(<Component />, container);
462 expect(renderPasses).toBe(2);
463 expect(instance.state.value).toBe(1);
439 - expectDev(console.error.calls.count()).toBe(1);
440 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
441 - 'Warning: setState(...): Cannot call setState() inside getChildContext()',
442 - );
464 + if (__DEV__) {
465 + expect(console.error.calls.count()).toBe(1);
466 + expect(console.error.calls.argsFor(0)[0]).toBe(
467 + 'Warning: setState(...): Cannot call setState() inside getChildContext()',
468 + );
469 + }
470
471 // Test deduplication
472 ReactDOM.unmountComponentAtNode(container);
473 ReactDOM.render(<Component />, container);
447 - expectDev(console.error.calls.count()).toBe(1);
474 + if (__DEV__) {
475 + expect(console.error.calls.count()).toBe(1);
476 + }
477 });
478
479 it('should cleanup even if render() fatals', () => {
@@ -496,7 +525,7 @@ describe('ReactCompositeComponent', () => {
525 });
526
527 it('should warn when shouldComponentUpdate() returns undefined', () => {
499 - spyOn(console, 'error');
528 + spyOnDev(console, 'error');
529
530 class Component extends React.Component {
531 state = {bogus: false};
@@ -513,15 +542,17 @@ describe('ReactCompositeComponent', () => {
542 var instance = ReactTestUtils.renderIntoDocument(<Component />);
543 instance.setState({bogus: true});
544
516 - expectDev(console.error.calls.count()).toBe(1);
517 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
518 - 'Warning: Component.shouldComponentUpdate(): Returned undefined instead of a ' +
519 - 'boolean value. Make sure to return true or false.',
520 - );
545 + if (__DEV__) {
546 + expect(console.error.calls.count()).toBe(1);
547 + expect(console.error.calls.argsFor(0)[0]).toBe(
548 + 'Warning: Component.shouldComponentUpdate(): Returned undefined instead of a ' +
549 + 'boolean value. Make sure to return true or false.',
550 + );
551 + }
552 });
553
554 it('should warn when componentDidUnmount method is defined', () => {
524 - spyOn(console, 'error');
555 + spyOnDev(console, 'error');
556
557 class Component extends React.Component {
558 componentDidUnmount = () => {};
@@ -533,16 +564,18 @@ describe('ReactCompositeComponent', () => {
564
565 ReactTestUtils.renderIntoDocument(<Component />);
566
536 - expectDev(console.error.calls.count()).toBe(1);
537 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
538 - 'Warning: Component has a method called ' +
539 - 'componentDidUnmount(). But there is no such lifecycle method. ' +
540 - 'Did you mean componentWillUnmount()?',
541 - );
567 + if (__DEV__) {
568 + expect(console.error.calls.count()).toBe(1);
569 + expect(console.error.calls.argsFor(0)[0]).toBe(
570 + 'Warning: Component has a method called ' +
571 + 'componentDidUnmount(). But there is no such lifecycle method. ' +
572 + 'Did you mean componentWillUnmount()?',
573 + );
574 + }
575 });
576
577 it('should warn when componentDidReceiveProps method is defined', () => {
545 - spyOn(console, 'error');
578 + spyOnDev(console, 'error');
579
580 class Component extends React.Component {
581 componentDidReceiveProps = () => {};
@@ -554,18 +587,20 @@ describe('ReactCompositeComponent', () => {
587
588 ReactTestUtils.renderIntoDocument(<Component />);
589
557 - expectDev(console.error.calls.count()).toBe(1);
558 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
559 - 'Warning: Component has a method called ' +
560 - 'componentDidReceiveProps(). But there is no such lifecycle method. ' +
561 - 'If you meant to update the state in response to changing props, ' +
562 - 'use componentWillReceiveProps(). If you meant to fetch data or ' +
563 - 'run side-effects or mutations after React has updated the UI, use componentDidUpdate().',
564 - );
590 + if (__DEV__) {
591 + expect(console.error.calls.count()).toBe(1);
592 + expect(console.error.calls.argsFor(0)[0]).toBe(
593 + 'Warning: Component has a method called ' +
594 + 'componentDidReceiveProps(). But there is no such lifecycle method. ' +
595 + 'If you meant to update the state in response to changing props, ' +
596 + 'use componentWillReceiveProps(). If you meant to fetch data or ' +
597 + 'run side-effects or mutations after React has updated the UI, use componentDidUpdate().',
598 + );
599 + }
600 });
601
602 it('should warn when defaultProps was defined as an instance property', () => {
568 - spyOn(console, 'error');
603 + spyOnDev(console, 'error');
604
605 class Component extends React.Component {
606 constructor(props) {
@@ -580,11 +615,13 @@ describe('ReactCompositeComponent', () => {
615
616 ReactTestUtils.renderIntoDocument(<Component />);
617
583 - expectDev(console.error.calls.count()).toBe(1);
584 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
585 - 'Warning: Setting defaultProps as an instance property on Component is not supported ' +
586 - 'and will be ignored. Instead, define defaultProps as a static property on Component.',
587 - );
618 + if (__DEV__) {
619 + expect(console.error.calls.count()).toBe(1);
620 + expect(console.error.calls.argsFor(0)[0]).toBe(
621 + 'Warning: Setting defaultProps as an instance property on Component is not supported ' +
622 + 'and will be ignored. Instead, define defaultProps as a static property on Component.',
623 + );
624 + }
625 });
626
627 it('should pass context to children when not owner', () => {
@@ -1056,7 +1093,7 @@ describe('ReactCompositeComponent', () => {
1093 });
1094
1095 it('should disallow nested render calls', () => {
1059 - spyOn(console, 'error');
1096 + spyOnDev(console, 'error');
1097
1098 class Inner extends React.Component {
1099 render() {
@@ -1072,13 +1109,15 @@ describe('ReactCompositeComponent', () => {
1109 }
1110
1111 ReactTestUtils.renderIntoDocument(<Outer />);
1075 - expectDev(console.error.calls.count()).toBe(1);
1076 - expectDev(console.error.calls.argsFor(0)[0]).toMatch(
1077 - 'Render methods should be a pure function of props and state; ' +
1078 - 'triggering nested component updates from render is not allowed. If ' +
1079 - 'necessary, trigger nested updates in componentDidUpdate.\n\nCheck the ' +
1080 - 'render method of Outer.',
1081 - );
1112 + if (__DEV__) {
1113 + expect(console.error.calls.count()).toBe(1);
1114 + expect(console.error.calls.argsFor(0)[0]).toMatch(
1115 + 'Render methods should be a pure function of props and state; ' +
1116 + 'triggering nested component updates from render is not allowed. If ' +
1117 + 'necessary, trigger nested updates in componentDidUpdate.\n\nCheck the ' +
1118 + 'render method of Outer.',
1119 + );
1120 + }
1121 });
1122
1123 it('only renders once if updated in componentWillReceiveProps', () => {
@@ -1353,7 +1392,7 @@ describe('ReactCompositeComponent', () => {
1392 });
1393
1394 it('should warn when mutated props are passed', () => {
1356 - spyOn(console, 'error');
1395 + spyOnDev(console, 'error');
1396
1397 var container = document.createElement('div');
1398
@@ -1368,15 +1407,19 @@ describe('ReactCompositeComponent', () => {
1407 }
1408 }
1409
1371 - expectDev(console.error.calls.count()).toBe(0);
1410 + if (__DEV__) {
1411 + expect(console.error.calls.count()).toBe(0);
1412 + }
1413
1414 ReactDOM.render(<Foo idx="qwe" />, container);
1415
1375 - expectDev(console.error.calls.count()).toBe(1);
1376 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
1377 - 'Foo(...): When calling super() in `Foo`, make sure to pass ' +
1378 - "up the same props that your component's constructor was passed.",
1379 - );
1416 + if (__DEV__) {
1417 + expect(console.error.calls.count()).toBe(1);
1418 + expect(console.error.calls.argsFor(0)[0]).toContain(
1419 + 'Foo(...): When calling super() in `Foo`, make sure to pass ' +
1420 + "up the same props that your component's constructor was passed.",
1421 + );
1422 + }
1423 });
1424
1425 it('should only call componentWillUnmount once', () => {
@@ -1592,7 +1635,7 @@ describe('ReactCompositeComponent', () => {
1635 });
1636
1637 it('should return a meaningful warning when constructor is returned', () => {
1595 - spyOn(console, 'error');
1638 + spyOnDev(console, 'error');
1639 class RenderTextInvalidConstructor extends React.Component {
1640 constructor(props) {
1641 super(props);
@@ -1608,25 +1651,29 @@ describe('ReactCompositeComponent', () => {
1651 ReactTestUtils.renderIntoDocument(<RenderTextInvalidConstructor />);
1652 }).toThrow();
1653
1611 - expectDev(console.error.calls.count()).toBe(1);
1612 - expectDev(console.error.calls.mostRecent().args[0]).toBe(
1613 - 'Warning: RenderTextInvalidConstructor(...): No `render` method found on the returned component instance: ' +
1614 - 'did you accidentally return an object from the constructor?',
1615 - );
1654 + if (__DEV__) {
1655 + expect(console.error.calls.count()).toBe(1);
1656 + expect(console.error.calls.mostRecent().args[0]).toBe(
1657 + 'Warning: RenderTextInvalidConstructor(...): No `render` method found on the returned component instance: ' +
1658 + 'did you accidentally return an object from the constructor?',
1659 + );
1660 + }
1661 });
1662
1663 it('should return error if render is not defined', () => {
1619 - spyOn(console, 'error');
1664 + spyOnDev(console, 'error');
1665 class RenderTestUndefinedRender extends React.Component {}
1666
1667 expect(function() {
1668 ReactTestUtils.renderIntoDocument(<RenderTestUndefinedRender />);
1669 }).toThrow();
1670
1626 - expectDev(console.error.calls.count()).toBe(1);
1627 - expectDev(console.error.calls.mostRecent().args[0]).toBe(
1628 - 'Warning: RenderTestUndefinedRender(...): No `render` method found on the returned ' +
1629 - 'component instance: you may have forgotten to define `render`.',
1630 - );
1671 + if (__DEV__) {
1672 + expect(console.error.calls.count()).toBe(1);
1673 + expect(console.error.calls.mostRecent().args[0]).toBe(
1674 + 'Warning: RenderTestUndefinedRender(...): No `render` method found on the returned ' +
1675 + 'component instance: you may have forgotten to define `render`.',
1676 + );
1677 + }
1678 });
1679 });
packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js
+21 -15
@@ -381,7 +381,7 @@ describe('ReactCompositeComponent-state', () => {
381 });
382
383 it('should treat assigning to this.state inside cWRP as a replaceState, with a warning', () => {
384 - spyOn(console, 'error');
384 + spyOnDev(console, 'error');
385
386 let ops = [];
387 class Test extends React.Component {
@@ -416,20 +416,24 @@ describe('ReactCompositeComponent-state', () => {
416 'render -- step: 3, extra: false',
417 'callback -- step: 3, extra: false',
418 ]);
419 - expect(console.error.calls.count()).toEqual(1);
420 - expect(console.error.calls.argsFor(0)[0]).toEqual(
421 - 'Warning: Test.componentWillReceiveProps(): Assigning directly to ' +
422 - "this.state is deprecated (except inside a component's constructor). " +
423 - 'Use setState instead.',
424 - );
419 + if (__DEV__) {
420 + expect(console.error.calls.count()).toEqual(1);
421 + expect(console.error.calls.argsFor(0)[0]).toEqual(
422 + 'Warning: Test.componentWillReceiveProps(): Assigning directly to ' +
423 + "this.state is deprecated (except inside a component's constructor). " +
424 + 'Use setState instead.',
425 + );
426 + }
427
428 // Check deduplication
429 ReactDOM.render(<Test />, container);
428 - expect(console.error.calls.count()).toEqual(1);
430 + if (__DEV__) {
431 + expect(console.error.calls.count()).toEqual(1);
432 + }
433 });
434
435 it('should treat assigning to this.state inside cWM as a replaceState, with a warning', () => {
432 - spyOn(console, 'error');
436 + spyOnDev(console, 'error');
437
438 let ops = [];
439 class Test extends React.Component {
@@ -461,11 +465,13 @@ describe('ReactCompositeComponent-state', () => {
465 'render -- step: 3, extra: false',
466 'callback -- step: 3, extra: false',
467 ]);
464 - expect(console.error.calls.count()).toEqual(1);
465 - expect(console.error.calls.argsFor(0)[0]).toEqual(
466 - 'Warning: Test.componentWillMount(): Assigning directly to ' +
467 - "this.state is deprecated (except inside a component's constructor). " +
468 - 'Use setState instead.',
469 - );
468 + if (__DEV__) {
469 + expect(console.error.calls.count()).toEqual(1);
470 + expect(console.error.calls.argsFor(0)[0]).toEqual(
471 + 'Warning: Test.componentWillMount(): Assigning directly to ' +
472 + "this.state is deprecated (except inside a component's constructor). " +
473 + 'Use setState instead.',
474 + );
475 + }
476 });
477 });
packages/react-dom/src/__tests__/ReactDOM-test.js
+62 -28
@@ -109,7 +109,7 @@ describe('ReactDOM', () => {
109 });
110
111 it('throws in render() if the mount callback is not a function', () => {
112 - spyOn(console, 'error');
112 + spyOnDev(console, 'error');
113
114 function Foo() {
115 this.a = 1;
@@ -129,31 +129,37 @@ describe('ReactDOM', () => {
129 'Invalid argument passed as callback. Expected a function. Instead ' +
130 'received: no',
131 );
132 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
133 - 'render(...): Expected the last optional `callback` argument to be ' +
134 - 'a function. Instead received: no.',
135 - );
132 + if (__DEV__) {
133 + expect(console.error.calls.argsFor(0)[0]).toContain(
134 + 'render(...): Expected the last optional `callback` argument to be ' +
135 + 'a function. Instead received: no.',
136 + );
137 + }
138 expect(() => ReactDOM.render(<A />, myDiv, {foo: 'bar'})).toThrowError(
139 'Invalid argument passed as callback. Expected a function. Instead ' +
140 'received: [object Object]',
141 );
140 - expectDev(console.error.calls.argsFor(1)[0]).toContain(
141 - 'render(...): Expected the last optional `callback` argument to be ' +
142 - 'a function. Instead received: [object Object].',
143 - );
142 + if (__DEV__) {
143 + expect(console.error.calls.argsFor(1)[0]).toContain(
144 + 'render(...): Expected the last optional `callback` argument to be ' +
145 + 'a function. Instead received: [object Object].',
146 + );
147 + }
148 expect(() => ReactDOM.render(<A />, myDiv, new Foo())).toThrowError(
149 'Invalid argument passed as callback. Expected a function. Instead ' +
150 'received: [object Object]',
151 );
148 - expectDev(console.error.calls.argsFor(2)[0]).toContain(
149 - 'render(...): Expected the last optional `callback` argument to be ' +
150 - 'a function. Instead received: [object Object].',
151 - );
152 - expect(console.error.calls.count()).toBe(3);
152 + if (__DEV__) {
153 + expect(console.error.calls.argsFor(2)[0]).toContain(
154 + 'render(...): Expected the last optional `callback` argument to be ' +
155 + 'a function. Instead received: [object Object].',
156 + );
157 + expect(console.error.calls.count()).toBe(3);
158 + }
159 });
160
161 it('throws in render() if the update callback is not a function', () => {
156 - spyOn(console, 'error');
162 + spyOnDev(console, 'error');
163
164 function Foo() {
165 this.a = 1;
@@ -174,29 +180,35 @@ describe('ReactDOM', () => {
180 'Invalid argument passed as callback. Expected a function. Instead ' +
181 'received: no',
182 );
177 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
178 - 'render(...): Expected the last optional `callback` argument to be ' +
179 - 'a function. Instead received: no.',
180 - );
183 + if (__DEV__) {
184 + expect(console.error.calls.argsFor(0)[0]).toContain(
185 + 'render(...): Expected the last optional `callback` argument to be ' +
186 + 'a function. Instead received: no.',
187 + );
188 + }
189 ReactDOM.render(<A />, myDiv); // Re-mount
190 expect(() => ReactDOM.render(<A />, myDiv, {foo: 'bar'})).toThrowError(
191 'Invalid argument passed as callback. Expected a function. Instead ' +
192 'received: [object Object]',
193 );
186 - expectDev(console.error.calls.argsFor(1)[0]).toContain(
187 - 'render(...): Expected the last optional `callback` argument to be ' +
188 - 'a function. Instead received: [object Object].',
189 - );
194 + if (__DEV__) {
195 + expect(console.error.calls.argsFor(1)[0]).toContain(
196 + 'render(...): Expected the last optional `callback` argument to be ' +
197 + 'a function. Instead received: [object Object].',
198 + );
199 + }
200 ReactDOM.render(<A />, myDiv); // Re-mount
201 expect(() => ReactDOM.render(<A />, myDiv, new Foo())).toThrowError(
202 'Invalid argument passed as callback. Expected a function. Instead ' +
203 'received: [object Object]',
204 );
195 - expectDev(console.error.calls.argsFor(2)[0]).toContain(
196 - 'render(...): Expected the last optional `callback` argument to be ' +
197 - 'a function. Instead received: [object Object].',
198 - );
199 - expect(console.error.calls.count()).toBe(3);
205 + if (__DEV__) {
206 + expect(console.error.calls.argsFor(2)[0]).toContain(
207 + 'render(...): Expected the last optional `callback` argument to be ' +
208 + 'a function. Instead received: [object Object].',
209 + );
210 + expect(console.error.calls.count()).toBe(3);
211 + }
212 });
213
214 it('preserves focus', () => {
@@ -338,4 +350,26 @@ describe('ReactDOM', () => {
350 ];
351 expect(actual).toEqual(expected);
352 });
353 +
354 + it('should not crash with devtools installed', () => {
355 + try {
356 + global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {
357 + inject: function() {},
358 + onCommitFiberRoot: function() {},
359 + onCommitFiberUnmount: function() {},
360 + supportsFiber: true,
361 + };
362 + jest.resetModules();
363 + React = require('react');
364 + ReactDOM = require('react-dom');
365 + class Component extends React.Component {
366 + render() {
367 + return <div />;
368 + }
369 + }
370 + ReactDOM.render(<Component />, document.createElement('container'));
371 + } finally {
372 + delete global.__REACT_DEVTOOLS_GLOBAL_HOOK__;
373 + }
374 + });
375 });
packages/react-dom/src/__tests__/ReactDOMAttribute-test.js
+52 -48
@@ -46,20 +46,20 @@ describe('ReactDOM unknown attribute', () => {
46 });
47
48 it('changes values true, false to null, and also warns once', () => {
49 - spyOn(console, 'error');
49 + spyOnDev(console, 'error');
50
51 testUnknownAttributeAssignment(true, null);
52 testUnknownAttributeAssignment(false, null);
53
54 - expectDev(
55 - normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
56 - ).toMatch(
57 - 'Received `true` for a non-boolean attribute `unknown`.\n\n' +
58 - 'If you want to write it to the DOM, pass a string instead: ' +
59 - 'unknown="true" or unknown={value.toString()}.\n' +
60 - ' in div (at **)',
61 - );
62 - expectDev(console.error.calls.count()).toBe(1);
54 + if (__DEV__) {
55 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toMatch(
56 + 'Received `true` for a non-boolean attribute `unknown`.\n\n' +
57 + 'If you want to write it to the DOM, pass a string instead: ' +
58 + 'unknown="true" or unknown={value.toString()}.\n' +
59 + ' in div (at **)',
60 + );
61 + expect(console.error.calls.count()).toBe(1);
62 + }
63 });
64
65 it('removes unknown attributes that were rendered but are now missing', () => {
@@ -82,17 +82,17 @@ describe('ReactDOM unknown attribute', () => {
82 });
83
84 it('coerces NaN to strings and warns', () => {
85 - spyOn(console, 'error');
85 + spyOnDev(console, 'error');
86
87 testUnknownAttributeAssignment(NaN, 'NaN');
88 - expectDev(
89 - normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
90 - ).toMatch(
91 - 'Warning: Received NaN for the `unknown` attribute. ' +
92 - 'If this is expected, cast the value to a string.\n' +
93 - ' in div (at **)',
94 - );
95 - expectDev(console.error.calls.count()).toBe(1);
88 + if (__DEV__) {
89 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toMatch(
90 + 'Warning: Received NaN for the `unknown` attribute. ' +
91 + 'If this is expected, cast the value to a string.\n' +
92 + ' in div (at **)',
93 + );
94 + expect(console.error.calls.count()).toBe(1);
95 + }
96 });
97
98 it('coerces objects to strings and warns', () => {
@@ -107,50 +107,54 @@ describe('ReactDOM unknown attribute', () => {
107 });
108
109 it('removes symbols and warns', () => {
110 - spyOn(console, 'error');
110 + spyOnDev(console, 'error');
111
112 testUnknownAttributeRemoval(Symbol('foo'));
113 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
114 - 'Warning: Invalid value for prop `unknown` on <div> tag. Either remove it ' +
115 - 'from the element, or pass a string or number value to keep it ' +
116 - 'in the DOM. For details, see https://fb.me/react-attribute-behavior\n' +
117 - ' in div (at **)',
118 - );
119 - expectDev(console.error.calls.count()).toBe(1);
113 + if (__DEV__) {
114 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
115 + 'Warning: Invalid value for prop `unknown` on <div> tag. Either remove it ' +
116 + 'from the element, or pass a string or number value to keep it ' +
117 + 'in the DOM. For details, see https://fb.me/react-attribute-behavior\n' +
118 + ' in div (at **)',
119 + );
120 + expect(console.error.calls.count()).toBe(1);
121 + }
122 });
123
124 it('removes functions and warns', () => {
123 - spyOn(console, 'error');
125 + spyOnDev(console, 'error');
126
127 testUnknownAttributeRemoval(function someFunction() {});
126 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
127 - 'Warning: Invalid value for prop `unknown` on <div> tag. Either remove ' +
128 - 'it from the element, or pass a string or number value to ' +
129 - 'keep it in the DOM. For details, see ' +
130 - 'https://fb.me/react-attribute-behavior\n' +
131 - ' in div (at **)',
132 - );
133 - expectDev(console.error.calls.count()).toBe(1);
128 + if (__DEV__) {
129 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
130 + 'Warning: Invalid value for prop `unknown` on <div> tag. Either remove ' +
131 + 'it from the element, or pass a string or number value to ' +
132 + 'keep it in the DOM. For details, see ' +
133 + 'https://fb.me/react-attribute-behavior\n' +
134 + ' in div (at **)',
135 + );
136 + expect(console.error.calls.count()).toBe(1);
137 + }
138 });
139
140 it('allows camelCase unknown attributes and warns', () => {
137 - spyOn(console, 'error');
141 + spyOnDev(console, 'error');
142
143 var el = document.createElement('div');
144 ReactDOM.render(<div helloWorld="something" />, el);
145 expect(el.firstChild.getAttribute('helloworld')).toBe('something');
146
143 - expectDev(console.error.calls.count()).toBe(1);
144 - expectDev(
145 - normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
146 - ).toMatch(
147 - 'React does not recognize the `helloWorld` prop on a DOM element. ' +
148 - 'If you intentionally want it to appear in the DOM as a custom ' +
149 - 'attribute, spell it as lowercase `helloworld` instead. ' +
150 - 'If you accidentally passed it from a parent component, remove ' +
151 - 'it from the DOM element.\n' +
152 - ' in div (at **)',
153 - );
147 + if (__DEV__) {
148 + expect(console.error.calls.count()).toBe(1);
149 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toMatch(
150 + 'React does not recognize the `helloWorld` prop on a DOM element. ' +
151 + 'If you intentionally want it to appear in the DOM as a custom ' +
152 + 'attribute, spell it as lowercase `helloworld` instead. ' +
153 + 'If you accidentally passed it from a parent component, remove ' +
154 + 'it from the DOM element.\n' +
155 + ' in div (at **)',
156 + );
157 + }
158 });
159 });
160 });
packages/react-dom/src/__tests__/ReactDOMComponent-test.js
+629 -480
@@ -132,47 +132,55 @@ describe('ReactDOMComponent', () => {
132 }
133
134 ReactTestUtils.renderIntoDocument(<App />);
135 - expectDev(() => (style.position = 'absolute')).toThrow();
135 + if (__DEV__) {
136 + expect(() => (style.position = 'absolute')).toThrow();
137 + }
138 });
139
140 it('should warn for unknown prop', () => {
139 - spyOn(console, 'error');
141 + spyOnDev(console, 'error');
142 var container = document.createElement('div');
143 ReactDOM.render(<div foo={() => {}} />, container);
142 - expectDev(console.error.calls.count(0)).toBe(1);
143 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
144 - 'Warning: Invalid value for prop `foo` on <div> tag. Either remove it ' +
145 - 'from the element, or pass a string or number value to keep ' +
146 - 'it in the DOM. For details, see https://fb.me/react-attribute-behavior' +
147 - '\n in div (at **)',
148 - );
144 + if (__DEV__) {
145 + expect(console.error.calls.count(0)).toBe(1);
146 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
147 + 'Warning: Invalid value for prop `foo` on <div> tag. Either remove it ' +
148 + 'from the element, or pass a string or number value to keep ' +
149 + 'it in the DOM. For details, see https://fb.me/react-attribute-behavior' +
150 + '\n in div (at **)',
151 + );
152 + }
153 });
154
155 it('should group multiple unknown prop warnings together', () => {
152 - spyOn(console, 'error');
156 + spyOnDev(console, 'error');
157 var container = document.createElement('div');
158 ReactDOM.render(<div foo={() => {}} baz={() => {}} />, container);
155 - expectDev(console.error.calls.count(0)).toBe(1);
156 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
157 - 'Warning: Invalid values for props `foo`, `baz` on <div> tag. Either remove ' +
158 - 'them from the element, or pass a string or number value to keep ' +
159 - 'them in the DOM. For details, see https://fb.me/react-attribute-behavior' +
160 - '\n in div (at **)',
161 - );
159 + if (__DEV__) {
160 + expect(console.error.calls.count(0)).toBe(1);
161 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
162 + 'Warning: Invalid values for props `foo`, `baz` on <div> tag. Either remove ' +
163 + 'them from the element, or pass a string or number value to keep ' +
164 + 'them in the DOM. For details, see https://fb.me/react-attribute-behavior' +
165 + '\n in div (at **)',
166 + );
167 + }
168 });
169
170 it('should warn for onDblClick prop', () => {
165 - spyOn(console, 'error');
171 + spyOnDev(console, 'error');
172 var container = document.createElement('div');
173 ReactDOM.render(<div onDblClick={() => {}} />, container);
168 - expectDev(console.error.calls.count(0)).toBe(1);
169 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
170 - 'Warning: Invalid event handler property `onDblClick`. Did you mean `onDoubleClick`?\n in div (at **)',
171 - );
174 + if (__DEV__) {
175 + expect(console.error.calls.count(0)).toBe(1);
176 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
177 + 'Warning: Invalid event handler property `onDblClick`. Did you mean `onDoubleClick`?\n in div (at **)',
178 + );
179 + }
180 });
181
182 it('should warn for unknown string event handlers', () => {
175 - spyOn(console, 'error');
183 + spyOnDev(console, 'error');
184 var container = document.createElement('div');
185 ReactDOM.render(<div onUnknown="alert(&quot;hack&quot;)" />, container);
186 expect(container.firstChild.hasAttribute('onUnknown')).toBe(false);
@@ -183,20 +191,22 @@ describe('ReactDOMComponent', () => {
191 ReactDOM.render(<div on-unknown="alert(&quot;hack&quot;)" />, container);
192 expect(container.firstChild.hasAttribute('on-unknown')).toBe(false);
193 expect(container.firstChild['on-unknown']).toBe(undefined);
186 - expectDev(console.error.calls.count(0)).toBe(3);
187 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
188 - 'Warning: Unknown event handler property `onUnknown`. It will be ignored.\n in div (at **)',
189 - );
190 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
191 - 'Warning: Unknown event handler property `onunknown`. It will be ignored.\n in div (at **)',
192 - );
193 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
194 - 'Warning: Unknown event handler property `on-unknown`. It will be ignored.\n in div (at **)',
195 - );
194 + if (__DEV__) {
195 + expect(console.error.calls.count(0)).toBe(3);
196 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
197 + 'Warning: Unknown event handler property `onUnknown`. It will be ignored.\n in div (at **)',
198 + );
199 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
200 + 'Warning: Unknown event handler property `onunknown`. It will be ignored.\n in div (at **)',
201 + );
202 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
203 + 'Warning: Unknown event handler property `on-unknown`. It will be ignored.\n in div (at **)',
204 + );
205 + }
206 });
207
208 it('should warn for unknown function event handlers', () => {
199 - spyOn(console, 'error');
209 + spyOnDev(console, 'error');
210 var container = document.createElement('div');
211 ReactDOM.render(<div onUnknown={function() {}} />, container);
212 expect(container.firstChild.hasAttribute('onUnknown')).toBe(false);
@@ -207,32 +217,34 @@ describe('ReactDOMComponent', () => {
217 ReactDOM.render(<div on-unknown={function() {}} />, container);
218 expect(container.firstChild.hasAttribute('on-unknown')).toBe(false);
219 expect(container.firstChild['on-unknown']).toBe(undefined);
210 - expectDev(console.error.calls.count(0)).toBe(3);
211 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
212 - 'Warning: Unknown event handler property `onUnknown`. It will be ignored.\n in div (at **)',
213 - );
214 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
215 - 'Warning: Unknown event handler property `onunknown`. It will be ignored.\n in div (at **)',
216 - );
217 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
218 - 'Warning: Unknown event handler property `on-unknown`. It will be ignored.\n in div (at **)',
219 - );
220 + if (__DEV__) {
221 + expect(console.error.calls.count(0)).toBe(3);
222 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
223 + 'Warning: Unknown event handler property `onUnknown`. It will be ignored.\n in div (at **)',
224 + );
225 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
226 + 'Warning: Unknown event handler property `onunknown`. It will be ignored.\n in div (at **)',
227 + );
228 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
229 + 'Warning: Unknown event handler property `on-unknown`. It will be ignored.\n in div (at **)',
230 + );
231 + }
232 });
233
234 it('should warn for badly cased React attributes', () => {
223 - spyOn(console, 'error');
235 + spyOnDev(console, 'error');
236 var container = document.createElement('div');
237 ReactDOM.render(<div CHILDREN="5" />, container);
238 expect(container.firstChild.getAttribute('CHILDREN')).toBe('5');
227 - expectDev(console.error.calls.count(0)).toBe(1);
228 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
229 - 'Warning: Invalid DOM property `CHILDREN`. Did you mean `children`?\n in div (at **)',
230 - );
239 + if (__DEV__) {
240 + expect(console.error.calls.count(0)).toBe(1);
241 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
242 + 'Warning: Invalid DOM property `CHILDREN`. Did you mean `children`?\n in div (at **)',
243 + );
244 + }
245 });
246
247 it('should not warn for "0" as a unitless style value', () => {
234 - spyOn(console, 'error');
235 -
248 class Component extends React.Component {
249 render() {
250 return <div style={{margin: '0'}} />;
@@ -240,24 +252,23 @@ describe('ReactDOMComponent', () => {
252 }
253
254 ReactTestUtils.renderIntoDocument(<Component />);
243 - expectDev(console.error.calls.count()).toBe(0);
255 });
256
257 it('should warn nicely about NaN in style', () => {
247 - spyOn(console, 'error');
258 + spyOnDev(console, 'error');
259
260 var style = {fontSize: NaN};
261 var div = document.createElement('div');
262 ReactDOM.render(<span style={style} />, div);
263 ReactDOM.render(<span style={style} />, div);
264
254 - expectDev(console.error.calls.count()).toBe(1);
255 - expectDev(
256 - normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
257 - ).toEqual(
258 - 'Warning: `NaN` is an invalid value for the `fontSize` css style property.' +
259 - '\n in span (at **)',
260 - );
265 + if (__DEV__) {
266 + expect(console.error.calls.count()).toBe(1);
267 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
268 + 'Warning: `NaN` is an invalid value for the `fontSize` css style property.' +
269 + '\n in span (at **)',
270 + );
271 + }
272 });
273
274 it('should update styles if initially null', () => {
@@ -445,7 +456,7 @@ describe('ReactDOMComponent', () => {
456 });
457
458 it('should reject attribute key injection attack on markup', () => {
448 - spyOn(console, 'error');
459 + spyOnDev(console, 'error');
460 for (var i = 0; i < 3; i++) {
461 var container = document.createElement('div');
462 var element = React.createElement(
@@ -455,14 +466,16 @@ describe('ReactDOMComponent', () => {
466 );
467 ReactDOM.render(element, container);
468 }
458 - expectDev(console.error.calls.count()).toBe(1);
459 - expectDev(console.error.calls.argsFor(0)[0]).toEqual(
460 - 'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
461 - );
469 + if (__DEV__) {
470 + expect(console.error.calls.count()).toBe(1);
471 + expect(console.error.calls.argsFor(0)[0]).toEqual(
472 + 'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
473 + );
474 + }
475 });
476
477 it('should reject attribute key injection attack on update', () => {
465 - spyOn(console, 'error');
478 + spyOnDev(console, 'error');
479 for (var i = 0; i < 3; i++) {
480 var container = document.createElement('div');
481 var beforeUpdate = React.createElement('x-foo-component', {}, null);
@@ -475,10 +488,12 @@ describe('ReactDOMComponent', () => {
488 );
489 ReactDOM.render(afterUpdate, container);
490 }
478 - expectDev(console.error.calls.count()).toBe(1);
479 - expectDev(console.error.calls.argsFor(0)[0]).toEqual(
480 - 'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
481 - );
491 + if (__DEV__) {
492 + expect(console.error.calls.count()).toBe(1);
493 + expect(console.error.calls.argsFor(0)[0]).toEqual(
494 + 'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
495 + );
496 + }
497 });
498
499 it('should update arbitrary attributes for tags containing dashes', () => {
@@ -723,15 +738,17 @@ describe('ReactDOMComponent', () => {
738 });
739
740 it('should warn about non-string "is" attribute', () => {
726 - spyOn(console, 'error');
741 + spyOnDev(console, 'error');
742 var container = document.createElement('div');
743 ReactDOM.render(<button is={function() {}} />, container);
744
730 - expectDev(console.error.calls.count()).toBe(1);
731 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
732 - 'Received a `function` for a string attribute `is`. If this is expected, cast ' +
733 - 'the value to a string.',
734 - );
745 + if (__DEV__) {
746 + expect(console.error.calls.count()).toBe(1);
747 + expect(console.error.calls.argsFor(0)[0]).toContain(
748 + 'Received a `function` for a string attribute `is`. If this is expected, cast ' +
749 + 'the value to a string.',
750 + );
751 + }
752 });
753
754 it('should not update when switching between null/undefined', () => {
@@ -860,14 +877,14 @@ describe('ReactDOMComponent', () => {
877 });
878
879 it('should work error event on <source> element', () => {
863 - spyOn(console, 'error');
880 + spyOn(console, 'log');
881 var container = document.createElement('div');
882 ReactDOM.render(
883 <video>
884 <source
885 src="http://example.org/video"
886 type="video/mp4"
870 - onError={e => console.error('onError called')}
887 + onError={e => console.log('onError called')}
888 />
889 </video>,
890 container,
@@ -877,12 +894,14 @@ describe('ReactDOMComponent', () => {
894 errorEvent.initEvent('error', false, false);
895 container.getElementsByTagName('source')[0].dispatchEvent(errorEvent);
896
880 - expectDev(console.error.calls.count()).toBe(1);
881 - expectDev(console.error.calls.argsFor(0)[0]).toContain('onError called');
897 + if (__DEV__) {
898 + expect(console.log.calls.count()).toBe(1);
899 + expect(console.log.calls.argsFor(0)[0]).toContain('onError called');
900 + }
901 });
902
903 it('should not duplicate uppercased selfclosing tags', () => {
885 - spyOn(console, 'error');
904 + spyOnDev(console, 'error');
905 class Container extends React.Component {
906 render() {
907 return React.createElement('BR', null);
@@ -891,39 +910,49 @@ describe('ReactDOMComponent', () => {
910
911 var returnedValue = ReactDOMServer.renderToString(<Container />);
912 expect(returnedValue).not.toContain('</BR>');
894 - expectDev(console.error.calls.count()).toBe(1);
895 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
896 - '<BR /> is using uppercase HTML.',
897 - );
913 + if (__DEV__) {
914 + expect(console.error.calls.count()).toBe(1);
915 + expect(console.error.calls.argsFor(0)[0]).toContain(
916 + '<BR /> is using uppercase HTML.',
917 + );
918 + }
919 });
920
921 it('should warn on upper case HTML tags, not SVG nor custom tags', () => {
901 - spyOn(console, 'error');
922 + spyOnDev(console, 'error');
923 ReactTestUtils.renderIntoDocument(
924 React.createElement('svg', null, React.createElement('PATH')),
925 );
905 - expectDev(console.error.calls.count()).toBe(0);
926 + if (__DEV__) {
927 + expect(console.error.calls.count()).toBe(0);
928 + }
929 ReactTestUtils.renderIntoDocument(React.createElement('CUSTOM-TAG'));
907 - expectDev(console.error.calls.count()).toBe(0);
930 + if (__DEV__) {
931 + expect(console.error.calls.count()).toBe(0);
932 + }
933 ReactTestUtils.renderIntoDocument(React.createElement('IMG'));
909 - expectDev(console.error.calls.count()).toBe(1);
910 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
911 - '<IMG /> is using uppercase HTML.',
912 - );
934 + if (__DEV__) {
935 + expect(console.error.calls.count()).toBe(1);
936 + expect(console.error.calls.argsFor(0)[0]).toContain(
937 + '<IMG /> is using uppercase HTML.',
938 + );
939 + }
940 });
941
942 it('should warn on props reserved for future use', () => {
916 - spyOn(console, 'error');
943 + spyOnDev(console, 'error');
944 ReactTestUtils.renderIntoDocument(<div aria="hello" />);
918 - expectDev(console.error.calls.count()).toBe(1);
919 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
920 - 'The `aria` attribute is reserved for future use in React. ' +
921 - 'Pass individual `aria-` attributes instead.',
922 - );
945 + if (__DEV__) {
946 + expect(console.error.calls.count()).toBe(1);
947 + expect(console.error.calls.argsFor(0)[0]).toContain(
948 + 'The `aria` attribute is reserved for future use in React. ' +
949 + 'Pass individual `aria-` attributes instead.',
950 + );
951 + }
952 });
953
954 it('should warn if the tag is unrecognized', () => {
926 - spyOn(console, 'error');
955 + spyOnDev(console, 'error');
956
957 let realToString;
958 try {
@@ -957,19 +986,21 @@ describe('ReactDOMComponent', () => {
986 Object.prototype.toString = realToString; // eslint-disable-line no-extend-native
987 }
988
960 - expectDev(console.error.calls.count()).toBe(4);
961 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
962 - 'The tag <bar> is unrecognized in this browser',
963 - );
964 - expectDev(console.error.calls.argsFor(1)[0]).toContain(
965 - 'The tag <foo> is unrecognized in this browser',
966 - );
967 - expectDev(console.error.calls.argsFor(2)[0]).toContain(
968 - '<hasOwnProperty /> is using uppercase HTML',
969 - );
970 - expectDev(console.error.calls.argsFor(3)[0]).toContain(
971 - 'The tag <hasOwnProperty> is unrecognized in this browser',
972 - );
989 + if (__DEV__) {
990 + expect(console.error.calls.count()).toBe(4);
991 + expect(console.error.calls.argsFor(0)[0]).toContain(
992 + 'The tag <bar> is unrecognized in this browser',
993 + );
994 + expect(console.error.calls.argsFor(1)[0]).toContain(
995 + 'The tag <foo> is unrecognized in this browser',
996 + );
997 + expect(console.error.calls.argsFor(2)[0]).toContain(
998 + '<hasOwnProperty /> is using uppercase HTML',
999 + );
1000 + expect(console.error.calls.argsFor(3)[0]).toContain(
1001 + 'The tag <hasOwnProperty> is unrecognized in this browser',
1002 + );
1003 + }
1004 });
1005
1006 it('should throw on children for void elements', () => {
@@ -983,8 +1014,8 @@ describe('ReactDOMComponent', () => {
1014 expect(caughtErr).not.toBe(undefined);
1015 expect(normalizeCodeLocInfo(caughtErr.message)).toContain(
1016 'input is a void element tag and must neither have `children` nor ' +
986 - 'use `dangerouslySetInnerHTML`.',
987 - '\n in input (at **)',
1017 + 'use `dangerouslySetInnerHTML`.' +
1018 + (__DEV__ ? '\n in input (at **)' : ''),
1019 );
1020 });
1021
@@ -1002,13 +1033,13 @@ describe('ReactDOMComponent', () => {
1033 expect(caughtErr).not.toBe(undefined);
1034 expect(normalizeCodeLocInfo(caughtErr.message)).toContain(
1035 'input is a void element tag and must neither have `children` nor ' +
1005 - 'use `dangerouslySetInnerHTML`.',
1006 - '\n in input (at **)',
1036 + 'use `dangerouslySetInnerHTML`.' +
1037 + (__DEV__ ? '\n in input (at **)' : ''),
1038 );
1039 });
1040
1041 it('should emit a warning once for a named custom component using shady DOM', () => {
1011 - spyOn(console, 'error');
1042 + spyOnDev(console, 'error');
1043
1044 var defaultCreateElement = document.createElement.bind(document);
1045
@@ -1025,20 +1056,24 @@ describe('ReactDOMComponent', () => {
1056 }
1057 var node = document.createElement('div');
1058 ReactDOM.render(<ShadyComponent />, node);
1028 - expectDev(console.error.calls.count()).toBe(1);
1029 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
1030 - 'ShadyComponent is using shady DOM. Using shady DOM with React can ' +
1031 - 'cause things to break subtly.',
1032 - );
1059 + if (__DEV__) {
1060 + expect(console.error.calls.count()).toBe(1);
1061 + expect(console.error.calls.argsFor(0)[0]).toContain(
1062 + 'ShadyComponent is using shady DOM. Using shady DOM with React can ' +
1063 + 'cause things to break subtly.',
1064 + );
1065 + }
1066 mountComponent({is: 'custom-shady-div2'});
1034 - expectDev(console.error.calls.count()).toBe(1);
1067 + if (__DEV__) {
1068 + expect(console.error.calls.count()).toBe(1);
1069 + }
1070 } finally {
1071 document.createElement = defaultCreateElement;
1072 }
1073 });
1074
1075 it('should emit a warning once for an unnamed custom component using shady DOM', () => {
1041 - spyOn(console, 'error');
1076 + spyOnDev(console, 'error');
1077
1078 var defaultCreateElement = document.createElement.bind(document);
1079
@@ -1050,14 +1085,18 @@ describe('ReactDOMComponent', () => {
1085 };
1086
1087 mountComponent({is: 'custom-shady-div'});
1053 - expectDev(console.error.calls.count()).toBe(1);
1054 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
1055 - 'A component is using shady DOM. Using shady DOM with React can ' +
1056 - 'cause things to break subtly.',
1057 - );
1088 + if (__DEV__) {
1089 + expect(console.error.calls.count()).toBe(1);
1090 + expect(console.error.calls.argsFor(0)[0]).toContain(
1091 + 'A component is using shady DOM. Using shady DOM with React can ' +
1092 + 'cause things to break subtly.',
1093 + );
1094 + }
1095
1096 mountComponent({is: 'custom-shady-div2'});
1060 - expectDev(console.error.calls.count()).toBe(1);
1097 + if (__DEV__) {
1098 + expect(console.error.calls.count()).toBe(1);
1099 + }
1100 } finally {
1101 document.createElement = defaultCreateElement;
1102 }
@@ -1065,7 +1104,7 @@ describe('ReactDOMComponent', () => {
1104
1105 it('should treat menuitem as a void element but still create the closing tag', () => {
1106 // menuitem is not implemented in jsdom, so this triggers the unknown warning error
1068 - spyOn(console, 'error');
1107 + spyOnDev(console, 'error');
1108 var container = document.createElement('div');
1109
1110 var returnedValue = ReactDOMServer.renderToString(
@@ -1098,21 +1137,25 @@ describe('ReactDOMComponent', () => {
1137 });
1138
1139 it('should validate against use of innerHTML', () => {
1101 - spyOn(console, 'error');
1140 + spyOnDev(console, 'error');
1141 mountComponent({innerHTML: '<span>Hi Jim!</span>'});
1103 - expectDev(console.error.calls.count()).toBe(1);
1104 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
1105 - 'Directly setting property `innerHTML` is not permitted. ',
1106 - );
1142 + if (__DEV__) {
1143 + expect(console.error.calls.count()).toBe(1);
1144 + expect(console.error.calls.argsFor(0)[0]).toContain(
1145 + 'Directly setting property `innerHTML` is not permitted. ',
1146 + );
1147 + }
1148 });
1149
1150 it('should validate against use of innerHTML without case sensitivity', () => {
1110 - spyOn(console, 'error');
1151 + spyOnDev(console, 'error');
1152 mountComponent({innerhtml: '<span>Hi Jim!</span>'});
1112 - expectDev(console.error.calls.count()).toBe(1);
1113 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
1114 - 'Directly setting property `innerHTML` is not permitted. ',
1115 - );
1153 + if (__DEV__) {
1154 + expect(console.error.calls.count()).toBe(1);
1155 + expect(console.error.calls.argsFor(0)[0]).toContain(
1156 + 'Directly setting property `innerHTML` is not permitted. ',
1157 + );
1158 + }
1159 });
1160
1161 it('should validate use of dangerouslySetInnerHTML', () => {
@@ -1140,25 +1183,25 @@ describe('ReactDOMComponent', () => {
1183 });
1184
1185 it('should warn about contentEditable and children', () => {
1143 - spyOn(console, 'error');
1186 + spyOnDev(console, 'error');
1187 mountComponent({contentEditable: true, children: ''});
1145 - expectDev(console.error.calls.count()).toBe(1);
1146 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1147 - 'Warning: A component is `contentEditable` and contains `children` ' +
1148 - 'managed by React. It is now your responsibility to guarantee that ' +
1149 - 'none of those nodes are unexpectedly modified or duplicated. This ' +
1150 - 'is probably not intentional.\n in div (at **)',
1151 - );
1188 + if (__DEV__) {
1189 + expect(console.error.calls.count()).toBe(1);
1190 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1191 + 'Warning: A component is `contentEditable` and contains `children` ' +
1192 + 'managed by React. It is now your responsibility to guarantee that ' +
1193 + 'none of those nodes are unexpectedly modified or duplicated. This ' +
1194 + 'is probably not intentional.\n in div (at **)',
1195 + );
1196 + }
1197 });
1198
1199 it('should respect suppressContentEditableWarning', () => {
1155 - spyOn(console, 'error');
1200 mountComponent({
1201 contentEditable: true,
1202 children: '',
1203 suppressContentEditableWarning: true,
1204 });
1161 - expectDev(console.error.calls.count()).toBe(0);
1205 });
1206
1207 it('should validate against invalid styles', () => {
@@ -1190,8 +1233,7 @@ describe('ReactDOMComponent', () => {
1233 expect(normalizeCodeLocInfo(caughtErr.message)).toContain(
1234 'input is a void element tag and must neither have `children` ' +
1235 'nor use `dangerouslySetInnerHTML`.' +
1193 - '\n in input (at **)' +
1194 - '\n in X (at **)',
1236 + (__DEV__ ? '\n in input (at **)' + '\n in X (at **)' : ''),
1237 );
1238 });
1239
@@ -1227,9 +1269,11 @@ describe('ReactDOMComponent', () => {
1269 container.getElementsByTagName('image')[0].dispatchEvent(errorEvent);
1270 container.getElementsByTagName('image')[0].dispatchEvent(loadEvent);
1271
1230 - expectDev(console.log.calls.count()).toBe(2);
1231 - expectDev(console.log.calls.argsFor(0)[0]).toContain('onError called');
1232 - expectDev(console.log.calls.argsFor(1)[0]).toContain('onLoad called');
1272 + if (__DEV__) {
1273 + expect(console.log.calls.count()).toBe(2);
1274 + expect(console.log.calls.argsFor(0)[0]).toContain('onError called');
1275 + expect(console.log.calls.argsFor(1)[0]).toContain('onLoad called');
1276 + }
1277 });
1278 });
1279
@@ -1279,15 +1323,17 @@ describe('ReactDOMComponent', () => {
1323 });
1324
1325 it('should warn about contentEditable and children', () => {
1282 - spyOn(console, 'error');
1326 + spyOnDev(console, 'error');
1327 ReactDOM.render(
1328 <div contentEditable={true}>
1329 <div />
1330 </div>,
1331 container,
1332 );
1289 - expectDev(console.error.calls.count()).toBe(1);
1290 - expectDev(console.error.calls.argsFor(0)[0]).toContain('contentEditable');
1333 + if (__DEV__) {
1334 + expect(console.error.calls.count()).toBe(1);
1335 + expect(console.error.calls.argsFor(0)[0]).toContain('contentEditable');
1336 + }
1337 });
1338
1339 it('should validate against invalid styles', () => {
@@ -1321,8 +1367,7 @@ describe('ReactDOMComponent', () => {
1367 'The `style` prop expects a mapping from style properties to values, ' +
1368 "not a string. For example, style={{marginRight: spacing + 'em'}} " +
1369 'when using JSX.' +
1324 - '\n in div (at **)' +
1325 - '\n in Animal (at **)',
1370 + (__DEV__ ? '\n in div (at **)' + '\n in Animal (at **)' : ''),
1371 );
1372 });
1373
@@ -1400,7 +1445,7 @@ describe('ReactDOMComponent', () => {
1445
1446 describe('nesting validation', () => {
1447 it('warns on invalid nesting', () => {
1403 - spyOn(console, 'error');
1448 + spyOnDev(console, 'error');
1449 ReactTestUtils.renderIntoDocument(
1450 <div>
1451 <tr />
@@ -1408,23 +1453,25 @@ describe('ReactDOMComponent', () => {
1453 </div>,
1454 );
1455
1411 - expectDev(console.error.calls.count()).toBe(2);
1412 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1413 - 'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
1414 - '<div>.' +
1415 - '\n in tr (at **)' +
1416 - '\n in div (at **)',
1417 - );
1418 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
1419 - 'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
1420 - '<div>.' +
1421 - '\n in tr (at **)' +
1422 - '\n in div (at **)',
1423 - );
1456 + if (__DEV__) {
1457 + expect(console.error.calls.count()).toBe(2);
1458 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1459 + 'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
1460 + '<div>.' +
1461 + '\n in tr (at **)' +
1462 + '\n in div (at **)',
1463 + );
1464 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
1465 + 'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
1466 + '<div>.' +
1467 + '\n in tr (at **)' +
1468 + '\n in div (at **)',
1469 + );
1470 + }
1471 });
1472
1473 it('warns on invalid nesting at root', () => {
1427 - spyOn(console, 'error');
1474 + spyOnDev(console, 'error');
1475 var p = document.createElement('p');
1476 ReactDOM.render(
1477 <span>
@@ -1433,18 +1480,20 @@ describe('ReactDOMComponent', () => {
1480 p,
1481 );
1482
1436 - expectDev(console.error.calls.count()).toBe(1);
1437 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1438 - 'Warning: validateDOMNesting(...): <p> cannot appear as a descendant ' +
1439 - 'of <p>.' +
1440 - // There is no outer `p` here because root container is not part of the stack.
1441 - '\n in p (at **)' +
1442 - '\n in span (at **)',
1443 - );
1483 + if (__DEV__) {
1484 + expect(console.error.calls.count()).toBe(1);
1485 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1486 + 'Warning: validateDOMNesting(...): <p> cannot appear as a descendant ' +
1487 + 'of <p>.' +
1488 + // There is no outer `p` here because root container is not part of the stack.
1489 + '\n in p (at **)' +
1490 + '\n in span (at **)',
1491 + );
1492 + }
1493 });
1494
1495 it('warns nicely for table rows', () => {
1447 - spyOn(console, 'error');
1496 + spyOnDev(console, 'error');
1497
1498 class Row extends React.Component {
1499 render() {
@@ -1463,38 +1512,40 @@ describe('ReactDOMComponent', () => {
1512 }
1513
1514 ReactTestUtils.renderIntoDocument(<Foo />);
1466 - expectDev(console.error.calls.count()).toBe(3);
1467 -
1468 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1469 - 'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
1470 - '<table>. Add a <tbody> to your code to match the DOM tree generated ' +
1471 - 'by the browser.' +
1472 - '\n in tr (at **)' +
1473 - '\n in Row (at **)' +
1474 - '\n in table (at **)' +
1475 - '\n in Foo (at **)',
1476 - );
1515 + if (__DEV__) {
1516 + expect(console.error.calls.count()).toBe(3);
1517 +
1518 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1519 + 'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
1520 + '<table>. Add a <tbody> to your code to match the DOM tree generated ' +
1521 + 'by the browser.' +
1522 + '\n in tr (at **)' +
1523 + '\n in Row (at **)' +
1524 + '\n in table (at **)' +
1525 + '\n in Foo (at **)',
1526 + );
1527
1478 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
1479 - 'Warning: validateDOMNesting(...): Text nodes cannot appear as a ' +
1480 - 'child of <tr>.' +
1481 - '\n in tr (at **)' +
1482 - '\n in Row (at **)' +
1483 - '\n in table (at **)' +
1484 - '\n in Foo (at **)',
1485 - );
1528 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
1529 + 'Warning: validateDOMNesting(...): Text nodes cannot appear as a ' +
1530 + 'child of <tr>.' +
1531 + '\n in tr (at **)' +
1532 + '\n in Row (at **)' +
1533 + '\n in table (at **)' +
1534 + '\n in Foo (at **)',
1535 + );
1536
1487 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
1488 - 'Warning: validateDOMNesting(...): Whitespace text nodes cannot ' +
1489 - "appear as a child of <table>. Make sure you don't have any extra " +
1490 - 'whitespace between tags on each line of your source code.' +
1491 - '\n in table (at **)' +
1492 - '\n in Foo (at **)',
1493 - );
1537 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
1538 + 'Warning: validateDOMNesting(...): Whitespace text nodes cannot ' +
1539 + "appear as a child of <table>. Make sure you don't have any extra " +
1540 + 'whitespace between tags on each line of your source code.' +
1541 + '\n in table (at **)' +
1542 + '\n in Foo (at **)',
1543 + );
1544 + }
1545 });
1546
1547 it('gives useful context in warnings', () => {
1497 - spyOn(console, 'error');
1548 + spyOnDev(console, 'error');
1549 function Row() {
1550 return <tr />;
1551 }
@@ -1525,16 +1576,18 @@ describe('ReactDOMComponent', () => {
1576 return <Viz1 />;
1577 }
1578 ReactTestUtils.renderIntoDocument(<App1 />);
1528 - expectDev(console.error.calls.count()).toBe(1);
1529 - expectDev(
1530 - normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
1531 - ).toContain(
1532 - '\n in tr (at **)' +
1533 - '\n in Row (at **)' +
1534 - '\n in FancyRow (at **)' +
1535 - '\n in table (at **)' +
1536 - '\n in Viz1 (at **)',
1537 - );
1579 + if (__DEV__) {
1580 + expect(console.error.calls.count()).toBe(1);
1581 + expect(
1582 + normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
1583 + ).toContain(
1584 + '\n in tr (at **)' +
1585 + '\n in Row (at **)' +
1586 + '\n in FancyRow (at **)' +
1587 + '\n in table (at **)' +
1588 + '\n in Viz1 (at **)',
1589 + );
1590 + }
1591
1592 function Viz2() {
1593 return (
@@ -1547,65 +1600,73 @@ describe('ReactDOMComponent', () => {
1600 return <Viz2 />;
1601 }
1602 ReactTestUtils.renderIntoDocument(<App2 />);
1550 - expectDev(console.error.calls.count()).toBe(2);
1551 - expectDev(
1552 - normalizeCodeLocInfo(console.error.calls.argsFor(1)[0]),
1553 - ).toContain(
1554 - '\n in tr (at **)' +
1555 - '\n in Row (at **)' +
1556 - '\n in FancyRow (at **)' +
1557 - '\n in table (at **)' +
1558 - '\n in Table (at **)' +
1559 - '\n in FancyTable (at **)' +
1560 - '\n in Viz2 (at **)',
1561 - );
1603 + if (__DEV__) {
1604 + expect(console.error.calls.count()).toBe(2);
1605 + expect(
1606 + normalizeCodeLocInfo(console.error.calls.argsFor(1)[0]),
1607 + ).toContain(
1608 + '\n in tr (at **)' +
1609 + '\n in Row (at **)' +
1610 + '\n in FancyRow (at **)' +
1611 + '\n in table (at **)' +
1612 + '\n in Table (at **)' +
1613 + '\n in FancyTable (at **)' +
1614 + '\n in Viz2 (at **)',
1615 + );
1616 + }
1617
1618 ReactTestUtils.renderIntoDocument(
1619 <FancyTable>
1620 <FancyRow />
1621 </FancyTable>,
1622 );
1568 - expectDev(console.error.calls.count()).toBe(3);
1569 - expectDev(
1570 - normalizeCodeLocInfo(console.error.calls.argsFor(2)[0]),
1571 - ).toContain(
1572 - '\n in tr (at **)' +
1573 - '\n in Row (at **)' +
1574 - '\n in FancyRow (at **)' +
1575 - '\n in table (at **)' +
1576 - '\n in Table (at **)' +
1577 - '\n in FancyTable (at **)',
1578 - );
1623 + if (__DEV__) {
1624 + expect(console.error.calls.count()).toBe(3);
1625 + expect(
1626 + normalizeCodeLocInfo(console.error.calls.argsFor(2)[0]),
1627 + ).toContain(
1628 + '\n in tr (at **)' +
1629 + '\n in Row (at **)' +
1630 + '\n in FancyRow (at **)' +
1631 + '\n in table (at **)' +
1632 + '\n in Table (at **)' +
1633 + '\n in FancyTable (at **)',
1634 + );
1635 + }
1636
1637 ReactTestUtils.renderIntoDocument(
1638 <table>
1639 <FancyRow />
1640 </table>,
1641 );
1585 - expectDev(console.error.calls.count()).toBe(4);
1586 - expectDev(
1587 - normalizeCodeLocInfo(console.error.calls.argsFor(3)[0]),
1588 - ).toContain(
1589 - '\n in tr (at **)' +
1590 - '\n in Row (at **)' +
1591 - '\n in FancyRow (at **)' +
1592 - '\n in table (at **)',
1593 - );
1642 + if (__DEV__) {
1643 + expect(console.error.calls.count()).toBe(4);
1644 + expect(
1645 + normalizeCodeLocInfo(console.error.calls.argsFor(3)[0]),
1646 + ).toContain(
1647 + '\n in tr (at **)' +
1648 + '\n in Row (at **)' +
1649 + '\n in FancyRow (at **)' +
1650 + '\n in table (at **)',
1651 + );
1652 + }
1653
1654 ReactTestUtils.renderIntoDocument(
1655 <FancyTable>
1656 <tr />
1657 </FancyTable>,
1658 );
1600 - expectDev(console.error.calls.count()).toBe(5);
1601 - expectDev(
1602 - normalizeCodeLocInfo(console.error.calls.argsFor(4)[0]),
1603 - ).toContain(
1604 - '\n in tr (at **)' +
1605 - '\n in table (at **)' +
1606 - '\n in Table (at **)' +
1607 - '\n in FancyTable (at **)',
1608 - );
1659 + if (__DEV__) {
1660 + expect(console.error.calls.count()).toBe(5);
1661 + expect(
1662 + normalizeCodeLocInfo(console.error.calls.argsFor(4)[0]),
1663 + ).toContain(
1664 + '\n in tr (at **)' +
1665 + '\n in table (at **)' +
1666 + '\n in Table (at **)' +
1667 + '\n in FancyTable (at **)',
1668 + );
1669 + }
1670
1671 class Link extends React.Component {
1672 render() {
@@ -1620,172 +1681,218 @@ describe('ReactDOMComponent', () => {
1681 </div>
1682 </Link>,
1683 );
1623 - expectDev(console.error.calls.count()).toBe(6);
1624 - expectDev(
1625 - normalizeCodeLocInfo(console.error.calls.argsFor(5)[0]),
1626 - ).toContain(
1627 - '\n in a (at **)' +
1628 - '\n in Link (at **)' +
1629 - '\n in div (at **)' +
1684 + if (__DEV__) {
1685 + expect(console.error.calls.count()).toBe(6);
1686 + expect(
1687 + normalizeCodeLocInfo(console.error.calls.argsFor(5)[0]),
1688 + ).toContain(
1689 '\n in a (at **)' +
1631 - '\n in Link (at **)',
1632 - );
1690 + '\n in Link (at **)' +
1691 + '\n in div (at **)' +
1692 + '\n in a (at **)' +
1693 + '\n in Link (at **)',
1694 + );
1695 + }
1696 });
1697
1698 it('should warn about incorrect casing on properties (ssr)', () => {
1636 - spyOn(console, 'error');
1699 + spyOnDev(console, 'error');
1700 ReactDOMServer.renderToString(
1701 React.createElement('input', {type: 'text', tabindex: '1'}),
1702 );
1640 - expectDev(console.error.calls.count()).toBe(1);
1641 - expectDev(console.error.calls.argsFor(0)[0]).toContain('tabIndex');
1703 + if (__DEV__) {
1704 + expect(console.error.calls.count()).toBe(1);
1705 + expect(console.error.calls.argsFor(0)[0]).toContain('tabIndex');
1706 + }
1707 });
1708
1709 it('should warn about incorrect casing on event handlers (ssr)', () => {
1645 - spyOn(console, 'error');
1710 + spyOnDev(console, 'error');
1711 ReactDOMServer.renderToString(
1712 React.createElement('input', {type: 'text', onclick: '1'}),
1713 );
1714 ReactDOMServer.renderToString(
1715 React.createElement('input', {type: 'text', onKeydown: '1'}),
1716 );
1652 - expectDev(console.error.calls.count()).toBe(2);
1653 - expectDev(console.error.calls.argsFor(0)[0]).toContain('onClick');
1654 - expectDev(console.error.calls.argsFor(1)[0]).toContain('onKeyDown');
1717 + if (__DEV__) {
1718 + expect(console.error.calls.count()).toBe(2);
1719 + expect(console.error.calls.argsFor(0)[0]).toContain('onClick');
1720 + expect(console.error.calls.argsFor(1)[0]).toContain('onKeyDown');
1721 + }
1722 });
1723
1724 it('should warn about incorrect casing on properties', () => {
1658 - spyOn(console, 'error');
1725 + spyOnDev(console, 'error');
1726 ReactTestUtils.renderIntoDocument(
1727 React.createElement('input', {type: 'text', tabindex: '1'}),
1728 );
1662 - expectDev(console.error.calls.count()).toBe(1);
1663 - expectDev(console.error.calls.argsFor(0)[0]).toContain('tabIndex');
1729 + if (__DEV__) {
1730 + expect(console.error.calls.count()).toBe(1);
1731 + expect(console.error.calls.argsFor(0)[0]).toContain('tabIndex');
1732 + }
1733 });
1734
1735 it('should warn about incorrect casing on event handlers', () => {
1667 - spyOn(console, 'error');
1736 + spyOnDev(console, 'error');
1737 ReactTestUtils.renderIntoDocument(
1738 React.createElement('input', {type: 'text', onclick: '1'}),
1739 );
1740 ReactTestUtils.renderIntoDocument(
1741 React.createElement('input', {type: 'text', onKeydown: '1'}),
1742 );
1674 - expectDev(console.error.calls.count()).toBe(2);
1675 - expectDev(console.error.calls.argsFor(0)[0]).toContain('onClick');
1676 - expectDev(console.error.calls.argsFor(1)[0]).toContain('onKeyDown');
1743 + if (__DEV__) {
1744 + expect(console.error.calls.count()).toBe(2);
1745 + expect(console.error.calls.argsFor(0)[0]).toContain('onClick');
1746 + expect(console.error.calls.argsFor(1)[0]).toContain('onKeyDown');
1747 + }
1748 });
1749
1750 it('should warn about class', () => {
1680 - spyOn(console, 'error');
1751 + spyOnDev(console, 'error');
1752 ReactTestUtils.renderIntoDocument(
1753 React.createElement('div', {class: 'muffins'}),
1754 );
1684 - expectDev(console.error.calls.count()).toBe(1);
1685 - expectDev(console.error.calls.argsFor(0)[0]).toContain('className');
1755 + if (__DEV__) {
1756 + expect(console.error.calls.count()).toBe(1);
1757 + expect(console.error.calls.argsFor(0)[0]).toContain('className');
1758 + }
1759 });
1760
1761 it('should warn about class (ssr)', () => {
1689 - spyOn(console, 'error');
1762 + spyOnDev(console, 'error');
1763 ReactDOMServer.renderToString(
1764 React.createElement('div', {class: 'muffins'}),
1765 );
1693 - expectDev(console.error.calls.count()).toBe(1);
1694 - expectDev(console.error.calls.argsFor(0)[0]).toContain('className');
1766 + if (__DEV__) {
1767 + expect(console.error.calls.count()).toBe(1);
1768 + expect(console.error.calls.argsFor(0)[0]).toContain('className');
1769 + }
1770 });
1771
1772 it('should warn about props that are no longer supported', () => {
1698 - spyOn(console, 'error');
1773 + spyOnDev(console, 'error');
1774 ReactTestUtils.renderIntoDocument(<div />);
1700 - expectDev(console.error.calls.count()).toBe(0);
1775 + if (__DEV__) {
1776 + expect(console.error.calls.count()).toBe(0);
1777 + }
1778
1779 ReactTestUtils.renderIntoDocument(<div onFocusIn={() => {}} />);
1703 - expectDev(console.error.calls.count()).toBe(1);
1780 + if (__DEV__) {
1781 + expect(console.error.calls.count()).toBe(1);
1782 + }
1783
1784 ReactTestUtils.renderIntoDocument(<div onFocusOut={() => {}} />);
1706 - expectDev(console.error.calls.count()).toBe(2);
1785 + if (__DEV__) {
1786 + expect(console.error.calls.count()).toBe(2);
1787 + }
1788 });
1789
1790 it('should warn about props that are no longer supported without case sensitivity', () => {
1710 - spyOn(console, 'error');
1791 + spyOnDev(console, 'error');
1792 ReactTestUtils.renderIntoDocument(<div />);
1712 - expectDev(console.error.calls.count()).toBe(0);
1793 + if (__DEV__) {
1794 + expect(console.error.calls.count()).toBe(0);
1795 + }
1796
1797 ReactTestUtils.renderIntoDocument(<div onfocusin={() => {}} />);
1715 - expectDev(console.error.calls.count()).toBe(1);
1798 + if (__DEV__) {
1799 + expect(console.error.calls.count()).toBe(1);
1800 + }
1801
1802 ReactTestUtils.renderIntoDocument(<div onfocusout={() => {}} />);
1718 - expectDev(console.error.calls.count()).toBe(2);
1803 + if (__DEV__) {
1804 + expect(console.error.calls.count()).toBe(2);
1805 + }
1806 });
1807
1808 it('should warn about props that are no longer supported (ssr)', () => {
1722 - spyOn(console, 'error');
1809 + spyOnDev(console, 'error');
1810 ReactDOMServer.renderToString(<div />);
1724 - expectDev(console.error.calls.count()).toBe(0);
1811 + if (__DEV__) {
1812 + expect(console.error.calls.count()).toBe(0);
1813 + }
1814
1815 ReactDOMServer.renderToString(<div onFocusIn={() => {}} />);
1727 - expectDev(console.error.calls.count()).toBe(1);
1816 + if (__DEV__) {
1817 + expect(console.error.calls.count()).toBe(1);
1818 + }
1819
1820 ReactDOMServer.renderToString(<div onFocusOut={() => {}} />);
1730 - expectDev(console.error.calls.count()).toBe(2);
1821 + if (__DEV__) {
1822 + expect(console.error.calls.count()).toBe(2);
1823 + }
1824 });
1825
1826 it('should warn about props that are no longer supported without case sensitivity (ssr)', () => {
1734 - spyOn(console, 'error');
1827 + spyOnDev(console, 'error');
1828 ReactDOMServer.renderToString(<div />);
1736 - expectDev(console.error.calls.count()).toBe(0);
1829 + if (__DEV__) {
1830 + expect(console.error.calls.count()).toBe(0);
1831 + }
1832
1833 ReactDOMServer.renderToString(<div onfocusin={() => {}} />);
1739 - expectDev(console.error.calls.count()).toBe(1);
1834 + if (__DEV__) {
1835 + expect(console.error.calls.count()).toBe(1);
1836 + }
1837
1838 ReactDOMServer.renderToString(<div onfocusout={() => {}} />);
1742 - expectDev(console.error.calls.count()).toBe(2);
1839 + if (__DEV__) {
1840 + expect(console.error.calls.count()).toBe(2);
1841 + }
1842 });
1843
1844 it('gives source code refs for unknown prop warning', () => {
1746 - spyOn(console, 'error');
1845 + spyOnDev(console, 'error');
1846 ReactTestUtils.renderIntoDocument(<div class="paladin" />);
1847 ReactTestUtils.renderIntoDocument(<input type="text" onclick="1" />);
1749 - expectDev(console.error.calls.count()).toBe(2);
1750 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1751 - 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
1752 - );
1753 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
1754 - 'Warning: Invalid event handler property `onclick`. Did you mean ' +
1755 - '`onClick`?\n in input (at **)',
1756 - );
1848 + if (__DEV__) {
1849 + expect(console.error.calls.count()).toBe(2);
1850 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1851 + 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
1852 + );
1853 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
1854 + 'Warning: Invalid event handler property `onclick`. Did you mean ' +
1855 + '`onClick`?\n in input (at **)',
1856 + );
1857 + }
1858 });
1859
1860 it('gives source code refs for unknown prop warning (ssr)', () => {
1760 - spyOn(console, 'error');
1861 + spyOnDev(console, 'error');
1862 ReactDOMServer.renderToString(<div class="paladin" />);
1863 ReactDOMServer.renderToString(<input type="text" onclick="1" />);
1763 - expectDev(console.error.calls.count()).toBe(2);
1764 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1765 - 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
1766 - );
1767 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
1768 - 'Warning: Invalid event handler property `onclick`. Did you mean ' +
1769 - '`onClick`?\n in input (at **)',
1770 - );
1864 + if (__DEV__) {
1865 + expect(console.error.calls.count()).toBe(2);
1866 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1867 + 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
1868 + );
1869 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
1870 + 'Warning: Invalid event handler property `onclick`. Did you mean ' +
1871 + '`onClick`?\n in input (at **)',
1872 + );
1873 + }
1874 });
1875
1876 it('gives source code refs for unknown prop warning for update render', () => {
1774 - spyOn(console, 'error');
1877 + spyOnDev(console, 'error');
1878 var container = document.createElement('div');
1879
1880 ReactTestUtils.renderIntoDocument(<div className="paladin" />, container);
1778 - expectDev(console.error.calls.count()).toBe(0);
1881 + if (__DEV__) {
1882 + expect(console.error.calls.count()).toBe(0);
1883 + }
1884
1885 ReactTestUtils.renderIntoDocument(<div class="paladin" />, container);
1781 - expectDev(console.error.calls.count()).toBe(1);
1782 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1783 - 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
1784 - );
1886 + if (__DEV__) {
1887 + expect(console.error.calls.count()).toBe(1);
1888 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1889 + 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
1890 + );
1891 + }
1892 });
1893
1894 it('gives source code refs for unknown prop warning for exact elements', () => {
1788 - spyOn(console, 'error');
1895 + spyOnDev(console, 'error');
1896
1897 ReactTestUtils.renderIntoDocument(
1898 <div className="foo1">
@@ -1797,23 +1904,27 @@ describe('ReactDOMComponent', () => {
1904 </div>,
1905 );
1906
1800 - expectDev(console.error.calls.count()).toBe(2);
1907 + if (__DEV__) {
1908 + expect(console.error.calls.count()).toBe(2);
1909
1802 - expectDev(console.error.calls.argsFor(0)[0]).toContain('className');
1803 - var matches = console.error.calls.argsFor(0)[0].match(/.*\(.*:(\d+)\).*/);
1804 - var previousLine = matches[1];
1910 + expect(console.error.calls.argsFor(0)[0]).toContain('className');
1911 + var matches = console.error.calls
1912 + .argsFor(0)[0]
1913 + .match(/.*\(.*:(\d+)\).*/);
1914 + var previousLine = matches[1];
1915
1806 - expectDev(console.error.calls.argsFor(1)[0]).toContain('onClick');
1807 - matches = console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/);
1808 - var currentLine = matches[1];
1916 + expect(console.error.calls.argsFor(1)[0]).toContain('onClick');
1917 + matches = console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/);
1918 + var currentLine = matches[1];
1919
1810 - //verify line number has a proper relative difference,
1811 - //since hard coding the line number would make test too brittle
1812 - expect(parseInt(previousLine, 10) + 2).toBe(parseInt(currentLine, 10));
1920 + //verify line number has a proper relative difference,
1921 + //since hard coding the line number would make test too brittle
1922 + expect(parseInt(previousLine, 10) + 2).toBe(parseInt(currentLine, 10));
1923 + }
1924 });
1925
1926 it('gives source code refs for unknown prop warning for exact elements (ssr)', () => {
1816 - spyOn(console, 'error');
1927 + spyOnDev(console, 'error');
1928
1929 ReactDOMServer.renderToString(
1930 <div className="foo1">
@@ -1825,24 +1936,28 @@ describe('ReactDOMComponent', () => {
1936 </div>,
1937 );
1938
1828 - expectDev(console.error.calls.count()).toBe(2);
1939 + if (__DEV__) {
1940 + expect(console.error.calls.count()).toBe(2);
1941
1830 - expectDev(console.error.calls.argsFor(0)[0]).toContain('className');
1831 - var matches = console.error.calls.argsFor(0)[0].match(/.*\(.*:(\d+)\).*/);
1832 - var previousLine = (matches || [])[1];
1942 + expect(console.error.calls.argsFor(0)[0]).toContain('className');
1943 + var matches = console.error.calls
1944 + .argsFor(0)[0]
1945 + .match(/.*\(.*:(\d+)\).*/);
1946 + var previousLine = (matches || [])[1];
1947
1834 - expectDev(console.error.calls.argsFor(1)[0]).toContain('onClick');
1835 - matches =
1836 - console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/) || {};
1837 - var currentLine = (matches || [])[1];
1948 + expect(console.error.calls.argsFor(1)[0]).toContain('onClick');
1949 + matches =
1950 + console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/) || {};
1951 + var currentLine = (matches || [])[1];
1952
1839 - //verify line number has a proper relative difference,
1840 - //since hard coding the line number would make test too brittle
1841 - expectDev(parseInt(previousLine, 10) + 2).toBe(parseInt(currentLine, 10));
1953 + //verify line number has a proper relative difference,
1954 + //since hard coding the line number would make test too brittle
1955 + expect(parseInt(previousLine, 10) + 2).toBe(parseInt(currentLine, 10));
1956 + }
1957 });
1958
1959 it('gives source code refs for unknown prop warning for exact elements in composition', () => {
1845 - spyOn(console, 'error');
1960 + spyOnDev(console, 'error');
1961 var container = document.createElement('div');
1962
1963 class Parent extends React.Component {
@@ -1884,23 +1999,27 @@ describe('ReactDOMComponent', () => {
1999
2000 ReactTestUtils.renderIntoDocument(<Parent />, container);
2001
1887 - expectDev(console.error.calls.count()).toBe(2);
2002 + if (__DEV__) {
2003 + expect(console.error.calls.count()).toBe(2);
2004
1889 - expectDev(console.error.calls.argsFor(0)[0]).toContain('className');
1890 - var matches = console.error.calls.argsFor(0)[0].match(/.*\(.*:(\d+)\).*/);
1891 - var previousLine = (matches || [])[1];
2005 + expect(console.error.calls.argsFor(0)[0]).toContain('className');
2006 + var matches = console.error.calls
2007 + .argsFor(0)[0]
2008 + .match(/.*\(.*:(\d+)\).*/);
2009 + var previousLine = (matches || [])[1];
2010
1893 - expectDev(console.error.calls.argsFor(1)[0]).toContain('onClick');
1894 - matches = console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/);
1895 - var currentLine = (matches || [])[1];
2011 + expect(console.error.calls.argsFor(1)[0]).toContain('onClick');
2012 + matches = console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/);
2013 + var currentLine = (matches || [])[1];
2014
1897 - //verify line number has a proper relative difference,
1898 - //since hard coding the line number would make test too brittle
1899 - expect(parseInt(previousLine, 10) + 12).toBe(parseInt(currentLine, 10));
2015 + //verify line number has a proper relative difference,
2016 + //since hard coding the line number would make test too brittle
2017 + expect(parseInt(previousLine, 10) + 12).toBe(parseInt(currentLine, 10));
2018 + }
2019 });
2020
2021 it('gives source code refs for unknown prop warning for exact elements in composition (ssr)', () => {
1903 - spyOn(console, 'error');
2022 + spyOnDev(console, 'error');
2023 var container = document.createElement('div');
2024
2025 class Parent extends React.Component {
@@ -1942,25 +2061,27 @@ describe('ReactDOMComponent', () => {
2061
2062 ReactDOMServer.renderToString(<Parent />, container);
2063
1945 - expectDev(console.error.calls.count()).toBe(2);
2064 + if (__DEV__) {
2065 + expect(console.error.calls.count()).toBe(2);
2066
1947 - expectDev(console.error.calls.argsFor(0)[0]).toContain('className');
1948 - var matches = console.error.calls.argsFor(0)[0].match(/.*\(.*:(\d+)\).*/);
1949 - var previousLine = (matches || [])[1];
2067 + expect(console.error.calls.argsFor(0)[0]).toContain('className');
2068 + var matches = console.error.calls
2069 + .argsFor(0)[0]
2070 + .match(/.*\(.*:(\d+)\).*/);
2071 + var previousLine = (matches || [])[1];
2072
1951 - expectDev(console.error.calls.argsFor(1)[0]).toContain('onClick');
1952 - matches = console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/);
1953 - var currentLine = (matches || [])[1];
2073 + expect(console.error.calls.argsFor(1)[0]).toContain('onClick');
2074 + matches = console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/);
2075 + var currentLine = (matches || [])[1];
2076
1955 - //verify line number has a proper relative difference,
1956 - //since hard coding the line number would make test too brittle
1957 - expectDev(parseInt(previousLine, 10) + 12).toBe(
1958 - parseInt(currentLine, 10),
1959 - );
2077 + //verify line number has a proper relative difference,
2078 + //since hard coding the line number would make test too brittle
2079 + expect(parseInt(previousLine, 10) + 12).toBe(parseInt(currentLine, 10));
2080 + }
2081 });
2082
2083 it('should suggest property name if available', () => {
1963 - spyOn(console, 'error');
2084 + spyOnDev(console, 'error');
2085
2086 ReactTestUtils.renderIntoDocument(
2087 React.createElement('label', {for: 'test'}),
@@ -1969,19 +2090,21 @@ describe('ReactDOMComponent', () => {
2090 React.createElement('input', {type: 'text', autofocus: true}),
2091 );
2092
1972 - expectDev(console.error.calls.count()).toBe(2);
2093 + if (__DEV__) {
2094 + expect(console.error.calls.count()).toBe(2);
2095
1974 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
1975 - 'Warning: Invalid DOM property `for`. Did you mean `htmlFor`?\n in label',
1976 - );
2096 + expect(console.error.calls.argsFor(0)[0]).toBe(
2097 + 'Warning: Invalid DOM property `for`. Did you mean `htmlFor`?\n in label',
2098 + );
2099
1978 - expectDev(console.error.calls.argsFor(1)[0]).toBe(
1979 - 'Warning: Invalid DOM property `autofocus`. Did you mean `autoFocus`?\n in input',
1980 - );
2100 + expect(console.error.calls.argsFor(1)[0]).toBe(
2101 + 'Warning: Invalid DOM property `autofocus`. Did you mean `autoFocus`?\n in input',
2102 + );
2103 + }
2104 });
2105
2106 it('should suggest property name if available (ssr)', () => {
1984 - spyOn(console, 'error');
2107 + spyOnDev(console, 'error');
2108
2109 ReactDOMServer.renderToString(
2110 React.createElement('label', {for: 'test'}),
@@ -1990,15 +2113,17 @@ describe('ReactDOMComponent', () => {
2113 React.createElement('input', {type: 'text', autofocus: true}),
2114 );
2115
1993 - expectDev(console.error.calls.count()).toBe(2);
2116 + if (__DEV__) {
2117 + expect(console.error.calls.count()).toBe(2);
2118
1995 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
1996 - 'Warning: Invalid DOM property `for`. Did you mean `htmlFor`?\n in label',
1997 - );
2119 + expect(console.error.calls.argsFor(0)[0]).toBe(
2120 + 'Warning: Invalid DOM property `for`. Did you mean `htmlFor`?\n in label',
2121 + );
2122
1999 - expectDev(console.error.calls.argsFor(1)[0]).toBe(
2000 - 'Warning: Invalid DOM property `autofocus`. Did you mean `autoFocus`?\n in input',
2001 - );
2123 + expect(console.error.calls.argsFor(1)[0]).toBe(
2124 + 'Warning: Invalid DOM property `autofocus`. Did you mean `autoFocus`?\n in input',
2125 + );
2126 + }
2127 });
2128 });
2129
@@ -2028,31 +2153,35 @@ describe('ReactDOMComponent', () => {
2153
2154 describe('Attributes with aliases', function() {
2155 it('sets aliased attributes on HTML attributes', function() {
2031 - spyOn(console, 'error');
2156 + spyOnDev(console, 'error');
2157
2158 var el = ReactTestUtils.renderIntoDocument(<div class="test" />);
2159
2160 expect(el.className).toBe('test');
2161
2037 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
2038 - 'Warning: Invalid DOM property `class`. Did you mean `className`?',
2039 - );
2162 + if (__DEV__) {
2163 + expect(console.error.calls.argsFor(0)[0]).toContain(
2164 + 'Warning: Invalid DOM property `class`. Did you mean `className`?',
2165 + );
2166 + }
2167 });
2168
2169 it('sets incorrectly cased aliased attributes on HTML attributes with a warning', function() {
2043 - spyOn(console, 'error');
2170 + spyOnDev(console, 'error');
2171
2172 var el = ReactTestUtils.renderIntoDocument(<div cLASS="test" />);
2173
2174 expect(el.className).toBe('test');
2175
2049 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
2050 - 'Warning: Invalid DOM property `cLASS`. Did you mean `className`?',
2051 - );
2176 + if (__DEV__) {
2177 + expect(console.error.calls.argsFor(0)[0]).toContain(
2178 + 'Warning: Invalid DOM property `cLASS`. Did you mean `className`?',
2179 + );
2180 + }
2181 });
2182
2183 it('sets aliased attributes on SVG elements with a warning', function() {
2055 - spyOn(console, 'error');
2184 + spyOnDev(console, 'error');
2185
2186 var el = ReactTestUtils.renderIntoDocument(
2187 <svg>
@@ -2063,9 +2192,11 @@ describe('ReactDOMComponent', () => {
2192
2193 expect(text.hasAttribute('arabic-form')).toBe(true);
2194
2066 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
2067 - 'Warning: Invalid DOM property `arabic-form`. Did you mean `arabicForm`?',
2068 - );
2195 + if (__DEV__) {
2196 + expect(console.error.calls.argsFor(0)[0]).toContain(
2197 + 'Warning: Invalid DOM property `arabic-form`. Did you mean `arabicForm`?',
2198 + );
2199 + }
2200 });
2201
2202 it('sets aliased attributes on custom elements', function() {
@@ -2112,32 +2243,36 @@ describe('ReactDOMComponent', () => {
2243 });
2244
2245 it('does not assign a boolean custom attributes as a string', function() {
2115 - spyOn(console, 'error');
2246 + spyOnDev(console, 'error');
2247
2248 var el = ReactTestUtils.renderIntoDocument(<div whatever={true} />);
2249
2250 expect(el.hasAttribute('whatever')).toBe(false);
2251
2121 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
2122 - 'Received `true` for a non-boolean attribute `whatever`.\n\n' +
2123 - 'If you want to write it to the DOM, pass a string instead: ' +
2124 - 'whatever="true" or whatever={value.toString()}.',
2125 - );
2252 + if (__DEV__) {
2253 + expect(console.error.calls.argsFor(0)[0]).toContain(
2254 + 'Received `true` for a non-boolean attribute `whatever`.\n\n' +
2255 + 'If you want to write it to the DOM, pass a string instead: ' +
2256 + 'whatever="true" or whatever={value.toString()}.',
2257 + );
2258 + }
2259 });
2260
2261 it('does not assign an implicit boolean custom attributes', function() {
2129 - spyOn(console, 'error');
2262 + spyOnDev(console, 'error');
2263
2264 // eslint-disable-next-line react/jsx-boolean-value
2265 var el = ReactTestUtils.renderIntoDocument(<div whatever />);
2266
2267 expect(el.hasAttribute('whatever')).toBe(false);
2268
2136 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
2137 - 'Received `true` for a non-boolean attribute `whatever`.\n\n' +
2138 - 'If you want to write it to the DOM, pass a string instead: ' +
2139 - 'whatever="true" or whatever={value.toString()}.',
2140 - );
2269 + if (__DEV__) {
2270 + expect(console.error.calls.argsFor(0)[0]).toContain(
2271 + 'Received `true` for a non-boolean attribute `whatever`.\n\n' +
2272 + 'If you want to write it to the DOM, pass a string instead: ' +
2273 + 'whatever="true" or whatever={value.toString()}.',
2274 + );
2275 + }
2276 });
2277
2278 it('assigns a numeric custom attributes as a string', function() {
@@ -2147,15 +2282,17 @@ describe('ReactDOMComponent', () => {
2282 });
2283
2284 it('will not assign a function custom attributes', function() {
2150 - spyOn(console, 'error');
2285 + spyOnDev(console, 'error');
2286
2287 var el = ReactTestUtils.renderIntoDocument(<div whatever={() => {}} />);
2288
2289 expect(el.hasAttribute('whatever')).toBe(false);
2290
2156 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
2157 - 'Warning: Invalid value for prop `whatever` on <div> tag',
2158 - );
2291 + if (__DEV__) {
2292 + expect(console.error.calls.argsFor(0)[0]).toContain(
2293 + 'Warning: Invalid value for prop `whatever` on <div> tag',
2294 + );
2295 + }
2296 });
2297
2298 it('will assign an object custom attributes', function() {
@@ -2164,58 +2301,60 @@ describe('ReactDOMComponent', () => {
2301 });
2302
2303 it('allows cased data attributes', function() {
2167 - spyOn(console, 'error');
2304 + spyOnDev(console, 'error');
2305
2306 var el = ReactTestUtils.renderIntoDocument(<div data-fooBar="true" />);
2307 expect(el.getAttribute('data-foobar')).toBe('true');
2308
2172 - expectDev(console.error.calls.count()).toBe(1);
2173 - expectDev(
2174 - normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
2175 - ).toMatch(
2176 - 'React does not recognize the `data-fooBar` prop on a DOM element. ' +
2177 - 'If you intentionally want it to appear in the DOM as a custom ' +
2178 - 'attribute, spell it as lowercase `data-foobar` instead. ' +
2179 - 'If you accidentally passed it from a parent component, remove ' +
2180 - 'it from the DOM element.\n' +
2181 - ' in div (at **)',
2182 - );
2309 + if (__DEV__) {
2310 + expect(console.error.calls.count()).toBe(1);
2311 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toMatch(
2312 + 'React does not recognize the `data-fooBar` prop on a DOM element. ' +
2313 + 'If you intentionally want it to appear in the DOM as a custom ' +
2314 + 'attribute, spell it as lowercase `data-foobar` instead. ' +
2315 + 'If you accidentally passed it from a parent component, remove ' +
2316 + 'it from the DOM element.\n' +
2317 + ' in div (at **)',
2318 + );
2319 + }
2320 });
2321
2322 it('allows cased custom attributes', function() {
2186 - spyOn(console, 'error');
2323 + spyOnDev(console, 'error');
2324
2325 var el = ReactTestUtils.renderIntoDocument(<div fooBar="true" />);
2326 expect(el.getAttribute('foobar')).toBe('true');
2327
2191 - expectDev(console.error.calls.count()).toBe(1);
2192 - expectDev(
2193 - normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
2194 - ).toMatch(
2195 - 'React does not recognize the `fooBar` prop on a DOM element. ' +
2196 - 'If you intentionally want it to appear in the DOM as a custom ' +
2197 - 'attribute, spell it as lowercase `foobar` instead. ' +
2198 - 'If you accidentally passed it from a parent component, remove ' +
2199 - 'it from the DOM element.\n' +
2200 - ' in div (at **)',
2201 - );
2328 + if (__DEV__) {
2329 + expect(console.error.calls.count()).toBe(1);
2330 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toMatch(
2331 + 'React does not recognize the `fooBar` prop on a DOM element. ' +
2332 + 'If you intentionally want it to appear in the DOM as a custom ' +
2333 + 'attribute, spell it as lowercase `foobar` instead. ' +
2334 + 'If you accidentally passed it from a parent component, remove ' +
2335 + 'it from the DOM element.\n' +
2336 + ' in div (at **)',
2337 + );
2338 + }
2339 });
2340
2341 it('warns on NaN attributes', function() {
2205 - spyOn(console, 'error');
2342 + spyOnDev(console, 'error');
2343
2344 var el = ReactTestUtils.renderIntoDocument(<div whatever={NaN} />);
2345
2346 expect(el.getAttribute('whatever')).toBe('NaN');
2347
2211 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
2212 - 'Warning: Received NaN for the `whatever` attribute. If this is ' +
2213 - 'expected, cast the value to a string.\n in div',
2214 - );
2348 + if (__DEV__) {
2349 + expect(console.error.calls.argsFor(0)[0]).toContain(
2350 + 'Warning: Received NaN for the `whatever` attribute. If this is ' +
2351 + 'expected, cast the value to a string.\n in div',
2352 + );
2353 + }
2354 });
2355
2356 it('removes a property when it becomes invalid', function() {
2218 - spyOn(console, 'error');
2357 + spyOnDev(console, 'error');
2358
2359 var container = document.createElement('div');
2360 ReactDOM.render(<div whatever={0} />, container);
@@ -2224,21 +2363,25 @@ describe('ReactDOMComponent', () => {
2363
2364 expect(el.hasAttribute('whatever')).toBe(false);
2365
2227 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
2228 - 'Warning: Invalid value for prop `whatever` on <div> tag.',
2229 - );
2366 + if (__DEV__) {
2367 + expect(console.error.calls.argsFor(0)[0]).toContain(
2368 + 'Warning: Invalid value for prop `whatever` on <div> tag.',
2369 + );
2370 + }
2371 });
2372
2373 it('warns on bad casing of known HTML attributes', function() {
2233 - spyOn(console, 'error');
2374 + spyOnDev(console, 'error');
2375
2376 var el = ReactTestUtils.renderIntoDocument(<div SiZe="30" />);
2377
2378 expect(el.getAttribute('size')).toBe('30');
2379
2239 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
2240 - 'Warning: Invalid DOM property `SiZe`. Did you mean `size`?',
2241 - );
2380 + if (__DEV__) {
2381 + expect(console.error.calls.argsFor(0)[0]).toContain(
2382 + 'Warning: Invalid DOM property `SiZe`. Did you mean `size`?',
2383 + );
2384 + }
2385 });
2386 });
2387
@@ -2304,17 +2447,19 @@ describe('ReactDOMComponent', () => {
2447
2448 describe('String boolean attributes', function() {
2449 it('does not assign string boolean attributes for custom attributes', function() {
2307 - spyOn(console, 'error');
2450 + spyOnDev(console, 'error');
2451
2452 var el = ReactTestUtils.renderIntoDocument(<div whatever={true} />);
2453
2454 expect(el.hasAttribute('whatever')).toBe(false);
2455
2313 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
2314 - 'Received `true` for a non-boolean attribute `whatever`.\n\n' +
2315 - 'If you want to write it to the DOM, pass a string instead: ' +
2316 - 'whatever="true" or whatever={value.toString()}.',
2317 - );
2456 + if (__DEV__) {
2457 + expect(console.error.calls.argsFor(0)[0]).toContain(
2458 + 'Received `true` for a non-boolean attribute `whatever`.\n\n' +
2459 + 'If you want to write it to the DOM, pass a string instead: ' +
2460 + 'whatever="true" or whatever={value.toString()}.',
2461 + );
2462 + }
2463 });
2464
2465 it('stringifies the boolean true for allowed attributes', function() {
@@ -2339,7 +2484,7 @@ describe('ReactDOMComponent', () => {
2484
2485 describe('Hyphenated SVG elements', function() {
2486 it('the font-face element is not a custom element', function() {
2342 - spyOn(console, 'error');
2487 + spyOnDev(console, 'error');
2488 var el = ReactTestUtils.renderIntoDocument(
2489 <svg>
2490 <font-face x-height={false} />
@@ -2350,14 +2495,16 @@ describe('ReactDOMComponent', () => {
2495 false,
2496 );
2497
2353 - expectDev(console.error.calls.count()).toBe(1);
2354 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
2355 - 'Warning: Invalid DOM property `x-height`. Did you mean `xHeight`',
2356 - );
2498 + if (__DEV__) {
2499 + expect(console.error.calls.count()).toBe(1);
2500 + expect(console.error.calls.argsFor(0)[0]).toContain(
2501 + 'Warning: Invalid DOM property `x-height`. Did you mean `xHeight`',
2502 + );
2503 + }
2504 });
2505
2506 it('the font-face element does not allow unknown boolean values', function() {
2360 - spyOn(console, 'error');
2507 + spyOnDev(console, 'error');
2508 var el = ReactTestUtils.renderIntoDocument(
2509 <svg>
2510 <font-face whatever={false} />
@@ -2368,14 +2515,16 @@ describe('ReactDOMComponent', () => {
2515 false,
2516 );
2517
2371 - expectDev(console.error.calls.count()).toBe(1);
2372 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
2373 - 'Received `false` for a non-boolean attribute `whatever`.\n\n' +
2374 - 'If you want to write it to the DOM, pass a string instead: ' +
2375 - 'whatever="false" or whatever={value.toString()}.\n\n' +
2376 - 'If you used to conditionally omit it with whatever={condition && value}, ' +
2377 - 'pass whatever={condition ? value : undefined} instead.',
2378 - );
2518 + if (__DEV__) {
2519 + expect(console.error.calls.count()).toBe(1);
2520 + expect(console.error.calls.argsFor(0)[0]).toContain(
2521 + 'Received `false` for a non-boolean attribute `whatever`.\n\n' +
2522 + 'If you want to write it to the DOM, pass a string instead: ' +
2523 + 'whatever="false" or whatever={value.toString()}.\n\n' +
2524 + 'If you used to conditionally omit it with whatever={condition && value}, ' +
2525 + 'pass whatever={condition ? value : undefined} instead.',
2526 + );
2527 + }
2528 });
2529 });
2530 });
packages/react-dom/src/__tests__/ReactDOMComponentTree-test.js
+35 -27
@@ -177,36 +177,42 @@ describe('ReactDOMComponentTree', () => {
177
178 const component = <Controlled />;
179 const instance = ReactDOM.render(component, container);
180 - spyOn(console, 'error');
181 - expectDev(console.error.calls.count()).toBe(0);
180 + spyOnDev(console, 'error');
181 + if (__DEV__) {
182 + expect(console.error.calls.count()).toBe(0);
183 + }
184 simulateInput(instance.a, finishValue);
183 - expectDev(console.error.calls.count()).toBe(1);
184 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
185 - 'Warning: A component is changing an uncontrolled input of ' +
186 - 'type text to be controlled. Input elements should not ' +
187 - 'switch from uncontrolled to controlled (or vice versa). ' +
188 - 'Decide between using a controlled or uncontrolled input ' +
189 - 'element for the lifetime of the component. More info: ' +
190 - 'https://fb.me/react-controlled-components',
191 - );
185 + if (__DEV__) {
186 + expect(console.error.calls.count()).toBe(1);
187 + expect(console.error.calls.argsFor(0)[0]).toContain(
188 + 'Warning: A component is changing an uncontrolled input of ' +
189 + 'type text to be controlled. Input elements should not ' +
190 + 'switch from uncontrolled to controlled (or vice versa). ' +
191 + 'Decide between using a controlled or uncontrolled input ' +
192 + 'element for the lifetime of the component. More info: ' +
193 + 'https://fb.me/react-controlled-components',
194 + );
195 + }
196 });
197
198 it('finds instance of node that is attempted to be unmounted', () => {
195 - spyOn(console, 'error');
199 + spyOnDev(console, 'error');
200 const component = <div />;
201 const node = ReactDOM.render(<div>{component}</div>, container);
202 ReactDOM.unmountComponentAtNode(node);
199 - expectDev(console.error.calls.count()).toBe(1);
200 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
201 - "unmountComponentAtNode(): The node you're attempting to unmount " +
202 - 'was rendered by React and is not a top-level container. You may ' +
203 - 'have accidentally passed in a React root node instead of its ' +
204 - 'container.',
205 - );
203 + if (__DEV__) {
204 + expect(console.error.calls.count()).toBe(1);
205 + expect(console.error.calls.argsFor(0)[0]).toContain(
206 + "unmountComponentAtNode(): The node you're attempting to unmount " +
207 + 'was rendered by React and is not a top-level container. You may ' +
208 + 'have accidentally passed in a React root node instead of its ' +
209 + 'container.',
210 + );
211 + }
212 });
213
214 it('finds instance from node to stop rendering over other react rendered components', () => {
209 - spyOn(console, 'error');
215 + spyOnDev(console, 'error');
216 const component = (
217 <div>
218 <span>Hello</span>
@@ -215,12 +221,14 @@ describe('ReactDOMComponentTree', () => {
221 const anotherComponent = <div />;
222 const instance = ReactDOM.render(component, container);
223 ReactDOM.render(anotherComponent, instance);
218 - expectDev(console.error.calls.count()).toBe(1);
219 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
220 - 'render(...): Replacing React-rendered children with a new root ' +
221 - 'component. If you intended to update the children of this node, ' +
222 - 'you should instead have the existing children update their state ' +
223 - 'and render the new components instead of calling ReactDOM.render.',
224 - );
224 + if (__DEV__) {
225 + expect(console.error.calls.count()).toBe(1);
226 + expect(console.error.calls.argsFor(0)[0]).toContain(
227 + 'render(...): Replacing React-rendered children with a new root ' +
228 + 'component. If you intended to update the children of this node, ' +
229 + 'you should instead have the existing children update their state ' +
230 + 'and render the new components instead of calling ReactDOM.render.',
231 + );
232 + }
233 });
234 });
packages/react-dom/src/__tests__/ReactDOMFiber-test.js
+59 -49
@@ -919,40 +919,40 @@ describe('ReactDOMFiber', () => {
919 });
920
921 it('should warn for non-functional event listeners', () => {
922 - spyOn(console, 'error');
922 + spyOnDev(console, 'error');
923 class Example extends React.Component {
924 render() {
925 return <div onClick="woops" />;
926 }
927 }
928 ReactDOM.render(<Example />, container);
929 - expectDev(console.error.calls.count()).toBe(1);
930 - expectDev(
931 - normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
932 - ).toContain(
933 - 'Expected `onClick` listener to be a function, instead got a value of `string` type.\n' +
934 - ' in div (at **)\n' +
935 - ' in Example (at **)',
936 - );
929 + if (__DEV__) {
930 + expect(console.error.calls.count()).toBe(1);
931 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toContain(
932 + 'Expected `onClick` listener to be a function, instead got a value of `string` type.\n' +
933 + ' in div (at **)\n' +
934 + ' in Example (at **)',
935 + );
936 + }
937 });
938
939 it('should warn with a special message for `false` event listeners', () => {
940 - spyOn(console, 'error');
940 + spyOnDev(console, 'error');
941 class Example extends React.Component {
942 render() {
943 return <div onClick={false} />;
944 }
945 }
946 ReactDOM.render(<Example />, container);
947 - expectDev(console.error.calls.count()).toBe(1);
948 - expectDev(
949 - normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
950 - ).toContain(
951 - 'Expected `onClick` listener to be a function, instead got `false`.\n\n' +
952 - 'If you used to conditionally omit it with onClick={condition && value}, ' +
953 - 'pass onClick={condition ? value : undefined} instead.\n',
954 - ' in div (at **)\n' + ' in Example (at **)',
955 - );
947 + if (__DEV__) {
948 + expect(console.error.calls.count()).toBe(1);
949 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toContain(
950 + 'Expected `onClick` listener to be a function, instead got `false`.\n\n' +
951 + 'If you used to conditionally omit it with onClick={condition && value}, ' +
952 + 'pass onClick={condition ? value : undefined} instead.\n',
953 + ' in div (at **)\n' + ' in Example (at **)',
954 + );
955 + }
956 });
957
958 it('should not update event handlers until commit', () => {
@@ -1048,19 +1048,23 @@ describe('ReactDOMFiber', () => {
1048 });
1049
1050 it('should not warn when rendering into an empty container', () => {
1051 - spyOn(console, 'error');
1051 + spyOnDev(console, 'error');
1052 ReactDOM.render(<div>foo</div>, container);
1053 expect(container.innerHTML).toBe('<div>foo</div>');
1054 ReactDOM.render(null, container);
1055 expect(container.innerHTML).toBe('');
1056 - expectDev(console.error.calls.count()).toBe(0);
1056 + if (__DEV__) {
1057 + expect(console.error.calls.count()).toBe(0);
1058 + }
1059 ReactDOM.render(<div>bar</div>, container);
1060 expect(container.innerHTML).toBe('<div>bar</div>');
1059 - expectDev(console.error.calls.count()).toBe(0);
1061 + if (__DEV__) {
1062 + expect(console.error.calls.count()).toBe(0);
1063 + }
1064 });
1065
1066 it('should warn when replacing a container which was manually updated outside of React', () => {
1063 - spyOn(console, 'error');
1067 + spyOnDev(console, 'error');
1068 // when not messing with the DOM outside of React
1069 ReactDOM.render(<div key="1">foo</div>, container);
1070 ReactDOM.render(<div key="1">bar</div>, container);
@@ -1072,18 +1076,20 @@ describe('ReactDOMFiber', () => {
1076 container.innerHTML = '<div>MEOW.</div>';
1077 ReactDOM.render(<div key="2">baz</div>, container);
1078 }).toThrowError();
1075 - expectDev(console.error.calls.count()).toBe(1);
1076 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
1077 - 'render(...): ' +
1078 - 'It looks like the React-rendered content of this container was ' +
1079 - 'removed without using React. This is not supported and will ' +
1080 - 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
1081 - 'to empty a container.',
1082 - );
1079 + if (__DEV__) {
1080 + expect(console.error.calls.count()).toBe(1);
1081 + expect(console.error.calls.argsFor(0)[0]).toContain(
1082 + 'render(...): ' +
1083 + 'It looks like the React-rendered content of this container was ' +
1084 + 'removed without using React. This is not supported and will ' +
1085 + 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
1086 + 'to empty a container.',
1087 + );
1088 + }
1089 });
1090
1091 it('should warn when doing an update to a container manually updated outside of React', () => {
1086 - spyOn(console, 'error');
1092 + spyOnDev(console, 'error');
1093 // when not messing with the DOM outside of React
1094 ReactDOM.render(<div>foo</div>, container);
1095 ReactDOM.render(<div>bar</div>, container);
@@ -1092,18 +1098,20 @@ describe('ReactDOMFiber', () => {
1098 container.innerHTML = '<div>MEOW.</div>';
1099 ReactDOM.render(<div>baz</div>, container);
1100 // silently fails to update
1095 - expectDev(console.error.calls.count()).toBe(1);
1096 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
1097 - 'render(...): ' +
1098 - 'It looks like the React-rendered content of this container was ' +
1099 - 'removed without using React. This is not supported and will ' +
1100 - 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
1101 - 'to empty a container.',
1102 - );
1101 + if (__DEV__) {
1102 + expect(console.error.calls.count()).toBe(1);
1103 + expect(console.error.calls.argsFor(0)[0]).toContain(
1104 + 'render(...): ' +
1105 + 'It looks like the React-rendered content of this container was ' +
1106 + 'removed without using React. This is not supported and will ' +
1107 + 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
1108 + 'to empty a container.',
1109 + );
1110 + }
1111 });
1112
1113 it('should warn when doing an update to a container manually cleared outside of React', () => {
1106 - spyOn(console, 'error');
1114 + spyOnDev(console, 'error');
1115 // when not messing with the DOM outside of React
1116 ReactDOM.render(<div>foo</div>, container);
1117 ReactDOM.render(<div>bar</div>, container);
@@ -1112,14 +1120,16 @@ describe('ReactDOMFiber', () => {
1120 container.innerHTML = '';
1121 ReactDOM.render(<div>baz</div>, container);
1122 // silently fails to update
1115 - expectDev(console.error.calls.count()).toBe(1);
1116 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
1117 - 'render(...): ' +
1118 - 'It looks like the React-rendered content of this container was ' +
1119 - 'removed without using React. This is not supported and will ' +
1120 - 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
1121 - 'to empty a container.',
1122 - );
1123 + if (__DEV__) {
1124 + expect(console.error.calls.count()).toBe(1);
1125 + expect(console.error.calls.argsFor(0)[0]).toContain(
1126 + 'render(...): ' +
1127 + 'It looks like the React-rendered content of this container was ' +
1128 + 'removed without using React. This is not supported and will ' +
1129 + 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
1130 + 'to empty a container.',
1131 + );
1132 + }
1133 });
1134
1135 it('should render a text component with a text DOM node on the same document as the container', () => {
packages/react-dom/src/__tests__/ReactDOMInput-test.js
+224 -166
@@ -38,7 +38,7 @@ describe('ReactDOMInput', () => {
38 ReactDOM = require('react-dom');
39 ReactDOMServer = require('react-dom/server');
40 ReactTestUtils = require('react-dom/test-utils');
41 - spyOn(console, 'error');
41 + spyOnDev(console, 'error');
42 });
43
44 it('should properly control a value even if no event listener exists', () => {
@@ -48,7 +48,9 @@ describe('ReactDOMInput', () => {
48 document.body.appendChild(container);
49
50 var node = ReactDOM.findDOMNode(stub);
51 - expectDev(console.error.calls.count()).toBe(1);
51 + if (__DEV__) {
52 + expect(console.error.calls.count()).toBe(1);
53 + }
54
55 setUntrackedValue.call(node, 'giraffe');
56
@@ -785,19 +787,23 @@ describe('ReactDOMInput', () => {
787 ReactTestUtils.renderIntoDocument(
788 <input type="text" value="zoink" readOnly={true} />,
789 );
788 - expectDev(console.error.calls.count()).toBe(0);
790 + if (__DEV__) {
791 + expect(console.error.calls.count()).toBe(0);
792 + }
793
794 ReactTestUtils.renderIntoDocument(
795 <input type="text" value="zoink" readOnly={false} />,
796 );
793 - expectDev(console.error.calls.count()).toBe(1);
794 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
795 - 'Warning: Failed prop type: You provided a `value` prop to a form ' +
796 - 'field without an `onChange` handler. This will render a read-only ' +
797 - 'field. If the field should be mutable use `defaultValue`. ' +
798 - 'Otherwise, set either `onChange` or `readOnly`.\n' +
799 - ' in input (at **)',
800 - );
797 + if (__DEV__) {
798 + expect(console.error.calls.count()).toBe(1);
799 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
800 + 'Warning: Failed prop type: You provided a `value` prop to a form ' +
801 + 'field without an `onChange` handler. This will render a read-only ' +
802 + 'field. If the field should be mutable use `defaultValue`. ' +
803 + 'Otherwise, set either `onChange` or `readOnly`.\n' +
804 + ' in input (at **)',
805 + );
806 + }
807 });
808
809 it('should have a this value of undefined if bind is not used', () => {
@@ -815,12 +821,16 @@ describe('ReactDOMInput', () => {
821 ReactTestUtils.renderIntoDocument(
822 <input type="checkbox" checked="false" readOnly={true} />,
823 );
818 - expectDev(console.error.calls.count()).toBe(0);
824 + if (__DEV__) {
825 + expect(console.error.calls.count()).toBe(0);
826 + }
827
828 ReactTestUtils.renderIntoDocument(
829 <input type="checkbox" checked="false" readOnly={false} />,
830 );
823 - expectDev(console.error.calls.count()).toBe(1);
831 + if (__DEV__) {
832 + expect(console.error.calls.count()).toBe(1);
833 + }
834 });
835
836 it('should update defaultValue to empty string', () => {
@@ -832,14 +842,18 @@ describe('ReactDOMInput', () => {
842
843 it('should warn if value is null', () => {
844 ReactTestUtils.renderIntoDocument(<input type="text" value={null} />);
835 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
836 - '`value` prop on `input` should not be null. ' +
837 - 'Consider using an empty string to clear the component or `undefined` ' +
838 - 'for uncontrolled components.',
839 - );
845 + if (__DEV__) {
846 + expect(console.error.calls.argsFor(0)[0]).toContain(
847 + '`value` prop on `input` should not be null. ' +
848 + 'Consider using an empty string to clear the component or `undefined` ' +
849 + 'for uncontrolled components.',
850 + );
851 + }
852
853 ReactTestUtils.renderIntoDocument(<input type="text" value={null} />);
842 - expectDev(console.error.calls.count()).toBe(1);
854 + if (__DEV__) {
855 + expect(console.error.calls.count()).toBe(1);
856 + }
857 });
858
859 it('should warn if checked and defaultChecked props are specified', () => {
@@ -851,14 +865,16 @@ describe('ReactDOMInput', () => {
865 readOnly={true}
866 />,
867 );
854 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
855 - 'A component contains an input of type radio with both checked and defaultChecked props. ' +
856 - 'Input elements must be either controlled or uncontrolled ' +
857 - '(specify either the checked prop, or the defaultChecked prop, but not ' +
858 - 'both). Decide between using a controlled or uncontrolled input ' +
859 - 'element and remove one of these props. More info: ' +
860 - 'https://fb.me/react-controlled-components',
861 - );
868 + if (__DEV__) {
869 + expect(console.error.calls.argsFor(0)[0]).toContain(
870 + 'A component contains an input of type radio with both checked and defaultChecked props. ' +
871 + 'Input elements must be either controlled or uncontrolled ' +
872 + '(specify either the checked prop, or the defaultChecked prop, but not ' +
873 + 'both). Decide between using a controlled or uncontrolled input ' +
874 + 'element and remove one of these props. More info: ' +
875 + 'https://fb.me/react-controlled-components',
876 + );
877 + }
878
879 ReactTestUtils.renderIntoDocument(
880 <input
@@ -868,26 +884,32 @@ describe('ReactDOMInput', () => {
884 readOnly={true}
885 />,
886 );
871 - expectDev(console.error.calls.count()).toBe(1);
887 + if (__DEV__) {
888 + expect(console.error.calls.count()).toBe(1);
889 + }
890 });
891
892 it('should warn if value and defaultValue props are specified', () => {
893 ReactTestUtils.renderIntoDocument(
894 <input type="text" value="foo" defaultValue="bar" readOnly={true} />,
895 );
878 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
879 - 'A component contains an input of type text with both value and defaultValue props. ' +
880 - 'Input elements must be either controlled or uncontrolled ' +
881 - '(specify either the value prop, or the defaultValue prop, but not ' +
882 - 'both). Decide between using a controlled or uncontrolled input ' +
883 - 'element and remove one of these props. More info: ' +
884 - 'https://fb.me/react-controlled-components',
885 - );
896 + if (__DEV__) {
897 + expect(console.error.calls.argsFor(0)[0]).toContain(
898 + 'A component contains an input of type text with both value and defaultValue props. ' +
899 + 'Input elements must be either controlled or uncontrolled ' +
900 + '(specify either the value prop, or the defaultValue prop, but not ' +
901 + 'both). Decide between using a controlled or uncontrolled input ' +
902 + 'element and remove one of these props. More info: ' +
903 + 'https://fb.me/react-controlled-components',
904 + );
905 + }
906
907 ReactTestUtils.renderIntoDocument(
908 <input type="text" value="foo" defaultValue="bar" readOnly={true} />,
909 );
890 - expectDev(console.error.calls.count()).toBe(1);
910 + if (__DEV__) {
911 + expect(console.error.calls.count()).toBe(1);
912 + }
913 });
914
915 it('should warn if controlled input switches to uncontrolled (value is undefined)', () => {
@@ -897,14 +919,16 @@ describe('ReactDOMInput', () => {
919 var container = document.createElement('div');
920 ReactDOM.render(stub, container);
921 ReactDOM.render(<input type="text" />, container);
900 - expectDev(console.error.calls.count()).toBe(1);
901 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
902 - 'Warning: A component is changing a controlled input of type text to be uncontrolled. ' +
903 - 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
904 - 'Decide between using a controlled or uncontrolled input ' +
905 - 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
906 - ' in input (at **)',
907 - );
922 + if (__DEV__) {
923 + expect(console.error.calls.count()).toBe(1);
924 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
925 + 'Warning: A component is changing a controlled input of type text to be uncontrolled. ' +
926 + 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
927 + 'Decide between using a controlled or uncontrolled input ' +
928 + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
929 + ' in input (at **)',
930 + );
931 + }
932 });
933
934 it('should warn if controlled input switches to uncontrolled (value is null)', () => {
@@ -914,14 +938,16 @@ describe('ReactDOMInput', () => {
938 var container = document.createElement('div');
939 ReactDOM.render(stub, container);
940 ReactDOM.render(<input type="text" value={null} />, container);
917 - expectDev(console.error.calls.count()).toBeGreaterThan(0);
918 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
919 - 'Warning: A component is changing a controlled input of type text to be uncontrolled. ' +
920 - 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
921 - 'Decide between using a controlled or uncontrolled input ' +
922 - 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
923 - ' in input (at **)',
924 - );
941 + if (__DEV__) {
942 + expect(console.error.calls.count()).toBeGreaterThan(0);
943 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
944 + 'Warning: A component is changing a controlled input of type text to be uncontrolled. ' +
945 + 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
946 + 'Decide between using a controlled or uncontrolled input ' +
947 + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
948 + ' in input (at **)',
949 + );
950 + }
951 });
952
953 it('should warn if controlled input switches to uncontrolled with defaultValue', () => {
@@ -934,14 +960,16 @@ describe('ReactDOMInput', () => {
960 <input type="text" defaultValue="uncontrolled" />,
961 container,
962 );
937 - expectDev(console.error.calls.count()).toBe(1);
938 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
939 - 'Warning: A component is changing a controlled input of type text to be uncontrolled. ' +
940 - 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
941 - 'Decide between using a controlled or uncontrolled input ' +
942 - 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
943 - ' in input (at **)',
944 - );
963 + if (__DEV__) {
964 + expect(console.error.calls.count()).toBe(1);
965 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
966 + 'Warning: A component is changing a controlled input of type text to be uncontrolled. ' +
967 + 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
968 + 'Decide between using a controlled or uncontrolled input ' +
969 + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
970 + ' in input (at **)',
971 + );
972 + }
973 });
974
975 it('should warn if uncontrolled input (value is undefined) switches to controlled', () => {
@@ -949,14 +977,16 @@ describe('ReactDOMInput', () => {
977 var container = document.createElement('div');
978 ReactDOM.render(stub, container);
979 ReactDOM.render(<input type="text" value="controlled" />, container);
952 - expectDev(console.error.calls.count()).toBe(1);
953 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
954 - 'Warning: A component is changing an uncontrolled input of type text to be controlled. ' +
955 - 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
956 - 'Decide between using a controlled or uncontrolled input ' +
957 - 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
958 - ' in input (at **)',
959 - );
980 + if (__DEV__) {
981 + expect(console.error.calls.count()).toBe(1);
982 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
983 + 'Warning: A component is changing an uncontrolled input of type text to be controlled. ' +
984 + 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
985 + 'Decide between using a controlled or uncontrolled input ' +
986 + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
987 + ' in input (at **)',
988 + );
989 + }
990 });
991
992 it('should warn if uncontrolled input (value is null) switches to controlled', () => {
@@ -964,14 +994,16 @@ describe('ReactDOMInput', () => {
994 var container = document.createElement('div');
995 ReactDOM.render(stub, container);
996 ReactDOM.render(<input type="text" value="controlled" />, container);
967 - expectDev(console.error.calls.count()).toBeGreaterThan(0);
968 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
969 - 'Warning: A component is changing an uncontrolled input of type text to be controlled. ' +
970 - 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
971 - 'Decide between using a controlled or uncontrolled input ' +
972 - 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
973 - ' in input (at **)',
974 - );
997 + if (__DEV__) {
998 + expect(console.error.calls.count()).toBeGreaterThan(0);
999 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
1000 + 'Warning: A component is changing an uncontrolled input of type text to be controlled. ' +
1001 + 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1002 + 'Decide between using a controlled or uncontrolled input ' +
1003 + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1004 + ' in input (at **)',
1005 + );
1006 + }
1007 });
1008
1009 it('should warn if controlled checkbox switches to uncontrolled (checked is undefined)', () => {
@@ -981,14 +1013,16 @@ describe('ReactDOMInput', () => {
1013 var container = document.createElement('div');
1014 ReactDOM.render(stub, container);
1015 ReactDOM.render(<input type="checkbox" />, container);
984 - expectDev(console.error.calls.count()).toBe(1);
985 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
986 - 'Warning: A component is changing a controlled input of type checkbox to be uncontrolled. ' +
987 - 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
988 - 'Decide between using a controlled or uncontrolled input ' +
989 - 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
990 - ' in input (at **)',
991 - );
1016 + if (__DEV__) {
1017 + expect(console.error.calls.count()).toBe(1);
1018 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1019 + 'Warning: A component is changing a controlled input of type checkbox to be uncontrolled. ' +
1020 + 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1021 + 'Decide between using a controlled or uncontrolled input ' +
1022 + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1023 + ' in input (at **)',
1024 + );
1025 + }
1026 });
1027
1028 it('should warn if controlled checkbox switches to uncontrolled (checked is null)', () => {
@@ -998,14 +1032,16 @@ describe('ReactDOMInput', () => {
1032 var container = document.createElement('div');
1033 ReactDOM.render(stub, container);
1034 ReactDOM.render(<input type="checkbox" checked={null} />, container);
1001 - expectDev(console.error.calls.count()).toBe(1);
1002 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1003 - 'Warning: A component is changing a controlled input of type checkbox to be uncontrolled. ' +
1004 - 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1005 - 'Decide between using a controlled or uncontrolled input ' +
1006 - 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1007 - ' in input (at **)',
1008 - );
1035 + if (__DEV__) {
1036 + expect(console.error.calls.count()).toBe(1);
1037 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1038 + 'Warning: A component is changing a controlled input of type checkbox to be uncontrolled. ' +
1039 + 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1040 + 'Decide between using a controlled or uncontrolled input ' +
1041 + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1042 + ' in input (at **)',
1043 + );
1044 + }
1045 });
1046
1047 it('should warn if controlled checkbox switches to uncontrolled with defaultChecked', () => {
@@ -1015,14 +1051,16 @@ describe('ReactDOMInput', () => {
1051 var container = document.createElement('div');
1052 ReactDOM.render(stub, container);
1053 ReactDOM.render(<input type="checkbox" defaultChecked={true} />, container);
1018 - expectDev(console.error.calls.count()).toBe(1);
1019 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1020 - 'Warning: A component is changing a controlled input of type checkbox to be uncontrolled. ' +
1021 - 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1022 - 'Decide between using a controlled or uncontrolled input ' +
1023 - 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1024 - ' in input (at **)',
1025 - );
1054 + if (__DEV__) {
1055 + expect(console.error.calls.count()).toBe(1);
1056 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1057 + 'Warning: A component is changing a controlled input of type checkbox to be uncontrolled. ' +
1058 + 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1059 + 'Decide between using a controlled or uncontrolled input ' +
1060 + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1061 + ' in input (at **)',
1062 + );
1063 + }
1064 });
1065
1066 it('should warn if uncontrolled checkbox (checked is undefined) switches to controlled', () => {
@@ -1030,14 +1068,16 @@ describe('ReactDOMInput', () => {
1068 var container = document.createElement('div');
1069 ReactDOM.render(stub, container);
1070 ReactDOM.render(<input type="checkbox" checked={true} />, container);
1033 - expectDev(console.error.calls.count()).toBe(1);
1034 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1035 - 'Warning: A component is changing an uncontrolled input of type checkbox to be controlled. ' +
1036 - 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1037 - 'Decide between using a controlled or uncontrolled input ' +
1038 - 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1039 - ' in input (at **)',
1040 - );
1071 + if (__DEV__) {
1072 + expect(console.error.calls.count()).toBe(1);
1073 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1074 + 'Warning: A component is changing an uncontrolled input of type checkbox to be controlled. ' +
1075 + 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1076 + 'Decide between using a controlled or uncontrolled input ' +
1077 + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1078 + ' in input (at **)',
1079 + );
1080 + }
1081 });
1082
1083 it('should warn if uncontrolled checkbox (checked is null) switches to controlled', () => {
@@ -1045,14 +1085,16 @@ describe('ReactDOMInput', () => {
1085 var container = document.createElement('div');
1086 ReactDOM.render(stub, container);
1087 ReactDOM.render(<input type="checkbox" checked={true} />, container);
1048 - expectDev(console.error.calls.count()).toBe(1);
1049 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1050 - 'Warning: A component is changing an uncontrolled input of type checkbox to be controlled. ' +
1051 - 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1052 - 'Decide between using a controlled or uncontrolled input ' +
1053 - 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1054 - ' in input (at **)',
1055 - );
1088 + if (__DEV__) {
1089 + expect(console.error.calls.count()).toBe(1);
1090 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1091 + 'Warning: A component is changing an uncontrolled input of type checkbox to be controlled. ' +
1092 + 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1093 + 'Decide between using a controlled or uncontrolled input ' +
1094 + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1095 + ' in input (at **)',
1096 + );
1097 + }
1098 });
1099
1100 it('should warn if controlled radio switches to uncontrolled (checked is undefined)', () => {
@@ -1060,14 +1102,16 @@ describe('ReactDOMInput', () => {
1102 var container = document.createElement('div');
1103 ReactDOM.render(stub, container);
1104 ReactDOM.render(<input type="radio" />, container);
1063 - expectDev(console.error.calls.count()).toBe(1);
1064 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1065 - 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
1066 - 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1067 - 'Decide between using a controlled or uncontrolled input ' +
1068 - 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1069 - ' in input (at **)',
1070 - );
1105 + if (__DEV__) {
1106 + expect(console.error.calls.count()).toBe(1);
1107 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1108 + 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
1109 + 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1110 + 'Decide between using a controlled or uncontrolled input ' +
1111 + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1112 + ' in input (at **)',
1113 + );
1114 + }
1115 });
1116
1117 it('should warn if controlled radio switches to uncontrolled (checked is null)', () => {
@@ -1075,14 +1119,16 @@ describe('ReactDOMInput', () => {
1119 var container = document.createElement('div');
1120 ReactDOM.render(stub, container);
1121 ReactDOM.render(<input type="radio" checked={null} />, container);
1078 - expectDev(console.error.calls.count()).toBe(1);
1079 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1080 - 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
1081 - 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1082 - 'Decide between using a controlled or uncontrolled input ' +
1083 - 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1084 - ' in input (at **)',
1085 - );
1122 + if (__DEV__) {
1123 + expect(console.error.calls.count()).toBe(1);
1124 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1125 + 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
1126 + 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1127 + 'Decide between using a controlled or uncontrolled input ' +
1128 + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1129 + ' in input (at **)',
1130 + );
1131 + }
1132 });
1133
1134 it('should warn if controlled radio switches to uncontrolled with defaultChecked', () => {
@@ -1090,14 +1136,16 @@ describe('ReactDOMInput', () => {
1136 var container = document.createElement('div');
1137 ReactDOM.render(stub, container);
1138 ReactDOM.render(<input type="radio" defaultChecked={true} />, container);
1093 - expectDev(console.error.calls.count()).toBe(1);
1094 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1095 - 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
1096 - 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1097 - 'Decide between using a controlled or uncontrolled input ' +
1098 - 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1099 - ' in input (at **)',
1100 - );
1139 + if (__DEV__) {
1140 + expect(console.error.calls.count()).toBe(1);
1141 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1142 + 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
1143 + 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1144 + 'Decide between using a controlled or uncontrolled input ' +
1145 + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1146 + ' in input (at **)',
1147 + );
1148 + }
1149 });
1150
1151 it('should warn if uncontrolled radio (checked is undefined) switches to controlled', () => {
@@ -1105,14 +1153,16 @@ describe('ReactDOMInput', () => {
1153 var container = document.createElement('div');
1154 ReactDOM.render(stub, container);
1155 ReactDOM.render(<input type="radio" checked={true} />, container);
1108 - expectDev(console.error.calls.count()).toBe(1);
1109 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1110 - 'Warning: A component is changing an uncontrolled input of type radio to be controlled. ' +
1111 - 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1112 - 'Decide between using a controlled or uncontrolled input ' +
1113 - 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1114 - ' in input (at **)',
1115 - );
1156 + if (__DEV__) {
1157 + expect(console.error.calls.count()).toBe(1);
1158 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1159 + 'Warning: A component is changing an uncontrolled input of type radio to be controlled. ' +
1160 + 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1161 + 'Decide between using a controlled or uncontrolled input ' +
1162 + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1163 + ' in input (at **)',
1164 + );
1165 + }
1166 });
1167
1168 it('should warn if uncontrolled radio (checked is null) switches to controlled', () => {
@@ -1120,14 +1170,16 @@ describe('ReactDOMInput', () => {
1170 var container = document.createElement('div');
1171 ReactDOM.render(stub, container);
1172 ReactDOM.render(<input type="radio" checked={true} />, container);
1123 - expectDev(console.error.calls.count()).toBe(1);
1124 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1125 - 'Warning: A component is changing an uncontrolled input of type radio to be controlled. ' +
1126 - 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1127 - 'Decide between using a controlled or uncontrolled input ' +
1128 - 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1129 - ' in input (at **)',
1130 - );
1173 + if (__DEV__) {
1174 + expect(console.error.calls.count()).toBe(1);
1175 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1176 + 'Warning: A component is changing an uncontrolled input of type radio to be controlled. ' +
1177 + 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1178 + 'Decide between using a controlled or uncontrolled input ' +
1179 + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1180 + ' in input (at **)',
1181 + );
1182 + }
1183 });
1184
1185 it('should not warn if radio value changes but never becomes controlled', () => {
@@ -1143,7 +1195,9 @@ describe('ReactDOMInput', () => {
1195 container,
1196 );
1197 ReactDOM.render(<input type="radio" />, container);
1146 - expectDev(console.error.calls.count()).toBe(0);
1198 + if (__DEV__) {
1199 + expect(console.error.calls.count()).toBe(0);
1200 + }
1201 });
1202
1203 it('should not warn if radio value changes but never becomes uncontrolled', () => {
@@ -1162,7 +1216,9 @@ describe('ReactDOMInput', () => {
1216 />,
1217 container,
1218 );
1165 - expectDev(console.error.calls.count()).toBe(0);
1219 + if (__DEV__) {
1220 + expect(console.error.calls.count()).toBe(0);
1221 + }
1222 });
1223
1224 it('should warn if radio checked false changes to become uncontrolled', () => {
@@ -1177,13 +1233,15 @@ describe('ReactDOMInput', () => {
1233 container,
1234 );
1235 ReactDOM.render(<input type="radio" value="value" />, container);
1180 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1181 - 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
1182 - 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1183 - 'Decide between using a controlled or uncontrolled input ' +
1184 - 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1185 - ' in input (at **)',
1186 - );
1236 + if (__DEV__) {
1237 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1238 + 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
1239 + 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1240 + 'Decide between using a controlled or uncontrolled input ' +
1241 + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1242 + ' in input (at **)',
1243 + );
1244 + }
1245 });
1246
1247 it('sets type, step, min, max before value always', () => {
packages/react-dom/src/__tests__/ReactDOMInvalidARIAHook-test.js
+44 -32
@@ -26,62 +26,74 @@ describe('ReactDOMInvalidARIAHook', () => {
26
27 describe('aria-* props', () => {
28 it('should allow valid aria-* props', () => {
29 - spyOn(console, 'error');
29 + spyOnDev(console, 'error');
30 mountComponent({'aria-label': 'Bumble bees'});
31 - expectDev(console.error.calls.count()).toBe(0);
31 + if (__DEV__) {
32 + expect(console.error.calls.count()).toBe(0);
33 + }
34 });
35 it('should warn for one invalid aria-* prop', () => {
34 - spyOn(console, 'error');
36 + spyOnDev(console, 'error');
37 mountComponent({'aria-badprop': 'maybe'});
36 - expectDev(console.error.calls.count()).toBe(1);
37 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
38 - 'Warning: Invalid aria prop `aria-badprop` on <div> tag. ' +
39 - 'For details, see https://fb.me/invalid-aria-prop',
40 - );
38 + if (__DEV__) {
39 + expect(console.error.calls.count()).toBe(1);
40 + expect(console.error.calls.argsFor(0)[0]).toContain(
41 + 'Warning: Invalid aria prop `aria-badprop` on <div> tag. ' +
42 + 'For details, see https://fb.me/invalid-aria-prop',
43 + );
44 + }
45 });
46 it('should warn for many invalid aria-* props', () => {
43 - spyOn(console, 'error');
47 + spyOnDev(console, 'error');
48 mountComponent({
49 'aria-badprop': 'Very tall trees',
50 'aria-malprop': 'Turbulent seas',
51 });
48 - expectDev(console.error.calls.count()).toBe(1);
49 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
50 - 'Warning: Invalid aria props `aria-badprop`, `aria-malprop` on <div> ' +
51 - 'tag. For details, see https://fb.me/invalid-aria-prop',
52 - );
52 + if (__DEV__) {
53 + expect(console.error.calls.count()).toBe(1);
54 + expect(console.error.calls.argsFor(0)[0]).toContain(
55 + 'Warning: Invalid aria props `aria-badprop`, `aria-malprop` on <div> ' +
56 + 'tag. For details, see https://fb.me/invalid-aria-prop',
57 + );
58 + }
59 });
60 it('should warn for an improperly cased aria-* prop', () => {
55 - spyOn(console, 'error');
61 + spyOnDev(console, 'error');
62 // The valid attribute name is aria-haspopup.
63 mountComponent({'aria-hasPopup': 'true'});
58 - expectDev(console.error.calls.count()).toBe(1);
59 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
60 - 'Warning: Unknown ARIA attribute `aria-hasPopup`. ' +
61 - 'Did you mean `aria-haspopup`?',
62 - );
64 + if (__DEV__) {
65 + expect(console.error.calls.count()).toBe(1);
66 + expect(console.error.calls.argsFor(0)[0]).toContain(
67 + 'Warning: Unknown ARIA attribute `aria-hasPopup`. ' +
68 + 'Did you mean `aria-haspopup`?',
69 + );
70 + }
71 });
72
73 it('should warn for use of recognized camel case aria attributes', () => {
66 - spyOn(console, 'error');
74 + spyOnDev(console, 'error');
75 // The valid attribute name is aria-haspopup.
76 mountComponent({ariaHasPopup: 'true'});
69 - expectDev(console.error.calls.count()).toBe(1);
70 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
71 - 'Warning: Invalid ARIA attribute `ariaHasPopup`. ' +
72 - 'Did you mean `aria-haspopup`?',
73 - );
77 + if (__DEV__) {
78 + expect(console.error.calls.count()).toBe(1);
79 + expect(console.error.calls.argsFor(0)[0]).toContain(
80 + 'Warning: Invalid ARIA attribute `ariaHasPopup`. ' +
81 + 'Did you mean `aria-haspopup`?',
82 + );
83 + }
84 });
85
86 it('should warn for use of unrecognized camel case aria attributes', () => {
77 - spyOn(console, 'error');
87 + spyOnDev(console, 'error');
88 // The valid attribute name is aria-haspopup.
89 mountComponent({ariaSomethingInvalid: 'true'});
80 - expectDev(console.error.calls.count()).toBe(1);
81 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
82 - 'Warning: Invalid ARIA attribute `ariaSomethingInvalid`. ARIA ' +
83 - 'attributes follow the pattern aria-* and must be lowercase.',
84 - );
90 + if (__DEV__) {
91 + expect(console.error.calls.count()).toBe(1);
92 + expect(console.error.calls.argsFor(0)[0]).toContain(
93 + 'Warning: Invalid ARIA attribute `ariaSomethingInvalid`. ARIA ' +
94 + 'attributes follow the pattern aria-* and must be lowercase.',
95 + );
96 + }
97 });
98 });
99 });
packages/react-dom/src/__tests__/ReactDOMOption-test.js
+14 -12
@@ -37,7 +37,7 @@ describe('ReactDOMOption', () => {
37 });
38
39 it('should ignore and warn invalid children types', () => {
40 - spyOn(console, 'error');
40 + spyOnDev(console, 'error');
41 var el = (
42 <option>
43 {1} <div /> {2}
@@ -46,15 +46,15 @@ describe('ReactDOMOption', () => {
46 var node = ReactTestUtils.renderIntoDocument(el);
47 expect(node.innerHTML).toBe('1 2');
48 ReactTestUtils.renderIntoDocument(el);
49 - // only warn once
50 - expectDev(console.error.calls.count()).toBe(1);
51 - expectDev(
52 - normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
53 - ).toContain(
54 - '<div> cannot appear as a child of <option>.\n' +
55 - ' in div (at **)\n' +
56 - ' in option (at **)',
57 - );
49 + if (__DEV__) {
50 + // only warn once
51 + expect(console.error.calls.count()).toBe(1);
52 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toContain(
53 + '<div> cannot appear as a child of <option>.\n' +
54 + ' in div (at **)\n' +
55 + ' in option (at **)',
56 + );
57 + }
58 });
59
60 it('should ignore null/undefined/false children without warning', () => {
@@ -66,12 +66,14 @@ describe('ReactDOMOption', () => {
66 {undefined} {2}
67 </option>
68 );
69 - spyOn(console, 'error');
69 + spyOnDev(console, 'error');
70 stub = ReactTestUtils.renderIntoDocument(stub);
71
72 var node = ReactDOM.findDOMNode(stub);
73
74 - expectDev(console.error.calls.count()).toBe(0);
74 + if (__DEV__) {
75 + expect(console.error.calls.count()).toBe(0);
76 + }
77 expect(node.innerHTML).toBe('1 2');
78 });
79
packages/react-dom/src/__tests__/ReactDOMProduction-test.js deleted
-361
@@ -1,361 +0,0 @@
1 -/**
2 - * Copyright (c) 2013-present, Facebook, Inc.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @emails react-core
8 - */
9 -'use strict';
10 -
11 -describe('ReactDOMProduction', () => {
12 - var React;
13 - var ReactDOM;
14 - var ReactDOMServer;
15 - var oldProcess;
16 -
17 - beforeEach(() => {
18 - __DEV__ = false;
19 -
20 - // Mutating process.env.NODE_ENV would cause our babel plugins to do the
21 - // wrong thing. If you change this, make sure to test with jest --no-cache.
22 - oldProcess = process;
23 - global.process = {
24 - ...process,
25 - env: {...process.env, NODE_ENV: 'production'},
26 - };
27 -
28 - jest.resetModules();
29 - React = require('react');
30 - ReactDOM = require('react-dom');
31 - ReactDOMServer = require('react-dom/server');
32 - });
33 -
34 - afterEach(() => {
35 - __DEV__ = true;
36 - global.process = oldProcess;
37 - });
38 -
39 - it('should use prod fbjs', () => {
40 - var warning = require('fbjs/lib/warning');
41 -
42 - spyOn(console, 'error');
43 - warning(false, 'Do cows go moo?');
44 - expectDev(console.error.calls.count()).toBe(0);
45 - });
46 -
47 - it('should use prod React', () => {
48 - spyOn(console, 'error');
49 -
50 - // no key warning
51 - void <div>{[<span />]}</div>;
52 -
53 - expectDev(console.error.calls.count()).toBe(0);
54 - });
55 -
56 - it('should handle a simple flow', () => {
57 - class Component extends React.Component {
58 - render() {
59 - return <span>{this.props.children}</span>;
60 - }
61 - }
62 -
63 - var container = document.createElement('div');
64 - var inst = ReactDOM.render(
65 - <div className="blue" style={{fontFamily: 'Helvetica'}}>
66 - <Component key={1}>A</Component>
67 - <Component key={2}>B</Component>
68 - <Component key={3}>C</Component>
69 - </div>,
70 - container,
71 - );
72 -
73 - expect(container.firstChild).toBe(inst);
74 - expect(inst.className).toBe('blue');
75 - expect(inst.style.fontFamily).toBe('Helvetica');
76 - expect(inst.textContent).toBe('ABC');
77 -
78 - ReactDOM.render(
79 - <div className="red" style={{fontFamily: 'Comic Sans MS'}}>
80 - <Component key={2}>B</Component>
81 - <Component key={1}>A</Component>
82 - <Component key={3}>C</Component>
83 - </div>,
84 - container,
85 - );
86 -
87 - expect(inst.className).toBe('red');
88 - expect(inst.style.fontFamily).toBe('Comic Sans MS');
89 - expect(inst.textContent).toBe('BAC');
90 -
91 - ReactDOM.unmountComponentAtNode(container);
92 -
93 - expect(container.childNodes.length).toBe(0);
94 - });
95 -
96 - it('should support createFactory', () => {
97 - var span = React.createFactory('span');
98 - class Component extends React.Component {
99 - render() {
100 - return span({children: this.props.children});
101 - }
102 - }
103 -
104 - var ComponentFactory = React.createFactory(Component);
105 -
106 - var container = document.createElement('div');
107 - ReactDOM.render(
108 - ComponentFactory(null, span(null, 'Hello'), span(null, 'world')),
109 - container,
110 - );
111 - expect(container.firstChild.tagName).toBe('SPAN');
112 - expect(container.firstChild.childNodes[0].tagName).toBe('SPAN');
113 - expect(container.firstChild.childNodes[0].textContent).toBe('Hello');
114 - expect(container.firstChild.childNodes[1].tagName).toBe('SPAN');
115 - expect(container.firstChild.childNodes[1].textContent).toBe('world');
116 - });
117 -
118 - it('should support React public API methods', () => {
119 - expect(React.isValidElement(42)).toBe(false);
120 - expect(React.isValidElement(<div />)).toBe(true);
121 - expect(React.cloneElement(<div />, {foo: 42})).toEqual(<div foo={42} />);
122 -
123 - const mapped = React.Children.map(<div />, el =>
124 - React.cloneElement(el, {foo: 42}),
125 - );
126 - expect(mapped.length).toBe(1);
127 - expect(mapped[0].type).toBe('div');
128 - expect(mapped[0].props.foo).toBe(42);
129 -
130 - const arr = React.Children.toArray(<div />);
131 - expect(arr.length).toBe(1);
132 - expect(arr[0].type).toBe('div');
133 -
134 - let called = 0;
135 - React.Children.forEach(<div />, () => called++);
136 - expect(called).toBe(1);
137 -
138 - expect(React.Children.count(<div />)).toBe(1);
139 - expect(() => React.Children.only(42)).toThrowError();
140 - });
141 -
142 - it('should handle a simple flow (ssr)', () => {
143 - class Component extends React.Component {
144 - render() {
145 - return <span>{this.props.children}</span>;
146 - }
147 - }
148 -
149 - var container = document.createElement('div');
150 - var markup = ReactDOMServer.renderToString(
151 - <div className="blue" style={{fontFamily: 'Helvetica'}}>
152 - <Component key={1}>A</Component>
153 - <Component key={2}>B</Component>
154 - <Component key={3}>C</Component>
155 - </div>,
156 - container,
157 - );
158 - container.innerHTML = markup;
159 - var inst = container.firstChild;
160 -
161 - expect(inst.className).toBe('blue');
162 - expect(inst.style.fontFamily).toBe('Helvetica');
163 - expect(inst.textContent).toBe('ABC');
164 - });
165 -
166 - it('should call lifecycle methods', () => {
167 - var log = [];
168 - class Component extends React.Component {
169 - state = {y: 1};
170 - shouldComponentUpdate(nextProps, nextState) {
171 - log.push(['shouldComponentUpdate', nextProps, nextState]);
172 - return nextProps.x !== this.props.x || nextState.y !== this.state.y;
173 - }
174 - componentWillMount() {
175 - log.push(['componentWillMount']);
176 - }
177 - componentDidMount() {
178 - log.push(['componentDidMount']);
179 - }
180 - componentWillReceiveProps(nextProps) {
181 - log.push(['componentWillReceiveProps', nextProps]);
182 - }
183 - componentWillUpdate(nextProps, nextState) {
184 - log.push(['componentWillUpdate', nextProps, nextState]);
185 - }
186 - componentDidUpdate(prevProps, prevState) {
187 - log.push(['componentDidUpdate', prevProps, prevState]);
188 - }
189 - componentWillUnmount() {
190 - log.push(['componentWillUnmount']);
191 - }
192 - render() {
193 - log.push(['render']);
194 - return null;
195 - }
196 - }
197 -
198 - var container = document.createElement('div');
199 - var inst = ReactDOM.render(<Component x={1} />, container);
200 - expect(log).toEqual([
201 - ['componentWillMount'],
202 - ['render'],
203 - ['componentDidMount'],
204 - ]);
205 - log = [];
206 -
207 - inst.setState({y: 2});
208 - expect(log).toEqual([
209 - ['shouldComponentUpdate', {x: 1}, {y: 2}],
210 - ['componentWillUpdate', {x: 1}, {y: 2}],
211 - ['render'],
212 - ['componentDidUpdate', {x: 1}, {y: 1}],
213 - ]);
214 - log = [];
215 -
216 - inst.setState({y: 2});
217 - expect(log).toEqual([['shouldComponentUpdate', {x: 1}, {y: 2}]]);
218 - log = [];
219 -
220 - ReactDOM.render(<Component x={2} />, container);
221 - expect(log).toEqual([
222 - ['componentWillReceiveProps', {x: 2}],
223 - ['shouldComponentUpdate', {x: 2}, {y: 2}],
224 - ['componentWillUpdate', {x: 2}, {y: 2}],
225 - ['render'],
226 - ['componentDidUpdate', {x: 1}, {y: 2}],
227 - ]);
228 - log = [];
229 -
230 - ReactDOM.render(<Component x={2} />, container);
231 - expect(log).toEqual([
232 - ['componentWillReceiveProps', {x: 2}],
233 - ['shouldComponentUpdate', {x: 2}, {y: 2}],
234 - ]);
235 - log = [];
236 -
237 - ReactDOM.unmountComponentAtNode(container);
238 - expect(log).toEqual([['componentWillUnmount']]);
239 - });
240 -
241 - it('should throw with an error code in production', () => {
242 - const errorCode = 152;
243 - expect(function() {
244 - class Component extends React.Component {
245 - render() {
246 - return undefined;
247 - }
248 - }
249 -
250 - var container = document.createElement('div');
251 - ReactDOM.render(<Component />, container);
252 - }).toThrowError(
253 - `Minified React error #${errorCode}; visit ` +
254 - `http://facebook.github.io/react/docs/error-decoder.html?invariant=${
255 - errorCode
256 - }&args[]=Component` +
257 - ' for the full message or use the non-minified dev environment' +
258 - ' for full errors and additional helpful warnings.',
259 - );
260 - });
261 -
262 - // Regression test verifying that trying to access (missing) component stack doesn't crash.
263 - it('should throw on children for void elements', () => {
264 - const errorCode = 137;
265 - const container = document.createElement('div');
266 - expect(() =>
267 - ReactDOM.render(<input>children</input>, container),
268 - ).toThrowError(
269 - `Minified React error #${errorCode}; visit ` +
270 - `http://facebook.github.io/react/docs/error-decoder.html?invariant=${
271 - errorCode
272 - }&args[]=input&args[]=` +
273 - ' for the full message or use the non-minified dev environment' +
274 - ' for full errors and additional helpful warnings.',
275 - );
276 - expect(() =>
277 - ReactDOMServer.renderToString(<input>children</input>, container),
278 - ).toThrowError(
279 - `Minified React error #${errorCode}; visit ` +
280 - `http://facebook.github.io/react/docs/error-decoder.html?invariant=${
281 - errorCode
282 - }&args[]=input&args[]=` +
283 - ' for the full message or use the non-minified dev environment' +
284 - ' for full errors and additional helpful warnings.',
285 - );
286 - });
287 -
288 - it('should not crash with devtools installed', () => {
289 - try {
290 - global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {
291 - inject: function() {},
292 - onCommitFiberRoot: function() {},
293 - onCommitFiberUnmount: function() {},
294 - supportsFiber: true,
295 - };
296 - jest.resetModules();
297 - React = require('react');
298 - ReactDOM = require('react-dom');
299 - class Component extends React.Component {
300 - render() {
301 - return <div />;
302 - }
303 - }
304 - ReactDOM.render(<Component />, document.createElement('container'));
305 - } finally {
306 - global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = undefined;
307 - }
308 - });
309 -
310 - // This test is originally from ReactDOMFiber-test but we replicate it here
311 - // to avoid production-only regressions because of host context differences
312 - // in dev and prod.
313 - it('should keep track of namespace across portals in production', () => {
314 - var svgEls, htmlEls;
315 - var expectSVG = {ref: el => svgEls.push(el)};
316 - var expectHTML = {ref: el => htmlEls.push(el)};
317 - var usePortal = function(tree) {
318 - return ReactDOM.createPortal(tree, document.createElement('div'));
319 - };
320 - var assertNamespacesMatch = function(tree) {
321 - var container = document.createElement('div');
322 - svgEls = [];
323 - htmlEls = [];
324 - ReactDOM.render(tree, container);
325 - svgEls.forEach(el => {
326 - expect(el.namespaceURI).toBe('http://www.w3.org/2000/svg');
327 - });
328 - htmlEls.forEach(el => {
329 - expect(el.namespaceURI).toBe('http://www.w3.org/1999/xhtml');
330 - });
331 - ReactDOM.unmountComponentAtNode(container);
332 - expect(container.innerHTML).toBe('');
333 - };
334 -
335 - assertNamespacesMatch(
336 - <div {...expectHTML}>
337 - <svg {...expectSVG}>
338 - <foreignObject {...expectSVG}>
339 - <p {...expectHTML} />
340 - {usePortal(
341 - <svg {...expectSVG}>
342 - <image {...expectSVG} />
343 - <svg {...expectSVG}>
344 - <image {...expectSVG} />
345 - <foreignObject {...expectSVG}>
346 - <p {...expectHTML} />
347 - </foreignObject>
348 - {usePortal(<p {...expectHTML} />)}
349 - </svg>
350 - <image {...expectSVG} />
351 - </svg>,
352 - )}
353 - <p {...expectHTML} />
354 - </foreignObject>
355 - <image {...expectSVG} />
356 - </svg>
357 - <p {...expectHTML} />
358 - </div>,
359 - );
360 - });
361 -});
packages/react-dom/src/__tests__/ReactDOMRoot-test.js
+9 -5
@@ -46,7 +46,7 @@ describe('ReactDOMRoot', () => {
46 ),
47 );
48
49 - spyOn(console, 'error');
49 + spyOnDev(console, 'error');
50
51 // Does not hydrate by default
52 const container1 = document.createElement('div');
@@ -57,7 +57,9 @@ describe('ReactDOMRoot', () => {
57 <span />
58 </div>,
59 );
60 - expect(console.error.calls.count()).toBe(0);
60 + if (__DEV__) {
61 + expect(console.error.calls.count()).toBe(0);
62 + }
63
64 // Accepts `hydrate` option
65 const container2 = document.createElement('div');
@@ -68,12 +70,14 @@ describe('ReactDOMRoot', () => {
70 <span />
71 </div>,
72 );
71 - expect(console.error.calls.count()).toBe(1);
72 - expect(console.error.calls.argsFor(0)[0]).toMatch('Extra attributes');
73 + if (__DEV__) {
74 + expect(console.error.calls.count()).toBe(1);
75 + expect(console.error.calls.argsFor(0)[0]).toMatch('Extra attributes');
76 + }
77 });
78
79 it('does not clear existing children', async () => {
76 - spyOn(console, 'error');
80 + spyOnDev(console, 'error');
81 container.innerHTML = '<div>a</div><div>b</div>';
82 const root = ReactDOM.createRoot(container);
83 root.render(
packages/react-dom/src/__tests__/ReactDOMSelect-test.js
+37 -25
@@ -532,49 +532,57 @@ describe('ReactDOMSelect', () => {
532 });
533
534 it('should warn if value is null', () => {
535 - spyOn(console, 'error');
535 + spyOnDev(console, 'error');
536
537 ReactTestUtils.renderIntoDocument(
538 <select value={null}>
539 <option value="test" />
540 </select>,
541 );
542 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
543 - '`value` prop on `select` should not be null. ' +
544 - 'Consider using an empty string to clear the component or `undefined` ' +
545 - 'for uncontrolled components.',
546 - );
542 + if (__DEV__) {
543 + expect(console.error.calls.argsFor(0)[0]).toContain(
544 + '`value` prop on `select` should not be null. ' +
545 + 'Consider using an empty string to clear the component or `undefined` ' +
546 + 'for uncontrolled components.',
547 + );
548 + }
549
550 ReactTestUtils.renderIntoDocument(
551 <select value={null}>
552 <option value="test" />
553 </select>,
554 );
553 - expectDev(console.error.calls.count()).toBe(1);
555 + if (__DEV__) {
556 + expect(console.error.calls.count()).toBe(1);
557 + }
558 });
559
560 it('should warn if value is null and multiple is true', () => {
557 - spyOn(console, 'error');
561 + spyOnDev(console, 'error');
562 ReactTestUtils.renderIntoDocument(
563 <select value={null} multiple={true}>
564 <option value="test" />
565 </select>,
566 );
567
564 - expectDev(console.error.calls.count()).toBe(1);
565 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
566 - '`value` prop on `select` should not be null. ' +
567 - 'Consider using an empty array when `multiple` is ' +
568 - 'set to `true` to clear the component or `undefined` ' +
569 - 'for uncontrolled components.',
570 - );
568 + if (__DEV__) {
569 + expect(console.error.calls.count()).toBe(1);
570 + expect(console.error.calls.argsFor(0)[0]).toContain(
571 + '`value` prop on `select` should not be null. ' +
572 + 'Consider using an empty array when `multiple` is ' +
573 + 'set to `true` to clear the component or `undefined` ' +
574 + 'for uncontrolled components.',
575 + );
576 + }
577
578 ReactTestUtils.renderIntoDocument(
579 <select value={null} multiple={true}>
580 <option value="test" />
581 </select>,
582 );
577 - expectDev(console.error.calls.count()).toBe(1);
583 + if (__DEV__) {
584 + expect(console.error.calls.count()).toBe(1);
585 + }
586 });
587
588 it('should refresh state on change', () => {
@@ -594,7 +602,7 @@ describe('ReactDOMSelect', () => {
602 });
603
604 it('should warn if value and defaultValue props are specified', () => {
597 - spyOn(console, 'error');
605 + spyOnDev(console, 'error');
606 ReactTestUtils.renderIntoDocument(
607 <select value="giraffe" defaultValue="giraffe" readOnly={true}>
608 <option value="monkey">A monkey!</option>
@@ -602,13 +610,15 @@ describe('ReactDOMSelect', () => {
610 <option value="gorilla">A gorilla!</option>
611 </select>,
612 );
605 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
606 - 'Select elements must be either controlled or uncontrolled ' +
607 - '(specify either the value prop, or the defaultValue prop, but not ' +
608 - 'both). Decide between using a controlled or uncontrolled select ' +
609 - 'element and remove one of these props. More info: ' +
610 - 'https://fb.me/react-controlled-components',
611 - );
613 + if (__DEV__) {
614 + expect(console.error.calls.argsFor(0)[0]).toContain(
615 + 'Select elements must be either controlled or uncontrolled ' +
616 + '(specify either the value prop, or the defaultValue prop, but not ' +
617 + 'both). Decide between using a controlled or uncontrolled select ' +
618 + 'element and remove one of these props. More info: ' +
619 + 'https://fb.me/react-controlled-components',
620 + );
621 + }
622
623 ReactTestUtils.renderIntoDocument(
624 <select value="giraffe" defaultValue="giraffe" readOnly={true}>
@@ -617,7 +627,9 @@ describe('ReactDOMSelect', () => {
627 <option value="gorilla">A gorilla!</option>
628 </select>,
629 );
620 - expectDev(console.error.calls.count()).toBe(1);
630 + if (__DEV__) {
631 + expect(console.error.calls.count()).toBe(1);
632 + }
633 });
634
635 it('should be able to safely remove select onChange', () => {
packages/react-dom/src/__tests__/ReactDOMServerIntegration-test.js
+39 -26
@@ -42,11 +42,12 @@ async function expectErrors(fn, count) {
42 if (console.error.calls && console.error.calls.reset) {
43 console.error.calls.reset();
44 } else {
45 - spyOn(console, 'error');
45 + spyOnDev(console, 'error');
46 }
47
48 const result = await fn();
49 if (
50 + console.error.calls &&
51 console.error.calls.count() !== count &&
52 console.error.calls.count() !== 0
53 ) {
@@ -62,7 +63,9 @@ async function expectErrors(fn, count) {
63 }
64 }
65 }
65 - expectDev(console.error.calls.count()).toBe(count);
66 + if (__DEV__) {
67 + expect(console.error.calls.count()).toBe(count);
68 + }
69 return result;
70 }
71
@@ -1856,9 +1859,11 @@ describe('ReactDOMServerIntegration', () => {
1859 const ObjectComponent = () => ({x: 123});
1860 await render(<ObjectComponent />, 1);
1861 },
1859 - 'Objects are not valid as a React child (found: object with keys ' +
1860 - '{x}). If you meant to render a collection of children, use ' +
1861 - 'an array instead.',
1862 + 'Objects are not valid as a React child (found: object with keys {x}).' +
1863 + (__DEV__
1864 + ? ' If you meant to render a collection of children, use ' +
1865 + 'an array instead.'
1866 + : ''),
1867 );
1868
1869 itThrowsWhenRendering(
@@ -1871,9 +1876,11 @@ describe('ReactDOMServerIntegration', () => {
1876 }
1877 await render(<ObjectComponent />, 1);
1878 },
1874 - 'Objects are not valid as a React child (found: object with keys ' +
1875 - '{x}). If you meant to render a collection of children, use ' +
1876 - 'an array instead.',
1879 + 'Objects are not valid as a React child (found: object with keys {x}).' +
1880 + (__DEV__
1881 + ? ' If you meant to render a collection of children, use ' +
1882 + 'an array instead.'
1883 + : ''),
1884 );
1885
1886 itThrowsWhenRendering(
@@ -1881,9 +1888,11 @@ describe('ReactDOMServerIntegration', () => {
1888 async render => {
1889 await render({x: 123});
1890 },
1884 - 'Objects are not valid as a React child (found: object with keys ' +
1885 - '{x}). If you meant to render a collection of children, use ' +
1886 - 'an array instead.',
1891 + 'Objects are not valid as a React child (found: object with keys {x}).' +
1892 + (__DEV__
1893 + ? ' If you meant to render a collection of children, use ' +
1894 + 'an array instead.'
1895 + : ''),
1896 );
1897 });
1898 });
@@ -2662,22 +2671,26 @@ describe('ReactDOMServerIntegration', () => {
2671 },
2672 );
2673
2665 - itThrowsWhenRendering(
2666 - 'if getChildContext exists without childContextTypes',
2667 - render => {
2668 - class MyComponent extends React.Component {
2669 - render() {
2670 - return <div />;
2671 - }
2672 - getChildContext() {
2673 - return {foo: 'bar'};
2674 + // TODO: this being DEV-only is likely a bug.
2675 + // https://github.com/facebook/react/issues/11618
2676 + if (__DEV__) {
2677 + itThrowsWhenRendering(
2678 + 'if getChildContext exists without childContextTypes',
2679 + render => {
2680 + class MyComponent extends React.Component {
2681 + render() {
2682 + return <div />;
2683 + }
2684 + getChildContext() {
2685 + return {foo: 'bar'};
2686 + }
2687 }
2675 - }
2676 - return render(<MyComponent />);
2677 - },
2678 - 'MyComponent.getChildContext(): childContextTypes must be defined ' +
2679 - 'in order to use getChildContext().',
2680 - );
2688 + return render(<MyComponent />);
2689 + },
2690 + 'MyComponent.getChildContext(): childContextTypes must be defined ' +
2691 + 'in order to use getChildContext().',
2692 + );
2693 + }
2694
2695 itThrowsWhenRendering(
2696 'if getChildContext returns a value not in childContextTypes',
packages/react-dom/src/__tests__/ReactDOMTextarea-test.js
+48 -28
@@ -207,7 +207,7 @@ describe('ReactDOMTextarea', () => {
207
208 expect(node.value).toBe('0');
209
210 - spyOn(console, 'error'); // deprecation warning for `children` content
210 + spyOnDev(console, 'error'); // deprecation warning for `children` content
211
212 ReactDOM.render(<textarea>1</textarea>, container);
213
@@ -247,13 +247,15 @@ describe('ReactDOMTextarea', () => {
247 });
248
249 it('should treat children like `defaultValue`', () => {
250 - spyOn(console, 'error');
250 + spyOnDev(console, 'error');
251
252 var container = document.createElement('div');
253 var stub = <textarea>giraffe</textarea>;
254 var node = renderTextarea(stub, container);
255
256 - expectDev(console.error.calls.count()).toBe(1);
256 + if (__DEV__) {
257 + expect(console.error.calls.count()).toBe(1);
258 + }
259 expect(node.value).toBe('giraffe');
260
261 // Changing children should do nothing, it functions like `defaultValue`.
@@ -299,33 +301,39 @@ describe('ReactDOMTextarea', () => {
301 });
302
303 it('should allow numbers as children', () => {
302 - spyOn(console, 'error');
304 + spyOnDev(console, 'error');
305 var node = renderTextarea(<textarea>{17}</textarea>);
304 - expectDev(console.error.calls.count()).toBe(1);
306 + if (__DEV__) {
307 + expect(console.error.calls.count()).toBe(1);
308 + }
309 expect(node.value).toBe('17');
310 });
311
312 it('should allow booleans as children', () => {
309 - spyOn(console, 'error');
313 + spyOnDev(console, 'error');
314 var node = renderTextarea(<textarea>{false}</textarea>);
311 - expectDev(console.error.calls.count()).toBe(1);
315 + if (__DEV__) {
316 + expect(console.error.calls.count()).toBe(1);
317 + }
318 expect(node.value).toBe('false');
319 });
320
321 it('should allow objects as children', () => {
316 - spyOn(console, 'error');
322 + spyOnDev(console, 'error');
323 var obj = {
324 toString: function() {
325 return 'sharkswithlasers';
326 },
327 };
328 var node = renderTextarea(<textarea>{obj}</textarea>);
323 - expectDev(console.error.calls.count()).toBe(1);
329 + if (__DEV__) {
330 + expect(console.error.calls.count()).toBe(1);
331 + }
332 expect(node.value).toBe('sharkswithlasers');
333 });
334
335 it('should throw with multiple or invalid children', () => {
328 - spyOn(console, 'error');
336 + spyOnDev(console, 'error');
337
338 expect(function() {
339 ReactTestUtils.renderIntoDocument(
@@ -336,7 +344,9 @@ describe('ReactDOMTextarea', () => {
344 );
345 }).toThrow();
346
339 - expectDev(console.error.calls.count()).toBe(1);
347 + if (__DEV__) {
348 + expect(console.error.calls.count()).toBe(1);
349 + }
350
351 var node;
352 expect(function() {
@@ -349,7 +359,9 @@ describe('ReactDOMTextarea', () => {
359
360 expect(node.value).toBe('[object Object]');
361
352 - expectDev(console.error.calls.count()).toBe(2);
362 + if (__DEV__) {
363 + expect(console.error.calls.count()).toBe(2);
364 + }
365 });
366
367 it('should unmount', () => {
@@ -359,35 +371,43 @@ describe('ReactDOMTextarea', () => {
371 });
372
373 it('should warn if value is null', () => {
362 - spyOn(console, 'error');
374 + spyOnDev(console, 'error');
375
376 ReactTestUtils.renderIntoDocument(<textarea value={null} />);
365 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
366 - '`value` prop on `textarea` should not be null. ' +
367 - 'Consider using an empty string to clear the component or `undefined` ' +
368 - 'for uncontrolled components.',
369 - );
377 + if (__DEV__) {
378 + expect(console.error.calls.argsFor(0)[0]).toContain(
379 + '`value` prop on `textarea` should not be null. ' +
380 + 'Consider using an empty string to clear the component or `undefined` ' +
381 + 'for uncontrolled components.',
382 + );
383 + }
384
385 ReactTestUtils.renderIntoDocument(<textarea value={null} />);
372 - expectDev(console.error.calls.count()).toBe(1);
386 + if (__DEV__) {
387 + expect(console.error.calls.count()).toBe(1);
388 + }
389 });
390
391 it('should warn if value and defaultValue are specified', () => {
376 - spyOn(console, 'error');
392 + spyOnDev(console, 'error');
393 ReactTestUtils.renderIntoDocument(
394 <textarea value="foo" defaultValue="bar" readOnly={true} />,
395 );
380 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
381 - 'Textarea elements must be either controlled or uncontrolled ' +
382 - '(specify either the value prop, or the defaultValue prop, but not ' +
383 - 'both). Decide between using a controlled or uncontrolled textarea ' +
384 - 'and remove one of these props. More info: ' +
385 - 'https://fb.me/react-controlled-components',
386 - );
396 + if (__DEV__) {
397 + expect(console.error.calls.argsFor(0)[0]).toContain(
398 + 'Textarea elements must be either controlled or uncontrolled ' +
399 + '(specify either the value prop, or the defaultValue prop, but not ' +
400 + 'both). Decide between using a controlled or uncontrolled textarea ' +
401 + 'and remove one of these props. More info: ' +
402 + 'https://fb.me/react-controlled-components',
403 + );
404 + }
405
406 ReactTestUtils.renderIntoDocument(
407 <textarea value="foo" defaultValue="bar" readOnly={true} />,
408 );
391 - expectDev(console.error.calls.count()).toBe(1);
409 + if (__DEV__) {
410 + expect(console.error.calls.count()).toBe(1);
411 + }
412 });
413 });
packages/react-dom/src/__tests__/ReactErrorBoundaries-test.js
+1 -1
@@ -1909,7 +1909,7 @@ describe('ReactErrorBoundaries', () => {
1909 });
1910
1911 it('discards a bad root if the root component fails', () => {
1912 - spyOn(console, 'error');
1912 + spyOnDev(console, 'error');
1913
1914 const X = null;
1915 const Y = undefined;
packages/react-dom/src/__tests__/ReactMockedComponent-test.js
+31 -7
@@ -30,18 +30,42 @@ describe('ReactMockedComponent', () => {
30 MockedComponent.prototype.render = jest.fn();
31 });
32
33 - it('should allow a mocked component to be rendered', () => {
33 + it('should allow a mocked component to be rendered in dev', () => {
34 var container = document.createElement('container');
35 - ReactDOM.render(<MockedComponent />, container);
35 + if (__DEV__) {
36 + ReactDOM.render(<MockedComponent />, container);
37 + } else {
38 + expect(() => ReactDOM.render(<MockedComponent />, container)).toThrow(
39 + 'Nothing was returned from render.',
40 + );
41 + }
42 });
43
38 - it('should allow a mocked component to be updated', () => {
44 + it('should allow a mocked component to be updated in dev', () => {
45 var container = document.createElement('container');
40 - ReactDOM.render(<MockedComponent />, container);
41 - ReactDOM.render(<MockedComponent />, container);
46 + if (__DEV__) {
47 + ReactDOM.render(<MockedComponent />, container);
48 + } else {
49 + expect(() => ReactDOM.render(<MockedComponent />, container)).toThrow(
50 + 'Nothing was returned from render.',
51 + );
52 + }
53 + if (__DEV__) {
54 + ReactDOM.render(<MockedComponent />, container);
55 + } else {
56 + expect(() => ReactDOM.render(<MockedComponent />, container)).toThrow(
57 + 'Nothing was returned from render.',
58 + );
59 + }
60 });
61
44 - it('should allow a mocked component to be rendered (SSR)', () => {
45 - ReactDOMServer.renderToString(<MockedComponent />);
62 + it('should allow a mocked component to be rendered in dev (SSR)', () => {
63 + if (__DEV__) {
64 + ReactDOMServer.renderToString(<MockedComponent />);
65 + } else {
66 + expect(() => ReactDOMServer.renderToString(<MockedComponent />)).toThrow(
67 + 'Nothing was returned from render.',
68 + );
69 + }
70 });
71 });
packages/react-dom/src/__tests__/ReactMount-test.js
+63 -45
@@ -55,7 +55,7 @@ describe('ReactMount', () => {
55 });
56
57 it('warns when given a factory', () => {
58 - spyOn(console, 'error');
58 + spyOnDev(console, 'error');
59 class Component extends React.Component {
60 render() {
61 return <div />;
@@ -63,12 +63,14 @@ describe('ReactMount', () => {
63 }
64
65 ReactTestUtils.renderIntoDocument(Component);
66 - expectDev(console.error.calls.count()).toBe(1);
67 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
68 - 'Functions are not valid as a React child. ' +
69 - 'This may happen if you return a Component instead of <Component /> from render. ' +
70 - 'Or maybe you meant to call this function rather than return it.',
71 - );
66 + if (__DEV__) {
67 + expect(console.error.calls.count()).toBe(1);
68 + expect(console.error.calls.argsFor(0)[0]).toContain(
69 + 'Functions are not valid as a React child. ' +
70 + 'This may happen if you return a Component instead of <Component /> from render. ' +
71 + 'Or maybe you meant to call this function rather than return it.',
72 + );
73 + }
74 });
75
76 it('should render different components in same root', () => {
@@ -129,46 +131,54 @@ describe('ReactMount', () => {
131 var container = document.createElement('container');
132 container.innerHTML = ReactDOMServer.renderToString(<div />) + ' ';
133
132 - spyOn(console, 'error');
134 + spyOnDev(console, 'error');
135 ReactDOM.hydrate(<div />, container);
134 - expectDev(console.error.calls.count()).toBe(1);
135 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
136 - 'Did not expect server HTML to contain the text node " " in <container>.',
137 - );
136 + if (__DEV__) {
137 + expect(console.error.calls.count()).toBe(1);
138 + expect(console.error.calls.argsFor(0)[0]).toContain(
139 + 'Did not expect server HTML to contain the text node " " in <container>.',
140 + );
141 + }
142 });
143
144 it('should warn if mounting into right padded rendered markup', () => {
145 var container = document.createElement('container');
146 container.innerHTML = ' ' + ReactDOMServer.renderToString(<div />);
147
144 - spyOn(console, 'error');
148 + spyOnDev(console, 'error');
149 ReactDOM.hydrate(<div />, container);
146 - expectDev(console.error.calls.count()).toBe(1);
147 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
148 - 'Did not expect server HTML to contain the text node " " in <container>.',
149 - );
150 + if (__DEV__) {
151 + expect(console.error.calls.count()).toBe(1);
152 + expect(console.error.calls.argsFor(0)[0]).toContain(
153 + 'Did not expect server HTML to contain the text node " " in <container>.',
154 + );
155 + }
156 });
157
158 it('should not warn if mounting into non-empty node', () => {
159 var container = document.createElement('container');
160 container.innerHTML = '<div></div>';
161
156 - spyOn(console, 'error');
162 + spyOnDev(console, 'error');
163 ReactDOM.render(<div />, container);
158 - expectDev(console.error.calls.count()).toBe(0);
164 + if (__DEV__) {
165 + expect(console.error.calls.count()).toBe(0);
166 + }
167 });
168
169 it('should warn when mounting into document.body', () => {
170 var iFrame = document.createElement('iframe');
171 document.body.appendChild(iFrame);
164 - spyOn(console, 'error');
172 + spyOnDev(console, 'error');
173
174 ReactDOM.render(<div />, iFrame.contentDocument.body);
175
168 - expectDev(console.error.calls.count()).toBe(1);
169 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
170 - 'Rendering components directly into document.body is discouraged',
171 - );
176 + if (__DEV__) {
177 + expect(console.error.calls.count()).toBe(1);
178 + expect(console.error.calls.argsFor(0)[0]).toContain(
179 + 'Rendering components directly into document.body is discouraged',
180 + );
181 + }
182 });
183
184 it('should account for escaping on a checksum mismatch', () => {
@@ -178,16 +188,18 @@ describe('ReactMount', () => {
188 );
189 div.innerHTML = markup;
190
181 - spyOn(console, 'error');
191 + spyOnDev(console, 'error');
192 ReactDOM.hydrate(
193 <div>This markup contains an nbsp entity: &nbsp; client text</div>,
194 div,
195 );
186 - expectDev(console.error.calls.count()).toBe(1);
187 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
188 - 'Server: "This markup contains an nbsp entity:   server text" ' +
189 - 'Client: "This markup contains an nbsp entity:   client text"',
190 - );
196 + if (__DEV__) {
197 + expect(console.error.calls.count()).toBe(1);
198 + expect(console.error.calls.argsFor(0)[0]).toContain(
199 + 'Server: "This markup contains an nbsp entity:   server text" ' +
200 + 'Client: "This markup contains an nbsp entity:   client text"',
201 + );
202 + }
203 });
204
205 it('should warn if render removes React-rendered children', () => {
@@ -206,16 +218,18 @@ describe('ReactMount', () => {
218 ReactDOM.render(<Component />, container);
219
220 // Test that blasting away children throws a warning
209 - spyOn(console, 'error');
221 + spyOnDev(console, 'error');
222 var rootNode = container.firstChild;
223 ReactDOM.render(<span />, rootNode);
212 - expectDev(console.error.calls.count()).toBe(1);
213 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
214 - 'Warning: render(...): Replacing React-rendered children with a new ' +
215 - 'root component. If you intended to update the children of this node, ' +
216 - 'you should instead have the existing children update their state and ' +
217 - 'render the new components instead of calling ReactDOM.render.',
218 - );
224 + if (__DEV__) {
225 + expect(console.error.calls.count()).toBe(1);
226 + expect(console.error.calls.argsFor(0)[0]).toBe(
227 + 'Warning: render(...): Replacing React-rendered children with a new ' +
228 + 'root component. If you intended to update the children of this node, ' +
229 + 'you should instead have the existing children update their state and ' +
230 + 'render the new components instead of calling ReactDOM.render.',
231 + );
232 + }
233 });
234
235 it('should warn if the unmounted node was rendered by another copy of React', () => {
@@ -237,17 +251,21 @@ describe('ReactMount', () => {
251 // Make sure ReactDOM and ReactDOMOther are different copies
252 expect(ReactDOM).not.toEqual(ReactDOMOther);
253
240 - spyOn(console, 'error');
254 + spyOnDev(console, 'error');
255 ReactDOMOther.unmountComponentAtNode(container);
242 - expectDev(console.error.calls.count()).toBe(1);
243 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
244 - "Warning: unmountComponentAtNode(): The node you're attempting to unmount " +
245 - 'was rendered by another copy of React.',
246 - );
256 + if (__DEV__) {
257 + expect(console.error.calls.count()).toBe(1);
258 + expect(console.error.calls.argsFor(0)[0]).toBe(
259 + "Warning: unmountComponentAtNode(): The node you're attempting to unmount " +
260 + 'was rendered by another copy of React.',
261 + );
262 + }
263
264 // Don't throw a warning if the correct React copy unmounts the node
265 ReactDOM.unmountComponentAtNode(container);
250 - expectDev(console.error.calls.count()).toBe(1);
266 + if (__DEV__) {
267 + expect(console.error.calls.count()).toBe(1);
268 + }
269 });
270
271 it('passes the correct callback context', () => {
packages/react-dom/src/__tests__/ReactMountDestruction-test.js
+20 -16
@@ -50,15 +50,17 @@ describe('ReactMount', () => {
50
51 // Test that unmounting at a root node gives a helpful warning
52 var rootDiv = mainContainerDiv.firstChild;
53 - spyOn(console, 'error');
53 + spyOnDev(console, 'error');
54 ReactDOM.unmountComponentAtNode(rootDiv);
55 - expectDev(console.error.calls.count()).toBe(1);
56 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
57 - "Warning: unmountComponentAtNode(): The node you're attempting to " +
58 - 'unmount was rendered by React and is not a top-level container. You ' +
59 - 'may have accidentally passed in a React root node instead of its ' +
60 - 'container.',
61 - );
55 + if (__DEV__) {
56 + expect(console.error.calls.count()).toBe(1);
57 + expect(console.error.calls.argsFor(0)[0]).toBe(
58 + "Warning: unmountComponentAtNode(): The node you're attempting to " +
59 + 'unmount was rendered by React and is not a top-level container. You ' +
60 + 'may have accidentally passed in a React root node instead of its ' +
61 + 'container.',
62 + );
63 + }
64 });
65
66 it('should warn when unmounting a non-container, non-root node', () => {
@@ -75,14 +77,16 @@ describe('ReactMount', () => {
77
78 // Test that unmounting at a non-root node gives a different warning
79 var nonRootDiv = mainContainerDiv.firstChild.firstChild;
78 - spyOn(console, 'error');
80 + spyOnDev(console, 'error');
81 ReactDOM.unmountComponentAtNode(nonRootDiv);
80 - expectDev(console.error.calls.count()).toBe(1);
81 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
82 - "Warning: unmountComponentAtNode(): The node you're attempting to " +
83 - 'unmount was rendered by React and is not a top-level container. ' +
84 - 'Instead, have the parent component update its state and rerender in ' +
85 - 'order to remove this component.',
86 - );
82 + if (__DEV__) {
83 + expect(console.error.calls.count()).toBe(1);
84 + expect(console.error.calls.argsFor(0)[0]).toBe(
85 + "Warning: unmountComponentAtNode(): The node you're attempting to " +
86 + 'unmount was rendered by React and is not a top-level container. ' +
87 + 'Instead, have the parent component update its state and rerender in ' +
88 + 'order to remove this component.',
89 + );
90 + }
91 });
92 });
packages/react-dom/src/__tests__/ReactMultiChild-test.js
+44 -38
@@ -183,7 +183,7 @@ describe('ReactMultiChild', () => {
183 });
184
185 it('should warn for duplicated array keys with component stack info', () => {
186 - spyOn(console, 'error');
186 + spyOnDev(console, 'error');
187
188 var container = document.createElement('div');
189
@@ -210,24 +210,26 @@ describe('ReactMultiChild', () => {
210 container,
211 );
212
213 - expectDev(console.error.calls.count()).toBe(1);
214 - expectDev(
215 - normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
216 - ).toContain(
217 - 'Encountered two children with the same key, `1`. ' +
218 - 'Keys should be unique so that components maintain their identity ' +
219 - 'across updates. Non-unique keys may cause children to be ' +
220 - 'duplicated and/or omitted — the behavior is unsupported and ' +
221 - 'could change in a future version.',
222 - ' in div (at **)\n' +
223 - ' in WrapperComponent (at **)\n' +
213 + if (__DEV__) {
214 + expect(console.error.calls.count()).toBe(1);
215 + expect(
216 + normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
217 + ).toContain(
218 + 'Encountered two children with the same key, `1`. ' +
219 + 'Keys should be unique so that components maintain their identity ' +
220 + 'across updates. Non-unique keys may cause children to be ' +
221 + 'duplicated and/or omitted — the behavior is unsupported and ' +
222 + 'could change in a future version.',
223 ' in div (at **)\n' +
225 - ' in Parent (at **)',
226 - );
224 + ' in WrapperComponent (at **)\n' +
225 + ' in div (at **)\n' +
226 + ' in Parent (at **)',
227 + );
228 + }
229 });
230
231 it('should warn for duplicated iterable keys with component stack info', () => {
230 - spyOn(console, 'error');
232 + spyOnDev(console, 'error');
233
234 var container = document.createElement('div');
235
@@ -275,25 +277,27 @@ describe('ReactMultiChild', () => {
277 container,
278 );
279
278 - expectDev(console.error.calls.count()).toBe(1);
279 - expectDev(
280 - normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
281 - ).toContain(
282 - 'Encountered two children with the same key, `1`. ' +
283 - 'Keys should be unique so that components maintain their identity ' +
284 - 'across updates. Non-unique keys may cause children to be ' +
285 - 'duplicated and/or omitted — the behavior is unsupported and ' +
286 - 'could change in a future version.',
287 - ' in div (at **)\n' +
288 - ' in WrapperComponent (at **)\n' +
280 + if (__DEV__) {
281 + expect(console.error.calls.count()).toBe(1);
282 + expect(
283 + normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
284 + ).toContain(
285 + 'Encountered two children with the same key, `1`. ' +
286 + 'Keys should be unique so that components maintain their identity ' +
287 + 'across updates. Non-unique keys may cause children to be ' +
288 + 'duplicated and/or omitted — the behavior is unsupported and ' +
289 + 'could change in a future version.',
290 ' in div (at **)\n' +
290 - ' in Parent (at **)',
291 - );
291 + ' in WrapperComponent (at **)\n' +
292 + ' in div (at **)\n' +
293 + ' in Parent (at **)',
294 + );
295 + }
296 });
297 });
298
299 it('should warn for using maps as children with owner info', () => {
296 - spyOn(console, 'error');
300 + spyOnDev(console, 'error');
301 class Parent extends React.Component {
302 render() {
303 return <div>{new Map([['foo', 0], ['bar', 1]])}</div>;
@@ -301,18 +305,20 @@ describe('ReactMultiChild', () => {
305 }
306 var container = document.createElement('div');
307 ReactDOM.render(<Parent />, container);
304 - expectDev(console.error.calls.count()).toBe(1);
305 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
306 - 'Warning: Using Maps as children is unsupported and will likely yield ' +
307 - 'unexpected results. Convert it to a sequence/iterable of keyed ' +
308 - 'ReactElements instead.\n' +
309 - ' in div (at **)\n' +
310 - ' in Parent (at **)',
311 - );
308 + if (__DEV__) {
309 + expect(console.error.calls.count()).toBe(1);
310 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
311 + 'Warning: Using Maps as children is unsupported and will likely yield ' +
312 + 'unexpected results. Convert it to a sequence/iterable of keyed ' +
313 + 'ReactElements instead.\n' +
314 + ' in div (at **)\n' +
315 + ' in Parent (at **)',
316 + );
317 + }
318 });
319
320 it('should reorder bailed-out children', () => {
315 - spyOn(console, 'error');
321 + spyOnDev(console, 'error');
322
323 class LetterInner extends React.Component {
324 render() {
packages/react-dom/src/__tests__/ReactMultiChildText-test.js
+10 -8
@@ -73,7 +73,7 @@ var expectChildren = function(container, children) {
73 */
74 describe('ReactMultiChildText', () => {
75 it('should correctly handle all possible children for render and update', () => {
76 - spyOn(console, 'error');
76 + spyOnDev(console, 'error');
77 // prettier-ignore
78 testAllPermutations([
79 // basic values
@@ -162,13 +162,15 @@ describe('ReactMultiChildText', () => {
162 ['', 'foo', <div>{true}{<div />}{1.2}{''}</div>, 'foo'], ['', 'foo', <div />, 'foo'],
163 ]);
164
165 - expectDev(console.error.calls.count()).toBe(2);
166 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
167 - 'Warning: Each child in an array or iterator should have a unique "key" prop.',
168 - );
169 - expectDev(console.error.calls.argsFor(1)[0]).toContain(
170 - 'Warning: Each child in an array or iterator should have a unique "key" prop.',
171 - );
165 + if (__DEV__) {
166 + expect(console.error.calls.count()).toBe(2);
167 + expect(console.error.calls.argsFor(0)[0]).toContain(
168 + 'Warning: Each child in an array or iterator should have a unique "key" prop.',
169 + );
170 + expect(console.error.calls.argsFor(1)[0]).toContain(
171 + 'Warning: Each child in an array or iterator should have a unique "key" prop.',
172 + );
173 + }
174 });
175
176 it('should throw if rendering both HTML and children', () => {
packages/react-dom/src/__tests__/ReactRenderDocument-test.js
+49 -39
@@ -35,16 +35,18 @@ describe('rendering React components at document', () => {
35
36 describe('with old implicit hydration API', () => {
37 function expectDeprecationWarningWithFiber() {
38 - expectDev(console.warn.calls.count()).toBe(1);
39 - expectDev(console.warn.calls.argsFor(0)[0]).toContain(
40 - 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
41 - 'will stop working in React v17. Replace the ReactDOM.render() call ' +
42 - 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
43 - );
38 + if (__DEV__) {
39 + expect(console.warn.calls.count()).toBe(1);
40 + expect(console.warn.calls.argsFor(0)[0]).toContain(
41 + 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
42 + 'will stop working in React v17. Replace the ReactDOM.render() call ' +
43 + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
44 + );
45 + }
46 }
47
48 it('should be able to adopt server markup', () => {
47 - spyOn(console, 'warn');
49 + spyOnDev(console, 'warn');
50 class Root extends React.Component {
51 render() {
52 return (
@@ -73,7 +75,7 @@ describe('rendering React components at document', () => {
75 });
76
77 it('should not be able to unmount component from document node', () => {
76 - spyOn(console, 'warn');
78 + spyOnDev(console, 'warn');
79 class Root extends React.Component {
80 render() {
81 return (
@@ -100,7 +102,7 @@ describe('rendering React components at document', () => {
102 });
103
104 it('should not be able to switch root constructors', () => {
103 - spyOn(console, 'warn');
105 + spyOnDev(console, 'warn');
106 class Component extends React.Component {
107 render() {
108 return (
@@ -141,7 +143,7 @@ describe('rendering React components at document', () => {
143 });
144
145 it('should be able to mount into document', () => {
144 - spyOn(console, 'warn');
146 + spyOnDev(console, 'warn');
147 class Component extends React.Component {
148 render() {
149 return (
@@ -195,20 +197,22 @@ describe('rendering React components at document', () => {
197 );
198 var testDocument = getTestDocument(markup);
199
198 - spyOn(console, 'warn');
199 - spyOn(console, 'error');
200 + spyOnDev(console, 'warn');
201 + spyOnDev(console, 'error');
202 ReactDOM.render(<Component text="Hello world" />, testDocument);
203 expect(testDocument.body.innerHTML).toBe('Hello world');
202 - expectDev(console.warn.calls.count()).toBe(1);
203 - expectDev(console.warn.calls.argsFor(0)[0]).toContain(
204 - 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
205 - 'will stop working in React v17. Replace the ReactDOM.render() call ' +
206 - 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
207 - );
208 - expectDev(console.error.calls.count()).toBe(1);
209 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
210 - 'Warning: Text content did not match.',
211 - );
204 + if (__DEV__) {
205 + expect(console.warn.calls.count()).toBe(1);
206 + expect(console.warn.calls.argsFor(0)[0]).toContain(
207 + 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
208 + 'will stop working in React v17. Replace the ReactDOM.render() call ' +
209 + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
210 + );
211 + expect(console.error.calls.count()).toBe(1);
212 + expect(console.error.calls.argsFor(0)[0]).toContain(
213 + 'Warning: Text content did not match.',
214 + );
215 + }
216 });
217
218 it('should throw on full document render w/ no markup', () => {
@@ -235,7 +239,7 @@ describe('rendering React components at document', () => {
239 });
240
241 it('supports findDOMNode on full-page components', () => {
238 - spyOn(console, 'warn');
242 + spyOnDev(console, 'warn');
243 var tree = (
244 <html>
245 <head>
@@ -371,15 +375,17 @@ describe('rendering React components at document', () => {
375 });
376
377 it('renders over an existing text child without throwing', () => {
374 - spyOn(console, 'error');
378 + spyOnDev(console, 'error');
379 const container = document.createElement('div');
380 container.textContent = 'potato';
381 ReactDOM.hydrate(<div>parsnip</div>, container);
382 expect(container.textContent).toBe('parsnip');
379 - expectDev(console.error.calls.count()).toBe(1);
380 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
381 - 'Expected server HTML to contain a matching <div> in <div>.',
382 - );
383 + if (__DEV__) {
384 + expect(console.error.calls.count()).toBe(1);
385 + expect(console.error.calls.argsFor(0)[0]).toContain(
386 + 'Expected server HTML to contain a matching <div> in <div>.',
387 + );
388 + }
389 });
390
391 it('should give helpful errors on state desync', () => {
@@ -401,17 +407,19 @@ describe('rendering React components at document', () => {
407 );
408 var testDocument = getTestDocument(markup);
409
404 - spyOn(console, 'error');
410 + spyOnDev(console, 'error');
411 ReactDOM.hydrate(<Component text="Hello world" />, testDocument);
412 expect(testDocument.body.innerHTML).toBe('Hello world');
407 - expectDev(console.error.calls.count()).toBe(1);
408 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
409 - 'Warning: Text content did not match.',
410 - );
413 + if (__DEV__) {
414 + expect(console.error.calls.count()).toBe(1);
415 + expect(console.error.calls.argsFor(0)[0]).toContain(
416 + 'Warning: Text content did not match.',
417 + );
418 + }
419 });
420
421 it('should render w/ no markup to full document', () => {
414 - spyOn(console, 'error');
422 + spyOnDev(console, 'error');
423 var testDocument = getTestDocument();
424
425 class Component extends React.Component {
@@ -429,11 +437,13 @@ describe('rendering React components at document', () => {
437
438 ReactDOM.hydrate(<Component text="Hello world" />, testDocument);
439 expect(testDocument.body.innerHTML).toBe('Hello world');
432 - expectDev(console.error.calls.count()).toBe(1);
433 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
434 - // getTestDocument() has an extra <meta> that we didn't render.
435 - 'Did not expect server HTML to contain a <meta> in <head>.',
436 - );
440 + if (__DEV__) {
441 + expect(console.error.calls.count()).toBe(1);
442 + expect(console.error.calls.argsFor(0)[0]).toContain(
443 + // getTestDocument() has an extra <meta> that we didn't render.
444 + 'Did not expect server HTML to contain a <meta> in <head>.',
445 + );
446 + }
447 });
448
449 it('supports findDOMNode on full-page components', () => {
packages/react-dom/src/__tests__/ReactServerRendering-test.js
+94 -64
@@ -183,8 +183,8 @@ describe('ReactDOMServer', () => {
183 });
184
185 it('should have the correct mounting behavior (old hydrate API)', () => {
186 - spyOn(console, 'warn');
187 - spyOn(console, 'error');
186 + spyOnDev(console, 'warn');
187 + spyOnDev(console, 'error');
188 // This test is testing client-side behavior.
189 ExecutionEnvironment.canUseDOM = true;
190
@@ -241,13 +241,15 @@ describe('ReactDOMServer', () => {
241
242 var instance = ReactDOM.render(<TestComponent name="x" />, element);
243 expect(mountCount).toEqual(3);
244 - expectDev(console.warn.calls.count()).toBe(1);
245 - expectDev(console.warn.calls.argsFor(0)[0]).toContain(
246 - 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
247 - 'will stop working in React v17. Replace the ReactDOM.render() call ' +
248 - 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
249 - );
250 - console.warn.calls.reset();
244 + if (__DEV__) {
245 + expect(console.warn.calls.count()).toBe(1);
246 + expect(console.warn.calls.argsFor(0)[0]).toContain(
247 + 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
248 + 'will stop working in React v17. Replace the ReactDOM.render() call ' +
249 + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
250 + );
251 + console.warn.calls.reset();
252 + }
253 expect(element.innerHTML).toBe(lastMarkup);
254
255 // Ensure the events system works after mount into server markup
@@ -263,11 +265,13 @@ describe('ReactDOMServer', () => {
265 element.innerHTML = lastMarkup;
266 instance = ReactDOM.render(<TestComponent name="y" />, element);
267 expect(mountCount).toEqual(4);
266 - expectDev(console.error.calls.count()).toBe(1);
267 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
268 - 'Text content did not match. Server: "x" Client: "y"',
269 - );
270 - console.error.calls.reset();
268 + if (__DEV__) {
269 + expect(console.error.calls.count()).toBe(1);
270 + expect(console.error.calls.argsFor(0)[0]).toContain(
271 + 'Text content did not match. Server: "x" Client: "y"',
272 + );
273 + console.error.calls.reset();
274 + }
275 expect(element.innerHTML.length > 0).toBe(true);
276 expect(element.innerHTML).not.toEqual(lastMarkup);
277
@@ -275,12 +279,14 @@ describe('ReactDOMServer', () => {
279 expect(numClicks).toEqual(1);
280 ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
281 expect(numClicks).toEqual(2);
278 - expectDev(console.warn.calls.count()).toBe(0);
279 - expectDev(console.error.calls.count()).toBe(0);
282 + if (__DEV__) {
283 + expect(console.warn.calls.count()).toBe(0);
284 + expect(console.error.calls.count()).toBe(0);
285 + }
286 });
287
288 it('should have the correct mounting behavior (new hydrate API)', () => {
283 - spyOn(console, 'error');
289 + spyOnDev(console, 'error');
290 // This test is testing client-side behavior.
291 ExecutionEnvironment.canUseDOM = true;
292
@@ -352,7 +358,9 @@ describe('ReactDOMServer', () => {
358 element.innerHTML = lastMarkup;
359 instance = ReactDOM.hydrate(<TestComponent name="y" />, element);
360 expect(mountCount).toEqual(4);
355 - expectDev(console.error.calls.count()).toBe(1);
361 + if (__DEV__) {
362 + expect(console.error.calls.count()).toBe(1);
363 + }
364 expect(element.innerHTML.length > 0).toBe(true);
365 expect(element.innerHTML).not.toEqual(lastMarkup);
366
@@ -416,7 +424,7 @@ describe('ReactDOMServer', () => {
424 expect(normalizeCodeLocInfo(caughtErr.message)).toContain(
425 'The `style` prop expects a mapping from style properties to values, not ' +
426 "a string. For example, style={{marginRight: spacing + 'em'}} when using JSX." +
419 - '\n in iframe (at **)',
427 + (__DEV__ ? '\n in iframe (at **)' : ''),
428 );
429 });
430 });
@@ -678,20 +686,24 @@ describe('ReactDOMServer', () => {
686 }
687 }
688
681 - spyOn(console, 'error');
689 + spyOnDev(console, 'error');
690 ReactDOMServer.renderToString(<Foo />);
691 jest.runOnlyPendingTimers();
684 - expectDev(console.error.calls.count()).toBe(1);
685 - expectDev(console.error.calls.mostRecent().args[0]).toBe(
686 - 'Warning: setState(...): Can only update a mounting component.' +
687 - ' This usually means you called setState() outside componentWillMount() on the server.' +
688 - ' This is a no-op.\n\nPlease check the code for the Foo component.',
689 - );
692 + if (__DEV__) {
693 + expect(console.error.calls.count()).toBe(1);
694 + expect(console.error.calls.mostRecent().args[0]).toBe(
695 + 'Warning: setState(...): Can only update a mounting component.' +
696 + ' This usually means you called setState() outside componentWillMount() on the server.' +
697 + ' This is a no-op.\n\nPlease check the code for the Foo component.',
698 + );
699 + }
700
701 var markup = ReactDOMServer.renderToStaticMarkup(<Foo />);
702 expect(markup).toBe('<div>hello</div>');
703 jest.runOnlyPendingTimers();
694 - expectDev(console.error.calls.count()).toBe(1);
704 + if (__DEV__) {
705 + expect(console.error.calls.count()).toBe(1);
706 + }
707 });
708
709 it('warns with a no-op when an async forceUpdate is triggered', () => {
@@ -708,38 +720,52 @@ describe('ReactDOMServer', () => {
720 }
721 }
722
711 - spyOn(console, 'error');
723 + spyOnDev(console, 'error');
724 ReactDOMServer.renderToString(<Baz />);
725 jest.runOnlyPendingTimers();
714 - expectDev(console.error.calls.count()).toBe(1);
715 - expectDev(console.error.calls.mostRecent().args[0]).toBe(
716 - 'Warning: forceUpdate(...): Can only update a mounting component. ' +
717 - 'This usually means you called forceUpdate() outside componentWillMount() on the server. ' +
718 - 'This is a no-op.\n\nPlease check the code for the Baz component.',
719 - );
726 + if (__DEV__) {
727 + expect(console.error.calls.count()).toBe(1);
728 + expect(console.error.calls.mostRecent().args[0]).toBe(
729 + 'Warning: forceUpdate(...): Can only update a mounting component. ' +
730 + 'This usually means you called forceUpdate() outside componentWillMount() on the server. ' +
731 + 'This is a no-op.\n\nPlease check the code for the Baz component.',
732 + );
733 + }
734 var markup = ReactDOMServer.renderToStaticMarkup(<Baz />);
735 expect(markup).toBe('<div></div>');
736 });
737
724 - it('should warn when children are mutated during render', () => {
725 - spyOn(console, 'error');
738 + it('should throw (in dev) when children are mutated during render', () => {
739 + spyOnDev(console, 'error');
740 function Wrapper(props) {
741 props.children[1] = <p key={1} />; // Mutation is illegal
742 return <div>{props.children}</div>;
743 }
730 - expect(() => {
731 - ReactDOMServer.renderToStaticMarkup(
732 - <Wrapper>
733 - <span key={0} />
734 - <span key={1} />
735 - <span key={2} />
736 - </Wrapper>,
737 - );
738 - }).toThrowError(/Cannot assign to read only property.*/);
744 + if (__DEV__) {
745 + expect(() => {
746 + ReactDOMServer.renderToStaticMarkup(
747 + <Wrapper>
748 + <span key={0} />
749 + <span key={1} />
750 + <span key={2} />
751 + </Wrapper>,
752 + );
753 + }).toThrowError(/Cannot assign to read only property.*/);
754 + } else {
755 + expect(
756 + ReactDOMServer.renderToStaticMarkup(
757 + <Wrapper>
758 + <span key={0} />
759 + <span key={1} />
760 + <span key={2} />
761 + </Wrapper>,
762 + ),
763 + ).toContain('<p>');
764 + }
765 });
766
767 it('warns about lowercase html but not in svg tags', () => {
742 - spyOn(console, 'error');
768 + spyOnDev(console, 'error');
769 function CompositeG(props) {
770 // Make sure namespace passes through composites
771 return <g>{props.children}</g>;
@@ -758,27 +784,31 @@ describe('ReactDOMServer', () => {
784 </svg>
785 </div>,
786 );
761 - expect(console.error.calls.count()).toBe(2);
762 - expect(console.error.calls.argsFor(0)[0]).toBe(
763 - 'Warning: <inPUT /> is using uppercase HTML. Always use lowercase ' +
764 - 'HTML tags in React.',
765 - );
766 - // linearGradient doesn't warn
767 - expect(console.error.calls.argsFor(1)[0]).toBe(
768 - 'Warning: <iFrame /> is using uppercase HTML. Always use lowercase ' +
769 - 'HTML tags in React.',
770 - );
787 + if (__DEV__) {
788 + expect(console.error.calls.count()).toBe(2);
789 + expect(console.error.calls.argsFor(0)[0]).toBe(
790 + 'Warning: <inPUT /> is using uppercase HTML. Always use lowercase ' +
791 + 'HTML tags in React.',
792 + );
793 + // linearGradient doesn't warn
794 + expect(console.error.calls.argsFor(1)[0]).toBe(
795 + 'Warning: <iFrame /> is using uppercase HTML. Always use lowercase ' +
796 + 'HTML tags in React.',
797 + );
798 + }
799 });
800
801 it('should warn about contentEditable and children', () => {
774 - spyOn(console, 'error');
802 + spyOnDev(console, 'error');
803 ReactDOMServer.renderToString(<div contentEditable={true} children="" />);
776 - expectDev(console.error.calls.count()).toBe(1);
777 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
778 - 'Warning: A component is `contentEditable` and contains `children` ' +
779 - 'managed by React. It is now your responsibility to guarantee that ' +
780 - 'none of those nodes are unexpectedly modified or duplicated. This ' +
781 - 'is probably not intentional.\n in div (at **)',
782 - );
804 + if (__DEV__) {
805 + expect(console.error.calls.count()).toBe(1);
806 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
807 + 'Warning: A component is `contentEditable` and contains `children` ' +
808 + 'managed by React. It is now your responsibility to guarantee that ' +
809 + 'none of those nodes are unexpectedly modified or duplicated. This ' +
810 + 'is probably not intentional.\n in div (at **)',
811 + );
812 + }
813 });
814 });
packages/react-dom/src/__tests__/ReactStatelessComponent-test.js
+118 -79
@@ -103,7 +103,7 @@ describe('ReactStatelessComponent', () => {
103 });
104
105 it('should warn for childContextTypes on a functional component', () => {
106 - spyOn(console, 'error');
106 + spyOnDev(console, 'error');
107 function StatelessComponentWithChildContext(props) {
108 return <div>{props.name}</div>;
109 }
@@ -116,11 +116,13 @@ describe('ReactStatelessComponent', () => {
116
117 ReactDOM.render(<StatelessComponentWithChildContext name="A" />, container);
118
119 - expectDev(console.error.calls.count()).toBe(1);
120 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
121 - 'StatelessComponentWithChildContext(...): childContextTypes cannot ' +
122 - 'be defined on a functional component.',
123 - );
119 + if (__DEV__) {
120 + expect(console.error.calls.count()).toBe(1);
121 + expect(console.error.calls.argsFor(0)[0]).toContain(
122 + 'StatelessComponentWithChildContext(...): childContextTypes cannot ' +
123 + 'be defined on a functional component.',
124 + );
125 + }
126 });
127
128 it('should throw when stateless component returns undefined', () => {
@@ -144,11 +146,18 @@ describe('ReactStatelessComponent', () => {
146
147 expect(function() {
148 ReactTestUtils.renderIntoDocument(<Child test="test" />);
147 - }).toThrowError('Stateless function components cannot have refs.');
149 + }).toThrowError(
150 + __DEV__
151 + ? 'Stateless function components cannot have refs.'
152 + : // TODO: the different message in production seems like a bug.
153 + // It happens because we don't save _owner in production for
154 + // functional components. We should probably show a better message.
155 + 'Element ref was specified as a string (me) but no owner was set.',
156 + );
157 });
158
159 it('should warn when given a string ref', () => {
151 - spyOn(console, 'error');
160 + spyOnDev(console, 'error');
161
162 function Indirection(props) {
163 return <div>{props.children}</div>;
@@ -165,23 +174,27 @@ describe('ReactStatelessComponent', () => {
174 }
175
176 ReactTestUtils.renderIntoDocument(<ParentUsingStringRef />);
168 - expectDev(console.error.calls.count()).toBe(1);
169 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
170 - 'Warning: Stateless function components cannot be given refs. ' +
171 - 'Attempts to access this ref will fail.\n\nCheck the render method ' +
172 - 'of `ParentUsingStringRef`.\n' +
173 - ' in StatelessComponent (at **)\n' +
174 - ' in div (at **)\n' +
175 - ' in Indirection (at **)\n' +
176 - ' in ParentUsingStringRef (at **)',
177 - );
177 + if (__DEV__) {
178 + expect(console.error.calls.count()).toBe(1);
179 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
180 + 'Warning: Stateless function components cannot be given refs. ' +
181 + 'Attempts to access this ref will fail.\n\nCheck the render method ' +
182 + 'of `ParentUsingStringRef`.\n' +
183 + ' in StatelessComponent (at **)\n' +
184 + ' in div (at **)\n' +
185 + ' in Indirection (at **)\n' +
186 + ' in ParentUsingStringRef (at **)',
187 + );
188 + }
189
190 ReactTestUtils.renderIntoDocument(<ParentUsingStringRef />);
180 - expectDev(console.error.calls.count()).toBe(1);
191 + if (__DEV__) {
192 + expect(console.error.calls.count()).toBe(1);
193 + }
194 });
195
196 it('should warn when given a function ref', () => {
184 - spyOn(console, 'error');
197 + spyOnDev(console, 'error');
198
199 function Indirection(props) {
200 return <div>{props.children}</div>;
@@ -203,23 +216,27 @@ describe('ReactStatelessComponent', () => {
216 }
217
218 ReactTestUtils.renderIntoDocument(<ParentUsingFunctionRef />);
206 - expectDev(console.error.calls.count()).toBe(1);
207 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
208 - 'Warning: Stateless function components cannot be given refs. ' +
209 - 'Attempts to access this ref will fail.\n\nCheck the render method ' +
210 - 'of `ParentUsingFunctionRef`.\n' +
211 - ' in StatelessComponent (at **)\n' +
212 - ' in div (at **)\n' +
213 - ' in Indirection (at **)\n' +
214 - ' in ParentUsingFunctionRef (at **)',
215 - );
219 + if (__DEV__) {
220 + expect(console.error.calls.count()).toBe(1);
221 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
222 + 'Warning: Stateless function components cannot be given refs. ' +
223 + 'Attempts to access this ref will fail.\n\nCheck the render method ' +
224 + 'of `ParentUsingFunctionRef`.\n' +
225 + ' in StatelessComponent (at **)\n' +
226 + ' in div (at **)\n' +
227 + ' in Indirection (at **)\n' +
228 + ' in ParentUsingFunctionRef (at **)',
229 + );
230 + }
231
232 ReactTestUtils.renderIntoDocument(<ParentUsingFunctionRef />);
218 - expectDev(console.error.calls.count()).toBe(1);
233 + if (__DEV__) {
234 + expect(console.error.calls.count()).toBe(1);
235 + }
236 });
237
238 it('deduplicates ref warnings based on element or owner', () => {
222 - spyOn(console, 'error');
239 + spyOnDev(console, 'error');
240
241 // When owner uses JSX, we can use exact line location to dedupe warnings
242 class AnonymousParentUsingJSX extends React.Component {
@@ -232,16 +249,20 @@ describe('ReactStatelessComponent', () => {
249 const instance1 = ReactTestUtils.renderIntoDocument(
250 <AnonymousParentUsingJSX />,
251 );
235 - expectDev(console.error.calls.count()).toBe(1);
236 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
237 - 'Warning: Stateless function components cannot be given refs.',
238 - );
252 + if (__DEV__) {
253 + expect(console.error.calls.count()).toBe(1);
254 + expect(console.error.calls.argsFor(0)[0]).toContain(
255 + 'Warning: Stateless function components cannot be given refs.',
256 + );
257 + }
258 // Should be deduped (offending element is on the same line):
259 instance1.forceUpdate();
260 // Should also be deduped (offending element is on the same line):
261 ReactTestUtils.renderIntoDocument(<AnonymousParentUsingJSX />);
243 - expectDev(console.error.calls.count()).toBe(1);
244 - console.error.calls.reset();
262 + if (__DEV__) {
263 + expect(console.error.calls.count()).toBe(1);
264 + console.error.calls.reset();
265 + }
266
267 // When owner doesn't use JSX, and is anonymous, we warn once per internal instance.
268 class AnonymousParentNotUsingJSX extends React.Component {
@@ -259,20 +280,26 @@ describe('ReactStatelessComponent', () => {
280 const instance2 = ReactTestUtils.renderIntoDocument(
281 <AnonymousParentNotUsingJSX />,
282 );
262 - expectDev(console.error.calls.count()).toBe(1);
263 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
264 - 'Warning: Stateless function components cannot be given refs.',
265 - );
283 + if (__DEV__) {
284 + expect(console.error.calls.count()).toBe(1);
285 + expect(console.error.calls.argsFor(0)[0]).toContain(
286 + 'Warning: Stateless function components cannot be given refs.',
287 + );
288 + }
289 // Should be deduped (same internal instance):
290 instance2.forceUpdate();
268 - expectDev(console.error.calls.count()).toBe(1);
291 + if (__DEV__) {
292 + expect(console.error.calls.count()).toBe(1);
293 + }
294 // Could not be deduped (different internal instance):
295 ReactTestUtils.renderIntoDocument(<AnonymousParentNotUsingJSX />);
271 - expectDev(console.error.calls.count()).toBe(2);
272 - expectDev(console.error.calls.argsFor(1)[0]).toContain(
273 - 'Warning: Stateless function components cannot be given refs.',
274 - );
275 - console.error.calls.reset();
296 + if (__DEV__) {
297 + expect(console.error.calls.count()).toBe(2);
298 + expect(console.error.calls.argsFor(1)[0]).toContain(
299 + 'Warning: Stateless function components cannot be given refs.',
300 + );
301 + console.error.calls.reset();
302 + }
303
304 // When owner doesn't use JSX, but is named, we warn once per owner name
305 class NamedParentNotUsingJSX extends React.Component {
@@ -286,23 +313,29 @@ describe('ReactStatelessComponent', () => {
313 const instance3 = ReactTestUtils.renderIntoDocument(
314 <NamedParentNotUsingJSX />,
315 );
289 - expectDev(console.error.calls.count()).toBe(1);
290 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
291 - 'Warning: Stateless function components cannot be given refs.',
292 - );
316 + if (__DEV__) {
317 + expect(console.error.calls.count()).toBe(1);
318 + expect(console.error.calls.argsFor(0)[0]).toContain(
319 + 'Warning: Stateless function components cannot be given refs.',
320 + );
321 + }
322 // Should be deduped (same owner name):
323 instance3.forceUpdate();
295 - expectDev(console.error.calls.count()).toBe(1);
324 + if (__DEV__) {
325 + expect(console.error.calls.count()).toBe(1);
326 + }
327 // Should also be deduped (same owner name):
328 ReactTestUtils.renderIntoDocument(<NamedParentNotUsingJSX />);
298 - expectDev(console.error.calls.count()).toBe(1);
299 - console.error.calls.reset();
329 + if (__DEV__) {
330 + expect(console.error.calls.count()).toBe(1);
331 + console.error.calls.reset();
332 + }
333 });
334
335 // This guards against a regression caused by clearing the current debug fiber.
336 // https://github.com/facebook/react/issues/10831
337 it('should warn when giving a function ref with context', () => {
305 - spyOn(console, 'error');
338 + spyOnDev(console, 'error');
339
340 function Child() {
341 return null;
@@ -326,14 +359,16 @@ describe('ReactStatelessComponent', () => {
359 }
360
361 ReactTestUtils.renderIntoDocument(<Parent />);
329 - expectDev(console.error.calls.count()).toBe(1);
330 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
331 - 'Warning: Stateless function components cannot be given refs. ' +
332 - 'Attempts to access this ref will fail.\n\nCheck the render method ' +
333 - 'of `Parent`.\n' +
334 - ' in Child (at **)\n' +
335 - ' in Parent (at **)',
336 - );
362 + if (__DEV__) {
363 + expect(console.error.calls.count()).toBe(1);
364 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
365 + 'Warning: Stateless function components cannot be given refs. ' +
366 + 'Attempts to access this ref will fail.\n\nCheck the render method ' +
367 + 'of `Parent`.\n' +
368 + ' in Child (at **)\n' +
369 + ' in Parent (at **)',
370 + );
371 + }
372 });
373
374 it('should provide a null ref', () => {
@@ -350,13 +385,15 @@ describe('ReactStatelessComponent', () => {
385 return <div>{[<span />]}</div>;
386 }
387
353 - spyOn(console, 'error');
388 + spyOnDev(console, 'error');
389 ReactTestUtils.renderIntoDocument(<Child />);
355 - expectDev(console.error.calls.count()).toBe(1);
356 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
357 - 'a unique "key" prop',
358 - );
359 - expectDev(console.error.calls.argsFor(0)[0]).toContain('Child');
390 + if (__DEV__) {
391 + expect(console.error.calls.count()).toBe(1);
392 + expect(console.error.calls.argsFor(0)[0]).toContain(
393 + 'a unique "key" prop',
394 + );
395 + expect(console.error.calls.argsFor(0)[0]).toContain('Child');
396 + }
397 });
398
399 it('should support default props and prop types', () => {
@@ -366,16 +403,18 @@ describe('ReactStatelessComponent', () => {
403 Child.defaultProps = {test: 2};
404 Child.propTypes = {test: PropTypes.string};
405
369 - spyOn(console, 'error');
406 + spyOnDev(console, 'error');
407 ReactTestUtils.renderIntoDocument(<Child />);
371 - expectDev(console.error.calls.count()).toBe(1);
372 - expect(
373 - console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)'),
374 - ).toBe(
375 - 'Warning: Failed prop type: Invalid prop `test` of type `number` ' +
376 - 'supplied to `Child`, expected `string`.\n' +
377 - ' in Child (at **)',
378 - );
408 + if (__DEV__) {
409 + expect(console.error.calls.count()).toBe(1);
410 + expect(
411 + console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)'),
412 + ).toBe(
413 + 'Warning: Failed prop type: Invalid prop `test` of type `number` ' +
414 + 'supplied to `Child`, expected `string`.\n' +
415 + ' in Child (at **)',
416 + );
417 + }
418 });
419
420 it('should receive context', () => {
packages/react-dom/src/__tests__/ReactTestUtils-test.js
-3
@@ -365,8 +365,6 @@ describe('ReactTestUtils', () => {
365 });
366
367 it('should not warn when used with extra properties', () => {
368 - spyOn(console, 'error');
369 -
368 const CLIENT_X = 100;
369
370 class Component extends React.Component {
@@ -384,7 +382,6 @@ describe('ReactTestUtils', () => {
382 ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance), {
383 clientX: CLIENT_X,
384 });
387 - expectDev(console.error.calls.count()).toBe(0);
385 });
386
387 it('should set the type of the event', () => {
packages/react-dom/src/__tests__/ReactUpdates-test.js
+47 -33
@@ -842,7 +842,7 @@ describe('ReactUpdates', () => {
842 });
843
844 it('throws in setState if the update callback is not a function', () => {
845 - spyOn(console, 'error');
845 + spyOnDev(console, 'error');
846
847 function Foo() {
848 this.a = 1;
@@ -863,33 +863,39 @@ describe('ReactUpdates', () => {
863 'Invalid argument passed as callback. Expected a function. Instead ' +
864 'received: no',
865 );
866 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
867 - 'setState(...): Expected the last optional `callback` argument to be ' +
868 - 'a function. Instead received: no.',
869 - );
866 + if (__DEV__) {
867 + expect(console.error.calls.argsFor(0)[0]).toContain(
868 + 'setState(...): Expected the last optional `callback` argument to be ' +
869 + 'a function. Instead received: no.',
870 + );
871 + }
872 component = ReactTestUtils.renderIntoDocument(<A />);
873 expect(() => component.setState({}, {foo: 'bar'})).toThrowError(
874 'Invalid argument passed as callback. Expected a function. Instead ' +
875 'received: [object Object]',
876 );
875 - expectDev(console.error.calls.argsFor(1)[0]).toContain(
876 - 'setState(...): Expected the last optional `callback` argument to be ' +
877 - 'a function. Instead received: [object Object].',
878 - );
877 + if (__DEV__) {
878 + expect(console.error.calls.argsFor(1)[0]).toContain(
879 + 'setState(...): Expected the last optional `callback` argument to be ' +
880 + 'a function. Instead received: [object Object].',
881 + );
882 + }
883 component = ReactTestUtils.renderIntoDocument(<A />);
884 expect(() => component.setState({}, new Foo())).toThrowError(
885 'Invalid argument passed as callback. Expected a function. Instead ' +
886 'received: [object Object]',
887 );
884 - expectDev(console.error.calls.argsFor(2)[0]).toContain(
885 - 'setState(...): Expected the last optional `callback` argument to be ' +
886 - 'a function. Instead received: [object Object].',
887 - );
888 - expect(console.error.calls.count()).toBe(3);
888 + if (__DEV__) {
889 + expect(console.error.calls.argsFor(2)[0]).toContain(
890 + 'setState(...): Expected the last optional `callback` argument to be ' +
891 + 'a function. Instead received: [object Object].',
892 + );
893 + expect(console.error.calls.count()).toBe(3);
894 + }
895 });
896
897 it('throws in forceUpdate if the update callback is not a function', () => {
892 - spyOn(console, 'error');
898 + spyOnDev(console, 'error');
899
900 function Foo() {
901 this.a = 1;
@@ -910,29 +916,35 @@ describe('ReactUpdates', () => {
916 'Invalid argument passed as callback. Expected a function. Instead ' +
917 'received: no',
918 );
913 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
914 - 'forceUpdate(...): Expected the last optional `callback` argument to be ' +
915 - 'a function. Instead received: no.',
916 - );
919 + if (__DEV__) {
920 + expect(console.error.calls.argsFor(0)[0]).toContain(
921 + 'forceUpdate(...): Expected the last optional `callback` argument to be ' +
922 + 'a function. Instead received: no.',
923 + );
924 + }
925 component = ReactTestUtils.renderIntoDocument(<A />);
926 expect(() => component.forceUpdate({foo: 'bar'})).toThrowError(
927 'Invalid argument passed as callback. Expected a function. Instead ' +
928 'received: [object Object]',
929 );
922 - expectDev(console.error.calls.argsFor(1)[0]).toContain(
923 - 'forceUpdate(...): Expected the last optional `callback` argument to be ' +
924 - 'a function. Instead received: [object Object].',
925 - );
930 + if (__DEV__) {
931 + expect(console.error.calls.argsFor(1)[0]).toContain(
932 + 'forceUpdate(...): Expected the last optional `callback` argument to be ' +
933 + 'a function. Instead received: [object Object].',
934 + );
935 + }
936 component = ReactTestUtils.renderIntoDocument(<A />);
937 expect(() => component.forceUpdate(new Foo())).toThrowError(
938 'Invalid argument passed as callback. Expected a function. Instead ' +
939 'received: [object Object]',
940 );
931 - expectDev(console.error.calls.argsFor(2)[0]).toContain(
932 - 'forceUpdate(...): Expected the last optional `callback` argument to be ' +
933 - 'a function. Instead received: [object Object].',
934 - );
935 - expect(console.error.calls.count()).toBe(3);
941 + if (__DEV__) {
942 + expect(console.error.calls.argsFor(2)[0]).toContain(
943 + 'forceUpdate(...): Expected the last optional `callback` argument to be ' +
944 + 'a function. Instead received: [object Object].',
945 + );
946 + expect(console.error.calls.count()).toBe(3);
947 + }
948 });
949
950 it('does not update one component twice in a batch (#2410)', () => {
@@ -1216,7 +1228,7 @@ describe('ReactUpdates', () => {
1228 );
1229
1230 it('uses correct base state for setState inside render phase', () => {
1219 - spyOn(console, 'error');
1231 + spyOnDev(console, 'error');
1232
1233 let ops = [];
1234
@@ -1236,10 +1248,12 @@ describe('ReactUpdates', () => {
1248 const container = document.createElement('div');
1249 ReactDOM.render(<Foo />, container);
1250 expect(ops).toEqual(['base: 0, memoized: 0', 'base: 1, memoized: 1']);
1239 - expect(console.error.calls.count()).toBe(1);
1240 - expect(console.error.calls.argsFor(0)[0]).toContain(
1241 - 'Cannot update during an existing state transition',
1242 - );
1251 + if (__DEV__) {
1252 + expect(console.error.calls.count()).toBe(1);
1253 + expect(console.error.calls.argsFor(0)[0]).toContain(
1254 + 'Cannot update during an existing state transition',
1255 + );
1256 + }
1257 });
1258
1259 it('does not re-render if state update is null', () => {
packages/react-dom/src/__tests__/refs-test.js
+9 -50
@@ -256,7 +256,7 @@ describe('ref swapping', () => {
256 expect(!!instance.refs).toBe(true);
257 });
258
259 - function testRefCall() {
259 + it('ref called correctly for stateless component', () => {
260 var refCalled = 0;
261 function Inner(props) {
262 return <a ref={props.saveA} />;
@@ -278,20 +278,6 @@ describe('ref swapping', () => {
278
279 ReactTestUtils.renderIntoDocument(<Outer />);
280 expect(refCalled).toBe(1);
281 - }
282 -
283 - it('ref called correctly for stateless component when __DEV__ = false', () => {
284 - var originalDev = __DEV__;
285 - __DEV__ = false;
286 - testRefCall();
287 - __DEV__ = originalDev;
288 - });
289 -
290 - it('ref called correctly for stateless component when __DEV__ = true', () => {
291 - var originalDev = __DEV__;
292 - __DEV__ = true;
293 - testRefCall();
294 - __DEV__ = originalDev;
281 });
282
283 it('coerces numbers to strings', () => {
@@ -400,42 +386,15 @@ describe('creating element with ref in constructor', () => {
386 }
387 }
388
403 - it('throws an error when __DEV__ = true', () => {
404 - ReactTestUtils = require('react-dom/test-utils');
405 -
406 - var originalDev = __DEV__;
407 - __DEV__ = true;
408 -
409 - try {
410 - expect(function() {
411 - ReactTestUtils.renderIntoDocument(<RefTest />);
412 - }).toThrowError(
413 - 'Element ref was specified as a string (p) but no owner was ' +
414 - 'set. You may have multiple copies of React loaded. ' +
415 - '(details: https://fb.me/react-refs-must-have-owner).',
416 - );
417 - } finally {
418 - __DEV__ = originalDev;
419 - }
420 - });
421 -
422 - it('throws an error when __DEV__ = false', () => {
389 + it('throws an error', () => {
390 ReactTestUtils = require('react-dom/test-utils');
391
425 - var originalDev = __DEV__;
426 - __DEV__ = false;
427 -
428 - try {
429 - expect(function() {
430 - ReactTestUtils.renderIntoDocument(<RefTest />);
431 - }).toThrowError(
432 - 'Minified React error #149; visit ' +
433 - 'http://facebook.github.io/react/docs/error-decoder.html?invariant=149&args[]=p ' +
434 - 'for the full message or use the non-minified dev environment for full errors and additional ' +
435 - 'helpful warnings.',
436 - );
437 - } finally {
438 - __DEV__ = originalDev;
439 - }
392 + expect(function() {
393 + ReactTestUtils.renderIntoDocument(<RefTest />);
394 + }).toThrowError(
395 + 'Element ref was specified as a string (p) but no owner was ' +
396 + 'set. You may have multiple copies of React loaded. ' +
397 + '(details: https://fb.me/react-refs-must-have-owner).',
398 + );
399 });
400 });
packages/react-dom/src/__tests__/renderSubtreeIntoContainer-test.js
+8 -6
@@ -310,12 +310,14 @@ describe('renderSubtreeIntoContainer', () => {
310 expect(() => {
311 c._reactInternalInstance._processChildContext({});
312 }).toThrow(
313 - '_processChildContext is not available in React 16+. This likely ' +
314 - 'means you have multiple copies of React and are attempting to nest ' +
315 - 'a React 15 tree inside a React 16 tree using ' +
316 - "unstable_renderSubtreeIntoContainer, which isn't supported. Try to " +
317 - 'make sure you have only one copy of React (and ideally, switch to ' +
318 - 'ReactDOM.createPortal).',
313 + __DEV__
314 + ? '_processChildContext is not available in React 16+. This likely ' +
315 + 'means you have multiple copies of React and are attempting to nest ' +
316 + 'a React 15 tree inside a React 16 tree using ' +
317 + "unstable_renderSubtreeIntoContainer, which isn't supported. Try to " +
318 + 'make sure you have only one copy of React (and ideally, switch to ' +
319 + 'ReactDOM.createPortal).'
320 + : "Cannot read property '_processChildContext' of undefined",
321 );
322 });
323 });
packages/react-dom/src/__tests__/validateDOMNesting-test.js
+34 -26
@@ -131,7 +131,7 @@ function isTagStackValid(stack) {
131 return true;
132 }
133
134 -describe('ReactContextValidator', () => {
134 +describe('validateDOMNesting', () => {
135 beforeEach(() => {
136 jest.resetModules();
137
@@ -140,37 +140,45 @@ describe('ReactContextValidator', () => {
140 });
141
142 it('allows any tag with no context', () => {
143 - // With renderToString (for example), we don't know where we're mounting the
144 - // tag so we must err on the side of leniency.
145 - var allTags = [].concat(specialTags, formattingTags, ['mysterytag']);
146 - allTags.forEach(function(tag) {
147 - expect(validateDOMNesting.isTagValidInContext(tag, null)).toBe(true);
148 - });
143 + if (__DEV__) {
144 + // With renderToString (for example), we don't know where we're mounting the
145 + // tag so we must err on the side of leniency.
146 + var allTags = [].concat(specialTags, formattingTags, ['mysterytag']);
147 + allTags.forEach(function(tag) {
148 + expect(validateDOMNesting.isTagValidInContext(tag, null)).toBe(true);
149 + });
150 + }
151 });
152
153 it('allows valid nestings', () => {
152 - expect(isTagStackValid(['table', 'tbody', 'tr', 'td', 'b'])).toBe(true);
153 - expect(isTagStackValid(['body', 'datalist', 'option'])).toBe(true);
154 - expect(isTagStackValid(['div', 'a', 'object', 'a'])).toBe(true);
155 - expect(isTagStackValid(['div', 'p', 'button', 'p'])).toBe(true);
156 - expect(isTagStackValid(['p', 'svg', 'foreignObject', 'p'])).toBe(true);
157 - expect(isTagStackValid(['html', 'body', 'div'])).toBe(true);
154 + if (__DEV__) {
155 + expect(isTagStackValid(['table', 'tbody', 'tr', 'td', 'b'])).toBe(true);
156 + expect(isTagStackValid(['body', 'datalist', 'option'])).toBe(true);
157 + expect(isTagStackValid(['div', 'a', 'object', 'a'])).toBe(true);
158 + expect(isTagStackValid(['div', 'p', 'button', 'p'])).toBe(true);
159 + expect(isTagStackValid(['p', 'svg', 'foreignObject', 'p'])).toBe(true);
160 + expect(isTagStackValid(['html', 'body', 'div'])).toBe(true);
161
159 - // Invalid, but not changed by browser parsing so we allow them
160 - expect(isTagStackValid(['div', 'ul', 'ul', 'li'])).toBe(true);
161 - expect(isTagStackValid(['div', 'label', 'div'])).toBe(true);
162 - expect(isTagStackValid(['div', 'ul', 'li', 'section', 'li'])).toBe(true);
163 - expect(isTagStackValid(['div', 'ul', 'li', 'dd', 'li'])).toBe(true);
162 + // Invalid, but not changed by browser parsing so we allow them
163 + expect(isTagStackValid(['div', 'ul', 'ul', 'li'])).toBe(true);
164 + expect(isTagStackValid(['div', 'label', 'div'])).toBe(true);
165 + expect(isTagStackValid(['div', 'ul', 'li', 'section', 'li'])).toBe(true);
166 + expect(isTagStackValid(['div', 'ul', 'li', 'dd', 'li'])).toBe(true);
167 + }
168 });
169
170 it('prevents problematic nestings', () => {
167 - expect(isTagStackValid(['a', 'a'])).toBe(false);
168 - expect(isTagStackValid(['form', 'form'])).toBe(false);
169 - expect(isTagStackValid(['p', 'p'])).toBe(false);
170 - expect(isTagStackValid(['table', 'tr'])).toBe(false);
171 - expect(isTagStackValid(['div', 'ul', 'li', 'div', 'li'])).toBe(false);
172 - expect(isTagStackValid(['div', 'html'])).toBe(false);
173 - expect(isTagStackValid(['body', 'body'])).toBe(false);
174 - expect(isTagStackValid(['svg', 'foreignObject', 'body', 'p'])).toBe(false);
171 + if (__DEV__) {
172 + expect(isTagStackValid(['a', 'a'])).toBe(false);
173 + expect(isTagStackValid(['form', 'form'])).toBe(false);
174 + expect(isTagStackValid(['p', 'p'])).toBe(false);
175 + expect(isTagStackValid(['table', 'tr'])).toBe(false);
176 + expect(isTagStackValid(['div', 'ul', 'li', 'div', 'li'])).toBe(false);
177 + expect(isTagStackValid(['div', 'html'])).toBe(false);
178 + expect(isTagStackValid(['body', 'body'])).toBe(false);
179 + expect(isTagStackValid(['svg', 'foreignObject', 'body', 'p'])).toBe(
180 + false,
181 + );
182 + }
183 });
184 });
packages/react-dom/src/events/__tests__/SyntheticEvent-test.js
+73 -59
@@ -157,7 +157,7 @@ describe('SyntheticEvent', () => {
157 });
158
159 it('should be nullified and log warnings if the synthetic event has not been persisted', () => {
160 - spyOn(console, 'error');
160 + spyOnDev(console, 'error');
161 var node;
162 var expectedCount = 0;
163 var syntheticEvent;
@@ -176,21 +176,23 @@ describe('SyntheticEvent', () => {
176 expect(syntheticEvent.type).toBe(null);
177 expect(syntheticEvent.nativeEvent).toBe(null);
178 expect(syntheticEvent.target).toBe(null);
179 - // once for each property accessed
180 - expectDev(console.error.calls.count()).toBe(3);
181 - // assert the first warning for accessing `type`
182 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
183 - 'Warning: This synthetic event is reused for performance reasons. If ' +
184 - "you're seeing this, you're accessing the property `type` on a " +
185 - 'released/nullified synthetic event. This is set to null. If you must ' +
186 - 'keep the original synthetic event around, use event.persist(). ' +
187 - 'See https://fb.me/react-event-pooling for more information.',
188 - );
179 + if (__DEV__) {
180 + // once for each property accessed
181 + expect(console.error.calls.count()).toBe(3);
182 + // assert the first warning for accessing `type`
183 + expect(console.error.calls.argsFor(0)[0]).toBe(
184 + 'Warning: This synthetic event is reused for performance reasons. If ' +
185 + "you're seeing this, you're accessing the property `type` on a " +
186 + 'released/nullified synthetic event. This is set to null. If you must ' +
187 + 'keep the original synthetic event around, use event.persist(). ' +
188 + 'See https://fb.me/react-event-pooling for more information.',
189 + );
190 + }
191 expect(expectedCount).toBe(1);
192 });
193
194 it('should warn when setting properties of a synthetic event that has not been persisted', () => {
193 - spyOn(console, 'error');
195 + spyOnDev(console, 'error');
196 var node;
197 var expectedCount = 0;
198 var syntheticEvent;
@@ -207,19 +209,21 @@ describe('SyntheticEvent', () => {
209 node.dispatchEvent(event);
210
211 syntheticEvent.type = 'MouseEvent';
210 - expectDev(console.error.calls.count()).toBe(1);
211 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
212 - 'Warning: This synthetic event is reused for performance reasons. If ' +
213 - "you're seeing this, you're setting the property `type` on a " +
214 - 'released/nullified synthetic event. This is effectively a no-op. If you must ' +
215 - 'keep the original synthetic event around, use event.persist(). ' +
216 - 'See https://fb.me/react-event-pooling for more information.',
217 - );
212 + if (__DEV__) {
213 + expect(console.error.calls.count()).toBe(1);
214 + expect(console.error.calls.argsFor(0)[0]).toBe(
215 + 'Warning: This synthetic event is reused for performance reasons. If ' +
216 + "you're seeing this, you're setting the property `type` on a " +
217 + 'released/nullified synthetic event. This is effectively a no-op. If you must ' +
218 + 'keep the original synthetic event around, use event.persist(). ' +
219 + 'See https://fb.me/react-event-pooling for more information.',
220 + );
221 + }
222 expect(expectedCount).toBe(1);
223 });
224
225 it('should warn when calling `preventDefault` if the synthetic event has not been persisted', () => {
222 - spyOn(console, 'error');
226 + spyOnDev(console, 'error');
227 var node;
228 var expectedCount = 0;
229 var syntheticEvent;
@@ -235,19 +239,21 @@ describe('SyntheticEvent', () => {
239 node.dispatchEvent(event);
240
241 syntheticEvent.preventDefault();
238 - expectDev(console.error.calls.count()).toBe(1);
239 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
240 - 'Warning: This synthetic event is reused for performance reasons. If ' +
241 - "you're seeing this, you're accessing the method `preventDefault` on a " +
242 - 'released/nullified synthetic event. This is a no-op function. If you must ' +
243 - 'keep the original synthetic event around, use event.persist(). ' +
244 - 'See https://fb.me/react-event-pooling for more information.',
245 - );
242 + if (__DEV__) {
243 + expect(console.error.calls.count()).toBe(1);
244 + expect(console.error.calls.argsFor(0)[0]).toBe(
245 + 'Warning: This synthetic event is reused for performance reasons. If ' +
246 + "you're seeing this, you're accessing the method `preventDefault` on a " +
247 + 'released/nullified synthetic event. This is a no-op function. If you must ' +
248 + 'keep the original synthetic event around, use event.persist(). ' +
249 + 'See https://fb.me/react-event-pooling for more information.',
250 + );
251 + }
252 expect(expectedCount).toBe(1);
253 });
254
255 it('should warn when calling `stopPropagation` if the synthetic event has not been persisted', () => {
250 - spyOn(console, 'error');
256 + spyOnDev(console, 'error');
257 var node;
258 var expectedCount = 0;
259 var syntheticEvent;
@@ -264,14 +270,16 @@ describe('SyntheticEvent', () => {
270 node.dispatchEvent(event);
271
272 syntheticEvent.stopPropagation();
267 - expectDev(console.error.calls.count()).toBe(1);
268 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
269 - 'Warning: This synthetic event is reused for performance reasons. If ' +
270 - "you're seeing this, you're accessing the method `stopPropagation` on a " +
271 - 'released/nullified synthetic event. This is a no-op function. If you must ' +
272 - 'keep the original synthetic event around, use event.persist(). ' +
273 - 'See https://fb.me/react-event-pooling for more information.',
274 - );
273 + if (__DEV__) {
274 + expect(console.error.calls.count()).toBe(1);
275 + expect(console.error.calls.argsFor(0)[0]).toBe(
276 + 'Warning: This synthetic event is reused for performance reasons. If ' +
277 + "you're seeing this, you're accessing the method `stopPropagation` on a " +
278 + 'released/nullified synthetic event. This is a no-op function. If you must ' +
279 + 'keep the original synthetic event around, use event.persist(). ' +
280 + 'See https://fb.me/react-event-pooling for more information.',
281 + );
282 + }
283 expect(expectedCount).toBe(1);
284 });
285
@@ -279,7 +287,7 @@ describe('SyntheticEvent', () => {
287 // using TestUtils.Simulate to avoid spurious warnings that result from the
288 // way we simulate events.
289 xit('should properly log warnings when events simulated with rendered components', () => {
282 - spyOn(console, 'error');
290 + spyOnDev(console, 'error');
291 var event;
292 var element = document.createElement('div');
293 function assignEvent(e) {
@@ -287,23 +295,27 @@ describe('SyntheticEvent', () => {
295 }
296 var node = ReactDOM.render(<div onClick={assignEvent} />, element);
297 ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(node));
290 - expectDev(console.error.calls.count()).toBe(0);
298 + if (__DEV__) {
299 + expect(console.error.calls.count()).toBe(0);
300 + }
301
302 // access a property to cause the warning
303 event.nativeEvent; // eslint-disable-line no-unused-expressions
304
295 - expectDev(console.error.calls.count()).toBe(1);
296 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
297 - 'Warning: This synthetic event is reused for performance reasons. If ' +
298 - "you're seeing this, you're accessing the property `nativeEvent` on a " +
299 - 'released/nullified synthetic event. This is set to null. If you must ' +
300 - 'keep the original synthetic event around, use event.persist(). ' +
301 - 'See https://fb.me/react-event-pooling for more information.',
302 - );
305 + if (__DEV__) {
306 + expect(console.error.calls.count()).toBe(1);
307 + expect(console.error.calls.argsFor(0)[0]).toBe(
308 + 'Warning: This synthetic event is reused for performance reasons. If ' +
309 + "you're seeing this, you're accessing the property `nativeEvent` on a " +
310 + 'released/nullified synthetic event. This is set to null. If you must ' +
311 + 'keep the original synthetic event around, use event.persist(). ' +
312 + 'See https://fb.me/react-event-pooling for more information.',
313 + );
314 + }
315 });
316
317 it('should warn if Proxy is supported and the synthetic event is added a property', () => {
306 - spyOn(console, 'error');
318 + spyOnDev(console, 'error');
319 var node;
320 var expectedCount = 0;
321 var syntheticEvent;
@@ -321,16 +333,18 @@ describe('SyntheticEvent', () => {
333 node.dispatchEvent(event);
334
335 expect(syntheticEvent.foo).toBe('bar');
324 - if (typeof Proxy === 'function') {
325 - expectDev(console.error.calls.count()).toBe(1);
326 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
327 - 'Warning: This synthetic event is reused for performance reasons. If ' +
328 - "you're seeing this, you're adding a new property in the synthetic " +
329 - 'event object. The property is never released. ' +
330 - 'See https://fb.me/react-event-pooling for more information.',
331 - );
332 - } else {
333 - expectDev(console.error.calls.count()).toBe(0);
336 + if (__DEV__) {
337 + if (typeof Proxy === 'function') {
338 + expect(console.error.calls.count()).toBe(1);
339 + expect(console.error.calls.argsFor(0)[0]).toBe(
340 + 'Warning: This synthetic event is reused for performance reasons. If ' +
341 + "you're seeing this, you're adding a new property in the synthetic " +
342 + 'event object. The property is never released. ' +
343 + 'See https://fb.me/react-event-pooling for more information.',
344 + );
345 + } else {
346 + expect(console.error.calls.count()).toBe(0);
347 + }
348 }
349 expect(expectedCount).toBe(1);
350 });
packages/react-reconciler/src/__tests__/ReactFragment-test.js
+15 -11
@@ -674,7 +674,7 @@ describe('ReactFragment', () => {
674 });
675
676 it('should not preserve state when switching to a keyed fragment to an array', function() {
677 - spyOn(console, 'error');
677 + spyOnDev(console, 'error');
678 var ops = [];
679
680 class Stateful extends React.Component {
@@ -719,14 +719,16 @@ describe('ReactFragment', () => {
719
720 expect(ops).toEqual([]);
721 expect(ReactNoop.getChildren()).toEqual([div(div(), span())]);
722 - expectDev(console.error.calls.count()).toBe(1);
723 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
724 - 'Each child in an array or iterator should have a unique "key" prop.',
725 - );
722 + if (__DEV__) {
723 + expect(console.error.calls.count()).toBe(1);
724 + expect(console.error.calls.argsFor(0)[0]).toContain(
725 + 'Each child in an array or iterator should have a unique "key" prop.',
726 + );
727 + }
728 });
729
730 it('should preserve state when it does not change positions', function() {
729 - spyOn(console, 'error');
731 + spyOnDev(console, 'error');
732 var ops = [];
733
734 class Stateful extends React.Component {
@@ -769,11 +771,13 @@ describe('ReactFragment', () => {
771
772 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
773 expect(ReactNoop.getChildren()).toEqual([span(), div()]);
772 - expectDev(console.error.calls.count()).toBe(3);
773 - for (let errorIndex = 0; errorIndex < 3; ++errorIndex) {
774 - expectDev(console.error.calls.argsFor(errorIndex)[0]).toContain(
775 - 'Each child in an array or iterator should have a unique "key" prop.',
776 - );
774 + if (__DEV__) {
775 + expect(console.error.calls.count()).toBe(3);
776 + for (let errorIndex = 0; errorIndex < 3; ++errorIndex) {
777 + expect(console.error.calls.argsFor(errorIndex)[0]).toContain(
778 + 'Each child in an array or iterator should have a unique "key" prop.',
779 + );
780 + }
781 }
782 });
783 });
packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.js
+124 -53
@@ -719,7 +719,7 @@ describe('ReactIncrementalErrorHandling', () => {
719 });
720
721 it('catches reconciler errors in a boundary during mounting', () => {
722 - spyOn(console, 'error');
722 + spyOnDev(console, 'error');
723
724 class ErrorBoundary extends React.Component {
725 state = {error: null};
@@ -747,17 +747,24 @@ describe('ReactIncrementalErrorHandling', () => {
747 expect(ReactNoop.getChildren()).toEqual([
748 span(
749 'Element type is invalid: expected a string (for built-in components) or ' +
750 - 'a class/function (for composite components) but got: undefined. ' +
751 - "You likely forgot to export your component from the file it's " +
752 - 'defined in, or you might have mixed up default and named imports.' +
753 - '\n\nCheck the render method of `BrokenRender`.',
750 + 'a class/function (for composite components) but got: undefined.' +
751 + (__DEV__
752 + ? " You likely forgot to export your component from the file it's " +
753 + 'defined in, or you might have mixed up default and named imports.' +
754 + '\n\nCheck the render method of `BrokenRender`.'
755 + : ''),
756 ),
757 ]);
756 - expect(console.error.calls.count()).toBe(1);
758 + if (__DEV__) {
759 + expect(console.error.calls.count()).toBe(1);
760 + expect(console.error.calls.argsFor(0)[0]).toContain(
761 + 'Warning: React.createElement: type is invalid -- expected a string',
762 + );
763 + }
764 });
765
766 it('catches reconciler errors in a boundary during update', () => {
760 - spyOn(console, 'error');
767 + spyOnDev(console, 'error');
768
769 class ErrorBoundary extends React.Component {
770 state = {error: null};
@@ -793,31 +800,46 @@ describe('ReactIncrementalErrorHandling', () => {
800 expect(ReactNoop.getChildren()).toEqual([
801 span(
802 'Element type is invalid: expected a string (for built-in components) or ' +
796 - 'a class/function (for composite components) but got: undefined. ' +
797 - "You likely forgot to export your component from the file it's " +
798 - 'defined in, or you might have mixed up default and named imports.' +
799 - '\n\nCheck the render method of `BrokenRender`.',
803 + 'a class/function (for composite components) but got: undefined.' +
804 + (__DEV__
805 + ? " You likely forgot to export your component from the file it's " +
806 + 'defined in, or you might have mixed up default and named imports.' +
807 + '\n\nCheck the render method of `BrokenRender`.'
808 + : ''),
809 ),
810 ]);
802 - expect(console.error.calls.count()).toBe(1);
811 + if (__DEV__) {
812 + expect(console.error.calls.count()).toBe(1);
813 + expect(console.error.calls.argsFor(0)[0]).toContain(
814 + 'Warning: React.createElement: type is invalid -- expected a string',
815 + );
816 + }
817 });
818
819 it('recovers from uncaught reconciler errors', () => {
806 - spyOn(console, 'error');
820 + spyOnDev(console, 'error');
821 const InvalidType = undefined;
822 ReactNoop.render(<InvalidType />);
823 expect(() => {
824 ReactNoop.flush();
825 }).toThrowError(
826 'Element type is invalid: expected a string (for built-in components) or ' +
813 - 'a class/function (for composite components) but got: undefined. ' +
814 - "You likely forgot to export your component from the file it's " +
815 - 'defined in, or you might have mixed up default and named imports.',
827 + 'a class/function (for composite components) but got: undefined.' +
828 + (__DEV__
829 + ? " You likely forgot to export your component from the file it's " +
830 + 'defined in, or you might have mixed up default and named imports.'
831 + : ''),
832 );
833
834 ReactNoop.render(<span prop="hi" />);
835 ReactNoop.flush();
836 expect(ReactNoop.getChildren()).toEqual([span('hi')]);
837 + if (__DEV__) {
838 + expect(console.error.calls.count()).toBe(1);
839 + expect(console.error.calls.argsFor(0)[0]).toContain(
840 + 'Warning: React.createElement: type is invalid -- expected a string',
841 + );
842 + }
843 });
844
845 it('unmounts components with uncaught errors', () => {
@@ -941,12 +963,33 @@ describe('ReactIncrementalErrorHandling', () => {
963 ReactNoop = require('react-noop-renderer');
964 }
965
966 + beforeEach(() => {
967 + // Assert that we're mocking this file by default.
968 + // If this ever changes, we'll need to update this test suite anyway.
969 + expect(
970 + require('../ReactFiberErrorLogger').logCapturedError._isMockFunction,
971 + ).toBe(true);
972 + });
973 +
974 + afterEach(() => {
975 + // Restore the default (we verified it was being mocked in beforeEach).
976 + jest.mock('../ReactFiberErrorLogger');
977 + });
978 +
979 function normalizeCodeLocInfo(str) {
980 return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
981 }
982
983 it('should log errors that occur during the begin phase', () => {
949 - initReactFiberErrorLoggerMock();
984 + initReactFiberErrorLoggerMock(false);
985 + // Intentionally spy in production too.
986 + // TODO: It is confusing how these tests correctly "see" console.error()s
987 + // from exceptions becase they called initReactFiberErrorLoggerMock(false)
988 + // and thus unmocked the error logger. It is globally mocked in all other
989 + // tests. We did this to silence warnings from intentionally caught errors
990 + // in tests. But perhaps we should instead make a pass through all tests,
991 + // intentionally assert console.error() is always called for uncaught
992 + // exceptions, and then remove the global mock of ReactFiberErrorLogger.
993 spyOn(console, 'error');
994
995 class ErrorThrowingComponent extends React.Component {
@@ -971,19 +1014,30 @@ describe('ReactIncrementalErrorHandling', () => {
1014
1015 expect(console.error.calls.count()).toBe(1);
1016 const errorMessage = console.error.calls.argsFor(0)[0];
974 - expect(normalizeCodeLocInfo(errorMessage)).toContain(
975 - 'The above error occurred in the <ErrorThrowingComponent> component:\n' +
976 - ' in ErrorThrowingComponent (at **)\n' +
977 - ' in span (at **)\n' +
978 - ' in div (at **)',
979 - );
980 - expect(errorMessage).toContain(
981 - 'Consider adding an error boundary to your tree to customize error handling behavior.',
982 - );
1017 + if (__DEV__) {
1018 + expect(normalizeCodeLocInfo(errorMessage)).toContain(
1019 + 'The above error occurred in the <ErrorThrowingComponent> component:\n' +
1020 + ' in ErrorThrowingComponent (at **)\n' +
1021 + ' in span (at **)\n' +
1022 + ' in div (at **)',
1023 + );
1024 + expect(errorMessage).toContain(
1025 + 'Consider adding an error boundary to your tree to customize error handling behavior.',
1026 + );
1027 + } else {
1028 + expect(errorMessage.message).toContain('componentWillMount error');
1029 + }
1030 });
1031
1032 it('should log errors that occur during the commit phase', () => {
986 - initReactFiberErrorLoggerMock();
1033 + initReactFiberErrorLoggerMock(false);
1034 + // TODO: It is confusing how these tests correctly "see" console.error()s
1035 + // from exceptions becase they called initReactFiberErrorLoggerMock(false)
1036 + // and thus unmocked the error logger. It is globally mocked in all other
1037 + // tests. We did this to silence warnings from intentionally caught errors
1038 + // in tests. But perhaps we should instead make a pass through all tests,
1039 + // intentionally assert console.error() is always called for uncaught
1040 + // exceptions, and then remove the global mock of ReactFiberErrorLogger.
1041 spyOn(console, 'error');
1042
1043 class ErrorThrowingComponent extends React.Component {
@@ -1008,19 +1062,29 @@ describe('ReactIncrementalErrorHandling', () => {
1062
1063 expect(console.error.calls.count()).toBe(1);
1064 const errorMessage = console.error.calls.argsFor(0)[0];
1011 - expect(normalizeCodeLocInfo(errorMessage)).toContain(
1012 - 'The above error occurred in the <ErrorThrowingComponent> component:\n' +
1013 - ' in ErrorThrowingComponent (at **)\n' +
1014 - ' in span (at **)\n' +
1015 - ' in div (at **)',
1016 - );
1017 - expect(errorMessage).toContain(
1018 - 'Consider adding an error boundary to your tree to customize error handling behavior.',
1019 - );
1065 + if (__DEV__) {
1066 + expect(normalizeCodeLocInfo(errorMessage)).toContain(
1067 + 'The above error occurred in the <ErrorThrowingComponent> component:\n' +
1068 + ' in ErrorThrowingComponent (at **)\n' +
1069 + ' in span (at **)\n' +
1070 + ' in div (at **)',
1071 + );
1072 + expect(errorMessage).toContain(
1073 + 'Consider adding an error boundary to your tree to customize error handling behavior.',
1074 + );
1075 + } else {
1076 + expect(errorMessage.message).toBe('componentDidMount error');
1077 + }
1078 });
1079
1080 it('should ignore errors thrown in log method to prevent cycle', () => {
1081 initReactFiberErrorLoggerMock(true);
1082 + // Intentionally spy in production too.
1083 + // Note: we're seeing console.error() even though ReactFiberErrorLogger is
1084 + // mocked because we're currently testing the console.error() call inside
1085 + // ReactFiberScheduler (for cycles). It doesn't get affected by mocking.
1086 + // TODO: mocking is confusing and we should simplify this and remove
1087 + // special cases.
1088 spyOn(console, 'error');
1089
1090 class ErrorThrowingComponent extends React.Component {
@@ -1060,7 +1124,14 @@ describe('ReactIncrementalErrorHandling', () => {
1124 });
1125
1126 it('should relay info about error boundary and retry attempts if applicable', () => {
1063 - initReactFiberErrorLoggerMock();
1127 + initReactFiberErrorLoggerMock(false);
1128 + // TODO: It is confusing how these tests correctly "see" console.error()s
1129 + // from exceptions becase they called initReactFiberErrorLoggerMock(false)
1130 + // and thus unmocked the error logger. It is globally mocked in all other
1131 + // tests. We did this to silence warnings from intentionally caught errors
1132 + // in tests. But perhaps we should instead make a pass through all tests,
1133 + // intentionally assert console.error() is always called for uncaught
1134 + // exceptions, and then remove the global mock of ReactFiberErrorLogger.
1135 spyOn(console, 'error');
1136
1137 class ParentComponent extends React.Component {
@@ -1100,26 +1171,26 @@ describe('ReactIncrementalErrorHandling', () => {
1171 expect(renderAttempts).toBe(2);
1172 expect(handleErrorCalls.length).toBe(1);
1173 expect(console.error.calls.count()).toBe(2);
1103 - expect(console.error.calls.argsFor(0)[0]).toContain(
1104 - 'The above error occurred in the <ErrorThrowingComponent> component:',
1105 - );
1106 - expect(console.error.calls.argsFor(0)[0]).toContain(
1107 - 'React will try to recreate this component tree from scratch ' +
1108 - 'using the error boundary you provided, ErrorBoundaryComponent.',
1109 - );
1110 - expect(console.error.calls.argsFor(1)[0]).toContain(
1111 - 'The above error occurred in the <ErrorThrowingComponent> component:',
1112 - );
1113 - expect(console.error.calls.argsFor(1)[0]).toContain(
1114 - 'This error was initially handled by the error boundary ErrorBoundaryComponent.\n' +
1115 - 'Recreating the tree from scratch failed so React will unmount the tree.',
1116 - );
1174 + if (__DEV__) {
1175 + expect(console.error.calls.argsFor(0)[0]).toContain(
1176 + 'The above error occurred in the <ErrorThrowingComponent> component:',
1177 + );
1178 + expect(console.error.calls.argsFor(0)[0]).toContain(
1179 + 'React will try to recreate this component tree from scratch ' +
1180 + 'using the error boundary you provided, ErrorBoundaryComponent.',
1181 + );
1182 + expect(console.error.calls.argsFor(1)[0]).toContain(
1183 + 'The above error occurred in the <ErrorThrowingComponent> component:',
1184 + );
1185 + expect(console.error.calls.argsFor(1)[0]).toContain(
1186 + 'This error was initially handled by the error boundary ErrorBoundaryComponent.\n' +
1187 + 'Recreating the tree from scratch failed so React will unmount the tree.',
1188 + );
1189 + }
1190 });
1191 });
1192
1193 it('resets instance variables before unmounting failed node', () => {
1121 - spyOn(console, 'error');
1122 -
1194 class ErrorBoundary extends React.Component {
1195 state = {error: null};
1196 componentDidCatch(error) {
packages/react-reconciler/src/__tests__/ReactIncrementalPerf-test.js
+2
@@ -115,6 +115,8 @@ describe('ReactDebugFiberPerf', () => {
115 resetFlamechart();
116 global.performance = createUserTimingPolyfill();
117
118 + require('shared/ReactFeatureFlags').enableUserTimingAPI = true;
119 +
120 // Import after the polyfill is set up:
121 React = require('react');
122 ReactNoop = require('react-noop-renderer');
packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js
+11 -9
@@ -986,7 +986,7 @@ describe('ReactIncrementalSideEffects', () => {
986 });
987
988 it('invokes ref callbacks after insertion/update/unmount', () => {
989 - spyOn(console, 'error');
989 + spyOnDev(console, 'error');
990 var classInstance = null;
991
992 var ops = [];
@@ -1044,14 +1044,16 @@ describe('ReactIncrementalSideEffects', () => {
1044 null,
1045 ]);
1046
1047 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1048 - 'Warning: Stateless function components cannot be given refs. ' +
1049 - 'Attempts to access this ref will fail.\n\nCheck the render method ' +
1050 - 'of `Foo`.\n' +
1051 - ' in FunctionalComponent (at **)\n' +
1052 - ' in div (at **)\n' +
1053 - ' in Foo (at **)',
1054 - );
1047 + if (__DEV__) {
1048 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1049 + 'Warning: Stateless function components cannot be given refs. ' +
1050 + 'Attempts to access this ref will fail.\n\nCheck the render method ' +
1051 + 'of `Foo`.\n' +
1052 + ' in FunctionalComponent (at **)\n' +
1053 + ' in div (at **)\n' +
1054 + ' in Foo (at **)',
1055 + );
1056 + }
1057 });
1058
1059 // TODO: Test that mounts, updates, refs, unmounts and deletions happen in the
packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js
+13 -9
@@ -320,7 +320,7 @@ describe('ReactIncrementalUpdates', () => {
320 });
321
322 it('enqueues setState inside an updater function as if the in-progress update is progressed (and warns)', () => {
323 - spyOn(console, 'error');
323 + spyOnDev(console, 'error');
324 let instance;
325 let ops = [];
326 class Foo extends React.Component {
@@ -352,13 +352,15 @@ describe('ReactIncrementalUpdates', () => {
352 ]);
353 expect(instance.state).toEqual({a: 'a', b: 'b'});
354
355 - expectDev(console.error.calls.count()).toBe(1);
356 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
357 - 'An update (setState, replaceState, or forceUpdate) was scheduled ' +
358 - 'from inside an update function. Update functions should be pure, ' +
359 - 'with zero side-effects. Consider using componentDidUpdate or a ' +
360 - 'callback.',
361 - );
355 + if (__DEV__) {
356 + expect(console.error.calls.count()).toBe(1);
357 + expect(console.error.calls.argsFor(0)[0]).toContain(
358 + 'An update (setState, replaceState, or forceUpdate) was scheduled ' +
359 + 'from inside an update function. Update functions should be pure, ' +
360 + 'with zero side-effects. Consider using componentDidUpdate or a ' +
361 + 'callback.',
362 + );
363 + }
364
365 // Test deduplication
366 instance.setState(function a() {
@@ -366,6 +368,8 @@ describe('ReactIncrementalUpdates', () => {
368 return {b: 'b'};
369 });
370 ReactNoop.flush();
369 - expectDev(console.error.calls.count()).toBe(1);
371 + if (__DEV__) {
372 + expect(console.error.calls.count()).toBe(1);
373 + }
374 });
375 });
packages/react-test-renderer/src/__tests__/ReactShallowRenderer-test.js
+27 -21
@@ -770,7 +770,7 @@ describe('ReactShallowRenderer', () => {
770 });
771
772 it('can fail context when shallowly rendering', () => {
773 - spyOn(console, 'error');
773 + spyOnDev(console, 'error');
774
775 class SimpleComponent extends React.Component {
776 static contextTypes = {
@@ -784,18 +784,20 @@ describe('ReactShallowRenderer', () => {
784
785 const shallowRenderer = createRenderer();
786 shallowRenderer.render(<SimpleComponent />);
787 - expectDev(console.error.calls.count()).toBe(1);
788 - expect(
789 - console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)'),
790 - ).toBe(
791 - 'Warning: Failed context type: The context `name` is marked as ' +
792 - 'required in `SimpleComponent`, but its value is `undefined`.\n' +
793 - ' in SimpleComponent (at **)',
794 - );
787 + if (__DEV__) {
788 + expect(console.error.calls.count()).toBe(1);
789 + expect(
790 + console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)'),
791 + ).toBe(
792 + 'Warning: Failed context type: The context `name` is marked as ' +
793 + 'required in `SimpleComponent`, but its value is `undefined`.\n' +
794 + ' in SimpleComponent (at **)',
795 + );
796 + }
797 });
798
799 it('should warn about propTypes (but only once)', () => {
798 - spyOn(console, 'error');
800 + spyOnDev(console, 'error');
801
802 class SimpleComponent extends React.Component {
803 render() {
@@ -810,14 +812,16 @@ describe('ReactShallowRenderer', () => {
812 const shallowRenderer = createRenderer();
813 shallowRenderer.render(React.createElement(SimpleComponent, {name: 123}));
814
813 - expect(console.error.calls.count()).toBe(1);
814 - expect(
815 - console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)'),
816 - ).toBe(
817 - 'Warning: Failed prop type: Invalid prop `name` of type `number` ' +
818 - 'supplied to `SimpleComponent`, expected `string`.\n' +
819 - ' in SimpleComponent',
820 - );
815 + if (__DEV__) {
816 + expect(console.error.calls.count()).toBe(1);
817 + expect(
818 + console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)'),
819 + ).toBe(
820 + 'Warning: Failed prop type: Invalid prop `name` of type `number` ' +
821 + 'supplied to `SimpleComponent`, expected `string`.\n' +
822 + ' in SimpleComponent',
823 + );
824 + }
825 });
826
827 it('should enable rendering of cloned element', () => {
@@ -846,7 +850,7 @@ describe('ReactShallowRenderer', () => {
850 });
851
852 it('throws usefully when rendering badly-typed elements', () => {
849 - spyOn(console, 'error');
853 + spyOnDev(console, 'error');
854 const shallowRenderer = createRenderer();
855
856 var Undef = undefined;
@@ -873,7 +877,9 @@ describe('ReactShallowRenderer', () => {
877 'components, but the provided element type was `object`.',
878 );
879
876 - // One warning for each element creation
877 - expectDev(console.error.calls.count()).toBe(4);
880 + if (__DEV__) {
881 + // One warning for each element creation
882 + expect(console.error.calls.count()).toBe(4);
883 + }
884 });
885 });
packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.js
+10 -8
@@ -263,7 +263,7 @@ describe('ReactTestRenderer', () => {
263 });
264
265 it('warns correctly for refs on SFCs', () => {
266 - spyOn(console, 'error');
266 + spyOnDev(console, 'error');
267 function Bar() {
268 return <div>Hello, world</div>;
269 }
@@ -279,13 +279,15 @@ describe('ReactTestRenderer', () => {
279 }
280 ReactTestRenderer.create(<Baz />);
281 ReactTestRenderer.create(<Foo />);
282 - expectDev(console.error.calls.count()).toBe(1);
283 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
284 - 'Warning: Stateless function components cannot be given refs. Attempts ' +
285 - 'to access this ref will fail.\n\nCheck the render method of `Foo`.\n' +
286 - ' in Bar (at **)\n' +
287 - ' in Foo (at **)',
288 - );
282 + if (__DEV__) {
283 + expect(console.error.calls.count()).toBe(1);
284 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
285 + 'Warning: Stateless function components cannot be given refs. Attempts ' +
286 + 'to access this ref will fail.\n\nCheck the render method of `Foo`.\n' +
287 + ' in Bar (at **)\n' +
288 + ' in Foo (at **)',
289 + );
290 + }
291 });
292
293 it('allows an optional createNodeMock function', () => {
packages/react/src/__tests__/ReactChildren-test.js
+44 -28
@@ -350,7 +350,7 @@ describe('ReactChildren', () => {
350 });
351
352 it('should be called for each child in an iterable without keys', () => {
353 - spyOn(console, 'error');
353 + spyOnDev(console, 'error');
354 var threeDivIterable = {
355 '@@iterator': function() {
356 var i = 0;
@@ -384,11 +384,13 @@ describe('ReactChildren', () => {
384
385 React.Children.forEach(instance.props.children, callback, context);
386 assertCalls();
387 - expectDev(console.error.calls.count()).toBe(1);
388 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
389 - 'Warning: Each child in an array or iterator should have a unique "key" prop.',
390 - );
391 - console.error.calls.reset();
387 + if (__DEV__) {
388 + expect(console.error.calls.count()).toBe(1);
389 + expect(console.error.calls.argsFor(0)[0]).toContain(
390 + 'Warning: Each child in an array or iterator should have a unique "key" prop.',
391 + );
392 + console.error.calls.reset();
393 + }
394
395 var mappedChildren = React.Children.map(
396 instance.props.children,
@@ -396,7 +398,9 @@ describe('ReactChildren', () => {
398 context,
399 );
400 assertCalls();
399 - expectDev(console.error.calls.count()).toBe(0);
401 + if (__DEV__) {
402 + expect(console.error.calls.count()).toBe(0);
403 + }
404 expect(mappedChildren).toEqual([
405 <div key=".0" />,
406 <div key=".1" />,
@@ -918,8 +922,11 @@ describe('ReactChildren', () => {
922 React.Children.forEach({a: 1, b: 2}, function() {}, null);
923 }).toThrowError(
924 'Objects are not valid as a React child (found: object with keys ' +
921 - '{a, b}). If you meant to render a collection of children, use an ' +
922 - 'array instead.',
925 + '{a, b}).' +
926 + (__DEV__
927 + ? ' If you meant to render a collection of children, use an ' +
928 + 'array instead.'
929 + : ''),
930 );
931 });
932
@@ -929,14 +936,17 @@ describe('ReactChildren', () => {
936 expect(function() {
937 React.Children.forEach(/abc/, function() {}, null);
938 }).toThrowError(
932 - 'Objects are not valid as a React child (found: /abc/). If you meant ' +
933 - 'to render a collection of children, use an array instead.',
939 + 'Objects are not valid as a React child (found: /abc/).' +
940 + (__DEV__
941 + ? ' If you meant to render a collection of children, use an ' +
942 + 'array instead.'
943 + : ''),
944 );
945 });
946
947 describe('with fragments enabled', () => {
948 it('warns for keys for arrays of elements in a fragment', () => {
939 - spyOn(console, 'error');
949 + spyOnDev(console, 'error');
950 class ComponentReturningArray extends React.Component {
951 render() {
952 return [<div />, <div />];
@@ -945,17 +955,19 @@ describe('ReactChildren', () => {
955
956 ReactTestUtils.renderIntoDocument(<ComponentReturningArray />);
957
948 - expectDev(console.error.calls.count()).toBe(1);
949 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
950 - 'Warning: ' +
951 - 'Each child in an array or iterator should have a unique "key" prop.' +
952 - ' See https://fb.me/react-warning-keys for more information.' +
953 - '\n in ComponentReturningArray (at **)',
954 - );
958 + if (__DEV__) {
959 + expect(console.error.calls.count()).toBe(1);
960 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
961 + 'Warning: ' +
962 + 'Each child in an array or iterator should have a unique "key" prop.' +
963 + ' See https://fb.me/react-warning-keys for more information.' +
964 + '\n in ComponentReturningArray (at **)',
965 + );
966 + }
967 });
968
969 it('does not warn when there are keys on elements in a fragment', () => {
958 - spyOn(console, 'error');
970 + spyOnDev(console, 'error');
971 class ComponentReturningArray extends React.Component {
972 render() {
973 return [<div key="foo" />, <div key="bar" />];
@@ -964,20 +976,24 @@ describe('ReactChildren', () => {
976
977 ReactTestUtils.renderIntoDocument(<ComponentReturningArray />);
978
967 - expectDev(console.error.calls.count()).toBe(0);
979 + if (__DEV__) {
980 + expect(console.error.calls.count()).toBe(0);
981 + }
982 });
983
984 it('warns for keys for arrays at the top level', () => {
971 - spyOn(console, 'error');
985 + spyOnDev(console, 'error');
986
987 ReactTestUtils.renderIntoDocument([<div />, <div />]);
988
975 - expectDev(console.error.calls.count()).toBe(1);
976 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
977 - 'Warning: ' +
978 - 'Each child in an array or iterator should have a unique "key" prop.' +
979 - ' See https://fb.me/react-warning-keys for more information.',
980 - );
989 + if (__DEV__) {
990 + expect(console.error.calls.count()).toBe(1);
991 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
992 + 'Warning: ' +
993 + 'Each child in an array or iterator should have a unique "key" prop.' +
994 + ' See https://fb.me/react-warning-keys for more information.',
995 + );
996 + }
997 });
998 });
999 });
packages/react/src/__tests__/ReactClassEquivalence-test.js
+1 -4
@@ -10,7 +10,6 @@
10 'use strict';
11
12 var spawnSync = require('child_process').spawnSync;
13 -var path = require('path');
13
14 describe('ReactClassEquivalence', () => {
15 it('tests the same thing for es6 classes and CoffeeScript', () => {
@@ -28,9 +27,7 @@ describe('ReactClassEquivalence', () => {
27
28 function runJest(testFile) {
29 var cwd = process.cwd();
31 - var extension = process.platform === 'win32' ? '.cmd' : '';
32 - var jestBin = path.resolve('node_modules', '.bin', 'jest' + extension);
33 - var result = spawnSync(jestBin, [testFile], {
30 + var result = spawnSync('yarn', ['test', testFile], {
31 cwd,
32 env: Object.assign({}, process.env, {
33 REACT_CLASS_EQUIVALENCE_TEST: 'true',
packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee
+60 -45
@@ -46,13 +46,14 @@ describe 'ReactCoffeeScriptClass', ->
46 expect(Foo.name).toBe 'Foo'
47
48 it 'throws if no render function is defined', ->
49 - spyOn console, 'error'
49 + spyOnDev console, 'error'
50 class Foo extends React.Component
51 expect(->
52 ReactDOM.render React.createElement(Foo), container
53 ).toThrow()
54 - expect(console.error.calls.count()).toBe(1)
55 - expect(console.error.calls.argsFor(0)[0]).toContain('No `render` method found on the returned component instance')
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')
57 undefined
58
59 it 'renders a simple stateless component with prop', ->
@@ -157,11 +158,20 @@ describe 'ReactCoffeeScriptClass', ->
158 render: ->
159 span()
160
160 - expect(->
161 - test React.createElement(Foo), 'span', ''
162 - ).toThrowError(
163 - 'Foo.state: must be set to an object or null'
164 - )
161 + if __DEV__
162 + expect(->
163 + test React.createElement(Foo), 'SPAN', ''
164 + ).toThrowError(
165 + 'Foo.state: must be set to an object or null'
166 + )
167 + else
168 + # This is a difference between development and production.
169 + # I'm not sure if this is intentional, as generally we avoid this.
170 + # TODO: investigate if this was intentional or an oversight.
171 + # https://github.com/facebook/react/issues/11618
172 + expect(->
173 + test React.createElement(Foo), 'SPAN', ''
174 + ).not.toThrowError()
175 undefined
176
177 it 'should render with null in the initial state property', ->
@@ -281,7 +291,7 @@ describe 'ReactCoffeeScriptClass', ->
291
292 it 'warns when classic properties are defined on the instance,
293 but does not invoke them.', ->
284 - spyOn console, 'error'
294 + spyOnDev console, 'error'
295 getInitialStateWasCalled = false
296 getDefaultPropsWasCalled = false
297 class Foo extends React.Component
@@ -304,24 +314,25 @@ describe 'ReactCoffeeScriptClass', ->
314 test React.createElement(Foo), 'SPAN', 'foo'
315 expect(getInitialStateWasCalled).toBe false
316 expect(getDefaultPropsWasCalled).toBe false
307 - expect(console.error.calls.count()).toBe 4
308 - expect(console.error.calls.argsFor(0)[0]).toContain(
309 - 'getInitialState was defined on Foo, a plain JavaScript class.'
310 - )
311 - expect(console.error.calls.argsFor(1)[0]).toContain(
312 - 'getDefaultProps was defined on Foo, a plain JavaScript class.'
313 - )
314 - expect(console.error.calls.argsFor(2)[0]).toContain(
315 - 'propTypes was defined as an instance property on Foo.'
316 - )
317 - expect(console.error.calls.argsFor(3)[0]).toContain(
318 - 'contextTypes was defined as an instance property on Foo.'
319 - )
317 + if __DEV__
318 + expect(console.error.calls.count()).toBe 4
319 + expect(console.error.calls.argsFor(0)[0]).toContain(
320 + 'getInitialState was defined on Foo, a plain JavaScript class.'
321 + )
322 + expect(console.error.calls.argsFor(1)[0]).toContain(
323 + 'getDefaultProps was defined on Foo, a plain JavaScript class.'
324 + )
325 + expect(console.error.calls.argsFor(2)[0]).toContain(
326 + 'propTypes was defined as an instance property on Foo.'
327 + )
328 + expect(console.error.calls.argsFor(3)[0]).toContain(
329 + 'contextTypes was defined as an instance property on Foo.'
330 + )
331 undefined
332
333 it 'does not warn about getInitialState() on class components
334 if state is also defined.', ->
324 - spyOn console, 'error'
335 + spyOnDev console, 'error'
336 class Foo extends React.Component
337 constructor: (props) ->
338 super props
@@ -335,11 +346,12 @@ describe 'ReactCoffeeScriptClass', ->
346 className: 'foo'
347
348 test React.createElement(Foo), 'SPAN', 'foo'
338 - expect(console.error.calls.count()).toBe 0
349 + if __DEV__
350 + expect(console.error.calls.count()).toBe 0
351 undefined
352
353 it 'should warn when misspelling shouldComponentUpdate', ->
342 - spyOn console, 'error'
354 + spyOnDev console, 'error'
355 class NamedComponent extends React.Component
356 componentShouldUpdate: ->
357 false
@@ -349,16 +361,17 @@ describe 'ReactCoffeeScriptClass', ->
361 className: 'foo'
362
363 test React.createElement(NamedComponent), 'SPAN', 'foo'
352 - expect(console.error.calls.count()).toBe 1
353 - expect(console.error.calls.argsFor(0)[0]).toBe(
354 - 'Warning: NamedComponent has a method called componentShouldUpdate().
355 - Did you mean shouldComponentUpdate()? The name is phrased as a
356 - question because the function is expected to return a value.'
357 - )
364 + if __DEV__
365 + expect(console.error.calls.count()).toBe 1
366 + expect(console.error.calls.argsFor(0)[0]).toBe(
367 + 'Warning: NamedComponent has a method called componentShouldUpdate().
368 + Did you mean shouldComponentUpdate()? The name is phrased as a
369 + question because the function is expected to return a value.'
370 + )
371 undefined
372
373 it 'should warn when misspelling componentWillReceiveProps', ->
361 - spyOn console, 'error'
374 + spyOnDev console, 'error'
375 class NamedComponent extends React.Component
376 componentWillRecieveProps: ->
377 false
@@ -368,26 +381,28 @@ describe 'ReactCoffeeScriptClass', ->
381 className: 'foo'
382
383 test React.createElement(NamedComponent), 'SPAN', 'foo'
371 - expect(console.error.calls.count()).toBe 1
372 - expect(console.error.calls.argsFor(0)[0]).toBe(
373 - 'Warning: NamedComponent has a method called componentWillRecieveProps().
374 - Did you mean componentWillReceiveProps()?'
375 - )
384 + if __DEV__
385 + expect(console.error.calls.count()).toBe 1
386 + expect(console.error.calls.argsFor(0)[0]).toBe(
387 + 'Warning: NamedComponent has a method called componentWillRecieveProps().
388 + Did you mean componentWillReceiveProps()?'
389 + )
390 undefined
391
392 it 'should throw AND warn when trying to access classic APIs', ->
379 - spyOn console, 'warn'
393 + spyOnDev console, 'warn'
394 instance =
395 test Inner(name: 'foo'), 'DIV', 'foo'
396 expect(-> instance.replaceState {}).toThrow()
397 expect(-> instance.isMounted()).toThrow()
384 - expect(console.warn.calls.count()).toBe 2
385 - expect(console.warn.calls.argsFor(0)[0]).toContain(
386 - 'replaceState(...) is deprecated in plain JavaScript React classes'
387 - )
388 - expect(console.warn.calls.argsFor(1)[0]).toContain(
389 - 'isMounted(...) is deprecated in plain JavaScript React classes'
390 - )
398 + if __DEV__
399 + expect(console.warn.calls.count()).toBe 2
400 + expect(console.warn.calls.argsFor(0)[0]).toContain(
401 + 'replaceState(...) is deprecated in plain JavaScript React classes'
402 + )
403 + expect(console.warn.calls.argsFor(1)[0]).toContain(
404 + 'isMounted(...) is deprecated in plain JavaScript React classes'
405 + )
406 undefined
407
408 it 'supports this.context passed via getChildContext', ->
packages/react/src/__tests__/ReactContextValidator-test.js
+67 -49
@@ -161,7 +161,7 @@ describe('ReactContextValidator', () => {
161 });
162
163 it('should check context types', () => {
164 - spyOn(console, 'error');
164 + spyOnDev(console, 'error');
165
166 class Component extends React.Component {
167 render() {
@@ -174,13 +174,15 @@ describe('ReactContextValidator', () => {
174
175 ReactTestUtils.renderIntoDocument(<Component />);
176
177 - expectDev(console.error.calls.count()).toBe(1);
178 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
179 - 'Warning: Failed context type: ' +
180 - 'The context `foo` is marked as required in `Component`, but its value ' +
181 - 'is `undefined`.\n' +
182 - ' in Component (at **)',
183 - );
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 + }
186
187 class ComponentInFooStringContext extends React.Component {
188 getChildContext() {
@@ -202,7 +204,9 @@ describe('ReactContextValidator', () => {
204 );
205
206 // Previous call should not error
205 - expectDev(console.error.calls.count()).toBe(1);
207 + if (__DEV__) {
208 + expect(console.error.calls.count()).toBe(1);
209 + }
210
211 class ComponentInFooNumberContext extends React.Component {
212 getChildContext() {
@@ -223,18 +227,20 @@ describe('ReactContextValidator', () => {
227 <ComponentInFooNumberContext fooValue={123} />,
228 );
229
226 - expectDev(console.error.calls.count()).toBe(2);
227 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
228 - 'Warning: Failed context type: ' +
229 - 'Invalid context `foo` of type `number` supplied ' +
230 - 'to `Component`, expected `string`.\n' +
231 - ' in Component (at **)\n' +
232 - ' in ComponentInFooNumberContext (at **)',
233 - );
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 + }
240 });
241
242 it('should check child context types', () => {
237 - spyOn(console, 'error');
243 + spyOnDev(console, 'error');
244
245 class Component extends React.Component {
246 getChildContext() {
@@ -251,23 +257,27 @@ describe('ReactContextValidator', () => {
257 };
258
259 ReactTestUtils.renderIntoDocument(<Component testContext={{bar: 123}} />);
254 - expectDev(console.error.calls.count()).toBe(1);
255 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
256 - 'Warning: Failed child context type: ' +
257 - 'The child context `foo` is marked as required in `Component`, but its ' +
258 - 'value is `undefined`.\n' +
259 - ' in Component (at **)',
260 - );
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}} />);
271
264 - expectDev(console.error.calls.count()).toBe(2);
265 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
266 - 'Warning: Failed child context type: ' +
267 - 'Invalid child context `foo` of type `number` ' +
268 - 'supplied to `Component`, expected `string`.\n' +
269 - ' in Component (at **)',
270 - );
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 + }
281
282 ReactTestUtils.renderIntoDocument(
283 <Component testContext={{foo: 'foo', bar: 123}} />,
@@ -275,14 +285,16 @@ describe('ReactContextValidator', () => {
285
286 ReactTestUtils.renderIntoDocument(<Component testContext={{foo: 'foo'}} />);
287
278 - // Previous calls should not log errors
279 - expectDev(console.error.calls.count()).toBe(2);
288 + if (__DEV__) {
289 + // Previous calls should not log errors
290 + expect(console.error.calls.count()).toBe(2);
291 + }
292 });
293
294 // TODO (bvaughn) Remove this test and the associated behavior in the future.
295 // It has only been added in Fiber to match the (unintentional) behavior in Stack.
296 it('should warn (but not error) if getChildContext method is missing', () => {
285 - spyOn(console, 'error');
297 + spyOnDev(console, 'error');
298
299 class ComponentA extends React.Component {
300 static childContextTypes = {
@@ -302,29 +314,35 @@ describe('ReactContextValidator', () => {
314 }
315
316 ReactTestUtils.renderIntoDocument(<ComponentA />);
305 - expectDev(console.error.calls.count()).toBe(1);
306 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
307 - 'Warning: ComponentA.childContextTypes is specified but there is no ' +
308 - 'getChildContext() method on the instance. You can either define ' +
309 - 'getChildContext() on ComponentA or remove childContextTypes from it.',
310 - );
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 + }
325
326 // Warnings should be deduped by component type
327 ReactTestUtils.renderIntoDocument(<ComponentA />);
314 - expectDev(console.error.calls.count()).toBe(1);
328 + if (__DEV__) {
329 + expect(console.error.calls.count()).toBe(1);
330 + }
331 ReactTestUtils.renderIntoDocument(<ComponentB />);
316 - expectDev(console.error.calls.count()).toBe(2);
317 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
318 - 'Warning: ComponentB.childContextTypes is specified but there is no ' +
319 - 'getChildContext() method on the instance. You can either define ' +
320 - 'getChildContext() on ComponentB or remove childContextTypes from it.',
321 - );
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 + }
340 });
341
342 // TODO (bvaughn) Remove this test and the associated behavior in the future.
343 // It has only been added in Fiber to match the (unintentional) behavior in Stack.
344 it('should pass parent context if getChildContext method is missing', () => {
327 - spyOn(console, 'error');
345 + spyOnDev(console, 'error');
346
347 class ParentContextProvider extends React.Component {
348 static childContextTypes = {
packages/react/src/__tests__/ReactES6Class-test.js
+68 -48
@@ -56,15 +56,17 @@ describe('ReactES6Class', () => {
56 });
57
58 it('throws if no render function is defined', () => {
59 - spyOn(console, 'error');
59 + spyOnDev(console, 'error');
60 class Foo extends React.Component {}
61 expect(() => ReactDOM.render(<Foo />, container)).toThrow();
62
63 - expect(console.error.calls.count()).toBe(1);
64 - expect(console.error.calls.argsFor(0)[0]).toBe(
65 - 'Warning: Foo(...): No `render` method found on the returned component ' +
66 - 'instance: you may have forgotten to define `render`.',
67 - );
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 + }
70 });
71
72 it('renders a simple stateless component with prop', () => {
@@ -172,9 +174,17 @@ describe('ReactES6Class', () => {
174 return <span />;
175 }
176 }
175 - expect(() => test(<Foo />, 'span', '')).toThrowError(
176 - 'Foo.state: must be set to an object or null',
177 - );
177 + if (__DEV__) {
178 + expect(() => test(<Foo />, 'SPAN', '')).toThrowError(
179 + 'Foo.state: must be set to an object or null',
180 + );
181 + } else {
182 + // This is a difference between development and production.
183 + // I'm not sure if this is intentional, as generally we avoid this.
184 + // TODO: investigate if this was intentional or an oversight.
185 + // https://github.com/facebook/react/issues/11618
186 + expect(() => test(<Foo />, 'SPAN', '')).not.toThrowError();
187 + }
188 });
189 });
190
@@ -302,7 +312,7 @@ describe('ReactES6Class', () => {
312 });
313
314 it('warns when classic properties are defined on the instance, but does not invoke them.', () => {
305 - spyOn(console, 'error');
315 + spyOnDev(console, 'error');
316 var getDefaultPropsWasCalled = false;
317 var getInitialStateWasCalled = false;
318 class Foo extends React.Component {
@@ -326,23 +336,25 @@ describe('ReactES6Class', () => {
336 test(<Foo />, 'SPAN', 'foo');
337 expect(getInitialStateWasCalled).toBe(false);
338 expect(getDefaultPropsWasCalled).toBe(false);
329 - expect(console.error.calls.count()).toBe(4);
330 - expect(console.error.calls.argsFor(0)[0]).toContain(
331 - 'getInitialState was defined on Foo, a plain JavaScript class.',
332 - );
333 - expect(console.error.calls.argsFor(1)[0]).toContain(
334 - 'getDefaultProps was defined on Foo, a plain JavaScript class.',
335 - );
336 - expect(console.error.calls.argsFor(2)[0]).toContain(
337 - 'propTypes was defined as an instance property on Foo.',
338 - );
339 - expect(console.error.calls.argsFor(3)[0]).toContain(
340 - 'contextTypes was defined as an instance property on Foo.',
341 - );
339 + if (__DEV__) {
340 + expect(console.error.calls.count()).toBe(4);
341 + expect(console.error.calls.argsFor(0)[0]).toContain(
342 + 'getInitialState was defined on Foo, a plain JavaScript class.',
343 + );
344 + expect(console.error.calls.argsFor(1)[0]).toContain(
345 + 'getDefaultProps was defined on Foo, a plain JavaScript class.',
346 + );
347 + expect(console.error.calls.argsFor(2)[0]).toContain(
348 + 'propTypes was defined as an instance property on Foo.',
349 + );
350 + expect(console.error.calls.argsFor(3)[0]).toContain(
351 + 'contextTypes was defined as an instance property on Foo.',
352 + );
353 + }
354 });
355
356 it('does not warn about getInitialState() on class components if state is also defined.', () => {
345 - spyOn(console, 'error');
357 + spyOnDev(console, 'error');
358 class Foo extends React.Component {
359 state = this.getInitialState();
360 getInitialState() {
@@ -353,11 +365,13 @@ describe('ReactES6Class', () => {
365 }
366 }
367 test(<Foo />, 'SPAN', 'foo');
356 - expect(console.error.calls.count()).toBe(0);
368 + if (__DEV__) {
369 + expect(console.error.calls.count()).toBe(0);
370 + }
371 });
372
373 it('should warn when misspelling shouldComponentUpdate', () => {
360 - spyOn(console, 'error');
374 + spyOnDev(console, 'error');
375
376 class NamedComponent extends React.Component {
377 componentShouldUpdate() {
@@ -369,17 +383,19 @@ describe('ReactES6Class', () => {
383 }
384 test(<NamedComponent />, 'SPAN', 'foo');
385
372 - expect(console.error.calls.count()).toBe(1);
373 - expect(console.error.calls.argsFor(0)[0]).toBe(
374 - 'Warning: ' +
375 - 'NamedComponent has a method called componentShouldUpdate(). Did you ' +
376 - 'mean shouldComponentUpdate()? The name is phrased as a question ' +
377 - 'because the function is expected to return a value.',
378 - );
386 + if (__DEV__) {
387 + expect(console.error.calls.count()).toBe(1);
388 + expect(console.error.calls.argsFor(0)[0]).toBe(
389 + 'Warning: ' +
390 + 'NamedComponent has a method called componentShouldUpdate(). Did you ' +
391 + 'mean shouldComponentUpdate()? The name is phrased as a question ' +
392 + 'because the function is expected to return a value.',
393 + );
394 + }
395 });
396
397 it('should warn when misspelling componentWillReceiveProps', () => {
382 - spyOn(console, 'error');
398 + spyOnDev(console, 'error');
399
400 class NamedComponent extends React.Component {
401 componentWillRecieveProps() {
@@ -391,26 +407,30 @@ describe('ReactES6Class', () => {
407 }
408 test(<NamedComponent />, 'SPAN', 'foo');
409
394 - expect(console.error.calls.count()).toBe(1);
395 - expect(console.error.calls.argsFor(0)[0]).toBe(
396 - 'Warning: ' +
397 - 'NamedComponent has a method called componentWillRecieveProps(). Did ' +
398 - 'you mean componentWillReceiveProps()?',
399 - );
410 + if (__DEV__) {
411 + expect(console.error.calls.count()).toBe(1);
412 + expect(console.error.calls.argsFor(0)[0]).toBe(
413 + 'Warning: ' +
414 + 'NamedComponent has a method called componentWillRecieveProps(). Did ' +
415 + 'you mean componentWillReceiveProps()?',
416 + );
417 + }
418 });
419
420 it('should throw AND warn when trying to access classic APIs', () => {
403 - spyOn(console, 'warn');
421 + spyOnDev(console, 'warn');
422 var instance = test(<Inner name="foo" />, 'DIV', 'foo');
423 expect(() => instance.replaceState({})).toThrow();
424 expect(() => instance.isMounted()).toThrow();
407 - expect(console.warn.calls.count()).toBe(2);
408 - expect(console.warn.calls.argsFor(0)[0]).toContain(
409 - 'replaceState(...) is deprecated in plain JavaScript React classes',
410 - );
411 - expect(console.warn.calls.argsFor(1)[0]).toContain(
412 - 'isMounted(...) is deprecated in plain JavaScript React classes',
413 - );
425 + if (__DEV__) {
426 + expect(console.warn.calls.count()).toBe(2);
427 + expect(console.warn.calls.argsFor(0)[0]).toContain(
428 + 'replaceState(...) is deprecated in plain JavaScript React classes',
429 + );
430 + expect(console.warn.calls.argsFor(1)[0]).toContain(
431 + 'isMounted(...) is deprecated in plain JavaScript React classes',
432 + );
433 + }
434 });
435
436 it('supports this.context passed via getChildContext', () => {
packages/react/src/__tests__/ReactElement-test.js
+94 -62
@@ -50,13 +50,15 @@ describe('ReactElement', () => {
50 expect(element.type).toBe(ComponentClass);
51 expect(element.key).toBe(null);
52 expect(element.ref).toBe(null);
53 - expect(Object.isFrozen(element)).toBe(true);
54 - expect(Object.isFrozen(element.props)).toBe(true);
53 + if (__DEV__) {
54 + expect(Object.isFrozen(element)).toBe(true);
55 + expect(Object.isFrozen(element.props)).toBe(true);
56 + }
57 expect(element.props).toEqual({});
58 });
59
60 it('should warn when `key` is being accessed on composite element', () => {
59 - spyOn(console, 'error');
61 + spyOnDev(console, 'error');
62 var container = document.createElement('div');
63 class Child extends React.Component {
64 render() {
@@ -74,33 +76,41 @@ describe('ReactElement', () => {
76 );
77 }
78 }
77 - expect(console.error.calls.count()).toBe(0);
79 + if (__DEV__) {
80 + expect(console.error.calls.count()).toBe(0);
81 + }
82 ReactDOM.render(<Parent />, container);
79 - expectDev(console.error.calls.count()).toBe(1);
80 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
81 - 'Child: `key` is not a prop. Trying to access it will result ' +
82 - 'in `undefined` being returned. If you need to access the same ' +
83 - 'value within the child component, you should pass it as a different ' +
84 - 'prop. (https://fb.me/react-special-props)',
85 - );
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 + }
92 });
93
94 it('should warn when `key` is being accessed on a host element', () => {
89 - spyOn(console, 'error');
95 + spyOnDev(console, 'error');
96 var element = <div key="3" />;
91 - expectDev(console.error.calls.count()).toBe(0);
97 + if (__DEV__) {
98 + expect(console.error.calls.count()).toBe(0);
99 + }
100 void element.props.key;
93 - expectDev(console.error.calls.count()).toBe(1);
94 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
95 - 'div: `key` is not a prop. Trying to access it will result ' +
96 - 'in `undefined` being returned. If you need to access the same ' +
97 - 'value within the child component, you should pass it as a different ' +
98 - 'prop. (https://fb.me/react-special-props)',
99 - );
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 + }
110 });
111
112 it('should warn when `ref` is being accessed', () => {
103 - spyOn(console, 'error');
113 + spyOnDev(console, 'error');
114 var container = document.createElement('div');
115 class Child extends React.Component {
116 render() {
@@ -116,15 +126,19 @@ describe('ReactElement', () => {
126 );
127 }
128 }
119 - expect(console.error.calls.count()).toBe(0);
129 + if (__DEV__) {
130 + expect(console.error.calls.count()).toBe(0);
131 + }
132 ReactDOM.render(<Parent />, container);
121 - expectDev(console.error.calls.count()).toBe(1);
122 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
123 - 'Child: `ref` is not a prop. Trying to access it will result ' +
124 - 'in `undefined` being returned. If you need to access the same ' +
125 - 'value within the child component, you should pass it as a different ' +
126 - 'prop. (https://fb.me/react-special-props)',
127 - );
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 + }
142 });
143
144 it('allows a string to be passed as the type', () => {
@@ -132,14 +146,20 @@ describe('ReactElement', () => {
146 expect(element.type).toBe('div');
147 expect(element.key).toBe(null);
148 expect(element.ref).toBe(null);
135 - expect(Object.isFrozen(element)).toBe(true);
136 - expect(Object.isFrozen(element.props)).toBe(true);
149 + if (__DEV__) {
150 + expect(Object.isFrozen(element)).toBe(true);
151 + expect(Object.isFrozen(element.props)).toBe(true);
152 + }
153 expect(element.props).toEqual({});
154 });
155
156 it('returns an immutable element', () => {
157 var element = React.createFactory(ComponentClass)();
142 - expect(() => (element.type = 'div')).toThrow();
158 + if (__DEV__) {
159 + expect(() => (element.type = 'div')).toThrow();
160 + } else {
161 + expect(() => (element.type = 'div')).not.toThrow();
162 + }
163 });
164
165 it('does not reuse the original config object', () => {
@@ -165,8 +185,10 @@ describe('ReactElement', () => {
185 expect(element.type).toBe(ComponentClass);
186 expect(element.key).toBe('12');
187 expect(element.ref).toBe('34');
168 - expect(Object.isFrozen(element)).toBe(true);
169 - expect(Object.isFrozen(element.props)).toBe(true);
188 + if (__DEV__) {
189 + expect(Object.isFrozen(element)).toBe(true);
190 + expect(Object.isFrozen(element.props)).toBe(true);
191 + }
192 expect(element.props).toEqual({foo: '56'});
193 });
194
@@ -179,8 +201,10 @@ describe('ReactElement', () => {
201 expect(element.type).toBe(ComponentClass);
202 expect(element.key).toBe('null');
203 expect(element.ref).toBe(null);
182 - expect(Object.isFrozen(element)).toBe(true);
183 - expect(Object.isFrozen(element.props)).toBe(true);
204 + if (__DEV__) {
205 + expect(Object.isFrozen(element)).toBe(true);
206 + expect(Object.isFrozen(element.props)).toBe(true);
207 + }
208 expect(element.props).toEqual({foo: '12'});
209 });
210
@@ -194,8 +218,10 @@ describe('ReactElement', () => {
218 expect(element.type).toBe(ComponentClass);
219 expect(element.key).toBe(null);
220 expect(element.ref).toBe(null);
197 - expect(Object.isFrozen(element)).toBe(true);
198 - expect(Object.isFrozen(element.props)).toBe(true);
221 + if (__DEV__) {
222 + expect(Object.isFrozen(element)).toBe(true);
223 + expect(Object.isFrozen(element.props)).toBe(true);
224 + }
225 expect(element.props).toEqual({foo: '56'});
226 });
227
@@ -214,8 +240,10 @@ describe('ReactElement', () => {
240 expect(element.type).toBe(ComponentClass);
241 expect(element.key).toBe('12');
242 expect(element.ref).toBe(null);
217 - expect(Object.isFrozen(element)).toBe(true);
218 - expect(Object.isFrozen(element.props)).toBe(true);
243 + if (__DEV__) {
244 + expect(Object.isFrozen(element)).toBe(true);
245 + expect(Object.isFrozen(element.props)).toBe(true);
246 + }
247 expect(element.props).toEqual({foo: '56'});
248 });
249
@@ -237,7 +265,6 @@ describe('ReactElement', () => {
265 });
266
267 it('merges an additional argument onto the children prop', () => {
240 - spyOn(console, 'error');
268 var a = 1;
269 var element = React.createFactory(ComponentClass)(
270 {
@@ -246,20 +273,16 @@ describe('ReactElement', () => {
273 a,
274 );
275 expect(element.props.children).toBe(a);
249 - expectDev(console.error.calls.count()).toBe(0);
276 });
277
278 it('does not override children if no rest args are provided', () => {
253 - spyOn(console, 'error');
279 var element = React.createFactory(ComponentClass)({
280 children: 'text',
281 });
282 expect(element.props.children).toBe('text');
258 - expectDev(console.error.calls.count()).toBe(0);
283 });
284
285 it('overrides children if null is provided as an argument', () => {
262 - spyOn(console, 'error');
286 var element = React.createFactory(ComponentClass)(
287 {
288 children: 'text',
@@ -267,24 +290,19 @@ describe('ReactElement', () => {
290 null,
291 );
292 expect(element.props.children).toBe(null);
270 - expectDev(console.error.calls.count()).toBe(0);
293 });
294
295 it('merges rest arguments onto the children prop in an array', () => {
274 - spyOn(console, 'error');
296 var a = 1;
297 var b = 2;
298 var c = 3;
299 var element = React.createFactory(ComponentClass)(null, a, b, c);
300 expect(element.props.children).toEqual([1, 2, 3]);
280 - expectDev(console.error.calls.count()).toBe(0);
301 });
302
303 // NOTE: We're explicitly not using JSX here. This is intended to test
304 // classic JS without JSX.
305 it('allows static methods to be called using the type property', () => {
286 - spyOn(console, 'error');
287 -
306 class StaticMethodComponentClass extends React.Component {
307 render() {
308 return React.createElement('div');
@@ -294,7 +312,6 @@ describe('ReactElement', () => {
312
313 var element = React.createElement(StaticMethodComponentClass);
314 expect(element.type.someStaticMethod()).toBe('someReturnValue');
297 - expectDev(console.error.calls.count()).toBe(0);
315 });
316
317 // NOTE: We're explicitly not using JSX here. This is intended to test
@@ -376,16 +393,25 @@ describe('ReactElement', () => {
393 render() {
394 var el = <div className="moo" />;
395
379 - expect(function() {
396 + if (__DEV__) {
397 + expect(function() {
398 + el.props.className = 'quack';
399 + }).toThrow();
400 + expect(el.props.className).toBe('moo');
401 + } else {
402 el.props.className = 'quack';
381 - }).toThrow();
382 - expect(el.props.className).toBe('moo');
403 + expect(el.props.className).toBe('quack');
404 + }
405
406 return el;
407 }
408 }
409 var outer = ReactTestUtils.renderIntoDocument(<Outer color="orange" />);
388 - expect(ReactDOM.findDOMNode(outer).className).toBe('moo');
410 + if (__DEV__) {
411 + expect(ReactDOM.findDOMNode(outer).className).toBe('moo');
412 + } else {
413 + expect(ReactDOM.findDOMNode(outer).className).toBe('quack');
414 + }
415 });
416
417 it('throws when adding a prop (in dev) after element creation', () => {
@@ -394,11 +420,15 @@ describe('ReactElement', () => {
420 render() {
421 var el = <div>{this.props.sound}</div>;
422
397 - expect(function() {
423 + if (__DEV__) {
424 + expect(function() {
425 + el.props.className = 'quack';
426 + }).toThrow();
427 + expect(el.props.className).toBe(undefined);
428 + } else {
429 el.props.className = 'quack';
399 - }).toThrow();
400 -
401 - expect(el.props.className).toBe(undefined);
430 + expect(el.props.className).toBe('quack');
431 + }
432
433 return el;
434 }
@@ -406,11 +436,14 @@ describe('ReactElement', () => {
436 Outer.defaultProps = {sound: 'meow'};
437 var outer = ReactDOM.render(<Outer />, container);
438 expect(ReactDOM.findDOMNode(outer).textContent).toBe('meow');
409 - expect(ReactDOM.findDOMNode(outer).className).toBe('');
439 + if (__DEV__) {
440 + expect(ReactDOM.findDOMNode(outer).className).toBe('');
441 + } else {
442 + expect(ReactDOM.findDOMNode(outer).className).toBe('quack');
443 + }
444 });
445
446 it('does not warn for NaN props', () => {
413 - spyOn(console, 'error');
447 class Test extends React.Component {
448 render() {
449 return <div />;
@@ -418,7 +451,6 @@ describe('ReactElement', () => {
451 }
452 var test = ReactTestUtils.renderIntoDocument(<Test value={+undefined} />);
453 expect(test.props.value).toBeNaN();
421 - expectDev(console.error.calls.count()).toBe(0);
454 });
455
456 // NOTE: We're explicitly not using JSX here. This is intended to test
packages/react/src/__tests__/ReactElementClone-test.js
+27 -31
@@ -257,42 +257,32 @@ describe('ReactElementClone', () => {
257 });
258
259 it('warns for keys for arrays of elements in rest args', () => {
260 - spyOn(console, 'error');
260 + spyOnDev(console, 'error');
261
262 React.cloneElement(<div />, null, [<div />, <div />]);
263
264 - expectDev(console.error.calls.count()).toBe(1);
265 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
266 - 'Each child in an array or iterator should have a unique "key" prop.',
267 - );
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 + }
270 });
271
272 it('does not warns for arrays of elements with keys', () => {
271 - spyOn(console, 'error');
272 -
273 React.cloneElement(<div />, null, [<div key="#1" />, <div key="#2" />]);
274 -
275 - expectDev(console.error.calls.count()).toBe(0);
274 });
275
276 it('does not warn when the element is directly in rest args', () => {
279 - spyOn(console, 'error');
280 -
277 React.cloneElement(<div />, null, <div />, <div />);
282 -
283 - expectDev(console.error.calls.count()).toBe(0);
278 });
279
280 it('does not warn when the array contains a non-element', () => {
287 - spyOn(console, 'error');
288 -
281 React.cloneElement(<div />, null, [{}, {}]);
290 -
291 - expectDev(console.error.calls.count()).toBe(0);
282 });
283
284 it('should check declared prop types after clone', () => {
295 - spyOn(console, 'error');
285 + spyOnDev(console, 'error');
286 class Component extends React.Component {
287 static propTypes = {
288 color: PropTypes.string.isRequired,
@@ -314,15 +304,17 @@ describe('ReactElementClone', () => {
304 }
305 }
306 ReactTestUtils.renderIntoDocument(React.createElement(GrandParent));
317 - expectDev(console.error.calls.count()).toBe(1);
318 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
319 - 'Warning: Failed prop type: ' +
320 - 'Invalid prop `color` of type `number` supplied to `Component`, ' +
321 - 'expected `string`.\n' +
322 - ' in Component (created by GrandParent)\n' +
323 - ' in Parent (created by GrandParent)\n' +
324 - ' in GrandParent',
325 - );
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 + }
318 });
319
320 it('should ignore key and ref warning getters', () => {
@@ -347,8 +339,10 @@ describe('ReactElementClone', () => {
339 expect(clone.type).toBe(ComponentClass);
340 expect(clone.key).toBe('12');
341 expect(clone.ref).toBe('34');
350 - expect(Object.isFrozen(element)).toBe(true);
351 - expect(Object.isFrozen(element.props)).toBe(true);
342 + if (__DEV__) {
343 + expect(Object.isFrozen(element)).toBe(true);
344 + expect(Object.isFrozen(element.props)).toBe(true);
345 + }
346 expect(clone.props).toEqual({foo: 'ef'});
347 });
348
@@ -367,8 +361,10 @@ describe('ReactElementClone', () => {
361 expect(clone.type).toBe(ComponentClass);
362 expect(clone.key).toBe('null');
363 expect(clone.ref).toBe(null);
370 - expect(Object.isFrozen(element)).toBe(true);
371 - expect(Object.isFrozen(element.props)).toBe(true);
364 + if (__DEV__) {
365 + expect(Object.isFrozen(element)).toBe(true);
366 + expect(Object.isFrozen(element.props)).toBe(true);
367 + }
368 expect(clone.props).toEqual({foo: 'ef'});
369 });
370 });
packages/react/src/__tests__/ReactElementValidator-test.js
+184 -166
@@ -39,19 +39,21 @@ describe('ReactElementValidator', () => {
39 });
40
41 it('warns for keys for arrays of elements in rest args', () => {
42 - spyOn(console, 'error');
42 + spyOnDev(console, 'error');
43 var Component = React.createFactory(ComponentClass);
44
45 Component(null, [Component(), Component()]);
46
47 - expectDev(console.error.calls.count()).toBe(1);
48 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
49 - 'Each child in an array or iterator should have a unique "key" prop.',
50 - );
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 + }
53 });
54
55 it('warns for keys for arrays of elements with owner info', () => {
54 - spyOn(console, 'error');
56 + spyOnDev(console, 'error');
57 var Component = React.createFactory(ComponentClass);
58
59 class InnerClass extends React.Component {
@@ -70,16 +72,18 @@ describe('ReactElementValidator', () => {
72
73 ReactTestUtils.renderIntoDocument(React.createElement(ComponentWrapper));
74
73 - expectDev(console.error.calls.count()).toBe(1);
74 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
75 - 'Each child in an array or iterator should have a unique "key" prop.' +
76 - '\n\nCheck the render method of `InnerClass`. ' +
77 - 'It was passed a child from ComponentWrapper. ',
78 - );
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 + }
83 });
84
85 it('warns for keys for arrays with no owner or parent info', () => {
82 - spyOn(console, 'error');
86 + spyOnDev(console, 'error');
87
88 function Anonymous() {
89 return <div />;
@@ -89,31 +93,35 @@ describe('ReactElementValidator', () => {
93 var divs = [<div />, <div />];
94 ReactTestUtils.renderIntoDocument(<Anonymous>{divs}</Anonymous>);
95
92 - expectDev(console.error.calls.count()).toBe(1);
93 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
94 - 'Warning: Each child in an array or iterator should have a unique ' +
95 - '"key" prop. See https://fb.me/react-warning-keys for more information.\n' +
96 - ' in div (at **)',
97 - );
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 + }
104 });
105
106 it('warns for keys for arrays of elements with no owner info', () => {
101 - spyOn(console, 'error');
107 + spyOnDev(console, 'error');
108
109 var divs = [<div />, <div />];
110 ReactTestUtils.renderIntoDocument(<div>{divs}</div>);
111
106 - expectDev(console.error.calls.count()).toBe(1);
107 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
108 - 'Warning: Each child in an array or iterator should have a unique ' +
109 - '"key" prop.\n\nCheck the top-level render call using <div>. See ' +
110 - 'https://fb.me/react-warning-keys for more information.\n' +
111 - ' in div (at **)',
112 - );
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 + }
121 });
122
123 it('warns for keys with component stack info', () => {
116 - spyOn(console, 'error');
124 + spyOnDev(console, 'error');
125
126 function Component() {
127 return <div>{[<div />, <div />]}</div>;
@@ -129,21 +137,21 @@ describe('ReactElementValidator', () => {
137
138 ReactTestUtils.renderIntoDocument(<GrandParent />);
139
132 - expectDev(console.error.calls.count()).toBe(1);
133 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
134 - 'Warning: Each child in an array or iterator should have a unique ' +
135 - '"key" prop.\n\nCheck the render method of `Component`. See ' +
136 - 'https://fb.me/react-warning-keys for more information.\n' +
137 - ' in div (at **)\n' +
138 - ' in Component (at **)\n' +
139 - ' in Parent (at **)\n' +
140 - ' in GrandParent (at **)',
141 - );
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 + }
152 });
153
154 it('does not warn for keys when passing children down', () => {
145 - spyOn(console, 'error');
146 -
155 function Wrapper(props) {
156 return (
157 <div>
@@ -159,12 +167,10 @@ describe('ReactElementValidator', () => {
167 <span />
168 </Wrapper>,
169 );
162 -
163 - expectDev(console.error.calls.count()).toBe(0);
170 });
171
172 it('warns for keys for iterables of elements in rest args', () => {
167 - spyOn(console, 'error');
173 + spyOnDev(console, 'error');
174 var Component = React.createFactory(ComponentClass);
175
176 var iterable = {
@@ -181,23 +187,21 @@ describe('ReactElementValidator', () => {
187
188 Component(null, iterable);
189
184 - expectDev(console.error.calls.count()).toBe(1);
185 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
186 - 'Each child in an array or iterator should have a unique "key" prop.',
187 - );
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 + }
196 });
197
198 it('does not warns for arrays of elements with keys', () => {
191 - spyOn(console, 'error');
199 var Component = React.createFactory(ComponentClass);
200
201 Component(null, [Component({key: '#1'}), Component({key: '#2'})]);
195 -
196 - expectDev(console.error.calls.count()).toBe(0);
202 });
203
204 it('does not warns for iterable elements with keys', () => {
200 - spyOn(console, 'error');
205 var Component = React.createFactory(ComponentClass);
206
207 var iterable = {
@@ -216,33 +220,25 @@ describe('ReactElementValidator', () => {
220 };
221
222 Component(null, iterable);
219 -
220 - expectDev(console.error.calls.count()).toBe(0);
223 });
224
225 it('does not warn when the element is directly in rest args', () => {
224 - spyOn(console, 'error');
226 var Component = React.createFactory(ComponentClass);
227
228 Component(null, Component(), Component());
228 -
229 - expectDev(console.error.calls.count()).toBe(0);
229 });
230
231 it('does not warn when the array contains a non-element', () => {
233 - spyOn(console, 'error');
232 var Component = React.createFactory(ComponentClass);
233
234 Component(null, [{}, {}]);
237 -
238 - expectDev(console.error.calls.count()).toBe(0);
235 });
236
237 it('should give context for PropType errors in nested components.', () => {
238 // In this test, we're making sure that if a proptype error is found in a
239 // component, we give a small hint as to which parent instantiated that
240 // component as per warnings about key usage in ReactElementValidator.
245 - spyOn(console, 'error');
241 + spyOnDev(console, 'error');
242 function MyComp(props) {
243 return React.createElement('div', null, 'My color is ' + props.color);
244 }
@@ -253,58 +249,64 @@ describe('ReactElementValidator', () => {
249 return React.createElement(MyComp, {color: 123});
250 }
251 ReactTestUtils.renderIntoDocument(React.createElement(ParentComp));
256 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
257 - 'Warning: Failed prop type: ' +
258 - 'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
259 - 'expected `string`.\n' +
260 - ' in MyComp (created by ParentComp)\n' +
261 - ' in ParentComp',
262 - );
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 + }
261 });
262
263 it('gives a helpful error when passing invalid types', () => {
266 - spyOn(console, 'error');
264 + spyOnDev(console, 'error');
265 React.createElement(undefined);
266 React.createElement(null);
267 React.createElement(true);
268 React.createElement({x: 17});
269 React.createElement({});
272 - expectDev(console.error.calls.count()).toBe(5);
273 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
274 - 'Warning: React.createElement: type is invalid -- expected a string ' +
275 - '(for built-in components) or a class/function (for composite ' +
276 - 'components) but got: undefined. You likely forgot to export your ' +
277 - "component from the file it's defined in, or you might have mixed up " +
278 - 'default and named imports.',
279 - );
280 - expectDev(console.error.calls.argsFor(1)[0]).toBe(
281 - 'Warning: React.createElement: type is invalid -- expected a string ' +
282 - '(for built-in components) or a class/function (for composite ' +
283 - 'components) but got: null.',
284 - );
285 - expectDev(console.error.calls.argsFor(2)[0]).toBe(
286 - 'Warning: React.createElement: type is invalid -- expected a string ' +
287 - '(for built-in components) or a class/function (for composite ' +
288 - 'components) but got: boolean.',
289 - );
290 - expectDev(console.error.calls.argsFor(3)[0]).toBe(
291 - 'Warning: React.createElement: type is invalid -- expected a string ' +
292 - '(for built-in components) or a class/function (for composite ' +
293 - 'components) but got: object.',
294 - );
295 - expectDev(console.error.calls.argsFor(4)[0]).toBe(
296 - 'Warning: React.createElement: type is invalid -- expected a string ' +
297 - '(for built-in components) or a class/function (for composite ' +
298 - 'components) but got: object. You likely forgot to export your ' +
299 - "component from the file it's defined in, or you might have mixed up " +
300 - 'default and named imports.',
301 - );
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 + }
302 React.createElement('div');
303 - expectDev(console.error.calls.count()).toBe(5);
303 + if (__DEV__) {
304 + expect(console.error.calls.count()).toBe(5);
305 + }
306 });
307
308 it('includes the owner name when passing null, undefined, boolean, or number', () => {
307 - spyOn(console, 'error');
309 + spyOnDev(console, 'error');
310 function ParentComp() {
311 return React.createElement(null);
312 }
@@ -312,20 +314,22 @@ describe('ReactElementValidator', () => {
314 ReactTestUtils.renderIntoDocument(React.createElement(ParentComp));
315 }).toThrowError(
316 'Element type is invalid: expected a string (for built-in components) ' +
315 - 'or a class/function (for composite components) but got: null.\n\nCheck ' +
316 - 'the render method of `ParentComp`.',
317 - );
318 - expectDev(console.error.calls.count()).toBe(1);
319 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
320 - 'Warning: React.createElement: type is invalid -- expected a string ' +
321 - '(for built-in components) or a class/function (for composite ' +
322 - 'components) but got: null.\n\nCheck the render method of `ParentComp`.' +
323 - '\n in ParentComp',
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',
327 + );
328 + }
329 });
330
331 it('should check default prop values', () => {
328 - spyOn(console, 'error');
332 + spyOnDev(console, 'error');
333
334 class Component extends React.Component {
335 static propTypes = {prop: PropTypes.string.isRequired};
@@ -337,16 +341,18 @@ describe('ReactElementValidator', () => {
341
342 ReactTestUtils.renderIntoDocument(React.createElement(Component));
343
340 - expectDev(console.error.calls.count()).toBe(1);
341 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
342 - 'Warning: Failed prop type: The prop `prop` is marked as required in ' +
343 - '`Component`, but its value is `null`.\n' +
344 - ' in Component',
345 - );
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 + }
352 });
353
354 it('should not check the default for explicit null', () => {
349 - spyOn(console, 'error');
355 + spyOnDev(console, 'error');
356
357 class Component extends React.Component {
358 static propTypes = {prop: PropTypes.string.isRequired};
@@ -360,16 +366,18 @@ describe('ReactElementValidator', () => {
366 React.createElement(Component, {prop: null}),
367 );
368
363 - expectDev(console.error.calls.count()).toBe(1);
364 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
365 - 'Warning: Failed prop type: The prop `prop` is marked as required in ' +
366 - '`Component`, but its value is `null`.\n' +
367 - ' in Component',
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',
375 + );
376 + }
377 });
378
379 it('should check declared prop types', () => {
372 - spyOn(console, 'error');
380 + spyOnDev(console, 'error');
381
382 class Component extends React.Component {
383 static propTypes = {
@@ -385,31 +393,35 @@ describe('ReactElementValidator', () => {
393 React.createElement(Component, {prop: 42}),
394 );
395
388 - expectDev(console.error.calls.count()).toBe(2);
389 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
390 - 'Warning: Failed prop type: ' +
391 - 'The prop `prop` is marked as required in `Component`, but its value ' +
392 - 'is `undefined`.\n' +
393 - ' in Component',
394 - );
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
396 - expectDev(console.error.calls.argsFor(1)[0]).toBe(
397 - 'Warning: Failed prop type: ' +
398 - 'Invalid prop `prop` of type `number` supplied to ' +
399 - '`Component`, expected `string`.\n' +
400 - ' in Component',
401 - );
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',
410 + );
411 + }
412
413 ReactTestUtils.renderIntoDocument(
414 React.createElement(Component, {prop: 'string'}),
415 );
416
407 - // Should not error for strings
408 - expectDev(console.error.calls.count()).toBe(2);
417 + if (__DEV__) {
418 + // Should not error for strings
419 + expect(console.error.calls.count()).toBe(2);
420 + }
421 });
422
423 it('should warn if a PropType creator is used as a PropType', () => {
412 - spyOn(console, 'error');
424 + spyOnDev(console, 'error');
425
426 class Component extends React.Component {
427 static propTypes = {
@@ -424,35 +436,40 @@ describe('ReactElementValidator', () => {
436 React.createElement(Component, {myProp: {value: 'hi'}}),
437 );
438
427 - expectDev(console.error.calls.count()).toBe(1);
428 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
429 - 'Warning: Component: type specification of prop `myProp` is invalid; ' +
430 - 'the type checker function must return `null` or an `Error` but ' +
431 - 'returned a function. You may have forgotten to pass an argument to ' +
432 - 'the type checker creator (arrayOf, instanceOf, objectOf, oneOf, ' +
433 - 'oneOfType, and shape all require an argument).',
434 - );
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).',
447 + );
448 + }
449 });
450
451 it('should warn when accessing .type on an element factory', () => {
438 - spyOn(console, 'warn');
452 + spyOnDev(console, 'warn');
453 function TestComponent() {
454 return <div />;
455 }
456 var TestFactory = React.createFactory(TestComponent);
457 expect(TestFactory.type).toBe(TestComponent);
444 - expectDev(console.warn.calls.count()).toBe(1);
445 - expectDev(console.warn.calls.argsFor(0)[0]).toBe(
446 - 'Warning: Factory.type is deprecated. Access the class directly before ' +
447 - 'passing it to createFactory.',
448 - );
458 + if (__DEV__) {
459 + expect(console.warn.calls.count()).toBe(1);
460 + expect(console.warn.calls.argsFor(0)[0]).toBe(
461 + 'Warning: Factory.type is deprecated. Access the class directly before ' +
462 + 'passing it to createFactory.',
463 + );
464 + }
465 // Warn once, not again
466 expect(TestFactory.type).toBe(TestComponent);
451 - expectDev(console.warn.calls.count()).toBe(1);
467 + if (__DEV__) {
468 + expect(console.warn.calls.count()).toBe(1);
469 + }
470 });
471
472 it('does not warn when using DOM node as children', () => {
455 - spyOn(console, 'error');
473 class DOMContainer extends React.Component {
474 render() {
475 return <div />;
@@ -465,7 +482,6 @@ describe('ReactElementValidator', () => {
482 var node = document.createElement('div');
483 // This shouldn't cause a stack overflow or any other problems (#3883)
484 ReactTestUtils.renderIntoDocument(<DOMContainer>{node}</DOMContainer>);
468 - expectDev(console.error.calls.count()).toBe(0);
485 });
486
487 it('should not enumerate enumerable numbers (#4776)', () => {
@@ -505,16 +521,18 @@ describe('ReactElementValidator', () => {
521 });
522
523 it('does not blow up on key warning with undefined type', () => {
508 - spyOn(console, 'error');
524 + spyOnDev(console, 'error');
525 var Foo = undefined;
526 void <Foo>{[<div />]}</Foo>;
511 - expectDev(console.error.calls.count()).toBe(1);
512 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
513 - 'Warning: React.createElement: type is invalid -- expected a string ' +
514 - '(for built-in components) or a class/function (for composite ' +
515 - 'components) but got: undefined. You likely forgot to export your ' +
516 - "component from the file it's defined in, or you might have mixed up " +
517 - 'default and named imports.\n\nCheck your code at **.',
518 - );
527 + if (__DEV__) {
528 + expect(console.error.calls.count()).toBe(1);
529 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
530 + 'Warning: React.createElement: type is invalid -- expected a string ' +
531 + '(for built-in components) or a class/function (for composite ' +
532 + 'components) but got: undefined. You likely forgot to export your ' +
533 + "component from the file it's defined in, or you might have mixed up " +
534 + 'default and named imports.\n\nCheck your code at **.',
535 + );
536 + }
537 });
538 });
packages/react/src/__tests__/ReactJSXElement-test.js
+5 -12
@@ -62,7 +62,11 @@ describe('ReactJSXElement', () => {
62
63 it('returns an immutable element', () => {
64 var element = <Component />;
65 - expect(() => (element.type = 'div')).toThrow();
65 + if (__DEV__) {
66 + expect(() => (element.type = 'div')).toThrow();
67 + } else {
68 + expect(() => (element.type = 'div')).not.toThrow();
69 + }
70 });
71
72 it('does not reuse the object that is spread into props', () => {
@@ -94,25 +98,19 @@ describe('ReactJSXElement', () => {
98 });
99
100 it('merges JSX children onto the children prop', () => {
97 - spyOn(console, 'error');
101 var a = 1;
102 var element = <Component children="text">{a}</Component>;
103 expect(element.props.children).toBe(a);
101 - expectDev(console.error.calls.count()).toBe(0);
104 });
105
106 it('does not override children if no JSX children are provided', () => {
105 - spyOn(console, 'error');
107 var element = <Component children="text" />;
108 expect(element.props.children).toBe('text');
108 - expectDev(console.error.calls.count()).toBe(0);
109 });
110
111 it('overrides children if null is provided as a JSX child', () => {
112 - spyOn(console, 'error');
112 var element = <Component children="text">{null}</Component>;
113 expect(element.props.children).toBe(null);
115 - expectDev(console.error.calls.count()).toBe(0);
114 });
115
116 it('overrides children if undefined is provided as an argument', () => {
@@ -128,7 +126,6 @@ describe('ReactJSXElement', () => {
126 });
127
128 it('merges JSX children onto the children prop in an array', () => {
131 - spyOn(console, 'error');
129 var a = 1;
130 var b = 2;
131 var c = 3;
@@ -140,12 +137,9 @@ describe('ReactJSXElement', () => {
137 </Component>
138 );
139 expect(element.props.children).toEqual([1, 2, 3]);
143 - expectDev(console.error.calls.count()).toBe(0);
140 });
141
142 it('allows static methods to be called using the type property', () => {
147 - spyOn(console, 'error');
148 -
143 class StaticMethodComponent {
144 static someStaticMethod() {
145 return 'someReturnValue';
@@ -157,7 +151,6 @@ describe('ReactJSXElement', () => {
151
152 var element = <StaticMethodComponent />;
153 expect(element.type.someStaticMethod()).toBe('someReturnValue');
160 - expectDev(console.error.calls.count()).toBe(0);
154 });
155
156 it('identifies valid elements', () => {
packages/react/src/__tests__/ReactJSXElementValidator-test.js
+160 -138
@@ -51,20 +51,22 @@ describe('ReactJSXElementValidator', () => {
51 });
52
53 it('warns for keys for arrays of elements in children position', () => {
54 - spyOn(console, 'error');
54 + spyOnDev(console, 'error');
55
56 ReactTestUtils.renderIntoDocument(
57 <Component>{[<Component />, <Component />]}</Component>,
58 );
59
60 - expectDev(console.error.calls.count()).toBe(1);
61 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
62 - 'Each child in an array or iterator should have a unique "key" prop.',
63 - );
60 + if (__DEV__) {
61 + expect(console.error.calls.count()).toBe(1);
62 + expect(console.error.calls.argsFor(0)[0]).toContain(
63 + 'Each child in an array or iterator should have a unique "key" prop.',
64 + );
65 + }
66 });
67
68 it('warns for keys for arrays of elements with owner info', () => {
67 - spyOn(console, 'error');
69 + spyOnDev(console, 'error');
70
71 class InnerComponent extends React.Component {
72 render() {
@@ -80,16 +82,18 @@ describe('ReactJSXElementValidator', () => {
82
83 ReactTestUtils.renderIntoDocument(<ComponentWrapper />);
84
83 - expectDev(console.error.calls.count()).toBe(1);
84 - expectDev(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 - );
85 + if (__DEV__) {
86 + expect(console.error.calls.count()).toBe(1);
87 + expect(console.error.calls.argsFor(0)[0]).toContain(
88 + 'Each child in an array or iterator should have a unique "key" prop.' +
89 + '\n\nCheck the render method of `InnerComponent`. ' +
90 + 'It was passed a child from ComponentWrapper. ',
91 + );
92 + }
93 });
94
95 it('warns for fragments with illegal attributes', () => {
92 - spyOn(console, 'error');
96 + spyOnDev(console, 'error');
97
98 class Foo extends React.Component {
99 render() {
@@ -103,16 +107,18 @@ describe('ReactJSXElementValidator', () => {
107
108 ReactTestUtils.renderIntoDocument(<Foo />);
109
106 - expectDev(console.error.calls.count()).toBe(1);
107 - expectDev(console.error.calls.argsFor(0)[0]).toContain('Invalid prop `');
108 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
109 - '` supplied to `React.Fragment`. React.Fragment ' +
110 - 'can only have `key` and `children` props.',
111 - );
110 + if (__DEV__) {
111 + expect(console.error.calls.count()).toBe(1);
112 + expect(console.error.calls.argsFor(0)[0]).toContain('Invalid prop `');
113 + expect(console.error.calls.argsFor(0)[0]).toContain(
114 + '` supplied to `React.Fragment`. React.Fragment ' +
115 + 'can only have `key` and `children` props.',
116 + );
117 + }
118 });
119
120 it('warns for fragments with refs', () => {
115 - spyOn(console, 'error');
121 + spyOnDev(console, 'error');
122
123 class Foo extends React.Component {
124 render() {
@@ -129,14 +135,16 @@ describe('ReactJSXElementValidator', () => {
135
136 ReactTestUtils.renderIntoDocument(<Foo />);
137
132 - expectDev(console.error.calls.count()).toBe(1);
133 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
134 - 'Invalid attribute `ref` supplied to `React.Fragment`.',
135 - );
138 + if (__DEV__) {
139 + expect(console.error.calls.count()).toBe(1);
140 + expect(console.error.calls.argsFor(0)[0]).toContain(
141 + 'Invalid attribute `ref` supplied to `React.Fragment`.',
142 + );
143 + }
144 });
145
146 it('warns for keys for iterables of elements in rest args', () => {
139 - spyOn(console, 'error');
147 + spyOnDev(console, 'error');
148
149 var iterable = {
150 '@@iterator': function() {
@@ -152,10 +160,12 @@ describe('ReactJSXElementValidator', () => {
160
161 ReactTestUtils.renderIntoDocument(<Component>{iterable}</Component>);
162
155 - expectDev(console.error.calls.count()).toBe(1);
156 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
157 - 'Each child in an array or iterator should have a unique "key" prop.',
158 - );
163 + if (__DEV__) {
164 + expect(console.error.calls.count()).toBe(1);
165 + expect(console.error.calls.argsFor(0)[0]).toContain(
166 + 'Each child in an array or iterator should have a unique "key" prop.',
167 + );
168 + }
169 });
170
171 it('does not warn for fragments of multiple elements without keys', () => {
@@ -168,7 +178,7 @@ describe('ReactJSXElementValidator', () => {
178 });
179
180 it('warns for fragments of multiple elements with same key', () => {
171 - spyOn(console, 'error');
181 + spyOnDev(console, 'error');
182
183 ReactTestUtils.renderIntoDocument(
184 <React.Fragment>
@@ -178,25 +188,21 @@ describe('ReactJSXElementValidator', () => {
188 </React.Fragment>,
189 );
190
181 - expectDev(console.error.calls.count()).toBe(1);
182 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
183 - 'Encountered two children with the same key, `a`.',
184 - );
191 + if (__DEV__) {
192 + expect(console.error.calls.count()).toBe(1);
193 + expect(console.error.calls.argsFor(0)[0]).toContain(
194 + 'Encountered two children with the same key, `a`.',
195 + );
196 + }
197 });
198
199 it('does not warn for arrays of elements with keys', () => {
188 - spyOn(console, 'error');
189 -
200 ReactTestUtils.renderIntoDocument(
201 <Component>{[<Component key="#1" />, <Component key="#2" />]}</Component>,
202 );
193 -
194 - expectDev(console.error.calls.count()).toBe(0);
203 });
204
205 it('does not warn for iterable elements with keys', () => {
198 - spyOn(console, 'error');
199 -
206 var iterable = {
207 '@@iterator': function() {
208 var i = 0;
@@ -213,13 +219,9 @@ describe('ReactJSXElementValidator', () => {
219 };
220
221 ReactTestUtils.renderIntoDocument(<Component>{iterable}</Component>);
216 -
217 - expectDev(console.error.calls.count()).toBe(0);
222 });
223
224 it('does not warn for numeric keys in entry iterable as a child', () => {
221 - spyOn(console, 'error');
222 -
225 var iterable = {
226 '@@iterator': function() {
227 var i = 0;
@@ -234,8 +236,6 @@ describe('ReactJSXElementValidator', () => {
236 iterable.entries = iterable['@@iterator'];
237
238 ReactTestUtils.renderIntoDocument(<Component>{iterable}</Component>);
237 -
238 - expectDev(console.error.calls.count()).toBe(0);
239 });
240
241 it('does not warn when the element is directly as children', () => {
@@ -255,7 +255,7 @@ describe('ReactJSXElementValidator', () => {
255 // In this test, we're making sure that if a proptype error is found in a
256 // component, we give a small hint as to which parent instantiated that
257 // component as per warnings about key usage in ReactElementValidator.
258 - spyOn(console, 'error');
258 + spyOnDev(console, 'error');
259 class MyComp extends React.Component {
260 render() {
261 return <div>My color is {this.color}</div>;
@@ -270,17 +270,19 @@ describe('ReactJSXElementValidator', () => {
270 }
271 }
272 ReactTestUtils.renderIntoDocument(<ParentComp />);
273 - expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
274 - 'Warning: Failed prop type: ' +
275 - 'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
276 - 'expected `string`.\n' +
277 - ' in MyComp (at **)\n' +
278 - ' in ParentComp (at **)',
279 - );
273 + if (__DEV__) {
274 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
275 + 'Warning: Failed prop type: ' +
276 + 'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
277 + 'expected `string`.\n' +
278 + ' in MyComp (at **)\n' +
279 + ' in ParentComp (at **)',
280 + );
281 + }
282 });
283
284 it('should update component stack after receiving next element', () => {
283 - spyOn(console, 'error');
285 + spyOnDev(console, 'error');
286 function MyComp() {
287 return null;
288 }
@@ -303,17 +305,19 @@ describe('ReactJSXElementValidator', () => {
305 ReactDOM.render(<ParentComp warn={false} />, container);
306 ReactDOM.render(<ParentComp warn={true} />, container);
307
306 - expect(console.error.calls.count()).toBe(1);
307 - // The warning should have the full stack with line numbers.
308 - // If it doesn't, it means we're using information from the old element.
309 - expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
310 - 'Warning: Failed prop type: ' +
311 - 'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
312 - 'expected `string`.\n' +
313 - ' in MyComp (at **)\n' +
314 - ' in MiddleComp (at **)\n' +
315 - ' in ParentComp (at **)',
316 - );
308 + if (__DEV__) {
309 + expect(console.error.calls.count()).toBe(1);
310 + // The warning should have the full stack with line numbers.
311 + // If it doesn't, it means we're using information from the old element.
312 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
313 + 'Warning: Failed prop type: ' +
314 + 'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
315 + 'expected `string`.\n' +
316 + ' in MyComp (at **)\n' +
317 + ' in MiddleComp (at **)\n' +
318 + ' in ParentComp (at **)',
319 + );
320 + }
321 });
322
323 it('gives a helpful error when passing null, undefined, or boolean', () => {
@@ -321,88 +325,100 @@ describe('ReactJSXElementValidator', () => {
325 var Null = null;
326 var True = true;
327 var Div = 'div';
324 - spyOn(console, 'error');
328 + spyOnDev(console, 'error');
329 void <Undefined />;
330 void <Null />;
331 void <True />;
328 - expectDev(console.error.calls.count()).toBe(3);
329 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
330 - 'Warning: React.createElement: type is invalid -- expected a string ' +
331 - '(for built-in components) or a class/function (for composite ' +
332 - 'components) but got: undefined. You likely forgot to export your ' +
333 - "component from the file it's defined in, or you might have mixed up " +
334 - 'default and named imports.' +
335 - '\n\nCheck your code at **.',
336 - );
337 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
338 - 'Warning: React.createElement: type is invalid -- expected a string ' +
339 - '(for built-in components) or a class/function (for composite ' +
340 - 'components) but got: null.' +
341 - '\n\nCheck your code at **.',
342 - );
343 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
344 - 'Warning: React.createElement: type is invalid -- expected a string ' +
345 - '(for built-in components) or a class/function (for composite ' +
346 - 'components) but got: boolean.' +
347 - '\n\nCheck your code at **.',
348 - );
332 + if (__DEV__) {
333 + expect(console.error.calls.count()).toBe(3);
334 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
335 + 'Warning: React.createElement: type is invalid -- expected a string ' +
336 + '(for built-in components) or a class/function (for composite ' +
337 + 'components) but got: undefined. You likely forgot to export your ' +
338 + "component from the file it's defined in, or you might have mixed up " +
339 + 'default and named imports.' +
340 + '\n\nCheck your code at **.',
341 + );
342 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
343 + 'Warning: React.createElement: type is invalid -- expected a string ' +
344 + '(for built-in components) or a class/function (for composite ' +
345 + 'components) but got: null.' +
346 + '\n\nCheck your code at **.',
347 + );
348 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
349 + 'Warning: React.createElement: type is invalid -- expected a string ' +
350 + '(for built-in components) or a class/function (for composite ' +
351 + 'components) but got: boolean.' +
352 + '\n\nCheck your code at **.',
353 + );
354 + }
355 void <Div />;
350 - expectDev(console.error.calls.count()).toBe(3);
356 + if (__DEV__) {
357 + expect(console.error.calls.count()).toBe(3);
358 + }
359 });
360
361 it('should check default prop values', () => {
354 - spyOn(console, 'error');
362 + spyOnDev(console, 'error');
363
364 RequiredPropComponent.defaultProps = {prop: null};
365
366 ReactTestUtils.renderIntoDocument(<RequiredPropComponent />);
367
360 - expectDev(console.error.calls.count()).toBe(1);
361 - expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
362 - 'Warning: Failed prop type: The prop `prop` is marked as required in ' +
363 - '`RequiredPropComponent`, but its value is `null`.\n' +
364 - ' in RequiredPropComponent (at **)',
365 - );
368 + if (__DEV__) {
369 + expect(console.error.calls.count()).toBe(1);
370 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
371 + 'Warning: Failed prop type: The prop `prop` is marked as required in ' +
372 + '`RequiredPropComponent`, but its value is `null`.\n' +
373 + ' in RequiredPropComponent (at **)',
374 + );
375 + }
376 });
377
378 it('should not check the default for explicit null', () => {
369 - spyOn(console, 'error');
379 + spyOnDev(console, 'error');
380
381 ReactTestUtils.renderIntoDocument(<RequiredPropComponent prop={null} />);
382
373 - expectDev(console.error.calls.count()).toBe(1);
374 - expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
375 - 'Warning: Failed prop type: The prop `prop` is marked as required in ' +
376 - '`RequiredPropComponent`, but its value is `null`.\n' +
377 - ' in RequiredPropComponent (at **)',
378 - );
383 + if (__DEV__) {
384 + expect(console.error.calls.count()).toBe(1);
385 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
386 + 'Warning: Failed prop type: The prop `prop` is marked as required in ' +
387 + '`RequiredPropComponent`, but its value is `null`.\n' +
388 + ' in RequiredPropComponent (at **)',
389 + );
390 + }
391 });
392
393 it('should check declared prop types', () => {
382 - spyOn(console, 'error');
394 + spyOnDev(console, 'error');
395
396 ReactTestUtils.renderIntoDocument(<RequiredPropComponent />);
397 ReactTestUtils.renderIntoDocument(<RequiredPropComponent prop={42} />);
398
387 - expectDev(console.error.calls.count()).toBe(2);
388 - expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
389 - 'Warning: Failed prop type: ' +
390 - 'The prop `prop` is marked as required in `RequiredPropComponent`, but ' +
391 - 'its value is `undefined`.\n' +
392 - ' in RequiredPropComponent (at **)',
393 - );
394 -
395 - expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
396 - 'Warning: Failed prop type: ' +
397 - 'Invalid prop `prop` of type `number` supplied to ' +
398 - '`RequiredPropComponent`, expected `string`.\n' +
399 - ' in RequiredPropComponent (at **)',
400 - );
399 + if (__DEV__) {
400 + expect(console.error.calls.count()).toBe(2);
401 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
402 + 'Warning: Failed prop type: ' +
403 + 'The prop `prop` is marked as required in `RequiredPropComponent`, but ' +
404 + 'its value is `undefined`.\n' +
405 + ' in RequiredPropComponent (at **)',
406 + );
407 +
408 + expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
409 + 'Warning: Failed prop type: ' +
410 + 'Invalid prop `prop` of type `number` supplied to ' +
411 + '`RequiredPropComponent`, expected `string`.\n' +
412 + ' in RequiredPropComponent (at **)',
413 + );
414 + }
415
416 ReactTestUtils.renderIntoDocument(<RequiredPropComponent prop="string" />);
417
404 - // Should not error for strings
405 - expectDev(console.error.calls.count()).toBe(2);
418 + if (__DEV__) {
419 + // Should not error for strings
420 + expect(console.error.calls.count()).toBe(2);
421 + }
422 });
423
424 it('should warn on invalid prop types', () => {
@@ -410,7 +426,7 @@ describe('ReactJSXElementValidator', () => {
426 // for us to issue a warning earlier than element creation when the error
427 // actually occurs. Since this step is skipped in production, we should just
428 // warn instead of throwing for this case.
413 - spyOn(console, 'error');
429 + spyOnDev(console, 'error');
430 class NullPropTypeComponent extends React.Component {
431 render() {
432 return <span>{this.props.prop}</span>;
@@ -420,15 +436,17 @@ describe('ReactJSXElementValidator', () => {
436 prop: null,
437 };
438 ReactTestUtils.renderIntoDocument(<NullPropTypeComponent />);
423 - expectDev(console.error.calls.count()).toBe(1);
424 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
425 - 'NullPropTypeComponent: prop type `prop` is invalid; it must be a ' +
426 - 'function, usually from the `prop-types` package,',
427 - );
439 + if (__DEV__) {
440 + expect(console.error.calls.count()).toBe(1);
441 + expect(console.error.calls.argsFor(0)[0]).toContain(
442 + 'NullPropTypeComponent: prop type `prop` is invalid; it must be a ' +
443 + 'function, usually from the `prop-types` package,',
444 + );
445 + }
446 });
447
448 it('should warn on invalid context types', () => {
431 - spyOn(console, 'error');
449 + spyOnDev(console, 'error');
450 class NullContextTypeComponent extends React.Component {
451 render() {
452 return <span>{this.props.prop}</span>;
@@ -438,15 +456,17 @@ describe('ReactJSXElementValidator', () => {
456 prop: null,
457 };
458 ReactTestUtils.renderIntoDocument(<NullContextTypeComponent />);
441 - expectDev(console.error.calls.count()).toBe(1);
442 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
443 - 'NullContextTypeComponent: context type `prop` is invalid; it must ' +
444 - 'be a function, usually from the `prop-types` package,',
445 - );
459 + if (__DEV__) {
460 + expect(console.error.calls.count()).toBe(1);
461 + expect(console.error.calls.argsFor(0)[0]).toContain(
462 + 'NullContextTypeComponent: context type `prop` is invalid; it must ' +
463 + 'be a function, usually from the `prop-types` package,',
464 + );
465 + }
466 });
467
468 it('should warn if getDefaultProps is specificed on the class', () => {
449 - spyOn(console, 'error');
469 + spyOnDev(console, 'error');
470 class GetDefaultPropsComponent extends React.Component {
471 render() {
472 return <span>{this.props.prop}</span>;
@@ -456,10 +476,12 @@ describe('ReactJSXElementValidator', () => {
476 prop: 'foo',
477 });
478 ReactTestUtils.renderIntoDocument(<GetDefaultPropsComponent />);
459 - expectDev(console.error.calls.count()).toBe(1);
460 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
461 - 'getDefaultProps is only used on classic React.createClass definitions.' +
462 - ' Use a static property named `defaultProps` instead.',
463 - );
479 + if (__DEV__) {
480 + expect(console.error.calls.count()).toBe(1);
481 + expect(console.error.calls.argsFor(0)[0]).toContain(
482 + 'getDefaultProps is only used on classic React.createClass definitions.' +
483 + ' Use a static property named `defaultProps` instead.',
484 + );
485 + }
486 });
487 });
packages/react/src/__tests__/ReactPureComponent-test.js
+20 -16
@@ -62,7 +62,7 @@ describe('ReactPureComponent', () => {
62 });
63
64 it('can override shouldComponentUpdate', () => {
65 - spyOn(console, 'error');
65 + spyOnDev(console, 'error');
66 var renders = 0;
67 class Component extends React.PureComponent {
68 render() {
@@ -76,13 +76,15 @@ describe('ReactPureComponent', () => {
76 var container = document.createElement('div');
77 ReactDOM.render(<Component />, container);
78 ReactDOM.render(<Component />, container);
79 - expect(console.error.calls.count()).toBe(1);
80 - expect(console.error.calls.argsFor(0)[0]).toBe(
81 - 'Warning: ' +
82 - 'Component has a method called shouldComponentUpdate(). ' +
83 - 'shouldComponentUpdate should not be used when extending React.PureComponent. ' +
84 - 'Please extend React.Component if shouldComponentUpdate is used.',
85 - );
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 + }
88 expect(renders).toBe(2);
89 });
90
@@ -101,7 +103,7 @@ describe('ReactPureComponent', () => {
103 });
104
105 it('should warn when shouldComponentUpdate is defined on React.PureComponent', () => {
104 - spyOn(console, 'error');
106 + spyOnDev(console, 'error');
107
108 class PureComponent extends React.PureComponent {
109 shouldComponentUpdate() {
@@ -114,12 +116,14 @@ describe('ReactPureComponent', () => {
116 var container = document.createElement('div');
117 ReactDOM.render(<PureComponent />, container);
118
117 - expect(console.error.calls.count()).toBe(1);
118 - expect(console.error.calls.argsFor(0)[0]).toBe(
119 - 'Warning: ' +
120 - 'PureComponent has a method called shouldComponentUpdate(). ' +
121 - 'shouldComponentUpdate should not be used when extending React.PureComponent. ' +
122 - 'Please extend React.Component if shouldComponentUpdate is used.',
123 - );
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 + }
128 });
129 });
packages/react/src/__tests__/ReactTypeScriptClass-test.ts
+85 -59
@@ -315,15 +315,17 @@ describe('ReactTypeScriptClass', function() {
315 });
316
317 it('throws if no render function is defined', function() {
318 - spyOn(console, 'error');
318 + spyOnDev(console, 'error');
319
320 expect(() => ReactDOM.render(React.createElement(Empty), container)).toThrow();
321
322 - expect((<any>console.error).calls.count()).toBe(1);
323 - expect((<any>console.error).calls.argsFor(0)[0]).toBe(
324 - 'Warning: Empty(...): No `render` method found on the returned ' +
325 - 'component instance: you may have forgotten to define `render`.'
326 - );
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 ' +
326 + 'component instance: you may have forgotten to define `render`.'
327 + );
328 + }
329 });
330
331 it('renders a simple stateless component with prop', function() {
@@ -360,18 +362,31 @@ describe('ReactTypeScriptClass', function() {
362 });
363
364 it('should throw with non-object in the initial state property', function() {
363 - expect(() => test(React.createElement(ArrayState), 'span', ''))
364 - .toThrowError(
365 - 'ArrayState.state: must be set to an object or null'
366 - );
367 - expect(() => test(React.createElement(StringState), 'span', ''))
368 - .toThrowError(
369 - 'StringState.state: must be set to an object or null'
370 - );
371 - expect(() => test(React.createElement(NumberState), 'span', ''))
372 - .toThrowError(
373 - 'NumberState.state: must be set to an object or null'
374 - );
365 + if (__DEV__) {
366 + expect(() => test(React.createElement(ArrayState), 'SPAN', ''))
367 + .toThrowError(
368 + 'ArrayState.state: must be set to an object or null'
369 + );
370 + expect(() => test(React.createElement(StringState), 'SPAN', ''))
371 + .toThrowError(
372 + 'StringState.state: must be set to an object or null'
373 + );
374 + expect(() => test(React.createElement(NumberState), 'SPAN', ''))
375 + .toThrowError(
376 + 'NumberState.state: must be set to an object or null'
377 + );
378 + } else {
379 + // This is a difference between development and production.
380 + // I'm not sure if this is intentional, as generally we avoid this.
381 + // TODO: investigate if this was intentional or an oversight.
382 + // https://github.com/facebook/react/issues/11618
383 + expect(() => test(React.createElement(ArrayState), 'SPAN', ''))
384 + .not.toThrowError();
385 + expect(() => test(React.createElement(StringState), 'SPAN', ''))
386 + .not.toThrowError();
387 + expect(() => test(React.createElement(NumberState), 'SPAN', ''))
388 + .not.toThrowError();
389 + }
390 });
391
392 it('should render with null in the initial state property', function() {
@@ -431,32 +446,35 @@ describe('ReactTypeScriptClass', function() {
446
447 it('warns when classic properties are defined on the instance, ' +
448 'but does not invoke them.', function() {
434 - spyOn(console, 'error');
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);
440 - expect((<any>console.error).calls.count()).toBe(4);
441 - expect((<any>console.error).calls.argsFor(0)[0]).toContain(
442 - 'getInitialState was defined on ClassicProperties, ' +
443 - 'a plain JavaScript class.'
444 - );
445 - expect((<any>console.error).calls.argsFor(1)[0]).toContain(
446 - 'getDefaultProps was defined on ClassicProperties, ' +
447 - 'a plain JavaScript class.'
448 - );
449 - expect((<any>console.error).calls.argsFor(2)[0]).toContain(
450 - 'propTypes was defined as an instance property on ClassicProperties.'
451 - );
452 - expect((<any>console.error).calls.argsFor(3)[0]).toContain(
453 - 'contextTypes was defined as an instance property on ClassicProperties.'
454 - );
455 +
456 + if (__DEV__) {
457 + expect((<any>console.error).calls.count()).toBe(4);
458 + expect((<any>console.error).calls.argsFor(0)[0]).toContain(
459 + 'getInitialState was defined on ClassicProperties, ' +
460 + 'a plain JavaScript class.'
461 + );
462 + expect((<any>console.error).calls.argsFor(1)[0]).toContain(
463 + '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 + );
472 + }
473 });
474
475 it('does not warn about getInitialState() on class components ' +
476 'if state is also defined.', () => {
459 - spyOn(console, 'error');
477 + spyOnDev(console, 'error');
478
479 class Example extends React.Component {
480 state = {};
@@ -469,51 +487,59 @@ describe('ReactTypeScriptClass', function() {
487 }
488
489 test(React.createElement(Example), 'SPAN', 'foo');
472 - expect((<any>console.error).calls.count()).toBe(0);
490 + if (__DEV__) {
491 + expect((<any>console.error).calls.count()).toBe(0);
492 + }
493 });
494
495 it('should warn when misspelling shouldComponentUpdate', function() {
476 - spyOn(console, 'error');
496 + spyOnDev(console, 'error');
497
498 test(React.createElement(MisspelledComponent1), 'SPAN', 'foo');
499
480 - expect((<any>console.error).calls.count()).toBe(1);
481 - expect((<any>console.error).calls.argsFor(0)[0]).toBe(
482 - 'Warning: ' +
483 - 'MisspelledComponent1 has a method called componentShouldUpdate(). Did ' +
484 - 'you mean shouldComponentUpdate()? The name is phrased as a question ' +
485 - 'because the function is expected to return a value.'
486 - );
500 + if (__DEV__) {
501 + expect((<any>console.error).calls.count()).toBe(1);
502 + expect((<any>console.error).calls.argsFor(0)[0]).toBe(
503 + 'Warning: ' +
504 + 'MisspelledComponent1 has a method called componentShouldUpdate(). Did ' +
505 + 'you mean shouldComponentUpdate()? The name is phrased as a question ' +
506 + 'because the function is expected to return a value.'
507 + );
508 + }
509 });
510
511 it('should warn when misspelling componentWillReceiveProps', function() {
490 - spyOn(console, 'error');
512 + spyOnDev(console, 'error');
513
514 test(React.createElement(MisspelledComponent2), 'SPAN', 'foo');
515
494 - expect((<any>console.error).calls.count()).toBe(1);
495 - expect((<any>console.error).calls.argsFor(0)[0]).toBe(
496 - 'Warning: ' +
497 - 'MisspelledComponent2 has a method called componentWillRecieveProps(). ' +
498 - 'Did you mean componentWillReceiveProps()?'
499 - );
516 + if (__DEV__) {
517 + expect((<any>console.error).calls.count()).toBe(1);
518 + expect((<any>console.error).calls.argsFor(0)[0]).toBe(
519 + 'Warning: ' +
520 + 'MisspelledComponent2 has a method called componentWillRecieveProps(). ' +
521 + 'Did you mean componentWillReceiveProps()?'
522 + );
523 + }
524 });
525
526 it('should throw AND warn when trying to access classic APIs', function() {
503 - spyOn(console, 'warn');
527 + spyOnDev(console, 'warn');
528 var instance = test(
529 React.createElement(Inner, {name: 'foo'}),
530 'DIV','foo'
531 );
532 expect(() => instance.replaceState({})).toThrow();
533 expect(() => instance.isMounted()).toThrow();
510 - expect((<any>console.warn).calls.count()).toBe(2);
511 - expect((<any>console.warn).calls.argsFor(0)[0]).toContain(
512 - 'replaceState(...) is deprecated in plain JavaScript React classes'
513 - );
514 - expect((<any>console.warn).calls.argsFor(1)[0]).toContain(
515 - 'isMounted(...) is deprecated in plain JavaScript React classes'
516 - );
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 + }
543 });
544
545 it('supports this.context passed via getChildContext', function() {
packages/react/src/__tests__/createReactClassIntegration-test.js
+86 -68
@@ -52,7 +52,7 @@ describe('create-react-class-integration', () => {
52 });
53
54 it('should warn on invalid prop types', () => {
55 - spyOn(console, 'error');
55 + spyOnDev(console, 'error');
56 createReactClass({
57 displayName: 'Component',
58 propTypes: {
@@ -62,15 +62,17 @@ describe('create-react-class-integration', () => {
62 return <span>{this.props.prop}</span>;
63 },
64 });
65 - expect(console.error.calls.count()).toBe(1);
66 - expect(console.error.calls.argsFor(0)[0]).toBe(
67 - 'Warning: Component: prop type `prop` is invalid; ' +
68 - 'it must be a function, usually from React.PropTypes.',
69 - );
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 + }
72 });
73
74 it('should warn on invalid context types', () => {
73 - spyOn(console, 'error');
75 + spyOnDev(console, 'error');
76 createReactClass({
77 displayName: 'Component',
78 contextTypes: {
@@ -80,15 +82,17 @@ describe('create-react-class-integration', () => {
82 return <span>{this.props.prop}</span>;
83 },
84 });
83 - expect(console.error.calls.count()).toBe(1);
84 - expect(console.error.calls.argsFor(0)[0]).toBe(
85 - 'Warning: Component: context type `prop` is invalid; ' +
86 - 'it must be a function, usually from React.PropTypes.',
87 - );
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 + }
92 });
93
94 it('should throw on invalid child context types', () => {
91 - spyOn(console, 'error');
95 + spyOnDev(console, 'error');
96 createReactClass({
97 displayName: 'Component',
98 childContextTypes: {
@@ -98,15 +102,17 @@ describe('create-react-class-integration', () => {
102 return <span>{this.props.prop}</span>;
103 },
104 });
101 - expect(console.error.calls.count()).toBe(1);
102 - expect(console.error.calls.argsFor(0)[0]).toBe(
103 - 'Warning: Component: child context type `prop` is invalid; ' +
104 - 'it must be a function, usually from React.PropTypes.',
105 - );
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 + }
112 });
113
114 it('should warn when misspelling shouldComponentUpdate', () => {
109 - spyOn(console, 'error');
115 + spyOnDev(console, 'error');
116
117 createReactClass({
118 componentShouldUpdate: function() {
@@ -116,12 +122,14 @@ describe('create-react-class-integration', () => {
122 return <div />;
123 },
124 });
119 - expect(console.error.calls.count()).toBe(1);
120 - expect(console.error.calls.argsFor(0)[0]).toBe(
121 - 'Warning: A component has a method called componentShouldUpdate(). Did you ' +
122 - 'mean shouldComponentUpdate()? The name is phrased as a question ' +
123 - 'because the function is expected to return a value.',
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 + }
133
134 createReactClass({
135 displayName: 'NamedComponent',
@@ -132,16 +140,18 @@ describe('create-react-class-integration', () => {
140 return <div />;
141 },
142 });
135 - expect(console.error.calls.count()).toBe(2);
136 - expect(console.error.calls.argsFor(1)[0]).toBe(
137 - 'Warning: NamedComponent has a method called componentShouldUpdate(). Did you ' +
138 - 'mean shouldComponentUpdate()? The name is phrased as a question ' +
139 - 'because the function is expected to return a value.',
140 - );
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 + }
151 });
152
153 it('should warn when misspelling componentWillReceiveProps', () => {
144 - spyOn(console, 'error');
154 + spyOnDev(console, 'error');
155 createReactClass({
156 componentWillRecieveProps: function() {
157 return false;
@@ -150,11 +160,13 @@ describe('create-react-class-integration', () => {
160 return <div />;
161 },
162 });
153 - expect(console.error.calls.count()).toBe(1);
154 - expect(console.error.calls.argsFor(0)[0]).toBe(
155 - 'Warning: A component has a method called componentWillRecieveProps(). Did you ' +
156 - 'mean componentWillReceiveProps()?',
157 - );
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 + }
170 });
171
172 it('should throw if a reserved property is in statics', () => {
@@ -183,7 +195,7 @@ describe('create-react-class-integration', () => {
195 // TODO: Consider actually moving these to statics or drop this unit test.
196
197 xit('should warn when using deprecated non-static spec keys', () => {
186 - spyOn(console, 'error');
198 + spyOnDev(console, 'error');
199 createReactClass({
200 mixins: [{}],
201 propTypes: {
@@ -199,23 +211,25 @@ describe('create-react-class-integration', () => {
211 return <div />;
212 },
213 });
202 - expect(console.error.calls.count()).toBe(4);
203 - expect(console.error.calls.argsFor(0)[0]).toBe(
204 - 'createClass(...): `mixins` is now a static property and should ' +
205 - 'be defined inside "statics".',
206 - );
207 - expect(console.error.calls.argsFor(1)[0]).toBe(
208 - 'createClass(...): `propTypes` is now a static property and should ' +
209 - 'be defined inside "statics".',
210 - );
211 - expect(console.error.calls.argsFor(2)[0]).toBe(
212 - 'createClass(...): `contextTypes` is now a static property and ' +
213 - 'should be defined inside "statics".',
214 - );
215 - expect(console.error.calls.argsFor(3)[0]).toBe(
216 - 'createClass(...): `childContextTypes` is now a static property and ' +
217 - 'should be defined inside "statics".',
218 - );
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 + }
233 });
234
235 it('should support statics', () => {
@@ -328,7 +342,7 @@ describe('create-react-class-integration', () => {
342 });
343
344 it('should throw when using legacy factories', () => {
331 - spyOn(console, 'error');
345 + spyOnDev(console, 'error');
346 var Component = createReactClass({
347 render() {
348 return <div />;
@@ -336,11 +350,13 @@ describe('create-react-class-integration', () => {
350 });
351
352 expect(() => Component()).toThrow();
339 - expect(console.error.calls.count()).toBe(1);
340 - expect(console.error.calls.argsFor(0)[0]).toBe(
341 - 'Warning: Something is calling a React component directly. Use a ' +
342 - 'factory or JSX instead. See: https://fb.me/react-legacyfactory',
343 - );
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 + }
360 });
361
362 it('replaceState and callback works', () => {
@@ -363,7 +379,7 @@ describe('create-react-class-integration', () => {
379 });
380
381 it('isMounted works', () => {
366 - spyOn(console, 'error');
382 + spyOnDev(console, 'error');
383
384 var ops = [];
385 var instance;
@@ -439,11 +455,13 @@ describe('create-react-class-integration', () => {
455 'after unmount: false',
456 ]);
457
442 - expect(console.error.calls.count()).toBe(1);
443 - expect(console.error.calls.argsFor(0)[0]).toEqual(
444 - 'Warning: MyComponent: isMounted is deprecated. Instead, make sure to ' +
445 - 'clean up subscriptions and pending requests in componentWillUnmount ' +
446 - 'to prevent memory leaks.',
447 - );
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 + }
466 });
467 });
packages/shared/__tests__/ReactDOMFrameScheduling-test.js
+7 -5
@@ -15,12 +15,14 @@ describe('ReactDOMFrameScheduling', () => {
15 try {
16 global.requestAnimationFrame = undefined;
17 jest.resetModules();
18 - spyOn(console, 'error');
18 + spyOnDev(console, 'error');
19 require('react-dom');
20 - expect(console.error.calls.count()).toBe(1);
21 - expect(console.error.calls.argsFor(0)[0]).toContain(
22 - 'React depends on requestAnimationFrame.',
23 - );
20 + if (__DEV__) {
21 + expect(console.error.calls.count()).toBe(1);
22 + expect(console.error.calls.argsFor(0)[0]).toContain(
23 + 'React depends on requestAnimationFrame.',
24 + );
25 + }
26 } finally {
27 global.requestAnimationFrame = previousRAF;
28 }
packages/shared/__tests__/ReactErrorUtils-test.js
+165 -203
@@ -17,227 +17,189 @@ describe('ReactErrorUtils', () => {
17 ReactErrorUtils = require('shared/ReactErrorUtils').default;
18 });
19
20 - // Run tests in both DEV and production
21 - describe(
22 - 'invokeGuardedCallback (development)',
23 - invokeGuardedCallbackTests.bind(null, 'development'),
24 - );
25 - describe('invokeGuardedCallback (production)', () => {
26 - let oldProcess;
27 - beforeEach(() => {
28 - __DEV__ = false;
29 -
30 - // Mutating process.env.NODE_ENV would cause our babel plugins to do the
31 - // wrong thing. If you change this, make sure to test with jest --no-cache.
32 - oldProcess = process;
33 - global.process = {
34 - ...process,
35 - env: {...process.env, NODE_ENV: 'production'},
36 - };
37 -
38 - jest.resetModules();
39 - ReactErrorUtils = require('shared/ReactErrorUtils').default;
40 - });
41 -
42 - afterEach(() => {
43 - __DEV__ = true;
44 - global.process = oldProcess;
45 - });
46 -
47 - invokeGuardedCallbackTests('production');
20 + it(`it should rethrow caught errors`, () => {
21 + var err = new Error('foo');
22 + var callback = function() {
23 + throw err;
24 + };
25 + ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError(
26 + 'foo',
27 + callback,
28 + null,
29 + );
30 + expect(ReactErrorUtils.hasCaughtError()).toBe(false);
31 + expect(() => ReactErrorUtils.rethrowCaughtError()).toThrow(err);
32 });
33
50 - function invokeGuardedCallbackTests(environment) {
51 - it(`it should rethrow caught errors (${environment})`, () => {
52 - var err = new Error('foo');
53 - var callback = function() {
54 - throw err;
55 - };
56 - ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError(
57 - 'foo',
58 - callback,
59 - null,
60 - );
61 - expect(ReactErrorUtils.hasCaughtError()).toBe(false);
62 - expect(() => ReactErrorUtils.rethrowCaughtError()).toThrow(err);
63 - });
34 + it(`should call the callback the passed arguments`, () => {
35 + var callback = jest.fn();
36 + ReactErrorUtils.invokeGuardedCallback(
37 + 'foo',
38 + callback,
39 + null,
40 + 'arg1',
41 + 'arg2',
42 + );
43 + expect(callback).toBeCalledWith('arg1', 'arg2');
44 + });
45
65 - it(`should call the callback the passed arguments (${environment})`, () => {
66 - var callback = jest.fn();
67 - ReactErrorUtils.invokeGuardedCallback(
68 - 'foo',
69 - callback,
70 - null,
71 - 'arg1',
72 - 'arg2',
73 - );
74 - expect(callback).toBeCalledWith('arg1', 'arg2');
75 - });
46 + it(`should call the callback with the provided context`, () => {
47 + var context = {didCall: false};
48 + ReactErrorUtils.invokeGuardedCallback(
49 + 'foo',
50 + function() {
51 + this.didCall = true;
52 + },
53 + context,
54 + );
55 + expect(context.didCall).toBe(true);
56 + });
57
77 - it(`should call the callback with the provided context (${
78 - environment
79 - })`, () => {
80 - var context = {didCall: false};
81 - ReactErrorUtils.invokeGuardedCallback(
82 - 'foo',
83 - function() {
84 - this.didCall = true;
85 - },
86 - context,
87 - );
88 - expect(context.didCall).toBe(true);
89 - });
58 + it(`should catch errors`, () => {
59 + const error = new Error();
60 + const returnValue = ReactErrorUtils.invokeGuardedCallback(
61 + 'foo',
62 + function() {
63 + throw error;
64 + },
65 + null,
66 + 'arg1',
67 + 'arg2',
68 + );
69 + expect(returnValue).toBe(undefined);
70 + expect(ReactErrorUtils.hasCaughtError()).toBe(true);
71 + expect(ReactErrorUtils.clearCaughtError()).toBe(error);
72 + });
73
91 - it(`should catch errors (${environment})`, () => {
92 - const error = new Error();
93 - const returnValue = ReactErrorUtils.invokeGuardedCallback(
94 - 'foo',
95 - function() {
96 - throw error;
97 - },
98 - null,
99 - 'arg1',
100 - 'arg2',
101 - );
102 - expect(returnValue).toBe(undefined);
103 - expect(ReactErrorUtils.hasCaughtError()).toBe(true);
104 - expect(ReactErrorUtils.clearCaughtError()).toBe(error);
105 - });
74 + it(`should return false from clearCaughtError if no error was thrown`, () => {
75 + var callback = jest.fn();
76 + ReactErrorUtils.invokeGuardedCallback('foo', callback, null);
77 + expect(ReactErrorUtils.hasCaughtError()).toBe(false);
78 + expect(ReactErrorUtils.clearCaughtError).toThrow('no error was captured');
79 + });
80
107 - it(`should return false from clearCaughtError if no error was thrown (${
108 - environment
109 - })`, () => {
110 - var callback = jest.fn();
111 - ReactErrorUtils.invokeGuardedCallback('foo', callback, null);
112 - expect(ReactErrorUtils.hasCaughtError()).toBe(false);
113 - expect(ReactErrorUtils.clearCaughtError).toThrow(
114 - __DEV__ ? 'no error was captured' : 'Minified React error #198',
115 - );
116 - });
81 + it(`can nest with same debug name`, () => {
82 + const err1 = new Error();
83 + let err2;
84 + const err3 = new Error();
85 + let err4;
86 + ReactErrorUtils.invokeGuardedCallback(
87 + 'foo',
88 + function() {
89 + ReactErrorUtils.invokeGuardedCallback(
90 + 'foo',
91 + function() {
92 + throw err1;
93 + },
94 + null,
95 + );
96 + err2 = ReactErrorUtils.clearCaughtError();
97 + throw err3;
98 + },
99 + null,
100 + );
101 + err4 = ReactErrorUtils.clearCaughtError();
102 +
103 + expect(err2).toBe(err1);
104 + expect(err4).toBe(err3);
105 + });
106
118 - it(`can nest with same debug name (${environment})`, () => {
119 - const err1 = new Error();
120 - let err2;
121 - const err3 = new Error();
122 - let err4;
123 - ReactErrorUtils.invokeGuardedCallback(
124 - 'foo',
125 - function() {
126 - ReactErrorUtils.invokeGuardedCallback(
127 - 'foo',
128 - function() {
129 - throw err1;
130 - },
131 - null,
132 - );
133 - err2 = ReactErrorUtils.clearCaughtError();
134 - throw err3;
135 - },
136 - null,
137 - );
138 - err4 = ReactErrorUtils.clearCaughtError();
107 + it(`handles nested errors`, () => {
108 + const err1 = new Error();
109 + let err2;
110 + ReactErrorUtils.invokeGuardedCallback(
111 + 'foo',
112 + function() {
113 + ReactErrorUtils.invokeGuardedCallback(
114 + 'foo',
115 + function() {
116 + throw err1;
117 + },
118 + null,
119 + );
120 + err2 = ReactErrorUtils.clearCaughtError();
121 + },
122 + null,
123 + );
124 + // Returns null because inner error was already captured
125 + expect(ReactErrorUtils.hasCaughtError()).toBe(false);
126 +
127 + expect(err2).toBe(err1);
128 + });
129
140 - expect(err2).toBe(err1);
141 - expect(err4).toBe(err3);
142 - });
130 + it('handles nested errors in separate renderers', () => {
131 + const ReactErrorUtils1 = require('shared/ReactErrorUtils').default;
132 + jest.resetModules();
133 + const ReactErrorUtils2 = require('shared/ReactErrorUtils').default;
134 + expect(ReactErrorUtils1).not.toEqual(ReactErrorUtils2);
135
144 - it(`handles nested errors (${environment})`, () => {
145 - const err1 = new Error();
146 - let err2;
147 - ReactErrorUtils.invokeGuardedCallback(
148 - 'foo',
149 - function() {
150 - ReactErrorUtils.invokeGuardedCallback(
151 - 'foo',
152 - function() {
153 - throw err1;
154 - },
155 - null,
156 - );
157 - err2 = ReactErrorUtils.clearCaughtError();
158 - },
159 - null,
160 - );
161 - // Returns null because inner error was already captured
162 - expect(ReactErrorUtils.hasCaughtError()).toBe(false);
136 + let ops = [];
137
164 - expect(err2).toBe(err1);
165 - });
138 + ReactErrorUtils1.invokeGuardedCallback(
139 + null,
140 + () => {
141 + ReactErrorUtils2.invokeGuardedCallback(
142 + null,
143 + () => {
144 + throw new Error('nested error');
145 + },
146 + null,
147 + );
148 + // ReactErrorUtils2 should catch the error
149 + ops.push(ReactErrorUtils2.hasCaughtError());
150 + ops.push(ReactErrorUtils2.clearCaughtError().message);
151 + },
152 + null,
153 + );
154
167 - it('handles nested errors in separate renderers', () => {
168 - const ReactErrorUtils1 = require('shared/ReactErrorUtils').default;
169 - jest.resetModules();
170 - const ReactErrorUtils2 = require('shared/ReactErrorUtils').default;
171 - expect(ReactErrorUtils1).not.toEqual(ReactErrorUtils2);
155 + // ReactErrorUtils1 should not catch the error
156 + ops.push(ReactErrorUtils1.hasCaughtError());
157
173 - let ops = [];
158 + expect(ops).toEqual([true, 'nested error', false]);
159 + });
160
175 - ReactErrorUtils1.invokeGuardedCallback(
161 + if (!__DEV__) {
162 + // jsdom doesn't handle this properly, but Chrome and Firefox should. Test
163 + // this with a fixture.
164 + it('catches null values', () => {
165 + ReactErrorUtils.invokeGuardedCallback(
166 null,
177 - () => {
178 - ReactErrorUtils2.invokeGuardedCallback(
179 - null,
180 - () => {
181 - throw new Error('nested error');
182 - },
183 - null,
184 - );
185 - // ReactErrorUtils2 should catch the error
186 - ops.push(ReactErrorUtils2.hasCaughtError());
187 - ops.push(ReactErrorUtils2.clearCaughtError().message);
167 + function() {
168 + throw null; // eslint-disable-line no-throw-literal
169 },
170 null,
171 );
191 -
192 - // ReactErrorUtils1 should not catch the error
193 - ops.push(ReactErrorUtils1.hasCaughtError());
194 -
195 - expect(ops).toEqual([true, 'nested error', false]);
172 + expect(ReactErrorUtils.hasCaughtError()).toBe(true);
173 + expect(ReactErrorUtils.clearCaughtError()).toBe(null);
174 });
175 + }
176
198 - if (environment === 'production') {
199 - // jsdom doesn't handle this properly, but Chrome and Firefox should. Test
200 - // this with a fixture.
201 - it('catches null values', () => {
202 - ReactErrorUtils.invokeGuardedCallback(
203 - null,
204 - function() {
205 - throw null; // eslint-disable-line no-throw-literal
206 - },
207 - null,
208 - );
209 - expect(ReactErrorUtils.hasCaughtError()).toBe(true);
210 - expect(ReactErrorUtils.clearCaughtError()).toBe(null);
211 - });
212 - }
213 -
214 - it(`can be shimmed (${environment})`, () => {
215 - const ops = [];
216 - // Override the original invokeGuardedCallback
217 - ReactErrorUtils.injection.injectErrorUtils({
218 - invokeGuardedCallback(name, func, context, a) {
219 - ops.push(a);
220 - try {
221 - func.call(context, a);
222 - } catch (error) {
223 - this._hasCaughtError = true;
224 - this._caughtError = error;
225 - }
226 - },
227 - });
228 -
229 - var err = new Error('foo');
230 - var callback = function() {
231 - throw err;
232 - };
233 - ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError(
234 - 'foo',
235 - callback,
236 - null,
237 - 'somearg',
238 - );
239 - expect(() => ReactErrorUtils.rethrowCaughtError()).toThrow(err);
240 - expect(ops).toEqual(['somearg']);
177 + it(`can be shimmed`, () => {
178 + const ops = [];
179 + // Override the original invokeGuardedCallback
180 + ReactErrorUtils.injection.injectErrorUtils({
181 + invokeGuardedCallback(name, func, context, a) {
182 + ops.push(a);
183 + try {
184 + func.call(context, a);
185 + } catch (error) {
186 + this._hasCaughtError = true;
187 + this._caughtError = error;
188 + }
189 + },
190 });
242 - }
191 +
192 + var err = new Error('foo');
193 + var callback = function() {
194 + throw err;
195 + };
196 + ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError(
197 + 'foo',
198 + callback,
199 + null,
200 + 'somearg',
201 + );
202 + expect(() => ReactErrorUtils.rethrowCaughtError()).toThrow(err);
203 + expect(ops).toEqual(['somearg']);
204 + });
205 });
packages/shared/__tests__/reactProdInvariant-test.js
+18
@@ -11,11 +11,29 @@
11 var reactProdInvariant;
12
13 describe('reactProdInvariant', () => {
14 + let globalErrorMock;
15 +
16 beforeEach(() => {
17 + if (!__DEV__) {
18 + // In production, our Jest environment overrides the global Error
19 + // class in order to decode error messages automatically. However
20 + // this is a single test where we actually *don't* want to decode
21 + // them. So we assert that the OriginalError exists, and temporarily
22 + // set the global Error object back to it.
23 + globalErrorMock = global.Error;
24 + global.Error = globalErrorMock.OriginalError;
25 + expect(typeof global.Error).toBe('function');
26 + }
27 jest.resetModules();
28 reactProdInvariant = require('shared/reactProdInvariant').default;
29 });
30
31 + afterEach(() => {
32 + if (!__DEV__) {
33 + global.Error = globalErrorMock;
34 + }
35 + });
36 +
37 it('should throw with the correct number of `%s`s in the URL', () => {
38 expect(function() {
39 reactProdInvariant(124, 'foo', 'bar');
scripts/circleci/test_coverage.sh
+4 -1
@@ -2,7 +2,10 @@
2
3 set -e
4
5 -node ./scripts/tasks/jest --coverage
5 +yarn test --coverage --runInBand
6 if [ -z $CI_PULL_REQUEST ]; then
7 cat ./coverage/lcov.info | ./node_modules/.bin/coveralls
8 fi
9 +
10 +# TODO: should we also track prod code coverage somehow?
11 +# yarn test-prod --coverage
scripts/circleci/test_entry_point.sh
+5 -1
@@ -8,6 +8,10 @@ if [ $((1 % CIRCLE_NODE_TOTAL)) -eq "$CIRCLE_NODE_INDEX" ]; then
8 COMMANDS_TO_RUN+=('./scripts/circleci/test_coverage.sh')
9 fi
10
11 +if [ $((2 % CIRCLE_NODE_TOTAL)) -eq "$CIRCLE_NODE_INDEX" ]; then
12 + COMMANDS_TO_RUN+=('yarn test-prod --runInBand')
13 +fi
14 +
15 if [ $((3 % CIRCLE_NODE_TOTAL)) -eq "$CIRCLE_NODE_INDEX" ]; then
16 COMMANDS_TO_RUN+=('node ./scripts/tasks/eslint')
17 fi
@@ -16,7 +20,7 @@ fi
20 if [ $((0 % CIRCLE_NODE_TOTAL)) -eq "$CIRCLE_NODE_INDEX" ]; then
21 COMMANDS_TO_RUN+=('node ./scripts/prettier/index')
22 COMMANDS_TO_RUN+=('node ./scripts/tasks/flow')
19 - COMMANDS_TO_RUN+=('node ./scripts/tasks/jest')
23 + COMMANDS_TO_RUN+=('yarn test --runInBand')
24 COMMANDS_TO_RUN+=('./scripts/circleci/build.sh')
25 COMMANDS_TO_RUN+=('./scripts/circleci/check_license.sh')
26 COMMANDS_TO_RUN+=('./scripts/circleci/check_modules.sh')
scripts/jest/environment.js
+6 -2
@@ -1,7 +1,11 @@
1 /* eslint-disable */
2 -global.__DEV__ = true;
2
4 -// For testing DOM Fiber.
3 +const NODE_ENV = process.env.NODE_ENV;
4 +if (NODE_ENV !== 'development' && NODE_ENV !== 'production') {
5 + throw new Error('NODE_ENV must either be set to development or production.');
6 +}
7 +global.__DEV__ = NODE_ENV === 'development';
8 +
9 global.requestAnimationFrame = function(callback) {
10 setTimeout(callback);
11 };
scripts/jest/jest.d.ts
+2
@@ -1,4 +1,5 @@
1 declare var jasmine: any;
2 +declare var __DEV__: boolean;
3
4 declare function afterEach(fn: any): any;
5 declare function beforeEach(fn: any): any;
@@ -11,6 +12,7 @@ declare function expect(val: any): Expect;
12 declare var jest: Jest;
13 declare function pit(name: string, fn: any): void;
14 declare function spyOn(obj: any, key: string): any;
15 +declare function spyOnDev(obj: any, key: string): any;
16 declare function xdescribe(name: string, fn: any): void;
17 declare function xit(name: string, fn: any): void;
18
scripts/jest/preprocessor.js
-2
@@ -1,7 +1,5 @@
1 'use strict';
2
3 -process.env.NODE_ENV = 'development';
4 -
3 var path = require('path');
4
5 var babel = require('babel-core');
scripts/jest/setupSpecEquivalenceReporter.js
+6
@@ -16,6 +16,12 @@ global.expect = function() {
16 return expect.apply(this, arguments);
17 };
18
19 +global.spyOnDev = function(...args) {
20 + if (__DEV__) {
21 + return spyOn(...args);
22 + }
23 +};
24 +
25 beforeEach(() => (numExpectations = 0));
26
27 jasmine.currentEnv_.addReporter({
scripts/jest/test-framework-setup.js
+36 -20
@@ -6,12 +6,17 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) {
6 require('./setupSpecEquivalenceReporter.js');
7 } else {
8 var env = jasmine.getEnv();
9 + var errorMap = require('../error-codes/codes.json');
10
11 // TODO: Stop using spyOn in all the test since that seem deprecated.
12 // This is a legacy upgrade path strategy from:
13 // https://github.com/facebook/jest/blob/v20.0.4/packages/jest-matchers/src/spyMatchers.js#L160
14 const isSpy = spy => spy.calls && typeof spy.calls.count === 'function';
15
16 + // Dev-only spyOn should be ignored in production runs.
17 + global.spyOnDev =
18 + process.env.NODE_ENV === 'production' ? () => {} : global.spyOn;
19 +
20 ['error', 'warn'].forEach(methodName => {
21 var oldMethod = console[methodName];
22 var newMethod = function() {
@@ -46,28 +51,39 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) {
51 });
52 });
53
49 - var wrapDevMatcher = function(obj, name) {
50 - const original = obj[name];
51 - obj[name] = function devMatcher() {
52 - try {
53 - original.apply(this, arguments);
54 - } catch (e) {
55 - global.__hadDevFailures = e.stack;
54 + if (process.env.NODE_ENV === 'production') {
55 + // In production, we strip error messages and turn them into codes.
56 + // This decodes them back so that the test assertions on them work.
57 + var decodeErrorMessage = function(message) {
58 + if (!message) {
59 + return message;
60 + }
61 + const re = /error-decoder.html\?invariant=(\d+)([^\s]*)/;
62 + const matches = message.match(re);
63 + if (!matches || matches.length !== 3) {
64 + return message;
65 }
66 + const code = parseInt(matches[1], 10);
67 + const args = matches[2]
68 + .split('&')
69 + .filter(s => s.startsWith('args[]='))
70 + .map(s => s.substr('args[]='.length))
71 + .map(decodeURIComponent);
72 + const format = errorMap[code];
73 + let argIndex = 0;
74 + return format.replace(/%s/g, () => args[argIndex++]);
75 };
58 - };
59 -
60 - const expectDev = function expectDev(actual) {
61 - const expectation = expect(actual);
62 - if (global.__suppressDevFailures) {
63 - Object.keys(expectation).forEach(name => {
64 - wrapDevMatcher(expectation, name);
65 - wrapDevMatcher(expectation.not, name);
66 - });
67 - }
68 - return expectation;
69 - };
70 - global.expectDev = expectDev;
76 + const OriginalError = global.Error;
77 + const ErrorProxy = new Proxy(OriginalError, {
78 + construct(target, argumentsList, newTarget) {
79 + const error = Reflect.construct(target, argumentsList, newTarget);
80 + error.message = decodeErrorMessage(error.message);
81 + return error;
82 + },
83 + });
84 + ErrorProxy.OriginalError = OriginalError;
85 + global.Error = ErrorProxy;
86 + }
87
88 require('jasmine-check').install();
89 }
scripts/tasks/jest.js deleted
-31
@@ -1,31 +0,0 @@
1 -'use strict';
2 -
3 -const path = require('path');
4 -const spawn = require('child_process').spawn;
5 -const argv = require('minimist')(process.argv.slice(2));
6 -
7 -console.log('Running Jest');
8 -
9 -const args = [
10 - path.join('node_modules', 'jest-cli', 'bin', 'jest'),
11 - '--runInBand',
12 -];
13 -if (argv.coverage) {
14 - args.push('--coverage');
15 -}
16 -
17 -const jest = spawn('node', args, {
18 - stdio: 'inherit',
19 - env: Object.assign({}, process.env, {
20 - NODE_ENV: 'test',
21 - }),
22 -});
23 -
24 -jest.on('close', code => {
25 - if (code === 1) {
26 - console.error('Jest failed!');
27 - } else {
28 - console.log('Jest passed!');
29 - }
30 - process.exit(code);
31 -});
yarn.lock
+12 -1
@@ -1270,6 +1270,13 @@ create-react-class@^15.6.2:
1270 loose-envify "^1.3.1"
1271 object-assign "^4.1.1"
1272
1273 +cross-env@^5.1.1:
1274 + version "5.1.1"
1275 + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.1.1.tgz#b6d8ab97f304c0f71dae7277b75fe424c08dfa74"
1276 + dependencies:
1277 + cross-spawn "^5.1.0"
1278 + is-windows "^1.0.0"
1279 +
1280 cross-spawn-async@^2.2.2:
1281 version "2.2.5"
1282 resolved "https://registry.yarnpkg.com/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz#845ff0c0834a3ded9d160daca6d390906bb288cc"
@@ -1284,7 +1291,7 @@ cross-spawn@^2.1.5:
1291 cross-spawn-async "^2.2.2"
1292 spawn-sync "^1.0.15"
1293
1287 -cross-spawn@^5.0.1:
1294 +cross-spawn@^5.0.1, cross-spawn@^5.1.0:
1295 version "5.1.0"
1296 resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
1297 dependencies:
@@ -2592,6 +2599,10 @@ is-windows@^0.2.0:
2599 version "0.2.0"
2600 resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c"
2601
2602 +is-windows@^1.0.0:
2603 + version "1.0.1"
2604 + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.1.tgz#310db70f742d259a16a369202b51af84233310d9"
2605 +
2606 isarray@0.0.1:
2607 version "0.0.1"
2608 resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"