var hasStrictMode = require('has-strict-mode')();
|
|
var global = Function('return this')(); // eslint-disable-line no-new-func
|
var identity = function (x) { return x; };
|
|
var canDistinguishSparseFromUndefined = 0 in [undefined]; // IE 6 - 8 have a bug where this returns false.
|
var undefinedIfNoSparseBug = canDistinguishSparseFromUndefined ? undefined : { valueOf: function () { return 0; } };
|
|
var createArrayLikeFromArray = function createArrayLike(arr) {
|
var o = {};
|
for (var i = 0; i < arr.length; i += 1) {
|
if (i in arr) {
|
o[i] = arr[i];
|
}
|
}
|
o.length = arr.length;
|
return o;
|
};
|
|
module.exports = function (reduce, t) {
|
t.test('passes the correct values to the callback', function (st) {
|
st.plan(7);
|
|
var expectedValue = {};
|
var initialValue = {};
|
var expectedResult = {};
|
var arr = [expectedValue];
|
var result = reduce(
|
arr,
|
function (accumulator, value, key, list) {
|
st.equal(arguments.length, 4);
|
st.equal(accumulator, initialValue, 'first argument is the accumulator');
|
st.equal(value, expectedValue, 'second argument is the value');
|
st.equal(key, 0, 'third argument is the index');
|
st.equal(list, arr, 'fourth argument is the array being iterated');
|
st.equal(this, global, 'sloppy: receiver is the expected value');
|
|
return expectedResult;
|
},
|
initialValue
|
);
|
st.equal(result, expectedResult, 'result is last return value of accumulator');
|
|
st.end();
|
});
|
|
t.test('strict mode callback receiver', { skip: !hasStrictMode }, function (st) {
|
reduce(
|
[null],
|
function () {
|
'use strict';
|
|
st.equal(this, undefined, 'strict: receiver is the expected value');
|
}
|
);
|
|
st.end();
|
});
|
|
t.test('starts with the right initialValue', function (st) {
|
var firstValue = {};
|
var secondValue = {};
|
|
reduce(
|
[firstValue, secondValue],
|
function (accumulator, value) {
|
st.equal(accumulator, firstValue, 'accumulator starts out as the first value when initialValue is omitted');
|
st.equal(value, secondValue, 'value starts out as the second value when initialValue is omitted');
|
}
|
);
|
|
reduce(
|
[secondValue],
|
function (accumulator, value) {
|
st.equal(accumulator, firstValue, 'accumulator starts out as the initialValue when provided');
|
st.equal(value, secondValue, 'value starts out as the first value when initialValue is provided');
|
},
|
firstValue
|
);
|
|
st.end();
|
});
|
|
t.test('does not visit elements added to the array after it has begun', function (st) {
|
st.plan(8);
|
|
var arr = [1, 2, 3];
|
var i = 0;
|
reduce(arr, function (acc, v) {
|
i += 1;
|
arr.push(v + 3);
|
});
|
st.deepEqual(arr, [1, 2, 3, 5, 6], 'array has received 3 new elements. initialValue omitted');
|
st.equal(i, 2, 'reduce callback only called twice');
|
|
i = 0;
|
arr = [1, 2, 3];
|
reduce(
|
arr,
|
function (acc, v) {
|
i += 1;
|
arr.push(v + 3);
|
},
|
null
|
);
|
st.deepEqual(arr, [1, 2, 3, 4, 5, 6], 'array has received 3 new elements. initialValue provided');
|
st.equal(i, 3, 'reduce callback only called thrice');
|
|
var arrayLike = createArrayLikeFromArray([1, 2, 3]);
|
i = 0;
|
reduce(arrayLike, function (acc, v) {
|
i += 1;
|
arrayLike[arrayLike.length] = v + 3;
|
arrayLike.length += 1;
|
});
|
st.deepEqual(Array.prototype.slice.call(arrayLike), [1, 2, 3, 5, 6], 'arrayLike has received 3 new elements. initialValue omitted');
|
st.equal(i, 2, 'reduce callback only called twice');
|
|
arrayLike = createArrayLikeFromArray([1, 2, 3]);
|
i = 0;
|
reduce(
|
arrayLike,
|
function (acc, v) {
|
i += 1;
|
arrayLike[arrayLike.length] = v + 3;
|
arrayLike.length += 1;
|
},
|
null
|
);
|
st.deepEqual(Array.prototype.slice.call(arrayLike), [1, 2, 3, 4, 5, 6], 'arrayLike has received 3 new elements. initialValue provided');
|
st.equal(i, 3, 'reduce callback only called thrice');
|
|
st.end();
|
});
|
|
t.test('empty array', function (st) {
|
var initialValue = {};
|
var actual = reduce([], identity, initialValue);
|
st.equal(actual, initialValue, 'empty array returns callback return');
|
|
st['throws'](
|
function () { reduce([], identity); },
|
TypeError,
|
'empty array with omitted initialValue throws'
|
);
|
|
var sparse = Array(10);
|
st['throws'](
|
function () { reduce(sparse, identity); },
|
TypeError,
|
'only-holes array with omitted initialValue throws (test262: 15.4.4.21-8-c-1)'
|
);
|
|
st.end();
|
});
|
|
t.test('skips holes', function (st) {
|
var arr = [1, undefinedIfNoSparseBug, 3];
|
var visited = {};
|
reduce(
|
arr,
|
function (a, b) {
|
if (a) { visited[a] = true; }
|
if (b) { visited[b] = true; }
|
return 0;
|
},
|
null
|
);
|
st.deepEqual(visited, { 1: true, 3: true }, 'only non-holes are visited; initialValue provided');
|
|
visited = {};
|
reduce(
|
arr,
|
function (a, b) {
|
if (a) { visited[a] = true; }
|
if (b) { visited[b] = true; }
|
return 0;
|
}
|
);
|
st.deepEqual(visited, { 1: true, 3: true }, 'only non-holes are visited; initialValue omitted');
|
|
st.end();
|
});
|
|
t.test('list arg boxing', function (st) {
|
st.plan(4);
|
|
reduce(
|
'f',
|
function (acc, item, index, list) {
|
st.equal(acc, null, 'accumulator matches');
|
st.equal(item, 'f', 'letter matches');
|
st.equal(typeof list, 'object', 'primitive list arg is boxed');
|
st.equal(Object.prototype.toString.call(list), '[object String]', 'boxed list arg is a String');
|
},
|
null
|
);
|
|
st.end();
|
});
|
|
t.test('test262: 15.4.4.21-3-12', function (st) {
|
var obj = {
|
1: 11,
|
2: 9,
|
length: '-4294967294'
|
};
|
|
var cb = function callbackfn(prevVal, curVal, idx, object) {
|
st.equal(object, obj, '4th argument is receiver');
|
return curVal === 11 && idx === 1;
|
};
|
|
st.equal(reduce(obj, cb, 1), 1, 'reduce(obj, callbackfn, 1)');
|
|
st.end();
|
});
|
};
|