From 3577a029369b53518de2600530505c1b0f445c5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B3=E4=B9=99=E5=B1=B1?= Date: Wed, 9 Jan 2019 12:52:56 +0800 Subject: [PATCH] [New]: supports lazy and Suspense in mount and shallow --- packages/enzyme-adapter-react-16/.babelrc | 2 + .../src/ReactSixteenAdapter.js | 17 ++++++++ .../src/_helpers/dynamicImportedComponent.jsx | 5 +++ ...detectFiberTags.js => detectFiberTags.jsx} | 38 +++++++++++++++++ .../enzyme-test-suite/test/Adapter-spec.jsx | 10 +++++ .../test/ReactWrapper-spec.jsx | 40 ++++++++++++++++++ .../test/ShallowWrapper-spec.jsx | 41 +++++++++++++++++++ .../_helpers/dynamicImportedComponent.jsx | 12 ++---- 8 files changed, 156 insertions(+), 9 deletions(-) create mode 100644 packages/enzyme-adapter-react-16/src/_helpers/dynamicImportedComponent.jsx rename packages/enzyme-adapter-react-16/src/{detectFiberTags.js => detectFiberTags.jsx} (63%) diff --git a/packages/enzyme-adapter-react-16/.babelrc b/packages/enzyme-adapter-react-16/.babelrc index d6bfb99df..c5568b914 100644 --- a/packages/enzyme-adapter-react-16/.babelrc +++ b/packages/enzyme-adapter-react-16/.babelrc @@ -2,5 +2,7 @@ "presets": ["airbnb"], "plugins": [ ["transform-replace-object-assign", { "moduleSpecifier": "object.assign" }], + "syntax-dynamic-import", + "dynamic-import-node" ], } diff --git a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js index 3840f47f6..3fe38d6c6 100644 --- a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js +++ b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js @@ -20,8 +20,10 @@ import { ContextProvider, StrictMode, ForwardRef, + Lazy, Profiler, Portal, + Suspense, } from 'react-is'; import { EnzymeAdapter } from 'enzyme'; import { typeOfNode } from 'enzyme/build/Utils'; @@ -187,6 +189,19 @@ function toTree(vnode) { rendered: childrenToTree(node.child), }; } + case FiberTags.Suspense: { + return { + nodeType: 'suspense', + type: Suspense, + props: { ...node.memoizedProps }, + key: ensureKeyOrUndefined(node.key), + ref: node.ref, + instance: null, + rendered: childrenToTree(node.child), + }; + } + case FiberTags.Lazy: + return childrenToTree(node.child); default: throw new Error(`Enzyme Internal Error: unknown node with tag ${node.tag}`); } @@ -504,6 +519,7 @@ class ReactSixteenAdapter extends EnzymeAdapter { case StrictMode || NaN: return 'StrictMode'; case Profiler || NaN: return 'Profiler'; case Portal || NaN: return 'Portal'; + case Suspense || NaN: return 'Suspense'; default: } } @@ -520,6 +536,7 @@ class ReactSixteenAdapter extends EnzymeAdapter { const name = type.render.displayName || functionName(type.render); return name ? `ForwardRef(${name})` : 'ForwardRef'; } + case Lazy || NaN: return 'LazyComponent'; default: return displayNameOfNode(node); } } diff --git a/packages/enzyme-adapter-react-16/src/_helpers/dynamicImportedComponent.jsx b/packages/enzyme-adapter-react-16/src/_helpers/dynamicImportedComponent.jsx new file mode 100644 index 000000000..2a938ffd1 --- /dev/null +++ b/packages/enzyme-adapter-react-16/src/_helpers/dynamicImportedComponent.jsx @@ -0,0 +1,5 @@ +import React from 'react'; + +const DynamicComponent = () =>
Dynamic Component
; + +module.exports = DynamicComponent; diff --git a/packages/enzyme-adapter-react-16/src/detectFiberTags.js b/packages/enzyme-adapter-react-16/src/detectFiberTags.jsx similarity index 63% rename from packages/enzyme-adapter-react-16/src/detectFiberTags.js rename to packages/enzyme-adapter-react-16/src/detectFiberTags.jsx index 57c037a91..b2b65228f 100644 --- a/packages/enzyme-adapter-react-16/src/detectFiberTags.js +++ b/packages/enzyme-adapter-react-16/src/detectFiberTags.jsx @@ -14,10 +14,38 @@ function getFiber(element) { return inst._reactInternalFiber.child; } +function getLazyFiber(LazyComponent) { + const container = global.document.createElement('div'); + let inst = null; + // eslint-disable-next-line react/prefer-stateless-function + class Tester extends React.Component { + render() { + inst = this; + return ( + + ); + } + } + // eslint-disable-next-line react/prefer-stateless-function + class SuspenseWrapper extends React.Component { + render() { + return ( + + + + ); + } + } + ReactDOM.render(React.createElement(SuspenseWrapper), container); + return inst._reactInternalFiber.child; +} + module.exports = function detectFiberTags() { const supportsMode = typeof React.StrictMode !== 'undefined'; const supportsContext = typeof React.createContext !== 'undefined'; const supportsForwardRef = typeof React.forwardRef !== 'undefined'; + const supportsSuspense = typeof React.Suspense !== 'undefined'; + const supportsLazy = typeof React.lazy !== 'undefined'; function Fn() { return null; @@ -30,6 +58,7 @@ module.exports = function detectFiberTags() { } let Ctx = null; let FwdRef = null; + let LazyComponent = null; if (supportsContext) { Ctx = React.createContext(); } @@ -38,6 +67,9 @@ module.exports = function detectFiberTags() { // eslint-disable-next-line no-unused-vars FwdRef = React.forwardRef((props, ref) => null); } + if (supportsLazy) { + LazyComponent = React.lazy(() => import('./_helpers/dynamicImportedComponent')); + } return { HostRoot: getFiber('test').return.return.tag, // Go two levels above to find the root @@ -59,5 +91,11 @@ module.exports = function detectFiberTags() { ForwardRef: supportsForwardRef ? getFiber(React.createElement(FwdRef)).tag : -1, + Suspense: supportsSuspense + ? getFiber(React.createElement(React.Suspense, { fallback: false })).tag + : -1, + Lazy: supportsLazy + ? getLazyFiber(LazyComponent).tag + : -1, }; }; diff --git a/packages/enzyme-test-suite/test/Adapter-spec.jsx b/packages/enzyme-test-suite/test/Adapter-spec.jsx index 3ab12ee2b..12946cb22 100644 --- a/packages/enzyme-test-suite/test/Adapter-spec.jsx +++ b/packages/enzyme-test-suite/test/Adapter-spec.jsx @@ -21,6 +21,7 @@ import { AsyncMode, ConcurrentMode, Profiler, + Suspense, } from './_helpers/react-compat'; import { is } from './_helpers/version'; import { itIf, describeWithDOM, describeIf } from './_helpers'; @@ -1009,6 +1010,15 @@ describe('Adapter', () => { itIf(is('>= 16.6'), 'supports ConcurrentMode', () => { expect(getDisplayName()).to.equal('ConcurrentMode'); }); + + itIf(is('>= 16.6'), 'supports Suspense', () => { + expect(getDisplayName()).to.equal('Suspense'); + }); + + itIf(is('>= 16.6'), 'supports lazy', () => { + const LazyComponent = lazy(() => import('./_helpers/dynamicImportedComponent')); + expect(getDisplayName()).to.equal('LazyComponent'); + }); }); describeIf(is('>= 16.2'), 'determines if node isFragment', () => { diff --git a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx index 2afb6bd44..6e9a57cef 100644 --- a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx @@ -26,9 +26,11 @@ import { createContext, createPortal, createRef, + lazy, Fragment, forwardRef, PureComponent, + Suspense, } from './_helpers/react-compat'; import { describeWithDOM, @@ -560,6 +562,44 @@ describeWithDOM('mount', () => { }); }); + describeIf(is('>= 16.6'), 'Suspense & lazy', () => { + it('finds Suspense and its children when no lazy component', () => { + class Component extends React.Component { + render() { + return ( +
test
+ ); + } + } + const SuspenseComponent = () => ( + + + + ); + + const wrapper = mount(); + + expect(wrapper.find('SuspenseComponent')).to.have.lengthOf(1); + expect(wrapper.find(Component)).to.have.lengthOf(1); + }); + + it('finds fallback when given lazy component in initial mount', () => { + const LazyComponent = lazy(() => import('./_helpers/dynamicImportedComponent.jsx')); + const Fallback = () =>
; + const SuspenseComponent = () => ( + }> + + + ); + + const wrapper = mount(); + + expect(wrapper.find('SuspenseComponent')).to.have.lengthOf(1); + expect(wrapper.find(LazyComponent)).to.have.lengthOf(0); + expect(wrapper.find('Fallback')).to.have.lengthOf(1); + }); + }); + 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 1f5ed13d5..e28684573 100644 --- a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx @@ -26,9 +26,11 @@ import { createContext, createPortal, createRef, + lazy, Fragment, forwardRef, PureComponent, + Suspense, } from './_helpers/react-compat'; import { describeIf, @@ -1273,6 +1275,45 @@ describe('shallow', () => { expect(wrapper.find(Component.displayName)).to.have.lengthOf(2); }); }); + + describeIf(is('>= 16.6'), 'Suspense & lazy', () => { + it('finds Suspense and its children when no lazy component', () => { + class Component extends React.Component { + render() { + return ( +
test
+ ); + } + } + const SuspenseComponent = () => ( + + + + ); + + const wrapper = shallow(); + + expect(wrapper.find('Suspense')).to.have.lengthOf(1); + expect(wrapper.find(Component)).to.have.lengthOf(1); + }); + + it('finds Lazy when given lazy component', () => { + const LazyComponent = lazy(() => import('./_helpers/dynamicImportedComponent.jsx')); + const Fallback = () =>
; + const SuspenseComponent = () => ( + }> + + + ); + + const wrapper = shallow(); + + expect(wrapper.find('Suspense')).to.have.lengthOf(1); + expect(wrapper.find(LazyComponent)).to.have.lengthOf(1); + // won't render fallback in shallow renderer + expect(wrapper.find(Fallback)).to.have.lengthOf(0); + }); + }); }); describe('.findWhere(predicate)', () => { diff --git a/packages/enzyme-test-suite/test/_helpers/dynamicImportedComponent.jsx b/packages/enzyme-test-suite/test/_helpers/dynamicImportedComponent.jsx index 3afa41bb0..2a938ffd1 100644 --- a/packages/enzyme-test-suite/test/_helpers/dynamicImportedComponent.jsx +++ b/packages/enzyme-test-suite/test/_helpers/dynamicImportedComponent.jsx @@ -1,11 +1,5 @@ -import React from 'react' +import React from 'react'; -class DynamicComponent extends React.Component { - render() { - return ( -
Dynamic Component
- ); - } -} +const DynamicComponent = () =>
Dynamic Component
; -module.exports = DynamicComponent; \ No newline at end of file +module.exports = DynamicComponent;