Update deps.

This commit is contained in:
John-David Dalton
2014-08-26 23:55:39 -07:00
parent c4a7f899db
commit baee6b9738
17 changed files with 1206 additions and 995 deletions

View File

@@ -8,35 +8,51 @@
deepEqual(_.first([1, 2, 3], 0), [], 'can pass an index to first');
deepEqual(_.first([1, 2, 3], 2), [1, 2], 'can pass an index to first');
deepEqual(_.first([1, 2, 3], 5), [1, 2, 3], 'can pass an index to first');
var result = (function(){ return _.first(arguments); })(4, 3, 2, 1);
var result = (function(){ return _.first(arguments); }(4, 3, 2, 1));
equal(result, 4, 'works on an arguments object.');
result = _.map([[1, 2, 3], [1, 2, 3]], _.first);
deepEqual(result, [1, 1], 'works well with _.map');
result = (function() { return _.take([1, 2, 3], 2); })();
deepEqual(result, [1, 2], 'aliased as take');
result = (function() { return _.first([1, 2, 3], 2); }());
deepEqual(result, [1, 2]);
equal(_.first(null), undefined, 'handles nulls');
strictEqual(_.first([1, 2, 3], -1).length, 0);
});
test('head', function() {
strictEqual(_.first, _.head, 'alias for first');
});
test('take', function() {
strictEqual(_.first, _.take, 'alias for first');
});
test('rest', function() {
var numbers = [1, 2, 3, 4];
deepEqual(_.rest(numbers), [2, 3, 4], 'working rest()');
deepEqual(_.rest(numbers, 0), [1, 2, 3, 4], 'working rest(0)');
deepEqual(_.rest(numbers, 2), [3, 4], 'rest can take an index');
var result = (function(){ return _(arguments).tail(); })(1, 2, 3, 4);
deepEqual(result, [2, 3, 4], 'aliased as tail and works on arguments object');
var result = (function(){ return _(arguments).rest(); }(1, 2, 3, 4));
deepEqual(result, [2, 3, 4], 'works on arguments object');
result = _.map([[1, 2, 3], [1, 2, 3]], _.rest);
deepEqual(_.flatten(result), [2, 3, 2, 3], 'works well with _.map');
result = (function(){ return _(arguments).drop(); })(1, 2, 3, 4);
deepEqual(result, [2, 3, 4], 'aliased as drop and works on arguments object');
result = (function(){ return _(arguments).rest(); }(1, 2, 3, 4));
deepEqual(result, [2, 3, 4], 'works on arguments object');
});
test('tail', function() {
strictEqual(_.rest, _.tail, 'alias for rest');
});
test('drop', function() {
strictEqual(_.rest, _.drop, 'alias for rest');
});
test('initial', function() {
deepEqual(_.initial([1, 2, 3, 4, 5]), [1, 2, 3, 4], 'working initial()');
deepEqual(_.initial([1, 2, 3, 4], 2), [1, 2], 'initial can take an index');
deepEqual(_.initial([1, 2, 3, 4], 6), [], 'initial can take a large index');
var result = (function(){ return _(arguments).initial(); })(1, 2, 3, 4);
var result = (function(){ return _(arguments).initial(); }(1, 2, 3, 4));
deepEqual(result, [1, 2, 3], 'initial works on arguments object');
result = _.map([[1, 2, 3], [1, 2, 3]], _.initial);
deepEqual(_.flatten(result), [1, 2, 1, 2], 'initial works with _.map');
@@ -47,7 +63,7 @@
deepEqual(_.last([1, 2, 3], 0), [], 'can pass an index to last');
deepEqual(_.last([1, 2, 3], 2), [2, 3], 'can pass an index to last');
deepEqual(_.last([1, 2, 3], 5), [1, 2, 3], 'can pass an index to last');
var result = (function(){ return _(arguments).last(); })(1, 2, 3, 4);
var result = (function(){ return _(arguments).last(); }(1, 2, 3, 4));
equal(result, 4, 'works on an arguments object');
result = _.map([[1, 2, 3], [1, 2, 3]], _.last);
deepEqual(result, [3, 3], 'works well with _.map');
@@ -58,7 +74,7 @@
test('compact', function() {
equal(_.compact([0, 1, false, 2, false, 3]).length, 3, 'can trim out all falsy values');
var result = (function(){ return _.compact(arguments).length; })(0, 1, false, 2, false, 3);
var result = (function(){ return _.compact(arguments).length; }(0, 1, false, 2, false, 3));
equal(result, 3, 'works on an arguments object');
});
@@ -66,7 +82,7 @@
var list = [1, [2], [3, [[[4]]]]];
deepEqual(_.flatten(list), [1, 2, 3, 4], 'can flatten nested arrays');
deepEqual(_.flatten(list, true), [1, 2, 3, [[[4]]]], 'can shallowly flatten nested arrays');
var result = (function(){ return _.flatten(arguments); })(1, [2], [3, [[[4]]]]);
var result = (function(){ return _.flatten(arguments); }(1, [2], [3, [[[4]]]]));
deepEqual(result, [1, 2, 3, 4], 'works on an arguments object');
list = [[1], [2], [3], [[4]]];
deepEqual(_.flatten(list, true), [1, 2, 3, [4]], 'can shallowly flatten arrays containing only other arrays');
@@ -75,12 +91,12 @@
test('without', function() {
var list = [1, 2, 1, 0, 3, 1, 4];
deepEqual(_.without(list, 0, 1), [2, 3, 4], 'can remove all instances of an object');
var result = (function(){ return _.without(arguments, 0, 1); })(1, 2, 1, 0, 3, 1, 4);
var result = (function(){ return _.without(arguments, 0, 1); }(1, 2, 1, 0, 3, 1, 4));
deepEqual(result, [2, 3, 4], 'works on an arguments object');
list = [{one : 1}, {two : 2}];
ok(_.without(list, {one : 1}).length == 2, 'uses real object identity for comparisons.');
ok(_.without(list, list[0]).length == 1, 'ditto.');
equal(_.without(list, {one : 1}).length, 2, 'uses real object identity for comparisons.');
equal(_.without(list, list[0]).length, 1, 'ditto.');
});
test('uniq', function() {
@@ -96,13 +112,16 @@
deepEqual(_.map(_.uniq(list, iterator), iterator), ['moe', 'curly', 'larry'], 'can find the unique values of an array using a custom iterator without specifying whether array is sorted');
iterator = function(value) { return value +1; };
iterator = function(value) { return value + 1; };
list = [1, 2, 2, 3, 4, 4];
deepEqual(_.uniq(list, true, iterator), [1, 2, 3, 4], 'iterator works with sorted array');
var result = (function(){ return _.uniq(arguments); })(1, 2, 1, 3, 1, 4);
var result = (function(){ return _.uniq(arguments); }(1, 2, 1, 3, 1, 4));
deepEqual(result, [1, 2, 3, 4], 'works on an arguments object');
var a = {}, b = {}, c = {};
deepEqual(_.uniq([a, b, a, b, c]), [a, b, c], 'works on values that can be tested for equivalency but not ordered');
deepEqual(_.uniq(null), []);
var context = {};
@@ -111,14 +130,22 @@
strictEqual(this, context);
strictEqual(value, 3);
strictEqual(index, 0);
strictEqual(array, list);
}, context);
deepEqual(_.uniq([{a: 1, b: 1}, {a: 1, b: 2}, {a: 1, b: 3}, {a: 2, b: 1}], 'a'), [{a: 1, b: 1}, {a: 2, b: 1}], 'can use pluck like iterator');
deepEqual(_.uniq([{0: 1, b: 1}, {0: 1, b: 2}, {0: 1, b: 3}, {0: 2, b: 1}], 0), [{0: 1, b: 1}, {0: 2, b: 1}], 'can use falsey pluck like iterator');
});
test('unique', function() {
strictEqual(_.uniq, _.unique, 'alias for uniq');
});
test('intersection', function() {
var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho'];
deepEqual(_.intersection(stooges, leaders), ['moe'], 'can take the set intersection of two arrays');
deepEqual(_(stooges).intersection(leaders), ['moe'], 'can perform an OO-style intersection');
var result = (function(){ return _.intersection(arguments, leaders); })('moe', 'curly', 'larry');
var result = (function(){ return _.intersection(arguments, leaders); }('moe', 'curly', 'larry'));
deepEqual(result, ['moe'], 'works on an arguments object');
var theSixStooges = ['moe', 'moe', 'curly', 'curly', 'larry', 'larry'];
deepEqual(_.intersection(theSixStooges, leaders), ['moe'], 'returns a duplicate-free array');
@@ -140,7 +167,7 @@
deepEqual(result, [1, 2, 3, 30, 40, [1]], 'takes the union of a list of nested arrays');
var args = null;
(function(){ args = arguments; })(1, 2, 3);
(function(){ args = arguments; }(1, 2, 3));
result = _.union(args, [2, 30, 1], [1, 40]);
deepEqual(result, [1, 2, 3, 30, 40], 'takes the union of a list of arrays');
@@ -177,6 +204,9 @@
var empty = _.zip([]);
deepEqual(empty, [], 'unzipped empty');
deepEqual(_.zip(null), [], 'handles null');
deepEqual(_.zip(), [], '_.zip() returns []');
});
test('object', function() {
@@ -196,9 +226,8 @@
test('indexOf', function() {
var numbers = [1, 2, 3];
numbers.indexOf = null;
equal(_.indexOf(numbers, 2), 1, 'can compute indexOf, even without the native function');
var result = (function(){ return _.indexOf(arguments, 2); })(1, 2, 3);
equal(_.indexOf(numbers, 2), 1, 'can compute indexOf');
var result = (function(){ return _.indexOf(arguments, 2); }(1, 2, 3));
equal(result, 1, 'works on an arguments object');
equal(_.indexOf(null, 2), -1, 'handles nulls properly');
@@ -212,8 +241,12 @@
equal(index, 3, '40 is in the list');
numbers = [1, 40, 40, 40, 40, 40, 40, 40, 50, 60, 70]; num = 40;
index = _.indexOf(numbers, num, true);
equal(index, 1, '40 is in the list');
equal(_.indexOf(numbers, num, true), 1, '40 is in the list');
equal(_.indexOf(numbers, 6, true), -1, '6 isnt in the list');
equal(_.indexOf([1, 2, 5, 4, 6, 7], 5, true), -1, 'sorted indexOf doesn\'t uses binary search');
ok(_.every(['1', [], {}, null], function() {
return _.indexOf(numbers, num, {}) === 1;
}), 'non-nums as fromIndex make indexOf assume sorted');
numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3];
index = _.indexOf(numbers, 2, 5);
@@ -221,23 +254,66 @@
index = _.indexOf([,,,], undefined);
equal(index, 0, 'treats sparse arrays as if they were dense');
var array = [1, 2, 3, 1, 2, 3];
strictEqual(_.indexOf(array, 1, -3), 3, 'neg `fromIndex` starts at the right index');
strictEqual(_.indexOf(array, 1, -2), -1, 'neg `fromIndex` starts at the right index');
strictEqual(_.indexOf(array, 2, -3), 4);
_.each([-6, -8, -Infinity], function(fromIndex) {
strictEqual(_.indexOf(array, 1, fromIndex), 0);
});
strictEqual(_.indexOf([1, 2, 3], 1, true), 0);
});
test('lastIndexOf', function() {
var numbers = [1, 0, 1];
var falsey = [void 0, '', 0, false, NaN, null, undefined];
equal(_.lastIndexOf(numbers, 1), 2);
numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0];
numbers.lastIndexOf = null;
equal(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function');
equal(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element');
var result = (function(){ return _.lastIndexOf(arguments, 1); })(1, 0, 1, 0, 0, 1, 0, 0, 0);
var result = (function(){ return _.lastIndexOf(arguments, 1); }(1, 0, 1, 0, 0, 1, 0, 0, 0));
equal(result, 5, 'works on an arguments object');
equal(_.lastIndexOf(null, 2), -1, 'handles nulls properly');
numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3];
var index = _.lastIndexOf(numbers, 2, 2);
equal(index, 1, 'supports the fromIndex argument');
var array = [1, 2, 3, 1, 2, 3];
strictEqual(_.lastIndexOf(array, 1, 0), 0, 'starts at the correct from idx');
strictEqual(_.lastIndexOf(array, 3), 5, 'should return the index of the last matched value');
strictEqual(_.lastIndexOf(array, 4), -1, 'should return `-1` for an unmatched value');
strictEqual(_.lastIndexOf(array, 1, 2), 0, 'should work with a positive `fromIndex`');
_.each([6, 8, Math.pow(2, 32), Infinity], function(fromIndex) {
strictEqual(_.lastIndexOf(array, undefined, fromIndex), -1);
strictEqual(_.lastIndexOf(array, 1, fromIndex), 3);
strictEqual(_.lastIndexOf(array, '', fromIndex), -1);
});
var expected = _.map(falsey, function(value) {
return typeof value == 'number' ? -1 : 5;
});
var actual = _.map(falsey, function(fromIndex) {
return _.lastIndexOf(array, 3, fromIndex);
});
deepEqual(actual, expected, 'should treat falsey `fromIndex` values, except `0` and `NaN`, as `array.length`');
strictEqual(_.lastIndexOf(array, 3, '1'), 5, 'should treat non-number `fromIndex` values as `array.length`');
strictEqual(_.lastIndexOf(array, 3, true), 5, 'should treat non-number `fromIndex` values as `array.length`');
strictEqual(_.lastIndexOf(array, 2, -3), 1, 'should work with a negative `fromIndex`');
strictEqual(_.lastIndexOf(array, 1, -3), 3, 'neg `fromIndex` starts at the right index');
deepEqual(_.map([-6, -8, -Infinity], function(fromIndex) {
return _.lastIndexOf(array, 1, fromIndex);
}), [0, -1, -1]);
});
test('range', function() {
@@ -251,4 +327,4 @@
deepEqual(_.range(0, -10, -1), [0, -1, -2, -3, -4, -5, -6, -7, -8, -9], 'final example in the Python docs');
});
})();
}());

View File

@@ -4,10 +4,10 @@
test('map/flatten/reduce', function() {
var lyrics = [
"I'm a lumberjack and I'm okay",
"I sleep all night and I work all day",
"He's a lumberjack and he's okay",
"He sleeps all night and he works all day"
'I\'m a lumberjack and I\'m okay',
'I sleep all night and I work all day',
'He\'s a lumberjack and he\'s okay',
'He sleeps all night and he works all day'
];
var counts = _(lyrics).chain()
.map(function(line) { return line.split(''); })
@@ -17,7 +17,8 @@
hash[l]++;
return hash;
}, {}).value();
ok(counts.a == 16 && counts.e == 10, 'counted all the letters in the song');
equal(counts.a, 16, 'counted all the letters in the song');
equal(counts.e, 10, 'counted all the letters in the song');
});
test('select/reject/sortBy', function() {
@@ -62,4 +63,4 @@
deepEqual(o.filter(function(i) { return i > 2; }).value(), [3, 4]);
});
})();
}());

View File

@@ -12,7 +12,7 @@
deepEqual(answers, [5, 10, 15], 'context object property accessed');
answers = [];
_.forEach([1, 2, 3], function(num){ answers.push(num); });
_.each([1, 2, 3], function(num){ answers.push(num); });
deepEqual(answers, [1, 2, 3], 'aliased as "forEach"');
answers = [];
@@ -43,17 +43,26 @@
equal(answers, 100, 'enumerates [0, length)');
});
test('forEach', function() {
strictEqual(_.each, _.forEach, 'alias for each');
});
test('lookupIterator with contexts', function() {
_.each([true, false, 'yes', '', 0, 1, {}], function(context) {
_.each([1], function() {
equal(this, context);
}, context);
});
});
test('map', function() {
var doubled = _.map([1, 2, 3], function(num){ return num * 2; });
deepEqual(doubled, [2, 4, 6], 'doubled numbers');
doubled = _.collect([1, 2, 3], function(num){ return num * 2; });
deepEqual(doubled, [2, 4, 6], 'aliased as "collect"');
var tripled = _.map([1, 2, 3], function(num){ return num * this.multiplier; }, {multiplier : 3});
deepEqual(tripled, [3, 6, 9], 'tripled numbers with context');
var doubled = _([1, 2, 3]).map(function(num){ return num * 2; });
doubled = _([1, 2, 3]).map(function(num){ return num * 2; });
deepEqual(doubled, [2, 4, 6], 'OO-style doubled numbers');
if (document.querySelectorAll) {
@@ -61,13 +70,24 @@
deepEqual(ids, ['id1', 'id2'], 'Can use collection methods on NodeLists.');
}
var ids = _.map({length: 2, 0: {id: '1'}, 1: {id: '2'}}, function(n){
ids = _.map({length: 2, 0: {id: '1'}, 1: {id: '2'}}, function(n){
return n.id;
});
deepEqual(ids, ['1', '2'], 'Can use collection methods on Array-likes.');
var ifnull = _.map(null, function(){});
ok(_.isArray(ifnull) && ifnull.length === 0, 'handles a null properly');
deepEqual(_.map(null, _.noop), [], 'handles a null properly');
deepEqual(_.map([1], function() {
return this.length;
}, [5]), [1], 'called with context');
// Passing a property name like _.pluck.
var people = [{name : 'moe', age : 30}, {name : 'curly', age : 50}];
deepEqual(_.map(people, 'name'), ['moe', 'curly'], 'predicate string map to object properties');
});
test('collect', function() {
strictEqual(_.map, _.collect, 'alias for map');
});
test('reduce', function() {
@@ -84,50 +104,41 @@
sum = _([1, 2, 3]).reduce(function(sum, num){ return sum + num; }, 0);
equal(sum, 6, 'OO-style reduce');
var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; });
sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; });
equal(sum, 6, 'default initial value');
var prod = _.reduce([1, 2, 3, 4], function(prod, num){ return prod * num; });
equal(prod, 24, 'can reduce via multiplication');
var ifnull;
try {
_.reduce(null, function(){});
} catch (ex) {
ifnull = ex;
}
ok(ifnull instanceof TypeError, 'handles a null (without initial value) properly');
ok(_.reduce(null, _.noop, 138) === 138, 'handles a null (with initial value) properly');
equal(_.reduce([], _.noop, undefined), undefined, 'undefined can be passed as a special case');
equal(_.reduce([_], _.noop), _, 'collection of length one with no initial value returns the first item');
ok(_.reduce(null, function(){}, 138) === 138, 'handles a null (with initial value) properly');
equal(_.reduce([], function(){}, undefined), undefined, 'undefined can be passed as a special case');
raises(function() { _.reduce([], function(){}); }, TypeError, 'throws an error for empty arrays with no initial value');
raises(function() { _.reduce([], _.noop); }, TypeError, 'throws an error for empty arrays with no initial value');
raises(function() {_.reduce(null, _.noop);}, TypeError, 'handles a null (without initial value) properly');
});
test('foldl', function() {
strictEqual(_.reduce, _.foldl, 'alias for reduce');
});
test('reduceRight', function() {
var list = _.reduceRight(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; }, '');
equal(list, 'bazbarfoo', 'can perform right folds');
var list = _.foldr(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; }, '');
equal(list, 'bazbarfoo', 'aliased as "foldr"');
var list = _.foldr(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; });
list = _.reduceRight(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; });
equal(list, 'bazbarfoo', 'default initial value');
var ifnull;
try {
_.reduceRight(null, function(){});
} catch (ex) {
ifnull = ex;
}
ok(ifnull instanceof TypeError, 'handles a null (without initial value) properly');
var sum = _.reduceRight({a: 1, b: 2, c: 3}, function(sum, num){ return sum + num; });
equal(sum, 6, 'default initial value on object');
ok(_.reduceRight(null, function(){}, 138) === 138, 'handles a null (with initial value) properly');
ok(_.reduceRight(null, _.noop, 138) === 138, 'handles a null (with initial value) properly');
equal(_.reduceRight([_], _.noop), _, 'collection of length one with no initial value returns the first item');
equal(_.reduceRight([], function(){}, undefined), undefined, 'undefined can be passed as a special case');
raises(function() { _.reduceRight([], function(){}); }, TypeError, 'throws an error for empty arrays with no initial value');
equal(_.reduceRight([], _.noop, undefined), undefined, 'undefined can be passed as a special case');
raises(function() { _.reduceRight([], _.noop); }, TypeError, 'throws an error for empty arrays with no initial value');
raises(function() {_.reduceRight(null, _.noop);}, TypeError, 'handles a null (without initial value) properly');
// Assert that the correct arguments are being passed.
@@ -136,12 +147,12 @@
object = {a: 1, b: 2},
lastKey = _.keys(object).pop();
var expected = lastKey == 'a'
var expected = lastKey === 'a'
? [memo, 1, 'a', object]
: [memo, 2, 'b', object];
_.reduceRight(object, function() {
args || (args = _.toArray(arguments));
if (!args) args = _.toArray(arguments);
}, memo);
deepEqual(args, expected);
@@ -152,79 +163,155 @@
lastKey = _.keys(object).pop();
args = null;
expected = lastKey == '2'
expected = lastKey === '2'
? [memo, 'a', '2', object]
: [memo, 'b', '1', object];
_.reduceRight(object, function() {
args || (args = _.toArray(arguments));
if (!args) args = _.toArray(arguments);
}, memo);
deepEqual(args, expected);
});
test('foldr', function() {
strictEqual(_.reduceRight, _.foldr, 'alias for reduceRight');
});
test('find', function() {
var array = [1, 2, 3, 4];
strictEqual(_.find(array, function(n) { return n > 2; }), 3, 'should return first found `value`');
strictEqual(_.find(array, function() { return false; }), void 0, 'should return `undefined` if `value` is not found');
});
test('detect', function() {
var result = _.detect([1, 2, 3], function(num){ return num * 2 == 4; });
// Matching an object like _.findWhere.
var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}, {a: 2, b: 4}];
deepEqual(_.find(list, {a: 1}), {a: 1, b: 2}, 'can be used as findWhere');
deepEqual(_.find(list, {b: 4}), {a: 1, b: 4});
ok(!_.find(list, {c: 1}), 'undefined when not found');
ok(!_.find([], {c: 1}), 'undefined when searching empty list');
var result = _.find([1, 2, 3], function(num){ return num * 2 === 4; });
equal(result, 2, 'found the first "2" and broke the loop');
});
test('select', function() {
var evens = _.select([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
deepEqual(evens, [2, 4, 6], 'selected each even number');
test('detect', function() {
strictEqual(_.detect, _.find, 'alias for detect');
});
evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
deepEqual(evens, [2, 4, 6], 'aliased as "filter"');
test('filter', function() {
var evenArray = [1, 2, 3, 4, 5, 6];
var evenObject = {one: 1, two: 2, three: 3};
var isEven = function(num){ return num % 2 === 0; };
deepEqual(_.filter(evenArray, isEven), [2, 4, 6]);
deepEqual(_.filter(evenObject, isEven), [2], 'can filter objects');
deepEqual(_.filter([{}, evenObject, []], 'two'), [evenObject], 'predicate string map to object properties');
_.filter([1], function() {
equal(this, evenObject, 'given context');
}, evenObject);
// Can be used like _.where.
var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}];
deepEqual(_.filter(list, {a: 1}), [{a: 1, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]);
deepEqual(_.filter(list, {b: 2}), [{a: 1, b: 2}, {a: 2, b: 2}]);
deepEqual(_.filter(list, {}), list, 'Empty object accepts all items');
deepEqual(_(list).filter({}), list, 'OO-filter');
});
test('select', function() {
strictEqual(_.filter, _.select, 'alias for filter');
});
test('reject', function() {
var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 === 0; });
deepEqual(odds, [1, 3, 5], 'rejected each even number');
var context = 'obj';
var evens = _.reject([1, 2, 3, 4, 5, 6], function(num){
equal(context, 'obj');
return num % 2 != 0;
return num % 2 !== 0;
}, context);
deepEqual(evens, [2, 4, 6], 'rejected each odd number');
deepEqual(_.reject([odds, {one: 1, two: 2, three: 3}], 'two'), [odds], 'predicate string map to object properties');
// Can be used like _.where.
var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}];
deepEqual(_.reject(list, {a: 1}), [{a: 2, b: 2}]);
deepEqual(_.reject(list, {b: 2}), [{a: 1, b: 3}, {a: 1, b: 4}]);
deepEqual(_.reject(list, {}), [], 'Returns empty list given empty object');
deepEqual(_.reject(list, []), [], 'Returns empty list given empty array');
});
test('every', function() {
ok(_.every([], _.identity), 'the empty set');
ok(_.every([true, true, true], _.identity), 'every true values');
ok(!_.every([true, false, true], _.identity), 'one false value');
ok(_.every([0, 10, 28], function(num){ return num % 2 === 0; }), 'even numbers');
ok(!_.every([0, 11, 28], function(num){ return num % 2 === 0; }), 'an odd number');
ok(_.every([1], _.identity) === true, 'cast to boolean - true');
ok(_.every([0], _.identity) === false, 'cast to boolean - false');
ok(!_.every([undefined, undefined, undefined], _.identity), 'works with arrays of undefined');
var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}];
ok(!_.every(list, {a: 1, b: 2}), 'Can be called with object');
ok(_.every(list, 'a'), 'String mapped to object property');
list = [{a: 1, b: 2}, {a: 2, b: 2, c: true}];
ok(_.every(list, {b: 2}), 'Can be called with object');
ok(!_.every(list, 'c'), 'String mapped to object property');
ok(_.every({a: 1, b: 2, c: 3, d: 4}, _.isNumber), 'takes objects');
ok(!_.every({a: 1, b: 2, c: 3, d: 4}, _.isObject), 'takes objects');
ok(_.every(['a', 'b', 'c', 'd'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works');
ok(!_.every(['a', 'b', 'c', 'd', 'f'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works');
});
test('all', function() {
ok(_.all([], _.identity), 'the empty set');
ok(_.all([true, true, true], _.identity), 'all true values');
ok(!_.all([true, false, true], _.identity), 'one false value');
ok(_.all([0, 10, 28], function(num){ return num % 2 == 0; }), 'even numbers');
ok(!_.all([0, 11, 28], function(num){ return num % 2 == 0; }), 'an odd number');
ok(_.all([1], _.identity) === true, 'cast to boolean - true');
ok(_.all([0], _.identity) === false, 'cast to boolean - false');
ok(_.every([true, true, true], _.identity), 'aliased as "every"');
ok(!_.all([undefined, undefined, undefined], _.identity), 'works with arrays of undefined');
strictEqual(_.all, _.every, 'alias for all');
});
test('some', function() {
ok(!_.some([]), 'the empty set');
ok(!_.some([false, false, false]), 'all false values');
ok(_.some([false, false, true]), 'one true value');
ok(_.some([null, 0, 'yes', false]), 'a string');
ok(!_.some([null, 0, '', false]), 'falsy values');
ok(!_.some([1, 11, 29], function(num){ return num % 2 === 0; }), 'all odd numbers');
ok(_.some([1, 10, 29], function(num){ return num % 2 === 0; }), 'an even number');
ok(_.some([1], _.identity) === true, 'cast to boolean - true');
ok(_.some([0], _.identity) === false, 'cast to boolean - false');
ok(_.some([false, false, true]));
var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}];
ok(!_.some(list, {a: 5, b: 2}), 'Can be called with object');
ok(_.some(list, 'a'), 'String mapped to object property');
list = [{a: 1, b: 2}, {a: 2, b: 2, c: true}];
ok(_.some(list, {b: 2}), 'Can be called with object');
ok(!_.some(list, 'd'), 'String mapped to object property');
ok(_.some({a: '1', b: '2', c: '3', d: '4', e: 6}, _.isNumber), 'takes objects');
ok(!_.some({a: 1, b: 2, c: 3, d: 4}, _.isObject), 'takes objects');
ok(_.some(['a', 'b', 'c', 'd'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works');
ok(!_.some(['x', 'y', 'z'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works');
});
test('any', function() {
ok(!_.any([]), 'the empty set');
ok(!_.any([false, false, false]), 'all false values');
ok(_.any([false, false, true]), 'one true value');
ok(_.any([null, 0, 'yes', false]), 'a string');
ok(!_.any([null, 0, '', false]), 'falsy values');
ok(!_.any([1, 11, 29], function(num){ return num % 2 == 0; }), 'all odd numbers');
ok(_.any([1, 10, 29], function(num){ return num % 2 == 0; }), 'an even number');
ok(_.any([1], _.identity) === true, 'cast to boolean - true');
ok(_.any([0], _.identity) === false, 'cast to boolean - false');
ok(_.some([false, false, true]), 'aliased as "some"');
strictEqual(_.any, _.some, 'alias for any');
});
test('contains', function() {
ok(_.contains([1, 2, 3], 2), 'two is in the array');
ok(!_.contains([1, 3, 9], 2), 'two is not in the array');
ok(_.contains({moe: 1, larry: 3, curly: 9}, 3) === true, '_.contains on objects checks their values');
ok(_([1, 2, 3]).contains(2), 'OO-style contains');
});
test('include', function() {
ok(_.include([1, 2, 3], 2), 'two is in the array');
ok(!_.include([1, 3, 9], 2), 'two is not in the array');
ok(_.contains({moe: 1, larry: 3, curly: 9}, 3) === true, '_.include on objects checks their values');
ok(_([1, 2, 3]).include(2), 'OO-style include');
strictEqual(_.contains, _.include, 'alias for contains');
});
test('invoke', function() {
@@ -257,8 +344,10 @@
});
test('pluck', function() {
var people = [{name : 'moe', age : 30}, {name : 'curly', age : 50}];
var people = [{name: 'moe', age: 30}, {name: 'curly', age: 50}];
deepEqual(_.pluck(people, 'name'), ['moe', 'curly'], 'pulls names out of objects');
//compat: most flexible handling of edge cases
deepEqual(_.pluck([{'[object Object]': 1}], {}), [1]);
});
test('where', function() {
@@ -271,6 +360,10 @@
equal(result[0].a, 1);
result = _.where(list, {});
equal(result.length, list.length);
function test() {}
test.map = _.map;
deepEqual(_.where([_, {a: 1, b: 2}, _], test), [_, _], 'checks properties given function');
});
test('findWhere', function() {
@@ -280,14 +373,29 @@
result = _.findWhere(list, {b: 4});
deepEqual(result, {a: 1, b: 4});
result = _.findWhere(list, {c: 1})
result = _.findWhere(list, {c: 1});
ok(_.isUndefined(result), 'undefined when not found');
result = _.findWhere([], {c: 1});
ok(_.isUndefined(result), 'undefined when searching empty list');
function test() {}
test.map = _.map;
equal(_.findWhere([_, {a: 1, b: 2}, _], test), _, 'checks properties given function');
function TestClass() {
this.y = 5;
this.x = 'foo';
}
var expect = {c: 1, x: 'foo', y: 5};
deepEqual(_.findWhere([{y: 5, b: 6}, expect], new TestClass()), expect, 'uses class instance properties');
});
test('max', function() {
equal(-Infinity, _.max(null), 'can handle null/undefined');
equal(-Infinity, _.max(undefined), 'can handle null/undefined');
equal(-Infinity, _.max(null, _.identity), 'can handle null/undefined');
equal(3, _.max([1, 2, 3]), 'can perform a regular Math.max');
var neg = _.max([1, 2, 3], function(num){ return -num; });
@@ -306,9 +414,19 @@
var b = {x: -Infinity};
var iterator = function(o){ return o.x; };
equal(_.max([a, b], iterator), a, 'Respects iterator return value of -Infinity');
deepEqual(_.max([{'a': 1}, {'a': 0, 'b': 3}, {'a': 4}, {'a': 2}], 'a'), {'a': 4}, 'String keys use property iterator');
deepEqual(_.max([0, 2], function(a){ return a * this.x; }, {x: 1}), 2, 'Iterator context');
deepEqual(_.max([[1], [2, 3], [-1, 4], [5]], 0), [5], 'Lookup falsy iterator');
deepEqual(_.max([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: 2}, 'Lookup falsy iterator');
});
test('min', function() {
equal(Infinity, _.min(null), 'can handle null/undefined');
equal(Infinity, _.min(undefined), 'can handle null/undefined');
equal(Infinity, _.min(null, _.identity), 'can handle null/undefined');
equal(1, _.min([1, 2, 3]), 'can perform a regular Math.min');
var neg = _.min([1, 2, 3], function(num){ return -num; });
@@ -331,6 +449,12 @@
var b = {x: Infinity};
var iterator = function(o){ return o.x; };
equal(_.min([a, b], iterator), a, 'Respects iterator return value of Infinity');
deepEqual(_.min([{'a': 1}, {'a': 0, 'b': 3}, {'a': 4}, {'a': 2}], 'a'), {'a': 0, 'b': 3}, 'String keys use property iterator');
deepEqual(_.min([0, 2], function(a){ return a * this.x; }, {x: -1}), 2, 'Iterator context');
deepEqual(_.min([[1], [2, 3], [-1, 4], [5]], 0), [-1, 4], 'Lookup falsy iterator');
deepEqual(_.min([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: -1}, 'Lookup falsy iterator');
});
test('sortBy', function() {
@@ -341,7 +465,7 @@
var list = [undefined, 4, 1, undefined, 3, 2];
deepEqual(_.sortBy(list, _.identity), [1, 2, 3, 4, undefined, undefined], 'sortBy with undefined values');
var list = ['one', 'two', 'three', 'four', 'five'];
list = ['one', 'two', 'three', 'four', 'five'];
var sorted = _.sortBy(list, 'length');
deepEqual(sorted, ['one', 'two', 'four', 'five', 'three'], 'sorted by length');
@@ -368,7 +492,9 @@
deepEqual(actual, collection, 'sortBy should be stable');
var list = ['q', 'w', 'e', 'r', 't', 'y'];
deepEqual(_.sortBy(collection, 'x'), collection, 'sortBy accepts property string');
list = ['q', 'w', 'e', 'r', 't', 'y'];
deepEqual(_.sortBy(list), ['e', 'q', 'r', 't', 'w', 'y'], 'uses _.identity if iterator is not specified');
});
@@ -395,8 +521,8 @@
var array = [{}];
_.groupBy(array, function(value, index, obj){ ok(obj === array); });
var array = [1, 2, 1, 2, 3];
var grouped = _.groupBy(array);
array = [1, 2, 1, 2, 3];
grouped = _.groupBy(array);
equal(grouped['1'].length, 2);
equal(grouped['3'].length, 1);
@@ -405,14 +531,14 @@
[1, 3],
[2, 3]
];
deepEqual(_.groupBy(matrix, 0), {1: [[1, 2], [1, 3]], 2: [[2, 3]]})
deepEqual(_.groupBy(matrix, 1), {2: [[1, 2]], 3: [[1, 3], [2, 3]]})
deepEqual(_.groupBy(matrix, 0), {1: [[1, 2], [1, 3]], 2: [[2, 3]]});
deepEqual(_.groupBy(matrix, 1), {2: [[1, 2]], 3: [[1, 3], [2, 3]]});
});
test('indexBy', function() {
var parity = _.indexBy([1, 2, 3, 4, 5], function(num){ return num % 2 == 0; });
equal(parity['true'], 4);
equal(parity['false'], 5);
var parity = _.indexBy([1, 2, 3, 4, 5], function(num){ return num % 2 === 0; });
equal(parity.true, 4);
equal(parity.false, 5);
var list = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'];
var grouped = _.indexBy(list, 'length');
@@ -421,16 +547,16 @@
equal(grouped['5'], 'eight');
var array = [1, 2, 1, 2, 3];
var grouped = _.indexBy(array);
grouped = _.indexBy(array);
equal(grouped['1'], 1);
equal(grouped['2'], 2);
equal(grouped['3'], 3);
});
test('countBy', function() {
var parity = _.countBy([1, 2, 3, 4, 5], function(num){ return num % 2 == 0; });
equal(parity['true'], 2);
equal(parity['false'], 3);
var parity = _.countBy([1, 2, 3, 4, 5], function(num){ return num % 2 === 0; });
equal(parity.true, 2);
equal(parity.false, 3);
var list = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'];
var grouped = _.countBy(list, 'length');
@@ -450,8 +576,8 @@
var array = [{}];
_.countBy(array, function(value, index, obj){ ok(obj === array); });
var array = [1, 2, 1, 2, 3];
var grouped = _.countBy(array);
array = [1, 2, 1, 2, 3];
grouped = _.countBy(array);
equal(grouped['1'], 2);
equal(grouped['3'], 1);
});
@@ -476,17 +602,23 @@
test('shuffle', function() {
var numbers = _.range(10);
var shuffled = _.shuffle(numbers).sort();
var shuffled = _.shuffle(numbers);
notStrictEqual(numbers, shuffled, 'original object is unmodified');
deepEqual(shuffled, numbers, 'contains the same members before and after shuffle');
ok(_.every(_.range(10), function() { //appears consistent?
return _.every(numbers, _.partial(_.contains, numbers));
}), 'contains the same members before and after shuffle');
shuffled = _.shuffle({a: 1, b: 2, c: 3, d: 4});
equal(shuffled.length, 4);
deepEqual(shuffled.sort(), [1, 2, 3, 4], 'works on objects');
});
test('sample', function() {
var numbers = _.range(10);
var all_sampled = _.sample(numbers, 10).sort();
deepEqual(all_sampled, numbers, 'contains the same members before and after sample');
all_sampled = _.sample(numbers, 20).sort();
deepEqual(all_sampled, numbers, 'also works when sampling more objects than are present');
var allSampled = _.sample(numbers, 10).sort();
deepEqual(allSampled, numbers, 'contains the same members before and after sample');
allSampled = _.sample(numbers, 20).sort();
deepEqual(allSampled, numbers, 'also works when sampling more objects than are present');
ok(_.contains(numbers, _.sample(numbers)), 'sampling a single element returns something from the array');
strictEqual(_.sample([]), undefined, 'sampling empty array with no number returns undefined');
notStrictEqual(_.sample([], 5), [], 'sampling empty array with a number returns an empty array');
@@ -536,7 +668,7 @@
deepEqual(_.partition(list, function(x) { return x & 1; }), [[1, 3, 5], [0, 2, 4]], 'handles 0 and 1 return values');
deepEqual(_.partition(list, function(x) { return x - 3; }), [[0, 1, 2, 4, 5], [3]], 'handles other numeric return values');
deepEqual(_.partition(list, function(x) { return x > 1 ? null : true; }), [[0, 1], [2, 3, 4, 5]], 'handles null return values');
deepEqual(_.partition(list, function(x) { if(x < 2) return true; }), [[0, 1], [2, 3, 4, 5]], 'handles undefined return values');
deepEqual(_.partition(list, function(x) { if (x < 2) return true; }), [[0, 1], [2, 3, 4, 5]], 'handles undefined return values');
deepEqual(_.partition({a: 1, b: 2, c: 3}, function(x) { return x > 1; }), [[2, 3], [1]], 'handles objects');
deepEqual(_.partition(list, function(x, index) { return index % 2; }), [[1, 3, 5], [0, 2, 4]], 'can reference the array index');
@@ -547,8 +679,18 @@
deepEqual(_.partition([{x: 1}, {x: 0}, {x: 1}], 'x'), [[{x: 1}, {x: 1}], [{x: 0}]], 'Takes a string');
// Context
var predicate = function(x){ return x === this.x };
var predicate = function(x){ return x === this.x; };
deepEqual(_.partition([1, 2, 3], predicate, {x: 2}), [[2], [1, 3]], 'partition takes a context argument');
deepEqual(_.partition([{a: 1}, {b: 2}, {a: 1, b: 2}], {a: 1}), [[{a: 1}, {a: 1, b: 2}], [{b: 2}]], 'predicate can be object');
var object = {a: 1};
_.partition(object, function(val, key, obj) {
equal(val, 1);
equal(key, 'a');
equal(obj, object);
equal(this, predicate);
}, predicate);
});
})();
}());

View File

@@ -33,10 +33,11 @@
// These tests are only meaningful when using a browser without a native bind function
// To test this with a modern browser, set underscore's nativeBind to undefined
var F = function () { return this; };
var Boundf = _.bind(F, {hello: 'moe curly'});
var boundf = _.bind(F, {hello: 'moe curly'});
var Boundf = boundf; // make eslint happy.
var newBoundf = new Boundf();
equal(newBoundf.hello, undefined, 'function should not be bound to the context, to comply with ECMAScript 5');
equal(Boundf().hello, 'moe curly', "When called without the new operator, it's OK to be bound to the context");
equal(boundf().hello, 'moe curly', "When called without the new operator, it's OK to be bound to the context");
ok(newBoundf instanceof F, 'a bound instance is an instance of the original function');
raises(function() { _.bind('notafunction'); }, TypeError, 'throws an error when binding to a non-function');
@@ -76,14 +77,20 @@
moe = {
name : 'moe',
getName : function() { return 'name: ' + this.name; },
sayHi : function() { return 'hi: ' + this.name; }
sayHi : function() { return 'hi: ' + this.name; },
sayLast : function() { return this.sayHi(_.last(arguments)); }
};
raises(function() { _.bindAll(moe); }, Error, 'throws an error for bindAll with no functions named');
raises(function() { _.bindAll(moe, 'sayBye'); }, TypeError, 'throws an error for bindAll if the given key is undefined');
raises(function() { _.bindAll(moe, 'name'); }, TypeError, 'throws an error for bindAll if the given key is not a function');
_.bindAll(moe, 'sayHi');
_.bindAll(moe, 'sayHi', 'sayLast');
curly.sayHi = moe.sayHi;
equal(curly.sayHi(), 'hi: moe');
var sayLast = moe.sayLast;
equal(sayLast(1, 2, 3, 4, 5, 6, 7, 'Tom'), 'hi: moe', 'createCallback works with any number of arguments');
});
test('memoize', function() {
@@ -111,6 +118,28 @@
upper.cache = {foo: 'BAR', bar: 'FOO'};
equal(upper('foo'), 'BAR');
equal(upper('bar'), 'FOO');
var hashed = _.memoize(function(key) {
//https://github.com/jashkenas/underscore/pull/1679#discussion_r13736209
ok(/[a-z]+/.test(key), 'hasher doesn\'t change keys');
return key;
}, function(key) {
return key.toUpperCase();
});
hashed('yep');
deepEqual(hashed.cache, {'YEP': 'yep'}, 'takes a hasher');
// Test that the hash function can be used to swizzle the key.
var objCacher = _.memoize(function(value, key) {
return {key: key, value: value};
}, function(value, key) {
return key;
});
var myObj = objCacher('a', 'alpha');
var myObjAlias = objCacher('b', 'alpha');
notStrictEqual(myObj, undefined, 'object is created if second argument used as key');
strictEqual(myObj, myObjAlias, 'object is cached if second argument used as key');
strictEqual(myObj.value, 'a', 'object is not modified if second argument used as key');
});
asyncTest('delay', 2, function() {
@@ -170,11 +199,11 @@
var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 30);
throttledIncr(); throttledIncr();
ok(counter == 1);
equal(counter, 1);
_.delay(function(){
ok(counter == 2);
equal(counter, 2);
throttledIncr();
ok(counter == 3);
equal(counter, 3);
start();
}, 85);
});
@@ -208,7 +237,7 @@
var throttledIncr = _.throttle(incr, 32);
var stamp = new Date;
while ((new Date - stamp) < limit) {
while (new Date - stamp < limit) {
throttledIncr();
}
var lastCount = counter;
@@ -226,10 +255,10 @@
var throttledIncr = _.throttle(incr, 60, {leading: false});
throttledIncr(); throttledIncr();
ok(counter === 0);
equal(counter, 0);
_.delay(function() {
ok(counter == 1);
equal(counter, 1);
start();
}, 96);
});
@@ -243,14 +272,14 @@
_.delay(throttledIncr, 50);
_.delay(throttledIncr, 60);
_.delay(throttledIncr, 200);
ok(counter === 0);
equal(counter, 0);
_.delay(function() {
ok(counter == 1);
equal(counter, 1);
}, 250);
_.delay(function() {
ok(counter == 2);
equal(counter, 2);
start();
}, 350);
});
@@ -276,16 +305,16 @@
var throttledIncr = _.throttle(incr, 60, {trailing: false});
throttledIncr(); throttledIncr(); throttledIncr();
ok(counter === 1);
equal(counter, 1);
_.delay(function() {
ok(counter == 1);
equal(counter, 1);
throttledIncr(); throttledIncr();
ok(counter == 2);
equal(counter, 2);
_.delay(function() {
ok(counter == 2);
equal(counter, 2);
start();
}, 96);
}, 96);
@@ -298,14 +327,14 @@
var origNowFunc = _.now;
throttledIncr();
ok(counter == 1);
equal(counter, 1);
_.now = function () {
return new Date(2013, 0, 1, 1, 1, 1);
};
_.delay(function() {
throttledIncr();
ok(counter == 2);
equal(counter, 2);
start();
_.now = origNowFunc;
}, 200);
@@ -320,7 +349,7 @@
var throttledAppend;
var append = function(arg){
value += this + arg;
var args = sequence.pop()
var args = sequence.pop();
if (args) {
throttledAppend.call(args[0], args[1]);
}
@@ -400,7 +429,7 @@
var debouncedAppend;
var append = function(arg){
value += this + arg;
var args = sequence.pop()
var args = sequence.pop();
if (args) {
debouncedAppend.call(args[0], args[1]);
}
@@ -416,10 +445,12 @@
test('once', function() {
var num = 0;
var increment = _.once(function(){ num++; });
var increment = _.once(function(){ return ++num; });
increment();
increment();
equal(num, 1);
equal(increment(), 1, 'stores a memo to the last value');
});
test('Recursive onced function.', 1, function() {
@@ -441,13 +472,13 @@
equal(obj.hi(), 'Hello Moe');
var noop = function(){};
var wrapped = _.wrap(noop, function(fn){ return Array.prototype.slice.call(arguments, 0); });
var wrapped = _.wrap(noop, function(){ return Array.prototype.slice.call(arguments, 0); });
var ret = wrapped(['whats', 'your'], 'vector', 'victor');
deepEqual(ret, [noop, ['whats', 'your'], 'vector', 'victor']);
});
test('negate', function() {
var isOdd = function(n){ return (n & 1) == 1; };
var isOdd = function(n){ return n & 1; };
equal(_.negate(isOdd)(2), true, 'should return the complement of the given function');
equal(_.negate(isOdd)(3), false, 'should return the complement of the given function');
});
@@ -460,6 +491,22 @@
composed = _.compose(greet, exclaim);
equal(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative');
// f(g(h(x, y, z)))
function h(x, y, z) {
equal(arguments.length, 3, 'First function called with multiple args');
return z * y;
}
function g(x) {
equal(arguments.length, 1, 'Composed function is called with 1 argument');
return x;
}
function f(x) {
equal(arguments.length, 1, 'Composed function is called with 1 argument');
return x * 2;
}
composed = _.compose(f, g, h);
equal(composed(1, 2, 3), 12);
});
test('after', function() {
@@ -478,4 +525,29 @@
equal(testAfter(0, 1), 1, 'after(0) should fire when first invoked');
});
})();
test('before', function() {
var testBefore = function(beforeAmount, timesCalled) {
var beforeCalled = 0;
var before = _.before(beforeAmount, function() { beforeCalled++; });
while (timesCalled--) before();
return beforeCalled;
};
equal(testBefore(5, 5), 4, 'before(N) should not fire after being called N times');
equal(testBefore(5, 4), 4, 'before(N) should fire before being called N times');
equal(testBefore(0, 0), 0, 'before(0) should not fire immediately');
equal(testBefore(0, 1), 0, 'before(0) should not fire when first invoked');
var context = {num: 0};
var increment = _.before(3, function(){ return ++this.num; });
_.times(10, increment, context);
equal(increment(), 2, 'stores a memo to the last value');
equal(context.num, 2, 'provides context');
});
test('iteratee', function() {
var identity = _.iteratee();
equal(identity, _.identity, '_.iteratee is exposed as an external function.');
});
}());

View File

@@ -1,6 +1,7 @@
(function() {
module('Objects');
/* global iObject, iElement, iArguments, iFunction, iArray, iString, iNumber, iBoolean, iDate, iRegExp, iNaN, iNull, iUndefined, ActiveXObject */
test('keys', function() {
deepEqual(_.keys({one : 1, two : 2}), ['one', 'two'], 'can extract the keys from an object');
@@ -29,12 +30,12 @@
deepEqual(_.keys(_.invert(obj)), ['Moe', 'Larry', 'Curly'], 'can invert an object');
deepEqual(_.invert(_.invert(obj)), obj, 'two inverts gets you back where you started');
var obj = {length: 3};
ok(_.invert(obj)['3'] == 'length', 'can invert an object with "length"')
obj = {length: 3};
equal(_.invert(obj)['3'], 'length', 'can invert an object with "length"');
});
test('functions', function() {
var obj = {a : 'dash', b : _.map, c : (/yo/), d : _.reduce};
var obj = {a : 'dash', b : _.map, c : /yo/, d : _.reduce};
deepEqual(['b', 'd'], _.functions(obj), 'can grab the function names of any passed-in object');
var Animal = function(){};
@@ -42,6 +43,10 @@
deepEqual(_.functions(new Animal), ['run'], 'also looks up functions on the prototype');
});
test('methods', function() {
strictEqual(_.functions, _.methods, 'alias for functions');
});
test('extend', function() {
var result;
equal(_.extend({}, {a: 'b'}).a, 'b', 'can extend an object with the attributes of another');
@@ -54,6 +59,12 @@
result = _.extend({}, {a: void 0, b: null});
deepEqual(_.keys(result), ['a', 'b'], 'extend copies undefined values');
var F = function() {};
F.prototype = {a: 'b'};
var subObj = new F();
subObj.c = 'd';
deepEqual(_.extend({}, subObj), {c: 'd'}, 'extend ignores any properties but own from source');
try {
result = {};
_.extend(result, null, undefined, {a: 1});
@@ -76,6 +87,10 @@
result = _.pick(['a', 'b'], 1);
deepEqual(result, {1: 'b'}, 'can pick numeric properties');
deepEqual(_.pick(null, 'a', 'b'), {}, 'non objects return empty object');
deepEqual(_.pick(undefined, 'toString'), {}, 'null/undefined return empty object');
deepEqual(_.pick(5, 'toString', 'b'), {toString: Number.prototype.toString}, 'can iterate primitives');
var data = {a: 1, b: 2, c: 3};
var callback = function(value, key, object) {
strictEqual(key, {1: 'a', 2: 'b', 3: 'c'}[value]);
@@ -87,7 +102,12 @@
var Obj = function(){};
Obj.prototype = {a: 1, b: 2, c: 3};
deepEqual(_.pick(new Obj, 'a', 'c'), {a: 1, c: 3}, 'include prototype props');
var instance = new Obj();
deepEqual(_.pick(instance, 'a', 'c'), {a: 1, c: 3}, 'include prototype props');
deepEqual(_.pick(data, function(val, key) {
return this[key] === 3 && this === instance;
}, instance), {c: 3}, 'function is given context');
});
test('omit', function() {
@@ -101,6 +121,10 @@
result = _.omit(['a', 'b'], 0);
deepEqual(result, {1: 'b'}, 'can omit numeric properties');
deepEqual(_.omit(null, 'a', 'b'), {}, 'non objects return empty object');
deepEqual(_.omit(undefined, 'toString'), {}, 'null/undefined return empty object');
deepEqual(_.omit(5, 'toString', 'b'), {}, 'returns empty object for primitives');
var data = {a: 1, b: 2, c: 3};
var callback = function(value, key, object) {
strictEqual(key, {1: 'a', 2: 'b', 3: 'c'}[value]);
@@ -112,11 +136,15 @@
var Obj = function(){};
Obj.prototype = {a: 1, b: 2, c: 3};
deepEqual(_.omit(new Obj, 'b'), {a: 1, c: 3}, 'include prototype props');
var instance = new Obj();
deepEqual(_.omit(instance, 'b'), {a: 1, c: 3}, 'include prototype props');
deepEqual(_.omit(data, function(val, key) {
return this[key] === 3 && this === instance;
}, instance), {a: 1, b: 2}, 'function is given context');
});
test('defaults', function() {
var result;
var options = {zero: 0, one: 1, empty: '', nan: NaN, nothing: null};
_.defaults(options, {zero: 1, one: 10, twenty: 20, nothing: 'str'});
@@ -147,7 +175,7 @@
equal(clone.name, 'moe', 'the clone as the attributes of the original');
clone.name = 'curly';
ok(clone.name == 'curly' && moe.name == 'moe', 'clones can change shallow attributes without affecting the original');
ok(clone.name === 'curly' && moe.name === 'moe', 'clones can change shallow attributes without affecting the original');
clone.lucky.push(101);
equal(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original');
@@ -199,6 +227,7 @@
// Comparisons involving `NaN`.
ok(_.isEqual(NaN, NaN), '`NaN` is equal to `NaN`');
ok(_.isEqual(new Object(NaN), NaN), 'Object(`NaN`) is equal to `NaN`');
ok(!_.isEqual(61, NaN), 'A number primitive is not equal to `NaN`');
ok(!_.isEqual(new Number(79), NaN), 'A number object is not equal to `NaN`');
ok(!_.isEqual(Infinity, NaN), '`Infinity` is not equal to `NaN`');
@@ -254,7 +283,7 @@
// Arrays with primitive and object values.
ok(_.isEqual([1, 'Larry', true], [1, 'Larry', true]), 'Arrays containing identical primitives are equal');
ok(_.isEqual([(/Moe/g), new Date(2009, 9, 25)], [(/Moe/g), new Date(2009, 9, 25)]), 'Arrays containing equivalent elements are equal');
ok(_.isEqual([/Moe/g, new Date(2009, 9, 25)], [/Moe/g, new Date(2009, 9, 25)]), 'Arrays containing equivalent elements are equal');
// Multi-dimensional arrays.
var a = [new Number(47), false, 'Larry', /Moe/, new Date(2009, 11, 13), ['running', 'biking', new String('programming')], {a: 47}];
@@ -277,6 +306,10 @@
ok(_.isEqual(Array(3), Array(3)), 'Sparse arrays of identical lengths are equal');
ok(!_.isEqual(Array(3), Array(6)), 'Sparse arrays of different lengths are not equal when both are empty');
var sparse = [];
sparse[1] = 5;
ok(_.isEqual(sparse, [undefined, 5]), 'Handles sparse arrays as dense');
// Simple objects.
ok(_.isEqual({a: 'Curly', b: 1, c: true}, {a: 'Curly', b: 1, c: true}), 'Objects containing identical primitives are equal');
ok(_.isEqual({a: /Curly/g, b: new Date(2009, 11, 13)}, {a: /Curly/g, b: new Date(2009, 11, 13)}), 'Objects containing equivalent members are equal');
@@ -393,14 +426,14 @@
if (Object.create) {
a = Object.create(null, {x: {value: 1, enumerable: true}});
b = {x: 1};
ok(_.isEqual(a, b));
ok(_.isEqual(a, b), 'Handles objects without a constructor (e.g. from Object.create');
}
function Foo() { this.a = 1; }
Foo.prototype.constructor = null;
var other = { 'a': 1 };
strictEqual(_.isEqual(new Foo, other), false);
var other = {a: 1};
strictEqual(_.isEqual(new Foo, other), false, 'Objects from different constructors are not equal');
});
test('isEmpty', function() {
@@ -425,25 +458,25 @@
// Setup remote variables for iFrame tests.
var iframe = document.createElement('iframe');
iframe.frameBorder = iframe.height = iframe.width = 0
iframe.frameBorder = iframe.height = iframe.width = 0;
document.body.appendChild(iframe);
var iDoc = (iDoc = iframe.contentDocument || iframe.contentWindow).document || iDoc;
iDoc.write(
'<script>\
parent.iElement = document.createElement("div");\
parent.iArguments = (function(){ return arguments; })(1, 2, 3);\
parent.iArray = [1, 2, 3];\
parent.iString = new String("hello");\
parent.iNumber = new Number(100);\
parent.iFunction = (function(){});\
parent.iDate = new Date();\
parent.iRegExp = /hi/;\
parent.iNaN = NaN;\
parent.iNull = null;\
parent.iBoolean = new Boolean(false);\
parent.iUndefined = undefined;\
parent.iObject = {};\
</script>'
'<script>' +
' parent.iElement = document.createElement("div");' +
' parent.iArguments = (function(){ return arguments; })(1, 2, 3);' +
' parent.iArray = [1, 2, 3];' +
' parent.iString = new String("hello");' +
' parent.iNumber = new Number(100);' +
' parent.iFunction = (function(){});' +
' parent.iDate = new Date();' +
' parent.iRegExp = /hi/;' +
' parent.iNaN = NaN;' +
' parent.iNull = null;' +
' parent.iBoolean = new Boolean(false);' +
' parent.iUndefined = undefined;' +
' parent.iObject = {};' +
'</script>'
);
iDoc.close();
@@ -454,7 +487,7 @@
});
test('isArguments', function() {
var args = (function(){ return arguments; })(1, 2, 3);
var args = (function(){ return arguments; }(1, 2, 3));
ok(!_.isArguments('string'), 'a string is not an arguments object');
ok(!_.isArguments(_.isArguments), 'a function is not an arguments object');
ok(_.isArguments(args), 'but the arguments object is an arguments object');
@@ -607,32 +640,79 @@
max().
tap(interceptor).
value();
ok(returned == 6 && intercepted == 6, 'can use tapped objects in a chain');
equal(returned, 6, 'can use tapped objects in a chain');
equal(intercepted, returned, 'can use tapped objects in a chain');
});
test("has", function () {
var obj = {foo: "bar", func: function () {} };
ok(_.has(obj, "foo"), "has() checks that the object has a property.");
ok(_.has(obj, "baz") == false, "has() returns false if the object doesn't have the property.");
ok(_.has(obj, "func"), "has() works for functions too.");
test('has', function () {
var obj = {foo: 'bar', func: function(){}};
ok(_.has(obj, 'foo'), 'has() checks that the object has a property.');
ok(!_.has(obj, 'baz'), "has() returns false if the object doesn't have the property.");
ok(_.has(obj, 'func'), 'has() works for functions too.');
obj.hasOwnProperty = null;
ok(_.has(obj, "foo"), "has() works even when the hasOwnProperty method is deleted.");
ok(_.has(obj, 'foo'), 'has() works even when the hasOwnProperty method is deleted.');
var child = {};
child.prototype = obj;
ok(_.has(child, "foo") == false, "has() does not check the prototype chain for a property.");
ok(!_.has(child, 'foo'), 'has() does not check the prototype chain for a property.');
strictEqual(_.has(null, 'foo'), false, 'has() returns false for null');
strictEqual(_.has(undefined, 'foo'), false, 'has() returns false for undefined');
});
test("matches", function() {
test('matches', function() {
var moe = {name: 'Moe Howard', hair: true};
var curly = {name: 'Curly Howard', hair: false};
var stooges = [moe, curly];
ok(_.find(stooges, _.matches({hair: false})) === curly, "returns a predicate that can be used by finding functions.");
ok(_.find(stooges, _.matches(moe)) === moe, "can be used to locate an object exists in a collection.");
equal(_.matches({hair: true})(moe), true, 'Returns a boolean');
equal(_.matches({hair: true})(curly), false, 'Returns a boolean');
equal(_.matches({__x__: undefined})(5), false, 'can match undefined props on primitives');
equal(_.matches({__x__: undefined})({__x__: undefined}), true, 'can match undefined props');
equal(_.matches({})(null), true, 'Empty spec called with null object returns true');
equal(_.matches({a: 1})(null), false, 'Non-empty spec called with null object returns false');
ok(_.find(stooges, _.matches({hair: false})) === curly, 'returns a predicate that can be used by finding functions.');
ok(_.find(stooges, _.matches(moe)) === moe, 'can be used to locate an object exists in a collection.');
deepEqual(_.where([null, undefined], {a: 1}), [], 'Do not throw on null values.');
deepEqual(_.where([null, undefined], null), [null, undefined], 'null matches null');
deepEqual(_.where([null, undefined], {}), [null, undefined], 'null matches {}');
deepEqual(_.where([{b: 1}], {a: undefined}), [], 'handles undefined values (1683)');
_.each([true, 5, NaN, null, undefined], function(item) {
deepEqual(_.where([{a: 1}], item), [{a: 1}], 'treats primitives as empty');
});
function Prototest() {}
Prototest.prototype.x = 1;
var specObj = new Prototest;
var protospec = _.matches(specObj);
equal(protospec({x: 2}), true, 'spec is restricted to own properties');
specObj.y = 5;
protospec = _.matches(specObj);
equal(protospec({x: 1, y: 5}), true);
equal(protospec({x: 1, y: 4}), false);
ok(_.matches({x: 1, y: 5})(specObj), 'inherited and own properties are checked on the test object');
Prototest.x = 5;
ok(_.matches(Prototest)({x: 5, y: 1}), 'spec can be a function');
// #1729
var o = {'b': 1};
var m = _.matches(o);
equal(m({'b': 1}), true);
o.b = 2;
o.a = 1;
equal(m({'b': 1}), true, 'changing spec object doesnt change matches result');
//null edge cases
var oCon = _.matches({'constructor': Object});
deepEqual(_.map([null, undefined, 5, {}], oCon), [false, false, false, true], 'doesnt fasley match constructor on undefined/null');
});
})();
}());

View File

@@ -60,7 +60,7 @@
test('uniqueId', function() {
var ids = [], i = 0;
while(i++ < 100) ids.push(_.uniqueId());
while (i++ < 100) ids.push(_.uniqueId());
equal(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids');
});
@@ -91,19 +91,49 @@
});
test('_.escape', function() {
equal(_.escape('Curly & Moe'), 'Curly &amp; Moe');
equal(_.escape('<a href="http://moe.com">Curly & Moe\'s</a>'), '&lt;a href=&quot;http://moe.com&quot;&gt;Curly &amp; Moe&#x27;s&lt;/a&gt;');
equal(_.escape('Curly &amp; Moe'), 'Curly &amp;amp; Moe');
equal(_.escape(null), '');
});
test('_.unescape', function() {
var string = 'Curly & Moe';
equal(_.unescape('Curly &amp; Moe'), string);
equal(_.unescape('&lt;a href=&quot;http://moe.com&quot;&gt;Curly &amp; Moe&#x27;s&lt;/a&gt;'), '<a href="http://moe.com">Curly & Moe\'s</a>');
equal(_.unescape('Curly &amp;amp; Moe'), 'Curly &amp; Moe');
equal(_.unescape(null), '');
equal(_.unescape(_.escape(string)), string);
equal(_.unescape(string), string, 'don\'t unescape unnecessarily');
});
// Don't care what they escape them to just that they're escaped and can be unescaped
test('_.escape & unescape', function() {
// test & (&amp;) seperately obviously
var escapeCharacters = ['<', '>', '"', '\'', '`'];
_.each(escapeCharacters, function(escapeChar) {
var str = 'a ' + escapeChar + ' string escaped';
var escaped = _.escape(str);
notEqual(str, escaped, escapeChar + ' is escaped');
equal(str, _.unescape(escaped), escapeChar + ' can be unescaped');
str = 'a ' + escapeChar + escapeChar + escapeChar + 'some more string' + escapeChar;
escaped = _.escape(str);
equal(escaped.indexOf(escapeChar), -1, 'can escape multiple occurances of ' + escapeChar);
equal(_.unescape(escaped), str, 'multiple occurrences of ' + escapeChar + ' can be unescaped');
});
// handles multiple escape characters at once
var joiner = ' other stuff ';
var allEscaped = escapeCharacters.join(joiner);
allEscaped += allEscaped;
ok(_.every(escapeCharacters, function(escapeChar) {
return allEscaped.indexOf(escapeChar) !== -1;
}), 'handles multiple characters');
ok(allEscaped.indexOf(joiner) >= 0, 'can escape multiple escape characters at the same time');
// test & -> &amp;
var str = 'some string & another string & yet another';
var escaped = _.escape(str);
ok(escaped.indexOf('&') !== -1, 'handles & aka &amp;');
equal(_.unescape(str), str, 'can unescape &amp;');
});
test('template', function() {
@@ -120,9 +150,9 @@
var escapeTemplate = _.template('<%= a ? "checked=\\"checked\\"" : "" %>');
equal(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.');
var fancyTemplate = _.template('<ul><% \
for (var key in people) { \
%><li><%= people[key] %></li><% } %></ul>');
var fancyTemplate = _.template('<ul><% ' +
' for (var key in people) { ' +
'%><li><%= people[key] %></li><% } %></ul>');
result = fancyTemplate({people : {moe : 'Moe', larry : 'Larry', curly : 'Curly'}});
equal(result, '<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>', 'can run arbitrary javascript in templates');
@@ -148,16 +178,16 @@
var quoteTemplate = _.template("It's its, not it's");
equal(quoteTemplate({}), "It's its, not it's");
var quoteInStatementAndBody = _.template("<%\
if(foo == 'bar'){ \
%>Statement quotes and 'quotes'.<% } %>");
var quoteInStatementAndBody = _.template('<% ' +
" if(foo == 'bar'){ " +
"%>Statement quotes and 'quotes'.<% } %>");
equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'.");
var withNewlinesAndTabs = _.template('This\n\t\tis: <%= x %>.\n\tok.\nend.');
equal(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.');
var template = _.template('<i><%- value %></i>');
var result = template({value: '<script>'});
result = template({value: '<script>'});
equal(result, '<i>&lt;script&gt;</i>');
var stooge = {
@@ -166,12 +196,12 @@
};
equal(stooge.template(), "I'm Moe");
template = _.template('\n \
<%\n \
// a comment\n \
if (data) { data += 12345; }; %>\n \
<li><%= data %></li>\n \
');
template = _.template('\n ' +
' <%\n ' +
' // a comment\n ' +
' if (data) { data += 12345; }; %>\n ' +
' <li><%= data %></li>\n '
);
equal(template({data : 12345}).replace(/\s/g, ''), '<li>24690</li>');
_.templateSettings = {
@@ -186,7 +216,7 @@
var customQuote = _.template("It's its, not it's");
equal(customQuote({}), "It's its, not it's");
var quoteInStatementAndBody = _.template("{{ if(foo == 'bar'){ }}Statement quotes and 'quotes'.{{ } }}");
quoteInStatementAndBody = _.template("{{ if(foo == 'bar'){ }}Statement quotes and 'quotes'.{{ } }}");
equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'.");
_.templateSettings = {
@@ -201,7 +231,7 @@
var customWithSpecialCharsQuote = _.template("It's its, not it's");
equal(customWithSpecialCharsQuote({}), "It's its, not it's");
var quoteInStatementAndBody = _.template("<? if(foo == 'bar'){ ?>Statement quotes and 'quotes'.<? } ?>");
quoteInStatementAndBody = _.template("<? if(foo == 'bar'){ ?>Statement quotes and 'quotes'.<? } ?>");
equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'.");
_.templateSettings = {
@@ -241,7 +271,8 @@
test('_.templateSettings.variable', function() {
var s = '<%=data.x%>';
var data = {x: 'x'};
strictEqual(_.template(s, data, {variable: 'data'}), 'x');
var tmp = _.template(s, {variable: 'data'});
strictEqual(tmp(data), 'x');
_.templateSettings.variable = 'data';
strictEqual(_.template(s)(data), 'x');
});
@@ -262,22 +293,22 @@
strictEqual(templateEscaped({x: undefined}), '');
var templateWithProperty = _.template('<%=x.foo%>');
strictEqual(templateWithProperty({x: {} }), '');
strictEqual(templateWithProperty({x: {} }), '');
strictEqual(templateWithProperty({x: {}}), '');
strictEqual(templateWithProperty({x: {}}), '');
var templateWithPropertyEscaped = _.template('<%-x.foo%>');
strictEqual(templateWithPropertyEscaped({x: {} }), '');
strictEqual(templateWithPropertyEscaped({x: {} }), '');
strictEqual(templateWithPropertyEscaped({x: {}}), '');
strictEqual(templateWithPropertyEscaped({x: {}}), '');
});
test('interpolate evaluates code only once.', 2, function() {
var count = 0;
var template = _.template('<%= f() %>');
template({f: function(){ ok(!(count++)); }});
template({f: function(){ ok(!count++); }});
var countEscaped = 0;
var templateEscaped = _.template('<%- f() %>');
templateEscaped({f: function(){ ok(!(countEscaped++)); }});
templateEscaped({f: function(){ ok(!countEscaped++); }});
});
test('#746 - _.template settings are not modified.', 1, function() {
@@ -291,4 +322,4 @@
strictEqual(template(), '<<\nx\n>>');
});
})();
}());

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff