Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make ReactWrapper and ShallowWrapper iterable #594

Merged
merged 5 commits into from
Oct 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion src/ReactWrapper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
propsOfNode,
typeOfNode,
displayNameOfNode,
ITERATOR_SYMBOL,
} from './Utils';
import {
debugInsts,
Expand Down Expand Up @@ -62,7 +63,7 @@ function filterWhereUnwrapped(wrapper, predicate) {
/**
* @class ReactWrapper
*/
export default class ReactWrapper {
class ReactWrapper {

constructor(nodes, root, options = {}) {
if (!global.window && !global.document) {
Expand Down Expand Up @@ -908,3 +909,15 @@ export default class ReactWrapper {
unmountComponentAtNode(this.options.attachTo);
}
}


if (ITERATOR_SYMBOL) {
Object.defineProperty(ReactWrapper.prototype, ITERATOR_SYMBOL, {
configurable: true,
value: function iterator() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

configurable defaults to false, so we need to explicitly make it true here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious why would we want it to be configurable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's just no reason to lock it down - someone might want to override it for their project, or something.

return this.nodes[ITERATOR_SYMBOL]();
},
});
}

export default ReactWrapper;
14 changes: 13 additions & 1 deletion src/ShallowWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
displayNameOfNode,
isFunctionalComponent,
isCustomComponentElement,
ITERATOR_SYMBOL,
} from './Utils';
import {
debugNodes,
Expand Down Expand Up @@ -63,7 +64,7 @@ function filterWhereUnwrapped(wrapper, predicate) {
/**
* @class ShallowWrapper
*/
export default class ShallowWrapper {
class ShallowWrapper {

constructor(nodes, root, options = {}) {
if (!root) {
Expand Down Expand Up @@ -982,3 +983,14 @@ export default class ShallowWrapper {
});
}
}

if (ITERATOR_SYMBOL) {
Object.defineProperty(ShallowWrapper.prototype, ITERATOR_SYMBOL, {
configurable: true,
value: function iterator() {
return this.nodes[ITERATOR_SYMBOL]();
},
});
}

export default ShallowWrapper;
2 changes: 2 additions & 0 deletions src/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
REACT15,
} from './version';

export const ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typeof Symbol.iterator === 'symbol'?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't that exclude environments where Symbol is polyfilled?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's true, because Symbols can't be polyfilled (because of typeof) and Symbol.for - ie, it'd not be a shim, but a sham.

I suppose I don't need to make it a sticking point here, but I'd prefer a more robust test than truthiness - for now this is fine, but I'll extract https://github.com/ljharb/object.assign/blob/master/hasSymbols.js out to a module or two and we can use that instead :-)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a great module to put out there!

As far as the tests go, I did consciously chose to test the actual public API that this provides, rather then testing the internal implementation. Though more robust tests are rarely a bad thing! I can add some more tests now that verify the iterator method is there, and only there when Symbol.iterator is defined.

Otherwise, I can make sure to add tests using hasSymbols once we can use it 😄


function internalInstanceKey(node) {
return Object.keys(Object(node)).filter(key => key.match(/^__reactInternalInstance\$/))[0];
}
Expand Down
28 changes: 28 additions & 0 deletions test/ReactWrapper-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
render,
ReactWrapper,
} from '../src';
import { ITERATOR_SYMBOL } from '../src/Utils';
import { REACT013, REACT014, REACT15 } from '../src/version';

describeWithDOM('mount', () => {
Expand Down Expand Up @@ -3024,4 +3025,31 @@ describeWithDOM('mount', () => {
});
});
});

describeIf(ITERATOR_SYMBOL, '@@iterator', () => {
it('should be iterable', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these tests should be skipped when the environment lacks Symbols.

class Foo extends React.Component {
render() {
return (
<div>
<a href="#1">Hello</a>
<a href="#2">Hello</a>
<a href="#3">Hello</a>
<a href="#4">Hello</a>
</div>
);
}
}
const wrapper = mount(<Foo />);
const [a, b, c, d] = wrapper.find('a');
const a1 = wrapper.find('a').get(0);
const b1 = wrapper.find('a').get(1);
const c1 = wrapper.find('a').get(2);
const d1 = wrapper.find('a').get(3);
expect(a1).to.equal(a);
expect(b1).to.equal(b);
expect(c1).to.equal(c);
expect(d1).to.equal(d);
});
});
});
28 changes: 28 additions & 0 deletions test/ShallowWrapper-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import sinon from 'sinon';

import { shallow, render, ShallowWrapper } from '../src/';
import { describeIf, itIf, itWithData, generateEmptyRenderData } from './_helpers';
import { ITERATOR_SYMBOL } from '../src/Utils';
import { REACT013, REACT15 } from '../src/version';

describe('shallow', () => {
Expand Down Expand Up @@ -3626,4 +3627,31 @@ describe('shallow', () => {
expect(underwater.is(RendersDOM)).to.equal(true);
});
});

describeIf(ITERATOR_SYMBOL, '@@iterator', () => {
it('should be iterable', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these tests should be skipped when the environment lacks Symbols.

class Foo extends React.Component {
render() {
return (
<div>
<a href="#1">Hello</a>
<a href="#2">Hello</a>
<a href="#3">Hello</a>
<a href="#4">Hello</a>
</div>
);
}
}
const wrapper = shallow(<Foo />);
const [a, b, c, d] = wrapper.find('a');
const a1 = wrapper.find('a').get(0);
const b1 = wrapper.find('a').get(1);
const c1 = wrapper.find('a').get(2);
const d1 = wrapper.find('a').get(3);
expect(a1).to.equal(a);
expect(b1).to.equal(b);
expect(c1).to.equal(c);
expect(d1).to.equal(d);
});
});
});