Update vendor/backbone and vendor/underscore.

This commit is contained in:
John-David Dalton
2015-12-10 00:22:20 -08:00
parent 2192b7748e
commit 08568fcc8f
18 changed files with 3486 additions and 2939 deletions

View File

@@ -30,12 +30,13 @@
QUnit.config.excused = {
'Arrays': {
'drop': [
'alias for rest'
'is an alias for rest'
],
'first': [
'can pass an index to first',
'[1,2]',
'0'
'returns an empty array when n <= 0 (0 case)',
'returns an empty array when n <= 0 (negative case)',
'can fetch the first n elements',
'returns the whole array if n > length'
],
'findIndex': [
'called with context'
@@ -44,29 +45,31 @@
'called with context'
],
'flatten': [
'Flattens empty arrays',
'supports empty arrays',
'can flatten nested arrays',
'works on an arguments object',
'Flatten can handle very deep arrays'
'can handle very deep arrays'
],
'head': [
'alias for first'
'is an alias for first'
],
'indexOf': [
"sorted indexOf doesn't uses binary search",
'0'
],
'initial': [
'initial can take an index',
'initial can take a large index',
'initial works on arguments object'
'returns all but the last n elements',
'returns an empty array when n > length',
'works on an arguments object'
],
'intersection': [
'can perform an OO-style intersection'
],
'last': [
'can pass an index to last',
'0'
'returns an empty array when n <= 0 (0 case)',
'returns an empty array when n <= 0 (negative case)',
'can fetch the last n elements',
'returns the whole array if n > length'
],
'lastIndexOf': [
'should treat falsey `fromIndex` values, except `0` and `NaN`, as `array.length`',
@@ -81,19 +84,19 @@
'range with two arguments a &amp; b, b&lt;a generates an empty array'
],
'rest': [
'working rest(0)',
'rest can take an index',
'works on arguments object'
'returns the whole array when index is 0',
'returns elements starting at the given index',
'works on an arguments object'
],
'sortedIndex': [
'2',
'3'
],
'tail': [
'alias for rest'
'is an alias for rest'
],
'take': [
'alias for first'
'is an alias for first'
],
'uniq': [
'can find the unique values of an array using a custom iterator',
@@ -101,9 +104,6 @@
'string iterator works with sorted array',
'can use pluck like iterator',
'can use falsey pluck like iterator'
],
'unique': [
'alias for uniq'
]
},
'Chaining': {
@@ -118,21 +118,9 @@
'lookupIterator with contexts': true,
'Iterating objects with sketchy length properties': true,
'Resistant to collection length and properties changing while iterating': true,
'all': [
'alias for all'
],
'any': [
'alias for any'
],
'collect': [
'alias for map'
],
'countBy': [
'true'
],
'detect': [
'alias for detect'
],
'each': [
'context object property accessed'
],
@@ -159,12 +147,6 @@
'findWhere': [
'checks properties given function'
],
'foldl': [
'alias for reduce'
],
'foldr': [
'alias for reduceRight'
],
'groupBy': [
'true'
],
@@ -237,6 +219,7 @@
},
'Functions': {
'debounce asap': true,
'debounce asap cancel': true,
'debounce after system time is set backwards': true,
'debounce asap recursively': true,
'throttle repeatedly with results': true,
@@ -281,8 +264,8 @@
'extending undefined results in undefined'
],
'extendOwn': [
'assigning non-objects results in returning the non-object value',
'assigning undefined results in undefined'
'extending non-objects results in returning the non-object value',
'extending undefined results in undefined'
],
'functions': [
'also looks up functions on the prototype'
@@ -339,11 +322,17 @@
var mixinPrereqs = (function() {
var aliasToReal = {
'all': 'every',
'allKeys': 'keysIn',
'any': 'some',
'collect': 'map',
'compose': 'flowRight',
'contains': 'includes',
'detect': 'find',
'extendOwn': 'assign',
'findWhere': 'find',
'foldl': 'reduce',
'foldr': 'reduceRight',
'include': 'includes',
'indexBy': 'keyBy',
'inject': 'reduce',
@@ -356,6 +345,7 @@
'pluck': 'map',
'restParam': 'restArgs',
'select': 'filter',
'unique': 'uniq',
'where': 'filter'
};

View File

@@ -1,4 +1,4 @@
// Backbone.js 1.2.1
// Backbone.js 1.2.3
// (c) 2010-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Backbone may be freely distributed under the MIT license.
@@ -41,10 +41,10 @@
var previousBackbone = root.Backbone;
// Create a local reference to a common array method we'll want to use later.
var slice = [].slice;
var slice = Array.prototype.slice;
// Current version of the library. Keep in sync with `package.json`.
Backbone.VERSION = '1.2.1';
Backbone.VERSION = '1.2.3';
// For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
// the `$` variable.
@@ -506,7 +506,7 @@
}
// Update the `id`.
this.id = this.get(this.idAttribute);
if (this.idAttribute in attrs) this.id = this.get(this.idAttribute);
// Trigger all relevant attribute changes.
if (!silent) {
@@ -764,6 +764,16 @@
var setOptions = {add: true, remove: true, merge: true};
var addOptions = {add: true, remove: false};
// Splices `insert` into `array` at index `at`.
var splice = function(array, insert, at) {
at = Math.min(Math.max(at, 0), array.length);
var tail = Array(array.length - at);
var length = insert.length;
for (var i = 0; i < tail.length; i++) tail[i] = array[i + at];
for (i = 0; i < length; i++) array[i + at] = insert[i];
for (i = 0; i < tail.length; i++) array[i + length + at] = tail[i];
};
// Define the Collection's inheritable methods.
_.extend(Collection.prototype, Events, {
@@ -797,9 +807,9 @@
remove: function(models, options) {
options = _.extend({}, options);
var singular = !_.isArray(models);
models = singular ? [models] : _.clone(models);
models = singular ? [models] : models.slice();
var removed = this._removeModels(models, options);
if (!options.silent && removed) this.trigger('update', this, options);
if (!options.silent && removed.length) this.trigger('update', this, options);
return singular ? removed[0] : removed;
},
@@ -808,83 +818,88 @@
// already exist in the collection, as necessary. Similar to **Model#set**,
// the core operation for updating the data contained by the collection.
set: function(models, options) {
if (models == null) return;
options = _.defaults({}, options, setOptions);
if (options.parse && !this._isModel(models)) models = this.parse(models, options);
var singular = !_.isArray(models);
models = singular ? (models ? [models] : []) : models.slice();
var id, model, attrs, existing, sort;
var at = options.at;
if (at != null) at = +at;
if (at < 0) at += this.length + 1;
var set = [];
var toAdd = [];
var toRemove = [];
var modelMap = {};
var add = options.add;
var merge = options.merge;
var remove = options.remove;
var sort = false;
var sortable = this.comparator && (at == null) && options.sort !== false;
var sortAttr = _.isString(this.comparator) ? this.comparator : null;
var toAdd = [], toRemove = [], modelMap = {};
var add = options.add, merge = options.merge, remove = options.remove;
var order = !sortable && add && remove ? [] : false;
var orderChanged = false;
// Turn bare objects into model references, and prevent invalid models
// from being added.
var model;
for (var i = 0; i < models.length; i++) {
attrs = models[i];
model = models[i];
// If a duplicate is found, prevent it from being added and
// optionally merge it into the existing model.
if (existing = this.get(attrs)) {
if (remove) modelMap[existing.cid] = true;
if (merge && attrs !== existing) {
attrs = this._isModel(attrs) ? attrs.attributes : attrs;
var existing = this.get(model);
if (existing) {
if (merge && model !== existing) {
var attrs = this._isModel(model) ? model.attributes : model;
if (options.parse) attrs = existing.parse(attrs, options);
existing.set(attrs, options);
if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;
if (sortable && !sort) sort = existing.hasChanged(sortAttr);
}
if (!modelMap[existing.cid]) {
modelMap[existing.cid] = true;
set.push(existing);
}
models[i] = existing;
// If this is a new, valid model, push it to the `toAdd` list.
} else if (add) {
model = models[i] = this._prepareModel(attrs, options);
if (!model) continue;
model = models[i] = this._prepareModel(model, options);
if (model) {
toAdd.push(model);
this._addReference(model, options);
modelMap[model.cid] = true;
set.push(model);
}
// Do not add multiple models with the same `id`.
model = existing || model;
if (!model) continue;
id = this.modelId(model.attributes);
if (order && (model.isNew() || !modelMap[id])) {
order.push(model);
// Check to see if this is actually a new model at this index.
orderChanged = orderChanged || !this.models[i] || model.cid !== this.models[i].cid;
}
modelMap[id] = true;
}
// Remove stale models.
if (remove) {
for (var i = 0; i < this.length; i++) {
if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model);
for (i = 0; i < this.length; i++) {
model = this.models[i];
if (!modelMap[model.cid]) toRemove.push(model);
}
if (toRemove.length) this._removeModels(toRemove, options);
}
// See if sorting is needed, update `length` and splice in new models.
if (toAdd.length || orderChanged) {
var orderChanged = false;
var replace = !sortable && add && remove;
if (set.length && replace) {
orderChanged = this.length != set.length || _.some(this.models, function(model, index) {
return model !== set[index];
});
this.models.length = 0;
splice(this.models, set, 0);
this.length = this.models.length;
} else if (toAdd.length) {
if (sortable) sort = true;
this.length += toAdd.length;
if (at != null) {
for (var i = 0; i < toAdd.length; i++) {
this.models.splice(at + i, 0, toAdd[i]);
}
} else {
if (order) this.models.length = 0;
var orderedModels = order || toAdd;
for (var i = 0; i < orderedModels.length; i++) {
this.models.push(orderedModels[i]);
}
}
splice(this.models, toAdd, at == null ? this.length : at);
this.length = this.models.length;
}
// Silently sort the collection if appropriate.
@@ -892,10 +907,10 @@
// Unless silenced, it's time to fire all appropriate add/sort events.
if (!options.silent) {
var addOpts = at != null ? _.clone(options) : options;
for (var i = 0; i < toAdd.length; i++) {
if (at != null) addOpts.index = at + i;
(model = toAdd[i]).trigger('add', model, this, addOpts);
for (i = 0; i < toAdd.length; i++) {
if (at != null) options.index = at + i;
model = toAdd[i];
model.trigger('add', model, this, options);
}
if (sort || orderChanged) this.trigger('sort', this, options);
if (toAdd.length || toRemove.length) this.trigger('update', this, options);
@@ -996,7 +1011,7 @@
// Pluck an attribute from each model in the collection.
pluck: function(attr) {
return _.invoke(this.models, 'get', attr);
return this.map(attr + '');
},
// Fetch the default set of models for this collection, resetting the
@@ -1088,6 +1103,12 @@
this.models.splice(index, 1);
this.length--;
// Remove references before triggering 'remove' event to prevent an
// infinite loop. #3693
delete this._byId[model.cid];
var id = this.modelId(model.attributes);
if (id != null) delete this._byId[id];
if (!options.silent) {
options.index = index;
model.trigger('remove', model, this, options);
@@ -1096,7 +1117,7 @@
removed.push(model);
this._removeReference(model, options);
}
return removed.length ? removed : false;
return removed;
},
// Method for checking whether an object should be considered a model for
@@ -1127,6 +1148,7 @@
// events simply proxy through. "add" and "remove" events that originate
// in other collections are ignored.
_onModelEvent: function(event, model, collection, options) {
if (model) {
if ((event === 'add' || event === 'remove') && collection !== this) return;
if (event === 'destroy') this.remove(model, options);
if (event === 'change') {
@@ -1137,6 +1159,7 @@
if (id != null) this._byId[id] = model;
}
}
}
this.trigger.apply(this, arguments);
}
@@ -1145,14 +1168,14 @@
// Underscore methods that we want to implement on the Collection.
// 90% of the core usefulness of Backbone Collections is actually implemented
// right here:
var collectionMethods = { forEach: 3, each: 3, map: 3, collect: 3, reduce: 4,
foldl: 4, inject: 4, reduceRight: 4, foldr: 4, find: 3, detect: 3, filter: 3,
var collectionMethods = { forEach: 3, each: 3, map: 3, collect: 3, reduce: 0,
foldl: 0, inject: 0, reduceRight: 0, foldr: 0, find: 3, detect: 3, filter: 3,
select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3,
contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3,
head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3,
without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3,
sortBy: 3, indexBy: 3};
sortBy: 3, indexBy: 3, findIndex: 3, findLastIndex: 3};
// Mix in each Underscore method as a proxy to `Collection#models`.
addUnderscoreMethods(Collection, collectionMethods, 'models');
@@ -1617,7 +1640,7 @@
this.options = _.extend({root: '/'}, this.options, options);
this.root = this.options.root;
this._wantsHashChange = this.options.hashChange !== false;
this._hasHashChange = 'onhashchange' in window;
this._hasHashChange = 'onhashchange' in window && (document.documentMode === void 0 || document.documentMode > 7);
this._useHashChange = this._wantsHashChange && this._hasHashChange;
this._wantsPushState = !!this.options.pushState;
this._hasPushState = !!(this.history && this.history.pushState);
@@ -1841,14 +1864,9 @@
_.extend(child, parent, staticProps);
// Set the prototype chain to inherit from `parent`, without calling
// `parent` constructor function.
var Surrogate = function(){ this.constructor = child; };
Surrogate.prototype = parent.prototype;
child.prototype = new Surrogate;
// Add prototype properties (instance properties) to the subclass,
// if supplied.
if (protoProps) _.extend(child.prototype, protoProps);
// `parent`'s constructor function and add the prototype properties.
child.prototype = _.create(parent.prototype, protoProps);
child.prototype.constructor = child;
// Set a convenience property in case the parent's prototype is needed
// later.

File diff suppressed because it is too large Load Diff

View File

@@ -1,41 +1,43 @@
(function() {
module("Backbone.Events");
QUnit.module("Backbone.Events");
test("on and trigger", 2, function() {
QUnit.test("on and trigger", function(assert) {
assert.expect(2);
var obj = { counter: 0 };
_.extend(obj,Backbone.Events);
obj.on('event', function() { obj.counter += 1; });
obj.trigger('event');
equal(obj.counter,1,'counter should be incremented.');
assert.equal(obj.counter, 1, 'counter should be incremented.');
obj.trigger('event');
obj.trigger('event');
obj.trigger('event');
obj.trigger('event');
equal(obj.counter, 5, 'counter should be incremented five times.');
assert.equal(obj.counter, 5, 'counter should be incremented five times.');
});
test("binding and triggering multiple events", 4, function() {
QUnit.test("binding and triggering multiple events", function(assert) {
assert.expect(4);
var obj = { counter: 0 };
_.extend(obj, Backbone.Events);
obj.on('a b c', function() { obj.counter += 1; });
obj.trigger('a');
equal(obj.counter, 1);
assert.equal(obj.counter, 1);
obj.trigger('a b');
equal(obj.counter, 3);
assert.equal(obj.counter, 3);
obj.trigger('c');
equal(obj.counter, 4);
assert.equal(obj.counter, 4);
obj.off('a c');
obj.trigger('a b c');
equal(obj.counter, 5);
assert.equal(obj.counter, 5);
});
test("binding and triggering with event maps", function() {
QUnit.test("binding and triggering with event maps", function(assert) {
var obj = { counter: 0 };
_.extend(obj, Backbone.Events);
@@ -50,23 +52,23 @@
}, obj);
obj.trigger('a');
equal(obj.counter, 1);
assert.equal(obj.counter, 1);
obj.trigger('a b');
equal(obj.counter, 3);
assert.equal(obj.counter, 3);
obj.trigger('c');
equal(obj.counter, 4);
assert.equal(obj.counter, 4);
obj.off({
a: increment,
c: increment
}, obj);
obj.trigger('a b c');
equal(obj.counter, 5);
assert.equal(obj.counter, 5);
});
test("binding and triggering multiple event names with event maps", function() {
QUnit.test("binding and triggering multiple event names with event maps", function(assert) {
var obj = { counter: 0 };
_.extend(obj, Backbone.Events);
@@ -79,29 +81,30 @@
});
obj.trigger('a');
equal(obj.counter, 1);
assert.equal(obj.counter, 1);
obj.trigger('a b');
equal(obj.counter, 3);
assert.equal(obj.counter, 3);
obj.trigger('c');
equal(obj.counter, 4);
assert.equal(obj.counter, 4);
obj.off({
'a c': increment
});
obj.trigger('a b c');
equal(obj.counter, 5);
assert.equal(obj.counter, 5);
});
test("binding and trigger with event maps context", 2, function() {
QUnit.test("binding and trigger with event maps context", function(assert) {
assert.expect(2);
var obj = { counter: 0 };
var context = {};
_.extend(obj, Backbone.Events);
obj.on({
a: function() {
strictEqual(this, context, 'defaults `context` to `callback` param');
assert.strictEqual(this, context, 'defaults `context` to `callback` param');
}
}, context).trigger('a');
@@ -112,20 +115,22 @@
}, this, context).trigger('a');
});
test("listenTo and stopListening", 1, function() {
QUnit.test("listenTo and stopListening", function(assert) {
assert.expect(1);
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
a.listenTo(b, 'all', function(){ ok(true); });
a.listenTo(b, 'all', function(){ assert.ok(true); });
b.trigger('anything');
a.listenTo(b, 'all', function(){ ok(false); });
a.listenTo(b, 'all', function(){ assert.ok(false); });
a.stopListening();
b.trigger('anything');
});
test("listenTo and stopListening with event maps", 4, function() {
QUnit.test("listenTo and stopListening with event maps", function(assert) {
assert.expect(4);
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
var cb = function(){ ok(true); };
var cb = function(){ assert.ok(true); };
a.listenTo(b, {event: cb});
b.trigger('event');
a.listenTo(b, {event2: cb});
@@ -136,10 +141,11 @@
b.trigger('event event2');
});
test("stopListening with omitted args", 2, function () {
QUnit.test("stopListening with omitted args", function(assert) {
assert.expect(2);
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
var cb = function () { ok(true); };
var cb = function () { assert.ok(true); };
a.listenTo(b, 'event', cb);
b.on('event', cb);
a.listenTo(b, 'event2', cb);
@@ -152,7 +158,8 @@
b.trigger('event2');
});
test("listenToOnce", 2, function() {
QUnit.test("listenToOnce", function(assert) {
assert.expect(2);
// Same as the previous test, but we use once rather than having to explicitly unbind
var obj = { counterA: 0, counterB: 0 };
_.extend(obj, Backbone.Events);
@@ -161,172 +168,186 @@
obj.listenToOnce(obj, 'event', incrA);
obj.listenToOnce(obj, 'event', incrB);
obj.trigger('event');
equal(obj.counterA, 1, 'counterA should have only been incremented once.');
equal(obj.counterB, 1, 'counterB should have only been incremented once.');
assert.equal(obj.counterA, 1, 'counterA should have only been incremented once.');
assert.equal(obj.counterB, 1, 'counterB should have only been incremented once.');
});
test("listenToOnce and stopListening", 1, function() {
QUnit.test("listenToOnce and stopListening", function(assert) {
assert.expect(1);
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
a.listenToOnce(b, 'all', function() { ok(true); });
a.listenToOnce(b, 'all', function() { assert.ok(true); });
b.trigger('anything');
b.trigger('anything');
a.listenToOnce(b, 'all', function() { ok(false); });
a.listenToOnce(b, 'all', function() { assert.ok(false); });
a.stopListening();
b.trigger('anything');
});
test("listenTo, listenToOnce and stopListening", 1, function() {
QUnit.test("listenTo, listenToOnce and stopListening", function(assert) {
assert.expect(1);
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
a.listenToOnce(b, 'all', function() { ok(true); });
a.listenToOnce(b, 'all', function() { assert.ok(true); });
b.trigger('anything');
b.trigger('anything');
a.listenTo(b, 'all', function() { ok(false); });
a.listenTo(b, 'all', function() { assert.ok(false); });
a.stopListening();
b.trigger('anything');
});
test("listenTo and stopListening with event maps", 1, function() {
QUnit.test("listenTo and stopListening with event maps", function(assert) {
assert.expect(1);
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
a.listenTo(b, {change: function(){ ok(true); }});
a.listenTo(b, {change: function(){ assert.ok(true); }});
b.trigger('change');
a.listenTo(b, {change: function(){ ok(false); }});
a.listenTo(b, {change: function(){ assert.ok(false); }});
a.stopListening();
b.trigger('change');
});
test("listenTo yourself", 1, function(){
QUnit.test("listenTo yourself", function(assert) {
assert.expect(1);
var e = _.extend({}, Backbone.Events);
e.listenTo(e, "foo", function(){ ok(true); });
e.listenTo(e, "foo", function(){ assert.ok(true); });
e.trigger("foo");
});
test("listenTo yourself cleans yourself up with stopListening", 1, function(){
QUnit.test("listenTo yourself cleans yourself up with stopListening", function(assert) {
assert.expect(1);
var e = _.extend({}, Backbone.Events);
e.listenTo(e, "foo", function(){ ok(true); });
e.listenTo(e, "foo", function(){ assert.ok(true); });
e.trigger("foo");
e.stopListening();
e.trigger("foo");
});
test("stopListening cleans up references", 12, function() {
QUnit.test("stopListening cleans up references", function(assert) {
assert.expect(12);
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
var fn = function() {};
b.on('event', fn);
a.listenTo(b, 'event', fn).stopListening();
equal(_.size(a._listeningTo), 0);
equal(_.size(b._events.event), 1);
equal(_.size(b._listeners), 0);
assert.equal(_.size(a._listeningTo), 0);
assert.equal(_.size(b._events.event), 1);
assert.equal(_.size(b._listeners), 0);
a.listenTo(b, 'event', fn).stopListening(b);
equal(_.size(a._listeningTo), 0);
equal(_.size(b._events.event), 1);
equal(_.size(b._listeners), 0);
assert.equal(_.size(a._listeningTo), 0);
assert.equal(_.size(b._events.event), 1);
assert.equal(_.size(b._listeners), 0);
a.listenTo(b, 'event', fn).stopListening(b, 'event');
equal(_.size(a._listeningTo), 0);
equal(_.size(b._events.event), 1);
equal(_.size(b._listeners), 0);
assert.equal(_.size(a._listeningTo), 0);
assert.equal(_.size(b._events.event), 1);
assert.equal(_.size(b._listeners), 0);
a.listenTo(b, 'event', fn).stopListening(b, 'event', fn);
equal(_.size(a._listeningTo), 0);
equal(_.size(b._events.event), 1);
equal(_.size(b._listeners), 0);
assert.equal(_.size(a._listeningTo), 0);
assert.equal(_.size(b._events.event), 1);
assert.equal(_.size(b._listeners), 0);
});
test("stopListening cleans up references from listenToOnce", 12, function() {
QUnit.test("stopListening cleans up references from listenToOnce", function(assert) {
assert.expect(12);
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
var fn = function() {};
b.on('event', fn);
a.listenToOnce(b, 'event', fn).stopListening();
equal(_.size(a._listeningTo), 0);
equal(_.size(b._events.event), 1);
equal(_.size(b._listeners), 0);
assert.equal(_.size(a._listeningTo), 0);
assert.equal(_.size(b._events.event), 1);
assert.equal(_.size(b._listeners), 0);
a.listenToOnce(b, 'event', fn).stopListening(b);
equal(_.size(a._listeningTo), 0);
equal(_.size(b._events.event), 1);
equal(_.size(b._listeners), 0);
assert.equal(_.size(a._listeningTo), 0);
assert.equal(_.size(b._events.event), 1);
assert.equal(_.size(b._listeners), 0);
a.listenToOnce(b, 'event', fn).stopListening(b, 'event');
equal(_.size(a._listeningTo), 0);
equal(_.size(b._events.event), 1);
equal(_.size(b._listeners), 0);
assert.equal(_.size(a._listeningTo), 0);
assert.equal(_.size(b._events.event), 1);
assert.equal(_.size(b._listeners), 0);
a.listenToOnce(b, 'event', fn).stopListening(b, 'event', fn);
equal(_.size(a._listeningTo), 0);
equal(_.size(b._events.event), 1);
equal(_.size(b._listeners), 0);
assert.equal(_.size(a._listeningTo), 0);
assert.equal(_.size(b._events.event), 1);
assert.equal(_.size(b._listeners), 0);
});
test("listenTo and off cleaning up references", 8, function() {
QUnit.test("listenTo and off cleaning up references", function(assert) {
assert.expect(8);
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
var fn = function() {};
a.listenTo(b, 'event', fn);
b.off();
equal(_.size(a._listeningTo), 0);
equal(_.size(b._listeners), 0);
assert.equal(_.size(a._listeningTo), 0);
assert.equal(_.size(b._listeners), 0);
a.listenTo(b, 'event', fn);
b.off('event');
equal(_.size(a._listeningTo), 0);
equal(_.size(b._listeners), 0);
assert.equal(_.size(a._listeningTo), 0);
assert.equal(_.size(b._listeners), 0);
a.listenTo(b, 'event', fn);
b.off(null, fn);
equal(_.size(a._listeningTo), 0);
equal(_.size(b._listeners), 0);
assert.equal(_.size(a._listeningTo), 0);
assert.equal(_.size(b._listeners), 0);
a.listenTo(b, 'event', fn);
b.off(null, null, a);
equal(_.size(a._listeningTo), 0);
equal(_.size(b._listeners), 0);
assert.equal(_.size(a._listeningTo), 0);
assert.equal(_.size(b._listeners), 0);
});
test("listenTo and stopListening cleaning up references", 2, function() {
QUnit.test("listenTo and stopListening cleaning up references", function(assert) {
assert.expect(2);
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
a.listenTo(b, 'all', function(){ ok(true); });
a.listenTo(b, 'all', function(){ assert.ok(true); });
b.trigger('anything');
a.listenTo(b, 'other', function(){ ok(false); });
a.listenTo(b, 'other', function(){ assert.ok(false); });
a.stopListening(b, 'other');
a.stopListening(b, 'all');
equal(_.size(a._listeningTo), 0);
assert.equal(_.size(a._listeningTo), 0);
});
test("listenToOnce without context cleans up references after the event has fired", 2, function() {
QUnit.test("listenToOnce without context cleans up references after the event has fired", function(assert) {
assert.expect(2);
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
a.listenToOnce(b, 'all', function(){ ok(true); });
a.listenToOnce(b, 'all', function(){ assert.ok(true); });
b.trigger('anything');
equal(_.size(a._listeningTo), 0);
assert.equal(_.size(a._listeningTo), 0);
});
test("listenToOnce with event maps cleans up references", 2, function() {
QUnit.test("listenToOnce with event maps cleans up references", function(assert) {
assert.expect(2);
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
a.listenToOnce(b, {
one: function() { ok(true); },
two: function() { ok(false); }
one: function() { assert.ok(true); },
two: function() { assert.ok(false); }
});
b.trigger('one');
equal(_.size(a._listeningTo), 1);
assert.equal(_.size(a._listeningTo), 1);
});
test("listenToOnce with event maps binds the correct `this`", 1, function() {
QUnit.test("listenToOnce with event maps binds the correct `this`", function(assert) {
assert.expect(1);
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
a.listenToOnce(b, {
one: function() { ok(this === a); },
two: function() { ok(false); }
one: function() { assert.ok(this === a); },
two: function() { assert.ok(false); }
});
b.trigger('one');
});
test("listenTo with empty callback doesn't throw an error", 1, function(){
QUnit.test("listenTo with empty callback doesn't throw an error", function(assert) {
assert.expect(1);
var e = _.extend({}, Backbone.Events);
e.listenTo(e, "foo", null);
e.trigger("foo");
ok(true);
assert.ok(true);
});
test("trigger all for each event", 3, function() {
QUnit.test("trigger all for each event", function(assert) {
assert.expect(3);
var a, b, obj = { counter: 0 };
_.extend(obj, Backbone.Events);
obj.on('all', function(event) {
@@ -335,12 +356,13 @@
if (event == 'b') b = true;
})
.trigger('a b');
ok(a);
ok(b);
equal(obj.counter, 2);
assert.ok(a);
assert.ok(b);
assert.equal(obj.counter, 2);
});
test("on, then unbind all functions", 1, function() {
QUnit.test("on, then unbind all functions", function(assert) {
assert.expect(1);
var obj = { counter: 0 };
_.extend(obj,Backbone.Events);
var callback = function() { obj.counter += 1; };
@@ -348,10 +370,11 @@
obj.trigger('event');
obj.off('event');
obj.trigger('event');
equal(obj.counter, 1, 'counter should have only been incremented once.');
assert.equal(obj.counter, 1, 'counter should have only been incremented once.');
});
test("bind two callbacks, unbind only one", 2, function() {
QUnit.test("bind two callbacks, unbind only one", function(assert) {
assert.expect(2);
var obj = { counterA: 0, counterB: 0 };
_.extend(obj,Backbone.Events);
var callback = function() { obj.counterA += 1; };
@@ -360,11 +383,12 @@
obj.trigger('event');
obj.off('event', callback);
obj.trigger('event');
equal(obj.counterA, 1, 'counterA should have only been incremented once.');
equal(obj.counterB, 2, 'counterB should have been incremented twice.');
assert.equal(obj.counterA, 1, 'counterA should have only been incremented once.');
assert.equal(obj.counterB, 2, 'counterB should have been incremented twice.');
});
test("unbind a callback in the midst of it firing", 1, function() {
QUnit.test("unbind a callback in the midst of it firing", function(assert) {
assert.expect(1);
var obj = {counter: 0};
_.extend(obj, Backbone.Events);
var callback = function() {
@@ -375,10 +399,11 @@
obj.trigger('event');
obj.trigger('event');
obj.trigger('event');
equal(obj.counter, 1, 'the callback should have been unbound.');
assert.equal(obj.counter, 1, 'the callback should have been unbound.');
});
test("two binds that unbind themeselves", 2, function() {
QUnit.test("two binds that unbind themeselves", function(assert) {
assert.expect(2);
var obj = { counterA: 0, counterB: 0 };
_.extend(obj,Backbone.Events);
var incrA = function(){ obj.counterA += 1; obj.off('event', incrA); };
@@ -388,16 +413,17 @@
obj.trigger('event');
obj.trigger('event');
obj.trigger('event');
equal(obj.counterA, 1, 'counterA should have only been incremented once.');
equal(obj.counterB, 1, 'counterB should have only been incremented once.');
assert.equal(obj.counterA, 1, 'counterA should have only been incremented once.');
assert.equal(obj.counterB, 1, 'counterB should have only been incremented once.');
});
test("bind a callback with a supplied context", 1, function () {
QUnit.test("bind a callback with a supplied context", function(assert) {
assert.expect(1);
var TestClass = function () {
return this;
};
TestClass.prototype.assertTrue = function () {
ok(true, '`this` was bound to the callback');
assert.ok(true, '`this` was bound to the callback');
};
var obj = _.extend({},Backbone.Events);
@@ -405,7 +431,8 @@
obj.trigger('event');
});
test("nested trigger with unbind", 1, function () {
QUnit.test("nested trigger with unbind", function(assert) {
assert.expect(1);
var obj = { counter: 0 };
_.extend(obj, Backbone.Events);
var incr1 = function(){ obj.counter += 1; obj.off('event', incr1); obj.trigger('event'); };
@@ -413,23 +440,25 @@
obj.on('event', incr1);
obj.on('event', incr2);
obj.trigger('event');
equal(obj.counter, 3, 'counter should have been incremented three times');
assert.equal(obj.counter, 3, 'counter should have been incremented three times');
});
test("callback list is not altered during trigger", 2, function () {
QUnit.test("callback list is not altered during trigger", function(assert) {
assert.expect(2);
var counter = 0, obj = _.extend({}, Backbone.Events);
var incr = function(){ counter++; };
var incrOn = function(){ obj.on('event all', incr); };
var incrOff = function(){ obj.off('event all', incr); };
obj.on('event all', incrOn).trigger('event');
equal(counter, 0, 'on does not alter callback list');
assert.equal(counter, 0, 'on does not alter callback list');
obj.off().on('event', incrOff).on('event all', incr).trigger('event');
equal(counter, 2, 'off does not alter callback list');
assert.equal(counter, 2, 'off does not alter callback list');
});
test("#1282 - 'all' callback list is retrieved after each event.", 1, function() {
QUnit.test("#1282 - 'all' callback list is retrieved after each event.", function(assert) {
assert.expect(1);
var counter = 0;
var obj = _.extend({}, Backbone.Events);
var incr = function(){ counter++; };
@@ -437,47 +466,53 @@
obj.on('y', incr).on('all', incr);
})
.trigger('x y');
strictEqual(counter, 2);
assert.strictEqual(counter, 2);
});
test("if no callback is provided, `on` is a noop", 0, function() {
QUnit.test("if no callback is provided, `on` is a noop", function(assert) {
assert.expect(0);
_.extend({}, Backbone.Events).on('test').trigger('test');
});
test("if callback is truthy but not a function, `on` should throw an error just like jQuery", 1, function() {
QUnit.test("if callback is truthy but not a function, `on` should throw an error just like jQuery", function(assert) {
assert.expect(1);
var view = _.extend({}, Backbone.Events).on('test', 'noop');
throws(function() {
assert.throws(function() {
view.trigger('test');
});
});
test("remove all events for a specific context", 4, function() {
QUnit.test("remove all events for a specific context", function(assert) {
assert.expect(4);
var obj = _.extend({}, Backbone.Events);
obj.on('x y all', function() { ok(true); });
obj.on('x y all', function() { ok(false); }, obj);
obj.on('x y all', function() { assert.ok(true); });
obj.on('x y all', function() { assert.ok(false); }, obj);
obj.off(null, null, obj);
obj.trigger('x y');
});
test("remove all events for a specific callback", 4, function() {
QUnit.test("remove all events for a specific callback", function(assert) {
assert.expect(4);
var obj = _.extend({}, Backbone.Events);
var success = function() { ok(true); };
var fail = function() { ok(false); };
var success = function() { assert.ok(true); };
var fail = function() { assert.ok(false); };
obj.on('x y all', success);
obj.on('x y all', fail);
obj.off(null, fail);
obj.trigger('x y');
});
test("#1310 - off does not skip consecutive events", 0, function() {
QUnit.test("#1310 - off does not skip consecutive events", function(assert) {
assert.expect(0);
var obj = _.extend({}, Backbone.Events);
obj.on('event', function() { ok(false); }, obj);
obj.on('event', function() { ok(false); }, obj);
obj.on('event', function() { assert.ok(false); }, obj);
obj.on('event', function() { assert.ok(false); }, obj);
obj.off(null, null, obj);
obj.trigger('event');
});
test("once", 2, function() {
QUnit.test("once", function(assert) {
assert.expect(2);
// Same as the previous test, but we use once rather than having to explicitly unbind
var obj = { counterA: 0, counterB: 0 };
_.extend(obj, Backbone.Events);
@@ -486,12 +521,13 @@
obj.once('event', incrA);
obj.once('event', incrB);
obj.trigger('event');
equal(obj.counterA, 1, 'counterA should have only been incremented once.');
equal(obj.counterB, 1, 'counterB should have only been incremented once.');
assert.equal(obj.counterA, 1, 'counterA should have only been incremented once.');
assert.equal(obj.counterB, 1, 'counterB should have only been incremented once.');
});
test("once variant one", 3, function() {
var f = function(){ ok(true); };
QUnit.test("once variant one", function(assert) {
assert.expect(3);
var f = function(){ assert.ok(true); };
var a = _.extend({}, Backbone.Events).once('event', f);
var b = _.extend({}, Backbone.Events).on('event', f);
@@ -502,8 +538,9 @@
b.trigger('event');
});
test("once variant two", 3, function() {
var f = function(){ ok(true); };
QUnit.test("once variant two", function(assert) {
assert.expect(3);
var f = function(){ assert.ok(true); };
var obj = _.extend({}, Backbone.Events);
obj
@@ -513,8 +550,9 @@
.trigger('event');
});
test("once with off", 0, function() {
var f = function(){ ok(true); };
QUnit.test("once with off", function(assert) {
assert.expect(0);
var f = function(){ assert.ok(true); };
var obj = _.extend({}, Backbone.Events);
obj.once('event', f);
@@ -522,7 +560,7 @@
obj.trigger('event');
});
test("once with event maps", function() {
QUnit.test("once with event maps", function(assert) {
var obj = { counter: 0 };
_.extend(obj, Backbone.Events);
@@ -537,94 +575,103 @@
}, obj);
obj.trigger('a');
equal(obj.counter, 1);
assert.equal(obj.counter, 1);
obj.trigger('a b');
equal(obj.counter, 2);
assert.equal(obj.counter, 2);
obj.trigger('c');
equal(obj.counter, 3);
assert.equal(obj.counter, 3);
obj.trigger('a b c');
equal(obj.counter, 3);
assert.equal(obj.counter, 3);
});
test("once with off only by context", 0, function() {
QUnit.test("once with off only by context", function(assert) {
assert.expect(0);
var context = {};
var obj = _.extend({}, Backbone.Events);
obj.once('event', function(){ ok(false); }, context);
obj.once('event', function(){ assert.ok(false); }, context);
obj.off(null, null, context);
obj.trigger('event');
});
test("Backbone object inherits Events", function() {
ok(Backbone.on === Backbone.Events.on);
QUnit.test("Backbone object inherits Events", function(assert) {
assert.ok(Backbone.on === Backbone.Events.on);
});
asyncTest("once with asynchronous events", 1, function() {
var func = _.debounce(function() { ok(true); start(); }, 50);
QUnit.test("once with asynchronous events", function(assert) {
var done = assert.async();
assert.expect(1);
var func = _.debounce(function() { assert.ok(true); done(); }, 50);
var obj = _.extend({}, Backbone.Events).once('async', func);
obj.trigger('async');
obj.trigger('async');
});
test("once with multiple events.", 2, function() {
QUnit.test("once with multiple events.", function(assert) {
assert.expect(2);
var obj = _.extend({}, Backbone.Events);
obj.once('x y', function() { ok(true); });
obj.once('x y', function() { assert.ok(true); });
obj.trigger('x y');
});
test("Off during iteration with once.", 2, function() {
QUnit.test("Off during iteration with once.", function(assert) {
assert.expect(2);
var obj = _.extend({}, Backbone.Events);
var f = function(){ this.off('event', f); };
obj.on('event', f);
obj.once('event', function(){});
obj.on('event', function(){ ok(true); });
obj.on('event', function(){ assert.ok(true); });
obj.trigger('event');
obj.trigger('event');
});
test("`once` on `all` should work as expected", 1, function() {
QUnit.test("`once` on `all` should work as expected", function(assert) {
assert.expect(1);
Backbone.once('all', function() {
ok(true);
assert.ok(true);
Backbone.trigger('all');
});
Backbone.trigger('all');
});
test("once without a callback is a noop", 0, function() {
QUnit.test("once without a callback is a noop", function(assert) {
assert.expect(0);
_.extend({}, Backbone.Events).once('event').trigger('event');
});
test("listenToOnce without a callback is a noop", 0, function() {
QUnit.test("listenToOnce without a callback is a noop", function(assert) {
assert.expect(0);
var obj = _.extend({}, Backbone.Events);
obj.listenToOnce(obj, 'event').trigger('event');
});
test("event functions are chainable", function() {
QUnit.test("event functions are chainable", function(assert) {
var obj = _.extend({}, Backbone.Events);
var obj2 = _.extend({}, Backbone.Events);
var fn = function() {};
equal(obj, obj.trigger('noeventssetyet'));
equal(obj, obj.off('noeventssetyet'));
equal(obj, obj.stopListening('noeventssetyet'));
equal(obj, obj.on('a', fn));
equal(obj, obj.once('c', fn));
equal(obj, obj.trigger('a'));
equal(obj, obj.listenTo(obj2, 'a', fn));
equal(obj, obj.listenToOnce(obj2, 'b', fn));
equal(obj, obj.off('a c'));
equal(obj, obj.stopListening(obj2, 'a'));
equal(obj, obj.stopListening());
assert.equal(obj, obj.trigger('noeventssetyet'));
assert.equal(obj, obj.off('noeventssetyet'));
assert.equal(obj, obj.stopListening('noeventssetyet'));
assert.equal(obj, obj.on('a', fn));
assert.equal(obj, obj.once('c', fn));
assert.equal(obj, obj.trigger('a'));
assert.equal(obj, obj.listenTo(obj2, 'a', fn));
assert.equal(obj, obj.listenToOnce(obj2, 'b', fn));
assert.equal(obj, obj.off('a c'));
assert.equal(obj, obj.stopListening(obj2, 'a'));
assert.equal(obj, obj.stopListening());
});
test("#3448 - listenToOnce with space-separated events", 2, function() {
QUnit.test("#3448 - listenToOnce with space-separated events", function(assert) {
assert.expect(2);
var one = _.extend({}, Backbone.Events);
var two = _.extend({}, Backbone.Events);
var count = 1;
one.listenToOnce(two, 'x y', function(n) { ok(n === count++); });
one.listenToOnce(two, 'x y', function(n) { assert.ok(n === count++); });
two.trigger('x', 1);
two.trigger('x', 1);
two.trigger('y', 2);

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,13 @@
(function() {
module("Backbone.noConflict");
QUnit.module("Backbone.noConflict");
test('noConflict', 2, function() {
QUnit.test('noConflict', function(assert) {
assert.expect(2);
var noconflictBackbone = Backbone.noConflict();
equal(window.Backbone, undefined, 'Returned window.Backbone');
assert.equal(window.Backbone, undefined, 'Returned window.Backbone');
window.Backbone = noconflictBackbone;
equal(window.Backbone, noconflictBackbone, 'Backbone is still pointing to the original Backbone');
assert.equal(window.Backbone, noconflictBackbone, 'Backbone is still pointing to the original Backbone');
});
})();

View File

@@ -40,7 +40,7 @@
});
module("Backbone.Router", {
QUnit.module("Backbone.Router", {
setup: function() {
location = new Location('http://example.com');
@@ -176,167 +176,189 @@
});
test("initialize", 1, function() {
equal(router.testing, 101);
QUnit.test("initialize", function(assert) {
assert.expect(1);
assert.equal(router.testing, 101);
});
test("routes (simple)", 4, function() {
QUnit.test("routes (simple)", function(assert) {
assert.expect(4);
location.replace('http://example.com#search/news');
Backbone.history.checkUrl();
equal(router.query, 'news');
equal(router.page, void 0);
equal(lastRoute, 'search');
equal(lastArgs[0], 'news');
assert.equal(router.query, 'news');
assert.equal(router.page, void 0);
assert.equal(lastRoute, 'search');
assert.equal(lastArgs[0], 'news');
});
test("routes (simple, but unicode)", 4, function() {
QUnit.test("routes (simple, but unicode)", function(assert) {
assert.expect(4);
location.replace('http://example.com#search/тест');
Backbone.history.checkUrl();
equal(router.query, "тест");
equal(router.page, void 0);
equal(lastRoute, 'search');
equal(lastArgs[0], "тест");
assert.equal(router.query, "тест");
assert.equal(router.page, void 0);
assert.equal(lastRoute, 'search');
assert.equal(lastArgs[0], "тест");
});
test("routes (two part)", 2, function() {
QUnit.test("routes (two part)", function(assert) {
assert.expect(2);
location.replace('http://example.com#search/nyc/p10');
Backbone.history.checkUrl();
equal(router.query, 'nyc');
equal(router.page, '10');
assert.equal(router.query, 'nyc');
assert.equal(router.page, '10');
});
test("routes via navigate", 2, function() {
QUnit.test("routes via navigate", function(assert) {
assert.expect(2);
Backbone.history.navigate('search/manhattan/p20', {trigger: true});
equal(router.query, 'manhattan');
equal(router.page, '20');
assert.equal(router.query, 'manhattan');
assert.equal(router.page, '20');
});
test("routes via navigate with params", 1, function() {
QUnit.test("routes via navigate with params", function(assert) {
assert.expect(1);
Backbone.history.navigate('query/test?a=b', {trigger: true});
equal(router.queryArgs, 'a=b');
assert.equal(router.queryArgs, 'a=b');
});
test("routes via navigate for backwards-compatibility", 2, function() {
QUnit.test("routes via navigate for backwards-compatibility", function(assert) {
assert.expect(2);
Backbone.history.navigate('search/manhattan/p20', true);
equal(router.query, 'manhattan');
equal(router.page, '20');
assert.equal(router.query, 'manhattan');
assert.equal(router.page, '20');
});
test("reports matched route via nagivate", 1, function() {
ok(Backbone.history.navigate('search/manhattan/p20', true));
QUnit.test("reports matched route via nagivate", function(assert) {
assert.expect(1);
assert.ok(Backbone.history.navigate('search/manhattan/p20', true));
});
test("route precedence via navigate", 6, function(){
QUnit.test("route precedence via navigate", function(assert){
assert.expect(6);
// check both 0.9.x and backwards-compatibility options
_.each([ { trigger: true }, true ], function( options ){
Backbone.history.navigate('contacts', options);
equal(router.contact, 'index');
assert.equal(router.contact, 'index');
Backbone.history.navigate('contacts/new', options);
equal(router.contact, 'new');
assert.equal(router.contact, 'new');
Backbone.history.navigate('contacts/foo', options);
equal(router.contact, 'load');
assert.equal(router.contact, 'load');
});
});
test("loadUrl is not called for identical routes.", 0, function() {
Backbone.history.loadUrl = function(){ ok(false); };
QUnit.test("loadUrl is not called for identical routes.", function(assert) {
assert.expect(0);
Backbone.history.loadUrl = function(){ assert.ok(false); };
location.replace('http://example.com#route');
Backbone.history.navigate('route');
Backbone.history.navigate('/route');
Backbone.history.navigate('/route');
});
test("use implicit callback if none provided", 1, function() {
QUnit.test("use implicit callback if none provided", function(assert) {
assert.expect(1);
router.count = 0;
router.navigate('implicit', {trigger: true});
equal(router.count, 1);
assert.equal(router.count, 1);
});
test("routes via navigate with {replace: true}", 1, function() {
QUnit.test("routes via navigate with {replace: true}", function(assert) {
assert.expect(1);
location.replace('http://example.com#start_here');
Backbone.history.checkUrl();
location.replace = function(href) {
strictEqual(href, new Location('http://example.com#end_here').href);
assert.strictEqual(href, new Location('http://example.com#end_here').href);
};
Backbone.history.navigate('end_here', {replace: true});
});
test("routes (splats)", 1, function() {
QUnit.test("routes (splats)", function(assert) {
assert.expect(1);
location.replace('http://example.com#splat/long-list/of/splatted_99args/end');
Backbone.history.checkUrl();
equal(router.args, 'long-list/of/splatted_99args');
assert.equal(router.args, 'long-list/of/splatted_99args');
});
test("routes (github)", 3, function() {
QUnit.test("routes (github)", function(assert) {
assert.expect(3);
location.replace('http://example.com#backbone/compare/1.0...braddunbar:with/slash');
Backbone.history.checkUrl();
equal(router.repo, 'backbone');
equal(router.from, '1.0');
equal(router.to, 'braddunbar:with/slash');
assert.equal(router.repo, 'backbone');
assert.equal(router.from, '1.0');
assert.equal(router.to, 'braddunbar:with/slash');
});
test("routes (optional)", 2, function() {
QUnit.test("routes (optional)", function(assert) {
assert.expect(2);
location.replace('http://example.com#optional');
Backbone.history.checkUrl();
ok(!router.arg);
assert.ok(!router.arg);
location.replace('http://example.com#optional/thing');
Backbone.history.checkUrl();
equal(router.arg, 'thing');
assert.equal(router.arg, 'thing');
});
test("routes (complex)", 3, function() {
QUnit.test("routes (complex)", function(assert) {
assert.expect(3);
location.replace('http://example.com#one/two/three/complex-part/four/five/six/seven');
Backbone.history.checkUrl();
equal(router.first, 'one/two/three');
equal(router.part, 'part');
equal(router.rest, 'four/five/six/seven');
assert.equal(router.first, 'one/two/three');
assert.equal(router.part, 'part');
assert.equal(router.rest, 'four/five/six/seven');
});
test("routes (query)", 5, function() {
QUnit.test("routes (query)", function(assert) {
assert.expect(5);
location.replace('http://example.com#query/mandel?a=b&c=d');
Backbone.history.checkUrl();
equal(router.entity, 'mandel');
equal(router.queryArgs, 'a=b&c=d');
equal(lastRoute, 'query');
equal(lastArgs[0], 'mandel');
equal(lastArgs[1], 'a=b&c=d');
assert.equal(router.entity, 'mandel');
assert.equal(router.queryArgs, 'a=b&c=d');
assert.equal(lastRoute, 'query');
assert.equal(lastArgs[0], 'mandel');
assert.equal(lastArgs[1], 'a=b&c=d');
});
test("routes (anything)", 1, function() {
QUnit.test("routes (anything)", function(assert) {
assert.expect(1);
location.replace('http://example.com#doesnt-match-a-route');
Backbone.history.checkUrl();
equal(router.anything, 'doesnt-match-a-route');
assert.equal(router.anything, 'doesnt-match-a-route');
});
test("routes (function)", 3, function() {
QUnit.test("routes (function)", function(assert) {
assert.expect(3);
router.on('route', function(name) {
ok(name === '');
assert.ok(name === '');
});
equal(ExternalObject.value, 'unset');
assert.equal(ExternalObject.value, 'unset');
location.replace('http://example.com#function/set');
Backbone.history.checkUrl();
equal(ExternalObject.value, 'set');
assert.equal(ExternalObject.value, 'set');
});
test("Decode named parameters, not splats.", 2, function() {
QUnit.test("Decode named parameters, not splats.", function(assert) {
assert.expect(2);
location.replace('http://example.com#decode/a%2Fb/c%2Fd/e');
Backbone.history.checkUrl();
strictEqual(router.named, 'a/b');
strictEqual(router.path, 'c/d/e');
assert.strictEqual(router.named, 'a/b');
assert.strictEqual(router.path, 'c/d/e');
});
test("fires event when router doesn't have callback on it", 1, function() {
router.on("route:noCallback", function(){ ok(true); });
QUnit.test("fires event when router doesn't have callback on it", function(assert) {
assert.expect(1);
router.on("route:noCallback", function(){ assert.ok(true); });
location.replace('http://example.com#noCallback');
Backbone.history.checkUrl();
});
test("No events are triggered if #execute returns false.", 1, function() {
QUnit.test("No events are triggered if #execute returns false.", function(assert) {
assert.expect(1);
var Router = Backbone.Router.extend({
routes: {
foo: function() {
ok(true);
assert.ok(true);
}
},
@@ -350,92 +372,100 @@
var router = new Router;
router.on('route route:foo', function() {
ok(false);
assert.ok(false);
});
Backbone.history.on('route', function() {
ok(false);
assert.ok(false);
});
location.replace('http://example.com#foo');
Backbone.history.checkUrl();
});
test("#933, #908 - leading slash", 2, function() {
QUnit.test("#933, #908 - leading slash", function(assert) {
assert.expect(2);
location.replace('http://example.com/root/foo');
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
Backbone.history.start({root: '/root', hashChange: false, silent: true});
strictEqual(Backbone.history.getFragment(), 'foo');
assert.strictEqual(Backbone.history.getFragment(), 'foo');
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
Backbone.history.start({root: '/root/', hashChange: false, silent: true});
strictEqual(Backbone.history.getFragment(), 'foo');
assert.strictEqual(Backbone.history.getFragment(), 'foo');
});
test("#967 - Route callback gets passed encoded values.", 3, function() {
QUnit.test("#967 - Route callback gets passed encoded values.", function(assert) {
assert.expect(3);
var route = 'has%2Fslash/complex-has%23hash/has%20space';
Backbone.history.navigate(route, {trigger: true});
strictEqual(router.first, 'has/slash');
strictEqual(router.part, 'has#hash');
strictEqual(router.rest, 'has space');
assert.strictEqual(router.first, 'has/slash');
assert.strictEqual(router.part, 'has#hash');
assert.strictEqual(router.rest, 'has space');
});
test("correctly handles URLs with % (#868)", 3, function() {
QUnit.test("correctly handles URLs with % (#868)", function(assert) {
assert.expect(3);
location.replace('http://example.com#search/fat%3A1.5%25');
Backbone.history.checkUrl();
location.replace('http://example.com#search/fat');
Backbone.history.checkUrl();
equal(router.query, 'fat');
equal(router.page, void 0);
equal(lastRoute, 'search');
assert.equal(router.query, 'fat');
assert.equal(router.page, void 0);
assert.equal(lastRoute, 'search');
});
test("#2666 - Hashes with UTF8 in them.", 2, function() {
QUnit.test("#2666 - Hashes with UTF8 in them.", function(assert) {
assert.expect(2);
Backbone.history.navigate('charñ', {trigger: true});
equal(router.charType, 'UTF');
assert.equal(router.charType, 'UTF');
Backbone.history.navigate('char%C3%B1', {trigger: true});
equal(router.charType, 'UTF');
assert.equal(router.charType, 'UTF');
});
test("#1185 - Use pathname when hashChange is not wanted.", 1, function() {
QUnit.test("#1185 - Use pathname when hashChange is not wanted.", function(assert) {
assert.expect(1);
Backbone.history.stop();
location.replace('http://example.com/path/name#hash');
Backbone.history = _.extend(new Backbone.History, {location: location});
Backbone.history.start({hashChange: false});
var fragment = Backbone.history.getFragment();
strictEqual(fragment, location.pathname.replace(/^\//, ''));
assert.strictEqual(fragment, location.pathname.replace(/^\//, ''));
});
test("#1206 - Strip leading slash before location.assign.", 1, function() {
QUnit.test("#1206 - Strip leading slash before location.assign.", function(assert) {
assert.expect(1);
Backbone.history.stop();
location.replace('http://example.com/root/');
Backbone.history = _.extend(new Backbone.History, {location: location});
Backbone.history.start({hashChange: false, root: '/root/'});
location.assign = function(pathname) {
strictEqual(pathname, '/root/fragment');
assert.strictEqual(pathname, '/root/fragment');
};
Backbone.history.navigate('/fragment');
});
test("#1387 - Root fragment without trailing slash.", 1, function() {
QUnit.test("#1387 - Root fragment without trailing slash.", function(assert) {
assert.expect(1);
Backbone.history.stop();
location.replace('http://example.com/root');
Backbone.history = _.extend(new Backbone.History, {location: location});
Backbone.history.start({hashChange: false, root: '/root/', silent: true});
strictEqual(Backbone.history.getFragment(), '');
assert.strictEqual(Backbone.history.getFragment(), '');
});
test("#1366 - History does not prepend root to fragment.", 2, function() {
QUnit.test("#1366 - History does not prepend root to fragment.", function(assert) {
assert.expect(2);
Backbone.history.stop();
location.replace('http://example.com/root/');
Backbone.history = _.extend(new Backbone.History, {
location: location,
history: {
pushState: function(state, title, url) {
strictEqual(url, '/root/x');
assert.strictEqual(url, '/root/x');
}
}
});
@@ -445,17 +475,18 @@
hashChange: false
});
Backbone.history.navigate('x');
strictEqual(Backbone.history.fragment, 'x');
assert.strictEqual(Backbone.history.fragment, 'x');
});
test("Normalize root.", 1, function() {
QUnit.test("Normalize root.", function(assert) {
assert.expect(1);
Backbone.history.stop();
location.replace('http://example.com/root');
Backbone.history = _.extend(new Backbone.History, {
location: location,
history: {
pushState: function(state, title, url) {
strictEqual(url, '/root/fragment');
assert.strictEqual(url, '/root/fragment');
}
}
});
@@ -467,7 +498,8 @@
Backbone.history.navigate('fragment');
});
test("Normalize root.", 1, function() {
QUnit.test("Normalize root.", function(assert) {
assert.expect(1);
Backbone.history.stop();
location.replace('http://example.com/root#fragment');
Backbone.history = _.extend(new Backbone.History, {
@@ -475,7 +507,7 @@
history: {
pushState: function(state, title, url) {},
replaceState: function(state, title, url) {
strictEqual(url, '/root/fragment');
assert.strictEqual(url, '/root/fragment');
}
}
});
@@ -485,18 +517,20 @@
});
});
test("Normalize root.", 1, function() {
QUnit.test("Normalize root.", function(assert) {
assert.expect(1);
Backbone.history.stop();
location.replace('http://example.com/root');
Backbone.history = _.extend(new Backbone.History, {location: location});
Backbone.history.loadUrl = function() { ok(true); };
Backbone.history.loadUrl = function() { assert.ok(true); };
Backbone.history.start({
pushState: true,
root: '/root'
});
});
test("Normalize root - leading slash.", 1, function() {
QUnit.test("Normalize root - leading slash.", function(assert) {
assert.expect(1);
Backbone.history.stop();
location.replace('http://example.com/root');
Backbone.history = _.extend(new Backbone.History, {
@@ -507,10 +541,11 @@
}
});
Backbone.history.start({root: 'root'});
strictEqual(Backbone.history.root, '/root/');
assert.strictEqual(Backbone.history.root, '/root/');
});
test("Transition from hashChange to pushState.", 1, function() {
QUnit.test("Transition from hashChange to pushState.", function(assert) {
assert.expect(1);
Backbone.history.stop();
location.replace('http://example.com/root#x/y');
Backbone.history = _.extend(new Backbone.History, {
@@ -518,7 +553,7 @@
history: {
pushState: function(){},
replaceState: function(state, title, url){
strictEqual(url, '/root/x/y');
assert.strictEqual(url, '/root/x/y');
}
}
});
@@ -528,7 +563,8 @@
});
});
test("#1619: Router: Normalize empty root", 1, function() {
QUnit.test("#1619: Router: Normalize empty root", function(assert) {
assert.expect(1);
Backbone.history.stop();
location.replace('http://example.com/');
Backbone.history = _.extend(new Backbone.History, {
@@ -539,17 +575,18 @@
}
});
Backbone.history.start({root: ''});
strictEqual(Backbone.history.root, '/');
assert.strictEqual(Backbone.history.root, '/');
});
test("#1619: Router: nagivate with empty root", 1, function() {
QUnit.test("#1619: Router: nagivate with empty root", function(assert) {
assert.expect(1);
Backbone.history.stop();
location.replace('http://example.com/');
Backbone.history = _.extend(new Backbone.History, {
location: location,
history: {
pushState: function(state, title, url) {
strictEqual(url, '/fragment');
assert.strictEqual(url, '/fragment');
}
}
});
@@ -561,11 +598,12 @@
Backbone.history.navigate('fragment');
});
test("Transition from pushState to hashChange.", 1, function() {
QUnit.test("Transition from pushState to hashChange.", function(assert) {
assert.expect(1);
Backbone.history.stop();
location.replace('http://example.com/root/x/y?a=b');
location.replace = function(url) {
strictEqual(url, '/root#x/y?a=b');
assert.strictEqual(url, '/root#x/y?a=b');
};
Backbone.history = _.extend(new Backbone.History, {
location: location,
@@ -580,7 +618,8 @@
});
});
test("#1695 - hashChange to pushState with search.", 1, function() {
QUnit.test("#1695 - hashChange to pushState with search.", function(assert) {
assert.expect(1);
Backbone.history.stop();
location.replace('http://example.com/root#x/y?a=b');
Backbone.history = _.extend(new Backbone.History, {
@@ -588,7 +627,7 @@
history: {
pushState: function(){},
replaceState: function(state, title, url){
strictEqual(url, '/root/x/y?a=b');
assert.strictEqual(url, '/root/x/y?a=b');
}
}
});
@@ -598,46 +637,51 @@
});
});
test("#1746 - Router allows empty route.", 1, function() {
QUnit.test("#1746 - Router allows empty route.", function(assert) {
assert.expect(1);
var Router = Backbone.Router.extend({
routes: {'': 'empty'},
empty: function(){},
route: function(route){
strictEqual(route, '');
assert.strictEqual(route, '');
}
});
new Router;
});
test("#1794 - Trailing space in fragments.", 1, function() {
QUnit.test("#1794 - Trailing space in fragments.", function(assert) {
assert.expect(1);
var history = new Backbone.History;
strictEqual(history.getFragment('fragment '), 'fragment');
assert.strictEqual(history.getFragment('fragment '), 'fragment');
});
test("#1820 - Leading slash and trailing space.", 1, function() {
QUnit.test("#1820 - Leading slash and trailing space.", 1, function(assert) {
var history = new Backbone.History;
strictEqual(history.getFragment('/fragment '), 'fragment');
assert.strictEqual(history.getFragment('/fragment '), 'fragment');
});
test("#1980 - Optional parameters.", 2, function() {
QUnit.test("#1980 - Optional parameters.", function(assert) {
assert.expect(2);
location.replace('http://example.com#named/optional/y');
Backbone.history.checkUrl();
strictEqual(router.z, undefined);
assert.strictEqual(router.z, undefined);
location.replace('http://example.com#named/optional/y123');
Backbone.history.checkUrl();
strictEqual(router.z, '123');
assert.strictEqual(router.z, '123');
});
test("#2062 - Trigger 'route' event on router instance.", 2, function() {
QUnit.test("#2062 - Trigger 'route' event on router instance.", function(assert) {
assert.expect(2);
router.on('route', function(name, args) {
strictEqual(name, 'routeEvent');
deepEqual(args, ['x', null]);
assert.strictEqual(name, 'routeEvent');
assert.deepEqual(args, ['x', null]);
});
location.replace('http://example.com#route-event/x');
Backbone.history.checkUrl();
});
test("#2255 - Extend routes by making routes a function.", 1, function() {
QUnit.test("#2255 - Extend routes by making routes a function.", function(assert) {
assert.expect(1);
var RouterBase = Backbone.Router.extend({
routes: function() {
return {
@@ -657,17 +701,18 @@
});
var router = new RouterExtended();
deepEqual({home: "root", index: "index.html", show: "show", search: "search"}, router.routes);
assert.deepEqual({home: "root", index: "index.html", show: "show", search: "search"}, router.routes);
});
test("#2538 - hashChange to pushState only if both requested.", 0, function() {
QUnit.test("#2538 - hashChange to pushState only if both requested.", function(assert) {
assert.expect(0);
Backbone.history.stop();
location.replace('http://example.com/root?a=b#x/y');
Backbone.history = _.extend(new Backbone.History, {
location: location,
history: {
pushState: function(){},
replaceState: function(){ ok(false); }
replaceState: function(){ assert.ok(false); }
}
});
Backbone.history.start({
@@ -677,7 +722,8 @@
});
});
test('No hash fallback.', 0, function() {
QUnit.test('No hash fallback.', function(assert) {
assert.expect(0);
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {
location: location,
@@ -689,7 +735,7 @@
var Router = Backbone.Router.extend({
routes: {
hash: function() { ok(false); }
hash: function() { assert.ok(false); }
}
});
var router = new Router;
@@ -703,13 +749,14 @@
Backbone.history.checkUrl();
});
test('#2656 - No trailing slash on root.', 1, function() {
QUnit.test('#2656 - No trailing slash on root.', function(assert) {
assert.expect(1);
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {
location: location,
history: {
pushState: function(state, title, url){
strictEqual(url, '/root');
assert.strictEqual(url, '/root');
}
}
});
@@ -718,13 +765,14 @@
Backbone.history.navigate('');
});
test('#2656 - No trailing slash on root.', 1, function() {
QUnit.test('#2656 - No trailing slash on root.', function(assert) {
assert.expect(1);
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {
location: location,
history: {
pushState: function(state, title, url) {
strictEqual(url, '/');
assert.strictEqual(url, '/');
}
}
});
@@ -733,13 +781,14 @@
Backbone.history.navigate('');
});
test('#2656 - No trailing slash on root.', 1, function() {
QUnit.test('#2656 - No trailing slash on root.', function(assert) {
assert.expect(1);
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {
location: location,
history: {
pushState: function(state, title, url){
strictEqual(url, '/root?x=1');
assert.strictEqual(url, '/root?x=1');
}
}
});
@@ -748,20 +797,21 @@
Backbone.history.navigate('?x=1');
});
test('#2765 - Fragment matching sans query/hash.', 2, function() {
QUnit.test('#2765 - Fragment matching sans query/hash.', function(assert) {
assert.expect(2);
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {
location: location,
history: {
pushState: function(state, title, url) {
strictEqual(url, '/path?query#hash');
assert.strictEqual(url, '/path?query#hash');
}
}
});
var Router = Backbone.Router.extend({
routes: {
path: function() { ok(true); }
path: function() { assert.ok(true); }
}
});
var router = new Router;
@@ -771,11 +821,12 @@
Backbone.history.navigate('path?query#hash', true);
});
test('Do not decode the search params.', function() {
QUnit.test('Do not decode the search params.', function(assert) {
assert.expect(1);
var Router = Backbone.Router.extend({
routes: {
path: function(params){
strictEqual(params, 'x=y%3Fz');
assert.strictEqual(params, 'x=y%3Fz');
}
}
});
@@ -783,14 +834,15 @@
Backbone.history.navigate('path?x=y%3Fz', true);
});
test('Navigate to a hash url.', function() {
QUnit.test('Navigate to a hash url.', function(assert) {
assert.expect(1);
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
Backbone.history.start({pushState: true});
var Router = Backbone.Router.extend({
routes: {
path: function(params) {
strictEqual(params, 'x=y');
assert.strictEqual(params, 'x=y');
}
}
});
@@ -799,14 +851,15 @@
Backbone.history.checkUrl();
});
test('#navigate to a hash url.', function() {
QUnit.test('#navigate to a hash url.', function(assert) {
assert.expect(1);
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
Backbone.history.start({pushState: true});
var Router = Backbone.Router.extend({
routes: {
path: function(params) {
strictEqual(params, 'x=y');
assert.strictEqual(params, 'x=y');
}
}
});
@@ -814,14 +867,15 @@
Backbone.history.navigate('path?x=y#hash', true);
});
test('unicode pathname', 1, function() {
QUnit.test('unicode pathname', function(assert) {
assert.expect(1);
location.replace('http://example.com/myyjä');
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
var Router = Backbone.Router.extend({
routes: {
myyjä: function() {
ok(true);
assert.ok(true);
}
}
});
@@ -829,7 +883,8 @@
Backbone.history.start({pushState: true});
});
test('unicode pathname with % in a parameter', 1, function() {
QUnit.test('unicode pathname with % in a parameter', function(assert) {
assert.expect(1);
location.replace('http://example.com/myyjä/foo%20%25%3F%2f%40%25%20bar');
location.pathname = '/myyj%C3%A4/foo%20%25%3F%2f%40%25%20bar';
Backbone.history.stop();
@@ -837,7 +892,7 @@
var Router = Backbone.Router.extend({
routes: {
'myyjä/:query': function(query) {
strictEqual(query, 'foo %?/@% bar');
assert.strictEqual(query, 'foo %?/@% bar');
}
}
});
@@ -845,14 +900,15 @@
Backbone.history.start({pushState: true});
});
test('newline in route', 1, function() {
QUnit.test('newline in route', function(assert) {
assert.expect(1);
location.replace('http://example.com/stuff%0Anonsense?param=foo%0Abar');
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
var Router = Backbone.Router.extend({
routes: {
'stuff\nnonsense': function() {
ok(true);
assert.ok(true);
}
}
});
@@ -860,7 +916,8 @@
Backbone.history.start({pushState: true});
});
test('Router#execute receives callback, args, name.', 3, function() {
QUnit.test('Router#execute receives callback, args, name.', function(assert) {
assert.expect(3);
location.replace('http://example.com#foo/123/bar?x=y');
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
@@ -868,20 +925,21 @@
routes: {'foo/:id/bar': 'foo'},
foo: function(){},
execute: function(callback, args, name) {
strictEqual(callback, this.foo);
deepEqual(args, ['123', 'x=y']);
strictEqual(name, 'foo');
assert.strictEqual(callback, this.foo);
assert.deepEqual(args, ['123', 'x=y']);
assert.strictEqual(name, 'foo');
}
});
var router = new Router;
Backbone.history.start();
});
test("pushState to hashChange with only search params.", 1, function() {
QUnit.test("pushState to hashChange with only search params.", function(assert) {
assert.expect(1);
Backbone.history.stop();
location.replace('http://example.com?a=b');
location.replace = function(url) {
strictEqual(url, '/#?a=b');
assert.strictEqual(url, '/#?a=b');
};
Backbone.history = _.extend(new Backbone.History, {
location: location,
@@ -890,37 +948,40 @@
Backbone.history.start({pushState: true});
});
test("#3123 - History#navigate decodes before comparison.", 1, function() {
QUnit.test("#3123 - History#navigate decodes before comparison.", function(assert) {
assert.expect(1);
Backbone.history.stop();
location.replace('http://example.com/shop/search?keyword=short%20dress');
Backbone.history = _.extend(new Backbone.History, {
location: location,
history: {
pushState: function(){ ok(false); },
replaceState: function(){ ok(false); }
pushState: function(){ assert.ok(false); },
replaceState: function(){ assert.ok(false); }
}
});
Backbone.history.start({pushState: true});
Backbone.history.navigate('shop/search?keyword=short%20dress', true);
strictEqual(Backbone.history.fragment, 'shop/search?keyword=short dress');
assert.strictEqual(Backbone.history.fragment, 'shop/search?keyword=short dress');
});
test('#3175 - Urls in the params', 1, function() {
QUnit.test('#3175 - Urls in the params', function(assert) {
assert.expect(1);
Backbone.history.stop();
location.replace('http://example.com#login?a=value&backUrl=https%3A%2F%2Fwww.msn.com%2Fidp%2Fidpdemo%3Fspid%3Dspdemo%26target%3Db');
Backbone.history = _.extend(new Backbone.History, {location: location});
var router = new Backbone.Router;
router.route('login', function(params) {
strictEqual(params, 'a=value&backUrl=https%3A%2F%2Fwww.msn.com%2Fidp%2Fidpdemo%3Fspid%3Dspdemo%26target%3Db');
assert.strictEqual(params, 'a=value&backUrl=https%3A%2F%2Fwww.msn.com%2Fidp%2Fidpdemo%3Fspid%3Dspdemo%26target%3Db');
});
Backbone.history.start();
});
test('#3358 - pushState to hashChange transition with search params', 1, function() {
QUnit.test('#3358 - pushState to hashChange transition with search params', function(assert) {
assert.expect(1);
Backbone.history.stop();
location.replace('http://example.com/root?foo=bar');
location.replace = function(url) {
strictEqual(url, '/root#?foo=bar');
assert.strictEqual(url, '/root#?foo=bar');
};
Backbone.history = _.extend(new Backbone.History, {
location: location,
@@ -932,14 +993,15 @@
Backbone.history.start({root: '/root', pushState: true});
});
test("Paths that don't match the root should not match no root", 0, function() {
QUnit.test("Paths that don't match the root should not match no root", function(assert) {
assert.expect(0);
location.replace('http://example.com/foo');
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
var Router = Backbone.Router.extend({
routes: {
foo: function(){
ok(false, 'should not match unless root matches');
assert.ok(false, 'should not match unless root matches');
}
}
});
@@ -947,14 +1009,15 @@
Backbone.history.start({root: 'root', pushState: true});
});
test("Paths that don't match the root should not match roots of the same length", 0, function() {
QUnit.test("Paths that don't match the root should not match roots of the same length", function(assert) {
assert.expect(0);
location.replace('http://example.com/xxxx/foo');
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
var Router = Backbone.Router.extend({
routes: {
foo: function(){
ok(false, 'should not match unless root matches');
assert.ok(false, 'should not match unless root matches');
}
}
});
@@ -962,34 +1025,37 @@
Backbone.history.start({root: 'root', pushState: true});
});
test("roots with regex characters", 1, function() {
QUnit.test("roots with regex characters", function(assert) {
assert.expect(1);
location.replace('http://example.com/x+y.z/foo');
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
var Router = Backbone.Router.extend({
routes: {foo: function(){ ok(true); }}
routes: {foo: function(){ assert.ok(true); }}
});
var router = new Router;
Backbone.history.start({root: 'x+y.z', pushState: true});
});
test("roots with unicode characters", 1, function() {
QUnit.test("roots with unicode characters", function(assert) {
assert.expect(1);
location.replace('http://example.com/®ooτ/foo');
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
var Router = Backbone.Router.extend({
routes: {foo: function(){ ok(true); }}
routes: {foo: function(){ assert.ok(true); }}
});
var router = new Router;
Backbone.history.start({root: '®ooτ', pushState: true});
});
test("roots without slash", 1, function() {
QUnit.test("roots without slash", function(assert) {
assert.expect(1);
location.replace('http://example.com/®ooτ');
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
var Router = Backbone.Router.extend({
routes: {'': function(){ ok(true); }}
routes: {'': function(){ assert.ok(true); }}
});
var router = new Router;
Backbone.history.start({root: '®ooτ', pushState: true});

View File

@@ -8,6 +8,8 @@
var pushState = history.pushState;
var replaceState = history.replaceState;
QUnit.config.noglobals = true;
QUnit.testStart(function() {
var env = QUnit.config.current.testEnvironment;

View File

@@ -11,208 +11,226 @@
length : 123
};
module("Backbone.sync", {
QUnit.module("Backbone.sync", {
setup : function() {
beforeEach : function(assert) {
library = new Library;
library.create(attrs, {wait: false});
},
teardown: function() {
afterEach: function(assert) {
Backbone.emulateHTTP = false;
}
});
test("read", 4, function() {
QUnit.test("read", function(assert) {
assert.expect(4);
library.fetch();
equal(this.ajaxSettings.url, '/library');
equal(this.ajaxSettings.type, 'GET');
equal(this.ajaxSettings.dataType, 'json');
ok(_.isEmpty(this.ajaxSettings.data));
assert.equal(this.ajaxSettings.url, '/library');
assert.equal(this.ajaxSettings.type, 'GET');
assert.equal(this.ajaxSettings.dataType, 'json');
assert.ok(_.isEmpty(this.ajaxSettings.data));
});
test("passing data", 3, function() {
QUnit.test("passing data", function(assert) {
assert.expect(3);
library.fetch({data: {a: 'a', one: 1}});
equal(this.ajaxSettings.url, '/library');
equal(this.ajaxSettings.data.a, 'a');
equal(this.ajaxSettings.data.one, 1);
assert.equal(this.ajaxSettings.url, '/library');
assert.equal(this.ajaxSettings.data.a, 'a');
assert.equal(this.ajaxSettings.data.one, 1);
});
test("create", 6, function() {
equal(this.ajaxSettings.url, '/library');
equal(this.ajaxSettings.type, 'POST');
equal(this.ajaxSettings.dataType, 'json');
QUnit.test("create", function(assert) {
assert.expect(6);
assert.equal(this.ajaxSettings.url, '/library');
assert.equal(this.ajaxSettings.type, 'POST');
assert.equal(this.ajaxSettings.dataType, 'json');
var data = JSON.parse(this.ajaxSettings.data);
equal(data.title, 'The Tempest');
equal(data.author, 'Bill Shakespeare');
equal(data.length, 123);
assert.equal(data.title, 'The Tempest');
assert.equal(data.author, 'Bill Shakespeare');
assert.equal(data.length, 123);
});
test("update", 7, function() {
QUnit.test("update", function(assert) {
assert.expect(7);
library.first().save({id: '1-the-tempest', author: 'William Shakespeare'});
equal(this.ajaxSettings.url, '/library/1-the-tempest');
equal(this.ajaxSettings.type, 'PUT');
equal(this.ajaxSettings.dataType, 'json');
assert.equal(this.ajaxSettings.url, '/library/1-the-tempest');
assert.equal(this.ajaxSettings.type, 'PUT');
assert.equal(this.ajaxSettings.dataType, 'json');
var data = JSON.parse(this.ajaxSettings.data);
equal(data.id, '1-the-tempest');
equal(data.title, 'The Tempest');
equal(data.author, 'William Shakespeare');
equal(data.length, 123);
assert.equal(data.id, '1-the-tempest');
assert.equal(data.title, 'The Tempest');
assert.equal(data.author, 'William Shakespeare');
assert.equal(data.length, 123);
});
test("update with emulateHTTP and emulateJSON", 7, function() {
QUnit.test("update with emulateHTTP and emulateJSON", function(assert) {
assert.expect(7);
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'}, {
emulateHTTP: true,
emulateJSON: true
});
equal(this.ajaxSettings.url, '/library/2-the-tempest');
equal(this.ajaxSettings.type, 'POST');
equal(this.ajaxSettings.dataType, 'json');
equal(this.ajaxSettings.data._method, 'PUT');
assert.equal(this.ajaxSettings.url, '/library/2-the-tempest');
assert.equal(this.ajaxSettings.type, 'POST');
assert.equal(this.ajaxSettings.dataType, 'json');
assert.equal(this.ajaxSettings.data._method, 'PUT');
var data = JSON.parse(this.ajaxSettings.data.model);
equal(data.id, '2-the-tempest');
equal(data.author, 'Tim Shakespeare');
equal(data.length, 123);
assert.equal(data.id, '2-the-tempest');
assert.equal(data.author, 'Tim Shakespeare');
assert.equal(data.length, 123);
});
test("update with just emulateHTTP", 6, function() {
QUnit.test("update with just emulateHTTP", function(assert) {
assert.expect(6);
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'}, {
emulateHTTP: true
});
equal(this.ajaxSettings.url, '/library/2-the-tempest');
equal(this.ajaxSettings.type, 'POST');
equal(this.ajaxSettings.contentType, 'application/json');
assert.equal(this.ajaxSettings.url, '/library/2-the-tempest');
assert.equal(this.ajaxSettings.type, 'POST');
assert.equal(this.ajaxSettings.contentType, 'application/json');
var data = JSON.parse(this.ajaxSettings.data);
equal(data.id, '2-the-tempest');
equal(data.author, 'Tim Shakespeare');
equal(data.length, 123);
assert.equal(data.id, '2-the-tempest');
assert.equal(data.author, 'Tim Shakespeare');
assert.equal(data.length, 123);
});
test("update with just emulateJSON", 6, function() {
QUnit.test("update with just emulateJSON", function(assert) {
assert.expect(6);
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'}, {
emulateJSON: true
});
equal(this.ajaxSettings.url, '/library/2-the-tempest');
equal(this.ajaxSettings.type, 'PUT');
equal(this.ajaxSettings.contentType, 'application/x-www-form-urlencoded');
assert.equal(this.ajaxSettings.url, '/library/2-the-tempest');
assert.equal(this.ajaxSettings.type, 'PUT');
assert.equal(this.ajaxSettings.contentType, 'application/x-www-form-urlencoded');
var data = JSON.parse(this.ajaxSettings.data.model);
equal(data.id, '2-the-tempest');
equal(data.author, 'Tim Shakespeare');
equal(data.length, 123);
assert.equal(data.id, '2-the-tempest');
assert.equal(data.author, 'Tim Shakespeare');
assert.equal(data.length, 123);
});
test("read model", 3, function() {
QUnit.test("read model", function(assert) {
assert.expect(3);
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
library.first().fetch();
equal(this.ajaxSettings.url, '/library/2-the-tempest');
equal(this.ajaxSettings.type, 'GET');
ok(_.isEmpty(this.ajaxSettings.data));
assert.equal(this.ajaxSettings.url, '/library/2-the-tempest');
assert.equal(this.ajaxSettings.type, 'GET');
assert.ok(_.isEmpty(this.ajaxSettings.data));
});
test("destroy", 3, function() {
QUnit.test("destroy", function(assert) {
assert.expect(3);
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
library.first().destroy({wait: true});
equal(this.ajaxSettings.url, '/library/2-the-tempest');
equal(this.ajaxSettings.type, 'DELETE');
equal(this.ajaxSettings.data, null);
assert.equal(this.ajaxSettings.url, '/library/2-the-tempest');
assert.equal(this.ajaxSettings.type, 'DELETE');
assert.equal(this.ajaxSettings.data, null);
});
test("destroy with emulateHTTP", 3, function() {
QUnit.test("destroy with emulateHTTP", function(assert) {
assert.expect(3);
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
library.first().destroy({
emulateHTTP: true,
emulateJSON: true
});
equal(this.ajaxSettings.url, '/library/2-the-tempest');
equal(this.ajaxSettings.type, 'POST');
equal(JSON.stringify(this.ajaxSettings.data), '{"_method":"DELETE"}');
assert.equal(this.ajaxSettings.url, '/library/2-the-tempest');
assert.equal(this.ajaxSettings.type, 'POST');
assert.equal(JSON.stringify(this.ajaxSettings.data), '{"_method":"DELETE"}');
});
test("urlError", 2, function() {
QUnit.test("urlError", function(assert) {
assert.expect(2);
var model = new Backbone.Model();
throws(function() {
assert.throws(function() {
model.fetch();
});
model.fetch({url: '/one/two'});
equal(this.ajaxSettings.url, '/one/two');
assert.equal(this.ajaxSettings.url, '/one/two');
});
test("#1052 - `options` is optional.", 0, function() {
QUnit.test("#1052 - `options` is optional.", function(assert) {
assert.expect(0);
var model = new Backbone.Model();
model.url = '/test';
Backbone.sync('create', model);
});
test("Backbone.ajax", 1, function() {
QUnit.test("Backbone.ajax", function(assert) {
assert.expect(1);
Backbone.ajax = function(settings){
strictEqual(settings.url, '/test');
assert.strictEqual(settings.url, '/test');
};
var model = new Backbone.Model();
model.url = '/test';
Backbone.sync('create', model);
});
test("Call provided error callback on error.", 1, function() {
QUnit.test("Call provided error callback on error.", function(assert) {
assert.expect(1);
var model = new Backbone.Model;
model.url = '/test';
Backbone.sync('read', model, {
error: function() { ok(true); }
error: function() { assert.ok(true); }
});
this.ajaxSettings.error();
});
test('Use Backbone.emulateHTTP as default.', 2, function() {
QUnit.test('Use Backbone.emulateHTTP as default.', function(assert) {
assert.expect(2);
var model = new Backbone.Model;
model.url = '/test';
Backbone.emulateHTTP = true;
model.sync('create', model);
strictEqual(this.ajaxSettings.emulateHTTP, true);
assert.strictEqual(this.ajaxSettings.emulateHTTP, true);
Backbone.emulateHTTP = false;
model.sync('create', model);
strictEqual(this.ajaxSettings.emulateHTTP, false);
assert.strictEqual(this.ajaxSettings.emulateHTTP, false);
});
test('Use Backbone.emulateJSON as default.', 2, function() {
QUnit.test('Use Backbone.emulateJSON as default.', function(assert) {
assert.expect(2);
var model = new Backbone.Model;
model.url = '/test';
Backbone.emulateJSON = true;
model.sync('create', model);
strictEqual(this.ajaxSettings.emulateJSON, true);
assert.strictEqual(this.ajaxSettings.emulateJSON, true);
Backbone.emulateJSON = false;
model.sync('create', model);
strictEqual(this.ajaxSettings.emulateJSON, false);
assert.strictEqual(this.ajaxSettings.emulateJSON, false);
});
test("#1756 - Call user provided beforeSend function.", 4, function() {
QUnit.test("#1756 - Call user provided beforeSend function.", function(assert) {
assert.expect(4);
Backbone.emulateHTTP = true;
var model = new Backbone.Model;
model.url = '/test';
var xhr = {
setRequestHeader: function(header, value) {
strictEqual(header, 'X-HTTP-Method-Override');
strictEqual(value, 'DELETE');
assert.strictEqual(header, 'X-HTTP-Method-Override');
assert.strictEqual(value, 'DELETE');
}
};
model.sync('delete', model, {
beforeSend: function(_xhr) {
ok(_xhr === xhr);
assert.ok(_xhr === xhr);
return false;
}
});
strictEqual(this.ajaxSettings.beforeSend(xhr), false);
assert.strictEqual(this.ajaxSettings.beforeSend(xhr), false);
});
test('#2928 - Pass along `textStatus` and `errorThrown`.', 2, function() {
QUnit.test('#2928 - Pass along `textStatus` and `errorThrown`.', function(assert) {
assert.expect(2);
var model = new Backbone.Model;
model.url = '/test';
model.on('error', function(model, xhr, options) {
strictEqual(options.textStatus, 'textStatus');
strictEqual(options.errorThrown, 'errorThrown');
assert.strictEqual(options.textStatus, 'textStatus');
assert.strictEqual(options.errorThrown, 'errorThrown');
});
model.fetch();
this.ajaxSettings.error({}, 'textStatus', 'errorThrown');

View File

@@ -2,9 +2,9 @@
var view;
module("Backbone.View", {
QUnit.module("Backbone.View", {
setup: function() {
beforeEach: function(assert) {
$('#qunit-fixture').append(
'<div id="testElement"><h1>Test</h1></div>'
);
@@ -18,46 +18,52 @@
});
test("constructor", 3, function() {
equal(view.el.id, 'test-view');
equal(view.el.className, 'test-view');
equal(view.el.other, void 0);
QUnit.test("constructor", function(assert) {
assert.expect(3);
assert.equal(view.el.id, 'test-view');
assert.equal(view.el.className, 'test-view');
assert.equal(view.el.other, void 0);
});
test("$", 2, function() {
QUnit.test("$", function(assert) {
assert.expect(2);
var view = new Backbone.View;
view.setElement('<p><a><b>test</b></a></p>');
var result = view.$('a b');
strictEqual(result[0].innerHTML, 'test');
ok(result.length === +result.length);
assert.strictEqual(result[0].innerHTML, 'test');
assert.ok(result.length === +result.length);
});
test("$el", 3, function() {
QUnit.test("$el", function(assert) {
assert.expect(3);
var view = new Backbone.View;
view.setElement('<p><a><b>test</b></a></p>');
strictEqual(view.el.nodeType, 1);
assert.strictEqual(view.el.nodeType, 1);
ok(view.$el instanceof Backbone.$);
strictEqual(view.$el[0], view.el);
assert.ok(view.$el instanceof Backbone.$);
assert.strictEqual(view.$el[0], view.el);
});
test("initialize", 1, function() {
QUnit.test("initialize", function(assert) {
assert.expect(1);
var View = Backbone.View.extend({
initialize: function() {
this.one = 1;
}
});
strictEqual(new View().one, 1);
assert.strictEqual(new View().one, 1);
});
test("render", 1, function() {
QUnit.test("render", function(assert) {
assert.expect(1);
var view = new Backbone.View;
equal(view.render(), view, '#render returns the view instance');
assert.equal(view.render(), view, '#render returns the view instance');
});
test("delegateEvents", 6, function() {
QUnit.test("delegateEvents", function(assert) {
assert.expect(6);
var counter1 = 0, counter2 = 0;
var view = new Backbone.View({el: '#testElement'});
@@ -68,33 +74,35 @@
view.delegateEvents(events);
view.$('h1').trigger('click');
equal(counter1, 1);
equal(counter2, 1);
assert.equal(counter1, 1);
assert.equal(counter2, 1);
view.$('h1').trigger('click');
equal(counter1, 2);
equal(counter2, 2);
assert.equal(counter1, 2);
assert.equal(counter2, 2);
view.delegateEvents(events);
view.$('h1').trigger('click');
equal(counter1, 3);
equal(counter2, 3);
assert.equal(counter1, 3);
assert.equal(counter2, 3);
});
test("delegate", 3, function() {
QUnit.test("delegate", function(assert) {
assert.expect(3);
var view = new Backbone.View({el: '#testElement'});
view.delegate('click', 'h1', function() {
ok(true);
assert.ok(true);
});
view.delegate('click', function() {
ok(true);
assert.ok(true);
});
view.$('h1').trigger('click');
equal(view.delegate(), view, '#delegate returns the view instance');
assert.equal(view.delegate(), view, '#delegate returns the view instance');
});
test("delegateEvents allows functions for callbacks", 3, function() {
QUnit.test("delegateEvents allows functions for callbacks", function(assert) {
assert.expect(3);
var view = new Backbone.View({el: '<p></p>'});
view.counter = 0;
@@ -106,24 +114,26 @@
view.delegateEvents(events);
view.$el.trigger('click');
equal(view.counter, 1);
assert.equal(view.counter, 1);
view.$el.trigger('click');
equal(view.counter, 2);
assert.equal(view.counter, 2);
view.delegateEvents(events);
view.$el.trigger('click');
equal(view.counter, 3);
assert.equal(view.counter, 3);
});
test("delegateEvents ignore undefined methods", 0, function() {
QUnit.test("delegateEvents ignore undefined methods", function(assert) {
assert.expect(0);
var view = new Backbone.View({el: '<p></p>'});
view.delegateEvents({'click': 'undefinedMethod'});
view.$el.trigger('click');
});
test("undelegateEvents", 7, function() {
QUnit.test("undelegateEvents", function(assert) {
assert.expect(7);
var counter1 = 0, counter2 = 0;
var view = new Backbone.View({el: '#testElement'});
@@ -134,107 +144,116 @@
view.delegateEvents(events);
view.$('h1').trigger('click');
equal(counter1, 1);
equal(counter2, 1);
assert.equal(counter1, 1);
assert.equal(counter2, 1);
view.undelegateEvents();
view.$('h1').trigger('click');
equal(counter1, 1);
equal(counter2, 2);
assert.equal(counter1, 1);
assert.equal(counter2, 2);
view.delegateEvents(events);
view.$('h1').trigger('click');
equal(counter1, 2);
equal(counter2, 3);
assert.equal(counter1, 2);
assert.equal(counter2, 3);
equal(view.undelegateEvents(), view, '#undelegateEvents returns the view instance');
assert.equal(view.undelegateEvents(), view, '#undelegateEvents returns the view instance');
});
test("undelegate", 1, function() {
QUnit.test("undelegate", function(assert) {
assert.expect(1);
view = new Backbone.View({el: '#testElement'});
view.delegate('click', function() { ok(false); });
view.delegate('click', 'h1', function() { ok(false); });
view.delegate('click', function() { assert.ok(false); });
view.delegate('click', 'h1', function() { assert.ok(false); });
view.undelegate('click');
view.$('h1').trigger('click');
view.$el.trigger('click');
equal(view.undelegate(), view, '#undelegate returns the view instance');
assert.equal(view.undelegate(), view, '#undelegate returns the view instance');
});
test("undelegate with passed handler", 1, function() {
QUnit.test("undelegate with passed handler", function(assert) {
assert.expect(1);
view = new Backbone.View({el: '#testElement'});
var listener = function() { ok(false); };
var listener = function() { assert.ok(false); };
view.delegate('click', listener);
view.delegate('click', function() { ok(true); });
view.delegate('click', function() { assert.ok(true); });
view.undelegate('click', listener);
view.$el.trigger('click');
});
test("undelegate with selector", 2, function() {
QUnit.test("undelegate with selector", function(assert) {
assert.expect(2);
view = new Backbone.View({el: '#testElement'});
view.delegate('click', function() { ok(true); });
view.delegate('click', 'h1', function() { ok(false); });
view.delegate('click', function() { assert.ok(true); });
view.delegate('click', 'h1', function() { assert.ok(false); });
view.undelegate('click', 'h1');
view.$('h1').trigger('click');
view.$el.trigger('click');
});
test("undelegate with handler and selector", 2, function() {
QUnit.test("undelegate with handler and selector", function(assert) {
assert.expect(2);
view = new Backbone.View({el: '#testElement'});
view.delegate('click', function() { ok(true); });
var handler = function(){ ok(false); };
view.delegate('click', function() { assert.ok(true); });
var handler = function(){ assert.ok(false); };
view.delegate('click', 'h1', handler);
view.undelegate('click', 'h1', handler);
view.$('h1').trigger('click');
view.$el.trigger('click');
});
test("tagName can be provided as a string", 1, function() {
QUnit.test("tagName can be provided as a string", function(assert) {
assert.expect(1);
var View = Backbone.View.extend({
tagName: 'span'
});
equal(new View().el.tagName, 'SPAN');
assert.equal(new View().el.tagName, 'SPAN');
});
test("tagName can be provided as a function", 1, function() {
QUnit.test("tagName can be provided as a function", function(assert) {
assert.expect(1);
var View = Backbone.View.extend({
tagName: function() {
return 'p';
}
});
ok(new View().$el.is('p'));
assert.ok(new View().$el.is('p'));
});
test("_ensureElement with DOM node el", 1, function() {
QUnit.test("_ensureElement with DOM node el", function(assert) {
assert.expect(1);
var View = Backbone.View.extend({
el: document.body
});
equal(new View().el, document.body);
assert.equal(new View().el, document.body);
});
test("_ensureElement with string el", 3, function() {
QUnit.test("_ensureElement with string el", function(assert) {
assert.expect(3);
var View = Backbone.View.extend({
el: "body"
});
strictEqual(new View().el, document.body);
assert.strictEqual(new View().el, document.body);
View = Backbone.View.extend({
el: "#testElement > h1"
});
strictEqual(new View().el, $("#testElement > h1").get(0));
assert.strictEqual(new View().el, $("#testElement > h1").get(0));
View = Backbone.View.extend({
el: "#nonexistent"
});
ok(!new View().el);
assert.ok(!new View().el);
});
test("with className and id functions", 2, function() {
QUnit.test("with className and id functions", function(assert) {
assert.expect(2);
var View = Backbone.View.extend({
className: function() {
return 'className';
@@ -244,11 +263,12 @@
}
});
strictEqual(new View().el.className, 'className');
strictEqual(new View().el.id, 'id');
assert.strictEqual(new View().el.className, 'className');
assert.strictEqual(new View().el.id, 'id');
});
test("with attributes", 2, function() {
QUnit.test("with attributes", function(assert) {
assert.expect(2);
var View = Backbone.View.extend({
attributes: {
id: 'id',
@@ -256,21 +276,41 @@
}
});
strictEqual(new View().el.className, 'class');
strictEqual(new View().el.id, 'id');
assert.strictEqual(new View().el.className, 'class');
assert.strictEqual(new View().el.id, 'id');
});
test("with attributes as a function", 1, function() {
QUnit.test("with attributes as a function", function(assert) {
assert.expect(1);
var View = Backbone.View.extend({
attributes: function() {
return {'class': 'dynamic'};
}
});
strictEqual(new View().el.className, 'dynamic');
assert.strictEqual(new View().el.className, 'dynamic');
});
test("multiple views per element", 3, function() {
QUnit.test("should default to className/id properties", function(assert) {
assert.expect(4);
var View = Backbone.View.extend({
className: 'backboneClass',
id: 'backboneId',
attributes: {
'class': 'attributeClass',
'id': 'attributeId'
}
});
var view = new View;
assert.strictEqual(view.el.className, 'backboneClass');
assert.strictEqual(view.el.id, 'backboneId');
assert.strictEqual(view.$el.attr('class'), 'backboneClass');
assert.strictEqual(view.$el.attr('id'), 'backboneId');
});
QUnit.test("multiple views per element", function(assert) {
assert.expect(3);
var count = 0;
var $el = $('<p></p>');
@@ -285,22 +325,23 @@
var view1 = new View;
$el.trigger("click");
equal(1, count);
assert.equal(1, count);
var view2 = new View;
$el.trigger("click");
equal(3, count);
assert.equal(3, count);
view1.delegateEvents();
$el.trigger("click");
equal(5, count);
assert.equal(5, count);
});
test("custom events", 2, function() {
QUnit.test("custom events", function(assert) {
assert.expect(2);
var View = Backbone.View.extend({
el: $('body'),
events: {
"fake$event": function() { ok(true); }
"fake$event": function() { assert.ok(true); }
}
});
@@ -311,24 +352,26 @@
$('body').trigger('fake$event');
});
test("#1048 - setElement uses provided object.", 2, function() {
QUnit.test("#1048 - setElement uses provided object.", function(assert) {
assert.expect(2);
var $el = $('body');
var view = new Backbone.View({el: $el});
ok(view.$el === $el);
assert.ok(view.$el === $el);
view.setElement($el = $($el));
ok(view.$el === $el);
assert.ok(view.$el === $el);
});
test("#986 - Undelegate before changing element.", 1, function() {
QUnit.test("#986 - Undelegate before changing element.", function(assert) {
assert.expect(1);
var button1 = $('<button></button>');
var button2 = $('<button></button>');
var View = Backbone.View.extend({
events: {
click: function(e) {
ok(view.el === e.target);
assert.ok(view.el === e.target);
}
}
});
@@ -340,23 +383,25 @@
button2.trigger('click');
});
test("#1172 - Clone attributes object", 2, function() {
QUnit.test("#1172 - Clone attributes object", function(assert) {
assert.expect(2);
var View = Backbone.View.extend({
attributes: {foo: 'bar'}
});
var view1 = new View({id: 'foo'});
strictEqual(view1.el.id, 'foo');
assert.strictEqual(view1.el.id, 'foo');
var view2 = new View();
ok(!view2.el.id);
assert.ok(!view2.el.id);
});
test("views stopListening", 0, function() {
QUnit.test("views stopListening", function(assert) {
assert.expect(0);
var View = Backbone.View.extend({
initialize: function() {
this.listenTo(this.model, 'all x', function(){ ok(false); });
this.listenTo(this.collection, 'all x', function(){ ok(false); });
this.listenTo(this.model, 'all x', function(){ assert.ok(false); });
this.listenTo(this.collection, 'all x', function(){ assert.ok(false); });
}
});
@@ -370,7 +415,8 @@
view.collection.trigger('x');
});
test("Provide function for el.", 2, function() {
QUnit.test("Provide function for el.", function(assert) {
assert.expect(2);
var View = Backbone.View.extend({
el: function() {
return "<p><a></a></p>";
@@ -378,11 +424,12 @@
});
var view = new View;
ok(view.$el.is('p'));
ok(view.$el.has('a'));
assert.ok(view.$el.is('p'));
assert.ok(view.$el.has('a'));
});
test("events passed in options", 1, function() {
QUnit.test("events passed in options", function(assert) {
assert.expect(1);
var counter = 0;
var View = Backbone.View.extend({
@@ -399,32 +446,34 @@
});
view.$('h1').trigger('click').trigger('click');
equal(counter, 2);
assert.equal(counter, 2);
});
test("remove", 2, function() {
QUnit.test("remove", function(assert) {
assert.expect(2);
var view = new Backbone.View;
document.body.appendChild(view.el);
view.delegate('click', function() { ok(false); });
view.listenTo(view, 'all x', function() { ok(false); });
view.delegate('click', function() { assert.ok(false); });
view.listenTo(view, 'all x', function() { assert.ok(false); });
equal(view.remove(), view, '#remove returns the view instance');
assert.equal(view.remove(), view, '#remove returns the view instance');
view.$el.trigger('click');
view.trigger('x');
// In IE8 and below, parentNode still exists but is not document.body.
notEqual(view.el.parentNode, document.body);
assert.notEqual(view.el.parentNode, document.body);
});
test("setElement", 3, function() {
QUnit.test("setElement", function(assert) {
assert.expect(3);
var view = new Backbone.View({
events: {
click: function() { ok(false); }
click: function() { assert.ok(false); }
}
});
view.events = {
click: function() { ok(true); }
click: function() { assert.ok(true); }
};
var oldEl = view.el;
var $oldEl = view.$el;
@@ -434,8 +483,8 @@
$oldEl.click();
view.$el.click();
notEqual(oldEl, view.el);
notEqual($oldEl, view.$el);
assert.notEqual(oldEl, view.el);
assert.notEqual($oldEl, view.$el);
});
})();

View File

@@ -3,161 +3,159 @@
QUnit.module('Arrays');
test('first', function() {
equal(_.first([1, 2, 3]), 1, 'can pull out the first element of an array');
equal(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"');
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');
test('first', function(assert) {
assert.equal(_.first([1, 2, 3]), 1, 'can pull out the first element of an array');
assert.equal(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"');
assert.deepEqual(_.first([1, 2, 3], 0), [], 'returns an empty array when n <= 0 (0 case)');
assert.deepEqual(_.first([1, 2, 3], -1), [], 'returns an empty array when n <= 0 (negative case)');
assert.deepEqual(_.first([1, 2, 3], 2), [1, 2], 'can fetch the first n elements');
assert.deepEqual(_.first([1, 2, 3], 5), [1, 2, 3], 'returns the whole array if n > length');
var result = (function(){ return _.first(arguments); }(4, 3, 2, 1));
equal(result, 4, 'works on an arguments object.');
assert.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 _.first([1, 2, 3], 2); }());
deepEqual(result, [1, 2]);
equal(_.first(null), void 0, 'handles nulls');
strictEqual(_.first([1, 2, 3], -1).length, 0);
assert.deepEqual(result, [1, 1], 'works well with _.map');
assert.equal(_.first(null), void 0, 'returns undefined when called on null');
});
test('head', function() {
strictEqual(_.first, _.head, 'alias for first');
test('head', function(assert) {
assert.strictEqual(_.head, _.first, 'is an alias for first');
});
test('take', function() {
strictEqual(_.first, _.take, 'alias for first');
test('take', function(assert) {
assert.strictEqual(_.take, _.first, 'is an alias for first');
});
test('rest', function() {
test('rest', function(assert) {
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');
assert.deepEqual(_.rest(numbers), [2, 3, 4], 'fetches all but the first element');
assert.deepEqual(_.rest(numbers, 0), [1, 2, 3, 4], 'returns the whole array when index is 0');
assert.deepEqual(_.rest(numbers, 2), [3, 4], 'returns elements starting at the given index');
var result = (function(){ return _(arguments).rest(); }(1, 2, 3, 4));
deepEqual(result, [2, 3, 4], 'works on arguments object');
assert.deepEqual(result, [2, 3, 4], 'works on an 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).rest(); }(1, 2, 3, 4));
deepEqual(result, [2, 3, 4], 'works on arguments object');
assert.deepEqual(_.flatten(result), [2, 3, 2, 3], 'works well with _.map');
});
test('tail', function() {
strictEqual(_.rest, _.tail, 'alias for rest');
test('tail', function(assert) {
assert.strictEqual(_.tail, _.rest, 'is an alias for rest');
});
test('drop', function() {
strictEqual(_.rest, _.drop, 'alias for rest');
test('drop', function(assert) {
assert.strictEqual(_.drop, _.rest, 'is an 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');
test('initial', function(assert) {
assert.deepEqual(_.initial([1, 2, 3, 4, 5]), [1, 2, 3, 4], 'returns all but the last element');
assert.deepEqual(_.initial([1, 2, 3, 4], 2), [1, 2], 'returns all but the last n elements');
assert.deepEqual(_.initial([1, 2, 3, 4], 6), [], 'returns an empty array when n > length');
var result = (function(){ return _(arguments).initial(); }(1, 2, 3, 4));
deepEqual(result, [1, 2, 3], 'initial works on arguments object');
assert.deepEqual(result, [1, 2, 3], 'works on an arguments object');
result = _.map([[1, 2, 3], [1, 2, 3]], _.initial);
deepEqual(_.flatten(result), [1, 2, 1, 2], 'initial works with _.map');
assert.deepEqual(_.flatten(result), [1, 2, 1, 2], 'works well with _.map');
});
test('last', function() {
equal(_.last([1, 2, 3]), 3, 'can pull out the last element of an array');
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');
test('last', function(assert) {
assert.equal(_.last([1, 2, 3]), 3, 'can pull out the last element of an array');
assert.equal(_([1, 2, 3]).last(), 3, 'can perform OO-style "last()"');
assert.deepEqual(_.last([1, 2, 3], 0), [], 'returns an empty array when n <= 0 (0 case)');
assert.deepEqual(_.last([1, 2, 3], -1), [], 'returns an empty array when n <= 0 (negative case)');
assert.deepEqual(_.last([1, 2, 3], 2), [2, 3], 'can fetch the last n elements');
assert.deepEqual(_.last([1, 2, 3], 5), [1, 2, 3], 'returns the whole array if n > length');
var result = (function(){ return _(arguments).last(); }(1, 2, 3, 4));
equal(result, 4, 'works on an arguments object');
assert.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');
equal(_.last(null), void 0, 'handles nulls');
strictEqual(_.last([1, 2, 3], -1).length, 0);
assert.deepEqual(result, [3, 3], 'works well with _.map');
assert.equal(_.last(null), void 0, 'returns undefined when called on null');
});
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));
equal(result, 3, 'works on an arguments object');
test('compact', function(assert) {
assert.deepEqual(_.compact([1, false, null, 0, '', void 0, NaN, 2]), [1, 2], 'removes all falsy values');
var result = (function(){ return _.compact(arguments); }(0, 1, false, 2, false, 3));
assert.deepEqual(result, [1, 2, 3], 'works on an arguments object');
result = _.map([[1, false, false], [false, false, 3]], _.compact);
assert.deepEqual(result, [[1], [3]], 'works well with _.map');
});
test('flatten', function() {
deepEqual(_.flatten(null), [], 'Flattens supports null');
deepEqual(_.flatten(void 0), [], 'Flattens supports undefined');
test('flatten', function(assert) {
assert.deepEqual(_.flatten(null), [], 'supports null');
assert.deepEqual(_.flatten(void 0), [], 'supports undefined');
deepEqual(_.flatten([[], [[]], []]), [], 'Flattens empty arrays');
deepEqual(_.flatten([[], [[]], []], true), [[]], 'Flattens empty arrays');
assert.deepEqual(_.flatten([[], [[]], []]), [], 'supports empty arrays');
assert.deepEqual(_.flatten([[], [[]], []], true), [[]], 'can shallowly flatten empty arrays');
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');
assert.deepEqual(_.flatten(list), [1, 2, 3, 4], 'can flatten nested arrays');
assert.deepEqual(_.flatten(list, true), [1, 2, 3, [[[4]]]], 'can shallowly flatten nested arrays');
var result = (function(){ return _.flatten(arguments); }(1, [2], [3, [[[4]]]]));
deepEqual(result, [1, 2, 3, 4], 'works on an arguments object');
assert.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');
assert.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');
assert.equal(_.flatten([_.range(10), _.range(10), 5, 1, 3], true).length, 23, 'can flatten medium length arrays');
assert.equal(_.flatten([_.range(10), _.range(10), 5, 1, 3]).length, 23, 'can shallowly flatten medium length arrays');
assert.equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3]).length, 1056003, 'can handle massive arrays');
assert.equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3], true).length, 1056003, 'can handle massive arrays in shallow mode');
var x = _.range(100000);
for (var i = 0; i < 1000; i++) x = [x];
deepEqual(_.flatten(x), _.range(100000), 'Flatten can handle very deep arrays');
deepEqual(_.flatten(x, true), x[0], 'Flatten can handle very deep arrays with shallow');
assert.deepEqual(_.flatten(x), _.range(100000), 'can handle very deep arrays');
assert.deepEqual(_.flatten(x, true), x[0], 'can handle very deep arrays in shallow mode');
});
test('without', function() {
test('without', function(assert) {
var list = [1, 2, 1, 0, 3, 1, 4];
deepEqual(_.without(list, 0, 1), [2, 3, 4], 'can remove all instances of an object');
assert.deepEqual(_.without(list, 0, 1), [2, 3, 4], 'removes all instances of the given values');
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');
assert.deepEqual(result, [2, 3, 4], 'works on an arguments object');
list = [{one: 1}, {two: 2}];
equal(_.without(list, {one: 1}).length, 2, 'uses real object identity for comparisons.');
equal(_.without(list, list[0]).length, 1, 'ditto.');
assert.deepEqual(_.without(list, {one: 1}), list, 'compares objects by reference (value case)');
assert.deepEqual(_.without(list, list[0]), [{two: 2}], 'compares objects by reference (reference case)');
});
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');
test('sortedIndex', function(assert) {
var numbers = [10, 20, 30, 40, 50];
var indexFor35 = _.sortedIndex(numbers, 35);
assert.equal(indexFor35, 3, 'finds the index at which a value should be inserted to retain order');
var indexFor30 = _.sortedIndex(numbers, 30);
equal(indexFor30, 2, '30 should be inserted at index 2');
assert.equal(indexFor30, 2, 'finds the smallest index at which a value could be inserted to retain order');
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);
assert.strictEqual(_.sortedIndex(objects, {x: 25}, iterator), 2, 'uses the result of `iterator` for order comparisons');
assert.strictEqual(_.sortedIndex(objects, {x: 35}, 'x'), 3, 'when `iterator` is a string, uses that key for order comparisons');
var context = {1: 2, 2: 3, 3: 4};
iterator = function(obj){ return this[obj]; };
strictEqual(_.sortedIndex([1, 3], 2, iterator, context), 1);
assert.strictEqual(_.sortedIndex([1, 3], 2, iterator, context), 1, 'can execute its iterator in the given context');
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 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 largeArray = Array(Math.pow(2, 32) - 1);
var length = values.length;
// Sparsely populate `array`
while (length--) {
array[values[length]] = values[length];
largeArray[values[length]] = values[length];
}
equal(_.sortedIndex(array, 2147483648), 2147483648, 'should work with large indexes');
assert.equal(_.sortedIndex(largeArray, 2147483648), 2147483648, 'works with large indexes');
});
test('uniq', function() {
test('uniq', function(assert) {
var list = [1, 2, 1, 3, 1, 4];
deepEqual(_.uniq(list), [1, 2, 3, 4], 'can find the unique values of an unsorted array');
assert.deepEqual(_.uniq(list), [1, 2, 3, 4], 'can find the unique values of an unsorted array');
list = [1, 1, 1, 2, 2, 3];
deepEqual(_.uniq(list, true), [1, 2, 3], 'can find the unique values of a sorted array faster');
assert.deepEqual(_.uniq(list, true), [1, 2, 3], 'can find the unique values of a sorted array faster');
list = [{name: 'moe'}, {name: 'curly'}, {name: 'larry'}, {name: 'curly'}];
var iterator = function(value) { return value.name; };
deepEqual(_.map(_.uniq(list, false, iterator), iterator), ['moe', 'curly', 'larry'], 'can find the unique values of an array using a custom iterator');
assert.deepEqual(_.map(_.uniq(list, false, iterator), iterator), ['moe', 'curly', 'larry'], 'can find the unique values of an array using a custom iterator');
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');
assert.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; };
list = [1, 2, 2, 3, 4, 4];
deepEqual(_.uniq(list, true, iterator), [1, 2, 3, 4], 'iterator works with sorted array');
assert.deepEqual(_.uniq(list, true, iterator), [1, 2, 3, 4], 'iterator works with sorted array');
var kittens = [
{kitten: 'Celery', cuteness: 8},
@@ -170,236 +168,236 @@
{kitten: 'Juniper', cuteness: 10}
];
deepEqual(_.uniq(kittens, true, 'cuteness'), expected, 'string iterator works with sorted array');
assert.deepEqual(_.uniq(kittens, true, 'cuteness'), expected, 'string iterator works with sorted array');
var result = (function(){ return _.uniq(arguments); }(1, 2, 1, 3, 1, 4));
deepEqual(result, [1, 2, 3, 4], 'works on an arguments object');
assert.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');
assert.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), []);
assert.deepEqual(_.uniq(null), []);
var context = {};
list = [3];
_.uniq(list, function(value, index, array) {
strictEqual(this, context);
strictEqual(value, 3);
strictEqual(index, 0);
strictEqual(array, list);
assert.strictEqual(this, context);
assert.strictEqual(value, 3);
assert.strictEqual(index, 0);
assert.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');
assert.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');
assert.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('unique', function(assert) {
assert.strictEqual(_.unique, _.uniq, 'is an alias for uniq');
});
test('intersection', function() {
test('intersection', function(assert) {
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');
assert.deepEqual(_.intersection(stooges, leaders), ['moe'], 'can take the set intersection of two arrays');
assert.deepEqual(_(stooges).intersection(leaders), ['moe'], 'can perform an OO-style intersection');
var result = (function(){ return _.intersection(arguments, leaders); }('moe', 'curly', 'larry'));
deepEqual(result, ['moe'], 'works on an arguments object');
assert.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');
assert.deepEqual(_.intersection(theSixStooges, leaders), ['moe'], 'returns a duplicate-free array');
result = _.intersection([2, 4, 3, 1], [1, 2, 3]);
deepEqual(result, [2, 3, 1], 'preserves order of first array');
assert.deepEqual(result, [2, 3, 1], 'preserves order of first array');
result = _.intersection(null, [1, 2, 3]);
equal(Object.prototype.toString.call(result), '[object Array]', 'returns an empty array when passed null as first argument');
equal(result.length, 0, 'returns an empty array when passed null as first argument');
assert.equal(Object.prototype.toString.call(result), '[object Array]', 'returns an empty array when passed null as first argument');
assert.equal(result.length, 0, 'returns an empty array when passed null as first argument');
result = _.intersection([1, 2, 3], null);
equal(Object.prototype.toString.call(result), '[object Array]', 'returns an empty array when passed null as argument beyond the first');
equal(result.length, 0, 'returns an empty array when passed null as argument beyond the first');
assert.equal(Object.prototype.toString.call(result), '[object Array]', 'returns an empty array when passed null as argument beyond the first');
assert.equal(result.length, 0, 'returns an empty array when passed null as argument beyond the first');
});
test('union', function() {
test('union', function(assert) {
var result = _.union([1, 2, 3], [2, 30, 1], [1, 40]);
deepEqual(result, [1, 2, 3, 30, 40], 'takes the union of a list of arrays');
assert.deepEqual(result, [1, 2, 3, 30, 40], 'takes the union of a list of arrays');
result = _.union([1, 2, 3], [2, 30, 1], [1, 40, [1]]);
deepEqual(result, [1, 2, 3, 30, 40, [1]], 'takes the union of a list of nested arrays');
assert.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));
result = _.union(args, [2, 30, 1], [1, 40]);
deepEqual(result, [1, 2, 3, 30, 40], 'takes the union of a list of arrays');
assert.deepEqual(result, [1, 2, 3, 30, 40], 'takes the union of a list of arrays');
result = _.union([1, 2, 3], 4);
deepEqual(result, [1, 2, 3], 'restrict the union to arrays only');
assert.deepEqual(result, [1, 2, 3], 'restrict the union to arrays only');
});
test('difference', function() {
test('difference', function(assert) {
var result = _.difference([1, 2, 3], [2, 30, 40]);
deepEqual(result, [1, 3], 'takes the difference of two arrays');
assert.deepEqual(result, [1, 3], 'takes the difference of two arrays');
result = _.difference([1, 2, 3, 4], [2, 30, 40], [1, 11, 111]);
deepEqual(result, [3, 4], 'takes the difference of three arrays');
assert.deepEqual(result, [3, 4], 'takes the difference of three arrays');
result = _.difference([1, 2, 3], 1);
deepEqual(result, [1, 2, 3], 'restrict the difference to arrays only');
assert.deepEqual(result, [1, 2, 3], 'restrict the difference to arrays only');
});
test('zip', function() {
test('zip', function(assert) {
var names = ['moe', 'larry', 'curly'], ages = [30, 40, 50], leaders = [true];
deepEqual(_.zip(names, ages, leaders), [
assert.deepEqual(_.zip(names, ages, leaders), [
['moe', 30, true],
['larry', 40, void 0],
['curly', 50, void 0]
], 'zipped together arrays of different lengths');
var stooges = _.zip(['moe', 30, 'stooge 1'], ['larry', 40, 'stooge 2'], ['curly', 50, 'stooge 3']);
deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], ['stooge 1', 'stooge 2', 'stooge 3']], 'zipped pairs');
assert.deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], ['stooge 1', 'stooge 2', 'stooge 3']], 'zipped pairs');
// In the case of difference lengths of the tuples undefineds
// In the case of different lengths of the tuples, undefined values
// should be used as placeholder
stooges = _.zip(['moe', 30], ['larry', 40], ['curly', 50, 'extra data']);
deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], [void 0, void 0, 'extra data']], 'zipped pairs with empties');
assert.deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], [void 0, void 0, 'extra data']], 'zipped pairs with empties');
var empty = _.zip([]);
deepEqual(empty, [], 'unzipped empty');
assert.deepEqual(empty, [], 'unzipped empty');
deepEqual(_.zip(null), [], 'handles null');
deepEqual(_.zip(), [], '_.zip() returns []');
assert.deepEqual(_.zip(null), [], 'handles null');
assert.deepEqual(_.zip(), [], '_.zip() returns []');
});
test('unzip', function() {
deepEqual(_.unzip(null), [], 'handles null');
test('unzip', function(assert) {
assert.deepEqual(_.unzip(null), [], 'handles null');
deepEqual(_.unzip([['a', 'b'], [1, 2]]), [['a', 1], ['b', 2]]);
assert.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]]);
assert.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');
assert.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(assert) {
var result = _.object(['moe', 'larry', 'curly'], [30, 40, 50]);
var shouldBe = {moe: 30, larry: 40, curly: 50};
deepEqual(result, shouldBe, 'two arrays zipped together into an object');
assert.deepEqual(result, shouldBe, 'two arrays zipped together into an object');
result = _.object([['one', 1], ['two', 2], ['three', 3]]);
shouldBe = {one: 1, two: 2, three: 3};
deepEqual(result, shouldBe, 'an array of pairs zipped together into an object');
assert.deepEqual(result, shouldBe, 'an array of pairs zipped together into an object');
var stooges = {moe: 30, larry: 40, curly: 50};
deepEqual(_.object(_.pairs(stooges)), stooges, 'an object converted to pairs and back to an object');
assert.deepEqual(_.object(_.pairs(stooges)), stooges, 'an object converted to pairs and back to an object');
deepEqual(_.object(null), {}, 'handles nulls');
assert.deepEqual(_.object(null), {}, 'handles nulls');
});
test('indexOf', function() {
test('indexOf', function(assert) {
var numbers = [1, 2, 3];
equal(_.indexOf(numbers, 2), 1, 'can compute indexOf');
assert.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');
assert.equal(result, 1, 'works on an arguments object');
_.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);
assert.equal(_.indexOf(val, 2), -1, msg);
assert.equal(_.indexOf(val, 2, -1), -1, msg);
assert.equal(_.indexOf(val, 2, -20), -1, msg);
assert.equal(_.indexOf(val, 2, 15), -1, msg);
});
var num = 35;
numbers = [10, 20, 30, 40, 50];
var index = _.indexOf(numbers, num, true);
equal(index, -1, '35 is not in the list');
assert.equal(index, -1, '35 is not in the list');
numbers = [10, 20, 30, 40, 50]; num = 40;
index = _.indexOf(numbers, num, true);
equal(index, 3, '40 is in the list');
assert.equal(index, 3, '40 is in the list');
numbers = [1, 40, 40, 40, 40, 40, 40, 40, 50, 60, 70]; num = 40;
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() {
assert.equal(_.indexOf(numbers, num, true), 1, '40 is in the list');
assert.equal(_.indexOf(numbers, 6, true), -1, '6 isnt in the list');
assert.equal(_.indexOf([1, 2, 5, 4, 6, 7], 5, true), -1, 'sorted indexOf doesn\'t uses binary search');
assert.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);
equal(index, 7, 'supports the fromIndex argument');
assert.equal(index, 7, 'supports the fromIndex argument');
index = _.indexOf([,,, 0], void 0);
equal(index, 0, 'treats sparse arrays as if they were dense');
assert.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);
assert.strictEqual(_.indexOf(array, 1, -3), 3, 'neg `fromIndex` starts at the right index');
assert.strictEqual(_.indexOf(array, 1, -2), -1, 'neg `fromIndex` starts at the right index');
assert.strictEqual(_.indexOf(array, 2, -3), 4);
_.each([-6, -8, -Infinity], function(fromIndex) {
strictEqual(_.indexOf(array, 1, fromIndex), 0);
assert.strictEqual(_.indexOf(array, 1, fromIndex), 0);
});
strictEqual(_.indexOf([1, 2, 3], 1, true), 0);
assert.strictEqual(_.indexOf([1, 2, 3], 1, true), 0);
index = _.indexOf([], void 0, true);
equal(index, -1, 'empty array with truthy `isSorted` returns -1');
assert.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');
test('indexOf with NaN', function(assert) {
assert.strictEqual(_.indexOf([1, 2, NaN, NaN], NaN), 2, 'Expected [1, 2, NaN] to contain NaN');
assert.strictEqual(_.indexOf([1, 2, Infinity], NaN), -1, 'Expected [1, 2, NaN] to contain NaN');
strictEqual(_.indexOf([1, 2, NaN, NaN], NaN, 1), 2, 'startIndex does not affect result');
strictEqual(_.indexOf([1, 2, NaN, NaN], NaN, -2), 2, 'startIndex does not affect result');
assert.strictEqual(_.indexOf([1, 2, NaN, NaN], NaN, 1), 2, 'startIndex does not affect result');
assert.strictEqual(_.indexOf([1, 2, NaN, NaN], NaN, -2), 2, 'startIndex does not affect result');
(function() {
strictEqual(_.indexOf(arguments, NaN), 2, 'Expected arguments [1, 2, NaN] to contain NaN');
assert.strictEqual(_.indexOf(arguments, NaN), 2, 'Expected arguments [1, 2, NaN] to contain NaN');
}(1, 2, NaN, NaN));
});
test('indexOf with +- 0', function() {
test('indexOf with +- 0', function(assert) {
_.each([-0, +0], function(val) {
strictEqual(_.indexOf([1, 2, val, val], val), 2);
strictEqual(_.indexOf([1, 2, val, val], -val), 2);
assert.strictEqual(_.indexOf([1, 2, val, val], val), 2);
assert.strictEqual(_.indexOf([1, 2, val, val], -val), 2);
});
});
test('lastIndexOf', function() {
test('lastIndexOf', function(assert) {
var numbers = [1, 0, 1];
var falsey = [void 0, '', 0, false, NaN, null, void 0];
equal(_.lastIndexOf(numbers, 1), 2);
assert.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');
assert.equal(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function');
assert.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));
equal(result, 5, 'works on an arguments object');
assert.equal(result, 5, 'works on an arguments object');
_.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);
assert.equal(_.lastIndexOf(val, 2), -1, msg);
assert.equal(_.lastIndexOf(val, 2, -1), -1, msg);
assert.equal(_.lastIndexOf(val, 2, -20), -1, msg);
assert.equal(_.lastIndexOf(val, 2, 15), -1, msg);
});
numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3];
var index = _.lastIndexOf(numbers, 2, 2);
equal(index, 1, 'supports the fromIndex argument');
assert.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');
assert.strictEqual(_.lastIndexOf(array, 1, 0), 0, 'starts at the correct from idx');
assert.strictEqual(_.lastIndexOf(array, 3), 5, 'should return the index of the last matched value');
assert.strictEqual(_.lastIndexOf(array, 4), -1, 'should return `-1` for an unmatched value');
strictEqual(_.lastIndexOf(array, 1, 2), 0, 'should work with a positive `fromIndex`');
assert.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, void 0, fromIndex), -1);
strictEqual(_.lastIndexOf(array, 1, fromIndex), 3);
strictEqual(_.lastIndexOf(array, '', fromIndex), -1);
assert.strictEqual(_.lastIndexOf(array, void 0, fromIndex), -1);
assert.strictEqual(_.lastIndexOf(array, 1, fromIndex), 3);
assert.strictEqual(_.lastIndexOf(array, '', fromIndex), -1);
});
var expected = _.map(falsey, function(value) {
@@ -410,39 +408,39 @@
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`');
assert.deepEqual(actual, expected, 'should treat falsey `fromIndex` values, except `0` and `NaN`, as `array.length`');
assert.strictEqual(_.lastIndexOf(array, 3, '1'), 5, 'should treat non-number `fromIndex` values as `array.length`');
assert.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');
assert.strictEqual(_.lastIndexOf(array, 2, -3), 1, 'should work with a negative `fromIndex`');
assert.strictEqual(_.lastIndexOf(array, 1, -3), 3, 'neg `fromIndex` starts at the right index');
deepEqual(_.map([-6, -8, -Infinity], function(fromIndex) {
assert.deepEqual(_.map([-6, -8, -Infinity], function(fromIndex) {
return _.lastIndexOf(array, 1, fromIndex);
}), [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');
test('lastIndexOf with NaN', function(assert) {
assert.strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN), 3, 'Expected [1, 2, NaN] to contain NaN');
assert.strictEqual(_.lastIndexOf([1, 2, Infinity], NaN), -1, 'Expected [1, 2, NaN] to contain NaN');
strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN, 2), 2, 'fromIndex does not affect result');
strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN, -2), 2, 'fromIndex does not affect result');
assert.strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN, 2), 2, 'fromIndex does not affect result');
assert.strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN, -2), 2, 'fromIndex does not affect result');
(function() {
strictEqual(_.lastIndexOf(arguments, NaN), 3, 'Expected arguments [1, 2, NaN] to contain NaN');
assert.strictEqual(_.lastIndexOf(arguments, NaN), 3, 'Expected arguments [1, 2, NaN] to contain NaN');
}(1, 2, NaN, NaN));
});
test('lastIndexOf with +- 0', function() {
test('lastIndexOf with +- 0', function(assert) {
_.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);
assert.strictEqual(_.lastIndexOf([1, 2, val, val], val), 3);
assert.strictEqual(_.lastIndexOf([1, 2, val, val], -val), 3);
assert.strictEqual(_.lastIndexOf([-1, 1, 2], -val), -1);
});
});
test('findIndex', function() {
test('findIndex', function(assert) {
var objects = [
{a: 0, b: 0},
{a: 1, b: 1},
@@ -450,42 +448,42 @@
{a: 0, b: 0}
];
equal(_.findIndex(objects, function(obj) {
assert.equal(_.findIndex(objects, function(obj) {
return obj.a === 0;
}), 0);
equal(_.findIndex(objects, function(obj) {
assert.equal(_.findIndex(objects, function(obj) {
return obj.b * obj.a === 4;
}), 2);
equal(_.findIndex(objects, 'a'), 1, 'Uses lookupIterator');
assert.equal(_.findIndex(objects, 'a'), 1, 'Uses lookupIterator');
equal(_.findIndex(objects, function(obj) {
assert.equal(_.findIndex(objects, function(obj) {
return obj.b * obj.a === 5;
}), -1);
equal(_.findIndex(null, _.noop), -1);
strictEqual(_.findIndex(objects, function(a) {
assert.equal(_.findIndex(null, _.noop), -1);
assert.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');
assert.equal(key, 0);
assert.deepEqual(obj, [{a: 1}]);
assert.strictEqual(this, objects, 'called with context');
}, objects);
var sparse = [];
sparse[20] = {a: 2, b: 2};
equal(_.findIndex(sparse, function(obj) {
assert.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');
assert.strictEqual(_.findIndex(array, function(x) { return x === 55; }), -1, 'doesn\'t match array-likes keys');
});
test('findLastIndex', function() {
test('findLastIndex', function(assert) {
var objects = [
{a: 0, b: 0},
{a: 1, b: 1},
@@ -493,65 +491,67 @@
{a: 0, b: 0}
];
equal(_.findLastIndex(objects, function(obj) {
assert.equal(_.findLastIndex(objects, function(obj) {
return obj.a === 0;
}), 3);
equal(_.findLastIndex(objects, function(obj) {
assert.equal(_.findLastIndex(objects, function(obj) {
return obj.b * obj.a === 4;
}), 2);
equal(_.findLastIndex(objects, 'a'), 2, 'Uses lookupIterator');
assert.equal(_.findLastIndex(objects, 'a'), 2, 'Uses lookupIterator');
equal(_.findLastIndex(objects, function(obj) {
assert.equal(_.findLastIndex(objects, function(obj) {
return obj.b * obj.a === 5;
}), -1);
equal(_.findLastIndex(null, _.noop), -1);
strictEqual(_.findLastIndex(objects, function(a) {
assert.equal(_.findLastIndex(null, _.noop), -1);
assert.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');
assert.equal(key, 0);
assert.deepEqual(obj, [{a: 1}]);
assert.strictEqual(this, objects, 'called with context');
}, objects);
var sparse = [];
sparse[20] = {a: 2, b: 2};
equal(_.findLastIndex(sparse, function(obj) {
assert.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');
assert.strictEqual(_.findLastIndex(array, function(x) { return x === 55; }), -1, 'doesn\'t match array-likes keys');
});
test('range', function() {
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(5, 8), [5, 6, 7], 'range with two arguments a &amp; b, a&lt;b generates an array of elements a,a+1,a+2,...,b-2,b-1');
deepEqual(_.range(8, 5), [], 'range with two arguments a &amp; b, b&lt;a generates an empty array');
deepEqual(_.range(3, 10, 3), [3, 6, 9], 'range with three arguments a &amp; b &amp; c, c &lt; b-a, a &lt; b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) &lt; c');
deepEqual(_.range(3, 10, 15), [3], 'range with three arguments a &amp; b &amp; c, c &gt; b-a, a &lt; b generates an array with a single element, equal to a');
deepEqual(_.range(12, 7, -2), [12, 10, 8], 'range with three arguments a &amp; b &amp; c, a &gt; b, c &lt; 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b');
deepEqual(_.range(0, -10, -1), [0, -1, -2, -3, -4, -5, -6, -7, -8, -9], 'final example in the Python docs');
test('range', function(assert) {
assert.deepEqual(_.range(0), [], 'range with 0 as a first argument generates an empty array');
assert.deepEqual(_.range(4), [0, 1, 2, 3], 'range with a single positive argument generates an array of elements 0,1,2,...,n-1');
assert.deepEqual(_.range(5, 8), [5, 6, 7], 'range with two arguments a &amp; b, a&lt;b generates an array of elements a,a+1,a+2,...,b-2,b-1');
assert.deepEqual(_.range(3, 10, 3), [3, 6, 9], 'range with three arguments a &amp; b &amp; c, c &lt; b-a, a &lt; b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) &lt; c');
assert.deepEqual(_.range(3, 10, 15), [3], 'range with three arguments a &amp; b &amp; c, c &gt; b-a, a &lt; b generates an array with a single element, equal to a');
assert.deepEqual(_.range(12, 7, -2), [12, 10, 8], 'range with three arguments a &amp; b &amp; c, a &gt; b, c &lt; 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b');
assert.deepEqual(_.range(0, -10, -1), [0, -1, -2, -3, -4, -5, -6, -7, -8, -9], 'final example in the Python docs');
assert.strictEqual(1 / _.range(-0, 1)[0], -Infinity, 'should preserve -0');
assert.deepEqual(_.range(8, 5), [8, 7, 6], 'negative range generates descending array');
assert.deepEqual(_.range(-3), [0, -1, -2], 'negative range generates descending array');
});
test('chunk', function() {
deepEqual(_.chunk([], 2), [], 'chunk for empty array returns an empty array');
test('chunk', function(assert) {
assert.deepEqual(_.chunk([], 2), [], 'chunk for empty array returns an empty array');
deepEqual(_.chunk([1, 2, 3], 0), [], 'chunk into parts of 0 elements returns empty array');
deepEqual(_.chunk([1, 2, 3], -1), [], 'chunk into parts of negative amount of elements returns an empty array');
deepEqual(_.chunk([1, 2, 3]), [], 'defaults to empty array (chunk size 0)');
assert.deepEqual(_.chunk([1, 2, 3], 0), [], 'chunk into parts of 0 elements returns empty array');
assert.deepEqual(_.chunk([1, 2, 3], -1), [], 'chunk into parts of negative amount of elements returns an empty array');
assert.deepEqual(_.chunk([1, 2, 3]), [], 'defaults to empty array (chunk size 0)');
deepEqual(_.chunk([1, 2, 3], 1), [[1], [2], [3]], 'chunk into parts of 1 elements returns original array');
assert.deepEqual(_.chunk([1, 2, 3], 1), [[1], [2], [3]], 'chunk into parts of 1 elements returns original array');
deepEqual(_.chunk([1, 2, 3], 3), [[1, 2, 3]], 'chunk into parts of current array length elements returns the original array');
deepEqual(_.chunk([1, 2, 3], 5), [[1, 2, 3]], 'chunk into parts of more then current array length elements returns the original array');
assert.deepEqual(_.chunk([1, 2, 3], 3), [[1, 2, 3]], 'chunk into parts of current array length elements returns the original array');
assert.deepEqual(_.chunk([1, 2, 3], 5), [[1, 2, 3]], 'chunk into parts of more then current array length elements returns the original array');
deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 2), [[10, 20], [30, 40], [50, 60], [70]], 'chunk into parts of less then current array length elements');
deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 3), [[10, 20, 30], [40, 50, 60], [70]], 'chunk into parts of less then current array length elements');
assert.deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 2), [[10, 20], [30, 40], [50, 60], [70]], 'chunk into parts of less then current array length elements');
assert.deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 3), [[10, 20, 30], [40, 50, 60], [70]], 'chunk into parts of less then current array length elements');
});
}());

View File

@@ -3,7 +3,7 @@
QUnit.module('Chaining');
test('map/flatten/reduce', function() {
test('map/flatten/reduce', function(assert) {
var lyrics = [
'I\'m a lumberjack and I\'m okay',
'I sleep all night and I work all day',
@@ -19,11 +19,11 @@
return hash;
}, {})
.value();
equal(counts.a, 16, 'counted all the letters in the song');
equal(counts.e, 10, 'counted all the letters in the song');
assert.equal(counts.a, 16, 'counted all the letters in the song');
assert.equal(counts.e, 10, 'counted all the letters in the song');
});
test('select/reject/sortBy', function() {
test('select/reject/sortBy', function(assert) {
var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
numbers = _(numbers).chain().select(function(n) {
return n % 2 === 0;
@@ -32,10 +32,10 @@
}).sortBy(function(n) {
return -n;
}).value();
deepEqual(numbers, [10, 6, 2], 'filtered and reversed the numbers');
assert.deepEqual(numbers, [10, 6, 2], 'filtered and reversed the numbers');
});
test('select/reject/sortBy in functional style', function() {
test('select/reject/sortBy in functional style', function(assert) {
var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
numbers = _.chain(numbers).select(function(n) {
return n % 2 === 0;
@@ -44,10 +44,10 @@
}).sortBy(function(n) {
return -n;
}).value();
deepEqual(numbers, [10, 6, 2], 'filtered and reversed the numbers');
assert.deepEqual(numbers, [10, 6, 2], 'filtered and reversed the numbers');
});
test('reverse/concat/unshift/pop/map', function() {
test('reverse/concat/unshift/pop/map', function(assert) {
var numbers = [1, 2, 3, 4, 5];
numbers = _(numbers).chain()
.reverse()
@@ -56,44 +56,44 @@
.pop()
.map(function(n){ return n * 2; })
.value();
deepEqual(numbers, [34, 10, 8, 6, 4, 2, 10, 10], 'can chain together array functions.');
assert.deepEqual(numbers, [34, 10, 8, 6, 4, 2, 10, 10], 'can chain together array functions.');
});
test('splice', function() {
test('splice', function(assert) {
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');
assert.deepEqual(instance.splice(1, 3).value(), [1, 5]);
assert.deepEqual(instance.splice(1, 0).value(), [1, 5]);
assert.deepEqual(instance.splice(1, 1).value(), [1]);
assert.deepEqual(instance.splice(0, 1).value(), [], '#397 Can create empty array');
});
test('shift', function() {
test('shift', function(assert) {
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');
assert.deepEqual(instance.shift().value(), [2, 3]);
assert.deepEqual(instance.shift().value(), [3]);
assert.deepEqual(instance.shift().value(), [], '#397 Can create empty array');
});
test('pop', function() {
test('pop', function(assert) {
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');
assert.deepEqual(instance.pop().value(), [1, 2]);
assert.deepEqual(instance.pop().value(), [1]);
assert.deepEqual(instance.pop().value(), [], '#397 Can create empty array');
});
test('chaining works in small stages', function() {
test('chaining works in small stages', function(assert) {
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 > 2; }).value(), [3, 4]);
assert.deepEqual(o.filter(function(i) { return i < 3; }).value(), [1, 2]);
assert.deepEqual(o.filter(function(i) { return i > 2; }).value(), [3, 4]);
});
test('#1562: Engine proxies for chained functions', function() {
test('#1562: Engine proxies for chained functions', function(assert) {
var wrapped = _(512);
strictEqual(wrapped.toJSON(), 512);
strictEqual(wrapped.valueOf(), 512);
strictEqual(+wrapped, 512);
strictEqual(wrapped.toString(), '512');
strictEqual('' + wrapped, '512');
assert.strictEqual(wrapped.toJSON(), 512);
assert.strictEqual(wrapped.valueOf(), 512);
assert.strictEqual(+wrapped, 512);
assert.strictEqual(wrapped.toString(), '512');
assert.strictEqual('' + wrapped, '512');
});
}());

File diff suppressed because it is too large Load Diff

View File

@@ -33,95 +33,95 @@
);
iDoc.close();
test('isEqual', function() {
test('isEqual', function(assert) {
ok(!_.isEqual(iNumber, 101));
ok(_.isEqual(iNumber, 100));
assert.ok(!_.isEqual(iNumber, 101));
assert.ok(_.isEqual(iNumber, 100));
// Objects from another frame.
ok(_.isEqual({}, iObject), 'Objects with equivalent members created in different documents are equal');
assert.ok(_.isEqual({}, iObject), 'Objects with equivalent members created in different documents are equal');
// Array from another frame.
ok(_.isEqual([1, 2, 3], iArray), 'Arrays with equivalent elements created in different documents are equal');
assert.ok(_.isEqual([1, 2, 3], iArray), 'Arrays with equivalent elements created in different documents are equal');
});
test('isEmpty', function() {
ok(!_([iNumber]).isEmpty(), '[1] is not empty');
ok(!_.isEmpty(iArray), '[] is empty');
ok(_.isEmpty(iObject), '{} is empty');
test('isEmpty', function(assert) {
assert.ok(!_([iNumber]).isEmpty(), '[1] is not empty');
assert.ok(!_.isEmpty(iArray), '[] is empty');
assert.ok(_.isEmpty(iObject), '{} is empty');
});
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('isElement', function(assert) {
assert.ok(!_.isElement('div'), 'strings are not dom elements');
assert.ok(_.isElement(document.body), 'the body tag is a DOM element');
assert.ok(_.isElement(iElement), 'even from another frame');
});
test('isArguments', function() {
ok(_.isArguments(iArguments), 'even from another frame');
test('isArguments', function(assert) {
assert.ok(_.isArguments(iArguments), 'even from another frame');
});
test('isObject', function() {
ok(_.isObject(iElement), 'even from another frame');
ok(_.isObject(iFunction), 'even from another frame');
test('isObject', function(assert) {
assert.ok(_.isObject(iElement), 'even from another frame');
assert.ok(_.isObject(iFunction), 'even from another frame');
});
test('isArray', function() {
ok(_.isArray(iArray), 'even from another frame');
test('isArray', function(assert) {
assert.ok(_.isArray(iArray), 'even from another frame');
});
test('isString', function() {
ok(_.isString(iString), 'even from another frame');
test('isString', function(assert) {
assert.ok(_.isString(iString), 'even from another frame');
});
test('isNumber', function() {
ok(_.isNumber(iNumber), 'even from another frame');
test('isNumber', function(assert) {
assert.ok(_.isNumber(iNumber), 'even from another frame');
});
test('isBoolean', function() {
ok(_.isBoolean(iBoolean), 'even from another frame');
test('isBoolean', function(assert) {
assert.ok(_.isBoolean(iBoolean), 'even from another frame');
});
test('isFunction', function() {
ok(_.isFunction(iFunction), 'even from another frame');
test('isFunction', function(assert) {
assert.ok(_.isFunction(iFunction), 'even from another frame');
});
test('isDate', function() {
ok(_.isDate(iDate), 'even from another frame');
test('isDate', function(assert) {
assert.ok(_.isDate(iDate), 'even from another frame');
});
test('isRegExp', function() {
ok(_.isRegExp(iRegExp), 'even from another frame');
test('isRegExp', function(assert) {
assert.ok(_.isRegExp(iRegExp), 'even from another frame');
});
test('isNaN', function() {
ok(_.isNaN(iNaN), 'even from another frame');
test('isNaN', function(assert) {
assert.ok(_.isNaN(iNaN), 'even from another frame');
});
test('isNull', function() {
ok(_.isNull(iNull), 'even from another frame');
test('isNull', function(assert) {
assert.ok(_.isNull(iNull), 'even from another frame');
});
test('isUndefined', function() {
ok(_.isUndefined(iUndefined), 'even from another frame');
test('isUndefined', function(assert) {
assert.ok(_.isUndefined(iUndefined), 'even from another frame');
});
test('isError', function() {
ok(_.isError(iError), 'even from another frame');
test('isError', function(assert) {
assert.ok(_.isError(iError), 'even from another frame');
});
if (typeof ActiveXObject != 'undefined') {
test('IE host objects', function() {
test('IE host objects', function(assert) {
var xml = new ActiveXObject('Msxml2.DOMDocument.3.0');
ok(!_.isNumber(xml));
ok(!_.isBoolean(xml));
ok(!_.isNaN(xml));
ok(!_.isFunction(xml));
ok(!_.isNull(xml));
ok(!_.isUndefined(xml));
assert.ok(!_.isNumber(xml));
assert.ok(!_.isBoolean(xml));
assert.ok(!_.isNaN(xml));
assert.ok(!_.isFunction(xml));
assert.ok(!_.isNull(xml));
assert.ok(!_.isUndefined(xml));
});
test('#1621 IE 11 compat mode DOM elements are not functions', function() {
test('#1621 IE 11 compat mode DOM elements are not functions', function(assert) {
var fn = function() {};
var xml = new ActiveXObject('Msxml2.DOMDocument.3.0');
var div = document.createElement('div');
@@ -132,9 +132,9 @@
_.isFunction(fn);
}
equal(_.isFunction(xml), false);
equal(_.isFunction(div), false);
equal(_.isFunction(fn), true);
assert.equal(_.isFunction(xml), false);
assert.equal(_.isFunction(div), false);
assert.equal(_.isFunction(fn), true);
});
}

View File

@@ -4,32 +4,32 @@
QUnit.module('Functions');
QUnit.config.asyncRetries = 3;
test('bind', function() {
test('bind', function(assert) {
var context = {name: 'moe'};
var func = function(arg) { return 'name: ' + (this.name || arg); };
var bound = _.bind(func, context);
equal(bound(), 'name: moe', 'can bind a function to a context');
assert.equal(bound(), 'name: moe', 'can bind a function to a context');
bound = _(func).bind(context);
equal(bound(), 'name: moe', 'can do OO-style binding');
assert.equal(bound(), 'name: moe', 'can do OO-style binding');
bound = _.bind(func, null, 'curly');
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');
assert.ok(result === 'name: curly' || result === 'name: ' + window.name, 'can bind without specifying a context');
func = function(salutation, name) { return salutation + ': ' + name; };
func = _.bind(func, this, 'hello');
equal(func('moe'), 'hello: moe', 'the function was partially applied in advance');
assert.equal(func('moe'), 'hello: moe', 'the function was partially applied in advance');
func = _.bind(func, this, 'curly');
equal(func(), 'hello: curly', 'the function was completely applied in advance');
assert.equal(func(), 'hello: curly', 'the function was completely applied in advance');
func = function(salutation, firstname, lastname) { return salutation + ': ' + firstname + ' ' + lastname; };
func = _.bind(func, this, 'hello', 'moe', 'curly');
equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments');
assert.equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments');
func = function(ctx, message) { equal(this, ctx, message); };
func = function(ctx, message) { assert.equal(this, ctx, message); };
_.bind(func, 0, 0, 'can bind a function to `0`')();
_.bind(func, '', '', 'can bind a function to an empty string')();
_.bind(func, false, false, 'can bind a function to `false`')();
@@ -40,29 +40,29 @@
var boundf = _.bind(F, {hello: 'moe curly'});
var Boundf = boundf; // make eslint happy.
var newBoundf = new Boundf();
equal(newBoundf.hello, void 0, '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");
ok(newBoundf instanceof F, 'a bound instance is an instance of the original function');
assert.equal(newBoundf.hello, void 0, 'function should not be bound to the context, to comply with ECMAScript 5');
assert.equal(boundf().hello, 'moe curly', "When called without the new operator, it's OK to be bound to the context");
assert.ok(newBoundf instanceof F, 'a bound instance is an instance of the original function');
throws(function() { _.bind('notafunction'); }, TypeError, 'throws an error when binding to a non-function');
assert.throws(function() { _.bind('notafunction'); }, TypeError, 'throws an error when binding to a non-function');
});
test('partial', function() {
test('partial', function(assert) {
var obj = {name: 'moe'};
var func = function() { return this.name + ' ' + _.toArray(arguments).join(' '); };
obj.func = _.partial(func, 'a', 'b');
equal(obj.func('c', 'd'), 'moe a b c d', 'can partially apply');
assert.equal(obj.func('c', 'd'), 'moe a b c d', 'can partially apply');
obj.func = _.partial(func, _, 'b', _, 'd');
equal(obj.func('a', 'c'), 'moe a b c d', 'can partially apply with placeholders');
assert.equal(obj.func('a', 'c'), 'moe a b c d', 'can partially apply with placeholders');
func = _.partial(function() { return arguments.length; }, _, 'b', _, 'd');
equal(func('a', 'c', 'e'), 5, 'accepts more arguments than the number of placeholders');
equal(func('a'), 4, 'accepts fewer arguments than the number of placeholders');
assert.equal(func('a', 'c', 'e'), 5, 'accepts more arguments than the number of placeholders');
assert.equal(func('a'), 4, 'accepts fewer arguments than the number of placeholders');
func = _.partial(function() { return typeof arguments[2]; }, _, 'b', _, 'd');
equal(func('a'), 'undefined', 'unfilled placeholders are undefined');
assert.equal(func('a'), 'undefined', 'unfilled placeholders are undefined');
// passes context
function MyWidget(name, options) {
@@ -74,22 +74,22 @@
};
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});
assert.ok(widget instanceof MyWidget, 'Can partially bind a constructor');
assert.equal(widget.get(), 'foo', 'keeps prototype');
assert.deepEqual(widget.options, {a: 1});
_.partial.placeholder = obj;
func = _.partial(function() { return arguments.length; }, obj, 'b', obj, 'd');
equal(func('a'), 4, 'allows the placeholder to be swapped out');
assert.equal(func('a'), 4, 'allows the placeholder to be swapped out');
_.partial.placeholder = {};
func = _.partial(function() { return arguments.length; }, obj, 'b', obj, 'd');
equal(func('a'), 5, 'swapping the placeholder preserves previously bound arguments');
assert.equal(func('a'), 5, 'swapping the placeholder preserves previously bound arguments');
_.partial.placeholder = _;
});
test('bindAll', function() {
test('bindAll', function(assert) {
var curly = {name: 'curly'}, moe = {
name: 'moe',
getName: function() { return 'name: ' + this.name; },
@@ -98,8 +98,8 @@
curly.getName = moe.getName;
_.bindAll(moe, 'getName', 'sayHi');
curly.sayHi = moe.sayHi;
equal(curly.getName(), 'name: curly', 'unbound function is bound to current object');
equal(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object');
assert.equal(curly.getName(), 'name: curly', 'unbound function is bound to current object');
assert.equal(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object');
curly = {name: 'curly'};
moe = {
@@ -109,57 +109,57 @@
sayLast: function() { return this.sayHi(_.last(arguments)); }
};
throws(function() { _.bindAll(moe); }, Error, 'throws an error for bindAll with no functions named');
throws(function() { _.bindAll(moe, 'sayBye'); }, TypeError, 'throws an error for bindAll if the given key is undefined');
throws(function() { _.bindAll(moe, 'name'); }, TypeError, 'throws an error for bindAll if the given key is not a function');
assert.throws(function() { _.bindAll(moe); }, Error, 'throws an error for bindAll with no functions named');
assert.throws(function() { _.bindAll(moe, 'sayBye'); }, TypeError, 'throws an error for bindAll if the given key is undefined');
assert.throws(function() { _.bindAll(moe, 'name'); }, TypeError, 'throws an error for bindAll if the given key is not a function');
_.bindAll(moe, 'sayHi', 'sayLast');
curly.sayHi = moe.sayHi;
equal(curly.sayHi(), 'hi: moe');
assert.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');
assert.equal(sayLast(1, 2, 3, 4, 5, 6, 7, 'Tom'), 'hi: moe', 'createCallback works with any number of arguments');
_.bindAll(moe, ['getName']);
var getName = moe.getName;
equal(getName(), 'name: moe', 'flattens arguments into a single list');
assert.equal(getName(), 'name: moe', 'flattens arguments into a single list');
});
test('memoize', function() {
test('memoize', function(assert) {
var fib = function(n) {
return n < 2 ? n : fib(n - 1) + fib(n - 2);
};
equal(fib(10), 55, 'a memoized version of fibonacci produces identical results');
assert.equal(fib(10), 55, 'a memoized version of fibonacci produces identical results');
fib = _.memoize(fib); // Redefine `fib` for memoization
equal(fib(10), 55, 'a memoized version of fibonacci produces identical results');
assert.equal(fib(10), 55, 'a memoized version of fibonacci produces identical results');
var o = function(str) {
return str;
};
var fastO = _.memoize(o);
equal(o('toString'), 'toString', 'checks hasOwnProperty');
equal(fastO('toString'), 'toString', 'checks hasOwnProperty');
assert.equal(o('toString'), 'toString', 'checks hasOwnProperty');
assert.equal(fastO('toString'), 'toString', 'checks hasOwnProperty');
// Expose the cache.
var upper = _.memoize(function(s) {
return s.toUpperCase();
});
equal(upper('foo'), 'FOO');
equal(upper('bar'), 'BAR');
deepEqual(upper.cache, {foo: 'FOO', bar: 'BAR'});
assert.equal(upper('foo'), 'FOO');
assert.equal(upper('bar'), 'BAR');
assert.deepEqual(upper.cache, {foo: 'FOO', bar: 'BAR'});
upper.cache = {foo: 'BAR', bar: 'FOO'};
equal(upper('foo'), 'BAR');
equal(upper('bar'), 'FOO');
assert.equal(upper('foo'), 'BAR');
assert.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');
assert.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');
assert.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) {
@@ -169,78 +169,78 @@
});
var myObj = objCacher('a', 'alpha');
var myObjAlias = objCacher('b', 'alpha');
notStrictEqual(myObj, void 0, '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');
assert.notStrictEqual(myObj, void 0, 'object is created if second argument used as key');
assert.strictEqual(myObj, myObjAlias, 'object is cached if second argument used as key');
assert.strictEqual(myObj.value, 'a', 'object is not modified if second argument used as key');
});
asyncTest('delay', 2, function() {
asyncTest('delay', 2, function(assert) {
var delayed = false;
_.delay(function(){ delayed = true; }, 100);
setTimeout(function(){ ok(!delayed, "didn't delay the function quite yet"); }, 50);
setTimeout(function(){ ok(delayed, 'delayed the function'); start(); }, 150);
setTimeout(function(){ assert.ok(!delayed, "didn't delay the function quite yet"); }, 50);
setTimeout(function(){ assert.ok(delayed, 'delayed the function'); start(); }, 150);
});
asyncTest('defer', 1, function() {
asyncTest('defer', 1, function(assert) {
var deferred = false;
_.defer(function(bool){ deferred = bool; }, true);
_.delay(function(){ ok(deferred, 'deferred the function'); start(); }, 50);
_.delay(function(){ assert.ok(deferred, 'deferred the function'); start(); }, 50);
});
asyncTest('throttle', 2, function() {
asyncTest('throttle', 2, function(assert) {
var counter = 0;
var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 32);
throttledIncr(); throttledIncr();
equal(counter, 1, 'incr was called immediately');
_.delay(function(){ equal(counter, 2, 'incr was throttled'); start(); }, 64);
assert.equal(counter, 1, 'incr was called immediately');
_.delay(function(){ assert.equal(counter, 2, 'incr was throttled'); start(); }, 64);
});
asyncTest('throttle arguments', 2, function() {
asyncTest('throttle arguments', 2, function(assert) {
var value = 0;
var update = function(val){ value = val; };
var throttledUpdate = _.throttle(update, 32);
throttledUpdate(1); throttledUpdate(2);
_.delay(function(){ throttledUpdate(3); }, 64);
equal(value, 1, 'updated to latest value');
_.delay(function(){ equal(value, 3, 'updated to latest value'); start(); }, 96);
assert.equal(value, 1, 'updated to latest value');
_.delay(function(){ assert.equal(value, 3, 'updated to latest value'); start(); }, 96);
});
asyncTest('throttle once', 2, function() {
asyncTest('throttle once', 2, function(assert) {
var counter = 0;
var incr = function(){ return ++counter; };
var throttledIncr = _.throttle(incr, 32);
var result = throttledIncr();
_.delay(function(){
equal(result, 1, 'throttled functions return their value');
equal(counter, 1, 'incr was called once'); start();
assert.equal(result, 1, 'throttled functions return their value');
assert.equal(counter, 1, 'incr was called once'); start();
}, 64);
});
asyncTest('throttle twice', 1, function() {
asyncTest('throttle twice', 1, function(assert) {
var counter = 0;
var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 32);
throttledIncr(); throttledIncr();
_.delay(function(){ equal(counter, 2, 'incr was called twice'); start(); }, 64);
_.delay(function(){ assert.equal(counter, 2, 'incr was called twice'); start(); }, 64);
});
asyncTest('more throttling', 3, function() {
asyncTest('more throttling', 3, function(assert) {
var counter = 0;
var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 30);
throttledIncr(); throttledIncr();
equal(counter, 1);
assert.equal(counter, 1);
_.delay(function(){
equal(counter, 2);
assert.equal(counter, 2);
throttledIncr();
equal(counter, 3);
assert.equal(counter, 3);
start();
}, 85);
});
asyncTest('throttle repeatedly with results', 6, function() {
asyncTest('throttle repeatedly with results', 6, function(assert) {
var counter = 0;
var incr = function(){ return ++counter; };
var throttledIncr = _.throttle(incr, 100);
@@ -252,17 +252,17 @@
_.delay(saveResult, 160);
_.delay(saveResult, 230);
_.delay(function() {
equal(results[0], 1, 'incr was called once');
equal(results[1], 1, 'incr was throttled');
equal(results[2], 1, 'incr was throttled');
equal(results[3], 2, 'incr was called twice');
equal(results[4], 2, 'incr was throttled');
equal(results[5], 3, 'incr was called trailing');
assert.equal(results[0], 1, 'incr was called once');
assert.equal(results[1], 1, 'incr was throttled');
assert.equal(results[2], 1, 'incr was throttled');
assert.equal(results[3], 2, 'incr was called twice');
assert.equal(results[4], 2, 'incr was throttled');
assert.equal(results[5], 3, 'incr was called trailing');
start();
}, 300);
});
asyncTest('throttle triggers trailing call when invoked repeatedly', 2, function() {
asyncTest('throttle triggers trailing call when invoked repeatedly', 2, function(assert) {
var counter = 0;
var limit = 48;
var incr = function(){ counter++; };
@@ -273,29 +273,29 @@
throttledIncr();
}
var lastCount = counter;
ok(counter > 1);
assert.ok(counter > 1);
_.delay(function() {
ok(counter > lastCount);
assert.ok(counter > lastCount);
start();
}, 96);
});
asyncTest('throttle does not trigger leading call when leading is set to false', 2, function() {
asyncTest('throttle does not trigger leading call when leading is set to false', 2, function(assert) {
var counter = 0;
var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 60, {leading: false});
throttledIncr(); throttledIncr();
equal(counter, 0);
assert.equal(counter, 0);
_.delay(function() {
equal(counter, 1);
assert.equal(counter, 1);
start();
}, 96);
});
asyncTest('more throttle does not trigger leading call when leading is set to false', 3, function() {
asyncTest('more throttle does not trigger leading call when leading is set to false', 3, function(assert) {
var counter = 0;
var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 100, {leading: false});
@@ -304,75 +304,75 @@
_.delay(throttledIncr, 50);
_.delay(throttledIncr, 60);
_.delay(throttledIncr, 200);
equal(counter, 0);
assert.equal(counter, 0);
_.delay(function() {
equal(counter, 1);
assert.equal(counter, 1);
}, 250);
_.delay(function() {
equal(counter, 2);
assert.equal(counter, 2);
start();
}, 350);
});
asyncTest('one more throttle with leading: false test', 2, function() {
asyncTest('one more throttle with leading: false test', 2, function(assert) {
var counter = 0;
var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 100, {leading: false});
var time = new Date;
while (new Date - time < 350) throttledIncr();
ok(counter <= 3);
assert.ok(counter <= 3);
_.delay(function() {
ok(counter <= 4);
assert.ok(counter <= 4);
start();
}, 200);
});
asyncTest('throttle does not trigger trailing call when trailing is set to false', 4, function() {
asyncTest('throttle does not trigger trailing call when trailing is set to false', 4, function(assert) {
var counter = 0;
var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 60, {trailing: false});
throttledIncr(); throttledIncr(); throttledIncr();
equal(counter, 1);
assert.equal(counter, 1);
_.delay(function() {
equal(counter, 1);
assert.equal(counter, 1);
throttledIncr(); throttledIncr();
equal(counter, 2);
assert.equal(counter, 2);
_.delay(function() {
equal(counter, 2);
assert.equal(counter, 2);
start();
}, 96);
}, 96);
});
asyncTest('throttle continues to function after system time is set backwards', 2, function() {
asyncTest('throttle continues to function after system time is set backwards', 2, function(assert) {
var counter = 0;
var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 100);
var origNowFunc = _.now;
throttledIncr();
equal(counter, 1);
assert.equal(counter, 1);
_.now = function() {
return new Date(2013, 0, 1, 1, 1, 1);
};
_.delay(function() {
throttledIncr();
equal(counter, 2);
assert.equal(counter, 2);
start();
_.now = origNowFunc;
}, 200);
});
asyncTest('throttle re-entrant', 2, function() {
asyncTest('throttle re-entrant', 2, function(assert) {
var sequence = [
['b1', 'b2'],
['c1', 'c2']
@@ -388,50 +388,100 @@
};
throttledAppend = _.throttle(append, 32);
throttledAppend.call('a1', 'a2');
equal(value, 'a1a2');
assert.equal(value, 'a1a2');
_.delay(function(){
equal(value, 'a1a2c1c2b1b2', 'append was throttled successfully');
assert.equal(value, 'a1a2c1c2b1b2', 'append was throttled successfully');
start();
}, 100);
});
asyncTest('debounce', 1, function() {
asyncTest('throttle cancel', function(assert) {
var counter = 0;
var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 32);
throttledIncr();
throttledIncr.cancel();
throttledIncr();
throttledIncr();
assert.equal(counter, 2, 'incr was called immediately');
_.delay(function(){ assert.equal(counter, 3, 'incr was throttled'); start(); }, 64);
});
asyncTest('throttle cancel with leading: false', function(assert) {
var counter = 0;
var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 32, {leading: false});
throttledIncr();
throttledIncr.cancel();
assert.equal(counter, 0, 'incr was throttled');
_.delay(function(){ assert.equal(counter, 0, 'incr was throttled'); start(); }, 64);
});
asyncTest('debounce', 1, function(assert) {
var counter = 0;
var incr = function(){ counter++; };
var debouncedIncr = _.debounce(incr, 32);
debouncedIncr(); debouncedIncr();
_.delay(debouncedIncr, 16);
_.delay(function(){ equal(counter, 1, 'incr was debounced'); start(); }, 96);
_.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); start(); }, 96);
});
asyncTest('debounce asap', 4, function() {
asyncTest('debounce cancel', 1, function(assert) {
var counter = 0;
var incr = function(){ counter++; };
var debouncedIncr = _.debounce(incr, 32);
debouncedIncr();
debouncedIncr.cancel();
_.delay(function(){ assert.equal(counter, 0, 'incr was not called'); start(); }, 96);
});
asyncTest('debounce asap', 4, function(assert) {
var a, b;
var counter = 0;
var incr = function(){ return ++counter; };
var debouncedIncr = _.debounce(incr, 64, true);
a = debouncedIncr();
b = debouncedIncr();
equal(a, 1);
equal(b, 1);
equal(counter, 1, 'incr was called immediately');
assert.equal(a, 1);
assert.equal(b, 1);
assert.equal(counter, 1, 'incr was called immediately');
_.delay(debouncedIncr, 16);
_.delay(debouncedIncr, 32);
_.delay(debouncedIncr, 48);
_.delay(function(){ equal(counter, 1, 'incr was debounced'); start(); }, 128);
_.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); start(); }, 128);
});
asyncTest('debounce asap recursively', 2, function() {
asyncTest('debounce asap cancel', 4, function(assert) {
var a, b;
var counter = 0;
var incr = function(){ return ++counter; };
var debouncedIncr = _.debounce(incr, 64, true);
a = debouncedIncr();
debouncedIncr.cancel();
b = debouncedIncr();
assert.equal(a, 1);
assert.equal(b, 2);
assert.equal(counter, 2, 'incr was called immediately');
_.delay(debouncedIncr, 16);
_.delay(debouncedIncr, 32);
_.delay(debouncedIncr, 48);
_.delay(function(){ assert.equal(counter, 2, 'incr was debounced'); start(); }, 128);
});
asyncTest('debounce asap recursively', 2, function(assert) {
var counter = 0;
var debouncedIncr = _.debounce(function(){
counter++;
if (counter < 10) debouncedIncr();
}, 32, true);
debouncedIncr();
equal(counter, 1, 'incr was called immediately');
_.delay(function(){ equal(counter, 1, 'incr was debounced'); start(); }, 96);
assert.equal(counter, 1, 'incr was called immediately');
_.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); start(); }, 96);
});
asyncTest('debounce after system time is set backwards', 2, function() {
asyncTest('debounce after system time is set backwards', 2, function(assert) {
var counter = 0;
var origNowFunc = _.now;
var debouncedIncr = _.debounce(function(){
@@ -439,7 +489,7 @@
}, 100, true);
debouncedIncr();
equal(counter, 1, 'incr was called immediately');
assert.equal(counter, 1, 'incr was called immediately');
_.now = function() {
return new Date(2013, 0, 1, 1, 1, 1);
@@ -447,13 +497,13 @@
_.delay(function() {
debouncedIncr();
equal(counter, 2, 'incr was debounced successfully');
assert.equal(counter, 2, 'incr was debounced successfully');
start();
_.now = origNowFunc;
}, 200);
});
asyncTest('debounce re-entrant', 2, function() {
asyncTest('debounce re-entrant', 2, function(assert) {
var sequence = [
['b1', 'b2']
];
@@ -468,80 +518,80 @@
};
debouncedAppend = _.debounce(append, 32);
debouncedAppend.call('a1', 'a2');
equal(value, '');
assert.equal(value, '');
_.delay(function(){
equal(value, 'a1a2b1b2', 'append was debounced successfully');
assert.equal(value, 'a1a2b1b2', 'append was debounced successfully');
start();
}, 100);
});
test('once', function() {
test('once', function(assert) {
var num = 0;
var increment = _.once(function(){ return ++num; });
increment();
increment();
equal(num, 1);
assert.equal(num, 1);
equal(increment(), 1, 'stores a memo to the last value');
assert.equal(increment(), 1, 'stores a memo to the last value');
});
test('Recursive onced function.', 1, function() {
test('Recursive onced function.', 1, function(assert) {
var f = _.once(function(){
ok(true);
assert.ok(true);
f();
});
f();
});
test('wrap', function() {
test('wrap', function(assert) {
var greet = function(name){ return 'hi: ' + name; };
var backwards = _.wrap(greet, function(func, name){ return func(name) + ' ' + name.split('').reverse().join(''); });
equal(backwards('moe'), 'hi: moe eom', 'wrapped the salutation function');
assert.equal(backwards('moe'), 'hi: moe eom', 'wrapped the salutation function');
var inner = function(){ return 'Hello '; };
var obj = {name: 'Moe'};
obj.hi = _.wrap(inner, function(fn){ return fn() + this.name; });
equal(obj.hi(), 'Hello Moe');
assert.equal(obj.hi(), 'Hello Moe');
var noop = function(){};
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']);
assert.deepEqual(ret, [noop, ['whats', 'your'], 'vector', 'victor']);
});
test('negate', function() {
test('negate', function(assert) {
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');
assert.equal(_.negate(isOdd)(2), true, 'should return the complement of the given function');
assert.equal(_.negate(isOdd)(3), false, 'should return the complement of the given function');
});
test('compose', function() {
test('compose', function(assert) {
var greet = function(name){ return 'hi: ' + name; };
var exclaim = function(sentence){ return sentence + '!'; };
var composed = _.compose(exclaim, greet);
equal(composed('moe'), 'hi: moe!', 'can compose a function that takes another');
assert.equal(composed('moe'), 'hi: moe!', 'can compose a function that takes another');
composed = _.compose(greet, exclaim);
equal(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative');
assert.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');
assert.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');
assert.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');
assert.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);
assert.equal(composed(1, 2, 3), 12);
});
test('after', function() {
test('after', function(assert) {
var testAfter = function(afterAmount, timesCalled) {
var afterCalled = 0;
var after = _.after(afterAmount, function() {
@@ -551,13 +601,13 @@
return afterCalled;
};
equal(testAfter(5, 5), 1, 'after(N) should fire after being called N times');
equal(testAfter(5, 4), 0, 'after(N) should not fire unless called N times');
equal(testAfter(0, 0), 0, 'after(0) should not fire immediately');
equal(testAfter(0, 1), 1, 'after(0) should fire when first invoked');
assert.equal(testAfter(5, 5), 1, 'after(N) should fire after being called N times');
assert.equal(testAfter(5, 4), 0, 'after(N) should not fire unless called N times');
assert.equal(testAfter(0, 0), 0, 'after(0) should not fire immediately');
assert.equal(testAfter(0, 1), 1, 'after(0) should fire when first invoked');
});
test('before', function() {
test('before', function(assert) {
var testBefore = function(beforeAmount, timesCalled) {
var beforeCalled = 0;
var before = _.before(beforeAmount, function() { beforeCalled++; });
@@ -565,58 +615,58 @@
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');
assert.equal(testBefore(5, 5), 4, 'before(N) should not fire after being called N times');
assert.equal(testBefore(5, 4), 4, 'before(N) should fire before being called N times');
assert.equal(testBefore(0, 0), 0, 'before(0) should not fire immediately');
assert.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');
assert.equal(increment(), 2, 'stores a memo to the last value');
assert.equal(context.num, 2, 'provides context');
});
test('iteratee', function() {
test('iteratee', function(assert) {
var identity = _.iteratee();
equal(identity, _.identity, '_.iteratee is exposed as an external function.');
assert.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));
assert.equal(cb().length, 0);
assert.deepEqual(_.toArray(cb(1, 2, 3)), _.range(1, 4));
assert.deepEqual(_.toArray(cb(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), _.range(1, 11));
});
});
test('restArgs', 10, function() {
test('restArgs', 10, function(assert) {
_.restArgs(function(a, args) {
strictEqual(a, 1);
deepEqual(args, [2, 3], 'collects rest arguments into an array');
assert.strictEqual(a, 1);
assert.deepEqual(args, [2, 3], 'collects rest arguments into an array');
})(1, 2, 3);
_.restArgs(function(a, args) {
strictEqual(a, void 0);
deepEqual(args, [], 'passes empty array if there are not enough arguments');
assert.strictEqual(a, void 0);
assert.deepEqual(args, [], 'passes empty array if there are not enough arguments');
})();
_.restArgs(function(a, b, c, args) {
strictEqual(arguments.length, 4);
deepEqual(args, [4, 5], 'works on functions with many named parameters');
assert.strictEqual(arguments.length, 4);
assert.deepEqual(args, [4, 5], 'works on functions with many named parameters');
})(1, 2, 3, 4, 5);
var obj = {};
_.restArgs(function() {
strictEqual(this, obj, 'invokes function with this context');
assert.strictEqual(this, obj, 'invokes function with this context');
}).call(obj);
_.restArgs(function(array, iteratee, context) {
deepEqual(array, [1, 2, 3, 4], 'startIndex can be used manually specify index of rest parameter');
strictEqual(iteratee, void 0);
strictEqual(context, void 0);
assert.deepEqual(array, [1, 2, 3, 4], 'startIndex can be used manually specify index of rest parameter');
assert.strictEqual(iteratee, void 0);
assert.strictEqual(context, void 0);
}, 0)(1, 2, 3, 4);
});

File diff suppressed because it is too large Load Diff

View File

@@ -15,18 +15,20 @@
});
if (typeof this == 'object') {
test('noConflict', function() {
test('noConflict', function(assert) {
var underscore = _.noConflict();
equal(underscore.identity(1), 1);
assert.equal(underscore.identity(1), 1);
if (typeof require != 'function') {
equal(this._, void 0, 'global underscore is removed');
assert.equal(this._, void 0, 'global underscore is removed');
this._ = underscore;
} else if (typeof global !== 'undefined') {
delete global._;
}
});
}
if (typeof require == 'function') {
asyncTest('noConflict (node vm)', 2, function() {
asyncTest('noConflict (node vm)', 2, function(assert) {
var fs = require('fs');
var vm = require('vm');
var filename = __dirname + '/../underscore.js';
@@ -37,179 +39,179 @@
);
var context = {_: 'oldvalue'};
sandbox.runInNewContext(context);
equal(context._, 'oldvalue');
equal(context.underscore.VERSION, _.VERSION);
assert.equal(context._, 'oldvalue');
assert.equal(context.underscore.VERSION, _.VERSION);
start();
});
});
}
test('#750 - Return _ instance.', 2, function() {
test('#750 - Return _ instance.', 2, function(assert) {
var instance = _([]);
ok(_(instance) === instance);
ok(new _(instance) === instance);
assert.ok(_(instance) === instance);
assert.ok(new _(instance) === instance);
});
test('identity', function() {
test('identity', function(assert) {
var stooge = {name: 'moe'};
equal(_.identity(stooge), stooge, 'stooge is the same as his identity');
assert.equal(_.identity(stooge), stooge, 'stooge is the same as his identity');
});
test('constant', function() {
test('constant', function(assert) {
var stooge = {name: 'moe'};
equal(_.constant(stooge)(), stooge, 'should create a function that returns stooge');
assert.equal(_.constant(stooge)(), stooge, 'should create a function that returns stooge');
});
test('noop', function() {
strictEqual(_.noop('curly', 'larry', 'moe'), void 0, 'should always return undefined');
test('noop', function(assert) {
assert.strictEqual(_.noop('curly', 'larry', 'moe'), void 0, 'should always return undefined');
});
test('property', function() {
test('property', function(assert) {
var stooge = {name: 'moe'};
equal(_.property('name')(stooge), 'moe', 'should return the property with the given name');
equal(_.property('name')(null), void 0, 'should return undefined for null values');
equal(_.property('name')(void 0), void 0, 'should return undefined for undefined values');
assert.equal(_.property('name')(stooge), 'moe', 'should return the property with the given name');
assert.equal(_.property('name')(null), void 0, 'should return undefined for null values');
assert.equal(_.property('name')(void 0), void 0, 'should return undefined for undefined values');
});
test('propertyOf', function() {
test('propertyOf', function(assert) {
var stoogeRanks = _.propertyOf({curly: 2, moe: 1, larry: 3});
equal(stoogeRanks('curly'), 2, 'should return the property with the given name');
equal(stoogeRanks(null), void 0, 'should return undefined for null values');
equal(stoogeRanks(void 0), void 0, 'should return undefined for undefined values');
assert.equal(stoogeRanks('curly'), 2, 'should return the property with the given name');
assert.equal(stoogeRanks(null), void 0, 'should return undefined for null values');
assert.equal(stoogeRanks(void 0), void 0, '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');
assert.equal(moreStoogeRanks('curly'), 2, 'should return properties from further up the prototype chain');
var nullPropertyOf = _.propertyOf(null);
equal(nullPropertyOf('curly'), void 0, 'should return undefined when obj is null');
assert.equal(nullPropertyOf('curly'), void 0, 'should return undefined when obj is null');
var undefPropertyOf = _.propertyOf(void 0);
equal(undefPropertyOf('curly'), void 0, 'should return undefined when obj is undefined');
assert.equal(undefPropertyOf('curly'), void 0, 'should return undefined when obj is undefined');
});
test('random', function() {
test('random', function(assert) {
var array = _.range(1000);
var min = Math.pow(2, 31);
var max = Math.pow(2, 62);
ok(_.every(array, function() {
assert.ok(_.every(array, function() {
return _.random(min, max) >= min;
}), 'should produce a random number greater than or equal to the minimum number');
ok(_.some(array, function() {
assert.ok(_.some(array, function() {
return _.random(Number.MAX_VALUE) > 0;
}), 'should produce a random number when passed `Number.MAX_VALUE`');
});
test('now', function() {
test('now', function(assert) {
var diff = _.now() - new Date().getTime();
ok(diff <= 0 && diff > -5, 'Produces the correct time in milliseconds');//within 5ms
assert.ok(diff <= 0 && diff > -5, 'Produces the correct time in milliseconds');//within 5ms
});
test('uniqueId', function() {
test('uniqueId', function(assert) {
var ids = [], i = 0;
while (i++ < 100) ids.push(_.uniqueId());
equal(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids');
assert.equal(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids');
});
test('times', function() {
test('times', function(assert) {
var vals = [];
_.times(3, function(i) { vals.push(i); });
deepEqual(vals, [0, 1, 2], 'is 0 indexed');
assert.deepEqual(vals, [0, 1, 2], 'is 0 indexed');
//
vals = [];
_(3).times(function(i) { vals.push(i); });
deepEqual(vals, [0, 1, 2], 'works as a wrapper');
assert.deepEqual(vals, [0, 1, 2], 'works as a wrapper');
// collects return values
deepEqual([0, 1, 2], _.times(3, function(i) { return i; }), 'collects return values');
assert.deepEqual([0, 1, 2], _.times(3, function(i) { return i; }), 'collects return values');
deepEqual(_.times(0, _.identity), []);
deepEqual(_.times(-1, _.identity), []);
deepEqual(_.times(parseFloat('-Infinity'), _.identity), []);
assert.deepEqual(_.times(0, _.identity), []);
assert.deepEqual(_.times(-1, _.identity), []);
assert.deepEqual(_.times(parseFloat('-Infinity'), _.identity), []);
});
test('mixin', function() {
test('mixin', function(assert) {
_.mixin({
myReverse: function(string) {
return string.split('').reverse().join('');
}
});
equal(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _');
equal(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper');
assert.equal(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _');
assert.equal(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper');
});
test('_.escape', function() {
equal(_.escape(null), '');
test('_.escape', function(assert) {
assert.equal(_.escape(null), '');
});
test('_.unescape', function() {
test('_.unescape', function(assert) {
var string = 'Curly & Moe';
equal(_.unescape(null), '');
equal(_.unescape(_.escape(string)), string);
equal(_.unescape(string), string, 'don\'t unescape unnecessarily');
assert.equal(_.unescape(null), '');
assert.equal(_.unescape(_.escape(string)), string);
assert.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('_.escape & unescape', function(assert) {
// test & (&amp;) seperately obviously
var escapeCharacters = ['<', '>', '"', '\'', '`'];
_.each(escapeCharacters, function(escapeChar) {
var s = 'a ' + escapeChar + ' string escaped';
var e = _.escape(s);
notEqual(s, e, escapeChar + ' is escaped');
equal(s, _.unescape(e), escapeChar + ' can be unescaped');
assert.notEqual(s, e, escapeChar + ' is escaped');
assert.equal(s, _.unescape(e), escapeChar + ' can be unescaped');
s = 'a ' + escapeChar + escapeChar + escapeChar + 'some more string' + escapeChar;
e = _.escape(s);
equal(e.indexOf(escapeChar), -1, 'can escape multiple occurances of ' + escapeChar);
equal(_.unescape(e), s, 'multiple occurrences of ' + escapeChar + ' can be unescaped');
assert.equal(e.indexOf(escapeChar), -1, 'can escape multiple occurances of ' + escapeChar);
assert.equal(_.unescape(e), s, '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) {
assert.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');
assert.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;');
assert.ok(escaped.indexOf('&') !== -1, 'handles & aka &amp;');
assert.equal(_.unescape(str), str, 'can unescape &amp;');
});
test('template', function() {
test('template', function(assert) {
var basicTemplate = _.template("<%= thing %> is gettin' on my noives!");
var result = basicTemplate({thing: 'This'});
equal(result, "This is gettin' on my noives!", 'can do basic attribute interpolation');
assert.equal(result, "This is gettin' on my noives!", 'can do basic attribute interpolation');
var sansSemicolonTemplate = _.template('A <% this %> B');
equal(sansSemicolonTemplate(), 'A B');
assert.equal(sansSemicolonTemplate(), 'A B');
var backslashTemplate = _.template('<%= thing %> is \\ridanculous');
equal(backslashTemplate({thing: 'This'}), 'This is \\ridanculous');
assert.equal(backslashTemplate({thing: 'This'}), 'This is \\ridanculous');
var escapeTemplate = _.template('<%= a ? "checked=\\"checked\\"" : "" %>');
equal(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.');
assert.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>');
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');
assert.equal(result, '<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>', 'can run arbitrary javascript in templates');
var escapedCharsInJavascriptTemplate = _.template('<ul><% _.each(numbers.split("\\n"), function(item) { %><li><%= item %></li><% }) %></ul>');
result = escapedCharsInJavascriptTemplate({numbers: 'one\ntwo\nthree\nfour'});
equal(result, '<ul><li>one</li><li>two</li><li>three</li><li>four</li></ul>', 'Can use escaped characters (e.g. \\n) in JavaScript');
assert.equal(result, '<ul><li>one</li><li>two</li><li>three</li><li>four</li></ul>', 'Can use escaped characters (e.g. \\n) in JavaScript');
var namespaceCollisionTemplate = _.template('<%= pageCount %> <%= thumbnails[pageCount] %> <% _.each(thumbnails, function(p) { %><div class="thumbnail" rel="<%= p %>"></div><% }); %>');
result = namespaceCollisionTemplate({
@@ -220,32 +222,32 @@
3: 'p3-thumbnail.gif'
}
});
equal(result, '3 p3-thumbnail.gif <div class="thumbnail" rel="p1-thumbnail.gif"></div><div class="thumbnail" rel="p2-thumbnail.gif"></div><div class="thumbnail" rel="p3-thumbnail.gif"></div>');
assert.equal(result, '3 p3-thumbnail.gif <div class="thumbnail" rel="p1-thumbnail.gif"></div><div class="thumbnail" rel="p2-thumbnail.gif"></div><div class="thumbnail" rel="p3-thumbnail.gif"></div>');
var noInterpolateTemplate = _.template('<div><p>Just some text. Hey, I know this is silly but it aids consistency.</p></div>');
result = noInterpolateTemplate();
equal(result, '<div><p>Just some text. Hey, I know this is silly but it aids consistency.</p></div>');
assert.equal(result, '<div><p>Just some text. Hey, I know this is silly but it aids consistency.</p></div>');
var quoteTemplate = _.template("It's its, not it's");
equal(quoteTemplate({}), "It's its, not it's");
assert.equal(quoteTemplate({}), "It's its, not it's");
var quoteInStatementAndBody = _.template('<% ' +
" if(foo == 'bar'){ " +
"%>Statement quotes and 'quotes'.<% } %>");
equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'.");
assert.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.');
assert.equal(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.');
var template = _.template('<i><%- value %></i>');
result = template({value: '<script>'});
equal(result, '<i>&lt;script&gt;</i>');
assert.equal(result, '<i>&lt;script&gt;</i>');
var stooge = {
name: 'Moe',
template: _.template("I'm <%= this.name %>")
};
equal(stooge.template(), "I'm Moe");
assert.equal(stooge.template(), "I'm Moe");
template = _.template('\n ' +
' <%\n ' +
@@ -253,7 +255,7 @@
' if (data) { data += 12345; }; %>\n ' +
' <li><%= data %></li>\n '
);
equal(template({data: 12345}).replace(/\s/g, ''), '<li>24690</li>');
assert.equal(template({data: 12345}).replace(/\s/g, ''), '<li>24690</li>');
_.templateSettings = {
evaluate: /\{\{([\s\S]+?)\}\}/g,
@@ -262,13 +264,13 @@
var custom = _.template('<ul>{{ for (var key in people) { }}<li>{{= people[key] }}</li>{{ } }}</ul>');
result = custom({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');
assert.equal(result, '<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>', 'can run arbitrary javascript in templates');
var customQuote = _.template("It's its, not it's");
equal(customQuote({}), "It's its, not it's");
assert.equal(customQuote({}), "It's its, not it's");
quoteInStatementAndBody = _.template("{{ if(foo == 'bar'){ }}Statement quotes and 'quotes'.{{ } }}");
equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'.");
assert.equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'.");
_.templateSettings = {
evaluate: /<\?([\s\S]+?)\?>/g,
@@ -277,136 +279,136 @@
var customWithSpecialChars = _.template('<ul><? for (var key in people) { ?><li><?= people[key] ?></li><? } ?></ul>');
result = customWithSpecialChars({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');
assert.equal(result, '<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>', 'can run arbitrary javascript in templates');
var customWithSpecialCharsQuote = _.template("It's its, not it's");
equal(customWithSpecialCharsQuote({}), "It's its, not it's");
assert.equal(customWithSpecialCharsQuote({}), "It's its, not it's");
quoteInStatementAndBody = _.template("<? if(foo == 'bar'){ ?>Statement quotes and 'quotes'.<? } ?>");
equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'.");
assert.equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'.");
_.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g
};
var mustache = _.template('Hello {{planet}}!');
equal(mustache({planet: 'World'}), 'Hello World!', 'can mimic mustache.js');
assert.equal(mustache({planet: 'World'}), 'Hello World!', 'can mimic mustache.js');
var templateWithNull = _.template('a null undefined {{planet}}');
equal(templateWithNull({planet: 'world'}), 'a null undefined world', 'can handle missing escape and evaluate settings');
assert.equal(templateWithNull({planet: 'world'}), 'a null undefined world', 'can handle missing escape and evaluate settings');
});
test('_.template provides the generated function source, when a SyntaxError occurs', function() {
test('_.template provides the generated function source, when a SyntaxError occurs', function(assert) {
var source;
try {
_.template('<b><%= if x %></b>');
} catch (ex) {
source = ex.source;
}
ok(/__p/.test(source));
assert.ok(/__p/.test(source));
});
test('_.template handles \\u2028 & \\u2029', function() {
test('_.template handles \\u2028 & \\u2029', function(assert) {
var tmpl = _.template('<p>\u2028<%= "\\u2028\\u2029" %>\u2029</p>');
strictEqual(tmpl(), '<p>\u2028\u2028\u2029\u2029</p>');
assert.strictEqual(tmpl(), '<p>\u2028\u2028\u2029\u2029</p>');
});
test('result calls functions and returns primitives', function() {
test('result calls functions and returns primitives', function(assert) {
var obj = {w: '', x: 'x', y: function(){ return this.x; }};
strictEqual(_.result(obj, 'w'), '');
strictEqual(_.result(obj, 'x'), 'x');
strictEqual(_.result(obj, 'y'), 'x');
strictEqual(_.result(obj, 'z'), void 0);
strictEqual(_.result(null, 'x'), void 0);
assert.strictEqual(_.result(obj, 'w'), '');
assert.strictEqual(_.result(obj, 'x'), 'x');
assert.strictEqual(_.result(obj, 'y'), 'x');
assert.strictEqual(_.result(obj, 'z'), void 0);
assert.strictEqual(_.result(null, 'x'), void 0);
});
test('result returns a default value if object is null or undefined', function() {
strictEqual(_.result(null, 'b', 'default'), 'default');
strictEqual(_.result(void 0, 'c', 'default'), 'default');
strictEqual(_.result(''.match('missing'), 1, 'default'), 'default');
test('result returns a default value if object is null or undefined', function(assert) {
assert.strictEqual(_.result(null, 'b', 'default'), 'default');
assert.strictEqual(_.result(void 0, 'c', 'default'), 'default');
assert.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 returns a default value if property of object is missing', function(assert) {
assert.strictEqual(_.result({d: null}, 'd', 'default'), null);
assert.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: void 0}, 'd', 'default'), 'default');
test('result only returns the default value if the object does not have the property or is undefined', function(assert) {
assert.strictEqual(_.result({}, 'b', 'default'), 'default');
assert.strictEqual(_.result({d: void 0}, 'd', 'default'), 'default');
});
test('result does not return the default if the property of an object is found in the prototype', function() {
test('result does not return the default if the property of an object is found in the prototype', function(assert) {
var Foo = function(){};
Foo.prototype.bar = 1;
strictEqual(_.result(new Foo, 'bar', 2), 1);
assert.strictEqual(_.result(new Foo, 'bar', 2), 1);
});
test('result does use the fallback when the result of invoking the property is undefined', function() {
test('result does use the fallback when the result of invoking the property is undefined', function(assert) {
var obj = {a: function() {}};
strictEqual(_.result(obj, 'a', 'failed'), void 0);
assert.strictEqual(_.result(obj, 'a', 'failed'), void 0);
});
test('result fallback can use a function', function() {
test('result fallback can use a function', function(assert) {
var obj = {a: [1, 2, 3]};
strictEqual(_.result(obj, 'b', _.constant(5)), 5);
strictEqual(_.result(obj, 'b', function() {
assert.strictEqual(_.result(obj, 'b', _.constant(5)), 5);
assert.strictEqual(_.result(obj, 'b', function() {
return this.a;
}), obj.a, 'called with context');
});
test('_.templateSettings.variable', function() {
test('_.templateSettings.variable', function(assert) {
var s = '<%=data.x%>';
var data = {x: 'x'};
var tmp = _.template(s, {variable: 'data'});
strictEqual(tmp(data), 'x');
assert.strictEqual(tmp(data), 'x');
_.templateSettings.variable = 'data';
strictEqual(_.template(s)(data), 'x');
assert.strictEqual(_.template(s)(data), 'x');
});
test('#547 - _.templateSettings is unchanged by custom settings.', function() {
ok(!_.templateSettings.variable);
test('#547 - _.templateSettings is unchanged by custom settings.', function(assert) {
assert.ok(!_.templateSettings.variable);
_.template('', {}, {variable: 'x'});
ok(!_.templateSettings.variable);
assert.ok(!_.templateSettings.variable);
});
test('#556 - undefined template variables.', function() {
test('#556 - undefined template variables.', function(assert) {
var template = _.template('<%=x%>');
strictEqual(template({x: null}), '');
strictEqual(template({x: void 0}), '');
assert.strictEqual(template({x: null}), '');
assert.strictEqual(template({x: void 0}), '');
var templateEscaped = _.template('<%-x%>');
strictEqual(templateEscaped({x: null}), '');
strictEqual(templateEscaped({x: void 0}), '');
assert.strictEqual(templateEscaped({x: null}), '');
assert.strictEqual(templateEscaped({x: void 0}), '');
var templateWithProperty = _.template('<%=x.foo%>');
strictEqual(templateWithProperty({x: {}}), '');
strictEqual(templateWithProperty({x: {}}), '');
assert.strictEqual(templateWithProperty({x: {}}), '');
assert.strictEqual(templateWithProperty({x: {}}), '');
var templateWithPropertyEscaped = _.template('<%-x.foo%>');
strictEqual(templateWithPropertyEscaped({x: {}}), '');
strictEqual(templateWithPropertyEscaped({x: {}}), '');
assert.strictEqual(templateWithPropertyEscaped({x: {}}), '');
assert.strictEqual(templateWithPropertyEscaped({x: {}}), '');
});
test('interpolate evaluates code only once.', 2, function() {
test('interpolate evaluates code only once.', 2, function(assert) {
var count = 0;
var template = _.template('<%= f() %>');
template({f: function(){ ok(!count++); }});
template({f: function(){ assert.ok(!count++); }});
var countEscaped = 0;
var templateEscaped = _.template('<%- f() %>');
templateEscaped({f: function(){ ok(!countEscaped++); }});
templateEscaped({f: function(){ assert.ok(!countEscaped++); }});
});
test('#746 - _.template settings are not modified.', 1, function() {
test('#746 - _.template settings are not modified.', 1, function(assert) {
var settings = {};
_.template('', null, settings);
deepEqual(settings, {});
assert.deepEqual(settings, {});
});
test('#779 - delimeters are applied to unescaped text.', 1, function() {
test('#779 - delimeters are applied to unescaped text.', 1, function(assert) {
var template = _.template('<<\nx\n>>', null, {evaluate: /<<(.*?)>>/g});
strictEqual(template(), '<<\nx\n>>');
assert.strictEqual(template(), '<<\nx\n>>');
});
}());

View File

@@ -11,8 +11,8 @@
// Establish the root object, `window` (`self`) in the browser, `global`
// on the server, or `this` in some virtual machines. We use `self`
// instead of `window` for `WebWorker` support.
var root = typeof self === 'object' && self.self === self && self ||
typeof global === 'object' && global.global === global && global ||
var root = typeof self == 'object' && self.self === self && self ||
typeof global == 'object' && global.global === global && global ||
this;
// Save the previous value of the `_` variable.
@@ -48,8 +48,10 @@
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for their old module API. If we're in
// the browser, add `_` as a global object.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
// (`nodeType` is checked to ensure that `module`
// and `exports` are not HTML elements.)
if (typeof exports != 'undefined' && !exports.nodeType) {
if (typeof module != 'undefined' && !module.nodeType && module.exports) {
exports = module.exports = _;
}
exports._ = _;
@@ -83,15 +85,17 @@
};
};
// A mostly-internal function to generate callbacks that can be applied
// to each element in a collection, returning the desired result — either
// identity, an arbitrary callback, a property matcher, or a property accessor.
// An internal function to generate callbacks that can be applied to each
// element in a collection, returning the desired result — either `identity`,
// an arbitrary callback, a property matcher, or a property accessor.
var cb = function(value, context, argCount) {
if (value == null) return _.identity;
if (_.isFunction(value)) return optimizeCb(value, context, argCount);
if (_.isObject(value)) return _.matcher(value);
return _.property(value);
};
// An external wrapper for the internal callback generator
_.iteratee = function(value, context) {
return cb(value, context, Infinity);
};
@@ -137,7 +141,7 @@
};
// Helper for collection methods to determine whether a collection
// should be iterated as an array or as an object
// should be iterated as an array or as an object.
// Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
// Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
@@ -184,8 +188,8 @@
// Create a reducing function iterating left or right.
var createReduce = function(dir) {
// Optimized iterator function as using arguments.length
// in the main function will deoptimize the, see #1991.
// Wrap code that reassigns argument variables in a separate function than
// the one that accesses `arguments.length` to avoid a perf hit. (#1191)
var reducer = function(obj, iteratee, memo, initial) {
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
@@ -309,7 +313,7 @@
obj = isArrayLike(obj) ? obj : _.values(obj);
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
if (value > result) {
if (value != null && value > result) {
result = value;
}
}
@@ -334,7 +338,7 @@
obj = isArrayLike(obj) ? obj : _.values(obj);
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
if (value < result) {
if (value != null && value < result) {
result = value;
}
}
@@ -431,10 +435,15 @@
if (_.has(result, key)) result[key]++; else result[key] = 1;
});
var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;
// Safely create a real, live array from anything iterable.
_.toArray = function(obj) {
if (!obj) return [];
if (_.isArray(obj)) return slice.call(obj);
if (_.isString(obj)) {
// Keep surrogate pair characters together
return obj.match(reStrSymbol);
}
if (isArrayLike(obj)) return _.map(obj, _.identity);
return _.values(obj);
};
@@ -497,7 +506,7 @@
for (var i = 0, length = getLength(input); i < length; i++) {
var value = input[i];
if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
//flatten current level of array or arguments object
// Flatten current level of array or arguments object
if (shallow) {
var j = 0, len = value.length;
while (j < len) output[idx++] = value[j++];
@@ -685,7 +694,9 @@
stop = start || 0;
start = 0;
}
step = step || 1;
if (!step) {
step = stop < start ? -1 : 1;
}
var length = Math.max(Math.ceil((stop - start) / step), 0);
var range = Array(length);
@@ -782,7 +793,7 @@
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = restArgs(function(func, wait, args) {
return setTimeout(function(){
return setTimeout(function() {
return func.apply(null, args);
}, wait);
});
@@ -797,17 +808,18 @@
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
_.throttle = function(func, wait, options) {
var context, args, result;
var timeout = null;
var timeout, context, args, result;
var previous = 0;
if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : _.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function() {
var throttled = function() {
var now = _.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
@@ -826,6 +838,14 @@
}
return result;
};
throttled.cancel = function() {
clearTimeout(timeout);
previous = 0;
timeout = context = args = null;
};
return throttled;
};
// Returns a function, that, as long as it continues to be invoked, will not
@@ -833,35 +853,32 @@
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
var timeout, args, context, timestamp, result;
var timeout, result;
var later = function() {
var last = _.now() - timestamp;
if (last < wait && last >= 0) {
timeout = setTimeout(later, wait - last);
} else {
var later = function(context, args) {
timeout = null;
if (!immediate) {
result = func.apply(context, args);
if (!timeout) context = args = null;
}
}
if (args) result = func.apply(context, args);
};
return function() {
context = this;
args = arguments;
timestamp = _.now();
var debounced = restArgs(function(args) {
var callNow = immediate && !timeout;
if (!timeout) timeout = setTimeout(later, wait);
if (timeout) clearTimeout(timeout);
if (callNow) {
result = func.apply(context, args);
context = args = null;
timeout = setTimeout(later, wait);
result = func.apply(this, args);
} else if (!immediate) {
timeout = _.delay(later, wait, this, args);
}
return result;
});
debounced.cancel = function() {
clearTimeout(timeout);
timeout = null;
};
return debounced;
};
// Returns the first function passed as an argument to the second,
@@ -1148,7 +1165,7 @@
if (a !== a) return b !== b;
// Exhaust primitive checks
var type = typeof a;
if (type !== 'function' && type !== 'object' && typeof b !== 'object') return false;
if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;
return deepEq(a, b, aStack, bStack);
};
@@ -1598,7 +1615,7 @@
// popular enough to be bundled in a third party lib, but not be part of
// an AMD load request. Those cases could generate an error when an
// anonymous define() is called outside of a loader request.
if (typeof define === 'function' && define.amd) {
if (typeof define == 'function' && define.amd) {
define('underscore', [], function() {
return _;
});