Skip to content

Commit

Permalink
Allow for non-Array, but Iterable, children
Browse files Browse the repository at this point in the history
  • Loading branch information
kmcq committed Oct 27, 2017
1 parent c9a0dbb commit 0fcd897
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 39 deletions.
47 changes: 8 additions & 39 deletions packages/enzyme-adapter-utils/src/Utils.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import flatten from 'lodash/flatten';
import createMountWrapper from './createMountWrapper';
import createRenderWrapper from './createRenderWrapper';
import elementToTree from './elementToTree';
import nodeTypeFromType from './nodeTypeFromType';

export { createMountWrapper, createRenderWrapper };
export {
createMountWrapper,
createRenderWrapper,
elementToTree,
nodeTypeFromType,
};

export function mapNativeEventNames(event) {
const nativeToReactEventMap = {
Expand Down Expand Up @@ -81,43 +87,6 @@ export function assertDomAvailable(feature) {
}
}

export function nodeTypeFromType(type) {
if (typeof type === 'string') {
return 'host';
}
if (
type &&
type.prototype &&
type.prototype.isReactComponent
) {
return 'class';
}
return 'function';
}

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);
} else if (typeof children !== 'undefined') {
rendered = elementToTree(children);
}
return {
nodeType: nodeTypeFromType(type),
type,
props,
key,
ref,
instance: null,
rendered,
};
}

export function propsWithKeysAndRef(node) {
if (node.ref !== null || node.key !== null) {
return {
Expand Down
73 changes: 73 additions & 0 deletions packages/enzyme-adapter-utils/src/elementToTree.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import flatten from 'lodash/flatten';
import nodeTypeFromType from './nodeTypeFromType';

const ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
const FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec.

/**
* Returns the iterator method function contained on the iterable object.
*
* Be sure to invoke the function with the iterable as context:
*
* const iteratorFn = getIteratorFn(myIterable);
* if (iteratorFn) {
* const iterator = iteratorFn.call(myIterable);
* ...
* }
*
* @param {?object} maybeIterable
* @return {?function}
*/
function getIteratorFn(maybeIterable) {
const iteratorFn = (
maybeIterable && (
(ITERATOR_SYMBOL && maybeIterable[ITERATOR_SYMBOL]) ||
maybeIterable[FAUX_ITERATOR_SYMBOL]
)
);
if (typeof iteratorFn === 'function') {
return iteratorFn;
}
return undefined;
}

export default 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);
} else if (typeof children === 'string') {
rendered = elementToTree(children);
} else {
// This allows children to be iterables that aren't Arrays,
// e.g. Immutable-js Lists, Seqs, etc.
// For more info see the relevant React pull request,
// off which this code is based:
// https://github.com/facebook/react/pull/2376
const iteratorFn = getIteratorFn(children);
if (typeof iteratorFn === 'function') {
const iterator = iteratorFn.call(children);
let step = null;
while (step && !step.done) {
const child = step.value;
rendered = elementToTree(child);
step = iterator.next();
}
} else if (typeof children !== 'undefined') {
rendered = elementToTree(children);
}
}
return {
nodeType: nodeTypeFromType(type),
type,
props,
key,
ref,
instance: null,
rendered,
};
}
13 changes: 13 additions & 0 deletions packages/enzyme-adapter-utils/src/nodeTypeFromType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export default function nodeTypeFromType(type) {
if (typeof type === 'string') {
return 'host';
}
if (
type &&
type.prototype &&
type.prototype.isReactComponent
) {
return 'class';
}
return 'function';
}
40 changes: 40 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,46 @@ 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 node = $(
<div>
{twoDivArray}
</div>,
);
treeForEach(node, spy);
expect(spy.callCount).to.equal(4);
});

it('should handle non-array iterable children', () => {
const spy = sinon.spy();
const twoDivIterable = {
'@@iterator'() {
let i = 0;
return {
next() {
i += 1;
if (i < 2) {
return { value: <div key={i} />, done: false };
}
return { value: undefined, done: true };
},
};
},
};
const node = $(
<div>
{twoDivIterable}
</div>,
);
treeForEach(node, spy);
expect(spy.callCount).to.equal(4);
});

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

0 comments on commit 0fcd897

Please sign in to comment.