From 47350d06bee4d2ee55901e400b97a655738cc4d9 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 9 Mar 2019 22:17:06 -0800 Subject: [PATCH 1/3] [Tests] `eslint`: disable `max-len`; fix react version settings --- packages/enzyme-adapter-react-13/.eslintrc | 1 + packages/enzyme-adapter-react-14/.eslintrc | 1 + packages/enzyme-adapter-react-15.4/.eslintrc | 1 + packages/enzyme-adapter-react-15/.eslintrc | 1 + packages/enzyme-adapter-react-16.1/.eslintrc | 1 + packages/enzyme-adapter-react-16.2/.eslintrc | 1 + packages/enzyme-adapter-react-16.3/.eslintrc | 1 + packages/enzyme-adapter-react-16/.eslintrc | 3 ++- packages/enzyme-adapter-react-helper/.eslintrc | 5 +++++ packages/enzyme-adapter-utils/.eslintrc | 8 ++++++++ packages/enzyme-test-suite/.eslintrc | 3 +++ 11 files changed, 25 insertions(+), 1 deletion(-) diff --git a/packages/enzyme-adapter-react-13/.eslintrc b/packages/enzyme-adapter-react-13/.eslintrc index eec6f28d2..804914eb1 100644 --- a/packages/enzyme-adapter-react-13/.eslintrc +++ b/packages/enzyme-adapter-react-13/.eslintrc @@ -2,6 +2,7 @@ "extends": "airbnb", "root": true, "rules": { + "max-len": 0, "react/no-find-dom-node": 0, "react/no-multi-comp": 0, "no-underscore-dangle": 0, diff --git a/packages/enzyme-adapter-react-14/.eslintrc b/packages/enzyme-adapter-react-14/.eslintrc index 840e6862e..7a040c2a4 100644 --- a/packages/enzyme-adapter-react-14/.eslintrc +++ b/packages/enzyme-adapter-react-14/.eslintrc @@ -2,6 +2,7 @@ "extends": "airbnb", "root": true, "rules": { + "max-len": 0, "react/no-find-dom-node": 0, "react/no-multi-comp": 0, "no-underscore-dangle": 0, diff --git a/packages/enzyme-adapter-react-15.4/.eslintrc b/packages/enzyme-adapter-react-15.4/.eslintrc index 3072a37cc..942f6c249 100644 --- a/packages/enzyme-adapter-react-15.4/.eslintrc +++ b/packages/enzyme-adapter-react-15.4/.eslintrc @@ -2,6 +2,7 @@ "extends": "airbnb", "root": true, "rules": { + "max-len": 0, "react/no-find-dom-node": 0, "react/no-multi-comp": 0, "no-underscore-dangle": 0, diff --git a/packages/enzyme-adapter-react-15/.eslintrc b/packages/enzyme-adapter-react-15/.eslintrc index 00a6704e8..abe19e9ba 100644 --- a/packages/enzyme-adapter-react-15/.eslintrc +++ b/packages/enzyme-adapter-react-15/.eslintrc @@ -2,6 +2,7 @@ "extends": "airbnb", "root": true, "rules": { + "max-len": 0, "react/no-find-dom-node": 0, "react/no-multi-comp": 0, "no-underscore-dangle": 0, diff --git a/packages/enzyme-adapter-react-16.1/.eslintrc b/packages/enzyme-adapter-react-16.1/.eslintrc index a06b20973..48b43f2bd 100644 --- a/packages/enzyme-adapter-react-16.1/.eslintrc +++ b/packages/enzyme-adapter-react-16.1/.eslintrc @@ -2,6 +2,7 @@ "extends": "airbnb", "root": true, "rules": { + "max-len": 0, "import/no-extraneous-dependencies": 0, "import/no-unresolved": 0, "import/extensions": 0, diff --git a/packages/enzyme-adapter-react-16.2/.eslintrc b/packages/enzyme-adapter-react-16.2/.eslintrc index b7876471b..8335b1acc 100644 --- a/packages/enzyme-adapter-react-16.2/.eslintrc +++ b/packages/enzyme-adapter-react-16.2/.eslintrc @@ -2,6 +2,7 @@ "extends": "airbnb", "root": true, "rules": { + "max-len": 0, "import/no-extraneous-dependencies": 0, "import/no-unresolved": 0, "import/extensions": 0, diff --git a/packages/enzyme-adapter-react-16.3/.eslintrc b/packages/enzyme-adapter-react-16.3/.eslintrc index f6fe85e0f..06426b575 100644 --- a/packages/enzyme-adapter-react-16.3/.eslintrc +++ b/packages/enzyme-adapter-react-16.3/.eslintrc @@ -2,6 +2,7 @@ "extends": "airbnb", "root": true, "rules": { + "max-len": 0, "import/no-extraneous-dependencies": 0, "import/no-unresolved": 0, "import/extensions": 0, diff --git a/packages/enzyme-adapter-react-16/.eslintrc b/packages/enzyme-adapter-react-16/.eslintrc index aef8cdee1..136d2264f 100644 --- a/packages/enzyme-adapter-react-16/.eslintrc +++ b/packages/enzyme-adapter-react-16/.eslintrc @@ -2,6 +2,7 @@ "extends": "airbnb", "root": true, "rules": { + "max-len": 0, "import/no-extraneous-dependencies": 0, "import/no-unresolved": 0, "import/extensions": 0, @@ -13,7 +14,7 @@ }, "settings": { "react": { - "version": "16.4.0", + "version": "16", }, }, } diff --git a/packages/enzyme-adapter-react-helper/.eslintrc b/packages/enzyme-adapter-react-helper/.eslintrc index 6ecc92b4c..52ad1bfbf 100644 --- a/packages/enzyme-adapter-react-helper/.eslintrc +++ b/packages/enzyme-adapter-react-helper/.eslintrc @@ -4,4 +4,9 @@ "rules": { "max-len": 0, }, + "settings": { + "react": { + "version": "detect", + }, + }, } diff --git a/packages/enzyme-adapter-utils/.eslintrc b/packages/enzyme-adapter-utils/.eslintrc index 146d063b2..7ccd2407a 100644 --- a/packages/enzyme-adapter-utils/.eslintrc +++ b/packages/enzyme-adapter-utils/.eslintrc @@ -9,4 +9,12 @@ }, }, ], + "rules": { + "max-len": 0, + }, + "settings": { + "react": { + "version": "detect", + }, + }, } diff --git a/packages/enzyme-test-suite/.eslintrc b/packages/enzyme-test-suite/.eslintrc index 2de886f7a..30b705142 100644 --- a/packages/enzyme-test-suite/.eslintrc +++ b/packages/enzyme-test-suite/.eslintrc @@ -7,6 +7,9 @@ "plugins": ["mocha"], "settings": { "mocha/additionalTestFunctions": ["itIf", "describeIf"], + "react": { + "version": "detect", + }, }, "rules": { "max-len": 0, From df0c3265a589c8f99d180c96796a90e9d310df14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B3=E4=B9=99=E5=B1=B1?= Date: Wed, 6 Mar 2019 09:07:48 +0800 Subject: [PATCH 2/3] [Tests] add `mount`-passing, `shallow`-failing test for `useEffect` hook Pending https://github.com/facebook/react/issues/14840 --- .../test/ReactWrapper-spec.jsx | 38 +++++++++++++++++++ .../test/ShallowWrapper-spec.jsx | 35 +++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx index c1dd129f4..1c18c6e67 100644 --- a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx @@ -31,6 +31,8 @@ import { forwardRef, memo, PureComponent, + useEffect, + useState, } from './_helpers/react-compat'; import { describeWithDOM, @@ -648,6 +650,42 @@ describeWithDOM('mount', () => { }); }); + describeIf(is('>= 16.8'), 'hooks', () => { + it('works with `useEffect`', (done) => { + function ComponentUsingEffectHook() { + const [ctr, setCtr] = useState(0); + useEffect(() => { + setCtr(1); + setTimeout(() => { + setCtr(2); + }, 1e3); + }, []); + return ( +
+ {ctr} +
+ ); + } + const wrapper = mount(); + + expect(wrapper.debug()).to.equal(` +
+ 1 +
+
`); + + setTimeout(() => { + wrapper.update(); + expect(wrapper.debug()).to.equal(` +
+ 2 +
+
`); + done(); + }, 1e3); + }); + }); + describe('.contains(node)', () => { it('allows matches on the root node', () => { const a =
; diff --git a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx index 358191880..bf3648237 100644 --- a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx @@ -31,6 +31,8 @@ import { forwardRef, memo, PureComponent, + useEffect, + useState, } from './_helpers/react-compat'; import { describeIf, @@ -653,6 +655,39 @@ describe('shallow', () => { }); }); + describeIf(is('>= 16.8'), 'hooks', () => { + // TODO: enable when the shallow renderer fixes its bug + it.skip('works with `useEffect`', (done) => { + function ComponentUsingEffectHook() { + const [ctr, setCtr] = useState(0); + useEffect(() => { + setCtr(1); + setTimeout(() => { + setCtr(2); + }, 1e3); + }, []); + return ( +
+ {ctr} +
+ ); + } + const wrapper = shallow(); + + expect(wrapper.debug()).to.equal(`
+ 1 +
`); + + setTimeout(() => { + wrapper.update(); + expect(wrapper.debug()).to.equal(`
+ 2 +
`); + done(); + }, 1e3); + }); + }); + describe('.contains(node)', () => { it('allows matches on the root node', () => { const a =
; From 35ec8fbb65fb755196fca1762ffcbe2f83733fc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B3=E4=B9=99=E5=B1=B1?= Date: Wed, 6 Mar 2019 09:04:35 +0800 Subject: [PATCH 3/3] [enzyme-adapter-react-16] [new] Wrap renders in `TestUtils.act` --- .../src/ReactSixteenAdapter.js | 155 ++++++++++-------- 1 file changed, 84 insertions(+), 71 deletions(-) diff --git a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js index 957555416..1ef9d011e 100644 --- a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js +++ b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js @@ -279,6 +279,15 @@ function getEmptyStateValue() { return testRenderer._instance.state; } +function wrapAct(fn) { + if (typeof TestUtils.act !== 'function') { + return fn(); + } + let returnVal; + TestUtils.act(() => { returnVal = fn(); }); + return returnVal; +} + class ReactSixteenAdapter extends EnzymeAdapter { constructor() { super(); @@ -316,25 +325,27 @@ class ReactSixteenAdapter extends EnzymeAdapter { const adapter = this; return { render(el, context, callback) { - if (instance === null) { - const { type, props, ref } = el; - const wrapperProps = { - Component: type, - props, - context, - ...(ref && { ref }), - }; - const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); - const wrappedEl = React.createElement(ReactWrapperComponent, wrapperProps); - instance = hydrateIn - ? ReactDOM.hydrate(wrappedEl, domNode) - : ReactDOM.render(wrappedEl, domNode); - if (typeof callback === 'function') { - callback(); + return wrapAct(() => { + if (instance === null) { + const { type, props, ref } = el; + const wrapperProps = { + Component: type, + props, + context, + ...(ref && { ref }), + }; + const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); + const wrappedEl = React.createElement(ReactWrapperComponent, wrapperProps); + instance = hydrateIn + ? ReactDOM.hydrate(wrappedEl, domNode) + : ReactDOM.render(wrappedEl, domNode); + if (typeof callback === 'function') { + callback(); + } + } else { + instance.setChildProps(el.props, context, callback); } - } else { - instance.setChildProps(el.props, context, callback); - } + }); }, unmount() { ReactDOM.unmountComponentAtNode(domNode); @@ -378,63 +389,65 @@ class ReactSixteenAdapter extends EnzymeAdapter { let cachedNode = null; return { render(el, unmaskedContext) { - cachedNode = el; - /* eslint consistent-return: 0 */ - if (typeof el.type === 'string') { - isDOM = true; - } else { - isDOM = false; - const { type: Component } = el; - - const isStateful = Component.prototype && ( - Component.prototype.isReactComponent - || Array.isArray(Component.__reactAutoBindPairs) // fallback for createClass components - ); - - const context = getMaskedContext(Component.contextTypes, unmaskedContext); - - if (!isStateful && isMemo(el.type)) { - const InnerComp = el.type.type; - const wrappedEl = Object.assign( - (...args) => InnerComp(...args), // eslint-disable-line new-cap - InnerComp, + return wrapAct(() => { + cachedNode = el; + /* eslint consistent-return: 0 */ + if (typeof el.type === 'string') { + isDOM = true; + } else { + isDOM = false; + const { type: Component } = el; + + const isStateful = Component.prototype && ( + Component.prototype.isReactComponent + || Array.isArray(Component.__reactAutoBindPairs) // fallback for createClass components ); - return withSetStateAllowed(() => renderer.render({ ...el, type: wrappedEl }, context)); - } - if (!isStateful && typeof Component === 'function') { - const wrappedEl = Object.assign( - (...args) => Component(...args), // eslint-disable-line new-cap - Component, - ); - 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; - }, - }); + const context = getMaskedContext(Component.contextTypes, unmaskedContext); + + if (!isStateful && isMemo(el.type)) { + const InnerComp = el.type.type; + const wrappedEl = Object.assign( + (...args) => InnerComp(...args), // eslint-disable-line new-cap + InnerComp, + ); + return withSetStateAllowed(() => renderer.render({ ...el, type: wrappedEl }, context)); } + + if (!isStateful && typeof Component === 'function') { + const wrappedEl = Object.assign( + (...args) => Component(...args), // eslint-disable-line new-cap + Component, + ); + 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)); } - return withSetStateAllowed(() => renderer.render(el, context)); - } + }); }, unmount() { renderer.unmount();