Update benchmark and underscore vendor files.

This commit is contained in:
jdalton
2015-02-22 15:24:36 -08:00
parent 861aaf06c6
commit 35b161450a
11 changed files with 1508 additions and 638 deletions

View File

@@ -51,6 +51,7 @@
'0' '0'
], ],
'flatten': [ 'flatten': [
'Flattens empty arrays',
'can flatten nested arrays', 'can flatten nested arrays',
'can shallowly flatten nested arrays', 'can shallowly flatten nested arrays',
'works on an arguments object', 'works on an arguments object',
@@ -83,6 +84,9 @@
] ]
}, },
'Chaining': { 'Chaining': {
'pop': true,
'shift': true,
'splice': true,
'select/reject/sortBy': [ 'select/reject/sortBy': [
'Died on test #1' 'Died on test #1'
], ],
@@ -97,16 +101,14 @@
'filter': [ 'filter': [
'OO-filter' 'OO-filter'
], ],
'invoke': [
'handles null & undefined'
],
'map': [ 'map': [
'OO-style doubled numbers' 'OO-style doubled numbers'
], ],
'reduce': [ 'Resistant to collection length and properties changing while iterating': [
'handles a null (without initial value) properly', 'Died on test #50'
'throws an error for empty arrays with no initial value'
],
'reduceRight': [
'handles a null (without initial value) properly',
'throws an error for empty arrays with no initial value'
] ]
}, },
'Functions': { 'Functions': {
@@ -127,14 +129,23 @@
'debounce asap': true 'debounce asap': true
}, },
'Objects': { 'Objects': {
'isEqual': [ '#1929 Typed Array constructors are functions': true,
'Died on test #60', 'allKeys': true,
'Died on test #63' 'extendOwn': true,
'mapObject': true,
'matcher': true,
'matcher ': true,
'extend': [
'extend copies all properties from source'
], ],
'isFinite': [ 'isFinite': [
'Numeric strings are numbers', 'Numeric strings are numbers',
'Number instances can be finite' 'Number instances can be finite'
], ],
'isMatch': [
'inherited and own properties are checked on the test object',
'doesnt falsey match constructor on undefined/null'
],
'keys': [ 'keys': [
'is not fooled by sparse arrays; see issue #95', 'is not fooled by sparse arrays; see issue #95',
'[]' '[]'
@@ -145,9 +156,6 @@
] ]
}, },
'Utility': { 'Utility': {
'now': [
'Produces the correct time in milliseconds'
],
'_.templateSettings.variable': [ '_.templateSettings.variable': [
'"x"' '"x"'
], ],

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
Copyright (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Copyright (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative
Reporters & Editors Reporters & Editors
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person

View File

@@ -1,6 +1,7 @@
(function() { (function() {
var _ = typeof require == 'function' ? require('..') : window._;
module('Arrays'); QUnit.module('Arrays');
test('first', function() { test('first', function() {
equal(_.first([1, 2, 3]), 1, 'can pull out the first element of an array'); equal(_.first([1, 2, 3]), 1, 'can pull out the first element of an array');
@@ -79,6 +80,12 @@
}); });
test('flatten', function() { test('flatten', function() {
deepEqual(_.flatten(null), [], 'Flattens supports null');
deepEqual(_.flatten(void 0), [], 'Flattens supports undefined');
deepEqual(_.flatten([[], [[]], []]), [], 'Flattens empty arrays');
deepEqual(_.flatten([[], [[]], []], true), [[]], 'Flattens empty arrays');
var list = [1, [2], [3, [[[4]]]]]; var list = [1, [2], [3, [[[4]]]]];
deepEqual(_.flatten(list), [1, 2, 3, 4], 'can flatten nested arrays'); deepEqual(_.flatten(list), [1, 2, 3, 4], 'can flatten nested arrays');
deepEqual(_.flatten(list, true), [1, 2, 3, [[[4]]]], 'can shallowly flatten nested arrays'); deepEqual(_.flatten(list, true), [1, 2, 3, [[[4]]]], 'can shallowly flatten nested arrays');
@@ -86,6 +93,11 @@
deepEqual(result, [1, 2, 3, 4], 'works on an arguments object'); deepEqual(result, [1, 2, 3, 4], 'works on an arguments object');
list = [[1], [2], [3], [[4]]]; list = [[1], [2], [3], [[4]]];
deepEqual(_.flatten(list, true), [1, 2, 3, [4]], 'can shallowly flatten arrays containing only other arrays'); deepEqual(_.flatten(list, true), [1, 2, 3, [4]], 'can shallowly flatten arrays containing only other arrays');
equal(_.flatten([_.range(10), _.range(10), 5, 1, 3], true).length, 23);
equal(_.flatten([_.range(10), _.range(10), 5, 1, 3]).length, 23);
equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3]).length, 1056003, 'Flatten can handle massive collections');
equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3], true).length, 1056003, 'Flatten can handle massive collections');
}); });
test('without', function() { test('without', function() {
@@ -99,6 +111,32 @@
equal(_.without(list, list[0]).length, 1, 'ditto.'); equal(_.without(list, list[0]).length, 1, 'ditto.');
}); });
test('sortedIndex', function() {
var numbers = [10, 20, 30, 40, 50], num = 35;
var indexForNum = _.sortedIndex(numbers, num);
equal(indexForNum, 3, '35 should be inserted at index 3');
var indexFor30 = _.sortedIndex(numbers, 30);
equal(indexFor30, 2, '30 should be inserted at index 2');
var objects = [{x: 10}, {x: 20}, {x: 30}, {x: 40}];
var iterator = function(obj){ return obj.x; };
strictEqual(_.sortedIndex(objects, {x: 25}, iterator), 2);
strictEqual(_.sortedIndex(objects, {x: 35}, 'x'), 3);
var context = {1: 2, 2: 3, 3: 4};
iterator = function(obj){ return this[obj]; };
strictEqual(_.sortedIndex([1, 3], 2, iterator, context), 1);
var values = [0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535, 131071, 262143, 524287, 1048575, 2097151, 4194303, 8388607, 16777215, 33554431, 67108863, 134217727, 268435455, 536870911, 1073741823, 2147483647];
var array = Array(Math.pow(2, 32) - 1);
var length = values.length;
while (length--) {
array[values[length]] = values[length];
}
equal(_.sortedIndex(array, 2147483648), 2147483648, 'should work with large indexes');
});
test('uniq', function() { test('uniq', function() {
var list = [1, 2, 1, 3, 1, 4]; var list = [1, 2, 1, 3, 1, 4];
deepEqual(_.uniq(list), [1, 2, 3, 4], 'can find the unique values of an unsorted array'); deepEqual(_.uniq(list), [1, 2, 3, 4], 'can find the unique values of an unsorted array');
@@ -116,6 +154,20 @@
list = [1, 2, 2, 3, 4, 4]; list = [1, 2, 2, 3, 4, 4];
deepEqual(_.uniq(list, true, iterator), [1, 2, 3, 4], 'iterator works with sorted array'); deepEqual(_.uniq(list, true, iterator), [1, 2, 3, 4], 'iterator works with sorted array');
var kittens = [
{kitten: 'Celery', cuteness: 8},
{kitten: 'Juniper', cuteness: 10},
{kitten: 'Spottis', cuteness: 10}
];
var expected = [
{kitten: 'Celery', cuteness: 8},
{kitten: 'Juniper', cuteness: 10}
];
deepEqual(_.uniq(kittens, true, 'cuteness'), expected, 'string 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'); deepEqual(result, [1, 2, 3, 4], 'works on an arguments object');
@@ -209,6 +261,19 @@
deepEqual(_.zip(), [], '_.zip() returns []'); deepEqual(_.zip(), [], '_.zip() returns []');
}); });
test('unzip', function() {
deepEqual(_.unzip(null), [], 'handles null');
deepEqual(_.unzip([['a', 'b'], [1, 2]]), [['a', 1], ['b', 2]]);
// complements zip
var zipped = _.zip(['fred', 'barney'], [30, 40], [true, false]);
deepEqual(_.unzip(zipped), [['fred', 'barney'], [30, 40], [true, false]]);
zipped = _.zip(['moe', 30], ['larry', 40], ['curly', 50, 'extra data']);
deepEqual(_.unzip(zipped), [['moe', 30, void 0], ['larry', 40, void 0], ['curly', 50, 'extra data']], 'Uses length of largest array');
});
test('object', function() { test('object', function() {
var result = _.object(['moe', 'larry', 'curly'], [30, 40, 50]); var result = _.object(['moe', 'larry', 'curly'], [30, 40, 50]);
var shouldBe = {moe: 30, larry: 40, curly: 50}; var shouldBe = {moe: 30, larry: 40, curly: 50};
@@ -229,7 +294,14 @@
equal(_.indexOf(numbers, 2), 1, 'can compute indexOf'); equal(_.indexOf(numbers, 2), 1, 'can compute indexOf');
var result = (function(){ return _.indexOf(arguments, 2); }(1, 2, 3)); var result = (function(){ return _.indexOf(arguments, 2); }(1, 2, 3));
equal(result, 1, 'works on an arguments object'); equal(result, 1, 'works on an arguments object');
equal(_.indexOf(null, 2), -1, 'handles nulls properly');
_.each([null, void 0, [], false], function(val) {
var msg = 'Handles: ' + (_.isArray(val) ? '[]' : val);
equal(_.indexOf(val, 2), -1, msg);
equal(_.indexOf(val, 2, -1), -1, msg);
equal(_.indexOf(val, 2, -20), -1, msg);
equal(_.indexOf(val, 2, 15), -1, msg);
});
var num = 35; var num = 35;
numbers = [10, 20, 30, 40, 50]; numbers = [10, 20, 30, 40, 50];
@@ -263,6 +335,25 @@
strictEqual(_.indexOf(array, 1, fromIndex), 0); strictEqual(_.indexOf(array, 1, fromIndex), 0);
}); });
strictEqual(_.indexOf([1, 2, 3], 1, true), 0); strictEqual(_.indexOf([1, 2, 3], 1, true), 0);
index = _.indexOf([], undefined, true);
equal(index, -1, 'empty array with truthy `isSorted` returns -1');
});
test('indexOf with NaN', function() {
strictEqual(_.indexOf([1, 2, NaN, NaN], NaN), 2, 'Expected [1, 2, NaN] to contain NaN');
strictEqual(_.indexOf([1, 2, Infinity], NaN), -1, 'Expected [1, 2, NaN] to contain NaN');
(function() {
strictEqual(_.indexOf(arguments, NaN), 2, 'Expected arguments [1, 2, NaN] to contain NaN');
}(1, 2, NaN, NaN));
});
test('indexOf with +- 0', function() {
_.each([-0, +0], function(val) {
strictEqual(_.indexOf([1, 2, val, val], val), 2);
strictEqual(_.indexOf([1, 2, val, val], -val), 2);
});
}); });
test('lastIndexOf', function() { test('lastIndexOf', function() {
@@ -276,7 +367,14 @@
equal(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element'); 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(result, 5, 'works on an arguments object');
equal(_.lastIndexOf(null, 2), -1, 'handles nulls properly');
_.each([null, void 0, [], false], function(val) {
var msg = 'Handles: ' + (_.isArray(val) ? '[]' : val);
equal(_.lastIndexOf(val, 2), -1, msg);
equal(_.lastIndexOf(val, 2, -1), -1, msg);
equal(_.lastIndexOf(val, 2, -20), -1, msg);
equal(_.lastIndexOf(val, 2, 15), -1, msg);
});
numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3];
var index = _.lastIndexOf(numbers, 2, 2); var index = _.lastIndexOf(numbers, 2, 2);
@@ -316,6 +414,109 @@
}), [0, -1, -1]); }), [0, -1, -1]);
}); });
test('lastIndexOf with NaN', function() {
strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN), 3, 'Expected [1, 2, NaN] to contain NaN');
strictEqual(_.lastIndexOf([1, 2, Infinity], NaN), -1, 'Expected [1, 2, NaN] to contain NaN');
(function() {
strictEqual(_.lastIndexOf(arguments, NaN), 3, 'Expected arguments [1, 2, NaN] to contain NaN');
}(1, 2, NaN, NaN));
});
test('lastIndexOf with +- 0', function() {
_.each([-0, +0], function(val) {
strictEqual(_.lastIndexOf([1, 2, val, val], val), 3);
strictEqual(_.lastIndexOf([1, 2, val, val], -val), 3);
strictEqual(_.lastIndexOf([-1, 1, 2], -val), -1);
});
});
test('findIndex', function() {
var objects = [
{'a': 0, 'b': 0},
{'a': 1, 'b': 1},
{'a': 2, 'b': 2},
{'a': 0, 'b': 0}
];
equal(_.findIndex(objects, function(obj) {
return obj.a === 0;
}), 0);
equal(_.findIndex(objects, function(obj) {
return obj.b * obj.a === 4;
}), 2);
equal(_.findIndex(objects, 'a'), 1, 'Uses lookupIterator');
equal(_.findIndex(objects, function(obj) {
return obj.b * obj.a === 5;
}), -1);
equal(_.findIndex(null, _.noop), -1);
strictEqual(_.findIndex(objects, function(a) {
return a.foo === null;
}), -1);
_.findIndex([{a: 1}], function(a, key, obj) {
equal(key, 0);
deepEqual(obj, [{a: 1}]);
strictEqual(this, objects, 'called with context');
}, objects);
var sparse = [];
sparse[20] = {'a': 2, 'b': 2};
equal(_.findIndex(sparse, function(obj) {
return obj && obj.b * obj.a === 4;
}), 20, 'Works with sparse arrays');
var array = [1, 2, 3, 4];
array.match = 55;
strictEqual(_.findIndex(array, function(x) { return x === 55; }), -1, 'doesn\'t match array-likes keys');
});
test('findLastIndex', function() {
var objects = [
{'a': 0, 'b': 0},
{'a': 1, 'b': 1},
{'a': 2, 'b': 2},
{'a': 0, 'b': 0}
];
equal(_.findLastIndex(objects, function(obj) {
return obj.a === 0;
}), 3);
equal(_.findLastIndex(objects, function(obj) {
return obj.b * obj.a === 4;
}), 2);
equal(_.findLastIndex(objects, 'a'), 2, 'Uses lookupIterator');
equal(_.findLastIndex(objects, function(obj) {
return obj.b * obj.a === 5;
}), -1);
equal(_.findLastIndex(null, _.noop), -1);
strictEqual(_.findLastIndex(objects, function(a) {
return a.foo === null;
}), -1);
_.findLastIndex([{a: 1}], function(a, key, obj) {
equal(key, 0);
deepEqual(obj, [{a: 1}]);
strictEqual(this, objects, 'called with context');
}, objects);
var sparse = [];
sparse[20] = {'a': 2, 'b': 2};
equal(_.findLastIndex(sparse, function(obj) {
return obj && obj.b * obj.a === 4;
}), 20, 'Works with sparse arrays');
var array = [1, 2, 3, 4];
array.match = 55;
strictEqual(_.findLastIndex(array, function(x) { return x === 55; }), -1, 'doesn\'t match array-likes keys');
});
test('range', function() { test('range', function() {
deepEqual(_.range(0), [], 'range with 0 as a first argument generates an empty array'); deepEqual(_.range(0), [], 'range with 0 as a first argument generates an empty array');
deepEqual(_.range(4), [0, 1, 2, 3], 'range with a single positive argument generates an array of elements 0,1,2,...,n-1'); deepEqual(_.range(4), [0, 1, 2, 3], 'range with a single positive argument generates an array of elements 0,1,2,...,n-1');

View File

@@ -1,6 +1,7 @@
(function() { (function() {
var _ = typeof require == 'function' ? require('..') : window._;
module('Chaining'); QUnit.module('Chaining');
test('map/flatten/reduce', function() { test('map/flatten/reduce', function() {
var lyrics = [ var lyrics = [
@@ -57,10 +58,41 @@
deepEqual(numbers, [34, 10, 8, 6, 4, 2, 10, 10], 'can chain together array functions.'); deepEqual(numbers, [34, 10, 8, 6, 4, 2, 10, 10], 'can chain together array functions.');
}); });
test('splice', function() {
var instance = _([1, 2, 3, 4, 5]).chain();
deepEqual(instance.splice(1, 3).value(), [1, 5]);
deepEqual(instance.splice(1, 0).value(), [1, 5]);
deepEqual(instance.splice(1, 1).value(), [1]);
deepEqual(instance.splice(0, 1).value(), [], '#397 Can create empty array');
});
test('shift', function() {
var instance = _([1, 2, 3]).chain();
deepEqual(instance.shift().value(), [2, 3]);
deepEqual(instance.shift().value(), [3]);
deepEqual(instance.shift().value(), [], '#397 Can create empty array');
});
test('pop', function() {
var instance = _([1, 2, 3]).chain();
deepEqual(instance.pop().value(), [1, 2]);
deepEqual(instance.pop().value(), [1]);
deepEqual(instance.pop().value(), [], '#397 Can create empty array');
});
test('chaining works in small stages', function() { test('chaining works in small stages', function() {
var o = _([1, 2, 3, 4]).chain(); var o = _([1, 2, 3, 4]).chain();
deepEqual(o.filter(function(i) { return i < 3; }).value(), [1, 2]); deepEqual(o.filter(function(i) { return i < 3; }).value(), [1, 2]);
deepEqual(o.filter(function(i) { return i > 2; }).value(), [3, 4]); deepEqual(o.filter(function(i) { return i > 2; }).value(), [3, 4]);
}); });
test('#1562: Engine proxies for chained functions', function() {
var wrapped = _(512);
strictEqual(wrapped.toJSON(), 512);
strictEqual(wrapped.valueOf(), 512);
strictEqual(+wrapped, 512);
strictEqual(wrapped.toString(), '512');
strictEqual('' + wrapped, '512');
});
}()); }());

View File

@@ -1,6 +1,7 @@
(function() { (function() {
var _ = typeof require == 'function' ? require('..') : window._;
module('Collections'); QUnit.module('Collections');
test('each', function() { test('each', function() {
_.each([1, 2, 3], function(num, i) { _.each([1, 2, 3], function(num, i) {
@@ -35,12 +36,6 @@
var a = [1, 2, 3]; var a = [1, 2, 3];
strictEqual(_.each(a, function(){}), a); strictEqual(_.each(a, function(){}), a);
strictEqual(_.each(null, function(){}), null); strictEqual(_.each(null, function(){}), null);
var b = [1, 2, 3];
b.length = 100;
answers = 0;
_.each(b, function(){ ++answers; });
equal(answers, 100, 'enumerates [0, length)');
}); });
test('forEach', function() { test('forEach', function() {
@@ -55,6 +50,94 @@
}); });
}); });
test('Iterating objects with sketchy length properties', function() {
var functions = [
'each', 'map', 'filter', 'find',
'some', 'every', 'max', 'min',
'groupBy', 'countBy', 'partition', 'indexBy'
];
var reducers = ['reduce', 'reduceRight'];
var tricks = [
{length: '5'},
{
length: {
valueOf: _.constant(5)
}
},
{length: Math.pow(2, 53) + 1},
{length: Math.pow(2, 53)},
{length: null},
{length: -2},
{length: new Number(15)}
];
expect(tricks.length * (functions.length + reducers.length + 4));
_.each(tricks, function(trick) {
var length = trick.length;
strictEqual(_.size(trick), 1, 'size on obj with length: ' + length);
deepEqual(_.toArray(trick), [length], 'toArray on obj with length: ' + length);
deepEqual(_.shuffle(trick), [length], 'shuffle on obj with length: ' + length);
deepEqual(_.sample(trick), length, 'sample on obj with length: ' + length);
_.each(functions, function(method) {
_[method](trick, function(val, key) {
strictEqual(key, 'length', method + ': ran with length = ' + val);
});
});
_.each(reducers, function(method) {
strictEqual(_[method](trick), trick.length, method);
});
});
});
test('Resistant to collection length and properties changing while iterating', function() {
var collection = [
'each', 'map', 'filter', 'find',
'some', 'every', 'max', 'min', 'reject',
'groupBy', 'countBy', 'partition', 'indexBy',
'reduce', 'reduceRight'
];
var array = [
'findIndex', 'findLastIndex'
];
var object = [
'mapObject', 'findKey', 'pick', 'omit'
];
_.each(collection.concat(array), function(method) {
var sparseArray = [1, 2, 3];
sparseArray.length = 100;
var answers = 0;
_[method](sparseArray, function(){
++answers;
return method === 'every' ? true : null;
}, {});
equal(answers, 100, method + ' enumerates [0, length)');
var growingCollection = [1, 2, 3], count = 0;
_[method](growingCollection, function() {
if (count < 10) growingCollection.push(count++);
return method === 'every' ? true : null;
}, {});
equal(count, 3, method + ' is resistant to length changes');
});
_.each(collection.concat(object), function(method) {
var changingObject = {0: 0, 1: 1}, count = 0;
_[method](changingObject, function(val) {
if (count < 10) changingObject[++count] = val + 1;
return method === 'every' ? true : null;
}, {});
equal(count, 2, method + ' is resistant to property changes');
});
});
test('map', function() { test('map', function() {
var doubled = _.map([1, 2, 3], function(num){ return num * 2; }); var doubled = _.map([1, 2, 3], function(num){ return num * 2; });
deepEqual(doubled, [2, 4, 6], 'doubled numbers'); deepEqual(doubled, [2, 4, 6], 'doubled numbers');
@@ -65,12 +148,7 @@
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'); deepEqual(doubled, [2, 4, 6], 'OO-style doubled numbers');
if (document.querySelectorAll) { var ids = _.map({length: 2, 0: {id: '1'}, 1: {id: '2'}}, function(n){
var ids = _.map(document.querySelectorAll('#map-test *'), function(n){ return n.id; });
deepEqual(ids, ['id1', 'id2'], 'Can use collection methods on NodeLists.');
}
ids = _.map({length: 2, 0: {id: '1'}, 1: {id: '2'}}, function(n){
return n.id; return n.id;
}); });
deepEqual(ids, ['1', '2'], 'Can use collection methods on Array-likes.'); deepEqual(ids, ['1', '2'], 'Can use collection methods on Array-likes.');
@@ -113,9 +191,7 @@
ok(_.reduce(null, _.noop, 138) === 138, 'handles a null (with 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, 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'); equal(_.reduce([_], _.noop), _, 'collection of length one with no initial value returns the first item');
equal(_.reduce([], _.noop), undefined, 'returns undefined when collection is empty and 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() { test('foldl', function() {
@@ -136,9 +212,7 @@
equal(_.reduceRight([_], _.noop), _, 'collection of length one with no initial value returns the first item'); equal(_.reduceRight([_], _.noop), _, 'collection of length one with no initial value returns the first item');
equal(_.reduceRight([], _.noop, undefined), undefined, 'undefined can be passed as a special case'); equal(_.reduceRight([], _.noop, undefined), undefined, 'undefined can be passed as a special case');
equal(_.reduceRight([], _.noop), undefined, 'returns undefined when collection is empty and no initial value');
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. // Assert that the correct arguments are being passed.
@@ -183,6 +257,9 @@
strictEqual(_.find(array, function(n) { return n > 2; }), 3, 'should return first found `value`'); 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'); strictEqual(_.find(array, function() { return false; }), void 0, 'should return `undefined` if `value` is not found');
array.dontmatch = 55;
strictEqual(_.find(array, function(x) { return x === 55; }), void 0, 'iterates array-likes correctly');
// Matching an object like _.findWhere. // 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}]; 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, {a: 1}), {a: 1, b: 2}, 'can be used as findWhere');
@@ -192,6 +269,25 @@
var result = _.find([1, 2, 3], function(num){ return num * 2 === 4; }); var result = _.find([1, 2, 3], function(num){ return num * 2 === 4; });
equal(result, 2, 'found the first "2" and broke the loop'); equal(result, 2, 'found the first "2" and broke the loop');
var obj = {
a: {x: 1, z: 3},
b: {x: 2, z: 2},
c: {x: 3, z: 4},
d: {x: 4, z: 1}
};
deepEqual(_.find(obj, {x: 2}), {x: 2, z: 2}, 'works on objects');
deepEqual(_.find(obj, {x: 2, z: 1}), void 0);
deepEqual(_.find(obj, function(x) {
return x.x === 4;
}), {x: 4, z: 1});
_.findIndex([{a: 1}], function(a, key, obj) {
equal(key, 0);
deepEqual(obj, [{a: 1}]);
strictEqual(this, _, 'called with context');
}, _);
}); });
test('detect', function() { test('detect', function() {
@@ -303,22 +399,66 @@
strictEqual(_.any, _.some, 'alias for any'); strictEqual(_.any, _.some, 'alias for any');
}); });
test('contains', function() { test('includes', function() {
ok(_.contains([1, 2, 3], 2), 'two is in the array'); _.each([null, void 0, 0, 1, NaN, {}, []], function(val) {
ok(!_.contains([1, 3, 9], 2), 'two is not in the array'); strictEqual(_.includes(val, 'hasOwnProperty'), false);
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'); strictEqual(_.includes([1, 2, 3], 2), true, 'two is in the array');
ok(!_.includes([1, 3, 9], 2), 'two is not in the array');
strictEqual(_.includes([5, 4, 3, 2, 1], 5, true), true, 'doesn\'t delegate to binary search');
ok(_.includes({moe: 1, larry: 3, curly: 9}, 3) === true, '_.includes on objects checks their values');
ok(_([1, 2, 3]).includes(2), 'OO-style includes');
}); });
test('include', function() { test('include', function() {
strictEqual(_.contains, _.include, 'alias for contains'); strictEqual(_.includes, _.include, 'alias for includes');
}); });
test('invoke', function() { test('contains', function() {
strictEqual(_.includes, _.contains, 'alias for includes');
var numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3];
strictEqual(_.includes(numbers, 1, 1), true);
strictEqual(_.includes(numbers, 1, -1), false);
strictEqual(_.includes(numbers, 1, -2), false);
strictEqual(_.includes(numbers, 1, -3), true);
strictEqual(_.includes(numbers, 1, 6), true);
strictEqual(_.includes(numbers, 1, 7), false);
});
test('includes with NaN', function() {
strictEqual(_.includes([1, 2, NaN, NaN], NaN), true, 'Expected [1, 2, NaN] to contain NaN');
strictEqual(_.includes([1, 2, Infinity], NaN), false, 'Expected [1, 2, NaN] to contain NaN');
});
test('includes with +- 0', function() {
_.each([-0, +0], function(val) {
strictEqual(_.includes([1, 2, val, val], val), true);
strictEqual(_.includes([1, 2, val, val], -val), true);
strictEqual(_.includes([-1, 1, 2], -val), false);
});
});
test('invoke', 5, function() {
var list = [[5, 1, 7], [3, 2, 1]]; var list = [[5, 1, 7], [3, 2, 1]];
var result = _.invoke(list, 'sort'); var result = _.invoke(list, 'sort');
deepEqual(result[0], [1, 5, 7], 'first array sorted'); deepEqual(result[0], [1, 5, 7], 'first array sorted');
deepEqual(result[1], [1, 2, 3], 'second array sorted'); deepEqual(result[1], [1, 2, 3], 'second array sorted');
_.invoke([{
method: function() {
deepEqual(_.toArray(arguments), [1, 2, 3], 'called with arguments');
}
}], 'method', 1, 2, 3);
deepEqual(_.invoke([{a: null}, {}, {a: _.constant(1)}], 'a'), [null, void 0, 1], 'handles null & undefined');
throws(function() {
_.invoke([{a: 1}], 'a');
}, TypeError, 'throws for non-functions');
}); });
test('invoke w/ function reference', function() { test('invoke w/ function reference', function() {
@@ -326,6 +466,10 @@
var result = _.invoke(list, Array.prototype.sort); var result = _.invoke(list, Array.prototype.sort);
deepEqual(result[0], [1, 5, 7], 'first array sorted'); deepEqual(result[0], [1, 5, 7], 'first array sorted');
deepEqual(result[1], [1, 2, 3], 'second array sorted'); deepEqual(result[1], [1, 2, 3], 'second array sorted');
deepEqual(_.invoke([1, 2, 3], function(a) {
return a + this;
}, 5), [6, 7, 8], 'receives params from invoke');
}); });
// Relevant when using ClojureScript // Relevant when using ClojureScript
@@ -346,6 +490,7 @@
test('pluck', function() { 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'); deepEqual(_.pluck(people, 'name'), ['moe', 'curly'], 'pulls names out of objects');
deepEqual(_.pluck(people, 'address'), [undefined, undefined], 'missing properties are returned as undefined');
//compat: most flexible handling of edge cases //compat: most flexible handling of edge cases
deepEqual(_.pluck([{'[object Object]': 1}], {}), [1]); deepEqual(_.pluck([{'[object Object]': 1}], {}), [1]);
}); });
@@ -582,24 +727,6 @@
equal(grouped['3'], 1); equal(grouped['3'], 1);
}); });
test('sortedIndex', function() {
var numbers = [10, 20, 30, 40, 50], num = 35;
var indexForNum = _.sortedIndex(numbers, num);
equal(indexForNum, 3, '35 should be inserted at index 3');
var indexFor30 = _.sortedIndex(numbers, 30);
equal(indexFor30, 2, '30 should be inserted at index 2');
var objects = [{x: 10}, {x: 20}, {x: 30}, {x: 40}];
var iterator = function(obj){ return obj.x; };
strictEqual(_.sortedIndex(objects, {x: 25}, iterator), 2);
strictEqual(_.sortedIndex(objects, {x: 35}, 'x'), 3);
var context = {1: 2, 2: 3, 3: 4};
iterator = function(obj){ return this[obj]; };
strictEqual(_.sortedIndex([1, 3], 2, iterator, context), 1);
});
test('shuffle', function() { test('shuffle', function() {
var numbers = _.range(10); var numbers = _.range(10);
var shuffled = _.shuffle(numbers); var shuffled = _.shuffle(numbers);
@@ -637,12 +764,14 @@
var numbers = _.toArray({one : 1, two : 2, three : 3}); var numbers = _.toArray({one : 1, two : 2, three : 3});
deepEqual(numbers, [1, 2, 3], 'object flattened into array'); deepEqual(numbers, [1, 2, 3], 'object flattened into array');
// test in IE < 9 if (typeof document != 'undefined') {
try { // test in IE < 9
var actual = _.toArray(document.childNodes); var actual;
} catch(ex) { } try {
actual = _.toArray(document.childNodes);
ok(_.isArray(actual), 'should not throw converting a node list'); } catch(ex) { }
deepEqual(actual, _.map(document.childNodes, _.identity), 'works on NodeList');
}
}); });
test('size', function() { test('size', function() {
@@ -693,4 +822,26 @@
}, predicate); }, predicate);
}); });
if (typeof document != 'undefined') {
test('Can use various collection methods on NodeLists', function() {
var parent = document.createElement('div');
parent.innerHTML = '<span id=id1></span>textnode<span id=id2></span>';
var elementChildren = _.filter(parent.childNodes, _.isElement);
equal(elementChildren.length, 2);
deepEqual(_.map(elementChildren, 'id'), ['id1', 'id2']);
deepEqual(_.map(parent.childNodes, 'nodeType'), [1, 3, 1]);
ok(!_.every(parent.childNodes, _.isElement));
ok(_.some(parent.childNodes, _.isElement));
function compareNode(node) {
return _.isElement(node) ? node.id.charAt(2) : void 0;
}
equal(_.max(parent.childNodes, compareNode), _.last(parent.childNodes));
equal(_.min(parent.childNodes, compareNode), _.first(parent.childNodes));
});
}
}()); }());

View File

@@ -1,6 +1,8 @@
(function() { (function() {
var _ = typeof require == 'function' ? require('..') : window._;
module('Functions'); QUnit.module('Functions');
QUnit.config.asyncRetries = 3;
test('bind', function() { test('bind', function() {
var context = {name : 'moe'}; var context = {name : 'moe'};
@@ -12,7 +14,9 @@
equal(bound(), 'name: moe', 'can do OO-style binding'); equal(bound(), 'name: moe', 'can do OO-style binding');
bound = _.bind(func, null, 'curly'); bound = _.bind(func, null, 'curly');
equal(bound(), 'name: curly', 'can bind without specifying a context'); var result = bound();
// Work around a PhantomJS bug when applying a function with null|undefined.
ok(result === 'name: curly' || result === 'name: ' + window.name, 'can bind without specifying a context');
func = function(salutation, name) { return salutation + ': ' + name; }; func = function(salutation, name) { return salutation + ': ' + name; };
func = _.bind(func, this, 'hello'); func = _.bind(func, this, 'hello');
@@ -40,7 +44,7 @@
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'); 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'); throws(function() { _.bind('notafunction'); }, TypeError, 'throws an error when binding to a non-function');
}); });
test('partial', function() { test('partial', function() {
@@ -59,6 +63,20 @@
func = _.partial(function() { return typeof arguments[2]; }, _, 'b', _, 'd'); func = _.partial(function() { return typeof arguments[2]; }, _, 'b', _, 'd');
equal(func('a'), 'undefined', 'unfilled placeholders are undefined'); equal(func('a'), 'undefined', 'unfilled placeholders are undefined');
// passes context
function MyWidget(name, options) {
this.name = name;
this.options = options;
}
MyWidget.prototype.get = function() {
return this.name;
};
var MyWidgetWithCoolOpts = _.partial(MyWidget, _, {a: 1});
var widget = new MyWidgetWithCoolOpts('foo');
ok(widget instanceof MyWidget, 'Can partially bind a constructor');
equal(widget.get(), 'foo', 'keeps prototype');
deepEqual(widget.options, {a: 1});
}); });
test('bindAll', function() { test('bindAll', function() {
@@ -81,9 +99,9 @@
sayLast : function() { return this.sayHi(_.last(arguments)); } sayLast : function() { return this.sayHi(_.last(arguments)); }
}; };
raises(function() { _.bindAll(moe); }, Error, 'throws an error for bindAll with no functions named'); throws(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'); throws(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'); throws(function() { _.bindAll(moe, 'name'); }, TypeError, 'throws an error for bindAll if the given key is not a function');
_.bindAll(moe, 'sayHi', 'sayLast'); _.bindAll(moe, 'sayHi', 'sayLast');
curly.sayHi = moe.sayHi; curly.sayHi = moe.sayHi;
@@ -548,6 +566,16 @@
test('iteratee', function() { test('iteratee', function() {
var identity = _.iteratee(); var identity = _.iteratee();
equal(identity, _.identity, '_.iteratee is exposed as an external function.'); equal(identity, _.identity, '_.iteratee is exposed as an external function.');
function fn() {
return arguments;
}
_.each([_.iteratee(fn), _.iteratee(fn, {})], function(cb) {
equal(cb().length, 0);
deepEqual(_.toArray(cb(1, 2, 3)), _.range(1, 4));
deepEqual(_.toArray(cb(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), _.range(1, 11));
});
}); });
}()); }());

View File

@@ -1,7 +1,9 @@
(function() { (function() {
var _ = typeof require == 'function' ? require('..') : window._;
module('Objects'); QUnit.module('Objects');
/* global iObject, iElement, iArguments, iFunction, iArray, iString, iNumber, iBoolean, iDate, iRegExp, iNaN, iNull, iUndefined, ActiveXObject */
var testElement = typeof document === 'object' ? document.createElement('div') : void 0;
test('keys', function() { test('keys', function() {
deepEqual(_.keys({one : 1, two : 2}), ['one', 'two'], 'can extract the keys from an object'); deepEqual(_.keys({one : 1, two : 2}), ['one', 'two'], 'can extract the keys from an object');
@@ -13,6 +15,62 @@
deepEqual(_.keys(1), []); deepEqual(_.keys(1), []);
deepEqual(_.keys('a'), []); deepEqual(_.keys('a'), []);
deepEqual(_.keys(true), []); deepEqual(_.keys(true), []);
// keys that may be missed if the implementation isn't careful
var trouble = {
'constructor': Object,
'valueOf': _.noop,
'hasOwnProperty': null,
'toString': 5,
'toLocaleString': undefined,
'propertyIsEnumerable': /a/,
'isPrototypeOf': this,
'__defineGetter__': Boolean,
'__defineSetter__': {},
'__lookupSetter__': false,
'__lookupGetter__': []
};
var troubleKeys = ['constructor', 'valueOf', 'hasOwnProperty', 'toString', 'toLocaleString', 'propertyIsEnumerable',
'isPrototypeOf', '__defineGetter__', '__defineSetter__', '__lookupSetter__', '__lookupGetter__'].sort();
deepEqual(_.keys(trouble).sort(), troubleKeys, 'matches non-enumerable properties');
});
test('allKeys', function() {
deepEqual(_.allKeys({one : 1, two : 2}), ['one', 'two'], 'can extract the allKeys from an object');
// the test above is not safe because it relies on for-in enumeration order
var a = []; a[1] = 0;
deepEqual(_.allKeys(a), ['1'], 'is not fooled by sparse arrays; see issue #95');
a.a = a;
deepEqual(_.allKeys(a), ['1', 'a'], 'is not fooled by sparse arrays with additional properties');
_.each([null, void 0, 1, 'a', true, NaN, {}, [], new Number(5), new Date(0)], function(val) {
deepEqual(_.allKeys(val), []);
});
// allKeys that may be missed if the implementation isn't careful
var trouble = {
constructor: Object,
valueOf: _.noop,
hasOwnProperty: null,
toString: 5,
toLocaleString: undefined,
propertyIsEnumerable: /a/,
isPrototypeOf: this
};
var troubleKeys = ['constructor', 'valueOf', 'hasOwnProperty', 'toString', 'toLocaleString', 'propertyIsEnumerable',
'isPrototypeOf'].sort();
deepEqual(_.allKeys(trouble).sort(), troubleKeys, 'matches non-enumerable properties');
function A() {}
A.prototype.foo = 'foo';
var b = new A();
b.bar = 'bar';
deepEqual(_.allKeys(b).sort(), ['bar', 'foo'], 'should include inherited keys');
function y() {}
y.x = 'z';
deepEqual(_.allKeys(y), ['x'], 'should get keys from constructor');
}); });
test('values', function() { test('values', function() {
@@ -63,7 +121,9 @@
F.prototype = {a: 'b'}; F.prototype = {a: 'b'};
var subObj = new F(); var subObj = new F();
subObj.c = 'd'; subObj.c = 'd';
deepEqual(_.extend({}, subObj), {c: 'd'}, 'extend ignores any properties but own from source'); deepEqual(_.extend({}, subObj), {a: 'b', c: 'd'}, 'extend copies all properties from source');
_.extend(subObj, {});
ok(!subObj.hasOwnProperty('a'), "extend does not convert destination object's 'in' properties to 'own' properties");
try { try {
result = {}; result = {};
@@ -76,6 +136,36 @@
strictEqual(_.extend(undefined, {a: 1}), undefined, 'extending undefined results in undefined'); strictEqual(_.extend(undefined, {a: 1}), undefined, 'extending undefined results in undefined');
}); });
test('extendOwn', function() {
var result;
equal(_.extendOwn({}, {a: 'b'}).a, 'b', 'can assign an object with the attributes of another');
equal(_.extendOwn({a: 'x'}, {a: 'b'}).a, 'b', 'properties in source override destination');
equal(_.extendOwn({x: 'x'}, {a: 'b'}).x, 'x', "properties not in source don't get overriden");
result = _.extendOwn({x: 'x'}, {a: 'a'}, {b: 'b'});
deepEqual(result, {x: 'x', a: 'a', b: 'b'}, 'can assign from multiple source objects');
result = _.assign({x: 'x'}, {a: 'a', x: 2}, {a: 'b'});
deepEqual(result, {x: 2, a: 'b'}, 'assigning from multiple source objects last property trumps');
deepEqual(_.extendOwn({}, {a: void 0, b: null}), {a: void 0, b: null}, 'assign copies undefined values');
var F = function() {};
F.prototype = {a: 'b'};
var subObj = new F();
subObj.c = 'd';
deepEqual(_.extendOwn({}, subObj), {c: 'd'}, 'assign copies own properties from source');
result = {};
deepEqual(_.assign(result, null, undefined, {a: 1}), {a: 1}, 'should not error on `null` or `undefined` sources');
_.each(['a', 5, null, false], function(val) {
strictEqual(_.assign(val, {a: 1}), val, 'assigning non-objects results in returning the non-object value');
});
strictEqual(_.extendOwn(undefined, {a: 1}), undefined, 'assigning undefined results in undefined');
result = _.extendOwn({a: 1, 0: 2, 1: '5', length: 6}, {0: 1, 1: 2, length: 2});
deepEqual(result, {a: 1, 0: 1, 1: 2, length: 2}, 'assign should treat array-like objects like normal objects');
});
test('pick', function() { test('pick', function() {
var result; var result;
result = _.pick({a: 1, b: 2, c: 3}, 'a', 'c'); result = _.pick({a: 1, b: 2, c: 3}, 'a', 'c');
@@ -87,8 +177,10 @@
result = _.pick(['a', 'b'], 1); result = _.pick(['a', 'b'], 1);
deepEqual(result, {1: 'b'}, 'can pick numeric properties'); deepEqual(result, {1: 'b'}, 'can pick numeric properties');
deepEqual(_.pick(null, 'a', 'b'), {}, 'non objects return empty object'); _.each([null, void 0], function(val) {
deepEqual(_.pick(undefined, 'toString'), {}, 'null/undefined return empty object'); deepEqual(_.pick(val, 'hasOwnProperty'), {}, 'Called with null/undefined');
deepEqual(_.pick(val, _.constant(true)), {});
});
deepEqual(_.pick(5, 'toString', 'b'), {toString: Number.prototype.toString}, 'can iterate primitives'); deepEqual(_.pick(5, 'toString', 'b'), {toString: Number.prototype.toString}, 'can iterate primitives');
var data = {a: 1, b: 2, c: 3}; var data = {a: 1, b: 2, c: 3};
@@ -108,6 +200,11 @@
deepEqual(_.pick(data, function(val, key) { deepEqual(_.pick(data, function(val, key) {
return this[key] === 3 && this === instance; return this[key] === 3 && this === instance;
}, instance), {c: 3}, 'function is given context'); }, instance), {c: 3}, 'function is given context');
ok(!_.has(_.pick({}, 'foo'), 'foo'), 'does not set own property if property not in object');
_.pick(data, function(value, key, obj) {
equal(obj, data, 'passes same object as third parameter of iteratee');
});
}); });
test('omit', function() { test('omit', function() {
@@ -419,9 +516,6 @@
b = _({x: 1, y: 2}).chain(); b = _({x: 1, y: 2}).chain();
equal(_.isEqual(a.isEqual(b), _(true)), true, '`isEqual` can be chained'); equal(_.isEqual(a.isEqual(b), _(true)), true, '`isEqual` can be chained');
// Objects from another frame.
ok(_.isEqual({}, iObject));
// Objects without a `constructor` property // Objects without a `constructor` property
if (Object.create) { if (Object.create) {
a = Object.create(null, {x: {value: 1, enumerable: true}}); a = Object.create(null, {x: {value: 1, enumerable: true}});
@@ -454,37 +548,18 @@
var args = function(){ return arguments; }; var args = function(){ return arguments; };
ok(_.isEmpty(args()), 'empty arguments object is empty'); ok(_.isEmpty(args()), 'empty arguments object is empty');
ok(!_.isEmpty(args('')), 'non-empty arguments object is not empty'); ok(!_.isEmpty(args('')), 'non-empty arguments object is not empty');
// covers collecting non-enumerable properties in IE < 9
var nonEnumProp = {'toString': 5};
ok(!_.isEmpty(nonEnumProp), 'non-enumerable property is not empty');
}); });
// Setup remote variables for iFrame tests. if (typeof document === 'object') {
var iframe = document.createElement('iframe'); test('isElement', function() {
iframe.frameBorder = iframe.height = iframe.width = 0; ok(!_.isElement('div'), 'strings are not dom elements');
document.body.appendChild(iframe); ok(_.isElement(testElement), 'an element is a DOM element');
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>'
);
iDoc.close();
test('isElement', function() {
ok(!_.isElement('div'), 'strings are not dom elements');
ok(_.isElement(document.body), 'the body tag is a DOM element');
ok(_.isElement(iElement), 'even from another frame');
});
test('isArguments', function() { test('isArguments', function() {
var args = (function(){ return arguments; }(1, 2, 3)); var args = (function(){ return arguments; }(1, 2, 3));
@@ -493,16 +568,15 @@
ok(_.isArguments(args), 'but the arguments object is an arguments object'); ok(_.isArguments(args), 'but the arguments object is an arguments object');
ok(!_.isArguments(_.toArray(args)), 'but not when it\'s converted into an array'); ok(!_.isArguments(_.toArray(args)), 'but not when it\'s converted into an array');
ok(!_.isArguments([1, 2, 3]), 'and not vanilla arrays.'); ok(!_.isArguments([1, 2, 3]), 'and not vanilla arrays.');
ok(_.isArguments(iArguments), 'even from another frame');
}); });
test('isObject', function() { test('isObject', function() {
ok(_.isObject(arguments), 'the arguments object is object'); ok(_.isObject(arguments), 'the arguments object is object');
ok(_.isObject([1, 2, 3]), 'and arrays'); ok(_.isObject([1, 2, 3]), 'and arrays');
ok(_.isObject(document.body), 'and DOM element'); if (testElement) {
ok(_.isObject(iElement), 'even from another frame'); ok(_.isObject(testElement), 'and DOM element');
}
ok(_.isObject(function () {}), 'and functions'); ok(_.isObject(function () {}), 'and functions');
ok(_.isObject(iFunction), 'even from another frame');
ok(!_.isObject(null), 'but not null'); ok(!_.isObject(null), 'but not null');
ok(!_.isObject(undefined), 'and not undefined'); ok(!_.isObject(undefined), 'and not undefined');
ok(!_.isObject('string'), 'and not string'); ok(!_.isObject('string'), 'and not string');
@@ -515,16 +589,17 @@
ok(!_.isArray(undefined), 'undefined vars are not arrays'); ok(!_.isArray(undefined), 'undefined vars are not arrays');
ok(!_.isArray(arguments), 'the arguments object is not an array'); ok(!_.isArray(arguments), 'the arguments object is not an array');
ok(_.isArray([1, 2, 3]), 'but arrays are'); ok(_.isArray([1, 2, 3]), 'but arrays are');
ok(_.isArray(iArray), 'even from another frame');
}); });
test('isString', function() { test('isString', function() {
var obj = new String('I am a string object'); var obj = new String('I am a string object');
ok(!_.isString(document.body), 'the document body is not a string'); if (testElement) {
ok(!_.isString(testElement), 'an element is not a string');
}
ok(_.isString([1, 2, 3].join(', ')), 'but strings are'); ok(_.isString([1, 2, 3].join(', ')), 'but strings are');
ok(_.isString(iString), 'even from another frame'); strictEqual(_.isString('I am a string literal'), true, 'string literals are');
ok(_.isString('I am a string literal'), 'string literals are');
ok(_.isString(obj), 'so are String objects'); ok(_.isString(obj), 'so are String objects');
strictEqual(_.isString(1), false);
}); });
test('isNumber', function() { test('isNumber', function() {
@@ -534,7 +609,6 @@
ok(_.isNumber(3 * 4 - 7 / 10), 'but numbers are'); ok(_.isNumber(3 * 4 - 7 / 10), 'but numbers are');
ok(_.isNumber(NaN), 'NaN *is* a number'); ok(_.isNumber(NaN), 'NaN *is* a number');
ok(_.isNumber(Infinity), 'Infinity is a number'); ok(_.isNumber(Infinity), 'Infinity is a number');
ok(_.isNumber(iNumber), 'even from another frame');
ok(!_.isNumber('1'), 'numeric strings are not numbers'); ok(!_.isNumber('1'), 'numeric strings are not numbers');
}); });
@@ -549,7 +623,6 @@
ok(!_.isBoolean(null), 'null is not a boolean'); ok(!_.isBoolean(null), 'null is not a boolean');
ok(_.isBoolean(true), 'but true is'); ok(_.isBoolean(true), 'but true is');
ok(_.isBoolean(false), 'and so is false'); ok(_.isBoolean(false), 'and so is false');
ok(_.isBoolean(iBoolean), 'even from another frame');
}); });
test('isFunction', function() { test('isFunction', function() {
@@ -557,21 +630,35 @@
ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); ok(!_.isFunction([1, 2, 3]), 'arrays are not functions');
ok(!_.isFunction('moe'), 'strings are not functions'); ok(!_.isFunction('moe'), 'strings are not functions');
ok(_.isFunction(_.isFunction), 'but functions are'); ok(_.isFunction(_.isFunction), 'but functions are');
ok(_.isFunction(iFunction), 'even from another frame');
ok(_.isFunction(function(){}), 'even anonymous ones'); ok(_.isFunction(function(){}), 'even anonymous ones');
if (testElement) {
ok(!_.isFunction(testElement), 'elements are not functions');
}
}); });
if (typeof Int8Array !== 'undefined') {
test('#1929 Typed Array constructors are functions', function() {
_.chain(['Float32Array', 'Float64Array', 'Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array'])
.map(_.propertyOf(typeof GLOBAL != 'undefined' ? GLOBAL : window))
.compact()
.each(function(TypedArray) {
// PhantomJS reports `typeof UInt8Array == 'object'` and doesn't report toString TypeArray
// as a function
strictEqual(_.isFunction(TypedArray), Object.prototype.toString.call(TypedArray) === '[object Function]');
});
});
}
test('isDate', function() { test('isDate', function() {
ok(!_.isDate(100), 'numbers are not dates'); ok(!_.isDate(100), 'numbers are not dates');
ok(!_.isDate({}), 'objects are not dates'); ok(!_.isDate({}), 'objects are not dates');
ok(_.isDate(new Date()), 'but dates are'); ok(_.isDate(new Date()), 'but dates are');
ok(_.isDate(iDate), 'even from another frame');
}); });
test('isRegExp', function() { test('isRegExp', function() {
ok(!_.isRegExp(_.identity), 'functions are not RegExps'); ok(!_.isRegExp(_.identity), 'functions are not RegExps');
ok(_.isRegExp(/identity/), 'but RegExps are'); ok(_.isRegExp(/identity/), 'but RegExps are');
ok(_.isRegExp(iRegExp), 'even from another frame');
}); });
test('isFinite', function() { test('isFinite', function() {
@@ -595,7 +682,6 @@
ok(!_.isNaN(null), 'null is not NaN'); ok(!_.isNaN(null), 'null is not NaN');
ok(!_.isNaN(0), '0 is not NaN'); ok(!_.isNaN(0), '0 is not NaN');
ok(_.isNaN(NaN), 'but NaN is'); ok(_.isNaN(NaN), 'but NaN is');
ok(_.isNaN(iNaN), 'even from another frame');
ok(_.isNaN(new Number(NaN)), 'wrapped NaN is still NaN'); ok(_.isNaN(new Number(NaN)), 'wrapped NaN is still NaN');
}); });
@@ -603,7 +689,6 @@
ok(!_.isNull(undefined), 'undefined is not null'); ok(!_.isNull(undefined), 'undefined is not null');
ok(!_.isNull(NaN), 'NaN is not null'); ok(!_.isNull(NaN), 'NaN is not null');
ok(_.isNull(null), 'but null is'); ok(_.isNull(null), 'but null is');
ok(_.isNull(iNull), 'even from another frame');
}); });
test('isUndefined', function() { test('isUndefined', function() {
@@ -613,20 +698,20 @@
ok(!_.isUndefined(NaN), 'NaN is defined'); ok(!_.isUndefined(NaN), 'NaN is defined');
ok(_.isUndefined(), 'nothing is undefined'); ok(_.isUndefined(), 'nothing is undefined');
ok(_.isUndefined(undefined), 'undefined is undefined'); ok(_.isUndefined(undefined), 'undefined is undefined');
ok(_.isUndefined(iUndefined), 'even from another frame');
}); });
if (window.ActiveXObject) { test('isError', function() {
test('IE host objects', function() { ok(!_.isError(1), 'numbers are not Errors');
var xml = new ActiveXObject('Msxml2.DOMDocument.3.0'); ok(!_.isError(null), 'null is not an Error');
ok(!_.isNumber(xml)); ok(!_.isError(Error), 'functions are not Errors');
ok(!_.isBoolean(xml)); ok(_.isError(new Error()), 'Errors are Errors');
ok(!_.isNaN(xml)); ok(_.isError(new EvalError()), 'EvalErrors are Errors');
ok(!_.isFunction(xml)); ok(_.isError(new RangeError()), 'RangeErrors are Errors');
ok(!_.isNull(xml)); ok(_.isError(new ReferenceError()), 'ReferenceErrors are Errors');
ok(!_.isUndefined(xml)); ok(_.isError(new SyntaxError()), 'SyntaxErrors are Errors');
}); ok(_.isError(new TypeError()), 'TypeErrors are Errors');
} ok(_.isError(new URIError()), 'URIErrors are Errors');
});
test('tap', function() { test('tap', function() {
var intercepted = null; var intercepted = null;
@@ -658,22 +743,62 @@
strictEqual(_.has(undefined, 'foo'), false, 'has() returns false for undefined'); strictEqual(_.has(undefined, 'foo'), false, 'has() returns false for undefined');
}); });
test('matches', function() { test('isMatch', function() {
var moe = {name: 'Moe Howard', hair: true};
var curly = {name: 'Curly Howard', hair: false};
equal(_.isMatch(moe, {hair: true}), true, 'Returns a boolean');
equal(_.isMatch(curly, {hair: true}), false, 'Returns a boolean');
equal(_.isMatch(5, {__x__: undefined}), false, 'can match undefined props on primitives');
equal(_.isMatch({__x__: undefined}, {__x__: undefined}), true, 'can match undefined props');
equal(_.isMatch(null, {}), true, 'Empty spec called with null object returns true');
equal(_.isMatch(null, {a: 1}), false, 'Non-empty spec called with null object returns false');
_.each([null, undefined], function(item) { strictEqual(_.isMatch(item, null), true, 'null matches null'); });
_.each([null, undefined], function(item) { strictEqual(_.isMatch(item, null), true, 'null matches {}'); });
strictEqual(_.isMatch({b: 1}, {a: undefined}), false, 'handles undefined values (1683)');
_.each([true, 5, NaN, null, undefined], function(item) {
strictEqual(_.isMatch({a: 1}, item), true, 'treats primitives as empty');
});
function Prototest() {}
Prototest.prototype.x = 1;
var specObj = new Prototest;
equal(_.isMatch({x: 2}, specObj), true, 'spec is restricted to own properties');
specObj.y = 5;
equal(_.isMatch({x: 1, y: 5}, specObj), true);
equal(_.isMatch({x: 1, y: 4}, specObj), false);
ok(_.isMatch(specObj, {x: 1, y: 5}), 'inherited and own properties are checked on the test object');
Prototest.x = 5;
ok(_.isMatch({x: 5, y: 1}, Prototest), 'spec can be a function');
//null edge cases
var oCon = {'constructor': Object};
deepEqual(_.map([null, undefined, 5, {}], _.partial(_.isMatch, _, oCon)), [false, false, false, true], 'doesnt falsey match constructor on undefined/null');
});
test('matcher', function() {
var moe = {name: 'Moe Howard', hair: true}; var moe = {name: 'Moe Howard', hair: true};
var curly = {name: 'Curly Howard', hair: false}; var curly = {name: 'Curly Howard', hair: false};
var stooges = [moe, curly]; var stooges = [moe, curly];
equal(_.matches({hair: true})(moe), true, 'Returns a boolean'); equal(_.matcher({hair: true})(moe), true, 'Returns a boolean');
equal(_.matches({hair: true})(curly), false, 'Returns a boolean'); equal(_.matcher({hair: true})(curly), false, 'Returns a boolean');
equal(_.matches({__x__: undefined})(5), false, 'can match undefined props on primitives'); equal(_.matcher({__x__: undefined})(5), false, 'can match undefined props on primitives');
equal(_.matches({__x__: undefined})({__x__: undefined}), true, 'can match undefined props'); equal(_.matcher({__x__: undefined})({__x__: undefined}), true, 'can match undefined props');
equal(_.matches({})(null), true, 'Empty spec called with null object returns true'); equal(_.matcher({})(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'); equal(_.matcher({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, _.matcher({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.'); ok(_.find(stooges, _.matcher(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], {a: 1}), [], 'Do not throw on null values.');
deepEqual(_.where([null, undefined], null), [null, undefined], 'null matches null'); deepEqual(_.where([null, undefined], null), [null, undefined], 'null matches null');
@@ -687,22 +812,22 @@
function Prototest() {} function Prototest() {}
Prototest.prototype.x = 1; Prototest.prototype.x = 1;
var specObj = new Prototest; var specObj = new Prototest;
var protospec = _.matches(specObj); var protospec = _.matcher(specObj);
equal(protospec({x: 2}), true, 'spec is restricted to own properties'); equal(protospec({x: 2}), true, 'spec is restricted to own properties');
specObj.y = 5; specObj.y = 5;
protospec = _.matches(specObj); protospec = _.matcher(specObj);
equal(protospec({x: 1, y: 5}), true); equal(protospec({x: 1, y: 5}), true);
equal(protospec({x: 1, y: 4}), false); equal(protospec({x: 1, y: 4}), false);
ok(_.matches({x: 1, y: 5})(specObj), 'inherited and own properties are checked on the test object'); ok(_.matcher({x: 1, y: 5})(specObj), 'inherited and own properties are checked on the test object');
Prototest.x = 5; Prototest.x = 5;
ok(_.matches(Prototest)({x: 5, y: 1}), 'spec can be a function'); ok(_.matcher(Prototest)({x: 5, y: 1}), 'spec can be a function');
// #1729 // #1729
var o = {'b': 1}; var o = {'b': 1};
var m = _.matches(o); var m = _.matcher(o);
equal(m({'b': 1}), true); equal(m({'b': 1}), true);
o.b = 2; o.b = 2;
@@ -711,8 +836,159 @@
//null edge cases //null edge cases
var oCon = _.matches({'constructor': Object}); var oCon = _.matcher({'constructor': Object});
deepEqual(_.map([null, undefined, 5, {}], oCon), [false, false, false, true], 'doesnt fasley match constructor on undefined/null'); deepEqual(_.map([null, undefined, 5, {}], oCon), [false, false, false, true], 'doesnt falsey match constructor on undefined/null');
}); });
test('matcher', function() {
var moe = {name: 'Moe Howard', hair: true};
var curly = {name: 'Curly Howard', hair: false};
var stooges = [moe, curly];
equal(_.matcher({hair: true})(moe), true, 'Returns a boolean');
equal(_.matcher({hair: true})(curly), false, 'Returns a boolean');
equal(_.matcher({__x__: undefined})(5), false, 'can match undefined props on primitives');
equal(_.matcher({__x__: undefined})({__x__: undefined}), true, 'can match undefined props');
equal(_.matcher({})(null), true, 'Empty spec called with null object returns true');
equal(_.matcher({a: 1})(null), false, 'Non-empty spec called with null object returns false');
ok(_.find(stooges, _.matcher({hair: false})) === curly, 'returns a predicate that can be used by finding functions.');
ok(_.find(stooges, _.matcher(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 = _.matcher(specObj);
equal(protospec({x: 2}), true, 'spec is restricted to own properties');
specObj.y = 5;
protospec = _.matcher(specObj);
equal(protospec({x: 1, y: 5}), true);
equal(protospec({x: 1, y: 4}), false);
ok(_.matcher({x: 1, y: 5})(specObj), 'inherited and own properties are checked on the test object');
Prototest.x = 5;
ok(_.matcher(Prototest)({x: 5, y: 1}), 'spec can be a function');
// #1729
var o = {'b': 1};
var m = _.matcher(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 = _.matcher({'constructor': Object});
deepEqual(_.map([null, undefined, 5, {}], oCon), [false, false, false, true], 'doesnt falsey match constructor on undefined/null');
});
test('findKey', function() {
var objects = {
a: {'a': 0, 'b': 0},
b: {'a': 1, 'b': 1},
c: {'a': 2, 'b': 2}
};
equal(_.findKey(objects, function(obj) {
return obj.a === 0;
}), 'a');
equal(_.findKey(objects, function(obj) {
return obj.b * obj.a === 4;
}), 'c');
equal(_.findKey(objects, 'a'), 'b', 'Uses lookupIterator');
equal(_.findKey(objects, function(obj) {
return obj.b * obj.a === 5;
}), undefined);
strictEqual(_.findKey([1, 2, 3, 4, 5, 6], function(obj) {
return obj === 3;
}), '2', 'Keys are strings');
strictEqual(_.findKey(objects, function(a) {
return a.foo === null;
}), undefined);
_.findKey({a: {a: 1}}, function(a, key, obj) {
equal(key, 'a');
deepEqual(obj, {a: {a: 1}});
strictEqual(this, objects, 'called with context');
}, objects);
var array = [1, 2, 3, 4];
array.match = 55;
strictEqual(_.findKey(array, function(x) { return x === 55; }), 'match', 'matches array-likes keys');
});
test('mapObject', function() {
var obj = {'a': 1, 'b': 2};
var objects = {
a: {'a': 0, 'b': 0},
b: {'a': 1, 'b': 1},
c: {'a': 2, 'b': 2}
};
deepEqual(_.mapObject(obj, function(val) {
return val * 2;
}), {'a': 2, 'b': 4}, 'simple objects');
deepEqual(_.mapObject(objects, function(val) {
return _.reduce(val, function(memo,v){
return memo + v;
},0);
}), {'a': 0, 'b': 2, 'c': 4}, 'nested objects');
deepEqual(_.mapObject(obj, function(val,key,obj) {
return obj[key] * 2;
}), {'a': 2, 'b': 4}, 'correct keys');
deepEqual(_.mapObject([1,2], function(val) {
return val * 2;
}), {'0': 2, '1': 4}, 'check behavior for arrays');
deepEqual(_.mapObject(obj, function(val) {
return val * this.multiplier;
}, {multiplier : 3}), {'a': 3, 'b': 6}, 'keep context');
deepEqual(_.mapObject({a: 1}, function() {
return this.length;
}, [1,2]), {'a': 2}, 'called with context');
var ids = _.mapObject({length: 2, 0: {id: '1'}, 1: {id: '2'}}, function(n){
return n.id;
});
deepEqual(ids, {'length': undefined, '0': '1', '1': '2'}, 'Check with array-like objects');
// Passing a property name like _.pluck.
var people = {'a': {name : 'moe', age : 30}, 'b': {name : 'curly', age : 50}};
deepEqual(_.mapObject(people, 'name'), {'a': 'moe', 'b': 'curly'}, 'predicate string map to object properties');
_.each([null, void 0, 1, 'abc', [], {}, undefined], function(val){
deepEqual(_.mapObject(val, _.identity), {}, 'mapValue identity');
});
var Proto = function(){this.a = 1;};
Proto.prototype.b = 1;
var protoObj = new Proto();
deepEqual(_.mapObject(protoObj, _.identity), {a: 1}, 'ignore inherited values from prototypes');
});
}()); }());

View File

@@ -1,8 +1,8 @@
(function() { (function() {
var _ = typeof require == 'function' ? require('..') : window._;
var templateSettings; var templateSettings;
module('Utility', { QUnit.module('Utility', {
setup: function() { setup: function() {
templateSettings = _.clone(_.templateSettings); templateSettings = _.clone(_.templateSettings);
@@ -21,13 +21,13 @@
}); });
test('identity', function() { test('identity', function() {
var moe = {name : 'moe'}; var stooge = {name : 'moe'};
equal(_.identity(moe), moe, 'moe is the same as his identity'); equal(_.identity(stooge), stooge, 'stooge is the same as his identity');
}); });
test('constant', function() { test('constant', function() {
var moe = {name : 'moe'}; var stooge = {name : 'moe'};
equal(_.constant(moe)(), moe, 'should create a function that returns moe'); equal(_.constant(stooge)(), stooge, 'should create a function that returns stooge');
}); });
test('noop', function() { test('noop', function() {
@@ -35,8 +35,28 @@
}); });
test('property', function() { test('property', function() {
var moe = {name : 'moe'}; var stooge = {name : 'moe'};
equal(_.property('name')(moe), 'moe', 'should return the property with the given name'); equal(_.property('name')(stooge), 'moe', 'should return the property with the given name');
equal(_.property('name')(null), undefined, 'should return undefined for null values');
equal(_.property('name')(undefined), undefined, 'should return undefined for undefined values');
});
test('propertyOf', function() {
var stoogeRanks = _.propertyOf({curly: 2, moe: 1, larry: 3});
equal(stoogeRanks('curly'), 2, 'should return the property with the given name');
equal(stoogeRanks(null), undefined, 'should return undefined for null values');
equal(stoogeRanks(undefined), undefined, 'should return undefined for undefined values');
function MoreStooges() { this.shemp = 87; }
MoreStooges.prototype = {curly: 2, moe: 1, larry: 3};
var moreStoogeRanks = _.propertyOf(new MoreStooges());
equal(moreStoogeRanks('curly'), 2, 'should return properties from further up the prototype chain');
var nullPropertyOf = _.propertyOf(null);
equal(nullPropertyOf('curly'), undefined, 'should return undefined when obj is null');
var undefPropertyOf = _.propertyOf(undefined);
equal(undefPropertyOf('curly'), undefined, 'should return undefined when obj is undefined');
}); });
test('random', function() { test('random', function() {
@@ -268,6 +288,41 @@
strictEqual(_.result(null, 'x'), undefined); strictEqual(_.result(null, 'x'), undefined);
}); });
test('result returns a default value if object is null or undefined', function() {
strictEqual(_.result(null, 'b', 'default'), 'default');
strictEqual(_.result(undefined, 'c', 'default'), 'default');
strictEqual(_.result(''.match('missing'), 1, 'default'), 'default');
});
test('result returns a default value if property of object is missing', function() {
strictEqual(_.result({d: null}, 'd', 'default'), null);
strictEqual(_.result({e: false}, 'e', 'default'), false);
});
test('result only returns the default value if the object does not have the property or is undefined', function() {
strictEqual(_.result({}, 'b', 'default'), 'default');
strictEqual(_.result({d: undefined}, 'd', 'default'), 'default');
});
test('result does not return the default if the property of an object is found in the prototype', function() {
var Foo = function(){};
Foo.prototype.bar = 1;
strictEqual(_.result(new Foo, 'bar', 2), 1);
});
test('result does use the fallback when the result of invoking the property is undefined', function() {
var obj = {a: function() {}};
strictEqual(_.result(obj, 'a', 'failed'), undefined);
});
test('result fallback can use a function', function() {
var obj = {a: [1, 2, 3]};
strictEqual(_.result(obj, 'b', _.constant(5)), 5);
strictEqual(_.result(obj, 'b', function() {
return this.a;
}), obj.a, 'called with context');
});
test('_.templateSettings.variable', function() { test('_.templateSettings.variable', function() {
var s = '<%=data.x%>'; var s = '<%=data.x%>';
var data = {x: 'x'}; var data = {x: 'x'};

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff