diff --git a/.eslintrc.js b/.eslintrc.js
index 3938a6cfe531b..ac446e7258025 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -59,6 +59,6 @@ module.exports = {
},
globals: {
- expectDev: true,
+ spyOnDev: true,
},
};
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 82b74cef3c6c0..3e93a2c8988b6 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -4,9 +4,10 @@
2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch TestName` is helpful in development.
-5. Format your code with [prettier](https://github.com/prettier/prettier) (`yarn prettier`).
-6. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only check changed files.
-7. Run the [Flow](https://flowtype.org/) typechecks (`yarn flow`).
-8. If you haven't already, complete the CLA.
+5. Run `yarn test-prod` to test in the production environment. It supports the same options as `yarn test`.
+6. Format your code with [prettier](https://github.com/prettier/prettier) (`yarn prettier`).
+7. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only check changed files.
+8. Run the [Flow](https://flowtype.org/) typechecks (`yarn flow`).
+9. If you haven't already, complete the CLA.
**Learn more about contributing:** https://reactjs.org/docs/how-to-contribute.html
diff --git a/.gitignore b/.gitignore
index 46f908d0cc31c..6de4ca21a6e44 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,4 +20,4 @@ chrome-user-data
*.iml
.vscode
*.swp
-*.swo
+*.swo
\ No newline at end of file
diff --git a/.watchmanconfig b/.watchmanconfig
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/fixtures/dom/src/components/fixtures/input-change-events/RadioNameChangeFixture.js b/fixtures/dom/src/components/fixtures/input-change-events/RadioNameChangeFixture.js
new file mode 100644
index 0000000000000..3df34baaf6e73
--- /dev/null
+++ b/fixtures/dom/src/components/fixtures/input-change-events/RadioNameChangeFixture.js
@@ -0,0 +1,48 @@
+const React = window.React;
+const noop = n => n;
+
+class RadioNameChangeFixture extends React.Component {
+ state = {
+ updated: false,
+ };
+ onClick = () => {
+ this.setState(state => {
+ return {updated: !state.updated};
+ });
+ };
+ render() {
+ const {updated} = this.state;
+ const radioName = updated ? 'firstName' : 'secondName';
+ return (
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default RadioNameChangeFixture;
diff --git a/fixtures/dom/src/components/fixtures/input-change-events/index.js b/fixtures/dom/src/components/fixtures/input-change-events/index.js
index 8390b080a44f4..a6d6ee4df2d1a 100644
--- a/fixtures/dom/src/components/fixtures/input-change-events/index.js
+++ b/fixtures/dom/src/components/fixtures/input-change-events/index.js
@@ -3,6 +3,7 @@ import TestCase from '../../TestCase';
import RangeKeyboardFixture from './RangeKeyboardFixture';
import RadioClickFixture from './RadioClickFixture';
import RadioGroupFixture from './RadioGroupFixture';
+import RadioNameChangeFixture from './RadioNameChangeFixture';
import InputPlaceholderFixture from './InputPlaceholderFixture';
const React = window.React;
@@ -88,6 +89,25 @@ class InputChangeEvents extends React.Component {
+
+
+
Click the toggle button
+
+
+
+ The checked radio button should switch between the first and second
+ radio button
+
+
+
+
);
}
diff --git a/package.json b/package.json
index 2db1c34839af2..c8ce7b99bce5a 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
"art": "^0.10.1",
"async": "^1.5.0",
"babel-cli": "^6.6.5",
+ "babel-code-frame": "^6.26.0",
"babel-core": "^6.0.0",
"babel-eslint": "^7.1.0",
"babel-jest": "^21.3.0-beta.4",
@@ -34,6 +35,7 @@
"babel-plugin-transform-es3-property-literals": "^6.5.0",
"babel-plugin-transform-object-rest-spread": "^6.6.5",
"babel-plugin-transform-react-jsx-source": "^6.8.0",
+ "babel-plugin-transform-regenerator": "^6.26.0",
"babel-preset-react": "^6.5.0",
"babel-traverse": "^6.9.0",
"babylon": "6.15.0",
@@ -44,6 +46,7 @@
"core-js": "^2.2.1",
"coveralls": "^2.11.6",
"create-react-class": "^15.6.2",
+ "cross-env": "^5.1.1",
"del": "^2.0.2",
"derequire": "^2.0.3",
"escape-string-regexp": "^1.0.5",
@@ -76,7 +79,7 @@
"prettier": "1.8.1",
"prop-types": "^15.6.0",
"rimraf": "^2.6.1",
- "rollup": "^0.49.3",
+ "rollup": "^0.51.7",
"rollup-plugin-alias": "^1.2.1",
"rollup-plugin-babel": "^2.7.1",
"rollup-plugin-closure-compiler-js": "^1.0.4",
@@ -101,40 +104,14 @@
"linc": "node ./scripts/tasks/linc.js",
"lint": "node ./scripts/tasks/eslint.js",
"postinstall": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json",
- "test": "jest",
+ "test": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.source.js",
+ "test-prod": "cross-env NODE_ENV=production jest --config ./scripts/jest/config.source.js",
+ "test-prod-build": "yarn test-build-prod",
+ "test-build": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.build.js",
+ "test-build-prod": "cross-env NODE_ENV=production jest --config ./scripts/jest/config.build.js",
"flow": "node ./scripts/tasks/flow.js",
"prettier": "node ./scripts/prettier/index.js write-changed",
"prettier-all": "node ./scripts/prettier/index.js write",
"version-check": "node ./scripts/tasks/version-check.js"
- },
- "jest": {
- "modulePathIgnorePatterns": [
- "/scripts/rollup/shims/",
- "/scripts/bench/"
- ],
- "transform": {
- ".*": "./scripts/jest/preprocessor.js"
- },
- "setupFiles": [
- "./scripts/jest/setup.js",
- "./scripts/jest/environment.js"
- ],
- "setupTestFrameworkScriptFile": "./scripts/jest/test-framework-setup.js",
- "testRegex": "/__tests__/.*(\\.js|\\.coffee|[^d]\\.ts)$",
- "moduleFileExtensions": [
- "js",
- "json",
- "node",
- "coffee",
- "ts"
- ],
- "roots": [
- "/packages",
- "/scripts"
- ],
- "collectCoverageFrom": [
- "packages/**/*.js"
- ],
- "timers": "fake"
}
}
diff --git a/packages/events/__tests__/EventPluginRegistry-test.js b/packages/events/__tests__/EventPluginRegistry-test.internal.js
similarity index 96%
rename from packages/events/__tests__/EventPluginRegistry-test.js
rename to packages/events/__tests__/EventPluginRegistry-test.internal.js
index 6273e7779901d..3af0a782378b0 100644
--- a/packages/events/__tests__/EventPluginRegistry-test.js
+++ b/packages/events/__tests__/EventPluginRegistry-test.internal.js
@@ -15,7 +15,10 @@ describe('EventPluginRegistry', () => {
beforeEach(() => {
jest.resetModuleRegistry();
- // TODO: can we express this test with only public API?
+ // These tests are intentionally testing the private injection interface.
+ // The public API surface of this is covered by other tests so
+ // if `EventPluginRegistry` is ever deleted, these tests should be
+ // safe to remove too.
EventPluginRegistry = require('events/EventPluginRegistry');
createPlugin = function(properties) {
diff --git a/packages/events/__tests__/ResponderEventPlugin-test.js b/packages/events/__tests__/ResponderEventPlugin-test.internal.js
similarity index 100%
rename from packages/events/__tests__/ResponderEventPlugin-test.js
rename to packages/events/__tests__/ResponderEventPlugin-test.internal.js
diff --git a/packages/events/__tests__/accumulateInto-test.js b/packages/events/__tests__/accumulateInto-test.internal.js
similarity index 100%
rename from packages/events/__tests__/accumulateInto-test.js
rename to packages/events/__tests__/accumulateInto-test.internal.js
diff --git a/packages/react-call-return/src/ReactCallReturn.js b/packages/react-call-return/src/ReactCallReturn.js
index c39c71dcea2c3..c6979dd436eeb 100644
--- a/packages/react-call-return/src/ReactCallReturn.js
+++ b/packages/react-call-return/src/ReactCallReturn.js
@@ -7,19 +7,9 @@
* @flow
*/
-import type {ReactCall, ReactNodeList, ReactReturn} from 'shared/ReactTypes';
+import {REACT_CALL_TYPE, REACT_RETURN_TYPE} from 'shared/ReactSymbols';
-// The Symbol used to tag the special React types. If there is no native Symbol
-// nor polyfill, then a plain number is used for performance.
-var REACT_CALL_TYPE;
-var REACT_RETURN_TYPE;
-if (typeof Symbol === 'function' && Symbol.for) {
- REACT_CALL_TYPE = Symbol.for('react.call');
- REACT_RETURN_TYPE = Symbol.for('react.return');
-} else {
- REACT_CALL_TYPE = 0xeac8;
- REACT_RETURN_TYPE = 0xeac9;
-}
+import type {ReactCall, ReactNodeList, ReactReturn} from 'shared/ReactTypes';
type CallHandler = (props: T, returns: Array) => ReactNodeList;
diff --git a/packages/react-cs-renderer/src/ReactNativeCSFeatureFlags.js b/packages/react-cs-renderer/src/ReactNativeCSFeatureFlags.js
index ccb7c09619dd1..e4c4d192e6e9e 100644
--- a/packages/react-cs-renderer/src/ReactNativeCSFeatureFlags.js
+++ b/packages/react-cs-renderer/src/ReactNativeCSFeatureFlags.js
@@ -12,6 +12,7 @@ import invariant from 'fbjs/lib/invariant';
import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
import typeof * as CSFeatureFlagsType from './ReactNativeCSFeatureFlags';
+export const debugRenderPhaseSideEffects = false;
export const enableAsyncSubtreeAPI = true;
export const enableAsyncSchedulingByDefaultInReactDOM = false;
export const enableReactFragment = false;
diff --git a/packages/react-cs-renderer/src/__tests__/ReactNativeCS-test.js b/packages/react-cs-renderer/src/__tests__/ReactNativeCS-test.internal.js
similarity index 100%
rename from packages/react-cs-renderer/src/__tests__/ReactNativeCS-test.js
rename to packages/react-cs-renderer/src/__tests__/ReactNativeCS-test.internal.js
diff --git a/packages/react-dom/src/__tests__/CSSPropertyOperations-test.js b/packages/react-dom/src/__tests__/CSSPropertyOperations-test.js
index 81b3efe48f76a..10190db51ef9e 100644
--- a/packages/react-dom/src/__tests__/CSSPropertyOperations-test.js
+++ b/packages/react-dom/src/__tests__/CSSPropertyOperations-test.js
@@ -91,15 +91,17 @@ describe('CSSPropertyOperations', () => {
}
}
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var root = document.createElement('div');
ReactDOM.render(, root);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
- 'Warning: Unsupported style property background-color. Did you mean backgroundColor?' +
- '\n in div (at **)' +
- '\n in Comp (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
+ 'Warning: Unsupported style property background-color. Did you mean backgroundColor?' +
+ '\n in div (at **)' +
+ '\n in Comp (at **)',
+ );
+ }
});
it('should warn when updating hyphenated style names', () => {
@@ -111,7 +113,7 @@ describe('CSSPropertyOperations', () => {
}
}
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var styles = {
'-ms-transform': 'translate3d(0, 0, 0)',
'-webkit-transform': 'translate3d(0, 0, 0)',
@@ -120,17 +122,19 @@ describe('CSSPropertyOperations', () => {
ReactDOM.render(, root);
ReactDOM.render(, root);
- expectDev(console.error.calls.count()).toBe(2);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
- 'Warning: Unsupported style property -ms-transform. Did you mean msTransform?' +
- '\n in div (at **)' +
- '\n in Comp (at **)',
- );
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toEqual(
- 'Warning: Unsupported style property -webkit-transform. Did you mean WebkitTransform?' +
- '\n in div (at **)' +
- '\n in Comp (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(2);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
+ 'Warning: Unsupported style property -ms-transform. Did you mean msTransform?' +
+ '\n in div (at **)' +
+ '\n in Comp (at **)',
+ );
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toEqual(
+ 'Warning: Unsupported style property -webkit-transform. Did you mean WebkitTransform?' +
+ '\n in div (at **)' +
+ '\n in Comp (at **)',
+ );
+ }
});
it('warns when miscapitalizing vendored style names', () => {
@@ -150,23 +154,25 @@ describe('CSSPropertyOperations', () => {
}
}
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var root = document.createElement('div');
ReactDOM.render(, root);
- // msTransform is correct already and shouldn't warn
- expectDev(console.error.calls.count()).toBe(2);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
- 'Warning: Unsupported vendor-prefixed style property oTransform. ' +
- 'Did you mean OTransform?' +
- '\n in div (at **)' +
- '\n in Comp (at **)',
- );
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toEqual(
- 'Warning: Unsupported vendor-prefixed style property webkitTransform. ' +
- 'Did you mean WebkitTransform?' +
- '\n in div (at **)' +
- '\n in Comp (at **)',
- );
+ if (__DEV__) {
+ // msTransform is correct already and shouldn't warn
+ expect(console.error.calls.count()).toBe(2);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
+ 'Warning: Unsupported vendor-prefixed style property oTransform. ' +
+ 'Did you mean OTransform?' +
+ '\n in div (at **)' +
+ '\n in Comp (at **)',
+ );
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toEqual(
+ 'Warning: Unsupported vendor-prefixed style property webkitTransform. ' +
+ 'Did you mean WebkitTransform?' +
+ '\n in div (at **)' +
+ '\n in Comp (at **)',
+ );
+ }
});
it('should warn about style having a trailing semicolon', () => {
@@ -187,22 +193,24 @@ describe('CSSPropertyOperations', () => {
}
}
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var root = document.createElement('div');
ReactDOM.render(, root);
- expectDev(console.error.calls.count()).toBe(2);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
- "Warning: Style property values shouldn't contain a semicolon. " +
- 'Try "backgroundColor: blue" instead.' +
- '\n in div (at **)' +
- '\n in Comp (at **)',
- );
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toEqual(
- "Warning: Style property values shouldn't contain a semicolon. " +
- 'Try "color: red" instead.' +
- '\n in div (at **)' +
- '\n in Comp (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(2);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
+ "Warning: Style property values shouldn't contain a semicolon. " +
+ 'Try "backgroundColor: blue" instead.' +
+ '\n in div (at **)' +
+ '\n in Comp (at **)',
+ );
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toEqual(
+ "Warning: Style property values shouldn't contain a semicolon. " +
+ 'Try "color: red" instead.' +
+ '\n in div (at **)' +
+ '\n in Comp (at **)',
+ );
+ }
});
it('should warn about style containing a NaN value', () => {
@@ -214,16 +222,18 @@ describe('CSSPropertyOperations', () => {
}
}
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var root = document.createElement('div');
ReactDOM.render(, root);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
- 'Warning: `NaN` is an invalid value for the `fontSize` css style property.' +
- '\n in div (at **)' +
- '\n in Comp (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
+ 'Warning: `NaN` is an invalid value for the `fontSize` css style property.' +
+ '\n in div (at **)' +
+ '\n in Comp (at **)',
+ );
+ }
});
it('should not warn when setting CSS custom properties', () => {
@@ -246,16 +256,18 @@ describe('CSSPropertyOperations', () => {
}
}
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var root = document.createElement('div');
ReactDOM.render(, root);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
- 'Warning: `Infinity` is an invalid value for the `fontSize` css style property.' +
- '\n in div (at **)' +
- '\n in Comp (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
+ 'Warning: `Infinity` is an invalid value for the `fontSize` css style property.' +
+ '\n in div (at **)' +
+ '\n in Comp (at **)',
+ );
+ }
});
it('should not add units to CSS custom properties', () => {
diff --git a/packages/react-dom/src/__tests__/DOMPropertyOperations-test.js b/packages/react-dom/src/__tests__/DOMPropertyOperations-test.js
index 7f544506a5f35..343adc05318d5 100644
--- a/packages/react-dom/src/__tests__/DOMPropertyOperations-test.js
+++ b/packages/react-dom/src/__tests__/DOMPropertyOperations-test.js
@@ -151,7 +151,7 @@ describe('DOMPropertyOperations', () => {
it('should not remove attributes for special properties', () => {
var container = document.createElement('div');
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactDOM.render(
,
container,
@@ -164,10 +164,12 @@ describe('DOMPropertyOperations', () => {
);
expect(container.firstChild.getAttribute('value')).toBe('foo');
expect(container.firstChild.value).toBe('foo');
- expect(console.error.calls.count()).toBe(1);
- expect(console.error.calls.argsFor(0)[0]).toContain(
- 'A component is changing a controlled input of type text to be uncontrolled',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'A component is changing a controlled input of type text to be uncontrolled',
+ );
+ }
});
});
});
diff --git a/packages/react-dom/src/__tests__/EventPluginHub-test.js b/packages/react-dom/src/__tests__/EventPluginHub-test.js
index eaf40602ab6b6..ec1f83d0ee821 100644
--- a/packages/react-dom/src/__tests__/EventPluginHub-test.js
+++ b/packages/react-dom/src/__tests__/EventPluginHub-test.js
@@ -22,7 +22,7 @@ describe('EventPluginHub', () => {
});
it('should prevent non-function listeners, at dispatch', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var node = ReactTestUtils.renderIntoDocument(
,
);
@@ -31,10 +31,12 @@ describe('EventPluginHub', () => {
}).toThrowError(
'Expected `onClick` listener to be a function, instead got a value of `string` type.',
);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'Expected `onClick` listener to be a function, instead got a value of `string` type.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'Expected `onClick` listener to be a function, instead got a value of `string` type.',
+ );
+ }
});
it('should not prevent null listeners, at dispatch', () => {
diff --git a/packages/react-dom/src/__tests__/ReactBrowserEventEmitter-test.js b/packages/react-dom/src/__tests__/ReactBrowserEventEmitter-test.internal.js
similarity index 99%
rename from packages/react-dom/src/__tests__/ReactBrowserEventEmitter-test.js
rename to packages/react-dom/src/__tests__/ReactBrowserEventEmitter-test.internal.js
index 647109a907b3d..cd4bff6102660 100644
--- a/packages/react-dom/src/__tests__/ReactBrowserEventEmitter-test.js
+++ b/packages/react-dom/src/__tests__/ReactBrowserEventEmitter-test.internal.js
@@ -285,13 +285,15 @@ describe('ReactBrowserEventEmitter', () => {
putListener(CHILD, ON_CLICK_KEY, recordIDAndReturnFalse.bind(null, CHILD));
putListener(PARENT, ON_CLICK_KEY, recordID.bind(null, PARENT));
putListener(GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, GRANDPARENT));
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactTestUtils.Simulate.click(CHILD);
expect(idCallOrder.length).toBe(3);
expect(idCallOrder[0]).toBe(CHILD);
expect(idCallOrder[1]).toBe(PARENT);
expect(idCallOrder[2]).toBe(GRANDPARENT);
- expectDev(console.error.calls.count()).toEqual(0);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toEqual(0);
+ }
});
/**
diff --git a/packages/react-dom/src/__tests__/ReactChildReconciler-test.js b/packages/react-dom/src/__tests__/ReactChildReconciler-test.js
index 09fa3d436e124..9bbf9176cf69a 100644
--- a/packages/react-dom/src/__tests__/ReactChildReconciler-test.js
+++ b/packages/react-dom/src/__tests__/ReactChildReconciler-test.js
@@ -46,7 +46,7 @@ describe('ReactChildReconciler', () => {
}
it('warns for duplicated array keys', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class Component extends React.Component {
render() {
@@ -56,17 +56,19 @@ describe('ReactChildReconciler', () => {
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'Keys should be unique so that components maintain their identity ' +
- 'across updates. Non-unique keys may cause children to be ' +
- 'duplicated and/or omitted — the behavior is unsupported and ' +
- 'could change in a future version.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'Keys should be unique so that components maintain their identity ' +
+ 'across updates. Non-unique keys may cause children to be ' +
+ 'duplicated and/or omitted — the behavior is unsupported and ' +
+ 'could change in a future version.',
+ );
+ }
});
it('warns for duplicated array keys with component stack info', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class Component extends React.Component {
render() {
@@ -88,24 +90,24 @@ describe('ReactChildReconciler', () => {
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(
- normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
- ).toContain(
- 'Encountered two children with the same key, `1`. ' +
- 'Keys should be unique so that components maintain their identity ' +
- 'across updates. Non-unique keys may cause children to be ' +
- 'duplicated and/or omitted — the behavior is unsupported and ' +
- 'could change in a future version.',
- ' in div (at **)\n' +
- ' in Component (at **)\n' +
- ' in Parent (at **)\n' +
- ' in GrandParent (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toContain(
+ 'Encountered two children with the same key, `1`. ' +
+ 'Keys should be unique so that components maintain their identity ' +
+ 'across updates. Non-unique keys may cause children to be ' +
+ 'duplicated and/or omitted — the behavior is unsupported and ' +
+ 'could change in a future version.',
+ ' in div (at **)\n' +
+ ' in Component (at **)\n' +
+ ' in Parent (at **)\n' +
+ ' in GrandParent (at **)',
+ );
+ }
});
it('warns for duplicated iterable keys', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class Component extends React.Component {
render() {
@@ -115,17 +117,19 @@ describe('ReactChildReconciler', () => {
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'Keys should be unique so that components maintain their identity ' +
- 'across updates. Non-unique keys may cause children to be ' +
- 'duplicated and/or omitted — the behavior is unsupported and ' +
- 'could change in a future version.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'Keys should be unique so that components maintain their identity ' +
+ 'across updates. Non-unique keys may cause children to be ' +
+ 'duplicated and/or omitted — the behavior is unsupported and ' +
+ 'could change in a future version.',
+ );
+ }
});
it('warns for duplicated iterable keys with component stack info', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class Component extends React.Component {
render() {
@@ -147,19 +151,19 @@ describe('ReactChildReconciler', () => {
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(
- normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
- ).toContain(
- 'Encountered two children with the same key, `1`. ' +
- 'Keys should be unique so that components maintain their identity ' +
- 'across updates. Non-unique keys may cause children to be ' +
- 'duplicated and/or omitted — the behavior is unsupported and ' +
- 'could change in a future version.',
- ' in div (at **)\n' +
- ' in Component (at **)\n' +
- ' in Parent (at **)\n' +
- ' in GrandParent (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toContain(
+ 'Encountered two children with the same key, `1`. ' +
+ 'Keys should be unique so that components maintain their identity ' +
+ 'across updates. Non-unique keys may cause children to be ' +
+ 'duplicated and/or omitted — the behavior is unsupported and ' +
+ 'could change in a future version.',
+ ' in div (at **)\n' +
+ ' in Component (at **)\n' +
+ ' in Parent (at **)\n' +
+ ' in GrandParent (at **)',
+ );
+ }
});
});
diff --git a/packages/react-dom/src/__tests__/ReactComponent-test.js b/packages/react-dom/src/__tests__/ReactComponent-test.js
index 0a63915429c2a..1f7da05821317 100644
--- a/packages/react-dom/src/__tests__/ReactComponent-test.js
+++ b/packages/react-dom/src/__tests__/ReactComponent-test.js
@@ -45,13 +45,23 @@ describe('ReactComponent', () => {
}).toThrow();
});
- it('should warn when children are mutated during render', () => {
- spyOn(console, 'error');
+ it('should throw (in dev) when children are mutated during render', () => {
+ spyOnDev(console, 'error');
function Wrapper(props) {
props.children[1] = ; // Mutation is illegal
return
{props.children}
;
}
- expect(() => {
+ if (__DEV__) {
+ expect(() => {
+ ReactTestUtils.renderIntoDocument(
+
+
+
+
+ ,
+ );
+ }).toThrowError(/Cannot assign to read only property.*/);
+ } else {
ReactTestUtils.renderIntoDocument(
@@ -59,11 +69,11 @@ describe('ReactComponent', () => {
,
);
- }).toThrowError(/Cannot assign to read only property.*/);
+ }
});
- it('should warn when children are mutated during update', () => {
- spyOn(console, 'error');
+ it('should throw (in dev) when children are mutated during update', () => {
+ spyOnDev(console, 'error');
class Wrapper extends React.Component {
componentDidMount() {
@@ -76,7 +86,17 @@ describe('ReactComponent', () => {
}
}
- expect(() => {
+ if (__DEV__) {
+ expect(() => {
+ ReactTestUtils.renderIntoDocument(
+
+
+
+
+ ,
+ );
+ }).toThrowError(/Cannot assign to read only property.*/);
+ } else {
ReactTestUtils.renderIntoDocument(
@@ -84,7 +104,7 @@ describe('ReactComponent', () => {
,
);
- }).toThrowError(/Cannot assign to read only property.*/);
+ }
});
it('should support refs on owned components', () => {
@@ -335,14 +355,16 @@ describe('ReactComponent', () => {
});
it('throws usefully when rendering badly-typed elements', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var X = undefined;
expect(() => ReactTestUtils.renderIntoDocument()).toThrowError(
'Element type is invalid: expected a string (for built-in components) ' +
- 'or a class/function (for composite components) but got: undefined. ' +
- "You likely forgot to export your component from the file it's " +
- 'defined in, or you might have mixed up default and named imports.',
+ 'or a class/function (for composite components) but got: undefined.' +
+ (__DEV__
+ ? " You likely forgot to export your component from the file it's " +
+ 'defined in, or you might have mixed up default and named imports.'
+ : ''),
);
var Y = null;
@@ -351,12 +373,14 @@ describe('ReactComponent', () => {
'or a class/function (for composite components) but got: null.',
);
- // One warning for each element creation
- expectDev(console.error.calls.count()).toBe(2);
+ if (__DEV__) {
+ // One warning for each element creation
+ expect(console.error.calls.count()).toBe(2);
+ }
});
it('includes owner name in the error about badly-typed elements', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var X = undefined;
@@ -378,14 +402,18 @@ describe('ReactComponent', () => {
expect(() => ReactTestUtils.renderIntoDocument()).toThrowError(
'Element type is invalid: expected a string (for built-in components) ' +
- 'or a class/function (for composite components) but got: undefined. ' +
- "You likely forgot to export your component from the file it's " +
- 'defined in, or you might have mixed up default and named imports.' +
- '\n\nCheck the render method of `Bar`.',
+ 'or a class/function (for composite components) but got: undefined.' +
+ (__DEV__
+ ? " You likely forgot to export your component from the file it's " +
+ 'defined in, or you might have mixed up default and named imports.' +
+ '\n\nCheck the render method of `Bar`.'
+ : ''),
);
- // One warning for each element creation
- expectDev(console.error.calls.count()).toBe(1);
+ if (__DEV__) {
+ // One warning for each element creation
+ expect(console.error.calls.count()).toBe(1);
+ }
});
it('throws if a plain object is used as a child', () => {
@@ -404,10 +432,12 @@ describe('ReactComponent', () => {
}
expect(ex).toBeDefined();
expect(normalizeCodeLocInfo(ex.message)).toBe(
- 'Objects are not valid as a React child (found: object with keys ' +
- '{x, y, z}). If you meant to render a collection of children, use ' +
- 'an array instead.' +
- '\n in div (at **)',
+ 'Objects are not valid as a React child (found: object with keys {x, y, z}).' +
+ (__DEV__
+ ? ' If you meant to render a collection of children, use ' +
+ 'an array instead.' +
+ '\n in div (at **)'
+ : ''),
);
});
@@ -431,11 +461,13 @@ describe('ReactComponent', () => {
}
expect(ex).toBeDefined();
expect(normalizeCodeLocInfo(ex.message)).toBe(
- 'Objects are not valid as a React child (found: object with keys ' +
- '{a, b, c}). If you meant to render a collection of children, use ' +
- 'an array instead.\n' +
- ' in div (at **)\n' +
- ' in Foo (at **)',
+ 'Objects are not valid as a React child (found: object with keys {a, b, c}).' +
+ (__DEV__
+ ? ' If you meant to render a collection of children, use ' +
+ 'an array instead.\n' +
+ ' in div (at **)\n' +
+ ' in Foo (at **)'
+ : ''),
);
});
@@ -454,10 +486,12 @@ describe('ReactComponent', () => {
}
expect(ex).toBeDefined();
expect(normalizeCodeLocInfo(ex.message)).toBe(
- 'Objects are not valid as a React child (found: object with keys ' +
- '{x, y, z}). If you meant to render a collection of children, use ' +
- 'an array instead.' +
- '\n in div (at **)',
+ 'Objects are not valid as a React child (found: object with keys {x, y, z}).' +
+ (__DEV__
+ ? ' If you meant to render a collection of children, use ' +
+ 'an array instead.' +
+ '\n in div (at **)'
+ : ''),
);
});
@@ -481,11 +515,13 @@ describe('ReactComponent', () => {
}
expect(ex).toBeDefined();
expect(normalizeCodeLocInfo(ex.message)).toBe(
- 'Objects are not valid as a React child (found: object with keys ' +
- '{a, b, c}). If you meant to render a collection of children, use ' +
- 'an array instead.\n' +
- ' in div (at **)\n' +
- ' in Foo (at **)',
+ 'Objects are not valid as a React child (found: object with keys {a, b, c}).' +
+ (__DEV__
+ ? ' If you meant to render a collection of children, use ' +
+ 'an array instead.\n' +
+ ' in div (at **)\n' +
+ ' in Foo (at **)'
+ : ''),
);
});
@@ -494,16 +530,18 @@ describe('ReactComponent', () => {
function Foo() {
return Foo;
}
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
ReactDOM.render(, container);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Functions are not valid as a React child. This may happen if ' +
- 'you return a Component instead of from render. ' +
- 'Or maybe you meant to call this function rather than return it.\n' +
- ' in Foo (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: Functions are not valid as a React child. This may happen if ' +
+ 'you return a Component instead of from render. ' +
+ 'Or maybe you meant to call this function rather than return it.\n' +
+ ' in Foo (at **)',
+ );
+ }
});
it('warns on function as a return value from a class', () => {
@@ -512,16 +550,18 @@ describe('ReactComponent', () => {
return Foo;
}
}
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
ReactDOM.render(, container);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Functions are not valid as a React child. This may happen if ' +
- 'you return a Component instead of from render. ' +
- 'Or maybe you meant to call this function rather than return it.\n' +
- ' in Foo (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: Functions are not valid as a React child. This may happen if ' +
+ 'you return a Component instead of from render. ' +
+ 'Or maybe you meant to call this function rather than return it.\n' +
+ ' in Foo (at **)',
+ );
+ }
});
it('warns on function as a child to host component', () => {
@@ -532,18 +572,20 @@ describe('ReactComponent', () => {
);
}
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
ReactDOM.render(, container);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Functions are not valid as a React child. This may happen if ' +
- 'you return a Component instead of from render. ' +
- 'Or maybe you meant to call this function rather than return it.\n' +
- ' in span (at **)\n' +
- ' in div (at **)\n' +
- ' in Foo (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: Functions are not valid as a React child. This may happen if ' +
+ 'you return a Component instead of from render. ' +
+ 'Or maybe you meant to call this function rather than return it.\n' +
+ ' in span (at **)\n' +
+ ' in div (at **)\n' +
+ ' in Foo (at **)',
+ );
+ }
});
it('does not warn for function-as-a-child that gets resolved', () => {
@@ -553,15 +595,13 @@ describe('ReactComponent', () => {
function Foo() {
return {() => 'Hello'};
}
- spyOn(console, 'error');
var container = document.createElement('div');
ReactDOM.render(, container);
expect(container.innerHTML).toBe('Hello');
- expectDev(console.error.calls.count()).toBe(0);
});
it('deduplicates function type warnings based on component type', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class Foo extends React.PureComponent {
constructor() {
super();
@@ -583,22 +623,24 @@ describe('ReactComponent', () => {
var container = document.createElement('div');
var component = ReactDOM.render(, container);
component.setState({type: 'portobello mushrooms'});
- expectDev(console.error.calls.count()).toBe(2);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Functions are not valid as a React child. This may happen if ' +
- 'you return a Component instead of from render. ' +
- 'Or maybe you meant to call this function rather than return it.\n' +
- ' in div (at **)\n' +
- ' in Foo (at **)',
- );
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
- 'Warning: Functions are not valid as a React child. This may happen if ' +
- 'you return a Component instead of from render. ' +
- 'Or maybe you meant to call this function rather than return it.\n' +
- ' in span (at **)\n' +
- ' in div (at **)\n' +
- ' in Foo (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(2);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: Functions are not valid as a React child. This may happen if ' +
+ 'you return a Component instead of from render. ' +
+ 'Or maybe you meant to call this function rather than return it.\n' +
+ ' in div (at **)\n' +
+ ' in Foo (at **)',
+ );
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
+ 'Warning: Functions are not valid as a React child. This may happen if ' +
+ 'you return a Component instead of from render. ' +
+ 'Or maybe you meant to call this function rather than return it.\n' +
+ ' in span (at **)\n' +
+ ' in div (at **)\n' +
+ ' in Foo (at **)',
+ );
+ }
});
});
});
diff --git a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
index 66eaecbcf2e57..4da75f8629121 100644
--- a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
+++ b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
@@ -198,7 +198,7 @@ describe('ReactComponentLifeCycle', () => {
});
it('should not allow update state inside of getInitialState', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class StatefulComponent extends React.Component {
constructor(props, context) {
@@ -214,21 +214,25 @@ describe('ReactComponentLifeCycle', () => {
}
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toBe(
- 'Warning: setState(...): Can only update a mounted or ' +
- 'mounting component. This usually means you called setState() on an ' +
- 'unmounted component. This is a no-op.\n\nPlease check the code for the ' +
- 'StatefulComponent component.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toBe(
+ 'Warning: setState(...): Can only update a mounted or ' +
+ 'mounting component. This usually means you called setState() on an ' +
+ 'unmounted component. This is a no-op.\n\nPlease check the code for the ' +
+ 'StatefulComponent component.',
+ );
+ }
// Check deduplication
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(1);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ }
});
it('should correctly determine if a component is mounted', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class Component extends React.Component {
_isMounted() {
// No longer a public API, but we can test that it works internally by
@@ -252,14 +256,16 @@ describe('ReactComponentLifeCycle', () => {
var instance = ReactTestUtils.renderIntoDocument(element);
expect(instance._isMounted()).toBeTruthy();
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'Component is accessing isMounted inside its render()',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'Component is accessing isMounted inside its render()',
+ );
+ }
});
it('should correctly determine if a null component is mounted', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class Component extends React.Component {
_isMounted() {
// No longer a public API, but we can test that it works internally by
@@ -283,10 +289,12 @@ describe('ReactComponentLifeCycle', () => {
var instance = ReactTestUtils.renderIntoDocument(element);
expect(instance._isMounted()).toBeTruthy();
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'Component is accessing isMounted inside its render()',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'Component is accessing isMounted inside its render()',
+ );
+ }
});
it('isMounted should return false when unmounted', () => {
@@ -309,7 +317,7 @@ describe('ReactComponentLifeCycle', () => {
});
it('warns if findDOMNode is used inside render', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class Component extends React.Component {
state = {isMounted: false};
componentDidMount() {
@@ -324,14 +332,16 @@ describe('ReactComponentLifeCycle', () => {
}
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'Component is accessing findDOMNode inside its render()',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'Component is accessing findDOMNode inside its render()',
+ );
+ }
});
it('should carry through each of the phases of setup', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class LifeCycleComponent extends React.Component {
constructor(props, context) {
@@ -440,10 +450,12 @@ describe('ReactComponentLifeCycle', () => {
expect(getLifeCycleState(instance)).toBe('UNMOUNTED');
expect(instance.state).toEqual(POST_WILL_UNMOUNT_STATE);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'LifeCycleComponent is accessing isMounted inside its render() function',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'LifeCycleComponent is accessing isMounted inside its render() function',
+ );
+ }
});
it('should not throw when updating an auxiliary component', () => {
diff --git a/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js b/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
index 17b54543fdf02..1bac93ec046c1 100644
--- a/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
+++ b/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
@@ -122,26 +122,29 @@ describe('ReactCompositeComponent', () => {
}
}
- spyOn(console, 'warn');
+ spyOnDev(console, 'warn');
var markup = ReactDOMServer.renderToString();
// Old API based on heuristic
var container = document.createElement('div');
container.innerHTML = markup;
ReactDOM.render(, container);
- expectDev(console.warn.calls.count()).toBe(1);
- expectDev(console.warn.calls.argsFor(0)[0]).toContain(
- 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
- 'will stop working in React v17. Replace the ReactDOM.render() call ' +
- 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
- );
-
+ if (__DEV__) {
+ expect(console.warn.calls.count()).toBe(1);
+ expect(console.warn.calls.argsFor(0)[0]).toContain(
+ 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
+ 'will stop working in React v17. Replace the ReactDOM.render() call ' +
+ 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
+ );
+ console.warn.calls.reset();
+ }
// New explicit API
- console.warn.calls.reset();
container = document.createElement('div');
container.innerHTML = markup;
ReactDOM.hydrate(, container);
- expectDev(console.warn.calls.count()).toBe(0);
+ if (__DEV__) {
+ expect(console.warn.calls.count()).toBe(0);
+ }
});
it('should react to state changes from callbacks', () => {
@@ -231,7 +234,7 @@ describe('ReactCompositeComponent', () => {
});
it('should warn about `forceUpdate` on unmounted components', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
document.body.appendChild(container);
@@ -248,25 +251,31 @@ describe('ReactCompositeComponent', () => {
instance = ReactDOM.render(instance, container);
instance.forceUpdate();
- expectDev(console.error.calls.count()).toBe(0);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(0);
+ }
ReactDOM.unmountComponentAtNode(container);
instance.forceUpdate();
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'Can only update a mounted or mounting component. This usually means ' +
- 'you called setState, replaceState, or forceUpdate on an unmounted ' +
- 'component. This is a no-op.\n\nPlease check the code for the ' +
- 'Component component.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'Can only update a mounted or mounting component. This usually means ' +
+ 'you called setState, replaceState, or forceUpdate on an unmounted ' +
+ 'component. This is a no-op.\n\nPlease check the code for the ' +
+ 'Component component.',
+ );
+ }
instance.forceUpdate();
- expectDev(console.error.calls.count()).toBe(1);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ }
});
it('should warn about `setState` on unmounted components', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
document.body.appendChild(container);
@@ -291,7 +300,9 @@ describe('ReactCompositeComponent', () => {
instance.setState({value: 1});
- expectDev(console.error.calls.count()).toBe(0);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(0);
+ }
expect(renders).toBe(2);
@@ -300,13 +311,15 @@ describe('ReactCompositeComponent', () => {
expect(renders).toBe(2);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'Can only update a mounted or mounting component. This usually means ' +
- 'you called setState, replaceState, or forceUpdate on an unmounted ' +
- 'component. This is a no-op.\n\nPlease check the code for the ' +
- 'Component component.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'Can only update a mounted or mounting component. This usually means ' +
+ 'you called setState, replaceState, or forceUpdate on an unmounted ' +
+ 'component. This is a no-op.\n\nPlease check the code for the ' +
+ 'Component component.',
+ );
+ }
});
it('should silently allow `setState`, not call cb on unmounting components', () => {
@@ -338,27 +351,31 @@ describe('ReactCompositeComponent', () => {
});
it('should warn when rendering a class with a render method that does not extend React.Component', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
class ClassWithRenderNotExtended {
render() {
return ;
}
}
- expectDev(console.error.calls.count()).toBe(0);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(0);
+ }
expect(() => {
ReactDOM.render(, container);
}).toThrow(TypeError);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'Warning: The component appears to have a render method, ' +
- "but doesn't extend React.Component. This is likely to cause errors. " +
- 'Change ClassWithRenderNotExtended to extend React.Component instead.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'Warning: The component appears to have a render method, ' +
+ "but doesn't extend React.Component. This is likely to cause errors. " +
+ 'Change ClassWithRenderNotExtended to extend React.Component instead.',
+ );
+ }
});
it('should warn about `setState` in render', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
@@ -378,17 +395,21 @@ describe('ReactCompositeComponent', () => {
}
}
- expectDev(console.error.calls.count()).toBe(0);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(0);
+ }
var instance = ReactDOM.render(, container);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'Cannot update during an existing state transition (such as within ' +
- "`render` or another component's constructor). Render methods should " +
- 'be a pure function of props and state; constructor side-effects are ' +
- 'an anti-pattern, but can be moved to `componentWillMount`.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'Cannot update during an existing state transition (such as within ' +
+ "`render` or another component's constructor). Render methods should " +
+ 'be a pure function of props and state; constructor side-effects are ' +
+ 'an anti-pattern, but can be moved to `componentWillMount`.',
+ );
+ }
// The setState call is queued and then executed as a second pass. This
// behavior is undefined though so we're free to change it to suit the
@@ -406,11 +427,13 @@ describe('ReactCompositeComponent', () => {
// Test deduplication
ReactDOM.unmountComponentAtNode(container);
ReactDOM.render(, container);
- expectDev(console.error.calls.count()).toBe(1);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ }
});
it('should warn about `setState` in getChildContext', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
@@ -432,19 +455,25 @@ describe('ReactCompositeComponent', () => {
}
Component.childContextTypes = {};
- expectDev(console.error.calls.count()).toBe(0);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(0);
+ }
var instance = ReactDOM.render(, container);
expect(renderPasses).toBe(2);
expect(instance.state.value).toBe(1);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toBe(
- 'Warning: setState(...): Cannot call setState() inside getChildContext()',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toBe(
+ 'Warning: setState(...): Cannot call setState() inside getChildContext()',
+ );
+ }
// Test deduplication
ReactDOM.unmountComponentAtNode(container);
ReactDOM.render(, container);
- expectDev(console.error.calls.count()).toBe(1);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ }
});
it('should cleanup even if render() fatals', () => {
@@ -496,7 +525,7 @@ describe('ReactCompositeComponent', () => {
});
it('should warn when shouldComponentUpdate() returns undefined', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class Component extends React.Component {
state = {bogus: false};
@@ -513,15 +542,17 @@ describe('ReactCompositeComponent', () => {
var instance = ReactTestUtils.renderIntoDocument();
instance.setState({bogus: true});
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toBe(
- 'Warning: Component.shouldComponentUpdate(): Returned undefined instead of a ' +
- 'boolean value. Make sure to return true or false.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toBe(
+ 'Warning: Component.shouldComponentUpdate(): Returned undefined instead of a ' +
+ 'boolean value. Make sure to return true or false.',
+ );
+ }
});
it('should warn when componentDidUnmount method is defined', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class Component extends React.Component {
componentDidUnmount = () => {};
@@ -533,16 +564,18 @@ describe('ReactCompositeComponent', () => {
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toBe(
- 'Warning: Component has a method called ' +
- 'componentDidUnmount(). But there is no such lifecycle method. ' +
- 'Did you mean componentWillUnmount()?',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toBe(
+ 'Warning: Component has a method called ' +
+ 'componentDidUnmount(). But there is no such lifecycle method. ' +
+ 'Did you mean componentWillUnmount()?',
+ );
+ }
});
it('should warn when componentDidReceiveProps method is defined', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class Component extends React.Component {
componentDidReceiveProps = () => {};
@@ -554,18 +587,20 @@ describe('ReactCompositeComponent', () => {
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toBe(
- 'Warning: Component has a method called ' +
- 'componentDidReceiveProps(). But there is no such lifecycle method. ' +
- 'If you meant to update the state in response to changing props, ' +
- 'use componentWillReceiveProps(). If you meant to fetch data or ' +
- 'run side-effects or mutations after React has updated the UI, use componentDidUpdate().',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toBe(
+ 'Warning: Component has a method called ' +
+ 'componentDidReceiveProps(). But there is no such lifecycle method. ' +
+ 'If you meant to update the state in response to changing props, ' +
+ 'use componentWillReceiveProps(). If you meant to fetch data or ' +
+ 'run side-effects or mutations after React has updated the UI, use componentDidUpdate().',
+ );
+ }
});
it('should warn when defaultProps was defined as an instance property', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class Component extends React.Component {
constructor(props) {
@@ -580,11 +615,13 @@ describe('ReactCompositeComponent', () => {
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toBe(
- 'Warning: Setting defaultProps as an instance property on Component is not supported ' +
- 'and will be ignored. Instead, define defaultProps as a static property on Component.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toBe(
+ 'Warning: Setting defaultProps as an instance property on Component is not supported ' +
+ 'and will be ignored. Instead, define defaultProps as a static property on Component.',
+ );
+ }
});
it('should pass context to children when not owner', () => {
@@ -1056,7 +1093,7 @@ describe('ReactCompositeComponent', () => {
});
it('should disallow nested render calls', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class Inner extends React.Component {
render() {
@@ -1072,13 +1109,15 @@ describe('ReactCompositeComponent', () => {
}
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toMatch(
- 'Render methods should be a pure function of props and state; ' +
- 'triggering nested component updates from render is not allowed. If ' +
- 'necessary, trigger nested updates in componentDidUpdate.\n\nCheck the ' +
- 'render method of Outer.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toMatch(
+ 'Render methods should be a pure function of props and state; ' +
+ 'triggering nested component updates from render is not allowed. If ' +
+ 'necessary, trigger nested updates in componentDidUpdate.\n\nCheck the ' +
+ 'render method of Outer.',
+ );
+ }
});
it('only renders once if updated in componentWillReceiveProps', () => {
@@ -1353,7 +1392,7 @@ describe('ReactCompositeComponent', () => {
});
it('should warn when mutated props are passed', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
@@ -1368,15 +1407,19 @@ describe('ReactCompositeComponent', () => {
}
}
- expectDev(console.error.calls.count()).toBe(0);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(0);
+ }
ReactDOM.render(, container);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'Foo(...): When calling super() in `Foo`, make sure to pass ' +
- "up the same props that your component's constructor was passed.",
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'Foo(...): When calling super() in `Foo`, make sure to pass ' +
+ "up the same props that your component's constructor was passed.",
+ );
+ }
});
it('should only call componentWillUnmount once', () => {
@@ -1591,8 +1634,75 @@ describe('ReactCompositeComponent', () => {
expect(mockArgs.length).toEqual(0);
});
+ it('this.state should be updated on setState callback inside componentWillMount', () => {
+ const div = document.createElement('div');
+ let stateSuccessfullyUpdated = false;
+
+ class Component extends React.Component {
+ constructor(props, context) {
+ super(props, context);
+ this.state = {
+ hasUpdatedState: false,
+ };
+ }
+
+ componentWillMount() {
+ this.setState(
+ {hasUpdatedState: true},
+ () => (stateSuccessfullyUpdated = this.state.hasUpdatedState),
+ );
+ }
+
+ render() {
+ return
;
+ }
+ }
+
+ ReactDOM.render(, div);
+
+ expect(instance).toBeDefined();
+ expect(mockFn).not.toBeCalled();
+
+ instance.setState({hasUpdatedState: true}, () => {
+ expect(mockFn).toBeCalled();
+ expect(instance.state.hasUpdatedState).toBe(true);
+ done();
+ });
+ });
+
it('should return a meaningful warning when constructor is returned', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class RenderTextInvalidConstructor extends React.Component {
constructor(props) {
super(props);
@@ -1608,25 +1718,29 @@ describe('ReactCompositeComponent', () => {
ReactTestUtils.renderIntoDocument();
}).toThrow();
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.mostRecent().args[0]).toBe(
- 'Warning: RenderTextInvalidConstructor(...): No `render` method found on the returned component instance: ' +
- 'did you accidentally return an object from the constructor?',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.mostRecent().args[0]).toBe(
+ 'Warning: RenderTextInvalidConstructor(...): No `render` method found on the returned component instance: ' +
+ 'did you accidentally return an object from the constructor?',
+ );
+ }
});
it('should return error if render is not defined', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class RenderTestUndefinedRender extends React.Component {}
expect(function() {
ReactTestUtils.renderIntoDocument();
}).toThrow();
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.mostRecent().args[0]).toBe(
- 'Warning: RenderTestUndefinedRender(...): No `render` method found on the returned ' +
- 'component instance: you may have forgotten to define `render`.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.mostRecent().args[0]).toBe(
+ 'Warning: RenderTestUndefinedRender(...): No `render` method found on the returned ' +
+ 'component instance: you may have forgotten to define `render`.',
+ );
+ }
});
});
diff --git a/packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js b/packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js
index 93b4d9280e72d..131488a500e91 100644
--- a/packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js
+++ b/packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js
@@ -381,7 +381,7 @@ describe('ReactCompositeComponent-state', () => {
});
it('should treat assigning to this.state inside cWRP as a replaceState, with a warning', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
let ops = [];
class Test extends React.Component {
@@ -416,20 +416,24 @@ describe('ReactCompositeComponent-state', () => {
'render -- step: 3, extra: false',
'callback -- step: 3, extra: false',
]);
- expect(console.error.calls.count()).toEqual(1);
- expect(console.error.calls.argsFor(0)[0]).toEqual(
- 'Warning: Test.componentWillReceiveProps(): Assigning directly to ' +
- "this.state is deprecated (except inside a component's constructor). " +
- 'Use setState instead.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toEqual(1);
+ expect(console.error.calls.argsFor(0)[0]).toEqual(
+ 'Warning: Test.componentWillReceiveProps(): Assigning directly to ' +
+ "this.state is deprecated (except inside a component's constructor). " +
+ 'Use setState instead.',
+ );
+ }
// Check deduplication
ReactDOM.render(, container);
- expect(console.error.calls.count()).toEqual(1);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toEqual(1);
+ }
});
it('should treat assigning to this.state inside cWM as a replaceState, with a warning', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
let ops = [];
class Test extends React.Component {
@@ -461,11 +465,13 @@ describe('ReactCompositeComponent-state', () => {
'render -- step: 3, extra: false',
'callback -- step: 3, extra: false',
]);
- expect(console.error.calls.count()).toEqual(1);
- expect(console.error.calls.argsFor(0)[0]).toEqual(
- 'Warning: Test.componentWillMount(): Assigning directly to ' +
- "this.state is deprecated (except inside a component's constructor). " +
- 'Use setState instead.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toEqual(1);
+ expect(console.error.calls.argsFor(0)[0]).toEqual(
+ 'Warning: Test.componentWillMount(): Assigning directly to ' +
+ "this.state is deprecated (except inside a component's constructor). " +
+ 'Use setState instead.',
+ );
+ }
});
});
diff --git a/packages/react-dom/src/__tests__/ReactDOM-test.js b/packages/react-dom/src/__tests__/ReactDOM-test.js
index 9caa898ff1c9b..dde2608bfa3bd 100644
--- a/packages/react-dom/src/__tests__/ReactDOM-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOM-test.js
@@ -109,7 +109,7 @@ describe('ReactDOM', () => {
});
it('throws in render() if the mount callback is not a function', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
function Foo() {
this.a = 1;
@@ -129,31 +129,37 @@ describe('ReactDOM', () => {
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: no',
);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'render(...): Expected the last optional `callback` argument to be ' +
- 'a function. Instead received: no.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'render(...): Expected the last optional `callback` argument to be ' +
+ 'a function. Instead received: no.',
+ );
+ }
expect(() => ReactDOM.render(, myDiv, {foo: 'bar'})).toThrowError(
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: [object Object]',
);
- expectDev(console.error.calls.argsFor(1)[0]).toContain(
- 'render(...): Expected the last optional `callback` argument to be ' +
- 'a function. Instead received: [object Object].',
- );
+ if (__DEV__) {
+ expect(console.error.calls.argsFor(1)[0]).toContain(
+ 'render(...): Expected the last optional `callback` argument to be ' +
+ 'a function. Instead received: [object Object].',
+ );
+ }
expect(() => ReactDOM.render(, myDiv, new Foo())).toThrowError(
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: [object Object]',
);
- expectDev(console.error.calls.argsFor(2)[0]).toContain(
- 'render(...): Expected the last optional `callback` argument to be ' +
- 'a function. Instead received: [object Object].',
- );
- expect(console.error.calls.count()).toBe(3);
+ if (__DEV__) {
+ expect(console.error.calls.argsFor(2)[0]).toContain(
+ 'render(...): Expected the last optional `callback` argument to be ' +
+ 'a function. Instead received: [object Object].',
+ );
+ expect(console.error.calls.count()).toBe(3);
+ }
});
it('throws in render() if the update callback is not a function', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
function Foo() {
this.a = 1;
@@ -174,29 +180,35 @@ describe('ReactDOM', () => {
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: no',
);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'render(...): Expected the last optional `callback` argument to be ' +
- 'a function. Instead received: no.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'render(...): Expected the last optional `callback` argument to be ' +
+ 'a function. Instead received: no.',
+ );
+ }
ReactDOM.render(, myDiv); // Re-mount
expect(() => ReactDOM.render(, myDiv, {foo: 'bar'})).toThrowError(
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: [object Object]',
);
- expectDev(console.error.calls.argsFor(1)[0]).toContain(
- 'render(...): Expected the last optional `callback` argument to be ' +
- 'a function. Instead received: [object Object].',
- );
+ if (__DEV__) {
+ expect(console.error.calls.argsFor(1)[0]).toContain(
+ 'render(...): Expected the last optional `callback` argument to be ' +
+ 'a function. Instead received: [object Object].',
+ );
+ }
ReactDOM.render(, myDiv); // Re-mount
expect(() => ReactDOM.render(, myDiv, new Foo())).toThrowError(
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: [object Object]',
);
- expectDev(console.error.calls.argsFor(2)[0]).toContain(
- 'render(...): Expected the last optional `callback` argument to be ' +
- 'a function. Instead received: [object Object].',
- );
- expect(console.error.calls.count()).toBe(3);
+ if (__DEV__) {
+ expect(console.error.calls.argsFor(2)[0]).toContain(
+ 'render(...): Expected the last optional `callback` argument to be ' +
+ 'a function. Instead received: [object Object].',
+ );
+ expect(console.error.calls.count()).toBe(3);
+ }
});
it('preserves focus', () => {
@@ -338,4 +350,26 @@ describe('ReactDOM', () => {
];
expect(actual).toEqual(expected);
});
+
+ it('should not crash with devtools installed', () => {
+ try {
+ global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {
+ inject: function() {},
+ onCommitFiberRoot: function() {},
+ onCommitFiberUnmount: function() {},
+ supportsFiber: true,
+ };
+ jest.resetModules();
+ React = require('react');
+ ReactDOM = require('react-dom');
+ class Component extends React.Component {
+ render() {
+ return ;
+ }
+ }
+ ReactDOM.render(, document.createElement('container'));
+ } finally {
+ delete global.__REACT_DEVTOOLS_GLOBAL_HOOK__;
+ }
+ });
});
diff --git a/packages/react-dom/src/__tests__/ReactDOMAttribute-test.js b/packages/react-dom/src/__tests__/ReactDOMAttribute-test.js
index 67589233e9652..fe2d647b7b292 100644
--- a/packages/react-dom/src/__tests__/ReactDOMAttribute-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMAttribute-test.js
@@ -46,20 +46,20 @@ describe('ReactDOM unknown attribute', () => {
});
it('changes values true, false to null, and also warns once', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
testUnknownAttributeAssignment(true, null);
testUnknownAttributeAssignment(false, null);
- expectDev(
- normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
- ).toMatch(
- 'Received `true` for a non-boolean attribute `unknown`.\n\n' +
- 'If you want to write it to the DOM, pass a string instead: ' +
- 'unknown="true" or unknown={value.toString()}.\n' +
- ' in div (at **)',
- );
- expectDev(console.error.calls.count()).toBe(1);
+ if (__DEV__) {
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toMatch(
+ 'Received `true` for a non-boolean attribute `unknown`.\n\n' +
+ 'If you want to write it to the DOM, pass a string instead: ' +
+ 'unknown="true" or unknown={value.toString()}.\n' +
+ ' in div (at **)',
+ );
+ expect(console.error.calls.count()).toBe(1);
+ }
});
it('removes unknown attributes that were rendered but are now missing', () => {
@@ -82,17 +82,17 @@ describe('ReactDOM unknown attribute', () => {
});
it('coerces NaN to strings and warns', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
testUnknownAttributeAssignment(NaN, 'NaN');
- expectDev(
- normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
- ).toMatch(
- 'Warning: Received NaN for the `unknown` attribute. ' +
- 'If this is expected, cast the value to a string.\n' +
- ' in div (at **)',
- );
- expectDev(console.error.calls.count()).toBe(1);
+ if (__DEV__) {
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toMatch(
+ 'Warning: Received NaN for the `unknown` attribute. ' +
+ 'If this is expected, cast the value to a string.\n' +
+ ' in div (at **)',
+ );
+ expect(console.error.calls.count()).toBe(1);
+ }
});
it('coerces objects to strings and warns', () => {
@@ -107,50 +107,54 @@ describe('ReactDOM unknown attribute', () => {
});
it('removes symbols and warns', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
testUnknownAttributeRemoval(Symbol('foo'));
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Invalid value for prop `unknown` on
tag. Either remove it ' +
- 'from the element, or pass a string or number value to keep it ' +
- 'in the DOM. For details, see https://fb.me/react-attribute-behavior\n' +
- ' in div (at **)',
- );
- expectDev(console.error.calls.count()).toBe(1);
+ if (__DEV__) {
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: Invalid value for prop `unknown` on
tag. Either remove it ' +
+ 'from the element, or pass a string or number value to keep it ' +
+ 'in the DOM. For details, see https://fb.me/react-attribute-behavior\n' +
+ ' in div (at **)',
+ );
+ expect(console.error.calls.count()).toBe(1);
+ }
});
it('removes functions and warns', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
testUnknownAttributeRemoval(function someFunction() {});
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Invalid value for prop `unknown` on
tag. Either remove ' +
- 'it from the element, or pass a string or number value to ' +
- 'keep it in the DOM. For details, see ' +
- 'https://fb.me/react-attribute-behavior\n' +
- ' in div (at **)',
- );
- expectDev(console.error.calls.count()).toBe(1);
+ if (__DEV__) {
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: Invalid value for prop `unknown` on
tag. Either remove ' +
+ 'it from the element, or pass a string or number value to ' +
+ 'keep it in the DOM. For details, see ' +
+ 'https://fb.me/react-attribute-behavior\n' +
+ ' in div (at **)',
+ );
+ expect(console.error.calls.count()).toBe(1);
+ }
});
it('allows camelCase unknown attributes and warns', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var el = document.createElement('div');
ReactDOM.render(, el);
expect(el.firstChild.getAttribute('helloworld')).toBe('something');
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(
- normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
- ).toMatch(
- 'React does not recognize the `helloWorld` prop on a DOM element. ' +
- 'If you intentionally want it to appear in the DOM as a custom ' +
- 'attribute, spell it as lowercase `helloworld` instead. ' +
- 'If you accidentally passed it from a parent component, remove ' +
- 'it from the DOM element.\n' +
- ' in div (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toMatch(
+ 'React does not recognize the `helloWorld` prop on a DOM element. ' +
+ 'If you intentionally want it to appear in the DOM as a custom ' +
+ 'attribute, spell it as lowercase `helloworld` instead. ' +
+ 'If you accidentally passed it from a parent component, remove ' +
+ 'it from the DOM element.\n' +
+ ' in div (at **)',
+ );
+ }
});
});
});
diff --git a/packages/react-dom/src/__tests__/ReactDOMComponent-test.js b/packages/react-dom/src/__tests__/ReactDOMComponent-test.js
index e9b1911952452..4414dd37e33ae 100644
--- a/packages/react-dom/src/__tests__/ReactDOMComponent-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMComponent-test.js
@@ -132,47 +132,55 @@ describe('ReactDOMComponent', () => {
}
ReactTestUtils.renderIntoDocument();
- expectDev(() => (style.position = 'absolute')).toThrow();
+ if (__DEV__) {
+ expect(() => (style.position = 'absolute')).toThrow();
+ }
});
it('should warn for unknown prop', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
ReactDOM.render(
{}} />, container);
- expectDev(console.error.calls.count(0)).toBe(1);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Invalid value for prop `foo` on
tag. Either remove it ' +
- 'from the element, or pass a string or number value to keep ' +
- 'it in the DOM. For details, see https://fb.me/react-attribute-behavior' +
- '\n in div (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count(0)).toBe(1);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: Invalid value for prop `foo` on
tag. Either remove it ' +
+ 'from the element, or pass a string or number value to keep ' +
+ 'it in the DOM. For details, see https://fb.me/react-attribute-behavior' +
+ '\n in div (at **)',
+ );
+ }
});
it('should group multiple unknown prop warnings together', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
ReactDOM.render(
{}} baz={() => {}} />, container);
- expectDev(console.error.calls.count(0)).toBe(1);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Invalid values for props `foo`, `baz` on
tag. Either remove ' +
- 'them from the element, or pass a string or number value to keep ' +
- 'them in the DOM. For details, see https://fb.me/react-attribute-behavior' +
- '\n in div (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count(0)).toBe(1);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: Invalid values for props `foo`, `baz` on
tag. Either remove ' +
+ 'them from the element, or pass a string or number value to keep ' +
+ 'them in the DOM. For details, see https://fb.me/react-attribute-behavior' +
+ '\n in div (at **)',
+ );
+ }
});
it('should warn for onDblClick prop', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
ReactDOM.render(
{}} />, container);
- expectDev(console.error.calls.count(0)).toBe(1);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Invalid event handler property `onDblClick`. Did you mean `onDoubleClick`?\n in div (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count(0)).toBe(1);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: Invalid event handler property `onDblClick`. Did you mean `onDoubleClick`?\n in div (at **)',
+ );
+ }
});
it('should warn for unknown string event handlers', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
ReactDOM.render(, container);
expect(container.firstChild.hasAttribute('onUnknown')).toBe(false);
@@ -183,20 +191,22 @@ describe('ReactDOMComponent', () => {
ReactDOM.render(, container);
expect(container.firstChild.hasAttribute('on-unknown')).toBe(false);
expect(container.firstChild['on-unknown']).toBe(undefined);
- expectDev(console.error.calls.count(0)).toBe(3);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Unknown event handler property `onUnknown`. It will be ignored.\n in div (at **)',
- );
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
- 'Warning: Unknown event handler property `onunknown`. It will be ignored.\n in div (at **)',
- );
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
- 'Warning: Unknown event handler property `on-unknown`. It will be ignored.\n in div (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count(0)).toBe(3);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: Unknown event handler property `onUnknown`. It will be ignored.\n in div (at **)',
+ );
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
+ 'Warning: Unknown event handler property `onunknown`. It will be ignored.\n in div (at **)',
+ );
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
+ 'Warning: Unknown event handler property `on-unknown`. It will be ignored.\n in div (at **)',
+ );
+ }
});
it('should warn for unknown function event handlers', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
ReactDOM.render(, container);
expect(container.firstChild.hasAttribute('onUnknown')).toBe(false);
@@ -207,32 +217,34 @@ describe('ReactDOMComponent', () => {
ReactDOM.render(, container);
expect(container.firstChild.hasAttribute('on-unknown')).toBe(false);
expect(container.firstChild['on-unknown']).toBe(undefined);
- expectDev(console.error.calls.count(0)).toBe(3);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Unknown event handler property `onUnknown`. It will be ignored.\n in div (at **)',
- );
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
- 'Warning: Unknown event handler property `onunknown`. It will be ignored.\n in div (at **)',
- );
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
- 'Warning: Unknown event handler property `on-unknown`. It will be ignored.\n in div (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count(0)).toBe(3);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: Unknown event handler property `onUnknown`. It will be ignored.\n in div (at **)',
+ );
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
+ 'Warning: Unknown event handler property `onunknown`. It will be ignored.\n in div (at **)',
+ );
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
+ 'Warning: Unknown event handler property `on-unknown`. It will be ignored.\n in div (at **)',
+ );
+ }
});
it('should warn for badly cased React attributes', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
ReactDOM.render(, container);
expect(container.firstChild.getAttribute('CHILDREN')).toBe('5');
- expectDev(console.error.calls.count(0)).toBe(1);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Invalid DOM property `CHILDREN`. Did you mean `children`?\n in div (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count(0)).toBe(1);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: Invalid DOM property `CHILDREN`. Did you mean `children`?\n in div (at **)',
+ );
+ }
});
it('should not warn for "0" as a unitless style value', () => {
- spyOn(console, 'error');
-
class Component extends React.Component {
render() {
return ;
@@ -240,24 +252,23 @@ describe('ReactDOMComponent', () => {
}
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(0);
});
it('should warn nicely about NaN in style', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var style = {fontSize: NaN};
var div = document.createElement('div');
ReactDOM.render(, div);
ReactDOM.render(, div);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(
- normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
- ).toEqual(
- 'Warning: `NaN` is an invalid value for the `fontSize` css style property.' +
- '\n in span (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
+ 'Warning: `NaN` is an invalid value for the `fontSize` css style property.' +
+ '\n in span (at **)',
+ );
+ }
});
it('should update styles if initially null', () => {
@@ -445,7 +456,7 @@ describe('ReactDOMComponent', () => {
});
it('should reject attribute key injection attack on markup', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
for (var i = 0; i < 3; i++) {
var container = document.createElement('div');
var element = React.createElement(
@@ -455,14 +466,16 @@ describe('ReactDOMComponent', () => {
);
ReactDOM.render(element, container);
}
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toEqual(
- 'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toEqual(
+ 'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
+ );
+ }
});
it('should reject attribute key injection attack on update', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
for (var i = 0; i < 3; i++) {
var container = document.createElement('div');
var beforeUpdate = React.createElement('x-foo-component', {}, null);
@@ -475,10 +488,12 @@ describe('ReactDOMComponent', () => {
);
ReactDOM.render(afterUpdate, container);
}
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toEqual(
- 'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toEqual(
+ 'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
+ );
+ }
});
it('should update arbitrary attributes for tags containing dashes', () => {
@@ -723,15 +738,17 @@ describe('ReactDOMComponent', () => {
});
it('should warn about non-string "is" attribute', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
ReactDOM.render(, container);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'Received a `function` for a string attribute `is`. If this is expected, cast ' +
- 'the value to a string.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'Received a `function` for a string attribute `is`. If this is expected, cast ' +
+ 'the value to a string.',
+ );
+ }
});
it('should not update when switching between null/undefined', () => {
@@ -860,14 +877,14 @@ describe('ReactDOMComponent', () => {
});
it('should work error event on element', () => {
- spyOn(console, 'error');
+ spyOn(console, 'log');
var container = document.createElement('div');
ReactDOM.render(
,
container,
@@ -877,12 +894,14 @@ describe('ReactDOMComponent', () => {
errorEvent.initEvent('error', false, false);
container.getElementsByTagName('source')[0].dispatchEvent(errorEvent);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain('onError called');
+ if (__DEV__) {
+ expect(console.log.calls.count()).toBe(1);
+ expect(console.log.calls.argsFor(0)[0]).toContain('onError called');
+ }
});
it('should not duplicate uppercased selfclosing tags', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class Container extends React.Component {
render() {
return React.createElement('BR', null);
@@ -891,39 +910,49 @@ describe('ReactDOMComponent', () => {
var returnedValue = ReactDOMServer.renderToString();
expect(returnedValue).not.toContain('');
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- ' is using uppercase HTML.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ ' is using uppercase HTML.',
+ );
+ }
});
it('should warn on upper case HTML tags, not SVG nor custom tags', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactTestUtils.renderIntoDocument(
React.createElement('svg', null, React.createElement('PATH')),
);
- expectDev(console.error.calls.count()).toBe(0);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(0);
+ }
ReactTestUtils.renderIntoDocument(React.createElement('CUSTOM-TAG'));
- expectDev(console.error.calls.count()).toBe(0);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(0);
+ }
ReactTestUtils.renderIntoDocument(React.createElement('IMG'));
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- ' is using uppercase HTML.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ ' is using uppercase HTML.',
+ );
+ }
});
it('should warn on props reserved for future use', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'The `aria` attribute is reserved for future use in React. ' +
- 'Pass individual `aria-` attributes instead.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'The `aria` attribute is reserved for future use in React. ' +
+ 'Pass individual `aria-` attributes instead.',
+ );
+ }
});
it('should warn if the tag is unrecognized', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
let realToString;
try {
@@ -957,19 +986,21 @@ describe('ReactDOMComponent', () => {
Object.prototype.toString = realToString; // eslint-disable-line no-extend-native
}
- expectDev(console.error.calls.count()).toBe(4);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'The tag is unrecognized in this browser',
- );
- expectDev(console.error.calls.argsFor(1)[0]).toContain(
- 'The tag is unrecognized in this browser',
- );
- expectDev(console.error.calls.argsFor(2)[0]).toContain(
- ' is using uppercase HTML',
- );
- expectDev(console.error.calls.argsFor(3)[0]).toContain(
- 'The tag is unrecognized in this browser',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(4);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'The tag is unrecognized in this browser',
+ );
+ expect(console.error.calls.argsFor(1)[0]).toContain(
+ 'The tag is unrecognized in this browser',
+ );
+ expect(console.error.calls.argsFor(2)[0]).toContain(
+ ' is using uppercase HTML',
+ );
+ expect(console.error.calls.argsFor(3)[0]).toContain(
+ 'The tag is unrecognized in this browser',
+ );
+ }
});
it('should throw on children for void elements', () => {
@@ -983,8 +1014,8 @@ describe('ReactDOMComponent', () => {
expect(caughtErr).not.toBe(undefined);
expect(normalizeCodeLocInfo(caughtErr.message)).toContain(
'input is a void element tag and must neither have `children` nor ' +
- 'use `dangerouslySetInnerHTML`.',
- '\n in input (at **)',
+ 'use `dangerouslySetInnerHTML`.' +
+ (__DEV__ ? '\n in input (at **)' : ''),
);
});
@@ -1002,13 +1033,13 @@ describe('ReactDOMComponent', () => {
expect(caughtErr).not.toBe(undefined);
expect(normalizeCodeLocInfo(caughtErr.message)).toContain(
'input is a void element tag and must neither have `children` nor ' +
- 'use `dangerouslySetInnerHTML`.',
- '\n in input (at **)',
+ 'use `dangerouslySetInnerHTML`.' +
+ (__DEV__ ? '\n in input (at **)' : ''),
);
});
it('should emit a warning once for a named custom component using shady DOM', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var defaultCreateElement = document.createElement.bind(document);
@@ -1025,20 +1056,24 @@ describe('ReactDOMComponent', () => {
}
var node = document.createElement('div');
ReactDOM.render(, node);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'ShadyComponent is using shady DOM. Using shady DOM with React can ' +
- 'cause things to break subtly.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'ShadyComponent is using shady DOM. Using shady DOM with React can ' +
+ 'cause things to break subtly.',
+ );
+ }
mountComponent({is: 'custom-shady-div2'});
- expectDev(console.error.calls.count()).toBe(1);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ }
} finally {
document.createElement = defaultCreateElement;
}
});
it('should emit a warning once for an unnamed custom component using shady DOM', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var defaultCreateElement = document.createElement.bind(document);
@@ -1050,14 +1085,18 @@ describe('ReactDOMComponent', () => {
};
mountComponent({is: 'custom-shady-div'});
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'A component is using shady DOM. Using shady DOM with React can ' +
- 'cause things to break subtly.',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'A component is using shady DOM. Using shady DOM with React can ' +
+ 'cause things to break subtly.',
+ );
+ }
mountComponent({is: 'custom-shady-div2'});
- expectDev(console.error.calls.count()).toBe(1);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ }
} finally {
document.createElement = defaultCreateElement;
}
@@ -1065,7 +1104,7 @@ describe('ReactDOMComponent', () => {
it('should treat menuitem as a void element but still create the closing tag', () => {
// menuitem is not implemented in jsdom, so this triggers the unknown warning error
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
var returnedValue = ReactDOMServer.renderToString(
@@ -1098,21 +1137,25 @@ describe('ReactDOMComponent', () => {
});
it('should validate against use of innerHTML', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
mountComponent({innerHTML: 'Hi Jim!'});
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'Directly setting property `innerHTML` is not permitted. ',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'Directly setting property `innerHTML` is not permitted. ',
+ );
+ }
});
it('should validate against use of innerHTML without case sensitivity', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
mountComponent({innerhtml: 'Hi Jim!'});
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'Directly setting property `innerHTML` is not permitted. ',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'Directly setting property `innerHTML` is not permitted. ',
+ );
+ }
});
it('should validate use of dangerouslySetInnerHTML', () => {
@@ -1140,25 +1183,25 @@ describe('ReactDOMComponent', () => {
});
it('should warn about contentEditable and children', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
mountComponent({contentEditable: true, children: ''});
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: A component is `contentEditable` and contains `children` ' +
- 'managed by React. It is now your responsibility to guarantee that ' +
- 'none of those nodes are unexpectedly modified or duplicated. This ' +
- 'is probably not intentional.\n in div (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: A component is `contentEditable` and contains `children` ' +
+ 'managed by React. It is now your responsibility to guarantee that ' +
+ 'none of those nodes are unexpectedly modified or duplicated. This ' +
+ 'is probably not intentional.\n in div (at **)',
+ );
+ }
});
it('should respect suppressContentEditableWarning', () => {
- spyOn(console, 'error');
mountComponent({
contentEditable: true,
children: '',
suppressContentEditableWarning: true,
});
- expectDev(console.error.calls.count()).toBe(0);
});
it('should validate against invalid styles', () => {
@@ -1190,8 +1233,7 @@ describe('ReactDOMComponent', () => {
expect(normalizeCodeLocInfo(caughtErr.message)).toContain(
'input is a void element tag and must neither have `children` ' +
'nor use `dangerouslySetInnerHTML`.' +
- '\n in input (at **)' +
- '\n in X (at **)',
+ (__DEV__ ? '\n in input (at **)' + '\n in X (at **)' : ''),
);
});
@@ -1227,9 +1269,11 @@ describe('ReactDOMComponent', () => {
container.getElementsByTagName('image')[0].dispatchEvent(errorEvent);
container.getElementsByTagName('image')[0].dispatchEvent(loadEvent);
- expectDev(console.log.calls.count()).toBe(2);
- expectDev(console.log.calls.argsFor(0)[0]).toContain('onError called');
- expectDev(console.log.calls.argsFor(1)[0]).toContain('onLoad called');
+ if (__DEV__) {
+ expect(console.log.calls.count()).toBe(2);
+ expect(console.log.calls.argsFor(0)[0]).toContain('onError called');
+ expect(console.log.calls.argsFor(1)[0]).toContain('onLoad called');
+ }
});
});
@@ -1279,15 +1323,17 @@ describe('ReactDOMComponent', () => {
});
it('should warn about contentEditable and children', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactDOM.render(
,
container,
);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain('contentEditable');
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain('contentEditable');
+ }
});
it('should validate against invalid styles', () => {
@@ -1321,8 +1367,7 @@ describe('ReactDOMComponent', () => {
'The `style` prop expects a mapping from style properties to values, ' +
"not a string. For example, style={{marginRight: spacing + 'em'}} " +
'when using JSX.' +
- '\n in div (at **)' +
- '\n in Animal (at **)',
+ (__DEV__ ? '\n in div (at **)' + '\n in Animal (at **)' : ''),
);
});
@@ -1400,7 +1445,7 @@ describe('ReactDOMComponent', () => {
describe('nesting validation', () => {
it('warns on invalid nesting', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactTestUtils.renderIntoDocument(
.' +
- '\n in tr (at **)' +
- '\n in div (at **)',
- );
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
- 'Warning: validateDOMNesting(...):
cannot appear as a child of ' +
- '
.' +
- '\n in tr (at **)' +
- '\n in div (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(2);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: validateDOMNesting(...):
cannot appear as a child of ' +
+ '
.' +
+ '\n in tr (at **)' +
+ '\n in div (at **)',
+ );
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
+ 'Warning: validateDOMNesting(...):
cannot appear as a child of ' +
+ '
.' +
+ '\n in tr (at **)' +
+ '\n in div (at **)',
+ );
+ }
});
it('warns on invalid nesting at root', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var p = document.createElement('p');
ReactDOM.render(
@@ -1433,18 +1480,20 @@ describe('ReactDOMComponent', () => {
p,
);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: validateDOMNesting(...):
cannot appear as a descendant ' +
- 'of
.' +
- // There is no outer `p` here because root container is not part of the stack.
- '\n in p (at **)' +
- '\n in span (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: validateDOMNesting(...):
cannot appear as a descendant ' +
+ 'of
.' +
+ // There is no outer `p` here because root container is not part of the stack.
+ '\n in p (at **)' +
+ '\n in span (at **)',
+ );
+ }
});
it('warns nicely for table rows', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
class Row extends React.Component {
render() {
@@ -1463,38 +1512,40 @@ describe('ReactDOMComponent', () => {
}
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(3);
-
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: validateDOMNesting(...):
cannot appear as a child of ' +
- '
. Add a to your code to match the DOM tree generated ' +
- 'by the browser.' +
- '\n in tr (at **)' +
- '\n in Row (at **)' +
- '\n in table (at **)' +
- '\n in Foo (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(3);
+
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: validateDOMNesting(...):
cannot appear as a child of ' +
+ '
. Add a to your code to match the DOM tree generated ' +
+ 'by the browser.' +
+ '\n in tr (at **)' +
+ '\n in Row (at **)' +
+ '\n in table (at **)' +
+ '\n in Foo (at **)',
+ );
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
- 'Warning: validateDOMNesting(...): Text nodes cannot appear as a ' +
- 'child of
.' +
- '\n in tr (at **)' +
- '\n in Row (at **)' +
- '\n in table (at **)' +
- '\n in Foo (at **)',
- );
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
+ 'Warning: validateDOMNesting(...): Text nodes cannot appear as a ' +
+ 'child of
.' +
+ '\n in tr (at **)' +
+ '\n in Row (at **)' +
+ '\n in table (at **)' +
+ '\n in Foo (at **)',
+ );
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
- 'Warning: validateDOMNesting(...): Whitespace text nodes cannot ' +
- "appear as a child of
. Make sure you don't have any extra " +
- 'whitespace between tags on each line of your source code.' +
- '\n in table (at **)' +
- '\n in Foo (at **)',
- );
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
+ 'Warning: validateDOMNesting(...): Whitespace text nodes cannot ' +
+ "appear as a child of
. Make sure you don't have any extra " +
+ 'whitespace between tags on each line of your source code.' +
+ '\n in table (at **)' +
+ '\n in Foo (at **)',
+ );
+ }
});
it('gives useful context in warnings', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
function Row() {
return
;
}
@@ -1525,16 +1576,18 @@ describe('ReactDOMComponent', () => {
return ;
}
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(
- normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
- ).toContain(
- '\n in tr (at **)' +
- '\n in Row (at **)' +
- '\n in FancyRow (at **)' +
- '\n in table (at **)' +
- '\n in Viz1 (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(
+ normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
+ ).toContain(
+ '\n in tr (at **)' +
+ '\n in Row (at **)' +
+ '\n in FancyRow (at **)' +
+ '\n in table (at **)' +
+ '\n in Viz1 (at **)',
+ );
+ }
function Viz2() {
return (
@@ -1547,65 +1600,73 @@ describe('ReactDOMComponent', () => {
return ;
}
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(2);
- expectDev(
- normalizeCodeLocInfo(console.error.calls.argsFor(1)[0]),
- ).toContain(
- '\n in tr (at **)' +
- '\n in Row (at **)' +
- '\n in FancyRow (at **)' +
- '\n in table (at **)' +
- '\n in Table (at **)' +
- '\n in FancyTable (at **)' +
- '\n in Viz2 (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(2);
+ expect(
+ normalizeCodeLocInfo(console.error.calls.argsFor(1)[0]),
+ ).toContain(
+ '\n in tr (at **)' +
+ '\n in Row (at **)' +
+ '\n in FancyRow (at **)' +
+ '\n in table (at **)' +
+ '\n in Table (at **)' +
+ '\n in FancyTable (at **)' +
+ '\n in Viz2 (at **)',
+ );
+ }
ReactTestUtils.renderIntoDocument(
,
);
- expectDev(console.error.calls.count()).toBe(3);
- expectDev(
- normalizeCodeLocInfo(console.error.calls.argsFor(2)[0]),
- ).toContain(
- '\n in tr (at **)' +
- '\n in Row (at **)' +
- '\n in FancyRow (at **)' +
- '\n in table (at **)' +
- '\n in Table (at **)' +
- '\n in FancyTable (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(3);
+ expect(
+ normalizeCodeLocInfo(console.error.calls.argsFor(2)[0]),
+ ).toContain(
+ '\n in tr (at **)' +
+ '\n in Row (at **)' +
+ '\n in FancyRow (at **)' +
+ '\n in table (at **)' +
+ '\n in Table (at **)' +
+ '\n in FancyTable (at **)',
+ );
+ }
ReactTestUtils.renderIntoDocument(
,
);
- expectDev(console.error.calls.count()).toBe(4);
- expectDev(
- normalizeCodeLocInfo(console.error.calls.argsFor(3)[0]),
- ).toContain(
- '\n in tr (at **)' +
- '\n in Row (at **)' +
- '\n in FancyRow (at **)' +
- '\n in table (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(4);
+ expect(
+ normalizeCodeLocInfo(console.error.calls.argsFor(3)[0]),
+ ).toContain(
+ '\n in tr (at **)' +
+ '\n in Row (at **)' +
+ '\n in FancyRow (at **)' +
+ '\n in table (at **)',
+ );
+ }
ReactTestUtils.renderIntoDocument(
,
);
- expectDev(console.error.calls.count()).toBe(5);
- expectDev(
- normalizeCodeLocInfo(console.error.calls.argsFor(4)[0]),
- ).toContain(
- '\n in tr (at **)' +
- '\n in table (at **)' +
- '\n in Table (at **)' +
- '\n in FancyTable (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(5);
+ expect(
+ normalizeCodeLocInfo(console.error.calls.argsFor(4)[0]),
+ ).toContain(
+ '\n in tr (at **)' +
+ '\n in table (at **)' +
+ '\n in Table (at **)' +
+ '\n in FancyTable (at **)',
+ );
+ }
class Link extends React.Component {
render() {
@@ -1620,172 +1681,233 @@ describe('ReactDOMComponent', () => {
,
);
- expectDev(console.error.calls.count()).toBe(6);
- expectDev(
- normalizeCodeLocInfo(console.error.calls.argsFor(5)[0]),
- ).toContain(
- '\n in a (at **)' +
- '\n in Link (at **)' +
- '\n in div (at **)' +
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(6);
+ expect(
+ normalizeCodeLocInfo(console.error.calls.argsFor(5)[0]),
+ ).toContain(
'\n in a (at **)' +
- '\n in Link (at **)',
- );
+ '\n in Link (at **)' +
+ '\n in div (at **)' +
+ '\n in a (at **)' +
+ '\n in Link (at **)',
+ );
+ }
});
it('should warn about incorrect casing on properties (ssr)', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactDOMServer.renderToString(
React.createElement('input', {type: 'text', tabindex: '1'}),
);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain('tabIndex');
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain('tabIndex');
+ }
});
it('should warn about incorrect casing on event handlers (ssr)', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactDOMServer.renderToString(
- React.createElement('input', {type: 'text', onclick: '1'}),
+ React.createElement('input', {type: 'text', oninput: '1'}),
);
ReactDOMServer.renderToString(
React.createElement('input', {type: 'text', onKeydown: '1'}),
);
- expectDev(console.error.calls.count()).toBe(2);
- expectDev(console.error.calls.argsFor(0)[0]).toContain('onClick');
- expectDev(console.error.calls.argsFor(1)[0]).toContain('onKeyDown');
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'Invalid event handler property `oninput`. ' +
+ 'React events use the camelCase naming convention, ' +
+ // Note: we don't know the right event name so we
+ // use a generic one (onClick) as a suggestion.
+ // This is because we don't bundle the event system
+ // on the server.
+ 'for example `onClick`.',
+ );
+ // We can't warn for `onKeydown` on the server because
+ // there is no way tell if this is a valid event or not
+ // without access to the event system (which we don't bundle).
+ }
});
it('should warn about incorrect casing on properties', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactTestUtils.renderIntoDocument(
React.createElement('input', {type: 'text', tabindex: '1'}),
);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain('tabIndex');
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain('tabIndex');
+ }
});
it('should warn about incorrect casing on event handlers', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactTestUtils.renderIntoDocument(
- React.createElement('input', {type: 'text', onclick: '1'}),
+ React.createElement('input', {type: 'text', oninput: '1'}),
);
ReactTestUtils.renderIntoDocument(
React.createElement('input', {type: 'text', onKeydown: '1'}),
);
- expectDev(console.error.calls.count()).toBe(2);
- expectDev(console.error.calls.argsFor(0)[0]).toContain('onClick');
- expectDev(console.error.calls.argsFor(1)[0]).toContain('onKeyDown');
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(2);
+ expect(console.error.calls.argsFor(0)[0]).toContain('onInput');
+ expect(console.error.calls.argsFor(1)[0]).toContain('onKeyDown');
+ }
});
it('should warn about class', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactTestUtils.renderIntoDocument(
React.createElement('div', {class: 'muffins'}),
);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain('className');
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain('className');
+ }
});
it('should warn about class (ssr)', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactDOMServer.renderToString(
React.createElement('div', {class: 'muffins'}),
);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(console.error.calls.argsFor(0)[0]).toContain('className');
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(console.error.calls.argsFor(0)[0]).toContain('className');
+ }
});
it('should warn about props that are no longer supported', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(0);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(0);
+ }
ReactTestUtils.renderIntoDocument(
{}} />);
- expectDev(console.error.calls.count()).toBe(2);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(2);
+ }
});
it('gives source code refs for unknown prop warning', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactTestUtils.renderIntoDocument();
ReactTestUtils.renderIntoDocument();
- expectDev(console.error.calls.count()).toBe(2);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
- );
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
- 'Warning: Invalid event handler property `onclick`. Did you mean ' +
- '`onClick`?\n in input (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(2);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
+ );
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
+ 'Warning: Invalid event handler property `onclick`. Did you mean ' +
+ '`onClick`?\n in input (at **)',
+ );
+ }
});
it('gives source code refs for unknown prop warning (ssr)', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactDOMServer.renderToString();
- ReactDOMServer.renderToString();
- expectDev(console.error.calls.count()).toBe(2);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
- );
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
- 'Warning: Invalid event handler property `onclick`. Did you mean ' +
- '`onClick`?\n in input (at **)',
- );
+ ReactDOMServer.renderToString();
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(2);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
+ );
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
+ 'Warning: Invalid event handler property `oninput`. ' +
+ // Note: we don't know the right event name so we
+ // use a generic one (onClick) as a suggestion.
+ // This is because we don't bundle the event system
+ // on the server.
+ 'React events use the camelCase naming convention, for example `onClick`.' +
+ '\n in input (at **)',
+ );
+ }
});
it('gives source code refs for unknown prop warning for update render', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
ReactTestUtils.renderIntoDocument(, container);
- expectDev(console.error.calls.count()).toBe(0);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(0);
+ }
ReactTestUtils.renderIntoDocument(, container);
- expectDev(console.error.calls.count()).toBe(1);
- expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
- 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
- );
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(1);
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
+ 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
+ );
+ }
});
it('gives source code refs for unknown prop warning for exact elements', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactTestUtils.renderIntoDocument(
,
);
- expectDev(console.error.calls.count()).toBe(2);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(2);
- expectDev(console.error.calls.argsFor(0)[0]).toContain('className');
- var matches = console.error.calls.argsFor(0)[0].match(/.*\(.*:(\d+)\).*/);
- var previousLine = matches[1];
+ expect(console.error.calls.argsFor(0)[0]).toContain('className');
+ var matches = console.error.calls
+ .argsFor(0)[0]
+ .match(/.*\(.*:(\d+)\).*/);
+ var previousLine = matches[1];
- expectDev(console.error.calls.argsFor(1)[0]).toContain('onClick');
- matches = console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/);
- var currentLine = matches[1];
+ expect(console.error.calls.argsFor(1)[0]).toContain('onClick');
+ matches = console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/);
+ var currentLine = matches[1];
- //verify line number has a proper relative difference,
- //since hard coding the line number would make test too brittle
- expect(parseInt(previousLine, 10) + 2).toBe(parseInt(currentLine, 10));
+ //verify line number has a proper relative difference,
+ //since hard coding the line number would make test too brittle
+ expect(parseInt(previousLine, 10) + 2).toBe(parseInt(currentLine, 10));
+ }
});
it('gives source code refs for unknown prop warning for exact elements (ssr)', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactDOMServer.renderToString(
,
);
- expectDev(console.error.calls.count()).toBe(2);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(2);
- expectDev(console.error.calls.argsFor(0)[0]).toContain('className');
- var matches = console.error.calls.argsFor(0)[0].match(/.*\(.*:(\d+)\).*/);
- var previousLine = (matches || [])[1];
+ expect(console.error.calls.argsFor(0)[0]).toContain('className');
+ var matches = console.error.calls
+ .argsFor(0)[0]
+ .match(/.*\(.*:(\d+)\).*/);
+ var previousLine = (matches || [])[1];
- expectDev(console.error.calls.argsFor(1)[0]).toContain('onClick');
- matches =
- console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/) || {};
- var currentLine = (matches || [])[1];
+ expect(console.error.calls.argsFor(1)[0]).toContain('onClick');
+ matches =
+ console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/) || {};
+ var currentLine = (matches || [])[1];
- //verify line number has a proper relative difference,
- //since hard coding the line number would make test too brittle
- expectDev(parseInt(previousLine, 10) + 2).toBe(parseInt(currentLine, 10));
+ //verify line number has a proper relative difference,
+ //since hard coding the line number would make test too brittle
+ expect(parseInt(previousLine, 10) + 2).toBe(parseInt(currentLine, 10));
+ }
});
it('gives source code refs for unknown prop warning for exact elements in composition', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
class Parent extends React.Component {
@@ -1884,23 +2014,27 @@ describe('ReactDOMComponent', () => {
ReactTestUtils.renderIntoDocument(, container);
- expectDev(console.error.calls.count()).toBe(2);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(2);
- expectDev(console.error.calls.argsFor(0)[0]).toContain('className');
- var matches = console.error.calls.argsFor(0)[0].match(/.*\(.*:(\d+)\).*/);
- var previousLine = (matches || [])[1];
+ expect(console.error.calls.argsFor(0)[0]).toContain('className');
+ var matches = console.error.calls
+ .argsFor(0)[0]
+ .match(/.*\(.*:(\d+)\).*/);
+ var previousLine = (matches || [])[1];
- expectDev(console.error.calls.argsFor(1)[0]).toContain('onClick');
- matches = console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/);
- var currentLine = (matches || [])[1];
+ expect(console.error.calls.argsFor(1)[0]).toContain('onClick');
+ matches = console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/);
+ var currentLine = (matches || [])[1];
- //verify line number has a proper relative difference,
- //since hard coding the line number would make test too brittle
- expect(parseInt(previousLine, 10) + 12).toBe(parseInt(currentLine, 10));
+ //verify line number has a proper relative difference,
+ //since hard coding the line number would make test too brittle
+ expect(parseInt(previousLine, 10) + 12).toBe(parseInt(currentLine, 10));
+ }
});
it('gives source code refs for unknown prop warning for exact elements in composition (ssr)', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var container = document.createElement('div');
class Parent extends React.Component {
@@ -1942,25 +2076,27 @@ describe('ReactDOMComponent', () => {
ReactDOMServer.renderToString(, container);
- expectDev(console.error.calls.count()).toBe(2);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(2);
- expectDev(console.error.calls.argsFor(0)[0]).toContain('className');
- var matches = console.error.calls.argsFor(0)[0].match(/.*\(.*:(\d+)\).*/);
- var previousLine = (matches || [])[1];
+ expect(console.error.calls.argsFor(0)[0]).toContain('className');
+ var matches = console.error.calls
+ .argsFor(0)[0]
+ .match(/.*\(.*:(\d+)\).*/);
+ var previousLine = (matches || [])[1];
- expectDev(console.error.calls.argsFor(1)[0]).toContain('onClick');
- matches = console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/);
- var currentLine = (matches || [])[1];
+ expect(console.error.calls.argsFor(1)[0]).toContain('onClick');
+ matches = console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/);
+ var currentLine = (matches || [])[1];
- //verify line number has a proper relative difference,
- //since hard coding the line number would make test too brittle
- expectDev(parseInt(previousLine, 10) + 12).toBe(
- parseInt(currentLine, 10),
- );
+ //verify line number has a proper relative difference,
+ //since hard coding the line number would make test too brittle
+ expect(parseInt(previousLine, 10) + 12).toBe(parseInt(currentLine, 10));
+ }
});
it('should suggest property name if available', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactTestUtils.renderIntoDocument(
React.createElement('label', {for: 'test'}),
@@ -1969,19 +2105,21 @@ describe('ReactDOMComponent', () => {
React.createElement('input', {type: 'text', autofocus: true}),
);
- expectDev(console.error.calls.count()).toBe(2);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(2);
- expectDev(console.error.calls.argsFor(0)[0]).toBe(
- 'Warning: Invalid DOM property `for`. Did you mean `htmlFor`?\n in label',
- );
+ expect(console.error.calls.argsFor(0)[0]).toBe(
+ 'Warning: Invalid DOM property `for`. Did you mean `htmlFor`?\n in label',
+ );
- expectDev(console.error.calls.argsFor(1)[0]).toBe(
- 'Warning: Invalid DOM property `autofocus`. Did you mean `autoFocus`?\n in input',
- );
+ expect(console.error.calls.argsFor(1)[0]).toBe(
+ 'Warning: Invalid DOM property `autofocus`. Did you mean `autoFocus`?\n in input',
+ );
+ }
});
it('should suggest property name if available (ssr)', () => {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
ReactDOMServer.renderToString(
React.createElement('label', {for: 'test'}),
@@ -1990,15 +2128,17 @@ describe('ReactDOMComponent', () => {
React.createElement('input', {type: 'text', autofocus: true}),
);
- expectDev(console.error.calls.count()).toBe(2);
+ if (__DEV__) {
+ expect(console.error.calls.count()).toBe(2);
- expectDev(console.error.calls.argsFor(0)[0]).toBe(
- 'Warning: Invalid DOM property `for`. Did you mean `htmlFor`?\n in label',
- );
+ expect(console.error.calls.argsFor(0)[0]).toBe(
+ 'Warning: Invalid DOM property `for`. Did you mean `htmlFor`?\n in label',
+ );
- expectDev(console.error.calls.argsFor(1)[0]).toBe(
- 'Warning: Invalid DOM property `autofocus`. Did you mean `autoFocus`?\n in input',
- );
+ expect(console.error.calls.argsFor(1)[0]).toBe(
+ 'Warning: Invalid DOM property `autofocus`. Did you mean `autoFocus`?\n in input',
+ );
+ }
});
});
@@ -2028,31 +2168,35 @@ describe('ReactDOMComponent', () => {
describe('Attributes with aliases', function() {
it('sets aliased attributes on HTML attributes', function() {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var el = ReactTestUtils.renderIntoDocument();
expect(el.className).toBe('test');
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'Warning: Invalid DOM property `class`. Did you mean `className`?',
- );
+ if (__DEV__) {
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'Warning: Invalid DOM property `class`. Did you mean `className`?',
+ );
+ }
});
it('sets incorrectly cased aliased attributes on HTML attributes with a warning', function() {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var el = ReactTestUtils.renderIntoDocument();
expect(el.className).toBe('test');
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
- 'Warning: Invalid DOM property `cLASS`. Did you mean `className`?',
- );
+ if (__DEV__) {
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'Warning: Invalid DOM property `cLASS`. Did you mean `className`?',
+ );
+ }
});
it('sets aliased attributes on SVG elements with a warning', function() {
- spyOn(console, 'error');
+ spyOnDev(console, 'error');
var el = ReactTestUtils.renderIntoDocument(