Skip to content

Commit

Permalink
Merge pull request #673 from ljharb/fix_reduce
Browse files Browse the repository at this point in the history
[Fix] `reduce`/`reduceRight`: follow `Array#reduce` when omitting initialValue
  • Loading branch information
ljharb authored Sep 1, 2017
2 parents aa8abc6 + 0c4dc58 commit d16c8ed
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 28 deletions.
3 changes: 1 addition & 2 deletions docs/api/ReactWrapper/reduce.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ following arguments:
- `node` (`ReactWrapper`): A wrapper around the current node being processed
- `index` (`Number`): The index of the current node being processed

2. `initialValue` (`T` [optional]): If provided, this will be passed in as the first argument to the
first invocation of the reducing function.
2. `initialValue` (`T` [optional]): If provided, this will be passed in as the first argument to the first invocation of the reducing function. If omitted, the first `node` will be provided and the iteration will begin on the second node in the collection.



Expand Down
3 changes: 1 addition & 2 deletions docs/api/ReactWrapper/reduceRight.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ following arguments:
- `node` (`ReactWrapper`): A wrapper around the current node being processed
- `index` (`Number`): The index of the current node being processed

2. `initialValue` (`T` [optional]): If provided, this will be passed in as the first argument to the
first invocation of the reducing function.
2. `initialValue` (`T` [optional]): If provided, this will be passed in as the first argument to the first invocation of the reducing function. If omitted, the first `node` will be provided and the iteration will begin on the second node in the collection.



Expand Down
3 changes: 1 addition & 2 deletions docs/api/ShallowWrapper/reduce.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ following arguments:
- `node` (`ShallowWrapper`): A wrapper around the current node being processed
- `index` (`Number`): The index of the current node being processed

2. `initialValue` (`T` [optional]): If provided, this will be passed in as the first argument to the
first invocation of the reducing function.
2. `initialValue` (`T` [optional]): If provided, this will be passed in as the first argument to the first invocation of the reducing function. If omitted, the first `node` will be provided and the iteration will begin on the second node in the collection.



Expand Down
3 changes: 1 addition & 2 deletions docs/api/ShallowWrapper/reduceRight.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ following arguments:
- `node` (`ShallowWrapper`): A wrapper around the current node being processed
- `index` (`Number`): The index of the current node being processed

2. `initialValue` (`T` [optional]): If provided, this will be passed in as the first argument to the
first invocation of the reducing function.
2. `initialValue` (`T` [optional]): If provided, this will be passed in as the first argument to the first invocation of the reducing function. If omitted, the first `node` will be provided and the iteration will begin on the second node in the collection.



Expand Down
52 changes: 52 additions & 0 deletions packages/enzyme-test-suite/test/ReactWrapper-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import {
import { ITERATOR_SYMBOL, sym } from 'enzyme/build/Utils';
import { REACT013, REACT014, REACT16, is } from './_helpers/version';

const getElementPropSelector = prop => x => x.props[prop];
const getWrapperPropSelector = prop => x => x.prop(prop);

describeWithDOM('mount', () => {
describe('top level wrapper', () => {
it('does what i expect', () => {
Expand Down Expand Up @@ -2348,6 +2351,10 @@ describeWithDOM('mount', () => {
});

describe('.reduce(fn[, initialValue])', () => {
it('has the right length', () => {
expect(ReactWrapper.prototype.reduce).to.have.lengthOf(1);
});

it('should call a function with a wrapper for each node in the wrapper', () => {
const wrapper = mount(
<div>
Expand Down Expand Up @@ -2388,9 +2395,33 @@ describeWithDOM('mount', () => {
baz: 'foo hoo',
});
});

it('allows the initialValue to be omitted', () => {
const one = (<div id="bax" className="foo qoo" />);
const two = (<div id="bar" className="foo boo" />);
const three = (<div id="baz" className="foo hoo" />);
const wrapper = mount(
<div>
{one}
{two}
{three}
</div>,
);
const counter = (<noscript id="counter" />);
const result = wrapper
.find('.foo')
.reduce((acc, n) => [].concat(acc, n, new ReactWrapper(counter)))
.map(getWrapperPropSelector('id'));

expect(result).to.eql([one, two, counter, three, counter].map(getElementPropSelector('id')));
});
});

describe('.reduceRight(fn[, initialValue])', () => {
it('has the right length', () => {
expect(ReactWrapper.prototype.reduceRight).to.have.lengthOf(1);
});

it('should call a function with a wrapper for each node in the wrapper in reverse', () => {
const wrapper = mount(
<div>
Expand Down Expand Up @@ -2431,6 +2462,27 @@ describeWithDOM('mount', () => {
baz: 'foo hoo',
});
});

it('allows the initialValue to be omitted', () => {
const one = (<div id="bax" className="foo qoo" />);
const two = (<div id="bar" className="foo boo" />);
const three = (<div id="baz" className="foo hoo" />);
const wrapper = mount(
<div>
{one}
{two}
{three}
</div>,
);

const counter = (<noscript id="counter" />);
const result = wrapper
.find('.foo')
.reduceRight((acc, n) => [].concat(acc, n, new ReactWrapper(counter)))
.map(getWrapperPropSelector('id'));

expect(result).to.eql([three, two, counter, one, counter].map(getElementPropSelector('id')));
});
});

describe('.slice([begin[, end]])', () => {
Expand Down
51 changes: 51 additions & 0 deletions packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import { REACT013, REACT014, REACT16, is } from './_helpers/version';
// we should be able to go un-skip all of the tests that are skipped with this flag.
const BATCHING = !REACT16;

const getElementPropSelector = prop => x => x.props[prop];
const getWrapperPropSelector = prop => x => x.prop(prop);

describe('shallow', () => {
describe('top level wrapper', () => {
it('does what i expect', () => {
Expand Down Expand Up @@ -2131,6 +2134,10 @@ describe('shallow', () => {
});

describe('.reduce(fn[, initialValue])', () => {
it('has the right length', () => {
expect(ShallowWrapper.prototype.reduce).to.have.lengthOf(1);
});

it('should call a function with a wrapper for each node in the wrapper', () => {
const wrapper = shallow(
<div>
Expand Down Expand Up @@ -2174,9 +2181,33 @@ describe('shallow', () => {
baz: 'foo hoo',
});
});

it('allows the initialValue to be omitted', () => {
const one = (<div id="bax" className="foo qoo" />);
const two = (<div id="bar" className="foo boo" />);
const three = (<div id="baz" className="foo hoo" />);
const wrapper = shallow(
<div>
{one}
{two}
{three}
</div>,
);
const counter = (<noscript id="counter" />);
const result = wrapper
.find('.foo')
.reduce((acc, n) => [].concat(acc, n, new ShallowWrapper(counter)))
.map(getWrapperPropSelector('id'));

expect(result).to.eql([one, two, counter, three, counter].map(getElementPropSelector('id')));
});
});

describe('.reduceRight(fn[, initialValue])', () => {
it('has the right length', () => {
expect(ShallowWrapper.prototype.reduceRight).to.have.lengthOf(1);
});

it('should call a function with a wrapper for each node in the wrapper in reverse', () => {
const wrapper = shallow(
<div>
Expand Down Expand Up @@ -2220,6 +2251,26 @@ describe('shallow', () => {
baz: 'foo hoo',
});
});

it('allows the initialValue to be omitted', () => {
const one = (<div id="bax" className="foo qoo" />);
const two = (<div id="bar" className="foo boo" />);
const three = (<div id="baz" className="foo hoo" />);
const wrapper = shallow(
<div>
{one}
{two}
{three}
</div>,
);
const counter = (<noscript id="counter" />);
const result = wrapper
.find('.foo')
.reduceRight((acc, n) => [].concat(acc, n, new ShallowWrapper(counter)))
.map(getWrapperPropSelector('id'));

expect(result).to.eql([three, two, counter, one, counter].map(getElementPropSelector('id')));
});
});

describe('.slice([begin[, end]])', () => {
Expand Down
36 changes: 26 additions & 10 deletions packages/enzyme/src/ReactWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -783,11 +783,19 @@ class ReactWrapper {
* @param {*} initialValue - the initial value
* @returns {*}
*/
reduce(fn, initialValue) {
return this.getNodesInternal().reduce(
(accum, n, i) => fn.call(this, accum, this.wrap(n), i),
initialValue,
);
reduce(fn, initialValue = undefined) {
if (arguments.length > 1) {
return this.getNodesInternal().reduce(
(accum, n, i) => fn.call(this, accum, this.wrap(n), i),
initialValue,
);
}
return this.getNodesInternal().reduce((accum, n, i) => fn.call(
this,
i === 1 ? this.wrap(accum) : accum,
this.wrap(n),
i,
));
}

/**
Expand All @@ -798,11 +806,19 @@ class ReactWrapper {
* @param {*} initialValue - the initial value
* @returns {*}
*/
reduceRight(fn, initialValue) {
return this.getNodesInternal().reduceRight(
(accum, n, i) => fn.call(this, accum, this.wrap(n), i),
initialValue,
);
reduceRight(fn, initialValue = undefined) {
if (arguments.length > 1) {
return this.getNodesInternal().reduceRight(
(accum, n, i) => fn.call(this, accum, this.wrap(n), i),
initialValue,
);
}
return this.getNodesInternal().reduceRight((accum, n, i) => fn.call(
this,
i === 1 ? this.wrap(accum) : accum,
this.wrap(n),
i,
));
}

/**
Expand Down
36 changes: 26 additions & 10 deletions packages/enzyme/src/ShallowWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -892,11 +892,19 @@ class ShallowWrapper {
* @param {*} initialValue - the initial value
* @returns {*}
*/
reduce(fn, initialValue) {
return this.getNodesInternal().reduce(
(accum, n, i) => fn.call(this, accum, this.wrap(n), i),
initialValue,
);
reduce(fn, initialValue = undefined) {
if (arguments.length > 1) {
return this.getNodesInternal().reduce(
(accum, n, i) => fn.call(this, accum, this.wrap(n), i),
initialValue,
);
}
return this.getNodesInternal().reduce((accum, n, i) => fn.call(
this,
i === 1 ? this.wrap(accum) : accum,
this.wrap(n),
i,
));
}

/**
Expand All @@ -907,11 +915,19 @@ class ShallowWrapper {
* @param {*} initialValue - the initial value
* @returns {*}
*/
reduceRight(fn, initialValue) {
return this.getNodesInternal().reduceRight(
(accum, n, i) => fn.call(this, accum, this.wrap(n), i),
initialValue,
);
reduceRight(fn, initialValue = undefined) {
if (arguments.length > 1) {
return this.getNodesInternal().reduceRight(
(accum, n, i) => fn.call(this, accum, this.wrap(n), i),
initialValue,
);
}
return this.getNodesInternal().reduceRight((accum, n, i) => fn.call(
this,
i === 1 ? this.wrap(accum) : accum,
this.wrap(n),
i,
));
}

/**
Expand Down

0 comments on commit d16c8ed

Please sign in to comment.