From e45b365f22a5c2e5947c9c061eb0e0017647218c Mon Sep 17 00:00:00 2001 From: Jason Zukewich Date: Thu, 16 Aug 2018 15:59:21 -0700 Subject: [PATCH 1/2] [enzyme-adapter-react-16*, enzyme-adapter-utils] `mount`: add support for Portals --- .../src/ReactSixteenOneAdapter.js | 13 +- .../src/ReactSixteenTwoAdapter.js | 13 +- .../src/ReactSixteenThreeAdapter.js | 13 +- .../src/ReactSixteenAdapter.js | 13 +- packages/enzyme-adapter-utils/package.json | 3 +- packages/enzyme-adapter-utils/src/Utils.js | 10 ++ .../enzyme-test-suite/test/Adapter-spec.jsx | 19 ++- .../test/ReactWrapper-spec.jsx | 138 ++++++++++++++++++ 8 files changed, 205 insertions(+), 17 deletions(-) diff --git a/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js b/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js index f7d90e451..17156059b 100644 --- a/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js +++ b/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js @@ -111,9 +111,16 @@ function toTree(vnode) { switch (node.tag) { case HostRoot: // 3 return childrenToTree(node.child); - case HostPortal: { // 4 - return childrenToTree(node.child); - } + case HostPortal: // 4 + return { + nodeType: 'portal', + type: Portal, + props: {}, + key: ensureKeyOrUndefined(node.key), + ref: node.ref, + instance: null, + rendered: childrenToTree(node.child), + }; case ClassComponent: return { nodeType: 'class', diff --git a/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js b/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js index 722fbe693..762116a78 100644 --- a/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js +++ b/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js @@ -112,9 +112,16 @@ function toTree(vnode) { switch (node.tag) { case HostRoot: // 3 return childrenToTree(node.child); - case HostPortal: { // 4 - return childrenToTree(node.child); - } + case HostPortal: // 4 + return { + nodeType: 'portal', + type: Portal, + props: {}, + key: ensureKeyOrUndefined(node.key), + ref: node.ref, + instance: null, + rendered: childrenToTree(node.child), + }; case ClassComponent: return { nodeType: 'class', diff --git a/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js b/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js index 3c7a07e7a..2816604a6 100644 --- a/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js +++ b/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js @@ -117,9 +117,16 @@ function toTree(vnode) { switch (node.tag) { case HostRoot: // 3 return childrenToTree(node.child); - case HostPortal: { // 4 - return childrenToTree(node.child); - } + case HostPortal: // 4 + return { + nodeType: 'portal', + type: Portal, + props: {}, + key: ensureKeyOrUndefined(node.key), + ref: node.ref, + instance: null, + rendered: childrenToTree(node.child), + }; case ClassComponent: return { nodeType: 'class', diff --git a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js index b2e1ca1f3..1484b9e68 100644 --- a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js +++ b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js @@ -117,9 +117,16 @@ function toTree(vnode) { switch (node.tag) { case HostRoot: // 3 return childrenToTree(node.child); - case HostPortal: { // 4 - return childrenToTree(node.child); - } + case HostPortal: // 4 + return { + nodeType: 'portal', + type: Portal, + props: {}, + key: ensureKeyOrUndefined(node.key), + ref: node.ref, + instance: null, + rendered: childrenToTree(node.child), + }; case ClassComponent: return { nodeType: 'class', diff --git a/packages/enzyme-adapter-utils/package.json b/packages/enzyme-adapter-utils/package.json index 81acdbdee..969b82646 100644 --- a/packages/enzyme-adapter-utils/package.json +++ b/packages/enzyme-adapter-utils/package.json @@ -36,7 +36,8 @@ "dependencies": { "function.prototype.name": "^1.1.0", "object.assign": "^4.1.0", - "prop-types": "^15.6.2" + "prop-types": "^15.6.2", + "react-is": "^16.4.2" }, "peerDependencies": { "react": "0.13.x || 0.14.x || ^15.0.0-0 || ^16.0.0-0" diff --git a/packages/enzyme-adapter-utils/src/Utils.js b/packages/enzyme-adapter-utils/src/Utils.js index 5c33d0da3..73a7990ba 100644 --- a/packages/enzyme-adapter-utils/src/Utils.js +++ b/packages/enzyme-adapter-utils/src/Utils.js @@ -1,4 +1,7 @@ import functionName from 'function.prototype.name'; +import { + Portal, +} from 'react-is'; import createMountWrapper from './createMountWrapper'; import createRenderWrapper from './createRenderWrapper'; @@ -107,6 +110,10 @@ export function displayNameOfNode(node) { if (!type) return null; + if (type === Portal) { + return 'Portal'; + } + return type.displayName || (typeof type === 'function' ? functionName(type) : type.name || type); } @@ -117,6 +124,9 @@ export function nodeTypeFromType(type) { if (type && type.prototype && type.prototype.isReactComponent) { return 'class'; } + if (type && type === Portal) { + return 'portal'; + } return 'function'; } diff --git a/packages/enzyme-test-suite/test/Adapter-spec.jsx b/packages/enzyme-test-suite/test/Adapter-spec.jsx index d988b9b1b..c310af3c9 100644 --- a/packages/enzyme-test-suite/test/Adapter-spec.jsx +++ b/packages/enzyme-test-suite/test/Adapter-spec.jsx @@ -4,6 +4,9 @@ import jsdom from 'jsdom'; import { get } from 'enzyme/build/configuration'; import { configure, shallow } from 'enzyme'; import inspect from 'object-inspect'; +import { + Portal, +} from 'react-is'; import './_helpers/setupAdapters'; import Adapter from './_helpers/adapter'; @@ -226,13 +229,21 @@ describe('Adapter', () => { ref: null, instance: null, rendered: { - nodeType: 'host', - type: 'div', - props: { className: 'Foo' }, + nodeType: 'portal', + type: Portal, + props: {}, key: undefined, ref: null, instance: null, - rendered: ['Hello World!'], + rendered: { + nodeType: 'host', + type: 'div', + props: { className: 'Foo' }, + key: undefined, + ref: null, + instance: null, + rendered: ['Hello World!'], + }, }, })); }); diff --git a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx index 19473ba15..2d6386274 100644 --- a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx @@ -15,6 +15,9 @@ import { sym, } from 'enzyme/build/Utils'; import getAdapter from 'enzyme/build/getAdapter'; +import { + Portal, +} from 'react-is'; import './_helpers/setupAdapters'; import { @@ -426,6 +429,102 @@ describeWithDOM('mount', () => { }); }); + describeIf(is('>= 16'), 'portals', () => { + it('should show portals in mount debug tree', () => { + const containerDiv = global.document.createElement('div'); + const Foo = () => ( +
+ {createPortal( +
InPortal
, + containerDiv, + )} +
+ ); + + const wrapper = mount(); + expect(wrapper.debug()).to.equal(` +
+ +
+ InPortal +
+
+
+
`); + }); + + it('should show portal container in shallow debug tree', () => { + const containerDiv = global.document.createElement('div'); + const Foo = () => ( +
+ {createPortal( +
InPortal
, + containerDiv, + )} +
+ ); + + const wrapper = mount(); + expect(wrapper.debug({ verbose: true })).to.equal(` +
+ +
+ InPortal +
+
+
+
`); + }); + + it('should show nested portal children in debug tree', () => { + const Bar = () => null; + + const containerDiv = global.document.createElement('div'); + const Foo = () => ( +
+ {createPortal( +
+
+ +
+
, + containerDiv, + )} +
+ ); + + const wrapper = mount(); + expect(wrapper.debug()).to.equal(` +
+ +
+
+ +
+
+
+
+
`); + }); + + it('should have top level portals in debug tree', () => { + const containerDiv = global.document.createElement('div'); + const Foo = () => createPortal( +
InPortal
, + containerDiv, + ); + + const wrapper = mount(); + expect(wrapper.debug()).to.equal(` + +
+ InPortal +
+
+
`); + }); + }); + describe('.contains(node)', () => { it('should allow matches on the root node', () => { const a =
; @@ -1260,6 +1359,25 @@ describeWithDOM('mount', () => { expect(wrapper.children()).to.have.lengthOf(1); }); }); + + itIf(is('>= 16'), 'should find mounted portals by name', () => { + const containerDiv = global.document.createElement('div'); + const Foo = () => ( +
+ {createPortal( +
InPortal
, + containerDiv, + )} +
+ ); + + const wrapper = mount(); + expect(wrapper.find('Portal').debug()).to.equal(` +
+ InPortal +
+
`); + }); }); describe('.findWhere(predicate)', () => { @@ -1582,6 +1700,26 @@ describeWithDOM('mount', () => { wrapper.findWhere(spy); expect(spy).to.have.property('callCount', 2); }); + + itIf(is('>= 16'), 'should find mounted portals by react-is Portal type', () => { + const containerDiv = global.document.createElement('div'); + const Foo = () => ( +
+ {createPortal( +
InPortal
, + containerDiv, + )} +
+ ); + + const wrapper = mount(); + expect(wrapper.findWhere(node => node.type() === Portal).debug()) + .to.equal(` +
+ InPortal +
+
`); + }); }); describe('.setProps(newProps[, callback])', () => { From 605021d24b31eac4fa88d5034420285de0d6b59e Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 17 Aug 2018 23:31:09 -0700 Subject: [PATCH 2/2] `mount`: add containerInfo to Portal representations --- .../src/ReactSixteenOneAdapter.js | 7 +++++-- .../src/ReactSixteenTwoAdapter.js | 7 +++++-- .../src/ReactSixteenThreeAdapter.js | 7 +++++-- .../src/ReactSixteenAdapter.js | 7 +++++-- packages/enzyme-test-suite/test/Adapter-spec.jsx | 4 +++- .../enzyme-test-suite/test/ReactWrapper-spec.jsx | 15 ++++++++------- 6 files changed, 31 insertions(+), 16 deletions(-) diff --git a/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js b/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js index 17156059b..9957e89da 100644 --- a/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js +++ b/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js @@ -111,16 +111,19 @@ function toTree(vnode) { switch (node.tag) { case HostRoot: // 3 return childrenToTree(node.child); - case HostPortal: // 4 + case HostPortal: { // 4 + const { stateNode: { containerInfo } } = node; + const props = { containerInfo }; return { nodeType: 'portal', type: Portal, - props: {}, + props, key: ensureKeyOrUndefined(node.key), ref: node.ref, instance: null, rendered: childrenToTree(node.child), }; + } case ClassComponent: return { nodeType: 'class', diff --git a/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js b/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js index 762116a78..446658f4c 100644 --- a/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js +++ b/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js @@ -112,16 +112,19 @@ function toTree(vnode) { switch (node.tag) { case HostRoot: // 3 return childrenToTree(node.child); - case HostPortal: // 4 + case HostPortal: { // 4 + const { stateNode: { containerInfo } } = node; + const props = { containerInfo }; return { nodeType: 'portal', type: Portal, - props: {}, + props, key: ensureKeyOrUndefined(node.key), ref: node.ref, instance: null, rendered: childrenToTree(node.child), }; + } case ClassComponent: return { nodeType: 'class', diff --git a/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js b/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js index 2816604a6..5a4557153 100644 --- a/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js +++ b/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js @@ -117,16 +117,19 @@ function toTree(vnode) { switch (node.tag) { case HostRoot: // 3 return childrenToTree(node.child); - case HostPortal: // 4 + case HostPortal: { // 4 + const { stateNode: { containerInfo } } = node; + const props = { containerInfo }; return { nodeType: 'portal', type: Portal, - props: {}, + props, key: ensureKeyOrUndefined(node.key), ref: node.ref, instance: null, rendered: childrenToTree(node.child), }; + } case ClassComponent: return { nodeType: 'class', diff --git a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js index 1484b9e68..b9b0f5016 100644 --- a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js +++ b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js @@ -117,16 +117,19 @@ function toTree(vnode) { switch (node.tag) { case HostRoot: // 3 return childrenToTree(node.child); - case HostPortal: // 4 + case HostPortal: { // 4 + const { stateNode: { containerInfo } } = node; + const props = { containerInfo }; return { nodeType: 'portal', type: Portal, - props: {}, + props, key: ensureKeyOrUndefined(node.key), ref: node.ref, instance: null, rendered: childrenToTree(node.child), }; + } case ClassComponent: return { nodeType: 'class', diff --git a/packages/enzyme-test-suite/test/Adapter-spec.jsx b/packages/enzyme-test-suite/test/Adapter-spec.jsx index c310af3c9..727361b1c 100644 --- a/packages/enzyme-test-suite/test/Adapter-spec.jsx +++ b/packages/enzyme-test-suite/test/Adapter-spec.jsx @@ -231,7 +231,9 @@ describe('Adapter', () => { rendered: { nodeType: 'portal', type: Portal, - props: {}, + props: { + containerInfo: document.body, + }, key: undefined, ref: null, instance: null, diff --git a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx index 2d6386274..4f2a08a89 100644 --- a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx @@ -444,7 +444,7 @@ describeWithDOM('mount', () => { const wrapper = mount(); expect(wrapper.debug()).to.equal(`
- +
InPortal
@@ -453,8 +453,9 @@ describeWithDOM('mount', () => { `); }); - it('should show portal container in shallow debug tree', () => { + it('should show portal container in debug tree', () => { const containerDiv = global.document.createElement('div'); + containerDiv.setAttribute('data-foo', 'bar'); const Foo = () => (
{createPortal( @@ -467,7 +468,7 @@ describeWithDOM('mount', () => { const wrapper = mount(); expect(wrapper.debug({ verbose: true })).to.equal(`
- + ...
}>
InPortal
@@ -496,7 +497,7 @@ describeWithDOM('mount', () => { const wrapper = mount(); expect(wrapper.debug()).to.equal(`
- +
@@ -516,7 +517,7 @@ describeWithDOM('mount', () => { const wrapper = mount(); expect(wrapper.debug()).to.equal(` - +
InPortal
@@ -1372,7 +1373,7 @@ describeWithDOM('mount', () => { ); const wrapper = mount(); - expect(wrapper.find('Portal').debug()).to.equal(` + expect(wrapper.find('Portal').debug()).to.equal(`
InPortal
@@ -1714,7 +1715,7 @@ describeWithDOM('mount', () => { const wrapper = mount(); expect(wrapper.findWhere(node => node.type() === Portal).debug()) - .to.equal(` + .to.equal(`
InPortal