From a7f1a4700abf55126c97e95c790d6d83056947a4 Mon Sep 17 00:00:00 2001 From: Julian Mulet Date: Thu, 20 Sep 2018 13:58:11 +0200 Subject: [PATCH 1/2] =?UTF-8?q?[Fix]=20`shallow`/`Utils`:=20call=20into=20?= =?UTF-8?q?adapter=E2=80=99s=20`isCustomComponentElement`=20if=20present?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../enzyme-test-suite/test/Utils-spec.jsx | 146 ++++++++++++++++++ packages/enzyme/src/Utils.js | 3 + 2 files changed, 149 insertions(+) diff --git a/packages/enzyme-test-suite/test/Utils-spec.jsx b/packages/enzyme-test-suite/test/Utils-spec.jsx index bfa0d4fdc..1c3d7ad2d 100644 --- a/packages/enzyme-test-suite/test/Utils-spec.jsx +++ b/packages/enzyme-test-suite/test/Utils-spec.jsx @@ -9,6 +9,7 @@ import { displayNameOfNode, spyMethod, nodeHasType, + isCustomComponentElement, } from 'enzyme/build/Utils'; import getAdapter from 'enzyme/build/getAdapter'; import { @@ -639,6 +640,151 @@ describe('Utils', () => { }); }); + describe('isCustomComponentElement()', () => { + const adapter = getAdapter(); + + wrap() + .withOverride(() => adapter, 'isCustomComponentElement', () => undefined) + .describe('with an adapter lacking `.isCustomComponentElement`', () => { + describe('given a valid CustomComponentElement', () => { + it('returns true', () => { + class Foo extends React.Component { + render() { return
; } + } + expect(isCustomComponentElement(, adapter)).to.equal(true); + }); + + describeIf(is('> 0.13'), 'stateless function elements', () => { + it('returns true', () => { + const Foo = () =>
; + + expect(isCustomComponentElement(, adapter)).to.equal(true); + }); + }); + + describeIf(is('>=16.3.0'), 'forwardRef Elements', () => { + it('returns false', () => { + const Foo = React.forwardRef(() =>
); + expect(isCustomComponentElement(, adapter)).to.equal(false); + }); + }); + }); + + describe('given an invalid CustomComponentElement', () => { + it('returns false for HTML elements', () => { + expect(isCustomComponentElement(
, adapter)).to.equal(false); + }); + + it('returns false for non-Components', () => { + [ + class Foo {}, + {}, + () => {}, + 'div', + 'Foo', + null, + ].forEach((nonComponent) => { + expect(isCustomComponentElement(nonComponent, adapter)).to.equal(false); + }); + }); + }); + }); + + wrap() + .withOverride(() => adapter, 'isCustomComponentElement', () => () => false) + .describe('with an adapter that has `.isCustomComponentElement` that always returns false', () => { + describe('given a valid CustomComponentElement', () => { + it('returns false', () => { + class Foo extends React.Component { + render() { return
; } + } + expect(isCustomComponentElement(, adapter)).to.equal(false); + }); + + describeIf(is('> 0.13'), 'stateless function elements', () => { + it('returns false', () => { + const Foo = () =>
; + + expect(isCustomComponentElement(, adapter)).to.equal(false); + }); + }); + + describeIf(is('>=16.3.0'), 'forwardRef Elements', () => { + it('returns false', () => { + const Foo = React.forwardRef(() =>
); + expect(isCustomComponentElement(, adapter)).to.equal(false); + }); + }); + }); + + describe('given an invalid CustomComponentElement', () => { + it('returns false for HTML elements', () => { + expect(isCustomComponentElement(
, adapter)).to.equal(false); + }); + + it('returns false for non-Components', () => { + [ + class Foo {}, + {}, + () => {}, + 'div', + 'Foo', + null, + ].forEach((nonComponent) => { + expect(isCustomComponentElement(nonComponent, adapter)).to.equal(false); + }); + }); + }); + }); + + wrap() + .withOverride(() => adapter, 'isCustomComponentElement', () => () => true) + .describe('with an adapter that has `.isCustomComponentElement` that always returns true', () => { + describe('given a valid CustomComponentElement', () => { + it('returns true', () => { + class Foo extends React.Component { + render() { return
; } + } + expect(isCustomComponentElement(, adapter)).to.equal(true); + }); + + describeIf(is('> 0.13'), 'stateless function elements', () => { + it('returns true', () => { + const Foo = () =>
; + + expect(isCustomComponentElement(, adapter)).to.equal(true); + }); + }); + + describeIf(is('>=16.3.0'), 'forwardRef Elements', () => { + it('returns true', () => { + const Foo = React.forwardRef(() =>
); + expect(isCustomComponentElement(, adapter)).to.equal(true); + }); + }); + }); + + describe('given an invalid CustomComponentElement', () => { + it('returns true for HTML elements', () => { + expect(isCustomComponentElement(
, adapter)).to.equal(true); + }); + + it('returns true for non-Components', () => { + [ + class Foo {}, + {}, + () => {}, + 'div', + 'Foo', + null, + ].forEach((nonComponent) => { + expect(isCustomComponentElement(nonComponent, adapter)).to.equal(true); + }); + }); + }); + }); + }); + wrap() .withOverride(() => getAdapter(), 'displayNameOfNode', () => undefined) .describe('nodeHasType', () => { diff --git a/packages/enzyme/src/Utils.js b/packages/enzyme/src/Utils.js index 63448a777..b1a8c93d4 100644 --- a/packages/enzyme/src/Utils.js +++ b/packages/enzyme/src/Utils.js @@ -42,6 +42,9 @@ export function makeOptions(options) { } export function isCustomComponentElement(inst, adapter) { + if (adapter.isCustomComponentElement) { + return !!adapter.isCustomComponentElement(inst); + } return !!inst && adapter.isValidElement(inst) && typeof inst.type === 'function'; } From 3254521ca6bfe0eb84c8dac7df6503bb47492401 Mon Sep 17 00:00:00 2001 From: Julian Mulet Date: Thu, 20 Sep 2018 13:58:11 +0200 Subject: [PATCH 2/2] [enzyme-adapter-react-{16,16.3}] [new] add `isCustomComponentElement` --- .../src/ReactSixteenThreeAdapter.js | 8 ++++++ .../src/ReactSixteenAdapter.js | 8 ++++++ .../test/ShallowWrapper-spec.jsx | 27 +++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js b/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js index 3617703aa..a243eddde 100644 --- a/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js +++ b/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js @@ -11,6 +11,7 @@ import TestUtils from 'react-dom/test-utils'; import { isElement, isPortal, + isForwardRef, isValidElementType, AsyncMode, Fragment, @@ -483,6 +484,13 @@ class ReactSixteenThreeAdapter extends EnzymeAdapter { return typeOfNode(fragment) === Fragment; } + isCustomComponentElement(inst) { + if (!inst || !this.isValidElement(inst)) { + return false; + } + return typeof inst.type === 'function' || isForwardRef(inst); + } + createElement(...args) { return React.createElement(...args); } diff --git a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js index ac2941fab..e18030688 100644 --- a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js +++ b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js @@ -11,6 +11,7 @@ import TestUtils from 'react-dom/test-utils'; import { isElement, isPortal, + isForwardRef, isValidElementType, AsyncMode, Fragment, @@ -525,6 +526,13 @@ class ReactSixteenAdapter extends EnzymeAdapter { return typeOfNode(fragment) === Fragment; } + isCustomComponentElement(inst) { + if (!inst || !this.isValidElement(inst)) { + return false; + } + return typeof inst.type === 'function' || isForwardRef(inst); + } + createElement(...args) { return React.createElement(...args); } diff --git a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx index 5f427aa1e..84a78810b 100644 --- a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx @@ -6923,6 +6923,33 @@ describe('shallow', () => { expect(underwater.is(RendersDOM)).to.equal(true); }); + describeIf(is('>=16.3.0'), 'forwardRef Elements', () => { + const ForwardRefWrapsRendersDOM = forwardRef && forwardRef(() => ); + const NestedForwarRefsWrapsRendersDom = forwardRef + && forwardRef(() => ); + + if (forwardRef) { + NestedForwarRefsWrapsRendersDom.contextTypes = { foo: PropTypes.string }; + ForwardRefWrapsRendersDOM.contextTypes = { foo: PropTypes.string }; + } + + it('dives + shallow-renders a forwardRef component', () => { + const wrapper = shallow(); + expect(wrapper.is(WrapsRendersDOM)).to.equal(true); + + const underwater = wrapper.dive(); + expect(underwater.is(RendersDOM)).to.equal(true); + }); + + it('dives + shallow-renders a with nested forwardRefs component', () => { + const wrapper = shallow(); + expect(wrapper.is(ForwardRefWrapsRendersDOM)).to.equal(true); + + const underwater = wrapper.dive(); + expect(underwater.is(WrapsRendersDOM)).to.equal(true); + }); + }); + it('merges and pass options through', () => { const wrapper = shallow(, { context: { foo: 'hello' } }); expect(wrapper.context()).to.deep.equal({ foo: 'hello' });