diff --git a/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js b/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js index 23bdcfef9..bec880000 100644 --- a/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js +++ b/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js @@ -210,6 +210,22 @@ function nodeToHostNode(_node) { const eventOptions = { animation: true }; +function getEmptyStateValue() { + // this handles a bug in React 16.0 - 16.2 + // see https://github.com/facebook/react/commit/39be83565c65f9c522150e52375167568a2a1459 + // also see https://github.com/facebook/react/pull/11965 + + // eslint-disable-next-line react/prefer-stateless-function + class EmptyState extends React.Component { + render() { + return null; + } + } + const testRenderer = new ShallowRenderer(); + testRenderer.render(React.createElement(EmptyState)); + return testRenderer._instance.state; +} + class ReactSixteenOneAdapter extends EnzymeAdapter { constructor() { super(); @@ -319,6 +335,30 @@ class ReactSixteenOneAdapter extends EnzymeAdapter { ); return withSetStateAllowed(() => renderer.render({ ...el, type: wrappedEl }, context)); } + if (isStateful) { + // fix react bug; see implementation of `getEmptyStateValue` + const emptyStateValue = getEmptyStateValue(); + if (emptyStateValue) { + Object.defineProperty(Component.prototype, 'state', { + configurable: true, + enumerable: true, + get() { + return null; + }, + set(value) { + if (value !== emptyStateValue) { + Object.defineProperty(this, 'state', { + configurable: true, + enumerable: true, + value, + writable: true, + }); + } + return true; + }, + }); + } + } return withSetStateAllowed(() => renderer.render(el, context)); } }, diff --git a/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js b/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js index 595bff921..8eedf3b41 100644 --- a/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js +++ b/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js @@ -211,6 +211,22 @@ function nodeToHostNode(_node) { const eventOptions = { animation: true }; +function getEmptyStateValue() { + // this handles a bug in React 16.0 - 16.2 + // see https://github.com/facebook/react/commit/39be83565c65f9c522150e52375167568a2a1459 + // also see https://github.com/facebook/react/pull/11965 + + // eslint-disable-next-line react/prefer-stateless-function + class EmptyState extends React.Component { + render() { + return null; + } + } + const testRenderer = new ShallowRenderer(); + testRenderer.render(React.createElement(EmptyState)); + return testRenderer._instance.state; +} + class ReactSixteenTwoAdapter extends EnzymeAdapter { constructor() { super(); @@ -321,6 +337,30 @@ class ReactSixteenTwoAdapter extends EnzymeAdapter { ); return withSetStateAllowed(() => renderer.render({ ...el, type: wrappedEl }, context)); } + if (isStateful) { + // fix react bug; see implementation of `getEmptyStateValue` + const emptyStateValue = getEmptyStateValue(); + if (emptyStateValue) { + Object.defineProperty(Component.prototype, 'state', { + configurable: true, + enumerable: true, + get() { + return null; + }, + set(value) { + if (value !== emptyStateValue) { + Object.defineProperty(this, 'state', { + configurable: true, + enumerable: true, + value, + writable: true, + }); + } + return true; + }, + }); + } + } return withSetStateAllowed(() => renderer.render(el, context)); } }, diff --git a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js index 0ac8861c0..ac2941fab 100644 --- a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js +++ b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js @@ -226,6 +226,22 @@ const eventOptions = { auxClick: !!TestUtils.Simulate.auxClick, // 16.5+ }; +function getEmptyStateValue() { + // this handles a bug in React 16.0 - 16.2 + // see https://github.com/facebook/react/commit/39be83565c65f9c522150e52375167568a2a1459 + // also see https://github.com/facebook/react/pull/11965 + + // eslint-disable-next-line react/prefer-stateless-function + class EmptyState extends React.Component { + render() { + return null; + } + } + const testRenderer = new ShallowRenderer(); + testRenderer.render(React.createElement(EmptyState)); + return testRenderer._instance.state; +} + class ReactSixteenAdapter extends EnzymeAdapter { constructor() { super(); @@ -341,6 +357,30 @@ class ReactSixteenAdapter extends EnzymeAdapter { ); return withSetStateAllowed(() => renderer.render({ ...el, type: wrappedEl }, context)); } + if (isStateful) { + // fix react bug; see implementation of `getEmptyStateValue` + const emptyStateValue = getEmptyStateValue(); + if (emptyStateValue) { + Object.defineProperty(Component.prototype, 'state', { + configurable: true, + enumerable: true, + get() { + return null; + }, + set(value) { + if (value !== emptyStateValue) { + Object.defineProperty(this, 'state', { + configurable: true, + enumerable: true, + value, + writable: true, + }); + } + return true; + }, + }); + } + } return withSetStateAllowed(() => renderer.render(el, context)); } }, diff --git a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx index 6e342676d..a2f6e17ea 100644 --- a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx @@ -139,6 +139,30 @@ describeWithDOM('mount', () => { expect(() => mount()).not.to.throw(); }); + it('starts out with undefined state', () => { + class Foo extends React.Component { + render() { + return ( +
+ {typeof this.state} + {JSON.stringify(this.state)} +
+ ); + } + } + + const wrapper = mount(); + expect(wrapper.state()).to.equal(null); + expect(wrapper.debug()).to.equal(` + +
+ object + null +
+
+ `.trim()); + }); + describeIf(is('>= 16.3'), 'uses the isValidElementType from the Adapter to validate the prop type of Component', () => { const Foo = () => null; const Bar = () => null; diff --git a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx index 8546d50f9..f1f7cdeca 100644 --- a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx @@ -132,6 +132,28 @@ describe('shallow', () => { expect(() => shallow()).not.to.throw(); }); + + it('starts out with undefined state', () => { + class Foo extends React.Component { + render() { + return ( +
+ {typeof this.state} + {JSON.stringify(this.state)} +
+ ); + } + } + + const wrapper = shallow(); + expect(wrapper.state()).to.equal(null); + expect(wrapper.debug()).to.equal(` +
+ object + null +
+ `.trim()); + }); }); describe('context', () => {