177 lines
4.6 KiB
Plaintext
177 lines
4.6 KiB
Plaintext
|
var hasStrictMode = require('has-strict-mode')();
|
||
|
var global = require('globalthis')();
|
||
|
var v = require('es-value-fixtures');
|
||
|
var forEach = require('for-each');
|
||
|
|
||
|
var truthy = function () { return true; };
|
||
|
var oddIndexes = function (x, i) { return i % 2 !== 0; };
|
||
|
|
||
|
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;
|
||
|
};
|
||
|
|
||
|
var getTestArr = function () {
|
||
|
var arr = [2, 3, 4, undefinedIfNoSparseBug, true, 'hej', null, false, 0];
|
||
|
delete arr[1];
|
||
|
return arr;
|
||
|
};
|
||
|
|
||
|
module.exports = function (filter, t) {
|
||
|
forEach(v.nonFunctions, function (nonFunction) {
|
||
|
t['throws'](
|
||
|
function () { filter([], nonFunction); },
|
||
|
TypeError,
|
||
|
'throws when a non-function is provided'
|
||
|
);
|
||
|
});
|
||
|
|
||
|
t.test('does not change the array it is called on', function (st) {
|
||
|
var arr = getTestArr();
|
||
|
var copy = getTestArr();
|
||
|
filter(arr, truthy);
|
||
|
st.deepEqual(arr, copy, 'array');
|
||
|
|
||
|
var arrayLike = createArrayLikeFromArray(arr);
|
||
|
filter(arrayLike, truthy);
|
||
|
st.deepEqual(arrayLike, createArrayLikeFromArray(copy), 'arrayLike');
|
||
|
|
||
|
st.end();
|
||
|
});
|
||
|
|
||
|
t.test('properly filters according to the callback', function (st) {
|
||
|
var expected = [undefinedIfNoSparseBug, 'hej', false];
|
||
|
|
||
|
var result = filter(getTestArr(), oddIndexes);
|
||
|
st.deepEqual(result, expected, 'array');
|
||
|
|
||
|
var arrayLikeResult = filter(createArrayLikeFromArray(getTestArr()), oddIndexes);
|
||
|
st.deepEqual(arrayLikeResult, expected, 'arrayLike');
|
||
|
|
||
|
st.end();
|
||
|
});
|
||
|
|
||
|
t.test('skips non-existing values', function (st) {
|
||
|
var array = [1, 2, 3, 4];
|
||
|
var arrayLike = createArrayLikeFromArray([1, 2, 3, 4]);
|
||
|
delete array[2];
|
||
|
delete arrayLike[2];
|
||
|
|
||
|
var i = 0;
|
||
|
filter(array, function () { i += 1; });
|
||
|
st.equal(i, 3, 'array');
|
||
|
|
||
|
i = 0;
|
||
|
filter(arrayLike, function () { i += 1; });
|
||
|
st.equal(i, 3, 'arrayLike');
|
||
|
|
||
|
st.end();
|
||
|
});
|
||
|
|
||
|
t.test('passes the correct values to the callback', function (st) {
|
||
|
st.plan(5);
|
||
|
|
||
|
var expectedValue = {};
|
||
|
var arr = [expectedValue];
|
||
|
var context = {};
|
||
|
filter(
|
||
|
arr,
|
||
|
function (value, key, list) {
|
||
|
st.equal(arguments.length, 3);
|
||
|
st.equal(value, expectedValue, 'first argument is the value');
|
||
|
st.equal(key, 0, 'second argument is the index');
|
||
|
st.equal(list, arr, 'third argument is the array being iterated');
|
||
|
st.equal(this, context, 'receiver is the expected value');
|
||
|
},
|
||
|
context
|
||
|
);
|
||
|
|
||
|
st.end();
|
||
|
});
|
||
|
|
||
|
t.test('does not visit elements added to the array after it has begun', function (st) {
|
||
|
st.plan(4);
|
||
|
|
||
|
var arr = [1, 2, 3];
|
||
|
var i = 0;
|
||
|
filter(arr, function (a) {
|
||
|
i += 1;
|
||
|
arr.push(a + 3);
|
||
|
});
|
||
|
st.deepEqual(arr, [1, 2, 3, 4, 5, 6], 'array has received 3 new elements');
|
||
|
st.equal(i, 3, 'filter callback only called thrice');
|
||
|
|
||
|
var arrayLike = createArrayLikeFromArray([1, 2, 3]);
|
||
|
i = 0;
|
||
|
filter(arrayLike, function (a) {
|
||
|
i += 1;
|
||
|
arrayLike[arrayLike.length] = a + 3;
|
||
|
arrayLike.length += 1;
|
||
|
});
|
||
|
st.deepEqual(Array.prototype.slice.call(arrayLike), [1, 2, 3, 4, 5, 6], 'arrayLike has received 3 new elements');
|
||
|
st.equal(i, 3, 'filter callback only called thrice');
|
||
|
|
||
|
st.end();
|
||
|
});
|
||
|
|
||
|
t.test('does not visit elements deleted from the array after it has begun', function (st) {
|
||
|
var arr = [1, 2, 3];
|
||
|
var actual = [];
|
||
|
filter(arr, function (x, i) {
|
||
|
actual.push([i, x]);
|
||
|
delete arr[1];
|
||
|
});
|
||
|
st.deepEqual(actual, [[0, 1], [2, 3]]);
|
||
|
|
||
|
st.end();
|
||
|
});
|
||
|
|
||
|
t.test('sets the right context when given none', function (st) {
|
||
|
var context;
|
||
|
filter([1], function () { context = this; });
|
||
|
st.equal(context, global, 'receiver is global object in sloppy mode');
|
||
|
|
||
|
st.test('strict mode', { skip: !hasStrictMode }, function (sst) {
|
||
|
filter([1], function () {
|
||
|
'use strict';
|
||
|
|
||
|
context = this;
|
||
|
});
|
||
|
sst.equal(context, undefined, 'receiver is undefined in strict mode');
|
||
|
sst.end();
|
||
|
});
|
||
|
|
||
|
st.end();
|
||
|
});
|
||
|
|
||
|
t.test('empty array', function (st) {
|
||
|
var arr = [];
|
||
|
var actual = filter(arr, truthy);
|
||
|
st.notEqual(actual, arr, 'empty array returns !== array');
|
||
|
st.deepEqual(actual, arr, 'empty array returns empty array');
|
||
|
|
||
|
st.end();
|
||
|
});
|
||
|
|
||
|
t.test('list arg boxing', function (st) {
|
||
|
st.plan(3);
|
||
|
|
||
|
filter('f', function (item, index, list) {
|
||
|
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');
|
||
|
});
|
||
|
|
||
|
st.end();
|
||
|
});
|
||
|
};
|