Skip to content

Commit

Permalink
Merge pull request #1760 from jgzuke/jgzuke-support-portal-mount
Browse files Browse the repository at this point in the history
[enzyme-adapter-react-16*, enzyme-adapter-utils] [new] `mount`: add support for Portals
  • Loading branch information
ljharb committed Aug 18, 2018
2 parents fe6f676 + 605021d commit 3bc5de2
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 9 deletions.
12 changes: 11 additions & 1 deletion packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,17 @@ function toTree(vnode) {
case HostRoot: // 3
return childrenToTree(node.child);
case HostPortal: { // 4
return childrenToTree(node.child);
const { stateNode: { containerInfo } } = node;
const props = { containerInfo };
return {
nodeType: 'portal',
type: Portal,
props,
key: ensureKeyOrUndefined(node.key),
ref: node.ref,
instance: null,
rendered: childrenToTree(node.child),
};
}
case ClassComponent:
return {
Expand Down
12 changes: 11 additions & 1 deletion packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,17 @@ function toTree(vnode) {
case HostRoot: // 3
return childrenToTree(node.child);
case HostPortal: { // 4
return childrenToTree(node.child);
const { stateNode: { containerInfo } } = node;
const props = { containerInfo };
return {
nodeType: 'portal',
type: Portal,
props,
key: ensureKeyOrUndefined(node.key),
ref: node.ref,
instance: null,
rendered: childrenToTree(node.child),
};
}
case ClassComponent:
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,17 @@ function toTree(vnode) {
case HostRoot: // 3
return childrenToTree(node.child);
case HostPortal: { // 4
return childrenToTree(node.child);
const { stateNode: { containerInfo } } = node;
const props = { containerInfo };
return {
nodeType: 'portal',
type: Portal,
props,
key: ensureKeyOrUndefined(node.key),
ref: node.ref,
instance: null,
rendered: childrenToTree(node.child),
};
}
case ClassComponent:
return {
Expand Down
12 changes: 11 additions & 1 deletion packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,17 @@ function toTree(vnode) {
case HostRoot: // 3
return childrenToTree(node.child);
case HostPortal: { // 4
return childrenToTree(node.child);
const { stateNode: { containerInfo } } = node;
const props = { containerInfo };
return {
nodeType: 'portal',
type: Portal,
props,
key: ensureKeyOrUndefined(node.key),
ref: node.ref,
instance: null,
rendered: childrenToTree(node.child),
};
}
case ClassComponent:
return {
Expand Down
3 changes: 2 additions & 1 deletion packages/enzyme-adapter-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
10 changes: 10 additions & 0 deletions packages/enzyme-adapter-utils/src/Utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import functionName from 'function.prototype.name';
import {
Portal,
} from 'react-is';
import createMountWrapper from './createMountWrapper';
import createRenderWrapper from './createRenderWrapper';

Expand Down Expand Up @@ -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);
}

Expand All @@ -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';
}

Expand Down
21 changes: 17 additions & 4 deletions packages/enzyme-test-suite/test/Adapter-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -226,13 +229,23 @@ describe('Adapter', () => {
ref: null,
instance: null,
rendered: {
nodeType: 'host',
type: 'div',
props: { className: 'Foo' },
nodeType: 'portal',
type: Portal,
props: {
containerInfo: document.body,
},
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!'],
},
},
}));
});
Expand Down
139 changes: 139 additions & 0 deletions packages/enzyme-test-suite/test/ReactWrapper-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -426,6 +429,103 @@ describeWithDOM('mount', () => {
});
});

describeIf(is('>= 16'), 'portals', () => {
it('should show portals in mount debug tree', () => {
const containerDiv = global.document.createElement('div');
const Foo = () => (
<div>
{createPortal(
<div className="in-portal">InPortal</div>,
containerDiv,
)}
</div>
);

const wrapper = mount(<Foo />);
expect(wrapper.debug()).to.equal(`<Foo>
<div>
<Portal containerInfo={{...}}>
<div className="in-portal">
InPortal
</div>
</Portal>
</div>
</Foo>`);
});

it('should show portal container in debug tree', () => {
const containerDiv = global.document.createElement('div');
containerDiv.setAttribute('data-foo', 'bar');
const Foo = () => (
<div className="foo">
{createPortal(
<div className="in-portal">InPortal</div>,
containerDiv,
)}
</div>
);

const wrapper = mount(<Foo />);
expect(wrapper.debug({ verbose: true })).to.equal(`<Foo>
<div className="foo">
<Portal containerInfo={<div data-foo="bar">...</div>}>
<div className="in-portal">
InPortal
</div>
</Portal>
</div>
</Foo>`);
});

it('should show nested portal children in debug tree', () => {
const Bar = () => null;

const containerDiv = global.document.createElement('div');
const Foo = () => (
<div className="foo">
{createPortal(
<div className="in-portal">
<div className="nested-in-portal">
<Bar />
</div>
</div>,
containerDiv,
)}
</div>
);

const wrapper = mount(<Foo />);
expect(wrapper.debug()).to.equal(`<Foo>
<div className="foo">
<Portal containerInfo={{...}}>
<div className="in-portal">
<div className="nested-in-portal">
<Bar />
</div>
</div>
</Portal>
</div>
</Foo>`);
});

it('should have top level portals in debug tree', () => {
const containerDiv = global.document.createElement('div');
const Foo = () => createPortal(
<div className="in-portal">InPortal</div>,
containerDiv,
);

const wrapper = mount(<Foo />);
expect(wrapper.debug()).to.equal(`<Foo>
<Portal containerInfo={{...}}>
<div className="in-portal">
InPortal
</div>
</Portal>
</Foo>`);
});
});

describe('.contains(node)', () => {
it('should allow matches on the root node', () => {
const a = <div className="foo" />;
Expand Down Expand Up @@ -1260,6 +1360,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 = () => (
<div>
{createPortal(
<div className="in-portal">InPortal</div>,
containerDiv,
)}
</div>
);

const wrapper = mount(<Foo />);
expect(wrapper.find('Portal').debug()).to.equal(`<Portal containerInfo={{...}}>
<div className="in-portal">
InPortal
</div>
</Portal>`);
});
});

describe('.findWhere(predicate)', () => {
Expand Down Expand Up @@ -1582,6 +1701,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 = () => (
<div>
{createPortal(
<div className="in-portal">InPortal</div>,
containerDiv,
)}
</div>
);

const wrapper = mount(<Foo />);
expect(wrapper.findWhere(node => node.type() === Portal).debug())
.to.equal(`<Portal containerInfo={{...}}>
<div className="in-portal">
InPortal
</div>
</Portal>`);
});
});

describe('.setProps(newProps[, callback])', () => {
Expand Down

0 comments on commit 3bc5de2

Please sign in to comment.