Skip to content

Commit

Permalink
Merge pull request #1187 from kmcq/iterable-children
Browse files Browse the repository at this point in the history
Support array-like iterable children in v3
  • Loading branch information
ljharb authored Oct 27, 2017
2 parents c9a0dbb + 3ad399c commit a1d076d
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import flatten from 'lodash/flatten';
import { flatten, isArrayLike } from 'enzyme-adapter-utils';

export function nodeTypeFromType(type) {
if (typeof type === 'string') {
Expand All @@ -21,8 +21,8 @@ export default function elementToTree(el) {
const { type, props, key, ref } = el;
const { children } = props;
let rendered = null;
if (Array.isArray(children)) {
rendered = flatten(children, true).map(elementToTree);
if (isArrayLike(children)) {
rendered = flatten([...children], true).map(elementToTree);
} else if (typeof children !== 'undefined') {
rendered = elementToTree(children);
}
Expand Down
25 changes: 22 additions & 3 deletions packages/enzyme-adapter-utils/src/Utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import flatten from 'lodash/flatten';
import createMountWrapper from './createMountWrapper';
import createRenderWrapper from './createRenderWrapper';

Expand Down Expand Up @@ -95,15 +94,35 @@ export function nodeTypeFromType(type) {
return 'function';
}

function isIterable(obj) {
return (
obj != null &&
typeof Symbol === 'function' &&
typeof Symbol.iterator === 'symbol' &&
typeof obj[Symbol.iterator] === 'function'
);
}

export function isArrayLike(obj) {
return Array.isArray(obj) || (isIterable(obj) && typeof obj !== 'string');
}

export function flatten(arrs) {
return arrs.reduce(
(flattened, item) => flattened.concat(isArrayLike(item) ? flatten([...item]) : item),
[],
);
}

export function elementToTree(el) {
if (el === null || typeof el !== 'object' || !('type' in el)) {
return el;
}
const { type, props, key, ref } = el;
const { children } = props;
let rendered = null;
if (Array.isArray(children)) {
rendered = flatten(children, true).map(elementToTree);
if (isArrayLike(children)) {
rendered = flatten([...children], true).map(elementToTree);
} else if (typeof children !== 'undefined') {
rendered = elementToTree(children);
}
Expand Down
135 changes: 135 additions & 0 deletions packages/enzyme-test-suite/test/RSTTraversal-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,141 @@ describe('RSTTraversal', () => {
expect(spy.callCount).to.equal(4);
});

it('should handle array children', () => {
const spy = sinon.spy();
const twoDivArray = [
<div key="a" />,
<div key="b" />,
];
const divA = $(<div key="a" />);
const divB = $(<div key="b" />);
const node = $(
<div>
{twoDivArray}
</div>,
);
treeForEach(node, spy);
expect(spy.callCount).to.equal(3);
const nodes = spy.args.map(arg => arg[0]);
expect(nodes).to.deep.equal([node, divA, divB]);
});

it('should handle array siblings', () => {
const spy = sinon.spy();
const array1 = [
<div key="a" />,
<div key="b" />,
];
const array2 = [
<div key="c" />,
<div key="d" />,
];
const divA = $(<div key="a" />);
const divB = $(<div key="b" />);
const divC = $(<div key="c" />);
const divD = $(<div key="d" />);
const node = $(
<div>
{array1}
{array2}
</div>,
);
treeForEach(node, spy);
expect(spy.callCount).to.equal(5);
const nodes = spy.args.map(arg => arg[0]);
expect(nodes).to.deep.equal([node, divA, divB, divC, divD]);
});

it('should handle Map children', () => {
const spy = sinon.spy();
const twoDivMap = new Map([
[<div key="a" />],
[<div key="b" />],
]);
const divA = $(<div key="a" />);
const divB = $(<div key="b" />);
const node = $(
<div>
{twoDivMap}
</div>,
);
treeForEach(node, spy);
expect(spy.callCount).to.equal(3);
const nodes = spy.args.map(arg => arg[0]);
expect(nodes).to.deep.equal([node, divA, divB]);
});

it('should handle Map siblings', () => {
const spy = sinon.spy();
const map1 = new Map([
[<div key="a" />],
[<div key="b" />],
]);
const map2 = new Map([
[<div key="c" />],
[<div key="d" />],
]);
const divA = $(<div key="a" />);
const divB = $(<div key="b" />);
const divC = $(<div key="c" />);
const divD = $(<div key="d" />);
const node = $(
<div>
{map1}
{map2}
</div>,
);
treeForEach(node, spy);
expect(spy.callCount).to.equal(5);
const nodes = spy.args.map(arg => arg[0]);
expect(nodes).to.deep.equal([node, divA, divB, divC, divD]);
});

it('should handle Set children', () => {
const spy = sinon.spy();
const twoDivSet = new Set([
<div key="a" />,
<div key="b" />,
]);
const divA = $(<div key="a" />);
const divB = $(<div key="b" />);
const node = $(
<div>
{twoDivSet}
</div>,
);
treeForEach(node, spy);
expect(spy.callCount).to.equal(3);
const nodes = spy.args.map(arg => arg[0]);
expect(nodes).to.deep.equal([node, divA, divB]);
});

it('should handle Set siblings', () => {
const spy = sinon.spy();
const set1 = new Set([
<div key="a" />,
<div key="b" />,
]);
const set2 = new Set([
<div key="c" />,
<div key="d" />,
]);
const divA = $(<div key="a" />);
const divB = $(<div key="b" />);
const divC = $(<div key="c" />);
const divD = $(<div key="d" />);
const node = $(
<div>
{set1}
{set2}
</div>,
);
treeForEach(node, spy);
expect(spy.callCount).to.equal(5);
const nodes = spy.args.map(arg => arg[0]);
expect(nodes).to.deep.equal([node, divA, divB, divC, divD]);
});

it('should not get trapped from empty strings', () => {
const spy = sinon.spy();
const node = $(
Expand Down

0 comments on commit a1d076d

Please sign in to comment.