Files
lodash/test/test.js

17989 lines
522 KiB
JavaScript

;(function() {
/** Used as a safe reference for `undefined` in pre-ES5 environments. */
var undefined;
/** Used to detect when a function becomes hot. */
var HOT_COUNT = 150;
/** Used as the size to cover large array optimizations. */
var LARGE_ARRAY_SIZE = 200;
/** Used as the `TypeError` message for "Functions" methods. */
var FUNC_ERROR_TEXT = 'Expected a function';
/** Used as references for the max length and index of an array. */
var MAX_ARRAY_LENGTH = Math.pow(2, 32) - 1,
MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1;
/** Used as the maximum length an array-like object. */
var MAX_SAFE_INTEGER = Math.pow(2, 53) - 1;
/** `Object#toString` result references. */
var funcTag = '[object Function]',
numberTag = '[object Number]',
objectTag = '[object Object]';
/** Used as a reference to the global object. */
var root = (typeof global == 'object' && global) || this;
/** Used to store lodash to test for bad extensions/shims. */
var lodashBizarro = root.lodashBizarro;
/** Used for native method references. */
var arrayProto = Array.prototype,
errorProto = Error.prototype,
funcProto = Function.prototype,
objectProto = Object.prototype,
numberProto = Number.prototype,
stringProto = String.prototype;
/** Method and object shortcuts. */
var phantom = root.phantom,
amd = root.define && define.amd,
argv = root.process && process.argv,
ArrayBuffer = root.ArrayBuffer,
document = !phantom && root.document,
body = root.document && root.document.body,
create = Object.create,
fnToString = funcProto.toString,
freeze = Object.freeze,
hasOwnProperty = objectProto.hasOwnProperty,
JSON = root.JSON,
objToString = objectProto.toString,
noop = function() {},
params = root.arguments,
push = arrayProto.push,
slice = arrayProto.slice,
Symbol = root.Symbol,
system = root.system,
Uint8Array = root.Uint8Array,
WeakMap = root.WeakMap;
/** Math helpers. */
var add = function(x, y) { return x + y; },
square = function(n) { return n * n; };
/** Used to set property descriptors. */
var defineProperty = (function() {
try {
var o = {},
func = Object.defineProperty,
result = func(o, o, o) && func;
} catch(e) {}
return result;
}());
/** The file path of the lodash file to test. */
var filePath = (function() {
var min = 0,
result = [];
if (phantom) {
result = params = phantom.args || require('system').args;
} else if (system) {
min = 1;
result = params = system.args;
} else if (argv) {
min = 2;
result = params = argv;
} else if (params) {
result = params;
}
var last = result[result.length - 1];
result = (result.length > min && !/test(?:\.js)?$/.test(last)) ? last : '../lodash.src.js';
if (!amd) {
try {
result = require('fs').realpathSync(result);
} catch(e) {}
try {
result = require.resolve(result);
} catch(e) {}
}
return result;
}());
/** The `ui` object. */
var ui = root.ui || (root.ui = {
'buildPath': filePath,
'loaderPath': '',
'isModularize': /\b(?:amd|commonjs|es6?|node|npm|(index|main)\.js)\b/.test(filePath),
'isStrict': /\bes6?\b/.test(filePath),
'urlParams': {}
});
/** The basename of the lodash file to test. */
var basename = /[\w.-]+$/.exec(filePath)[0];
/** Detect if in a Java environment. */
var isJava = !document && !!root.java;
/** Used to indicate testing a modularized build. */
var isModularize = ui.isModularize;
/** Detect if testing `npm` modules. */
var isNpm = isModularize && /\bnpm\b/.test([ui.buildPath, ui.urlParams.build]);
/** Detect if running in PhantomJS. */
var isPhantom = phantom || typeof callPhantom == 'function';
/** Detect if running in Rhino. */
var isRhino = isJava && typeof global == 'function' && global().Array === root.Array;
/** Detect if lodash is in strict mode. */
var isStrict = ui.isStrict;
/** Used to test Web Workers. */
var Worker = !(ui.isForeign || ui.isSauceLabs || isModularize) && (document && document.origin != 'null') && root.Worker;
/** Used to test host objects in IE. */
try {
var xml = new ActiveXObject('Microsoft.XMLDOM');
} catch(e) {}
/** Use a single "load" function. */
var load = (typeof require == 'function' && !amd)
? require
: (isJava ? root.load : noop);
/** The unit testing framework. */
var QUnit = root.QUnit || (root.QUnit = (
QUnit = load('../node_modules/qunitjs/qunit/qunit.js') || root.QUnit,
QUnit = QUnit.QUnit || QUnit
));
/** Load QUnit Extras and ES6 Set/WeakMap shims. */
(function() {
var paths = [
'./asset/set.js',
'./asset/weakmap.js',
'../node_modules/qunit-extras/qunit-extras.js'
];
var index = -1,
length = paths.length;
while (++index < length) {
var object = load(paths[index]);
if (object) {
object.runInContext(root);
}
}
}());
/*--------------------------------------------------------------------------*/
// Log params provided to `test.js`.
if (params) {
console.log('test.js invoked with arguments: ' + JSON.stringify(slice.call(params)));
}
// Exit early if going to run tests in a PhantomJS web page.
if (phantom && isModularize) {
var page = require('webpage').create();
page.open(filePath, function(status) {
if (status != 'success') {
console.log('PhantomJS failed to load page: ' + filePath);
phantom.exit(1);
}
});
page.onCallback = function(details) {
var coverage = details.coverage;
if (coverage) {
var fs = require('fs'),
cwd = fs.workingDirectory,
sep = fs.separator;
fs.write([cwd, 'coverage', 'coverage.json'].join(sep), JSON.stringify(coverage));
}
phantom.exit(details.failed ? 1 : 0);
};
page.onConsoleMessage = function(message) {
console.log(message);
};
page.onInitialized = function() {
page.evaluate(function() {
document.addEventListener('DOMContentLoaded', function() {
QUnit.done(function(details) {
details.coverage = window.__coverage__;
callPhantom(details);
});
});
});
};
return;
}
/*--------------------------------------------------------------------------*/
/** The `lodash` function to test. */
var _ = root._ || (root._ = (
_ = load(filePath) || root._,
_ = _._ || (isStrict = ui.isStrict = isStrict || 'default' in _, _['default']) || _,
(_.runInContext ? _.runInContext(root) : _)
));
/** List of latin-1 supplementary letters to basic latin letters. */
var burredLetters = [
'\xc0', '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6', '\xc7', '\xc8', '\xc9', '\xca', '\xcb', '\xcc', '\xcd', '\xce',
'\xcf', '\xd0', '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xd8', '\xd9', '\xda', '\xdb', '\xdc', '\xdd', '\xde',
'\xdf', '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7', '\xe8', '\xe9', '\xea', '\xeb', '\xec', '\xed', '\xee',
'\xef', '\xf0', '\xf1', '\xf2', '\xf3', '\xf4', '\xf5', '\xf6', '\xf8', '\xf9', '\xfa', '\xfb', '\xfc', '\xfd', '\xfe', '\xff'
];
/** List of combining diacritical marks for spanning multiple characters. */
var comboHalfs = [
'\ufe20', '\ufe21', '\ufe22', '\ufe23'
];
/** List of common combining diacritical marks. */
var comboMarks = [
'\u0300', '\u0301', '\u0302', '\u0303', '\u0304', '\u0305', '\u0306', '\u0307', '\u0308', '\u0309', '\u030a', '\u030b', '\u030c', '\u030d', '\u030e', '\u030f',
'\u0310', '\u0311', '\u0312', '\u0313', '\u0314', '\u0315', '\u0316', '\u0317', '\u0318', '\u0319', '\u031a', '\u031b', '\u031c', '\u031d', '\u031e', '\u031f',
'\u0320', '\u0321', '\u0322', '\u0323', '\u0324', '\u0325', '\u0326', '\u0327', '\u0328', '\u0329', '\u032a', '\u032b', '\u032c', '\u032d', '\u032e', '\u032f',
'\u0330', '\u0331', '\u0332', '\u0333', '\u0334', '\u0335', '\u0336', '\u0337', '\u0338', '\u0339', '\u033a', '\u033b', '\u033c', '\u033d', '\u033e', '\u033f',
'\u0340', '\u0341', '\u0342', '\u0343', '\u0344', '\u0345', '\u0346', '\u0347', '\u0348', '\u0349', '\u034a', '\u034b', '\u034c', '\u034d', '\u034e', '\u034f',
'\u0350', '\u0351', '\u0352', '\u0353', '\u0354', '\u0355', '\u0356', '\u0357', '\u0358', '\u0359', '\u035a', '\u035b', '\u035c', '\u035d', '\u035e', '\u035f',
'\u0360', '\u0361', '\u0362', '\u0363', '\u0364', '\u0365', '\u0366', '\u0367', '\u0368', '\u0369', '\u036a', '\u036b', '\u036c', '\u036d', '\u036e', '\u036f'
];
/** List of `burredLetters` translated to basic latin letters. */
var deburredLetters = [
'A', 'A', 'A', 'A', 'A', 'A', 'Ae', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I',
'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'Y', 'Th',
'ss', 'a', 'a', 'a', 'a', 'a', 'a', 'ae', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i',
'i', 'd', 'n', 'o', 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 'u', 'y', 'th', 'y'
];
/** Used to provide falsey values to methods. */
var falsey = [, '', 0, false, NaN, null, undefined];
/** Used to provide empty values to methods. */
var empties = [[], {}].concat(falsey.slice(1));
/** Used to test error objects. */
var errors = [
new Error,
new EvalError,
new RangeError,
new ReferenceError,
new SyntaxError,
new TypeError,
new URIError
];
/** Used to check problem JScript properties (a.k.a. the `[[DontEnum]]` bug). */
var shadowProps = [
'constructor',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'toLocaleString',
'toString',
'valueOf'
];
/** Used to check problem JScript properties too. */
var shadowObject = _.invert(shadowProps);
/** Used to check whether methods support typed arrays. */
var typedArrays = [
'Float32Array',
'Float64Array',
'Int8Array',
'Int16Array',
'Int32Array',
'Uint8Array',
'Uint8ClampedArray',
'Uint16Array',
'Uint32Array'
];
/**
* Used to check for problems removing whitespace. For a whitespace reference,
* see [V8's unit test](https://code.google.com/p/v8/source/browse/branches/bleeding_edge/test/mjsunit/whitespaces.js).
*/
var whitespace = ' \t\x0b\f\xa0\ufeff\n\r\u2028\u2029\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000';
/**
* Extracts the unwrapped value from its wrapper.
*
* @private
* @param {Object} wrapper The wrapper to unwrap.
* @returns {*} Returns the unwrapped value.
*/
function getUnwrappedValue(wrapper) {
var index = -1,
actions = wrapper.__actions__,
length = actions.length,
result = wrapper.__wrapped__;
while (++index < length) {
var args = [result],
action = actions[index];
push.apply(args, action.args);
result = action.func.apply(action.thisArg, args);
}
return result;
}
/**
* Removes all own enumerable properties from a given object.
*
* @private
* @param {Object} object The object to empty.
*/
function emptyObject(object) {
_.forOwn(object, function(value, key, object) {
delete object[key];
});
}
/**
* Sets a non-enumerable property value on `object`.
*
* Note: This function is used to avoid a bug in older versions of V8 where
* overwriting non-enumerable built-ins makes them enumerable.
* See https://code.google.com/p/v8/issues/detail?id=1623
*
* @private
* @param {Object} object The object augment.
* @param {string} key The name of the property to set.
* @param {*} value The property value.
*/
function setProperty(object, key, value) {
try {
defineProperty(object, key, {
'configurable': true,
'enumerable': false,
'writable': true,
'value': value
});
} catch(e) {
object[key] = value;
}
}
/**
* Skips a given number of tests with a passing result.
*
* @private
* @param {number} [count=1] The number of tests to skip.
*/
function skipTest(count) {
count || (count = 1);
while (count--) {
ok(true, 'test skipped');
}
}
/*--------------------------------------------------------------------------*/
// Setup values for Node.js.
(function() {
if (amd) {
return;
}
try {
// Add values from a different realm.
_.extend(_, require('vm').runInNewContext([
'(function() {',
' var object = {',
" '_arguments': (function() { return arguments; }(1, 2, 3)),",
" '_array': [1, 2, 3],",
" '_boolean': Object(false),",
" '_date': new Date,",
" '_errors': [new Error, new EvalError, new RangeError, new ReferenceError, new SyntaxError, new TypeError, new URIError],",
" '_function': function() {},",
" '_nan': NaN,",
" '_null': null,",
" '_number': Object(0),",
" '_object': { 'a': 1, 'b': 2, 'c': 3 },",
" '_regexp': /x/,",
" '_string': Object('a'),",
" '_undefined': undefined",
' };',
'',
" ['" + typedArrays.join("', '") + "'].forEach(function(type) {",
" var Ctor = Function('return typeof ' + type + \" != 'undefined' && \" + type)()",
' if (Ctor) {',
" object['_' + type.toLowerCase()] = new Ctor(new ArrayBuffer(24));",
' }',
" });",
'',
' return object;',
'}())'
].join('\n')));
}
catch(e) {
if (!phantom) {
return;
}
}
var nativeString = fnToString.call(toString),
reToString = /toString/g;
function createToString(funcName) {
return _.constant(nativeString.replace(reToString, funcName));
}
// Expose internal modules for better code coverage.
if (isModularize && !isNpm) {
_.each(['baseEach', 'isIndex', 'isIterateeCall', 'isLength'], function(funcName) {
var path = require('path'),
func = require(path.join(path.dirname(filePath), 'internal', funcName + '.js'));
_['_' + funcName] = func[funcName] || func['default'] || func;
});
}
// Allow bypassing native checks.
setProperty(funcProto, 'toString', function wrapper() {
setProperty(funcProto, 'toString', fnToString);
var result = _.has(this, 'toString') ? this.toString() : fnToString.call(this);
setProperty(funcProto, 'toString', wrapper);
return result;
});
// Add built-in prototype extensions.
funcProto._method = _.noop;
// Set bad shims.
var _isArray = Array.isArray;
setProperty(Array, 'isArray', _.noop);
var _now = Date.now;
setProperty(Date, 'now', _.noop);
var _getPrototypeOf = Object.getPrototypeOf;
setProperty(Object, 'getPrototypeOf', _.noop);
var _keys = Object.keys;
setProperty(Object, 'keys', _.noop);
var _propertyIsEnumerable = objectProto.propertyIsEnumerable;
setProperty(objectProto, 'propertyIsEnumerable', function(key) {
if (key == '1' && _.isArguments(this) && _.isEqual(_.values(this), [1, 0])) {
throw new Error;
}
return _propertyIsEnumerable.call(this, key);
});
var _isFinite = Number.isFinite;
setProperty(Number, 'isFinite', _.noop);
var _ArrayBuffer = ArrayBuffer;
setProperty(root, 'ArrayBuffer', (function() {
function ArrayBuffer(byteLength) {
var buffer = new _ArrayBuffer(byteLength);
if (!byteLength) {
setProperty(buffer, 'slice', buffer.slice ? null : bufferSlice);
}
return buffer;
}
function bufferSlice() {
var newBuffer = new _ArrayBuffer(this.byteLength),
view = new Uint8Array(newBuffer);
view.set(new Uint8Array(this));
return newBuffer;
}
setProperty(ArrayBuffer, 'toString', createToString('ArrayBuffer'));
setProperty(bufferSlice, 'toString', createToString('slice'));
return ArrayBuffer;
}()));
if (!root.Float64Array) {
setProperty(root, 'Float64Array', (function() {
function Float64Array(buffer, byteOffset, length) {
return arguments.length == 1
? new Uint8Array(buffer)
: new Uint8Array(buffer, byteOffset || 0, length || buffer.byteLength);
}
setProperty(Float64Array, 'BYTES_PER_ELEMENT', 8);
setProperty(Float64Array, 'toString', createToString('Float64Array'));
return Float64Array;
}()));
}
var _parseInt = parseInt;
setProperty(root, 'parseInt', (function() {
var checkStr = whitespace + '08',
isFaked = _parseInt(checkStr) != 8,
reHexPrefix = /^0[xX]/,
reTrim = RegExp('^[' + whitespace + ']+|[' + whitespace + ']+$');
return function(value, radix) {
if (value == checkStr && !isFaked) {
isFaked = true;
return 0;
}
value = String(value == null ? '' : value).replace(reTrim, '');
return _parseInt(value, +radix || (reHexPrefix.test(value) ? 16 : 10));
};
}()));
var _Set = root.Set;
setProperty(root, 'Set', _.noop);
var _WeakMap = root.WeakMap;
setProperty(root, 'WeakMap', _.noop);
// Fake the DOM.
setProperty(root, 'window', {});
setProperty(root.window, 'document', {});
setProperty(root.window.document, 'createDocumentFragment', function() {
return { 'nodeType': 11 };
});
// Fake `WinRTError`.
setProperty(root, 'WinRTError', Error);
// Clear cache so lodash can be reloaded.
emptyObject(require.cache);
// Load lodash and expose it to the bad extensions/shims.
lodashBizarro = (lodashBizarro = require(filePath))._ || lodashBizarro['default'] || lodashBizarro;
// Restore built-in methods.
setProperty(Array, 'isArray', _isArray);
setProperty(Date, 'now', _now);
setProperty(Object, 'getPrototypeOf', _getPrototypeOf);
setProperty(Object, 'keys', _keys);
setProperty(objectProto, 'propertyIsEnumerable', _propertyIsEnumerable);
setProperty(root, 'parseInt', _parseInt);
if (_isFinite) {
setProperty(Number, 'isFinite', _isFinite);
} else {
delete Number.isFinite;
}
if (_ArrayBuffer) {
setProperty(root, 'ArrayBuffer', _ArrayBuffer);
} else {
delete root.ArrayBuffer;
}
if (_Set) {
setProperty(root, 'Set', Set);
} else {
delete root.Set;
}
if (_WeakMap) {
setProperty(root, 'WeakMap', WeakMap);
} else {
delete root.WeakMap;
}
delete root.WinRTError;
delete root.window;
delete funcProto._method;
}());
// Add values from an iframe.
(function() {
if (_._object || !document) {
return;
}
var iframe = document.createElement('iframe');
iframe.frameBorder = iframe.height = iframe.width = 0;
body.appendChild(iframe);
var idoc = (idoc = iframe.contentDocument || iframe.contentWindow).document || idoc;
idoc.write([
'<script>',
'parent._._arguments = (function() { return arguments; }(1, 2, 3));',
'parent._._array = [1, 2, 3];',
'parent._._boolean = Object(false);',
'parent._._date = new Date;',
"parent._._element = document.createElement('div');",
'parent._._errors = [new Error, new EvalError, new RangeError, new ReferenceError, new SyntaxError, new TypeError, new URIError];',
'parent._._function = function() {};',
'parent._._nan = NaN;',
'parent._._null = null;',
'parent._._number = Object(0);',
"parent._._object = { 'a': 1, 'b': 2, 'c': 3 };",
'parent._._regexp = /x/;',
"parent._._string = Object('a');",
'parent._._undefined = undefined;',
'',
'var root = this;',
"parent._.each(['" + typedArrays.join("', '") + "'], function(type) {",
' var Ctor = root[type];',
' if (Ctor) {',
" parent._['_' + type.toLowerCase()] = new Ctor(new ArrayBuffer(24));",
' }',
'});',
'<\/script>'
].join('\n'));
idoc.close();
}());
// Add a web worker.
(function() {
if (!Worker) {
return;
}
var worker = new Worker('./asset/worker.js?t=' + (+new Date));
worker.addEventListener('message', function(e) {
_._VERSION = e.data || '';
}, false);
worker.postMessage(ui.buildPath);
}());
/*--------------------------------------------------------------------------*/
QUnit.module(basename);
(function() {
test('should support loading ' + basename + ' as the "lodash" module', 1, function() {
if (amd) {
strictEqual((lodashModule || {}).moduleName, 'lodash');
}
else {
skipTest();
}
});
test('should support loading ' + basename + ' with the Require.js "shim" configuration option', 1, function() {
if (amd && _.includes(ui.loaderPath, 'requirejs')) {
strictEqual((shimmedModule || {}).moduleName, 'shimmed');
} else {
skipTest();
}
});
test('should support loading ' + basename + ' as the "underscore" module', 1, function() {
if (amd) {
strictEqual((underscoreModule || {}).moduleName, 'underscore');
}
else {
skipTest();
}
});
asyncTest('should support loading ' + basename + ' in a web worker', 1, function() {
if (Worker) {
var limit = 30000 / QUnit.config.asyncRetries,
start = +new Date;
var attempt = function() {
var actual = _._VERSION;
if ((new Date - start) < limit && typeof actual != 'string') {
setTimeout(attempt, 16);
return;
}
strictEqual(actual, _.VERSION);
QUnit.start();
};
attempt();
}
else {
skipTest();
QUnit.start();
}
});
test('should not add `Function.prototype` extensions to lodash', 1, function() {
if (lodashBizarro) {
ok(!('_method' in lodashBizarro));
}
else {
skipTest();
}
});
test('should avoid overwritten native methods', 12, function() {
function Foo() {}
function message(lodashMethod, nativeMethod) {
return '`' + lodashMethod + '` should avoid overwritten native `' + nativeMethod + '`';
}
var object = { 'a': 1 },
otherObject = { 'b': 2 },
largeArray = _.times(LARGE_ARRAY_SIZE, _.constant(object));
if (lodashBizarro) {
try {
var actual = [lodashBizarro.isArray([]), lodashBizarro.isArray({ 'length': 0 })];
} catch(e) {
actual = null;
}
deepEqual(actual, [true, false], message('_.isArray', 'Array.isArray'));
try {
actual = lodashBizarro.now();
} catch(e) {
actual = null;
}
ok(typeof actual == 'number', message('_.now', 'Date.now'));
try {
actual = [lodashBizarro.isPlainObject({}), lodashBizarro.isPlainObject([])];
} catch(e) {
actual = null;
}
deepEqual(actual, [true, false], message('_.isPlainObject', 'Object.getPrototypeOf'));
try {
actual = [lodashBizarro.keys(object), lodashBizarro.keys()];
} catch(e) {
actual = null;
}
deepEqual(actual, [['a'], []], message('_.keys', 'Object.keys'));
try {
actual = [lodashBizarro.isFinite(1), lodashBizarro.isFinite(NaN)];
} catch(e) {
actual = null;
}
deepEqual(actual, [true, false], message('_.isFinite', 'Number.isFinite'));
try {
actual = [
lodashBizarro.difference([object, otherObject], largeArray),
lodashBizarro.intersection(largeArray, [object]),
lodashBizarro.uniq(largeArray)
];
} catch(e) {
actual = null;
}
deepEqual(actual, [[otherObject], [object], [object]], message('_.difference`, `_.intersection`, and `_.uniq', 'Set'));
try {
actual = _.map(['6', '08', '10'], lodashBizarro.parseInt);
} catch(e) {
actual = null;
}
deepEqual(actual, [6, 8, 10], '`_.parseInt` should work in its bizarro form');
if (ArrayBuffer) {
try {
var buffer = new ArrayBuffer(10);
actual = lodashBizarro.clone(buffer);
} catch(e) {
actual = null;
}
deepEqual(actual, buffer, message('_.clone', 'ArrayBuffer#slice'));
notStrictEqual(actual, buffer, message('_.clone', 'ArrayBuffer#slice'));
}
else {
skipTest(2);
}
if (ArrayBuffer && Uint8Array) {
try {
var array = new Uint8Array(new ArrayBuffer(10));
actual = lodashBizarro.cloneDeep(array);
} catch(e) {
actual = null;
}
deepEqual(actual, array, message('_.cloneDeep', 'Float64Array'));
notStrictEqual(actual && actual.buffer, array.buffer, message('_.cloneDeep', 'Float64Array'));
notStrictEqual(actual, array, message('_.cloneDeep', 'Float64Array'));
}
else {
skipTest(3);
}
}
else {
skipTest(12);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('isIndex');
(function() {
var func = _._isIndex;
test('should return `true` for indexes', 4, function() {
if (func) {
strictEqual(func(0), true);
strictEqual(func('1'), true);
strictEqual(func(3, 4), true);
strictEqual(func(MAX_SAFE_INTEGER - 1), true);
}
else {
skipTest(4);
}
});
test('should return `false` for non-indexes', 4, function() {
if (func) {
strictEqual(func(-1), false);
strictEqual(func(3, 3), false);
strictEqual(func(1.1), false);
strictEqual(func(MAX_SAFE_INTEGER), false);
}
else {
skipTest(4);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('isIterateeCall');
(function() {
var array = [1],
func = _._isIterateeCall,
object = { 'a': 1 };
test('should return `true` for iteratee calls', 3, function() {
function Foo() {}
Foo.prototype.a = 1;
if (func) {
strictEqual(func(1, 0, array), true);
strictEqual(func(1, 'a', object), true);
strictEqual(func(1, 'a', new Foo), true);
}
else {
skipTest(3);
}
});
test('should return `false` for non-iteratee calls', 4, function() {
if (func) {
strictEqual(func(2, 0, array), false);
strictEqual(func(1, 1.1, array), false);
strictEqual(func(1, 0, { 'length': MAX_SAFE_INTEGER + 1 }), false);
strictEqual(func(1, 'b', object), false);
}
else {
skipTest(4);
}
});
test('should work with `NaN` values', 2, function() {
if (func) {
strictEqual(func(NaN, 0, [NaN]), true);
strictEqual(func(NaN, 'a', { 'a': NaN }), true);
}
else {
skipTest(2);
}
});
test('should not error when `index` is an object without a `toString` method', 1, function() {
if (func) {
try {
var actual = func(1, { 'toString': null }, [1]);
} catch(e) {
var message = e.message;
}
strictEqual(actual, false, message || '');
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('isLength');
(function() {
var func = _._isLength;
test('should return `true` for lengths', 3, function() {
if (func) {
strictEqual(func(0), true);
strictEqual(func(3), true);
strictEqual(func(MAX_SAFE_INTEGER), true);
}
else {
skipTest(3);
}
});
test('should return `false` for non-lengths', 4, function() {
if (func) {
strictEqual(func(-1), false);
strictEqual(func('1'), false);
strictEqual(func(1.1), false);
strictEqual(func(MAX_SAFE_INTEGER + 1), false);
}
else {
skipTest(4);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash constructor');
(function() {
var values = empties.concat(true, 1, 'a'),
expected = _.map(values, _.constant(true));
test('creates a new instance when called without the `new` operator', 1, function() {
if (!isNpm) {
var actual = _.map(values, function(value) {
return _(value) instanceof _;
});
deepEqual(actual, expected);
}
else {
skipTest();
}
});
test('should return provided `lodash` instances', 1, function() {
if (!isNpm) {
var actual = _.map(values, function(value) {
var wrapped = _(value);
return _(wrapped) === wrapped;
});
deepEqual(actual, expected);
}
else {
skipTest();
}
});
test('should convert foreign wrapped values to `lodash` instances', 1, function() {
if (!isNpm && lodashBizarro) {
var actual = _.map(values, function(value) {
var wrapped = _(lodashBizarro(value)),
unwrapped = wrapped.value();
return wrapped instanceof _ &&
(unwrapped === value || (_.isNaN(unwrapped) && _.isNaN(value)));
});
deepEqual(actual, expected);
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.add');
(function() {
test('should add two numbers together', 1, function() {
strictEqual(_.add(6, 4), 10);
});
test('should coerce params to numbers', 3, function() {
strictEqual(_.add('6', '4'), 10);
strictEqual(_.add('6', 'y'), 6);
strictEqual(_.add('x', 'y'), 0);
});
test('should return an unwrapped value when implicitly chaining', 1, function() {
if (!isNpm) {
strictEqual(_(1).add(2), 3);
}
else {
skipTest();
}
});
test('should return a wrapped value when explicitly chaining', 1, function() {
if (!isNpm) {
ok(_(1).chain().add(2) instanceof _);
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.after');
(function() {
function after(n, times) {
var count = 0;
_.times(times, _.after(n, function() { count++; }));
return count;
}
test('should create a function that invokes `func` after `n` calls', 4, function() {
strictEqual(after(5, 5), 1, 'after(n) should invoke `func` after being called `n` times');
strictEqual(after(5, 4), 0, 'after(n) should not invoke `func` before being called `n` times');
strictEqual(after(0, 0), 0, 'after(0) should not invoke `func` immediately');
strictEqual(after(0, 1), 1, 'after(0) should invoke `func` when called once');
});
test('should coerce non-finite `n` values to `0`', 1, function() {
var values = [-Infinity, NaN, Infinity],
expected = _.map(values, _.constant(1));
var actual = _.map(values, function(n) {
return after(n, 1);
});
deepEqual(actual, expected);
});
test('should allow `func` as the first argument', 1, function() {
var count = 0;
try {
var after = _.after(function() { count++; }, 1);
after();
after();
} catch(e) {}
strictEqual(count, 2);
});
test('should not set a `this` binding', 2, function() {
var after = _.after(1, function() { return ++this.count; }),
object = { 'count': 0, 'after': after };
object.after();
strictEqual(object.after(), 2);
strictEqual(object.count, 2);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.ary');
(function() {
function fn(a, b, c) {
return slice.call(arguments);
}
test('should cap the number of params provided to `func`', 2, function() {
var actual = _.map(['6', '8', '10'], _.ary(parseInt, 1));
deepEqual(actual, [6, 8, 10]);
var capped = _.ary(fn, 2);
deepEqual(capped('a', 'b', 'c', 'd'), ['a', 'b']);
});
test('should use `func.length` if `n` is not provided', 1, function() {
var capped = _.ary(fn);
deepEqual(capped('a', 'b', 'c', 'd'), ['a', 'b', 'c']);
});
test('should treat a negative `n` as `0`', 1, function() {
var capped = _.ary(fn, -1);
try {
var actual = capped('a');
} catch(e) {}
deepEqual(actual, []);
});
test('should work when provided less than the capped numer of arguments', 1, function() {
var capped = _.ary(fn, 3);
deepEqual(capped('a'), ['a']);
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var funcs = _.map([fn], _.ary),
actual = funcs[0]('a', 'b', 'c');
deepEqual(actual, ['a', 'b', 'c']);
});
test('should work when combined with other methods that use metadata', 2, function() {
var array = ['a', 'b', 'c'],
includes = _.curry(_.rearg(_.ary(_.includes, 2), 1, 0), 2);
strictEqual(includes('b')(array, 2), true);
if (!isNpm) {
includes = _(_.includes).ary(2).rearg(1, 0).curry(2).value();
strictEqual(includes('b')(array, 2), true);
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.assign');
(function() {
test('should assign properties of a source object to the destination object', 1, function() {
deepEqual(_.assign({ 'a': 1 }, { 'b': 2 }), { 'a': 1, 'b': 2 });
});
test('should accept multiple source objects', 2, function() {
var expected = { 'a': 1, 'b': 2, 'c': 3 };
deepEqual(_.assign({ 'a': 1 }, { 'b': 2 }, { 'c': 3 }), expected);
deepEqual(_.assign({ 'a': 1 }, { 'b': 2, 'c': 2 }, { 'c': 3 }), expected);
});
test('should overwrite destination properties', 1, function() {
var expected = { 'a': 3, 'b': 2, 'c': 1 };
deepEqual(_.assign({ 'a': 1, 'b': 2 }, expected), expected);
});
test('should assign source properties with nullish values', 1, function() {
var expected = { 'a': null, 'b': undefined, 'c': null };
deepEqual(_.assign({ 'a': 1, 'b': 2 }, expected), expected);
});
test('should work with a `customizer` callback', 1, function() {
var actual = _.assign({ 'a': 1, 'b': 2 }, { 'a': 3, 'c': 3 }, function(a, b) {
return typeof a == 'undefined' ? b : a;
});
deepEqual(actual, { 'a': 1, 'b': 2, 'c': 3 });
});
test('should work with a `customizer` that returns `undefined`', 1, function() {
var expected = { 'a': undefined };
deepEqual(_.assign({}, expected, _.identity), expected);
});
test('should be aliased', 1, function() {
strictEqual(_.extend, _.assign);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.at');
(function() {
var args = arguments,
array = ['a', 'b', 'c'];
array['1.1'] = array['-1'] = 1;
test('should return the elements corresponding to the specified keys', 1, function() {
var actual = _.at(array, [0, 2]);
deepEqual(actual, ['a', 'c']);
});
test('should return `undefined` for nonexistent keys', 1, function() {
var actual = _.at(array, [2, 4, 0]);
deepEqual(actual, ['c', undefined, 'a']);
});
test('should use `undefined` for non-index keys on array-like values', 1, function() {
var values = _.reject(empties, function(value) {
return value === 0 || _.isArray(value);
}).concat(-1, 1.1);
var expected = _.map(values, _.constant(undefined)),
actual = _.at(array, values);
deepEqual(actual, expected);
});
test('should return an empty array when no keys are provided', 2, function() {
deepEqual(_.at(array), []);
deepEqual(_.at(array, [], []), []);
});
test('should accept multiple key arguments', 1, function() {
var actual = _.at(['a', 'b', 'c', 'd'], 3, 0, 2);
deepEqual(actual, ['d', 'a', 'c']);
});
test('should work with a falsey `collection` argument when keys are provided', 1, function() {
var expected = _.map(falsey, _.constant(Array(4)));
var actual = _.map(falsey, function(value) {
try {
return _.at(value, 0, 1, 'pop', 'push');
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should work with an `arguments` object for `collection`', 1, function() {
var actual = _.at(args, [2, 0]);
deepEqual(actual, [3, 1]);
});
test('should work with `arguments` object as secondary arguments', 1, function() {
var actual = _.at([1, 2, 3, 4, 5], args);
deepEqual(actual, [2, 3, 4]);
});
test('should work with an object for `collection`', 1, function() {
var actual = _.at({ 'a': 1, 'b': 2, 'c': 3 }, ['c', 'a']);
deepEqual(actual, [3, 1]);
});
test('should pluck inherited property values', 1, function() {
function Foo() { this.a = 1; }
Foo.prototype.b = 2;
var actual = _.at(new Foo, 'b');
deepEqual(actual, [2]);
});
_.each({
'literal': 'abc',
'object': Object('abc')
},
function(collection, key) {
test('should work with a string ' + key + ' for `collection`', 1, function() {
deepEqual(_.at(collection, [2, 0]), ['c', 'a']);
});
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.attempt');
(function() {
test('should return the result of `func`', 1, function() {
strictEqual(_.attempt(_.constant('x')), 'x');
});
test('should provide additional arguments to `func`', 1, function() {
var actual = _.attempt(function() { return slice.call(arguments); }, 1, 2);
deepEqual(actual, [1, 2]);
});
test('should return the caught error', 1, function() {
var expected = _.map(errors, _.constant(true));
var actual = _.map(errors, function(error) {
return _.attempt(function() { throw error; }) === error;
});
deepEqual(actual, expected);
});
test('should coerce errors to error objects', 1, function() {
var actual = _.attempt(function() { throw 'x'; });
ok(_.isEqual(actual, Error('x')));
});
test('should work with an error object from another realm', 1, function() {
if (_._object) {
var expected = _.map(_._errors, _.constant(true));
var actual = _.map(_._errors, function(error) {
return _.attempt(function() { throw error; }) === error;
});
deepEqual(actual, expected);
}
else {
skipTest();
}
});
test('should return an unwrapped value when implicitly chaining', 1, function() {
if (!isNpm) {
strictEqual(_(_.constant('x')).attempt(), 'x');
}
else {
skipTest();
}
});
test('should return a wrapped value when explicitly chaining', 1, function() {
if (!isNpm) {
ok(_(_.constant('x')).chain().attempt() instanceof _);
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.before');
(function() {
function before(n, times) {
var count = 0;
_.times(times, _.before(n, function() { count++; }));
return count;
}
test('should create a function that invokes `func` after `n` calls', 4, function() {
strictEqual(before(5, 4), 4, 'before(n) should invoke `func` before being called `n` times');
strictEqual(before(5, 6), 4, 'before(n) should not invoke `func` after being called `n - 1` times');
strictEqual(before(0, 0), 0, 'before(0) should not invoke `func` immediately');
strictEqual(before(0, 1), 0, 'before(0) should not invoke `func` when called');
});
test('should coerce non-finite `n` values to `0`', 1, function() {
var values = [-Infinity, NaN, Infinity],
expected = _.map(values, _.constant(0));
var actual = _.map(values, function(n) {
return before(n);
});
deepEqual(actual, expected);
});
test('should allow `func` as the first argument', 1, function() {
var count = 0;
try {
var before = _.before(function() { count++; }, 2);
before();
before();
} catch(e) {}
strictEqual(count, 1);
});
test('should not set a `this` binding', 2, function() {
var before = _.before(2, function() { return ++this.count; }),
object = { 'count': 0, 'before': before };
object.before();
strictEqual(object.before(), 1);
strictEqual(object.count, 1);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.bind');
(function() {
function fn() {
var result = [this];
push.apply(result, arguments);
return result;
}
test('should bind a function to an object', 1, function() {
var object = {},
bound = _.bind(fn, object);
deepEqual(bound('a'), [object, 'a']);
});
test('should accept a falsey `thisArg` argument', 1, function() {
var values = _.reject(falsey.slice(1), function(value) { return value == null; }),
expected = _.map(values, function(value) { return [value]; });
var actual = _.map(values, function(value) {
try {
var bound = _.bind(fn, value);
return bound();
} catch(e) {}
});
ok(_.every(actual, function(value, index) {
return _.isEqual(value, expected[index]);
}));
});
test('should bind a function to nullish values', 6, function() {
var bound = _.bind(fn, null),
actual = bound('a');
ok(actual[0] === null || actual[0] && actual[0].Array);
strictEqual(actual[1], 'a');
_.times(2, function(index) {
bound = index ? _.bind(fn, undefined) : _.bind(fn);
actual = bound('b');
ok(actual[0] === undefined || actual[0] && actual[0].Array);
strictEqual(actual[1], 'b');
});
});
test('should partially apply arguments ', 4, function() {
var object = {},
bound = _.bind(fn, object, 'a');
deepEqual(bound(), [object, 'a']);
bound = _.bind(fn, object, 'a');
deepEqual(bound('b'), [object, 'a', 'b']);
bound = _.bind(fn, object, 'a', 'b');
deepEqual(bound(), [object, 'a', 'b']);
deepEqual(bound('c', 'd'), [object, 'a', 'b', 'c', 'd']);
});
test('should support placeholders', 4, function() {
var object = {},
ph = _.bind.placeholder,
bound = _.bind(fn, object, ph, 'b', ph);
deepEqual(bound('a', 'c'), [object, 'a', 'b', 'c']);
deepEqual(bound('a'), [object, 'a', 'b', undefined]);
deepEqual(bound('a', 'c', 'd'), [object, 'a', 'b', 'c', 'd']);
deepEqual(bound(), [object, undefined, 'b', undefined]);
});
test('should create a function with a `length` of `0`', 2, function() {
var fn = function(a, b, c) {},
bound = _.bind(fn, {});
strictEqual(bound.length, 0);
bound = _.bind(fn, {}, 1);
strictEqual(bound.length, 0);
});
test('should ignore binding when called with the `new` operator', 3, function() {
function Foo() {
return this;
}
var bound = _.bind(Foo, { 'a': 1 }),
newBound = new bound;
strictEqual(newBound.a, undefined);
strictEqual(bound().a, 1);
ok(newBound instanceof Foo);
});
test('ensure `new bound` is an instance of `func`', 2, function() {
function Foo(value) {
return value && object;
}
var bound = _.bind(Foo),
object = {};
ok(new bound instanceof Foo);
strictEqual(new bound(true), object);
});
test('should append array arguments to partially applied arguments (test in IE < 9)', 1, function() {
var object = {},
bound = _.bind(fn, object, 'a');
deepEqual(bound(['b'], 'c'), [object, 'a', ['b'], 'c']);
});
test('should not rebind functions', 3, function() {
var object1 = {},
object2 = {},
object3 = {};
var bound1 = _.bind(fn, object1),
bound2 = _.bind(bound1, object2, 'a'),
bound3 = _.bind(bound1, object3, 'b');
deepEqual(bound1(), [object1]);
deepEqual(bound2(), [object1, 'a']);
deepEqual(bound3(), [object1, 'b']);
});
test('should not error when calling bound class constructors', 1, function() {
var createCtor = _.attempt(Function, '"use strict";return class A{}');
if (typeof createCtor == 'function') {
var bound = _.bind(createCtor()),
expected = _.times(6, _.constant(true));
var actual = _.times(6, function(index) {
try {
switch (index) {
case 0: return !!(new bound);
case 1: return !!(new bound(1));
case 2: return !!(new bound(1, 2));
case 3: return !!(new bound(1, 2, 3));
case 4: return !!(new bound(1, 2, 3, 4));
case 5: return !!(new bound(1, 2, 3, 4, 5));
}
} catch(e) {}
});
deepEqual(actual, expected);
}
else {
skipTest();
}
});
test('should return a wrapped value when chaining', 2, function() {
if (!isNpm) {
var object = {},
bound = _(fn).bind({}, 'a', 'b');
ok(bound instanceof _);
var actual = bound.value()('c');
deepEqual(actual, [object, 'a', 'b', 'c']);
}
else {
skipTest(2);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.bindAll');
(function() {
var args = arguments;
test('should bind all methods of `object`', 1, function() {
function Foo() {
this._a = 1;
this._b = 2;
this.a = function() { return this._a; };
}
Foo.prototype.b = function() { return this._b; };
var object = new Foo;
_.bindAll(object);
var actual = _.map(_.functions(object).sort(), function(methodName) {
return object[methodName].call({});
});
deepEqual(actual, [1, 2]);
});
test('should accept individual method names', 1, function() {
var object = {
'_a': 1,
'_b': 2,
'_c': 3,
'a': function() { return this._a; },
'b': function() { return this._b; },
'c': function() { return this._c; }
};
_.bindAll(object, 'a', 'b');
var actual = _.map(_.functions(object).sort(), function(methodName) {
return object[methodName].call({});
});
deepEqual(actual, [1, 2, undefined]);
});
test('should accept arrays of method names', 1, function() {
var object = {
'_a': 1,
'_b': 2,
'_c': 3,
'_d': 4,
'a': function() { return this._a; },
'b': function() { return this._b; },
'c': function() { return this._c; },
'd': function() { return this._d; }
};
_.bindAll(object, ['a', 'b'], ['c']);
var actual = _.map(_.functions(object).sort(), function(methodName) {
return object[methodName].call({});
});
deepEqual(actual, [1, 2, 3, undefined]);
});
test('should work with an array `object` argument', 1, function() {
var array = ['push', 'pop'];
_.bindAll(array);
strictEqual(array.pop, arrayProto.pop);
});
test('should work with `arguments` objects as secondary arguments', 1, function() {
var object = {
'_a': 1,
'a': function() { return this._a; }
};
_.bindAll(object, args);
var actual = _.map(_.functions(object).sort(), function(methodName) {
return object[methodName].call({});
});
deepEqual(actual, [1]);
});
}('a'));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.bindKey');
(function() {
test('should work when the target function is overwritten', 2, function() {
var object = {
'user': 'fred',
'greet': function(greeting) {
return this.user + ' says: ' + greeting;
}
};
var bound = _.bindKey(object, 'greet', 'hi');
strictEqual(bound(), 'fred says: hi');
object.greet = function(greeting) {
return this.user + ' says: ' + greeting + '!';
};
strictEqual(bound(), 'fred says: hi!');
});
test('should support placeholders', 4, function() {
var object = {
'fn': function() {
return slice.call(arguments);
}
};
var ph = _.bindKey.placeholder,
bound = _.bindKey(object, 'fn', ph, 'b', ph);
deepEqual(bound('a', 'c'), ['a', 'b', 'c']);
deepEqual(bound('a'), ['a', 'b', undefined]);
deepEqual(bound('a', 'c', 'd'), ['a', 'b', 'c', 'd']);
deepEqual(bound(), [undefined, 'b', undefined]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('case methods');
_.each(['camel', 'kebab', 'snake', 'start'], function(caseName) {
var methodName = caseName + 'Case',
func = _[methodName];
var strings = [
'foo bar', 'Foo bar', 'foo Bar', 'Foo Bar',
'FOO BAR', 'fooBar', '--foo-bar', '__foo_bar__'
];
var converted = (function() {
switch (caseName) {
case 'camel': return 'fooBar';
case 'kebab': return 'foo-bar';
case 'snake': return 'foo_bar';
case 'start': return 'Foo Bar';
}
}());
test('`_.' + methodName + '` should convert `string` to ' + caseName + ' case', 1, function() {
var actual = _.map(strings, function(string) {
var expected = (caseName === 'start' && string === 'FOO BAR') ? string : converted;
return func(string) === expected;
});
deepEqual(actual, _.map(strings, _.constant(true)));
});
test('`_.' + methodName + '` should handle double-converting strings', 1, function() {
var actual = _.map(strings, function(string) {
var expected = (caseName === 'start' && string === 'FOO BAR') ? string : converted;
return func(func(string)) === expected;
});
deepEqual(actual, _.map(strings, _.constant(true)));
});
test('`_.' + methodName + '` should deburr letters', 1, function() {
var actual = _.map(burredLetters, function(burred, index) {
var letter = deburredLetters[index];
letter = caseName == 'start' ? _.capitalize(letter) : letter.toLowerCase();
return func(burred) === letter;
});
deepEqual(actual, _.map(burredLetters, _.constant(true)));
});
test('`_.' + methodName + '` should trim latin-1 mathematical operators', 1, function() {
var actual = _.map(['\xd7', '\xf7'], func);
deepEqual(actual, ['', '']);
});
test('`_.' + methodName + '` should coerce `string` to a string', 2, function() {
var string = 'foo bar';
strictEqual(func(Object(string)), converted);
strictEqual(func({ 'toString': _.constant(string) }), converted);
});
test('`_.' + methodName + '` should return an unwrapped value implicitly when chaining', 1, function() {
if (!isNpm) {
strictEqual(_('foo bar')[methodName](), converted);
}
else {
skipTest();
}
});
test('`_.' + methodName + '` should return a wrapped value when explicitly chaining', 1, function() {
if (!isNpm) {
ok(_('foo bar').chain()[methodName]() instanceof _);
}
else {
skipTest();
}
});
});
(function() {
test('should get the original value after cycling through all case methods', 1, function() {
var funcs = [_.camelCase, _.kebabCase, _.snakeCase, _.startCase, _.camelCase];
var actual = _.reduce(funcs, function(result, func) {
return func(result);
}, 'enable 24h format');
strictEqual(actual, 'enable24HFormat');
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.camelCase');
(function() {
test('should work with numbers', 4, function() {
strictEqual(_.camelCase('enable 24h format'), 'enable24HFormat');
strictEqual(_.camelCase('too legit 2 quit'), 'tooLegit2Quit');
strictEqual(_.camelCase('walk 500 miles'), 'walk500Miles');
strictEqual(_.camelCase('xhr2 request'), 'xhr2Request');
});
test('should handle acronyms', 6, function() {
_.each(['safe HTML', 'safeHTML'], function(string) {
strictEqual(_.camelCase(string), 'safeHtml');
});
_.each(['escape HTML entities', 'escapeHTMLEntities'], function(string) {
strictEqual(_.camelCase(string), 'escapeHtmlEntities');
});
_.each(['XMLHttpRequest', 'XmlHTTPRequest'], function(string) {
strictEqual(_.camelCase(string), 'xmlHttpRequest');
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.capitalize');
(function() {
test('should capitalize the first character of a string', 3, function() {
strictEqual(_.capitalize('fred'), 'Fred');
strictEqual(_.capitalize('Fred'), 'Fred');
strictEqual(_.capitalize(' fred'), ' fred');
});
test('should return an unwrapped value when implicitly chaining', 1, function() {
if (!isNpm) {
strictEqual(_('fred').capitalize(), 'Fred');
}
else {
skipTest();
}
});
test('should return a wrapped value when explicitly chaining', 1, function() {
if (!isNpm) {
ok(_('fred').chain().capitalize() instanceof _);
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.chain');
(function() {
test('should return a wrapped value', 1, function() {
if (!isNpm) {
var actual = _.chain({ 'a': 0 });
ok(actual instanceof _);
}
else {
skipTest();
}
});
test('should return existing wrapped values', 2, function() {
if (!isNpm) {
var wrapped = _({ 'a': 0 });
strictEqual(_.chain(wrapped), wrapped);
strictEqual(wrapped.chain(), wrapped);
}
else {
skipTest(2);
}
});
test('should enable chaining of methods that return unwrapped values by default', 6, function() {
if (!isNpm) {
var array = ['c', 'b', 'a'];
ok(_.chain(array).first() instanceof _);
ok(_(array).chain().first() instanceof _);
ok(_.chain(array).isArray() instanceof _);
ok(_(array).chain().isArray() instanceof _);
ok(_.chain(array).sortBy().first() instanceof _);
ok(_(array).chain().sortBy().first() instanceof _);
}
else {
skipTest(6);
}
});
test('should chain multiple methods', 6, function() {
if (!isNpm) {
_.times(2, function(index) {
var array = ['one two three four', 'five six seven eight', 'nine ten eleven twelve'],
expected = { ' ': 9, 'e': 14, 'f': 2, 'g': 1, 'h': 2, 'i': 4, 'l': 2, 'n': 6, 'o': 3, 'r': 2, 's': 2, 't': 5, 'u': 1, 'v': 4, 'w': 2, 'x': 1 },
wrapped = index ? _(array).chain() : _.chain(array);
var actual = wrapped
.chain()
.map(function(value) { return value.split(''); })
.flatten()
.reduce(function(object, chr) {
object[chr] || (object[chr] = 0);
object[chr]++;
return object;
}, {})
.value();
deepEqual(actual, expected);
array = [1, 2, 3, 4, 5, 6];
wrapped = index ? _(array).chain() : _.chain(array);
actual = wrapped
.chain()
.filter(function(n) { return n % 2; })
.reject(function(n) { return n % 3 == 0; })
.sortBy(function(n) { return -n; })
.value();
deepEqual(actual, [5, 1]);
array = [3, 4];
wrapped = index ? _(array).chain() : _.chain(array);
actual = wrapped
.reverse()
.concat([2, 1])
.unshift(5)
.tap(function(value) { value.pop(); })
.map(square)
.value();
deepEqual(actual,[25, 16, 9, 4]);
});
}
else {
skipTest(6);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.chunk');
(function() {
var array = [0, 1, 2, 3, 4, 5];
test('should return chunked arrays', 1, function() {
var actual = _.chunk(array, 3);
deepEqual(actual, [[0, 1, 2], [3, 4, 5]]);
});
test('should return the last chunk as remaining elements', 1, function() {
var actual = _.chunk(array, 4);
deepEqual(actual, [[0, 1, 2, 3], [4, 5]]);
});
test('should ensure the minimum `chunkSize` is `1`', 1, function() {
var values = falsey.concat(-1, -Infinity),
expected = _.map(values, _.constant([[0], [1], [2], [3], [4], [5]]));
var actual = _.map(values, function(value, index) {
return index ? _.chunk(array, value) : _.chunk(array);
});
deepEqual(actual, expected);
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var actual = _.map([[1, 2], [3, 4]], _.chunk);
deepEqual(actual, [[[1], [2]], [[3], [4]]]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('clone methods');
(function() {
function Foo() { this.a = 1; }
Foo.prototype = { 'b': 1 };
Foo.c = function() {};
var objects = {
'`arguments` objects': arguments,
'arrays': ['a', ''],
'array-like-objects': { '0': 'a', '1': '', 'length': 3 },
'booleans': false,
'boolean objects': Object(false),
'Foo instances': new Foo,
'objects': { 'a': 0, 'b': 1, 'c': 3 },
'objects with object values': { 'a': /a/, 'b': ['B'], 'c': { 'C': 1 } },
'objects from another document': _._object || {},
'null values': null,
'numbers': 3,
'number objects': Object(3),
'regexes': /a/gim,
'strings': 'a',
'string objects': Object('a'),
'undefined values': undefined
};
objects['arrays'].length = 3;
var uncloneable = {
'DOM elements': body,
'functions': Foo
};
_.each(errors, function(error) {
uncloneable[error.name + 's'] = error;
});
test('`_.clone` should perform a shallow clone', 2, function() {
var array = [{ 'a': 0 }, { 'b': 1 }],
actual = _.clone(array);
deepEqual(actual, array);
ok(actual !== array && actual[0] === array[0]);
});
test('`_.clone` should work with `isDeep`', 8, function() {
var array = [{ 'a': 0 }, { 'b': 1 }],
values = [true, 1, {}, '1'];
_.each(values, function(isDeep) {
var actual = _.clone(array, isDeep);
deepEqual(actual, array);
ok(actual !== array && actual[0] !== array[0]);
});
});
test('`_.cloneDeep` should deep clone objects with circular references', 1, function() {
var object = {
'foo': { 'b': { 'c': { 'd': {} } } },
'bar': {}
};
object.foo.b.c.d = object;
object.bar.b = object.foo.b;
var actual = _.cloneDeep(object);
ok(actual.bar.b === actual.foo.b && actual === actual.foo.b.c.d && actual !== object);
});
_.each(['clone', 'cloneDeep'], function(methodName) {
var func = _[methodName],
isDeep = methodName == 'cloneDeep';
_.forOwn(objects, function(object, key) {
test('`_.' + methodName + '` should clone ' + key, 2, function() {
var actual = func(object);
ok(_.isEqual(actual, object));
if (_.isObject(object)) {
notStrictEqual(actual, object);
} else {
strictEqual(actual, object);
}
});
});
_.forOwn(uncloneable, function(value, key) {
test('`_.' + methodName + '` should not clone ' + key, 3, function() {
var object = { 'a': value, 'b': { 'c': value } },
actual = func(object);
deepEqual(actual, object);
notStrictEqual(actual, object);
var expected = typeof value == 'function' ? { 'c': Foo.c } : (value && {});
deepEqual(func(value), expected);
});
test('`_.' + methodName + '` should work with a `customizer` callback and ' + key, 4, function() {
var customizer = function(value) {
return _.isPlainObject(value) ? undefined : value;
};
var actual = func(value, customizer);
deepEqual(actual, value);
strictEqual(actual, value);
var object = { 'a': value, 'b': { 'c': value } };
actual = func(object, customizer);
deepEqual(actual, object);
notStrictEqual(actual, object);
});
});
test('`_.' + methodName + '` should clone array buffers', 2, function() {
if (ArrayBuffer) {
var buffer = new ArrayBuffer(10),
actual = func(buffer);
strictEqual(actual.byteLength, buffer.byteLength);
notStrictEqual(actual, buffer);
}
else {
skipTest(2);
}
});
_.each(typedArrays, function(type) {
test('`_.' + methodName + '` should clone ' + type + ' arrays', 10, function() {
var Ctor = root[type];
_.times(2, function(index) {
if (Ctor) {
var buffer = new ArrayBuffer(24),
array = index ? new Ctor(buffer, 8, 1) : new Ctor(buffer),
actual = func(array);
deepEqual(actual, array);
notStrictEqual(actual, array);
strictEqual(actual.buffer === array.buffer, !isDeep);
strictEqual(actual.byteOffset, array.byteOffset);
strictEqual(actual.length, array.length);
}
else {
skipTest(5);
}
});
});
});
test('`_.' + methodName + '` should clone problem JScript properties (test in IE < 9)', 2, function() {
var actual = func(shadowObject);
deepEqual(actual, shadowObject);
notStrictEqual(actual, shadowObject);
});
test('`_.' + methodName + '` should provide the correct `customizer` arguments', 1, function() {
var argsList = [],
foo = new Foo;
func(foo, function() {
argsList.push(slice.call(arguments));
});
deepEqual(argsList, isDeep ? [[foo], [1, 'a', foo]] : [[foo]]);
});
test('`_.' + methodName + '` should support the `thisArg` argument', 1, function() {
var actual = func('a', function(value) {
return this[value];
}, { 'a': 'A' });
strictEqual(actual, 'A');
});
test('`_.' + methodName + '` should handle cloning if `customizer` returns `undefined`', 1, function() {
var actual = func({ 'a': { 'b': 'c' } }, _.noop);
deepEqual(actual, { 'a': { 'b': 'c' } });
});
test('`_.' + methodName + '` should clone `index` and `input` array properties', 2, function() {
var array = /x/.exec('vwxyz'),
actual = func(array);
strictEqual(actual.index, 2);
strictEqual(actual.input, 'vwxyz');
});
test('`_.' + methodName + '` should clone `lastIndex` regexp property', 1, function() {
// Avoid a regexp literal for older Opera and use `exec` for older Safari.
var regexp = RegExp('x', 'g');
regexp.exec('vwxyz');
var actual = func(regexp);
strictEqual(actual.lastIndex, 3);
});
test('`_.' + methodName + '` should not error on DOM elements', 1, function() {
if (document) {
var element = document.createElement('div');
try {
deepEqual(func(element), {});
} catch(e) {
ok(false, e.message);
}
}
else {
skipTest();
}
});
test('`_.' + methodName + '` should perform a ' + (isDeep ? 'deep' : 'shallow') + ' clone when used as an iteratee for methods like `_.map`', 3, function() {
var expected = [{ 'a': [0] }, { 'b': [1] }],
actual = _.map(expected, func);
deepEqual(actual, expected);
if (isDeep) {
ok(actual[0] !== expected[0] && actual[0].a !== expected[0].a && actual[1].b !== expected[1].b);
} else {
ok(actual[0] !== expected[0] && actual[0].a === expected[0].a && actual[1].b === expected[1].b);
}
actual = _.map(isDeep ? Object('abc') : 'abc', func);
deepEqual(actual, ['a', 'b', 'c']);
});
test('`_.' + methodName + '` should create an object from the same realm as `value`', 1, function() {
var props = [];
var objects = _.transform(_, function(result, value, key) {
if (_.startsWith(key, '_') && _.isObject(value) && !_.isArguments(value) && !_.isElement(value) && !_.isFunction(value)) {
props.push(_.capitalize(_.camelCase(key)));
result.push(value);
}
}, []);
var expected = _.times(objects.length, _.constant(true));
var actual = _.map(objects, function(object) {
var Ctor = object.constructor,
result = func(object);
return result !== object && (result instanceof Ctor || !(new Ctor instanceof Ctor));
});
deepEqual(actual, expected, props.join(', '));
});
test('`_.' + methodName + '` should return a unwrapped value when chaining', 2, function() {
if (!isNpm) {
var object = objects['objects'],
actual = _(object)[methodName]();
deepEqual(actual, object);
notStrictEqual(actual, object);
}
else {
skipTest(2);
}
});
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.compact');
(function() {
test('should filter falsey values', 1, function() {
var array = ['0', '1', '2'];
deepEqual(_.compact(falsey.concat(array)), array);
});
test('should work when in between lazy operators', 2, function() {
if (!isNpm) {
var actual = _(falsey).thru(_.slice).compact().thru(_.slice).value();
deepEqual(actual, []);
actual = _(falsey).thru(_.slice).push(true, 1).compact().push('a').value();
deepEqual(actual, [true, 1, 'a']);
}
else {
skipTest(2);
}
});
test('should work in a lazy chain sequence', 1, function() {
if (!isNpm) {
var array = [1, null, 3],
actual = _(array).map(square).compact().reverse().take().value();
deepEqual(actual, [9]);
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.flowRight');
(function() {
test('should be aliased', 2, function() {
strictEqual(_.backflow, _.flowRight);
strictEqual(_.compose, _.flowRight);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('flow methods');
_.each(['flow', 'flowRight'], function(methodName) {
var func = _[methodName],
isFlow = methodName == 'flow';
test('`_.' + methodName + '` should supply each function with the return value of the previous', 1, function() {
var fixed = function(n) { return n.toFixed(1); },
combined = isFlow ? func(add, square, fixed) : func(fixed, square, add);
strictEqual(combined(1, 2), '9.0');
});
test('`_.' + methodName + '` should return a new function', 1, function() {
notStrictEqual(func(_.noop), _.noop);
});
test('`_.' + methodName + '` should return an identity function when no arguments are provided', 3, function() {
var combined = func();
try {
strictEqual(combined('a'), 'a');
} catch(e) {
ok(false, e.message);
}
strictEqual(combined.length, 0);
notStrictEqual(combined, _.identity);
});
test('`_.' + methodName + '` should support shortcut fusion', 3, function() {
var filterCount = 0,
mapCount = 0;
var map = _.curry(_.rearg(_.ary(_.map, 2), 1, 0), 2),
filter = _.curry(_.rearg(_.ary(_.filter, 2), 1, 0), 2),
take = _.curry(_.rearg(_.ary(_.take, 2), 1, 0), 2);
var partialMap = map(function(value) { mapCount++; return value * value; }),
partialFilter = filter(function(value) { filterCount++; return value % 2 == 0; }),
partialTake = take(2);
var combined = isFlow
? func(partialMap, partialFilter, _.compact, partialTake)
: func(partialTake, _.compact, partialFilter, partialMap);
deepEqual(combined(_.range(100)), [4, 16]);
if (!isNpm && WeakMap && _.support.funcNames) {
strictEqual(filterCount, 5, 'filterCount');
strictEqual(mapCount, 5, 'mapCount');
}
else {
skipTest(2);
}
});
test('`_.' + methodName + '` should work with curried functions with placeholders', 1, function() {
var curried = _.curry(_.pluck),
getProp = curried(curried.placeholder, 'a'),
objects = [{ 'a': 1 }, { 'a': 2 }, { 'a': 1 }];
var combined = isFlow
? func(getProp, _.uniq)
: func(_.uniq, getProp);
deepEqual(combined(objects), [1, 2]);
});
test('`_.' + methodName + '` should return a wrapped value when chaining', 1, function() {
if (!isNpm) {
var wrapped = _(_.noop)[methodName]();
ok(wrapped instanceof _);
}
else {
skipTest();
}
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.constant');
(function() {
test('should create a function that returns `value`', 1, function() {
var object = { 'a': 1 },
values = Array(2).concat(empties, true, 1, 'a'),
constant = _.constant(object),
expected = _.map(values, function() { return true; });
var actual = _.map(values, function(value, index) {
if (index == 0) {
var result = constant();
} else if (index == 1) {
result = constant.call({});
} else {
result = constant(value);
}
return result === object;
});
deepEqual(actual, expected);
});
test('should work with falsey values', 1, function() {
var expected = _.map(falsey, function() { return true; });
var actual = _.map(falsey, function(value, index) {
var constant = index ? _.constant(value) : _.constant(),
result = constant();
return result === value || (_.isNaN(result) && _.isNaN(value));
});
deepEqual(actual, expected);
});
test('should return a wrapped value when chaining', 1, function() {
if (!isNpm) {
var wrapped = _(true).constant();
ok(wrapped instanceof _);
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.countBy');
(function() {
var array = [4.2, 6.1, 6.4];
test('should work with an iteratee', 1, function() {
var actual = _.countBy(array, function(num) {
return Math.floor(num);
}, Math);
deepEqual(actual, { '4': 1, '6': 2 });
});
test('should use `_.identity` when `iteratee` is nullish', 1, function() {
var array = [4, 6, 6],
values = [, null, undefined],
expected = _.map(values, _.constant({ '4': 1, '6': 2 }));
var actual = _.map(values, function(value, index) {
return index ? _.countBy(array, value) : _.countBy(array);
});
deepEqual(actual, expected);
});
test('should provide the correct `iteratee` arguments', 1, function() {
var args;
_.countBy(array, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [4.2, 0, array]);
});
test('should support the `thisArg` argument', 1, function() {
var actual = _.countBy(array, function(num) {
return this.floor(num);
}, Math);
deepEqual(actual, { '4': 1, '6': 2 });
});
test('should work with a "_.property" style `iteratee`', 1, function() {
var actual = _.countBy(['one', 'two', 'three'], 'length');
deepEqual(actual, { '3': 2, '5': 1 });
});
test('should only add values to own, not inherited, properties', 2, function() {
var actual = _.countBy([4.2, 6.1, 6.4], function(num) {
return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor';
});
deepEqual(actual.constructor, 1);
deepEqual(actual.hasOwnProperty, 2);
});
test('should work with a number for `iteratee`', 2, function() {
var array = [
[1, 'a'],
[2, 'a'],
[2, 'b']
];
deepEqual(_.countBy(array, 0), { '1': 1, '2': 2 });
deepEqual(_.countBy(array, 1), { 'a': 2, 'b': 1 });
});
test('should work with an object for `collection`', 1, function() {
var actual = _.countBy({ 'a': 4.2, 'b': 6.1, 'c': 6.4 }, function(num) {
return Math.floor(num);
});
deepEqual(actual, { '4': 1, '6': 2 });
});
test('should work in a lazy chain sequence', 1, function() {
if (!isNpm) {
var array = [1, 2, 1, 3],
predicate = function(value) { return value > 2; },
actual = _(array).countBy(_.identity).map(square).filter(predicate).take().value();
deepEqual(actual, [4]);
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.create');
(function() {
function Shape() {
this.x = 0;
this.y = 0;
}
function Circle() {
Shape.call(this);
}
test('should create an object that inherits from the given `prototype` object', 3, function() {
Circle.prototype = _.create(Shape.prototype);
Circle.prototype.constructor = Circle;
var actual = new Circle;
ok(actual instanceof Circle);
ok(actual instanceof Shape);
notStrictEqual(Circle.prototype, Shape.prototype);
});
test('should assign `properties` to the created object', 3, function() {
var expected = { 'constructor': Circle, 'radius': 0 };
Circle.prototype = _.create(Shape.prototype, expected);
var actual = new Circle;
ok(actual instanceof Circle);
ok(actual instanceof Shape);
deepEqual(Circle.prototype, expected);
});
test('should assign own properties', 1, function() {
function Foo() {
this.a = 1;
this.c = 3;
}
Foo.prototype.b = 2;
deepEqual(_.create({}, new Foo), { 'a': 1, 'c': 3 });
});
test('should accept a falsey `prototype` argument', 1, function() {
var expected = _.map(falsey, _.constant({}));
var actual = _.map(falsey, function(value, index) {
return index ? _.create(value) : _.create();
});
deepEqual(actual, expected);
});
test('should ignore primitive `prototype` arguments and use an empty object instead', 1, function() {
var primitives = [true, null, 1, 'a', undefined],
expected = _.map(primitives, _.constant(true));
var actual = _.map(primitives, function(value, index) {
return _.isPlainObject(index ? _.create(value) : _.create());
});
deepEqual(actual, expected);
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var array = [{ 'a': 1 }, { 'a': 1 }, { 'a': 1 }],
expected = _.map(array, _.constant(true)),
objects = _.map(array, _.create);
var actual = _.map(objects, function(object) {
return object.a === 1 && !_.keys(object).length;
});
deepEqual(actual, expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.callback');
(function() {
test('should provide arguments to `func`', 3, function() {
function fn() {
var result = [this];
push.apply(result, arguments);
return result;
}
var callback = _.callback(fn),
actual = callback('a', 'b', 'c', 'd', 'e', 'f');
ok(actual[0] === null || actual[0] && actual[0].Array);
deepEqual(actual.slice(1), ['a', 'b', 'c', 'd', 'e', 'f']);
var object = {};
callback = _.callback(fn, object);
actual = callback('a', 'b');
deepEqual(actual, [object, 'a', 'b']);
});
test('should return `_.identity` when `func` is nullish', 1, function() {
var object = {},
values = [, null, undefined],
expected = _.map(values, _.constant([!isNpm && _.identity, object]));
var actual = _.map(values, function(value, index) {
var identity = index ? _.callback(value) : _.callback();
return [!isNpm && identity, identity(object)];
});
deepEqual(actual, expected);
});
test('should not error when `func` is nullish and a `thisArg` is provided', 2, function() {
var object = {};
_.each([null, undefined], function(value) {
try {
var callback = _.callback(value, {});
strictEqual(callback(object), object);
} catch(e) {
ok(false, e.message);
}
});
});
test('should create a callback with a falsey `thisArg`', 1, function() {
var fn = function() { return this; },
object = {};
var expected = _.map(falsey, function(value) {
var result = fn.call(value);
return (result && result.Array) ? object : result;
});
var actual = _.map(falsey, function(value) {
var callback = _.callback(fn, value),
result = callback();
return (result && result.Array) ? object : result;
});
ok(_.isEqual(actual, expected));
});
test('should return a callback created by `_.matches` when `func` is an object', 2, function() {
var matches = _.callback({ 'a': 1, 'b': 2 });
strictEqual(matches({ 'a': 1, 'b': 2, 'c': 3 }), true);
strictEqual(matches({ 'b': 2 }), false);
});
test('should return a callback created by `_.matches` when `func` is an array', 2, function() {
var matches = _.callback(['a', 'b']);
strictEqual(matches({ '0': 'a', '1': 'b', '2': 'c' }), true);
strictEqual(matches({ '1': 'b' }), false);
});
test('should not change match behavior if `source` is augmented', 9, function() {
var sources = [
{ 'a': { 'b': 2, 'c': 3 } },
{ 'a': 1, 'b': 2 },
{ 'a': 1 }
];
_.each(sources, function(source, index) {
var object = _.cloneDeep(source),
matches = _.callback(source);
strictEqual(matches(object), true);
if (index) {
source.a = 2;
source.b = 1;
source.c = 3;
} else {
source.a.b = 1;
source.a.c = 2;
source.a.d = 3;
}
strictEqual(matches(object), true);
strictEqual(matches(source), false);
});
});
test('should return a callback created by `_.matchesProperty` when `func` is a number or string and `thisArg` is not `undefined`', 3, function() {
var array = ['a'],
matches = _.callback(0, 'a');
strictEqual(matches(array), true);
matches = _.callback('0', 'a');
strictEqual(matches(array), true);
matches = _.callback(1, undefined);
strictEqual(matches(array), undefined);
});
test('should support deep paths for `_.matchesProperty` shorthands', 1, function() {
var object = { 'a': { 'b': { 'c': { 'd': 1, 'e': 2 } } } },
matches = _.callback('a.b.c', { 'e': 2 });
strictEqual(matches(object), true);
});
test('should return a callback created by `_.property` when `func` is a number or string', 2, function() {
var array = ['a'],
prop = _.callback(0);
strictEqual(prop(array), 'a');
prop = _.callback('0');
strictEqual(prop(array), 'a');
});
test('should support deep paths for `_.property` shorthands', 1, function() {
var object = { 'a': { 'b': { 'c': 3 } } },
prop = _.callback('a.b.c');
strictEqual(prop(object), 3);
});
test('should work with functions created by `_.partial` and `_.partialRight`', 2, function() {
function fn() {
var result = [this.a];
push.apply(result, arguments);
return result;
}
var expected = [1, 2, 3],
object = { 'a': 1 },
callback = _.callback(_.partial(fn, 2), object);
deepEqual(callback(3), expected);
callback = _.callback(_.partialRight(fn, 3), object);
deepEqual(callback(2), expected);
});
test('should support binding built-in methods', 2, function() {
var fn = function() {},
object = { 'a': 1 },
bound = fn.bind && fn.bind(object),
callback = _.callback(hasOwnProperty, object);
strictEqual(callback('a'), true);
if (bound) {
callback = _.callback(bound, object);
notStrictEqual(callback, bound);
}
else {
skipTest();
}
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var fn = function() { return this instanceof Number; },
array = [fn, fn, fn],
callbacks = _.map(array, _.callback),
expected = _.map(array, _.constant(false));
var actual = _.map(callbacks, function(callback) {
return callback();
});
deepEqual(actual, expected);
});
test('should be aliased', 1, function() {
strictEqual(_.iteratee, _.callback);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('custom `_.callback` methods');
(function() {
var array = ['one', 'two', 'three'],
callback = _.callback,
getPropA = _.partial(_.property, 'a'),
getPropB = _.partial(_.property, 'b'),
getLength = _.partial(_.property, 'length');
var getSum = function() {
return function(result, object) {
return result + object.a;
};
};
var objects = [
{ 'a': 0, 'b': 0 },
{ 'a': 1, 'b': 0 },
{ 'a': 1, 'b': 1 }
];
test('`_.countBy` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getLength;
deepEqual(_.countBy(array), { '3': 2, '5': 1 });
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.dropRightWhile` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropB;
deepEqual(_.dropRightWhile(objects), objects.slice(0, 2));
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.dropWhile` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropB;
deepEqual(_.dropWhile(objects.reverse()).reverse(), objects.reverse().slice(0, 2));
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.every` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropA;
strictEqual(_.every(objects.slice(1)), true);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.filter` should use `_.callback` internally', 1, function() {
if (!isModularize) {
var objects = [{ 'a': 0 }, { 'a': 1 }];
_.callback = getPropA;
deepEqual(_.filter(objects), [objects[1]]);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.find` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropA;
strictEqual(_.find(objects), objects[1]);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.findIndex` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropA;
strictEqual(_.findIndex(objects), 1);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.findLast` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropA;
strictEqual(_.findLast(objects), objects[2]);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.findLastIndex` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropA;
strictEqual(_.findLastIndex(objects), 2);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.findLastKey` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropB;
strictEqual(_.findKey(objects), '2');
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.findKey` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropB;
strictEqual(_.findLastKey(objects), '2');
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.groupBy` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getLength;
deepEqual(_.groupBy(array), { '3': ['one', 'two'], '5': ['three'] });
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.indexBy` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getLength;
deepEqual(_.indexBy(array), { '3': 'two', '5': 'three' });
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.map` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropA;
deepEqual(_.map(objects), [0, 1, 1]);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.mapKeys` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropB;
deepEqual(_.mapKeys({ 'a': { 'b': 1 } }), { '1': { 'b': 1 } });
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.mapValues` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropB;
deepEqual(_.mapValues({ 'a': { 'b': 1 } }), { 'a': 1 });
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.max` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropB;
deepEqual(_.max(objects), objects[2]);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.min` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropB;
deepEqual(_.min(objects), objects[0]);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.partition` should use `_.callback` internally', 1, function() {
if (!isModularize) {
var objects = [{ 'a': 1 }, { 'a': 1 }, { 'b': 2 }];
_.callback = getPropA;
deepEqual(_.partition(objects), [objects.slice(0, 2), objects.slice(2)]);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.reduce` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getSum;
strictEqual(_.reduce(objects, undefined, 0), 2);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.reduceRight` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getSum;
strictEqual(_.reduceRight(objects, undefined, 0), 2);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.reject` should use `_.callback` internally', 1, function() {
if (!isModularize) {
var objects = [{ 'a': 0 }, { 'a': 1 }];
_.callback = getPropA;
deepEqual(_.reject(objects), [objects[0]]);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.remove` should use `_.callback` internally', 1, function() {
if (!isModularize) {
var objects = [{ 'a': 0 }, { 'a': 1 }];
_.callback = getPropA;
_.remove(objects);
deepEqual(objects, [{ 'a': 0 }]);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.some` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropB;
strictEqual(_.some(objects), true);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.sortBy` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropA;
deepEqual(_.sortBy(objects.slice().reverse()), [objects[0], objects[2], objects[1]]);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.sortedIndex` should use `_.callback` internally', 1, function() {
if (!isModularize) {
var objects = [{ 'a': 30 }, { 'a': 50 }];
_.callback = getPropA;
strictEqual(_.sortedIndex(objects, { 'a': 40 }), 1);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.sortedLastIndex` should use `_.callback` internally', 1, function() {
if (!isModularize) {
var objects = [{ 'a': 30 }, { 'a': 50 }];
_.callback = getPropA;
strictEqual(_.sortedLastIndex(objects, { 'a': 40 }), 1);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.sum` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropB;
strictEqual(_.sum(objects), 1);
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.takeRightWhile` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropB;
deepEqual(_.takeRightWhile(objects), objects.slice(2));
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.takeWhile` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropB;
deepEqual(_.takeWhile(objects.reverse()), objects.reverse().slice(2));
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.transform` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = function() {
return function(result, object) {
result.sum += object.a;
};
};
deepEqual(_.transform(objects, undefined, { 'sum': 0 }), { 'sum': 2 });
_.callback = callback;
}
else {
skipTest();
}
});
test('`_.uniq` should use `_.callback` internally', 1, function() {
if (!isModularize) {
_.callback = getPropB;
deepEqual(_.uniq(objects), [objects[0], objects[2]]);
_.callback = callback;
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.curry');
(function() {
function fn(a, b, c, d) {
return slice.call(arguments);
}
test('should curry based on the number of arguments provided', 3, function() {
var curried = _.curry(fn),
expected = [1, 2, 3, 4];
deepEqual(curried(1)(2)(3)(4), expected);
deepEqual(curried(1, 2)(3, 4), expected);
deepEqual(curried(1, 2, 3, 4), expected);
});
test('should allow specifying `arity`', 3, function() {
var curried = _.curry(fn, 3),
expected = [1, 2, 3];
deepEqual(curried(1)(2, 3), expected);
deepEqual(curried(1, 2)(3), expected);
deepEqual(curried(1, 2, 3), expected);
});
test('should coerce `arity` to a number', 2, function() {
var values = ['0', 'xyz'],
expected = _.map(values, _.constant([]));
var actual = _.map(values, function(arity) {
return _.curry(fn, arity)();
});
deepEqual(actual, expected);
deepEqual(_.curry(fn, '2')(1)(2), [1, 2]);
});
test('should support placeholders', 4, function() {
var curried = _.curry(fn),
ph = curried.placeholder;
deepEqual(curried(1)(ph, 3)(ph, 4)(2), [1, 2, 3, 4]);
deepEqual(curried(ph, 2)(1)(ph, 4)(3), [1, 2, 3, 4]);
deepEqual(curried(ph, ph, 3)(ph, 2)(ph, 4)(1), [1, 2, 3, 4]);
deepEqual(curried(ph, ph, ph, 4)(ph, ph, 3)(ph, 2)(1), [1, 2, 3, 4]);
});
test('should work with partialed methods', 2, function() {
var curried = _.curry(fn),
expected = [1, 2, 3, 4];
var a = _.partial(curried, 1),
b = _.bind(a, null, 2),
c = _.partialRight(b, 4),
d = _.partialRight(b(3), 4);
deepEqual(c(3), expected);
deepEqual(d(), expected);
});
test('should provide additional arguments after reaching the target arity', 3, function() {
var curried = _.curry(fn, 3);
deepEqual(curried(1)(2, 3, 4), [1, 2, 3, 4]);
deepEqual(curried(1, 2)(3, 4, 5), [1, 2, 3, 4, 5]);
deepEqual(curried(1, 2, 3, 4, 5, 6), [1, 2, 3, 4, 5, 6]);
});
test('should return a function with a `length` of `0`', 6, function() {
_.times(2, function(index) {
var curried = index ? _.curry(fn, 4) : _.curry(fn);
strictEqual(curried.length, 0);
strictEqual(curried(1).length, 0);
strictEqual(curried(1, 2).length, 0);
});
});
test('ensure `new curried` is an instance of `func`', 2, function() {
var Foo = function(value) {
return value && object;
};
var curried = _.curry(Foo),
object = {};
ok(new curried(false) instanceof Foo);
strictEqual(new curried(true), object);
});
test('should not set a `this` binding', 9, function() {
var fn = function(a, b, c) {
var value = this || {};
return [value[a], value[b], value[c]];
};
var object = { 'a': 1, 'b': 2, 'c': 3 },
expected = [1, 2, 3];
deepEqual(_.curry(_.bind(fn, object), 3)('a')('b')('c'), expected);
deepEqual(_.curry(_.bind(fn, object), 3)('a', 'b')('c'), expected);
deepEqual(_.curry(_.bind(fn, object), 3)('a', 'b', 'c'), expected);
deepEqual(_.bind(_.curry(fn), object)('a')('b')('c'), Array(3));
deepEqual(_.bind(_.curry(fn), object)('a', 'b')('c'), Array(3));
deepEqual(_.bind(_.curry(fn), object)('a', 'b', 'c'), expected);
object.curried = _.curry(fn);
deepEqual(object.curried('a')('b')('c'), Array(3));
deepEqual(object.curried('a', 'b')('c'), Array(3));
deepEqual(object.curried('a', 'b', 'c'), expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.curryRight');
(function() {
function fn(a, b, c, d) {
return slice.call(arguments);
}
test('should curry based on the number of arguments provided', 3, function() {
var curried = _.curryRight(fn),
expected = [1, 2, 3, 4];
deepEqual(curried(4)(3)(2)(1), expected);
deepEqual(curried(3, 4)(1, 2), expected);
deepEqual(curried(1, 2, 3, 4), expected);
});
test('should allow specifying `arity`', 3, function() {
var curried = _.curryRight(fn, 3),
expected = [1, 2, 3];
deepEqual(curried(3)(1, 2), expected);
deepEqual(curried(2, 3)(1), expected);
deepEqual(curried(1, 2, 3), expected);
});
test('should work with partialed methods', 2, function() {
var curried = _.curryRight(fn),
expected = [1, 2, 3, 4];
var a = _.partialRight(curried, 4),
b = _.partialRight(a, 3),
c = _.bind(b, null, 1),
d = _.partial(b(2), 1);
deepEqual(c(2), expected);
deepEqual(d(), expected);
});
test('should support placeholders', 4, function() {
var curried = _.curryRight(fn),
expected = [1, 2, 3, 4],
ph = curried.placeholder;
deepEqual(curried(4)(2, ph)(1, ph)(3), expected);
deepEqual(curried(3, ph)(4)(1, ph)(2), expected);
deepEqual(curried(ph, ph, 4)(ph, 3)(ph, 2)(1), expected);
deepEqual(curried(ph, ph, ph, 4)(ph, ph, 3)(ph, 2)(1), expected);
});
test('should provide additional arguments after reaching the target arity', 3, function() {
var curried = _.curryRight(fn, 3);
deepEqual(curried(4)(1, 2, 3), [1, 2, 3, 4]);
deepEqual(curried(4, 5)(1, 2, 3), [1, 2, 3, 4, 5]);
deepEqual(curried(1, 2, 3, 4, 5, 6), [1, 2, 3, 4, 5, 6]);
});
test('should return a function with a `length` of `0`', 6, function() {
_.times(2, function(index) {
var curried = index ? _.curryRight(fn, 4) : _.curryRight(fn);
strictEqual(curried.length, 0);
strictEqual(curried(4).length, 0);
strictEqual(curried(3, 4).length, 0);
});
});
test('ensure `new curried` is an instance of `func`', 2, function() {
var Foo = function(value) {
return value && object;
};
var curried = _.curryRight(Foo),
object = {};
ok(new curried(false) instanceof Foo);
strictEqual(new curried(true), object);
});
test('should not set a `this` binding', 9, function() {
var fn = function(a, b, c) {
var value = this || {};
return [value[a], value[b], value[c]];
};
var object = { 'a': 1, 'b': 2, 'c': 3 },
expected = [1, 2, 3];
deepEqual(_.curryRight(_.bind(fn, object), 3)('c')('b')('a'), expected);
deepEqual(_.curryRight(_.bind(fn, object), 3)('b', 'c')('a'), expected);
deepEqual(_.curryRight(_.bind(fn, object), 3)('a', 'b', 'c'), expected);
deepEqual(_.bind(_.curryRight(fn), object)('c')('b')('a'), Array(3));
deepEqual(_.bind(_.curryRight(fn), object)('b', 'c')('a'), Array(3));
deepEqual(_.bind(_.curryRight(fn), object)('a', 'b', 'c'), expected);
object.curried = _.curryRight(fn);
deepEqual(object.curried('c')('b')('a'), Array(3));
deepEqual(object.curried('b', 'c')('a'), Array(3));
deepEqual(object.curried('a', 'b', 'c'), expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('curry methods');
_.each(['curry', 'curryRight'], function(methodName) {
var func = _[methodName],
fn = function(a, b) { return slice.call(arguments); },
isCurry = methodName == 'curry';
test('`_.' + methodName + '` should work as an iteratee for methods like `_.map`', 2, function() {
var array = [fn, fn, fn],
object = { 'a': fn, 'b': fn, 'c': fn };
_.each([array, object], function(collection) {
var curries = _.map(collection, func),
expected = _.map(collection, _.constant(isCurry ? ['a', 'b'] : ['b', 'a']));
var actual = _.map(curries, function(curried) {
return curried('a')('b');
});
deepEqual(actual, expected);
});
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.debounce');
(function() {
asyncTest('should debounce a function', 2, function() {
if (!(isRhino && isModularize)) {
var callCount = 0,
debounced = _.debounce(function() { callCount++; }, 32);
debounced();
debounced();
debounced();
strictEqual(callCount, 0);
setTimeout(function() {
strictEqual(callCount, 1);
QUnit.start();
}, 96);
}
else {
skipTest(2);
QUnit.start();
}
});
asyncTest('subsequent debounced calls return the last `func` result', 2, function() {
if (!(isRhino && isModularize)) {
var debounced = _.debounce(_.identity, 32);
debounced('x');
setTimeout(function() {
notEqual(debounced('y'), 'y');
}, 64);
setTimeout(function() {
notEqual(debounced('z'), 'z');
QUnit.start();
}, 128);
}
else {
skipTest(2);
QUnit.start();
}
});
asyncTest('subsequent "immediate" debounced calls return the last `func` result', 2, function() {
if (!(isRhino && isModularize)) {
var debounced = _.debounce(_.identity, 32, true),
result = [debounced('x'), debounced('y')];
deepEqual(result, ['x', 'x']);
setTimeout(function() {
var result = [debounced('a'), debounced('b')];
deepEqual(result, ['a', 'a']);
QUnit.start();
}, 64);
}
else {
skipTest(2);
QUnit.start();
}
});
asyncTest('should apply default options', 2, function() {
if (!(isRhino && isModularize)) {
var callCount = 0;
var debounced = _.debounce(function(value) {
callCount++;
return value;
}, 32, {});
strictEqual(debounced('x'), undefined);
setTimeout(function() {
strictEqual(callCount, 1);
QUnit.start();
}, 64);
}
else {
skipTest(2);
QUnit.start();
}
});
asyncTest('should support a `leading` option', 7, function() {
if (!(isRhino && isModularize)) {
var withLeading,
callCounts = [0, 0, 0];
_.each([true, { 'leading': true }], function(options, index) {
var debounced = _.debounce(function(value) {
callCounts[index]++;
return value;
}, 32, options);
if (index == 1) {
withLeading = debounced;
}
strictEqual(debounced('x'), 'x');
});
_.each([false, { 'leading': false }], function(options) {
var withoutLeading = _.debounce(_.identity, 32, options);
strictEqual(withoutLeading('x'), undefined);
});
var withLeadingAndTrailing = _.debounce(function() {
callCounts[2]++;
}, 32, { 'leading': true });
withLeadingAndTrailing();
withLeadingAndTrailing();
strictEqual(callCounts[2], 1);
setTimeout(function() {
deepEqual(callCounts, [1, 1, 2]);
withLeading('x');
strictEqual(callCounts[1], 2);
QUnit.start();
}, 64);
}
else {
skipTest(7);
QUnit.start();
}
});
asyncTest('should support a `trailing` option', 4, function() {
if (!(isRhino && isModularize)) {
var withCount = 0,
withoutCount = 0;
var withTrailing = _.debounce(function(value) {
withCount++;
return value;
}, 32, { 'trailing': true });
var withoutTrailing = _.debounce(function(value) {
withoutCount++;
return value;
}, 32, { 'trailing': false });
strictEqual(withTrailing('x'), undefined);
strictEqual(withoutTrailing('x'), undefined);
setTimeout(function() {
strictEqual(withCount, 1);
strictEqual(withoutCount, 0);
QUnit.start();
}, 64);
}
else {
skipTest(4);
QUnit.start();
}
});
asyncTest('should support a `maxWait` option', 1, function() {
if (!(isRhino && isModularize)) {
var limit = (argv || isPhantom) ? 1000 : 320,
withCount = 0,
withoutCount = 0;
var withMaxWait = _.debounce(function() {
withCount++;
}, 64, { 'maxWait': 128 });
var withoutMaxWait = _.debounce(function() {
withoutCount++;
}, 96);
var start = +new Date;
while ((new Date - start) < limit) {
withMaxWait();
withoutMaxWait();
}
var actual = [Boolean(withCount), Boolean(withoutCount)];
setTimeout(function() {
deepEqual(actual, [true, false]);
QUnit.start();
}, 1);
}
else {
skipTest();
QUnit.start();
}
});
asyncTest('should cancel `maxDelayed` when `delayed` is invoked', 1, function() {
if (!(isRhino && isModularize)) {
var callCount = 0;
var debounced = _.debounce(function() {
callCount++;
}, 32, { 'maxWait': 64 });
debounced();
setTimeout(function() {
strictEqual(callCount, 1);
QUnit.start();
}, 128);
}
else {
skipTest();
QUnit.start();
}
});
asyncTest('should invoke the `trailing` call with the correct arguments and `this` binding', 2, function() {
if (!(isRhino && isModularize)) {
var actual,
callCount = 0,
object = {};
var debounced = _.debounce(function(value) {
actual = [this];
push.apply(actual, arguments);
return ++callCount != 2;
}, 32, { 'leading': true, 'maxWait': 64 });
while (true) {
if (!debounced.call(object, 'a')) {
break;
}
}
setTimeout(function() {
strictEqual(callCount, 2);
deepEqual(actual, [object, 'a']);
QUnit.start();
}, 64);
}
else {
skipTest(2);
QUnit.start();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.deburr');
(function() {
test('should convert latin-1 supplementary letters to basic latin', 1, function() {
var actual = _.map(burredLetters, _.deburr);
deepEqual(actual, deburredLetters);
});
test('should not deburr latin-1 mathematical operators', 1, function() {
var operators = ['\xd7', '\xf7'],
actual = _.map(operators, _.deburr);
deepEqual(actual, operators);
});
test('should deburr combining diacritical marks', 1, function() {
var values = comboMarks.concat(comboHalfs),
expected = _.map(values, _.constant('ei'));
var actual = _.map(values, function(chr) {
return _.deburr('e' + chr + 'i');
});
deepEqual(actual, expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.defaults');
(function() {
test('should assign properties of a source object if missing on the destination object', 1, function() {
deepEqual(_.defaults({ 'a': 1 }, { 'a': 2, 'b': 2 }), { 'a': 1, 'b': 2 });
});
test('should accept multiple source objects', 2, function() {
var expected = { 'a': 1, 'b': 2, 'c': 3 };
deepEqual(_.defaults({ 'a': 1, 'b': 2 }, { 'b': 3 }, { 'c': 3 }), expected);
deepEqual(_.defaults({ 'a': 1, 'b': 2 }, { 'b': 3, 'c': 3 }, { 'c': 2 }), expected);
});
test('should not overwrite `null` values', 1, function() {
var actual = _.defaults({ 'a': null }, { 'a': 1 });
strictEqual(actual.a, null);
});
test('should overwrite `undefined` values', 1, function() {
var actual = _.defaults({ 'a': undefined }, { 'a': 1 });
strictEqual(actual.a, 1);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.defer');
(function() {
asyncTest('should defer `func` execution', 1, function() {
if (!(isRhino && isModularize)) {
var pass = false;
_.defer(function() { pass = true; });
setTimeout(function() {
ok(pass);
QUnit.start();
}, 128);
}
else {
skipTest();
QUnit.start();
}
});
asyncTest('should provide additional arguments to `func`', 1, function() {
if (!(isRhino && isModularize)) {
var args;
_.defer(function() {
args = slice.call(arguments);
}, 1, 2);
setTimeout(function() {
deepEqual(args, [1, 2]);
QUnit.start();
}, 128);
}
else {
skipTest();
QUnit.start();
}
});
asyncTest('should be cancelable', 1, function() {
if (!(isRhino && isModularize)) {
var pass = true;
var timerId = _.defer(function() {
pass = false;
});
clearTimeout(timerId);
setTimeout(function() {
ok(pass);
QUnit.start();
}, 128);
}
else {
skipTest();
QUnit.start();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.delay');
(function() {
asyncTest('should delay `func` execution', 2, function() {
if (!(isRhino && isModularize)) {
var pass = false;
_.delay(function() { pass = true; }, 96);
setTimeout(function() {
ok(!pass);
}, 32);
setTimeout(function() {
ok(pass);
QUnit.start();
}, 160);
}
else {
skipTest(2);
QUnit.start();
}
});
asyncTest('should provide additional arguments to `func`', 1, function() {
if (!(isRhino && isModularize)) {
var args;
_.delay(function() {
args = slice.call(arguments);
}, 32, 1, 2);
setTimeout(function() {
deepEqual(args, [1, 2]);
QUnit.start();
}, 128);
}
else {
skipTest();
QUnit.start();
}
});
asyncTest('should be cancelable', 1, function() {
if (!(isRhino && isModularize)) {
var pass = true;
var timerId = _.delay(function() {
pass = false;
}, 32);
clearTimeout(timerId);
setTimeout(function() {
ok(pass);
QUnit.start();
}, 128);
}
else {
skipTest();
QUnit.start();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.difference');
(function() {
var args = arguments;
test('should return the difference of the given arrays', 2, function() {
var actual = _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
deepEqual(actual, [1, 3, 4]);
actual = _.difference([1, 2, 3, 4, 5], [5, 2, 10], [8, 4]);
deepEqual(actual, [1, 3]);
});
test('should match `NaN`', 1, function() {
deepEqual(_.difference([1, NaN, 3], [NaN, 5, NaN]), [1, 3]);
});
test('should work with large arrays', 1, function() {
var array1 = _.range(LARGE_ARRAY_SIZE + 1),
array2 = _.range(LARGE_ARRAY_SIZE),
a = {},
b = {},
c = {};
array1.push(a, b, c);
array2.push(b, c, a);
deepEqual(_.difference(array1, array2), [LARGE_ARRAY_SIZE]);
});
test('should work with large arrays of objects', 1, function() {
var object1 = {},
object2 = {},
largeArray = _.times(LARGE_ARRAY_SIZE, _.constant(object1));
deepEqual(_.difference([object1, object2], largeArray), [object2]);
});
test('should work with large arrays of `NaN`', 1, function() {
var largeArray = _.times(LARGE_ARRAY_SIZE, _.constant(NaN));
deepEqual(_.difference([1, NaN, 3], largeArray), [1, 3]);
});
test('should ignore values that are not array-like', 3, function() {
var array = [1, null, 3];
deepEqual(_.difference(args, 3, { '0': 1 }), [1, 2, 3]);
deepEqual(_.difference(null, array, 1), []);
deepEqual(_.difference(array, args, null), [null]);
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.drop');
(function() {
var array = [1, 2, 3];
test('should drop the first two elements', 1, function() {
deepEqual(_.drop(array, 2), [3]);
});
test('should treat falsey `n` values, except nullish, as `0`', 1, function() {
var expected = _.map(falsey, function(value) {
return value == null ? [2, 3] : array;
});
var actual = _.map(falsey, function(n) {
return _.drop(array, n);
});
deepEqual(actual, expected);
});
test('should return all elements when `n` < `1`', 3, function() {
_.each([0, -1, -Infinity], function(n) {
deepEqual(_.drop(array, n), array);
});
});
test('should return an empty array when `n` >= `array.length`', 4, function() {
_.each([3, 4, Math.pow(2, 32), Infinity], function(n) {
deepEqual(_.drop(array, n), []);
});
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
actual = _.map(array, _.drop);
deepEqual(actual, [[2, 3], [5, 6], [8, 9]]);
});
test('should work in a lazy chain sequence', 6, function() {
if (!isNpm) {
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
values = [],
predicate = function(value) { values.push(value); return value > 2; },
actual = _(array).drop(2).drop().value();
deepEqual(actual, [4, 5, 6, 7, 8, 9, 10]);
actual = _(array).filter(predicate).drop(2).drop().value();
deepEqual(actual, [6, 7, 8, 9, 10]);
deepEqual(values, array);
actual = _(array).drop(2).dropRight().drop().dropRight(2).value();
deepEqual(actual, [4, 5, 6, 7]);
values = [];
actual = _(array).drop().filter(predicate).drop(2).dropRight().drop().dropRight(2).value();
deepEqual(actual, [6, 7]);
deepEqual(values, array.slice(1));
}
else {
skipTest(6);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.dropRight');
(function() {
var array = [1, 2, 3];
test('should drop the last two elements', 1, function() {
deepEqual(_.dropRight(array, 2), [1]);
});
test('should treat falsey `n` values, except nullish, as `0`', 1, function() {
var expected = _.map(falsey, function(value) {
return value == null ? [1, 2] : array;
});
var actual = _.map(falsey, function(n) {
return _.dropRight(array, n);
});
deepEqual(actual, expected);
});
test('should return all elements when `n` < `1`', 3, function() {
_.each([0, -1, -Infinity], function(n) {
deepEqual(_.dropRight(array, n), array);
});
});
test('should return an empty array when `n` >= `array.length`', 4, function() {
_.each([3, 4, Math.pow(2, 32), Infinity], function(n) {
deepEqual(_.dropRight(array, n), []);
});
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
actual = _.map(array, _.dropRight);
deepEqual(actual, [[1, 2], [4, 5], [7, 8]]);
});
test('should work in a lazy chain sequence', 6, function() {
if (!isNpm) {
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
values = [],
predicate = function(value) { values.push(value); return value < 9; },
actual = _(array).dropRight(2).dropRight().value();
deepEqual(actual, [1, 2, 3, 4, 5, 6, 7]);
actual = _(array).filter(predicate).dropRight(2).dropRight().value();
deepEqual(actual, [1, 2, 3, 4, 5]);
deepEqual(values, array);
actual = _(array).dropRight(2).drop().dropRight().drop(2).value();
deepEqual(actual, [4, 5, 6, 7]);
values = [];
actual = _(array).dropRight().filter(predicate).dropRight(2).drop().dropRight().drop(2).value();
deepEqual(actual, [4, 5]);
deepEqual(values, array.slice(0, -1));
}
else {
skipTest(6);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.dropRightWhile');
(function() {
var array = [1, 2, 3, 4];
var objects = [
{ 'a': 0, 'b': 0 },
{ 'a': 1, 'b': 1 },
{ 'a': 2, 'b': 2 }
];
test('should drop elements while `predicate` returns truthy', 1, function() {
var actual = _.dropRightWhile(array, function(num) {
return num > 2;
});
deepEqual(actual, [1, 2]);
});
test('should provide the correct `predicate` arguments', 1, function() {
var args;
_.dropRightWhile(array, function() {
args = slice.call(arguments);
});
deepEqual(args, [4, 3, array]);
});
test('should support the `thisArg` argument', 1, function() {
var actual = _.dropRightWhile(array, function(num, index) {
return this[index] > 2;
}, array);
deepEqual(actual, [1, 2]);
});
test('should work with a "_.matches" style `predicate`', 1, function() {
deepEqual(_.dropRightWhile(objects, { 'b': 2 }), objects.slice(0, 2));
});
test('should work with a "_.matchesProperty" style `predicate`', 1, function() {
deepEqual(_.dropRightWhile(objects, 'b', 2), objects.slice(0, 2));
});
test('should work with a "_.property" style `predicate`', 1, function() {
deepEqual(_.dropRightWhile(objects, 'b'), objects.slice(0, 1));
});
test('should return a wrapped value when chaining', 2, function() {
if (!isNpm) {
var wrapped = _(array).dropRightWhile(function(num) {
return num > 2;
});
ok(wrapped instanceof _);
deepEqual(wrapped.value(), [1, 2]);
}
else {
skipTest(2);
}
});
test('should provide the correct `predicate` arguments in a lazy chain sequence', 5, function() {
if (!isNpm) {
var args,
expected = [16, 3, [1, 4, 9 ,16]];
_(array).dropRightWhile(function(value, index, array) {
args = slice.call(arguments);
}).value();
deepEqual(args, [4, 3, array]);
_(array).map(square).dropRightWhile(function(value, index, array) {
args = slice.call(arguments);
}).value();
deepEqual(args, expected);
_(array).map(square).dropRightWhile(function(value, index) {
args = slice.call(arguments);
}).value();
deepEqual(args, expected);
_(array).map(square).dropRightWhile(function(value) {
args = slice.call(arguments);
}).value();
deepEqual(args, [16]);
_(array).map(square).dropRightWhile(function() {
args = slice.call(arguments);
}).value();
deepEqual(args, expected);
}
else {
skipTest(5);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.dropWhile');
(function() {
var array = [1, 2, 3, 4];
var objects = [
{ 'a': 2, 'b': 2 },
{ 'a': 1, 'b': 1 },
{ 'a': 0, 'b': 0 }
];
test('should drop elements while `predicate` returns truthy', 1, function() {
var actual = _.dropWhile(array, function(num) {
return num < 3;
});
deepEqual(actual, [3, 4]);
});
test('should provide the correct `predicate` arguments', 1, function() {
var args;
_.dropWhile(array, function() {
args = slice.call(arguments);
});
deepEqual(args, [1, 0, array]);
});
test('should support the `thisArg` argument', 1, function() {
var actual = _.dropWhile(array, function(num, index) {
return this[index] < 3;
}, array);
deepEqual(actual, [3, 4]);
});
test('should work with a "_.matches" style `predicate`', 1, function() {
deepEqual(_.dropWhile(objects, { 'b': 2 }), objects.slice(1));
});
test('should work with a "_.matchesProperty" style `predicate`', 1, function() {
deepEqual(_.dropWhile(objects, 'b', 2), objects.slice(1));
});
test('should work with a "_.property" style `predicate`', 1, function() {
deepEqual(_.dropWhile(objects, 'b'), objects.slice(2));
});
test('should work in a lazy chain sequence', 3, function() {
if (!isNpm) {
var wrapped = _(array).dropWhile(function(num) {
return num < 3;
});
deepEqual(wrapped.value(), [3, 4]);
deepEqual(wrapped.reverse().value(), [4, 3]);
strictEqual(wrapped.last(), 4);
}
else {
skipTest(3);
}
});
test('should work in a lazy chain sequence with `drop`', 1, function() {
if (!isNpm) {
var actual = _(array)
.dropWhile(function(num) { return num == 1; })
.drop()
.dropWhile(function(num) { return num == 3; })
.value();
deepEqual(actual, [4]);
}
else {
skipTest();
}
});
test('should provide the correct `predicate` arguments in a lazy chain sequence', 5, function() {
if (!isNpm) {
var args,
expected = [1, 0, [1, 4, 9, 16]];
_(array).dropWhile(function(value, index, array) {
args = slice.call(arguments);
}).value();
deepEqual(args, [1, 0, array]);
_(array).map(square).dropWhile(function(value, index, array) {
args = slice.call(arguments);
}).value();
deepEqual(args, expected);
_(array).map(square).dropWhile(function(value, index) {
args = slice.call(arguments);
}).value();
deepEqual(args, expected);
_(array).map(square).dropWhile(function(index) {
args = slice.call(arguments);
}).value();
deepEqual(args, [1]);
_(array).map(square).dropWhile(function() {
args = slice.call(arguments);
}).value();
deepEqual(args, expected);
}
else {
skipTest(5);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.endsWith');
(function() {
var string = 'abc';
test('should return `true` if a string ends with `target`', 1, function() {
strictEqual(_.endsWith(string, 'c'), true);
});
test('should return `false` if a string does not end with `target`', 1, function() {
strictEqual(_.endsWith(string, 'b'), false);
});
test('should work with a `position` argument', 1, function() {
strictEqual(_.endsWith(string, 'b', 2), true);
});
test('should work with `position` >= `string.length`', 4, function() {
_.each([3, 5, MAX_SAFE_INTEGER, Infinity], function(position) {
strictEqual(_.endsWith(string, 'c', position), true);
});
});
test('should treat falsey `position` values, except `undefined`, as `0`', 1, function() {
var expected = _.map(falsey, _.constant(true));
var actual = _.map(falsey, function(position) {
return _.endsWith(string, position === undefined ? 'c' : '', position);
});
deepEqual(actual, expected);
});
test('should treat a negative `position` as `0`', 6, function() {
_.each([-1, -3, -Infinity], function(position) {
ok(_.every(string, function(chr) {
return _.endsWith(string, chr, position) === false;
}));
strictEqual(_.endsWith(string, '', position), true);
});
});
test('should return `true` when `target` is an empty string regardless of `position`', 1, function() {
ok(_.every([-Infinity, NaN, -3, -1, 0, 1, 2, 3, 5, MAX_SAFE_INTEGER, Infinity], function(position) {
return _.endsWith(string, '', position, true);
}));
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.escape');
(function() {
var escaped = '&amp;&lt;&gt;&quot;&#39;&#96;\/',
unescaped = '&<>"\'`\/';
escaped += escaped;
unescaped += unescaped;
test('should escape values', 1, function() {
strictEqual(_.escape(unescaped), escaped);
});
test('should not escape the "/" character', 1, function() {
strictEqual(_.escape('/'), '/');
});
test('should handle strings with nothing to escape', 1, function() {
strictEqual(_.escape('abc'), 'abc');
});
test('should escape the same characters unescaped by `_.unescape`', 1, function() {
strictEqual(_.escape(_.unescape(escaped)), escaped);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.escapeRegExp');
(function() {
test('should escape values', 1, function() {
var escaped = '\\.\\*\\+\\?\\^\\$\\{\\}\\(\\)\\|\\[\\]\\/\\\\',
unescaped = '.*+?^${}()|[\]\/\\';
escaped += escaped;
unescaped += unescaped;
strictEqual(_.escapeRegExp(unescaped), escaped);
});
test('should handle strings with nothing to escape', 1, function() {
strictEqual(_.escapeRegExp('abc'), 'abc');
});
test('should work with `eval` and `Function`', 2, function() {
var string = '[lodash](https://lodash.com/)',
escaped = _.escapeRegExp(string),
regexp = eval('(/' + escaped + '/)');
ok(regexp.test(string));
regexp = Function('return /' + escaped + '/')();
ok(regexp.test(string));
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.every');
(function() {
test('should return `true` for empty collections', 1, function() {
var expected = _.map(empties, _.constant(true));
var actual = _.map(empties, function(value) {
try {
return _.every(value, _.identity);
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should return `true` if `predicate` returns truthy for all elements in the collection', 1, function() {
strictEqual(_.every([true, 1, 'a'], _.identity), true);
});
test('should return `false` as soon as `predicate` returns falsey', 1, function() {
strictEqual(_.every([true, null, true], _.identity), false);
});
test('should work with collections of `undefined` values (test in IE < 9)', 1, function() {
strictEqual(_.every([undefined, undefined, undefined], _.identity), false);
});
test('should use `_.identity` when `predicate` is nullish', 2, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant(false));
var actual = _.map(values, function(value, index) {
var array = [0];
return index ? _.every(array, value) : _.every(array);
});
deepEqual(actual, expected);
expected = _.map(values, _.constant(true));
actual = _.map(values, function(value, index) {
var array = [1];
return index ? _.every(array, value) : _.every(array);
});
deepEqual(actual, expected);
});
test('should work with a "_.property" style `predicate`', 2, function() {
var objects = [{ 'a': 0, 'b': 1 }, { 'a': 1, 'b': 2 }];
strictEqual(_.every(objects, 'a'), false);
strictEqual(_.every(objects, 'b'), true);
});
test('should work with a "_.matches" style `predicate`', 2, function() {
var objects = [{ 'a': 0, 'b': 0 }, { 'a': 0, 'b': 1 }];
strictEqual(_.every(objects, { 'a': 0 }), true);
strictEqual(_.every(objects, { 'b': 1 }), false);
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var actual = _.map([[1]], _.every);
deepEqual(actual, [true]);
});
test('should be aliased', 1, function() {
strictEqual(_.all, _.every);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('strict mode checks');
_.each(['assign', 'bindAll', 'defaults'], function(methodName) {
var func = _[methodName];
test('`_.' + methodName + '` should ' + (isStrict ? '' : 'not ') + 'throw strict mode errors', 1, function() {
if (freeze) {
var object = { 'a': undefined, 'b': function() {} },
pass = !isStrict;
freeze(object);
try {
if (methodName == 'bindAll') {
func(object);
} else {
func(object, { 'a': 1 });
}
} catch(e) {
pass = !pass;
}
ok(pass);
}
else {
skipTest();
}
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.fill');
(function() {
test('should use a default `start` of `0` and a default `end` of `array.length`', 1, function() {
var array = [1, 2, 3];
deepEqual(_.fill(array, 'a'), ['a', 'a', 'a']);
});
test('should use `undefined` for `value` if not provided', 2, function() {
var array = [1, 2, 3],
actual = _.fill(array);
deepEqual(actual, Array(3));
ok(_.every(actual, function(value, index) { return index in actual; }));
});
test('should work with a positive `start`', 1, function() {
var array = [1, 2, 3];
deepEqual(_.fill(array, 'a', 1), [1, 'a', 'a']);
});
test('should work with a `start` >= `array.length`', 4, function() {
_.each([3, 4, Math.pow(2, 32), Infinity], function(start) {
var array = [1, 2, 3];
deepEqual(_.fill(array, 'a', start), [1, 2, 3]);
});
});
test('should treat falsey `start` values as `0`', 1, function() {
var expected = _.map(falsey, _.constant(['a', 'a', 'a']));
var actual = _.map(falsey, function(start) {
var array = [1, 2, 3];
return _.fill(array, 'a', start);
});
deepEqual(actual, expected);
});
test('should work with a negative `start`', 1, function() {
var array = [1, 2, 3];
deepEqual(_.fill(array, 'a', -1), [1, 2, 'a']);
});
test('should work with a negative `start` <= negative `array.length`', 3, function() {
_.each([-3, -4, -Infinity], function(start) {
var array = [1, 2, 3];
deepEqual(_.fill(array, 'a', start), ['a', 'a', 'a']);
});
});
test('should work with `start` >= `end`', 2, function() {
_.each([2, 3], function(start) {
var array = [1, 2, 3];
deepEqual(_.fill(array, 'a', start, 2), [1, 2, 3]);
});
});
test('should work with a positive `end`', 1, function() {
var array = [1, 2, 3];
deepEqual(_.fill(array, 'a', 0, 1), ['a', 2, 3]);
});
test('should work with a `end` >= `array.length`', 4, function() {
_.each([3, 4, Math.pow(2, 32), Infinity], function(end) {
var array = [1, 2, 3];
deepEqual(_.fill(array, 'a', 0, end), ['a', 'a', 'a']);
});
});
test('should treat falsey `end` values, except `undefined`, as `0`', 1, function() {
var expected = _.map(falsey, function(value) {
return value === undefined ? ['a', 'a', 'a'] : [1, 2, 3];
});
var actual = _.map(falsey, function(end) {
var array = [1, 2, 3];
return _.fill(array, 'a', 0, end);
});
deepEqual(actual, expected);
});
test('should work with a negative `end`', 1, function() {
var array = [1, 2, 3];
deepEqual(_.fill(array, 'a', 0, -1), ['a', 'a', 3]);
});
test('should work with a negative `end` <= negative `array.length`', 3, function() {
_.each([-3, -4, -Infinity], function(end) {
var array = [1, 2, 3];
deepEqual(_.fill(array, 'a', 0, end), [1, 2, 3]);
});
});
test('should coerce `start` and `end` to integers', 1, function() {
var positions = [[0.1, 1.1], ['0', 1], [0, '1'], ['1'], [NaN, 1], [1, NaN]];
var actual = _.map(positions, function(pos) {
var array = [1, 2, 3];
return _.fill.apply(_, [array, 'a'].concat(pos));
});
deepEqual(actual, [['a', 2, 3], ['a', 2, 3], ['a', 2, 3], [1, 'a', 'a'], ['a', 2, 3], [1, 2, 3]]);
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var array = [[1, 2], [3, 4]],
actual = _.map(array, _.fill);
deepEqual(actual, [[0, 0], [1, 1]]);
});
test('should return a wrapped value when chaining', 3, function() {
if (!isNpm) {
var array = [1, 2, 3],
wrapped = _(array).fill('a'),
actual = wrapped.value();
ok(wrapped instanceof _);
deepEqual(actual, ['a', 'a', 'a']);
strictEqual(actual, array);
}
else {
skipTest(3);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.filter');
(function() {
var array = [1, 2, 3];
test('should return elements `predicate` returns truthy for', 1, function() {
var actual = _.filter(array, function(num) {
return num % 2;
});
deepEqual(actual, [1, 3]);
});
test('should iterate over an object with numeric keys (test in Mobile Safari 8)', 1, function() {
// Trigger a Mobile Safari 8 JIT bug.
// See https://github.com/lodash/lodash/issues/799.
var counter = 0,
object = { '1': 'foo', '8': 'bar', '50': 'baz' };
_.times(1000, function() {
_.filter([], _.constant(true));
});
_.filter(object, function() {
counter++;
return true;
});
strictEqual(counter, 3);
});
test('should be aliased', 1, function() {
strictEqual(_.select, _.filter);
});
}());
/*--------------------------------------------------------------------------*/
_.each(['find', 'findLast', 'findIndex', 'findLastIndex', 'findKey', 'findLastKey'], function(methodName) {
QUnit.module('lodash.' + methodName);
var func = _[methodName];
(function() {
var objects = [
{ 'a': 0, 'b': 0 },
{ 'a': 1, 'b': 1 },
{ 'a': 2, 'b': 2 }
];
var expected = ({
'find': [objects[1], undefined, objects[2], objects[1]],
'findLast': [objects[2], undefined, objects[2], objects[2]],
'findIndex': [1, -1, 2, 1],
'findLastIndex': [2, -1, 2, 2],
'findKey': ['1', undefined, '2', '1'],
'findLastKey': ['2', undefined, '2', '2']
})[methodName];
test('should return the found value', 1, function() {
strictEqual(func(objects, function(object) { return object.a; }), expected[0]);
});
test('should work with a `thisArg`', 1, function() {
strictEqual(func(objects, function(object, index) { return this[index].a; }, objects), expected[0]);
});
test('should return `' + expected[1] + '` if value is not found', 1, function() {
strictEqual(func(objects, function(object) { return object.a === 3; }), expected[1]);
});
test('should work with a "_.matches" style `predicate`', 1, function() {
strictEqual(func(objects, { 'b': 2 }), expected[2]);
});
test('should work with a "_.matchesProperty" style `predicate`', 1, function() {
strictEqual(func(objects, 'b', 2), expected[2]);
});
test('should work with a "_.property" style `predicate`', 1, function() {
strictEqual(func(objects, 'b'), expected[3]);
});
test('should return `' + expected[1] + '` for empty collections', 1, function() {
var emptyValues = _.endsWith(methodName, 'Index') ? _.reject(empties, _.isPlainObject) : empties,
expecting = _.map(emptyValues, _.constant(expected[1]));
var actual = _.map(emptyValues, function(value) {
try {
return func(value, { 'a': 3 });
} catch(e) {}
});
deepEqual(actual, expecting);
});
}());
(function() {
var expected = ({
'find': 1,
'findLast': 2,
'findKey': 'a',
'findLastKey': 'b'
})[methodName];
if (expected != null) {
test('should work with an object for `collection`', 1, function() {
var actual = func({ 'a': 1, 'b': 2, 'c': 3 }, function(num) {
return num < 3;
});
strictEqual(actual, expected);
});
}
}());
(function() {
var expected = ({
'find': 'a',
'findLast': 'b',
'findIndex': 0,
'findLastIndex': 1
})[methodName];
if (expected != null) {
test('should work with a string for `collection`', 1, function() {
var actual = func('abc', function(chr, index) {
return index < 2;
});
strictEqual(actual, expected);
});
}
if (methodName == 'find') {
test('should be aliased', 1, function() {
strictEqual(_.detect, func);
});
}
}());
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.findWhere');
(function() {
var objects = [
{ 'a': 1 },
{ 'a': 1 },
{ 'a': 1, 'b': 2 },
{ 'a': 2, 'b': 2 },
{ 'a': 3 }
];
test('should filter by `source` properties', 6, function() {
strictEqual(_.findWhere(objects, { 'a': 1 }), objects[0]);
strictEqual(_.findWhere(objects, { 'a': 2 }), objects[3]);
strictEqual(_.findWhere(objects, { 'a': 3 }), objects[4]);
strictEqual(_.findWhere(objects, { 'b': 1 }), undefined);
strictEqual(_.findWhere(objects, { 'b': 2 }), objects[2]);
strictEqual(_.findWhere(objects, { 'a': 1, 'b': 2 }), objects[2]);
});
test('should work with a function for `source`', 1, function() {
function source() {}
source.a = 2;
strictEqual(_.findWhere(objects, source), objects[3]);
});
test('should match all elements when provided an empty `source`', 1, function() {
var expected = _.map(empties, _.constant(true));
var actual = _.map(empties, function(value) {
return _.findWhere(objects, value) === objects[0];
});
deepEqual(actual, expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.first');
(function() {
var array = [1, 2, 3];
test('should return the first element', 1, function() {
strictEqual(_.first(array), 1);
});
test('should return `undefined` when querying empty arrays', 1, function() {
var array = [];
array['-1'] = 1;
strictEqual(_.first(array), undefined);
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
actual = _.map(array, _.first);
deepEqual(actual, [1, 4, 7]);
});
test('should return an unwrapped value when implicitly chaining', 1, function() {
if (!isNpm) {
strictEqual(_(array).first(), 1);
}
else {
skipTest();
}
});
test('should return a wrapped value when explicitly chaining', 1, function() {
if (!isNpm) {
ok(_(array).chain().first() instanceof _);
}
else {
skipTest();
}
});
test('should work in a lazy chain sequence', 1, function() {
if (!isNpm) {
var array = [1, 2, 3, 4];
var wrapped = _(array).filter(function(value) {
return value % 2 == 0;
});
strictEqual(wrapped.first(), 2);
}
else {
skipTest();
}
});
test('should be aliased', 1, function() {
strictEqual(_.head, _.first);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.take');
(function() {
var array = [1, 2, 3];
test('should take the first two elements', 1, function() {
deepEqual(_.take(array, 2), [1, 2]);
});
test('should treat falsey `n` values, except nullish, as `0`', 1, function() {
var expected = _.map(falsey, function(value) {
return value == null ? [1] : [];
});
var actual = _.map(falsey, function(n) {
return _.take(array, n);
});
deepEqual(actual, expected);
});
test('should return an empty array when `n` < `1`', 3, function() {
_.each([0, -1, -Infinity], function(n) {
deepEqual(_.take(array, n), []);
});
});
test('should return all elements when `n` >= `array.length`', 4, function() {
_.each([3, 4, Math.pow(2, 32), Infinity], function(n) {
deepEqual(_.take(array, n), array);
});
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
actual = _.map(array, _.take);
deepEqual(actual, [[1], [4], [7]]);
});
test('should work in a lazy chain sequence', 6, function() {
if (!isNpm) {
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9 , 10],
values = [],
predicate = function(value) { values.push(value); return value > 2; },
actual = _(array).take(2).take().value();
deepEqual(actual, [1]);
actual = _(array).filter(predicate).take(2).take().value();
deepEqual(actual, [3]);
deepEqual(values, array.slice(0, 3));
actual = _(array).take(6).takeRight(4).take(2).takeRight().value();
deepEqual(actual, [4]);
values = [];
actual = _(array).take(array.length - 1).filter(predicate).take(6).takeRight(4).take(2).takeRight().value();
deepEqual(actual, [6]);
deepEqual(values, array.slice(0, -2));
}
else {
skipTest(6);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.takeRight');
(function() {
var array = [1, 2, 3];
test('should take the last two elements', 1, function() {
deepEqual(_.takeRight(array, 2), [2, 3]);
});
test('should treat falsey `n` values, except nullish, as `0`', 1, function() {
var expected = _.map(falsey, function(value) {
return value == null ? [3] : [];
});
var actual = _.map(falsey, function(n) {
return _.takeRight(array, n);
});
deepEqual(actual, expected);
});
test('should return an empty array when `n` < `1`', 3, function() {
_.each([0, -1, -Infinity], function(n) {
deepEqual(_.takeRight(array, n), []);
});
});
test('should return all elements when `n` >= `array.length`', 4, function() {
_.each([3, 4, Math.pow(2, 32), Infinity], function(n) {
deepEqual(_.takeRight(array, n), array);
});
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
actual = _.map(array, _.takeRight);
deepEqual(actual, [[3], [6], [9]]);
});
test('should work in a lazy chain sequence', 6, function() {
if (!isNpm) {
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
values = [],
predicate = function(value) { values.push(value); return value < 9; },
actual = _(array).takeRight(2).takeRight().value();
deepEqual(actual, [10]);
actual = _(array).filter(predicate).takeRight(2).takeRight().value();
deepEqual(actual, [8]);
deepEqual(values, array);
actual = _(array).takeRight(6).take(4).takeRight(2).take().value();
deepEqual(actual, [7]);
values = [];
actual = _(array).filter(predicate).takeRight(6).take(4).takeRight(2).take().value();
deepEqual(actual, [5]);
deepEqual(values, array);
}
else {
skipTest(6);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.takeRightWhile');
(function() {
var array = [1, 2, 3, 4];
var objects = [
{ 'a': 0, 'b': 0 },
{ 'a': 1, 'b': 1 },
{ 'a': 2, 'b': 2 }
];
test('should take elements while `predicate` returns truthy', 1, function() {
var actual = _.takeRightWhile(array, function(num) {
return num > 2;
});
deepEqual(actual, [3, 4]);
});
test('should provide the correct `predicate` arguments', 1, function() {
var args;
_.takeRightWhile(array, function() {
args = slice.call(arguments);
});
deepEqual(args, [4, 3, array]);
});
test('should support the `thisArg` argument', 1, function() {
var actual = _.takeRightWhile(array, function(num, index) {
return this[index] > 2;
}, array);
deepEqual(actual, [3, 4]);
});
test('should work with a "_.matches" style `predicate`', 1, function() {
deepEqual(_.takeRightWhile(objects, { 'b': 2 }), objects.slice(2));
});
test('should work with a "_.matchesProperty" style `predicate`', 1, function() {
deepEqual(_.takeRightWhile(objects, 'b', 2), objects.slice(2));
});
test('should work with a "_.property" style `predicate`', 1, function() {
deepEqual(_.takeRightWhile(objects, 'b'), objects.slice(1));
});
test('should work in a lazy chain sequence', 3, function() {
if (!isNpm) {
var wrapped = _(array).takeRightWhile(function(num) {
return num > 2;
});
deepEqual(wrapped.value(), [3, 4]);
deepEqual(wrapped.reverse().value(), [4, 3]);
strictEqual(wrapped.last(), 4);
}
else {
skipTest(3);
}
});
test('should provide the correct `predicate` arguments in a lazy chain sequence', 5, function() {
if (!isNpm) {
var args,
expected = [16, 3, [1, 4, 9 , 16]];
_(array).takeRightWhile(function(value, index, array) {
args = slice.call(arguments)
}).value();
deepEqual(args, [4, 3, array]);
_(array).map(square).takeRightWhile(function(value, index, array) {
args = slice.call(arguments)
}).value();
deepEqual(args, expected);
_(array).map(square).takeRightWhile(function(value, index) {
args = slice.call(arguments)
}).value();
deepEqual(args, expected);
_(array).map(square).takeRightWhile(function(index) {
args = slice.call(arguments);
}).value();
deepEqual(args, [16]);
_(array).map(square).takeRightWhile(function() {
args = slice.call(arguments);
}).value();
deepEqual(args, expected);
}
else {
skipTest(5);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.takeWhile');
(function() {
var array = [1, 2, 3, 4];
var objects = [
{ 'a': 2, 'b': 2 },
{ 'a': 1, 'b': 1 },
{ 'a': 0, 'b': 0 }
];
test('should take elements while `predicate` returns truthy', 1, function() {
var actual = _.takeWhile(array, function(num) {
return num < 3;
});
deepEqual(actual, [1, 2]);
});
test('should provide the correct `predicate` arguments', 1, function() {
var args;
_.takeWhile(array, function() {
args = slice.call(arguments);
});
deepEqual(args, [1, 0, array]);
});
test('should support the `thisArg` argument', 1, function() {
var actual = _.takeWhile(array, function(num, index) {
return this[index] < 3;
}, array);
deepEqual(actual, [1, 2]);
});
test('should work with a "_.matches" style `predicate`', 1, function() {
deepEqual(_.takeWhile(objects, { 'b': 2 }), objects.slice(0, 1));
});
test('should work with a "_.matchesProperty" style `predicate`', 1, function() {
deepEqual(_.takeWhile(objects, 'b', 2), objects.slice(0, 1));
});
test('should work with a "_.property" style `predicate`', 1, function() {
deepEqual(_.takeWhile(objects, 'b'), objects.slice(0, 2));
});
test('should work in a lazy chain sequence', 3, function() {
if (!isNpm) {
var wrapped = _(array).takeWhile(function(num) {
return num < 3;
});
deepEqual(wrapped.value(), [1, 2]);
deepEqual(wrapped.reverse().value(), [2, 1]);
strictEqual(wrapped.last(), 2);
}
else {
skipTest(3);
}
});
test('should work in a lazy chain sequence with `take`', 1, function() {
if (!isNpm) {
var actual = _(array)
.takeWhile(function(num) { return num < 4; })
.take(2)
.takeWhile(function(num) { return num == 1; })
.value();
deepEqual(actual, [1]);
}
else {
skipTest();
}
});
test('should provide the correct `predicate` arguments in a lazy chain sequence', 5, function() {
if (!isNpm) {
var args,
expected = [1, 0, [1, 4, 9, 16]];
_(array).takeWhile(function(value, index, array) {
args = slice.call(arguments);
}).value();
deepEqual(args, [1, 0, array]);
_(array).map(square).takeWhile(function(value, index, array) {
args = slice.call(arguments);
}).value();
deepEqual(args, expected);
_(array).map(square).takeWhile(function(value, index) {
args = slice.call(arguments);
}).value();
deepEqual(args, expected);
_(array).map(square).takeWhile(function(value) {
args = slice.call(arguments);
}).value();
deepEqual(args, [1]);
_(array).map(square).takeWhile(function() {
args = slice.call(arguments);
}).value();
deepEqual(args, expected);
}
else {
skipTest(5);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('flatten methods');
(function() {
var args = arguments;
test('should perform a shallow flatten', 1, function() {
var array = [[['a']], [['b']]];
deepEqual(_.flatten(array), [['a'], ['b']]);
});
test('should work with `isDeep`', 2, function() {
var array = [[['a']], [['b']]],
expected = ['a', 'b'];
deepEqual(_.flatten(array, true), expected);
deepEqual(_.flattenDeep(array), expected);
});
test('should flatten `arguments` objects', 3, function() {
var array = [args, [args]],
expected = [1, 2, 3, args];
deepEqual(_.flatten(array), expected);
expected = [1, 2, 3, 1, 2, 3];
deepEqual(_.flatten(array, true), expected);
deepEqual(_.flattenDeep(array), expected);
});
test('should work as an iteratee for methods like `_.map`', 2, function() {
var array = [[[['a']]], [[['b']]]];
deepEqual(_.map(array, _.flatten), [[['a']], [['b']]]);
deepEqual(_.map(array, _.flattenDeep), [['a'], ['b']]);
});
test('should treat sparse arrays as dense', 6, function() {
var array = [[1, 2, 3], Array(3)],
expected = [1, 2, 3];
expected.push(undefined, undefined, undefined);
_.each([_.flatten(array), _.flatten(array, true), _.flattenDeep(array)], function(actual) {
deepEqual(actual, expected);
ok('4' in actual);
});
});
test('should work with extremely large arrays', 3, function() {
// Test in modern browsers only to avoid browser hangs.
_.times(3, function(index) {
if (freeze) {
var expected = Array(5e5);
try {
if (index) {
var actual = actual == 1 ? _.flatten([expected], true) : _.flattenDeep([expected]);
} else {
actual = _.flatten(expected);
}
deepEqual(actual, expected);
} catch(e) {
ok(false, e.message);
}
}
else {
skipTest();
}
});
});
test('should work with empty arrays', 3, function() {
var array = [[], [[]], [[], [[[]]]]],
expected = [[], [], [[[]]]];
deepEqual(_.flatten(array), expected);
expected = [];
deepEqual(_.flatten(array, true), expected);
deepEqual(_.flattenDeep(array), expected);
});
test('should support flattening of nested arrays', 3, function() {
var array = [1, [2], [3, [4]]],
expected = [1, 2, 3, [4]];
deepEqual(_.flatten(array), expected);
expected = [1, 2, 3, 4];
deepEqual(_.flatten(array, true), expected);
deepEqual(_.flattenDeep(array), expected);
});
test('should return an empty array for non array-like objects', 3, function() {
var expected = [];
deepEqual(_.flatten({ 'a': 1 }), expected);
deepEqual(_.flatten({ 'a': 1 }, true), expected);
deepEqual(_.flattenDeep({ 'a': 1 }), expected);
});
test('should return a wrapped value when chaining', 6, function() {
if (!isNpm) {
var wrapped = _([1, [2], [3, [4]]]),
actual = wrapped.flatten(),
expected = [1, 2, 3, [4]];
ok(actual instanceof _);
deepEqual(actual.value(), expected);
expected = [1, 2, 3, 4];
actual = wrapped.flatten(true);
ok(actual instanceof _);
deepEqual(actual.value(), expected);
actual = wrapped.flattenDeep();
ok(actual instanceof _);
deepEqual(actual.value(), expected);
}
else {
skipTest(6);
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.forEach');
(function() {
test('should be aliased', 1, function() {
strictEqual(_.each, _.forEach);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.forEachRight');
(function() {
test('should be aliased', 1, function() {
strictEqual(_.eachRight, _.forEachRight);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('forIn methods');
_.each(['forIn', 'forInRight'], function(methodName) {
var func = _[methodName];
test('`_.' + methodName + '` iterates over inherited properties', 1, function() {
function Foo() { this.a = 1; }
Foo.prototype.b = 2;
var keys = [];
func(new Foo, function(value, key) { keys.push(key); });
deepEqual(keys.sort(), ['a', 'b']);
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('forOwn methods');
_.each(['forOwn', 'forOwnRight'], function(methodName) {
var func = _[methodName];
test('iterates over the `length` property', 1, function() {
var object = { '0': 'zero', '1': 'one', 'length': 2 },
props = [];
func(object, function(value, prop) { props.push(prop); });
deepEqual(props.sort(), ['0', '1', 'length']);
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('iteration methods');
(function() {
var methods = [
'_baseEach',
'countBy',
'every',
'filter',
'find',
'findIndex',
'findKey',
'findLast',
'findLastIndex',
'findLastKey',
'forEach',
'forEachRight',
'forIn',
'forInRight',
'forOwn',
'forOwnRight',
'groupBy',
'indexBy',
'map',
'mapKeys',
'mapValues',
'max',
'min',
'omit',
'partition',
'pick',
'reject',
'some'
];
var arrayMethods = [
'findIndex',
'findLastIndex'
];
var collectionMethods = [
'_baseEach',
'countBy',
'every',
'filter',
'find',
'findLast',
'forEach',
'forEachRight',
'groupBy',
'indexBy',
'map',
'max',
'min',
'partition',
'reduce',
'reduceRight',
'reject',
'some'
];
var forInMethods = [
'forIn',
'forInRight',
'omit',
'pick'
];
var iterationMethods = [
'_baseEach',
'forEach',
'forEachRight',
'forIn',
'forInRight',
'forOwn',
'forOwnRight'
]
var objectMethods = [
'findKey',
'findLastKey',
'forIn',
'forInRight',
'forOwn',
'forOwnRight',
'mapKeys',
'mapValues',
'omit',
'pick'
];
var rightMethods = [
'findLast',
'findLastIndex',
'findLastKey',
'forEachRight',
'forInRight',
'forOwnRight'
];
var unwrappedMethods = [
'every',
'find',
'findIndex',
'findKey',
'findLast',
'findLastIndex',
'findLastKey',
'max',
'min',
'some'
];
_.each(methods, function(methodName) {
var array = [1, 2, 3],
func = _[methodName],
isFind = /^find/.test(methodName),
isSome = methodName == 'some';
test('`_.' + methodName + '` should provide the correct `iteratee` arguments', 1, function() {
if (func) {
var args,
expected = [1, 0, array];
func(array, function() {
args || (args = slice.call(arguments));
});
if (_.includes(rightMethods, methodName)) {
expected[0] = 3;
expected[1] = 2;
}
if (_.includes(objectMethods, methodName)) {
expected[1] += '';
}
deepEqual(args, expected);
}
else {
skipTest();
}
});
test('`_.' + methodName + '` should support the `thisArg` argument', 2, function() {
if (methodName != '_baseEach') {
var actual,
callback = function(num, index) { actual = this[index]; };
func([1], callback, [2]);
strictEqual(actual, 2);
func({ 'a': 1 }, callback, { 'a': 2 });
strictEqual(actual, 2);
}
else {
skipTest(2);
}
});
test('`_.' + methodName + '` should treat sparse arrays as dense', 1, function() {
if (func) {
var array = [1];
array[2] = 3;
var expected = [[1, 0, array], [undefined, 1, array], [3, 2, array]];
if (_.includes(objectMethods, methodName)) {
expected = _.map(expected, function(args) {
args[1] += '';
return args;
});
}
if (_.includes(rightMethods, methodName)) {
expected.reverse();
}
var argsList = [];
func(array, function() {
argsList.push(slice.call(arguments));
return !(isFind || isSome);
});
deepEqual(argsList, expected);
}
else {
skipTest();
}
});
});
_.each(_.difference(methods, objectMethods), function(methodName) {
var array = [1, 2, 3],
func = _[methodName],
isEvery = methodName == 'every';
array.a = 1;
test('`_.' + methodName + '` should not iterate custom properties on arrays', 1, function() {
if (func) {
var keys = [];
func(array, function(value, key) {
keys.push(key);
return isEvery;
});
ok(!_.includes(keys, 'a'));
}
else {
skipTest();
}
});
});
_.each(_.difference(methods, unwrappedMethods), function(methodName) {
var array = [1, 2, 3],
func = _[methodName],
isBaseEach = methodName == '_baseEach';
test('`_.' + methodName + '` should return a wrapped value when implicitly chaining', 1, function() {
if (!(isBaseEach || isNpm)) {
var wrapped = _(array)[methodName](_.noop);
ok(wrapped instanceof _);
}
else {
skipTest();
}
});
});
_.each(unwrappedMethods, function(methodName) {
var array = [1, 2, 3],
func = _[methodName];
test('`_.' + methodName + '` should return an unwrapped value when implicitly chaining', 1, function() {
if (!isNpm) {
var wrapped = _(array)[methodName](_.noop);
ok(!(wrapped instanceof _));
}
else {
skipTest();
}
});
test('`_.' + methodName + '` should return a wrapped value when implicitly chaining', 1, function() {
if (!isNpm) {
var wrapped = _(array).chain()[methodName](_.noop);
ok(wrapped instanceof _);
}
else {
skipTest();
}
});
});
_.each(_.difference(methods, arrayMethods, forInMethods), function(methodName) {
var array = [1, 2, 3],
func = _[methodName];
test('`_.' + methodName + '` iterates over own properties of objects', 1, function() {
function Foo() { this.a = 1; }
Foo.prototype.b = 2;
if (func) {
var keys = [];
func(new Foo, function(value, key) { keys.push(key); });
deepEqual(keys, ['a']);
}
else {
skipTest();
}
});
});
_.each(iterationMethods, function(methodName) {
var array = [1, 2, 3],
func = _[methodName],
isBaseEach = methodName == '_baseEach',
isObject = _.includes(objectMethods, methodName),
isRight = _.includes(rightMethods, methodName);
test('`_.' + methodName + '` should return the collection', 1, function() {
if (func) {
strictEqual(func(array, Boolean), array);
}
else {
skipTest();
}
});
test('`_.' + methodName + '` should not return the existing wrapped value when chaining', 1, function() {
if (!(isBaseEach || isNpm)) {
var wrapped = _(array);
notStrictEqual(wrapped[methodName](_.noop), wrapped);
}
else {
skipTest();
}
});
_.each({
'literal': 'abc',
'object': Object('abc')
},
function(collection, key) {
test('`_.' + methodName + '` should work with a string ' + key + ' for `collection` (test in IE < 9)', 6, function() {
if (func) {
var args,
values = [],
expectedChars = ['a', 'b', 'c'];
var expectedArgs = isObject
? (isRight ? ['c', '2', collection] : ['a', '0', collection])
: (isRight ? ['c', 2, collection] : ['a', 0, collection]);
var actual = func(collection, function(value) {
args || (args = slice.call(arguments));
values.push(value);
});
var stringObject = args[2];
ok(_.isString(stringObject));
ok(_.isObject(stringObject));
deepEqual([stringObject[0], stringObject[1], stringObject[2]], expectedChars);
deepEqual(args, expectedArgs);
deepEqual(values, isRight ? ['c', 'b', 'a'] : expectedChars);
strictEqual(actual, collection);
}
else {
skipTest(6);
}
});
});
});
_.each(collectionMethods, function(methodName) {
var func = _[methodName];
test('`_.' + methodName + '` should use `isLength` to determine whether a value is array-like', 2, function() {
if (func) {
var isIteratedAsObject = function(length) {
var result = false;
func({ 'length': length }, function() { result = true; }, 0);
return result;
};
var values = [-1, '1', 1.1, Object(1), MAX_SAFE_INTEGER + 1],
expected = _.map(values, _.constant(true)),
actual = _.map(values, isIteratedAsObject);
deepEqual(actual, expected);
ok(!isIteratedAsObject(0));
}
else {
skipTest(2);
}
});
});
_.each(methods, function(methodName) {
var array = [1, 2, 3],
func = _[methodName],
isFind = /^find/.test(methodName),
isSome = methodName == 'some';
test('`_.' + methodName + '` should ignore changes to `array.length`', 1, function() {
if (func) {
var count = 0,
array = [1];
func(array, function() {
if (++count == 1) {
array.push(2);
}
return !(isFind || isSome);
}, array);
strictEqual(count, 1);
}
else {
skipTest();
}
});
});
_.each(_.difference(_.union(methods, collectionMethods), arrayMethods), function(methodName) {
var func = _[methodName],
isFind = /^find/.test(methodName),
isSome = methodName == 'some';
test('`_.' + methodName + '` should ignore added `object` properties', 1, function() {
if (func) {
var count = 0,
object = { 'a': 1 };
func(object, function() {
if (++count == 1) {
object.b = 2;
}
return !(isFind || isSome);
}, object);
strictEqual(count, 1);
}
else {
skipTest();
}
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('collection iteration bugs');
_.each(['forEach', 'forEachRight', 'forIn', 'forInRight', 'forOwn', 'forOwnRight'], function(methodName) {
var func = _[methodName];
test('`_.' + methodName + '` fixes the JScript `[[DontEnum]]` bug (test in IE < 9)', 1, function() {
var props = [];
func(shadowObject, function(value, prop) { props.push(prop); });
deepEqual(props.sort(), shadowProps);
});
test('`_.' + methodName + '` skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', 2, function() {
function Foo() {}
Foo.prototype.a = 1;
var props = [];
function callback(value, prop) { props.push(prop); }
func(Foo, callback);
deepEqual(props, []);
props.length = 0;
Foo.prototype = { 'a': 1 };
func(Foo, callback);
deepEqual(props, []);
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('object assignments');
_.each(['assign', 'defaults', 'merge'], function(methodName) {
var func = _[methodName],
isDefaults = methodName == 'defaults';
test('`_.' + methodName + '` should pass thru falsey `object` values', 1, function() {
var actual = _.map(falsey, function(value, index) {
return index ? func(value) : func();
});
deepEqual(actual, falsey);
});
test('`_.' + methodName + '` should assign own source properties', 1, function() {
function Foo() {
this.a = 1;
}
Foo.prototype.b = 2;
deepEqual(func({}, new Foo), { 'a': 1 });
});
test('`_.' + methodName + '` should assign problem JScript properties (test in IE < 9)', 1, function() {
var object = {
'constructor': '0',
'hasOwnProperty': '1',
'isPrototypeOf': '2',
'propertyIsEnumerable': undefined,
'toLocaleString': undefined,
'toString': undefined,
'valueOf': undefined
};
var source = {
'propertyIsEnumerable': '3',
'toLocaleString': '4',
'toString': '5',
'valueOf': '6'
};
deepEqual(func(object, source), shadowObject);
});
test('`_.' + methodName + '` skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', 2, function() {
function Foo() {}
Foo.a = 1;
Foo.prototype.b = 2;
var expected = { 'a': 1 };
deepEqual(func({}, Foo), expected);
Foo.prototype = { 'b': 2 };
deepEqual(func({}, Foo), expected);
});
test('`_.' + methodName + '` should not error on nullish sources (test in IE < 9)', 1, function() {
try {
deepEqual(func({ 'a': 1 }, undefined, { 'b': 2 }, null), { 'a': 1, 'b': 2 });
} catch(e) {
ok(false, e.message);
}
});
test('`_.' + methodName + '` should not error when `object` is nullish and source objects are provided', 1, function() {
var expected = _.times(2, _.constant(true));
var actual = _.map([null, undefined], function(value) {
try {
return _.isEqual(func(value, { 'a': 1 }), value);
} catch(e) {
return false;
}
});
deepEqual(actual, expected);
});
test('`_.' + methodName + '` should work as an iteratee for methods like `_.reduce`', 1, function() {
var array = [{ 'a': 1 }, { 'b': 2 }, { 'c': 3 }],
expected = { 'a': 1, 'b': 2, 'c': 3 };
expected.a = isDefaults ? 0 : 1;
deepEqual(_.reduce(array, func, { 'a': 0 }), expected);
});
test('`_.' + methodName + '` should not return the existing wrapped value when chaining', 1, function() {
if (!isNpm) {
var wrapped = _({ 'a': 1 });
notStrictEqual(wrapped[methodName]({ 'b': 2 }), wrapped);
}
else {
skipTest();
}
});
});
_.each(['assign', 'merge'], function(methodName) {
var func = _[methodName],
isMerge = methodName == 'merge';
test('`_.' + methodName + '` should provide the correct `customizer` arguments', 3, function() {
var args,
object = { 'a': 1 },
source = { 'a': 2 };
func(object, source, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [1, 2, 'a', object, source], 'primitive property values');
args = null;
object = { 'a': 1 };
source = { 'b': 2 };
func(object, source, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [undefined, 2, 'b', object, source], 'missing destination property');
var argsList = [],
objectValue = [1, 2],
sourceValue = { 'b': 2 };
object = { 'a': objectValue };
source = { 'a': sourceValue };
func(object, source, function() {
argsList.push(slice.call(arguments));
});
var expected = [[objectValue, sourceValue, 'a', object, source]];
if (isMerge) {
expected.push([undefined, 2, 'b', sourceValue, sourceValue]);
}
deepEqual(argsList, expected, 'object property values');
});
test('`_.' + methodName + '` should support the `thisArg` argument', 1, function() {
var actual = func({}, { 'a': 0 }, function(a, b) {
return this[b];
}, [2]);
deepEqual(actual, { 'a': 2 });
});
test('`_.' + methodName + '` should not treat `object` as `source`', 1, function() {
function Foo() {}
Foo.prototype.a = 1;
var actual = func(new Foo, { 'b': 2 });
ok(!_.has(actual, 'a'));
});
test('`_.' + methodName + '` should not treat the second argument as a `customizer` callback', 2, function() {
function callback() {}
callback.b = 2;
var actual = func({ 'a': 1 }, callback);
deepEqual(actual, { 'a': 1, 'b': 2 });
actual = func({ 'a': 1 }, callback, { 'c': 3 });
deepEqual(actual, { 'a': 1, 'b': 2, 'c': 3 });
});
test('`_.' + methodName + '` should not assign the `customizer` result if it is the same as the destination value', 4, function() {
_.each(['a', ['a'], { 'a': 1 }, NaN], function(value) {
if (defineProperty) {
var object = {},
pass = true;
defineProperty(object, 'a', {
'get': _.constant(value),
'set': function() { pass = false; }
});
func(object, { 'a': value }, _.identity);
ok(pass);
}
else {
skipTest();
}
});
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('exit early');
_.each(['_baseEach', 'forEach', 'forEachRight', 'forIn', 'forInRight', 'forOwn', 'forOwnRight', 'transform'], function(methodName) {
var func = _[methodName];
test('`_.' + methodName + '` can exit early when iterating arrays', 1, function() {
if (func) {
var array = [1, 2, 3],
values = [];
func(array, function(value, other) {
values.push(_.isArray(value) ? other : value);
return false;
});
deepEqual(values, [_.endsWith(methodName, 'Right') ? 3 : 1]);
}
else {
skipTest();
}
});
test('`_.' + methodName + '` can exit early when iterating objects', 1, function() {
if (func) {
var object = { 'a': 1, 'b': 2, 'c': 3 },
values = [];
func(object, function(value, other) {
values.push(_.isArray(value) ? other : value);
return false;
});
strictEqual(values.length, 1);
}
else {
skipTest();
}
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('`__proto__` property bugs');
(function() {
test('internal data objects should work with the `__proto__` key', 4, function() {
var stringLiteral = '__proto__',
stringObject = Object(stringLiteral),
expected = [stringLiteral, stringObject];
var largeArray = _.times(LARGE_ARRAY_SIZE, function(count) {
return count % 2 ? stringObject : stringLiteral;
});
deepEqual(_.difference(largeArray, largeArray), []);
deepEqual(_.intersection(largeArray, largeArray), expected);
deepEqual(_.uniq(largeArray), expected);
deepEqual(_.without.apply(_, [largeArray].concat(largeArray)), []);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.functions');
(function() {
test('should return the function names of an object', 1, function() {
var object = { 'a': 'a', 'b': _.identity, 'c': /x/, 'd': _.each };
deepEqual(_.functions(object).sort(), ['b', 'd']);
});
test('should include inherited functions', 1, function() {
function Foo() {
this.a = _.identity;
this.b = 'b';
}
Foo.prototype.c = _.noop;
deepEqual(_.functions(new Foo).sort(), ['a', 'c']);
});
test('should be aliased', 1, function() {
strictEqual(_.methods, _.functions);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.groupBy');
(function() {
var array = [4.2, 6.1, 6.4];
test('should use `_.identity` when `iteratee` is nullish', 1, function() {
var array = [4, 6, 6],
values = [, null, undefined],
expected = _.map(values, _.constant({ '4': [4], '6': [6, 6] }));
var actual = _.map(values, function(value, index) {
return index ? _.groupBy(array, value) : _.groupBy(array);
});
deepEqual(actual, expected);
});
test('should provide the correct `iteratee` arguments', 1, function() {
var args;
_.groupBy(array, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [4.2, 0, array]);
});
test('should support the `thisArg` argument', 1, function() {
var actual = _.groupBy(array, function(num) {
return this.floor(num);
}, Math);
deepEqual(actual, { '4': [4.2], '6': [6.1, 6.4] });
});
test('should work with a "_.property" style `iteratee`', 1, function() {
var actual = _.groupBy(['one', 'two', 'three'], 'length');
deepEqual(actual, { '3': ['one', 'two'], '5': ['three'] });
});
test('should only add values to own, not inherited, properties', 2, function() {
var actual = _.groupBy([4.2, 6.1, 6.4], function(num) {
return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor';
});
deepEqual(actual.constructor, [4.2]);
deepEqual(actual.hasOwnProperty, [6.1, 6.4]);
});
test('should work with a number for `iteratee`', 2, function() {
var array = [
[1, 'a'],
[2, 'a'],
[2, 'b']
];
deepEqual(_.groupBy(array, 0), { '1': [[1, 'a']], '2': [[2, 'a'], [2, 'b']] });
deepEqual(_.groupBy(array, 1), { 'a': [[1, 'a'], [2, 'a']], 'b': [[2, 'b']] });
});
test('should work with an object for `collection`', 1, function() {
var actual = _.groupBy({ 'a': 4.2, 'b': 6.1, 'c': 6.4 }, function(num) {
return Math.floor(num);
});
deepEqual(actual, { '4': [4.2], '6': [6.1, 6.4] });
});
test('should work in a lazy chain sequence', 1, function() {
if (!isNpm) {
var array = [1, 2, 1, 3],
iteratee = function(value) { value.push(value[0]); return value; },
predicate = function(value) { return value[0] > 1; },
actual = _(array).groupBy(_.identity).map(iteratee).filter(predicate).take().value();
deepEqual(actual, [[2, 2]]);
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.has');
(function() {
test('should check for own properties', 2, function() {
var object = { 'a': 1 };
_.each(['a', ['a']], function(path) {
strictEqual(_.has(object, path), true);
});
});
test('should not use the `hasOwnProperty` method of the object', 1, function() {
var object = { 'hasOwnProperty': null, 'a': 1 };
strictEqual(_.has(object, 'a'), true);
});
test('should support deep paths', 2, function() {
var object = { 'a': { 'b': { 'c': 3 } } };
_.each(['a.b.c', ['a', 'b', 'c']], function(path) {
strictEqual(_.has(object, path), true);
});
});
test('should work with non-string `path` arguments', 2, function() {
var array = [1, 2, 3];
_.each([1, [1]], function(path) {
strictEqual(_.has(array, path), true);
});
});
test('should coerce key to a string', 1, function() {
function fn() {}
fn.toString = _.constant('fn');
var expected = [1, 1, 2, 2, 3, 3, 4, 4],
objects = [{ 'null': 1 }, { 'undefined': 2 }, { 'fn': 3 }, { '[object Object]': 4 }],
values = [null, undefined, fn, {}];
var actual = _.transform(objects, function(result, object, index) {
var key = values[index];
_.each([key, [key]], function(path) {
var prop = _.property(key);
result.push(prop(object));
});
});
deepEqual(actual, expected);
});
test('should return `false` for inherited properties', 2, function() {
function Foo() {}
Foo.prototype.a = 1;
_.each(['a', ['a']], function(path) {
strictEqual(_.has(new Foo, path), false);
});
});
test('should return `true` for string indexes (test in IE < 9)', 2, function() {
_.each([1, [1]], function(path) {
strictEqual(_.has('xo', path), true);
});
});
test('should check for a key over a path', 2, function() {
var object = { 'a.b.c': 3, 'a': { 'b': { 'c': 4 } } };
_.each(['a.b.c', ['a.b.c']], function(path) {
strictEqual(_.has(object, path), true);
});
});
test('should return `false` when `object` is nullish', 2, function() {
var values = [null, undefined],
expected = _.map(values, _.constant(false));
_.each(['constructor', ['constructor']], function(path) {
var actual = _.map(values, function(value) {
return _.has(value, path);
});
deepEqual(actual, expected);
});
});
test('should return `false` with deep paths when `object` is nullish', 2, function() {
var values = [null, undefined],
expected = _.map(values, _.constant(false));
_.each(['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], function(path) {
var actual = _.map(values, function(value) {
return _.has(value, path);
});
deepEqual(actual, expected);
});
});
test('should return `false` if parts of `path` are missing', 4, function() {
var object = {};
_.each(['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']], function(path) {
strictEqual(_.has(object, path), false);
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.identity');
(function() {
test('should return the first argument provided', 1, function() {
var object = { 'name': 'fred' };
strictEqual(_.identity(object), object);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.includes');
(function() {
_.each({
'an `arguments` object': arguments,
'an array': [1, 2, 3, 4],
'an object': { 'a': 1, 'b': 2, 'c': 3, 'd': 4 },
'a string': '1234'
},
function(collection, key) {
var values = _.toArray(collection);
test('should work with ' + key + ' and return `true` for matched values', 1, function() {
strictEqual(_.includes(collection, 3), true);
});
test('should work with ' + key + ' and return `false` for unmatched values', 1, function() {
strictEqual(_.includes(collection, 5), false);
});
test('should work with ' + key + ' and a positive `fromIndex`', 2, function() {
strictEqual(_.includes(collection, values[2], 2), true);
strictEqual(_.includes(collection, values[1], 2), false);
});
test('should work with ' + key + ' and a `fromIndex` >= `collection.length`', 12, function() {
_.each([4, 6, Math.pow(2, 32), Infinity], function(fromIndex) {
strictEqual(_.includes(collection, 1, fromIndex), false);
strictEqual(_.includes(collection, undefined, fromIndex), false);
strictEqual(_.includes(collection, '', fromIndex), false);
});
});
test('should work with ' + key + ' and treat falsey `fromIndex` values as `0`', 1, function() {
var expected = _.map(falsey, _.constant(true));
var actual = _.map(falsey, function(fromIndex) {
return _.includes(collection, values[0], fromIndex);
});
deepEqual(actual, expected);
});
test('should work with ' + key + ' and treat non-number `fromIndex` values as `0`', 1, function() {
strictEqual(_.includes(collection, values[0], '1'), true);
});
test('should work with ' + key + ' and a negative `fromIndex`', 2, function() {
strictEqual(_.includes(collection, values[2], -2), true);
strictEqual(_.includes(collection, values[1], -2), false);
});
test('should work with ' + key + ' and a negative `fromIndex` <= negative `collection.length`', 3, function() {
_.each([-4, -6, -Infinity], function(fromIndex) {
strictEqual(_.includes(collection, values[0], fromIndex), true);
});
});
test('should work with ' + key + ' and return an unwrapped value implicitly when chaining', 1, function() {
if (!isNpm) {
strictEqual(_(collection).includes(3), true);
}
else {
skipTest();
}
});
test('should work with ' + key + ' and return a wrapped value when explicitly chaining', 1, function() {
if (!isNpm) {
ok(_(collection).chain().includes(3) instanceof _);
}
else {
skipTest();
}
});
});
_.each({
'literal': 'abc',
'object': Object('abc')
},
function(collection, key) {
test('should work with a string ' + key + ' for `collection`', 2, function() {
strictEqual(_.includes(collection, 'bc'), true);
strictEqual(_.includes(collection, 'd'), false);
});
});
test('should return `false` for empty collections', 1, function() {
var expected = _.map(empties, _.constant(false));
var actual = _.map(empties, function(value) {
try {
return _.includes(value);
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should not be possible to perform a binary search', 1, function() {
strictEqual(_.includes([3, 2, 1], 3, true), true);
});
test('should match `NaN`', 1, function() {
strictEqual(_.includes([1, NaN, 3], NaN), true);
});
test('should match `-0` as `0`', 2, function() {
strictEqual(_.includes([-0], 0), true);
strictEqual(_.includes([0], -0), true);
});
test('should work as an iteratee for methods like `_.reduce`', 1, function() {
var array1 = [1, 2, 3],
array2 = [2, 3, 1];
ok(_.every(array1, _.partial(_.includes, array2)));
});
test('should be aliased', 2, function() {
strictEqual(_.contains, _.includes);
strictEqual(_.include, _.includes);
});
}(1, 2, 3, 4));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.indexBy');
(function() {
test('should use `_.identity` when `iteratee` is nullish', 1, function() {
var array = [4, 6, 6],
values = [, null, undefined],
expected = _.map(values, _.constant({ '4': 4, '6': 6 }));
var actual = _.map(values, function(value, index) {
return index ? _.indexBy(array, value) : _.indexBy(array);
});
deepEqual(actual, expected);
});
test('should support the `thisArg` argument', 1, function() {
var actual = _.indexBy([4.2, 6.1, 6.4], function(num) {
return this.floor(num);
}, Math);
deepEqual(actual, { '4': 4.2, '6': 6.4 });
});
test('should work with a "_.property" style `iteratee`', 1, function() {
var actual = _.indexBy(['one', 'two', 'three'], 'length');
deepEqual(actual, { '3': 'two', '5': 'three' });
});
test('should only add values to own, not inherited, properties', 2, function() {
var actual = _.indexBy([4.2, 6.1, 6.4], function(num) {
return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor';
});
deepEqual(actual.constructor, 4.2);
deepEqual(actual.hasOwnProperty, 6.4);
});
test('should work with a number for `iteratee`', 2, function() {
var array = [
[1, 'a'],
[2, 'a'],
[2, 'b']
];
deepEqual(_.indexBy(array, 0), { '1': [1, 'a'], '2': [2, 'b'] });
deepEqual(_.indexBy(array, 1), { 'a': [2, 'a'], 'b': [2, 'b'] });
});
test('should work with an object for `collection`', 1, function() {
var actual = _.indexBy({ 'a': 4.2, 'b': 6.1, 'c': 6.4 }, function(num) {
return Math.floor(num);
});
deepEqual(actual, { '4': 4.2, '6': 6.4 });
});
test('should work in a lazy chain sequence', 1, function() {
if (!isNpm) {
var array = [1, 2, 1, 3],
predicate = function(value) { return value > 2; },
actual = _(array).indexBy(_.identity).map(square).filter(predicate).take().value();
deepEqual(actual, [4]);
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.indexOf');
(function() {
var array = [1, 2, 3, 1, 2, 3];
test('should return the index of the first matched value', 1, function() {
strictEqual(_.indexOf(array, 3), 2);
});
test('should work with a positive `fromIndex`', 1, function() {
strictEqual(_.indexOf(array, 1, 2), 3);
});
test('should work with `fromIndex` >= `array.length`', 1, function() {
var values = [6, 8, Math.pow(2, 32), Infinity],
expected = _.map(values, _.constant([-1, -1, -1]));
var actual = _.map(values, function(fromIndex) {
return [
_.indexOf(array, undefined, fromIndex),
_.indexOf(array, 1, fromIndex),
_.indexOf(array, '', fromIndex)
];
});
deepEqual(actual, expected);
});
test('should treat falsey `fromIndex` values as `0`', 1, function() {
var expected = _.map(falsey, _.constant(0));
var actual = _.map(falsey, function(fromIndex) {
return _.indexOf(array, 1, fromIndex);
});
deepEqual(actual, expected);
});
test('should perform a binary search when `fromIndex` is a non-number truthy value', 1, function() {
var sorted = [4, 4, 5, 5, 6, 6],
values = [true, '1', {}],
expected = _.map(values, _.constant(2));
var actual = _.map(values, function(value) {
return _.indexOf(sorted, 5, value);
});
deepEqual(actual, expected);
});
test('should work with a negative `fromIndex`', 1, function() {
strictEqual(_.indexOf(array, 2, -3), 4);
});
test('should work with a negative `fromIndex` <= `-array.length`', 1, function() {
var values = [-6, -8, -Infinity],
expected = _.map(values, _.constant(0));
var actual = _.map(values, function(fromIndex) {
return _.indexOf(array, 1, fromIndex);
});
deepEqual(actual, expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('custom `_.indexOf` methods');
(function() {
function Foo() {}
function custom(array, value, fromIndex) {
var index = (fromIndex || 0) - 1,
length = array.length;
while (++index < length) {
var other = array[index];
if (other === value || (value instanceof Foo && other instanceof Foo)) {
return index;
}
}
return -1;
}
var array = [1, new Foo, 3, new Foo],
indexOf = _.indexOf;
var largeArray = _.times(LARGE_ARRAY_SIZE, function() {
return new Foo;
});
test('`_.includes` should work with a custom `_.indexOf` method', 2, function() {
if (!isModularize) {
_.indexOf = custom;
ok(_.includes(array, new Foo));
ok(_.includes({ 'a': 1, 'b': new Foo, 'c': 3 }, new Foo));
_.indexOf = indexOf;
}
else {
skipTest(2);
}
});
test('`_.difference` should work with a custom `_.indexOf` method', 2, function() {
if (!isModularize) {
_.indexOf = custom;
deepEqual(_.difference(array, [new Foo]), [1, 3]);
deepEqual(_.difference(array, largeArray), [1, 3]);
_.indexOf = indexOf;
}
else {
skipTest(2);
}
});
test('`_.intersection` should work with a custom `_.indexOf` method', 2, function() {
if (!isModularize) {
_.indexOf = custom;
deepEqual(_.intersection(array, [new Foo]), [array[1]]);
deepEqual(_.intersection(largeArray, [new Foo]), [array[1]]);
_.indexOf = indexOf;
}
else {
skipTest(2);
}
});
test('`_.uniq` should work with a custom `_.indexOf` method', 6, function() {
_.each([false, true, _.identity], function(param) {
if (!isModularize) {
_.indexOf = custom;
deepEqual(_.uniq(array, param), array.slice(0, 3));
deepEqual(_.uniq(largeArray, param), [largeArray[0]]);
_.indexOf = indexOf;
}
else {
skipTest(2);
}
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.initial');
(function() {
var array = [1, 2, 3];
test('should accept a falsey `array` argument', 1, function() {
var expected = _.map(falsey, _.constant([]));
var actual = _.map(falsey, function(value, index) {
try {
return index ? _.initial(value) : _.initial();
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should exclude last element', 1, function() {
deepEqual(_.initial(array), [1, 2]);
});
test('should return an empty when querying empty arrays', 1, function() {
deepEqual(_.initial([]), []);
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
actual = _.map(array, _.initial);
deepEqual(actual, [[1, 2], [4, 5], [7, 8]]);
});
test('should work in a lazy chain sequence', 4, function() {
if (!isNpm) {
var array = [1, 2, 3],
values = [];
var actual = _(array).initial().filter(function(value) {
values.push(value);
return false;
})
.value();
deepEqual(actual, []);
deepEqual(values, [1, 2]);
values = [];
actual = _(array).filter(function(value) {
values.push(value);
return value < 3;
})
.initial()
.value();
deepEqual(actual, [1]);
deepEqual(values, array);
}
else {
skipTest(4);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.inRange');
(function() {
test('should work with an `end` argument', 3, function() {
strictEqual(_.inRange(3, 5), true);
strictEqual(_.inRange(5, 5), false);
strictEqual(_.inRange(6, 5), false);
});
test('should work with `start` and `end` arguments', 4, function() {
strictEqual(_.inRange(1, 1, 5), true);
strictEqual(_.inRange(3, 1, 5), true);
strictEqual(_.inRange(0, 1, 5), false);
strictEqual(_.inRange(5, 1, 5), false);
});
test('should treat falsey `start` arguments as `0`', 13, function() {
_.each(falsey, function(value, index) {
if (index) {
strictEqual(_.inRange(0, value), false);
strictEqual(_.inRange(0, value, 1), true);
} else {
strictEqual(_.inRange(0), false);
}
});
});
test('should swap `start` and `end` when `start` is greater than `end`', 2, function() {
strictEqual(_.inRange(2, 5, 1), true);
strictEqual(_.inRange(-3, -2, -6), true);
});
test('should work with a floating point `n` value', 4, function() {
strictEqual(_.inRange(0.5, 5), true);
strictEqual(_.inRange(1.2, 1, 5), true);
strictEqual(_.inRange(5.2, 5), false);
strictEqual(_.inRange(0.5, 1, 5), false);
});
test('should coerce arguments to finite numbers', 1, function() {
var actual = [_.inRange(0, '0', 1), _.inRange(0, '1'), _.inRange(0, 0, '1'), _.inRange(0, NaN, 1), _.inRange(-1, -1, NaN)],
expected = _.map(actual, _.constant(true));
deepEqual(actual, expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.intersection');
(function() {
var args = arguments;
test('should return the intersection of the given arrays', 1, function() {
var actual = _.intersection([1, 3, 2], [5, 2, 1, 4], [2, 1]);
deepEqual(actual, [1, 2]);
});
test('should return an array of unique values', 1, function() {
var actual = _.intersection([1, 1, 3, 2, 2], [5, 2, 2, 1, 4], [2, 1, 1]);
deepEqual(actual, [1, 2]);
});
test('should match `NaN`', 1, function() {
var actual = _.intersection([1, NaN, 3], [NaN, 5, NaN]);
deepEqual(actual, [NaN]);
});
test('should work with large arrays of objects', 1, function() {
var object = {},
largeArray = _.times(LARGE_ARRAY_SIZE, _.constant(object));
deepEqual(_.intersection([object], largeArray), [object]);
});
test('should work with large arrays of objects', 2, function() {
var object = {},
largeArray = _.times(LARGE_ARRAY_SIZE, _.constant(object));
deepEqual(_.intersection([object], largeArray), [object]);
deepEqual(_.intersection(_.range(LARGE_ARRAY_SIZE), null, [1]), [1]);
});
test('should work with large arrays of `NaN`', 1, function() {
var largeArray = _.times(LARGE_ARRAY_SIZE, _.constant(NaN));
deepEqual(_.intersection([1, NaN, 3], largeArray), [NaN]);
});
test('should ignore values that are not arrays or `arguments` objects', 3, function() {
var array = [0, 1, null, 3];
deepEqual(_.intersection(array, 3, null, { '0': 1 }), []);
deepEqual(_.intersection(null, array, null, [2, 1]), [1]);
deepEqual(_.intersection(array, null, args, null), [1, 3]);
});
test('should return a wrapped value when chaining', 2, function() {
if (!isNpm) {
var wrapped = _([1, 3, 2]).intersection([5, 2, 1, 4]);
ok(wrapped instanceof _);
deepEqual(wrapped.value(), [1, 2]);
}
else {
skipTest(2);
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.invert');
(function() {
test('should invert an object', 2, function() {
var object = { 'a': 1, 'b': 2 },
actual = _.invert(object);
deepEqual(actual, { '1': 'a', '2': 'b' });
deepEqual(_.invert(actual), { 'a': '1', 'b': '2' });
});
test('should work with an object that has a `length` property', 1, function() {
var object = { '0': 'a', '1': 'b', 'length': 2 };
deepEqual(_.invert(object), { 'a': '0', 'b': '1', '2': 'length' });
});
test('should accept a `multiValue` flag', 1, function() {
var object = { 'a': 1, 'b': 2, 'c': 1 };
deepEqual(_.invert(object, true), { '1': ['a', 'c'], '2': ['b'] });
});
test('should only add multiple values to own, not inherited, properties', 2, function() {
var object = { 'a': 'hasOwnProperty', 'b': 'constructor' };
deepEqual(_.invert(object), { 'hasOwnProperty': 'a', 'constructor': 'b' });
ok(_.isEqual(_.invert(object, true), { 'hasOwnProperty': ['a'], 'constructor': ['b'] }));
});
test('should work as an iteratee for methods like `_.map`', 2, function() {
var regular = { 'a': 1, 'b': 2, 'c': 1 },
inverted = { '1': 'c', '2': 'b' };
var array = [regular, regular, regular],
object = { 'a': regular, 'b': regular, 'c': regular },
expected = _.map(array, _.constant(inverted));
_.each([array, object], function(collection) {
var actual = _.map(collection, _.invert);
deepEqual(actual, expected);
});
});
test('should return a wrapped value when chaining', 2, function() {
if (!isNpm) {
var object = { 'a': 1, 'b': 2 },
wrapped = _(object).invert();
ok(wrapped instanceof _);
deepEqual(wrapped.value(), { '1': 'a', '2': 'b' });
}
else {
skipTest(2);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.invoke');
(function() {
test('should invoke a methods on each element of a collection', 1, function() {
var array = ['a', 'b', 'c'];
deepEqual(_.invoke(array, 'toUpperCase'), ['A', 'B', 'C']);
});
test('should support invoking with arguments', 1, function() {
var array = [function() { return slice.call(arguments); }],
actual = _.invoke(array, 'call', null, 'a', 'b', 'c');
deepEqual(actual, [['a', 'b', 'c']]);
});
test('should work with a function `methodName` argument', 1, function() {
var array = ['a', 'b', 'c'];
var actual = _.invoke(array, function(left, right) {
return left + this.toUpperCase() + right;
}, '(', ')');
deepEqual(actual, ['(A)', '(B)', '(C)']);
});
test('should work with an object for `collection`', 1, function() {
var object = { 'a': 1, 'b': 2, 'c': 3 };
deepEqual(_.invoke(object, 'toFixed', 1), ['1.0', '2.0', '3.0']);
});
test('should treat number values for `collection` as empty', 1, function() {
deepEqual(_.invoke(1), []);
});
test('should not error on nullish elements', 1, function() {
var array = ['a', null, undefined, 'd'];
try {
var actual = _.invoke(array, 'toUpperCase');
} catch(e) {}
deepEqual(_.invoke(array, 'toUpperCase'), ['A', undefined, undefined, 'D']);
});
test('should not error on elements with missing properties', 1, function() {
var objects = _.map([null, undefined, _.constant(1)], function(value) {
return { 'a': value };
});
var expected = _.times(objects.length - 1, _.constant(undefined)).concat(1);
try {
var actual = _.invoke(objects, 'a');
} catch(e) {}
deepEqual(actual, expected);
});
test('should invoke deep property methods with the correct `this` binding', 2, function() {
var object = { 'a': { 'b': function() { return this.c; }, 'c': 1 } };
_.each(['a.b', ['a', 'b']], function(path) {
deepEqual(_.invoke([object], path), [1]);
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isArguments');
(function() {
var args = arguments;
test('should return `true` for `arguments` objects', 1, function() {
strictEqual(_.isArguments(args), true);
});
test('should return `false` for non `arguments` objects', 12, function() {
var expected = _.map(falsey, _.constant(false));
var actual = _.map(falsey, function(value, index) {
return index ? _.isArguments(value) : _.isArguments();
});
deepEqual(actual, expected);
strictEqual(_.isArguments([1, 2, 3]), false);
strictEqual(_.isArguments(true), false);
strictEqual(_.isArguments(new Date), false);
strictEqual(_.isArguments(new Error), false);
strictEqual(_.isArguments(_), false);
strictEqual(_.isArguments(slice), false);
strictEqual(_.isArguments({ '0': 1, 'callee': _.noop, 'length': 1 }), false);
strictEqual(_.isArguments(1), false);
strictEqual(_.isArguments(NaN), false);
strictEqual(_.isArguments(/x/), false);
strictEqual(_.isArguments('a'), false);
});
test('should work with an `arguments` object from another realm', 1, function() {
if (_._object) {
strictEqual(_.isArguments(_._arguments), true);
}
else {
skipTest();
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isArray');
(function() {
var args = arguments;
test('should return `true` for arrays', 1, function() {
strictEqual(_.isArray([1, 2, 3]), true);
});
test('should return `false` for non-arrays', 12, function() {
var expected = _.map(falsey, _.constant(false));
var actual = _.map(falsey, function(value, index) {
return index ? _.isArray(value) : _.isArray();
});
deepEqual(actual, expected);
strictEqual(_.isArray(args), false);
strictEqual(_.isArray(true), false);
strictEqual(_.isArray(new Date), false);
strictEqual(_.isArray(new Error), false);
strictEqual(_.isArray(_), false);
strictEqual(_.isArray(slice), false);
strictEqual(_.isArray({ '0': 1, 'length': 1 }), false);
strictEqual(_.isArray(1), false);
strictEqual(_.isArray(NaN), false);
strictEqual(_.isArray(/x/), false);
strictEqual(_.isArray('a'), false);
});
test('should work with an array from another realm', 1, function() {
if (_._object) {
strictEqual(_.isArray(_._array), true);
}
else {
skipTest();
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isBoolean');
(function() {
var args = arguments;
test('should return `true` for booleans', 4, function() {
strictEqual(_.isBoolean(true), true);
strictEqual(_.isBoolean(false), true);
strictEqual(_.isBoolean(Object(true)), true);
strictEqual(_.isBoolean(Object(false)), true);
});
test('should return `false` for non-booleans', 12, function() {
var expected = _.map(falsey, function(value) { return value === false; });
var actual = _.map(falsey, function(value, index) {
return index ? _.isBoolean(value) : _.isBoolean();
});
deepEqual(actual, expected);
strictEqual(_.isBoolean(args), false);
strictEqual(_.isBoolean([1, 2, 3]), false);
strictEqual(_.isBoolean(new Date), false);
strictEqual(_.isBoolean(new Error), false);
strictEqual(_.isBoolean(_), false);
strictEqual(_.isBoolean(slice), false);
strictEqual(_.isBoolean({ 'a': 1 }), false);
strictEqual(_.isBoolean(1), false);
strictEqual(_.isBoolean(NaN), false);
strictEqual(_.isBoolean(/x/), false);
strictEqual(_.isBoolean('a'), false);
});
test('should work with a boolean from another realm', 1, function() {
if (_._object) {
strictEqual(_.isBoolean(_._boolean), true);
}
else {
skipTest();
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isDate');
(function() {
var args = arguments;
test('should return `true` for dates', 1, function() {
strictEqual(_.isDate(new Date), true);
});
test('should return `false` for non-dates', 12, function() {
var expected = _.map(falsey, _.constant(false));
var actual = _.map(falsey, function(value, index) {
return index ? _.isDate(value) : _.isDate();
});
deepEqual(actual, expected);
strictEqual(_.isDate(args), false);
strictEqual(_.isDate([1, 2, 3]), false);
strictEqual(_.isDate(true), false);
strictEqual(_.isDate(new Error), false);
strictEqual(_.isDate(_), false);
strictEqual(_.isDate(slice), false);
strictEqual(_.isDate({ 'a': 1 }), false);
strictEqual(_.isDate(1), false);
strictEqual(_.isDate(NaN), false);
strictEqual(_.isDate(/x/), false);
strictEqual(_.isDate('a'), false);
});
test('should work with a date object from another realm', 1, function() {
if (_._object) {
strictEqual(_.isDate(_._date), true);
}
else {
skipTest();
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isElement');
(function() {
var args = arguments;
function Element() {
this.nodeType = 1;
}
test('should use robust check', 7, function() {
var element = body || new Element;
strictEqual(_.isElement(element), true);
strictEqual(_.isElement({ 'nodeType': 1 }), false);
strictEqual(_.isElement({ 'nodeType': Object(1) }), false);
strictEqual(_.isElement({ 'nodeType': true }), false);
strictEqual(_.isElement({ 'nodeType': [1] }), false);
strictEqual(_.isElement({ 'nodeType': '1' }), false);
strictEqual(_.isElement({ 'nodeType': '001' }), false);
});
test('should use a stronger check in browsers', 2, function() {
var expected = !_.support.dom;
strictEqual(_.isElement(new Element), expected);
if (lodashBizarro) {
expected = !lodashBizarro.support.dom;
strictEqual(lodashBizarro.isElement(new Element), expected);
}
else {
skipTest();
}
});
test('should return `false` for non DOM elements', 13, function() {
var expected = _.map(falsey, _.constant(false));
var actual = _.map(falsey, function(value, index) {
return index ? _.isElement(value) : _.isElement();
});
deepEqual(actual, expected);
strictEqual(_.isElement(args), false);
strictEqual(_.isElement([1, 2, 3]), false);
strictEqual(_.isElement(true), false);
strictEqual(_.isElement(new Date), false);
strictEqual(_.isElement(new Error), false);
strictEqual(_.isElement(_), false);
strictEqual(_.isElement(slice), false);
strictEqual(_.isElement({ 'a': 1 }), false);
strictEqual(_.isElement(1), false);
strictEqual(_.isElement(NaN), false);
strictEqual(_.isElement(/x/), false);
strictEqual(_.isElement('a'), false);
});
test('should work with a DOM element from another realm', 1, function() {
if (_._element) {
strictEqual(_.isElement(_._element), true);
}
else {
skipTest();
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isEmpty');
(function() {
var args = arguments;
test('should return `true` for empty values', 7, function() {
var expected = _.map(empties, _.constant(true));
var actual = _.map(empties, function(value) {
return _.isEmpty(value);
});
deepEqual(actual, expected);
strictEqual(_.isEmpty(true), true);
strictEqual(_.isEmpty(slice), true);
strictEqual(_.isEmpty(1), true);
strictEqual(_.isEmpty(NaN), true);
strictEqual(_.isEmpty(/x/), true);
strictEqual(_.isEmpty(), true);
});
test('should return `false` for non-empty values', 3, function() {
strictEqual(_.isEmpty([0]), false);
strictEqual(_.isEmpty({ 'a': 0 }), false);
strictEqual(_.isEmpty('a'), false);
});
test('should work with an object that has a `length` property', 1, function() {
strictEqual(_.isEmpty({ 'length': 0 }), false);
});
test('should work with `arguments` objects (test in IE < 9)', 1, function() {
strictEqual(_.isEmpty(args), false);
});
test('should work with jQuery/MooTools DOM query collections', 1, function() {
function Foo(elements) { push.apply(this, elements); }
Foo.prototype = { 'length': 0, 'splice': arrayProto.splice };
strictEqual(_.isEmpty(new Foo([])), true);
});
test('should not treat objects with negative lengths as array-like', 1, function() {
function Foo() {}
Foo.prototype.length = -1;
strictEqual(_.isEmpty(new Foo), true);
});
test('should not treat objects with lengths larger than `MAX_SAFE_INTEGER` as array-like', 1, function() {
function Foo() {}
Foo.prototype.length = MAX_SAFE_INTEGER + 1;
strictEqual(_.isEmpty(new Foo), true);
});
test('should not treat objects with non-number lengths as array-like', 1, function() {
strictEqual(_.isEmpty({ 'length': '0' }), false);
});
test('fixes the JScript `[[DontEnum]]` bug (test in IE < 9)', 1, function() {
strictEqual(_.isEmpty(shadowObject), false);
});
test('skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', 2, function() {
function Foo() {}
Foo.prototype.a = 1;
strictEqual(_.isEmpty(Foo), true);
Foo.prototype = { 'a': 1 };
strictEqual(_.isEmpty(Foo), true);
});
test('should return an unwrapped value when implicitly chaining', 1, function() {
if (!isNpm) {
strictEqual(_({}).isEmpty(), true);
}
else {
skipTest();
}
});
test('should return a wrapped value when explicitly chaining', 1, function() {
if (!isNpm) {
ok(_({}).chain().isEmpty() instanceof _);
}
else {
skipTest();
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isEqual');
(function() {
test('should perform comparisons between primitive values', 1, function() {
var pairs = [
[1, 1, true], [1, Object(1), true], [1, '1', false], [1, 2, false],
[-0, -0, true], [0, 0, true], [0, Object(0), true], [Object(0), Object(0), true], [-0, 0, true], [0, '0', false], [0, null, false],
[NaN, NaN, true], [NaN, Object(NaN), true], [Object(NaN), Object(NaN), true], [NaN, 'a', false], [NaN, Infinity, false],
['a', 'a', true], ['a', Object('a'), true], [Object('a'), Object('a'), true], ['a', 'b', false], ['a', ['a'], false],
[true, true, true], [true, Object(true), true], [Object(true), Object(true), true], [true, 1, false], [true, 'a', false],
[false, false, true], [false, Object(false), true], [Object(false), Object(false), true], [false, 0, false], [false, '', false],
[null, null, true], [null, undefined, false], [null, {}, false], [null, '', false],
[undefined, undefined, true], [undefined, null, false], [undefined, '', false]
];
var expected = _.map(pairs, function(pair) {
return pair[2];
});
var actual = _.map(pairs, function(pair) {
return _.isEqual(pair[0], pair[1]);
});
deepEqual(actual, expected);
});
test('should perform comparisons between arrays', 6, function() {
var array1 = [true, null, 1, 'a', undefined],
array2 = [true, null, 1, 'a', undefined];
strictEqual(_.isEqual(array1, array2), true);
array1 = [[1, 2, 3], new Date(2012, 4, 23), /x/, { 'e': 1 }];
array2 = [[1, 2, 3], new Date(2012, 4, 23), /x/, { 'e': 1 }];
strictEqual(_.isEqual(array1, array2), true);
array1 = [1];
array1[2] = 3;
array2 = [1];
array2[1] = undefined;
array2[2] = 3;
strictEqual(_.isEqual(array1, array2), true);
array1 = [Object(1), false, Object('a'), /x/, new Date(2012, 4, 23), ['a', 'b', [Object('c')]], { 'a': 1 }];
array2 = [1, Object(false), 'a', /x/, new Date(2012, 4, 23), ['a', Object('b'), ['c']], { 'a': 1 }];
strictEqual(_.isEqual(array1, array2), true);
array1 = [1, 2, 3];
array2 = [3, 2, 1];
strictEqual(_.isEqual(array1, array2), false);
array1 = [1, 2];
array2 = [1, 2, 3];
strictEqual(_.isEqual(array1, array2), false);
});
test('should treat arrays with identical values but different non-numeric properties as equal', 3, function() {
var array1 = [1, 2, 3],
array2 = [1, 2, 3];
array1.every = array1.filter = array1.forEach = array1.indexOf = array1.lastIndexOf = array1.map = array1.some = array1.reduce = array1.reduceRight = null;
array2.concat = array2.join = array2.pop = array2.reverse = array2.shift = array2.slice = array2.sort = array2.splice = array2.unshift = null;
strictEqual(_.isEqual(array1, array2), true);
array1 = [1, 2, 3];
array1.a = 1;
array2 = [1, 2, 3];
array2.b = 1;
strictEqual(_.isEqual(array1, array2), true);
array1 = /x/.exec('vwxyz');
array2 = ['x'];
strictEqual(_.isEqual(array1, array2), true);
});
test('should work with sparse arrays', 3, function() {
var array = Array(1);
strictEqual(_.isEqual(array, Array(1)), true);
strictEqual(_.isEqual(array, [undefined]), true);
strictEqual(_.isEqual(array, Array(2)), false);
});
test('should perform comparisons between plain objects', 5, function() {
var object1 = { 'a': true, 'b': null, 'c': 1, 'd': 'a', 'e': undefined },
object2 = { 'a': true, 'b': null, 'c': 1, 'd': 'a', 'e': undefined };
strictEqual(_.isEqual(object1, object2), true);
object1 = { 'a': [1, 2, 3], 'b': new Date(2012, 4, 23), 'c': /x/, 'd': { 'e': 1 } };
object2 = { 'a': [1, 2, 3], 'b': new Date(2012, 4, 23), 'c': /x/, 'd': { 'e': 1 } };
strictEqual(_.isEqual(object1, object2), true);
object1 = { 'a': 1, 'b': 2, 'c': 3 };
object2 = { 'a': 3, 'b': 2, 'c': 1 };
strictEqual(_.isEqual(object1, object2), false);
object1 = { 'a': 1, 'b': 2, 'c': 3 };
object2 = { 'd': 1, 'e': 2, 'f': 3 };
strictEqual(_.isEqual(object1, object2), false);
object1 = { 'a': 1, 'b': 2 };
object2 = { 'a': 1, 'b': 2, 'c': 3 };
strictEqual(_.isEqual(object1, object2), false);
});
test('should perform comparisons of nested objects', 1, function() {
var object1 = {
'a': [1, 2, 3],
'b': true,
'c': Object(1),
'd': 'a',
'e': {
'f': ['a', Object('b'), 'c'],
'g': Object(false),
'h': new Date(2012, 4, 23),
'i': _.noop,
'j': 'a'
}
};
var object2 = {
'a': [1, Object(2), 3],
'b': Object(true),
'c': 1,
'd': Object('a'),
'e': {
'f': ['a', 'b', 'c'],
'g': false,
'h': new Date(2012, 4, 23),
'i': _.noop,
'j': 'a'
}
};
strictEqual(_.isEqual(object1, object2), true);
});
test('should perform comparisons between object instances', 4, function() {
function Foo() { this.value = 1; }
Foo.prototype.value = 1;
function Bar() {
this.value = 1;
}
Bar.prototype.value = 2;
strictEqual(_.isEqual(new Foo, new Foo), true);
strictEqual(_.isEqual(new Foo, new Bar), false);
strictEqual(_.isEqual({ 'value': 1 }, new Foo), false);
strictEqual(_.isEqual({ 'value': 2 }, new Bar), false);
});
test('should perform comparisons between objects with constructor properties', 5, function() {
strictEqual(_.isEqual({ 'constructor': 1 }, { 'constructor': 1 }), true);
strictEqual(_.isEqual({ 'constructor': 1 }, { 'constructor': '1' }), false);
strictEqual(_.isEqual({ 'constructor': [1] }, { 'constructor': [1] }), true);
strictEqual(_.isEqual({ 'constructor': [1] }, { 'constructor': ['1'] }), false);
strictEqual(_.isEqual({ 'constructor': Object }, {}), false);
});
test('should perform comparisons between arrays with circular references', 4, function() {
var array1 = [],
array2 = [];
array1.push(array1);
array2.push(array2);
strictEqual(_.isEqual(array1, array2), true);
array1.push('b');
array2.push('b');
strictEqual(_.isEqual(array1, array2), true);
array1.push('c');
array2.push('d');
strictEqual(_.isEqual(array1, array2), false);
array1 = ['a', 'b', 'c'];
array1[1] = array1;
array2 = ['a', ['a', 'b', 'c'], 'c'];
strictEqual(_.isEqual(array1, array2), false);
});
test('should perform comparisons between objects with circular references', 4, function() {
var object1 = {},
object2 = {};
object1.a = object1;
object2.a = object2;
strictEqual(_.isEqual(object1, object2), true);
object1.b = 0;
object2.b = Object(0);
strictEqual(_.isEqual(object1, object2), true);
object1.c = Object(1);
object2.c = Object(2);
strictEqual(_.isEqual(object1, object2), false);
object1 = { 'a': 1, 'b': 2, 'c': 3 };
object1.b = object1;
object2 = { 'a': 1, 'b': { 'a': 1, 'b': 2, 'c': 3 }, 'c': 3 };
strictEqual(_.isEqual(object1, object2), false);
});
test('should perform comparisons between objects with multiple circular references', 3, function() {
var array1 = [{}],
array2 = [{}];
(array1[0].a = array1).push(array1);
(array2[0].a = array2).push(array2);
strictEqual(_.isEqual(array1, array2), true);
array1[0].b = 0;
array2[0].b = Object(0);
strictEqual(_.isEqual(array1, array2), true);
array1[0].c = Object(1);
array2[0].c = Object(2);
strictEqual(_.isEqual(array1, array2), false);
});
test('should perform comparisons between objects with complex circular references', 1, function() {
var object1 = {
'foo': { 'b': { 'c': { 'd': {} } } },
'bar': { 'a': 2 }
};
var object2 = {
'foo': { 'b': { 'c': { 'd': {} } } },
'bar': { 'a': 2 }
};
object1.foo.b.c.d = object1;
object1.bar.b = object1.foo.b;
object2.foo.b.c.d = object2;
object2.bar.b = object2.foo.b;
strictEqual(_.isEqual(object1, object2), true);
});
test('should perform comparisons between objects with shared property values', 1, function() {
var object1 = {
'a': [1, 2]
};
var object2 = {
'a': [1, 2],
'b': [1, 2]
};
object1.b = object1.a;
strictEqual(_.isEqual(object1, object2), true);
});
test('should work with `arguments` objects (test in IE < 9)', 2, function() {
var args1 = (function() { return arguments; }(1, 2, 3)),
args2 = (function() { return arguments; }(1, 2, 3)),
args3 = (function() { return arguments; }(1, 2));
strictEqual(_.isEqual(args1, args2), true);
strictEqual(_.isEqual(args1, args3), false);
});
test('should treat `arguments` objects like `Object` objects', 4, function() {
var args = (function() { return arguments; }(1, 2, 3)),
object = { '0': 1, '1': 2, '2': 3 };
function Foo() {}
Foo.prototype = object;
strictEqual(_.isEqual(args, object), true);
strictEqual(_.isEqual(object, args), true);
strictEqual(_.isEqual(args, new Foo), false);
strictEqual(_.isEqual(new Foo, args), false);
});
test('should perform comparisons between date objects', 4, function() {
strictEqual(_.isEqual(new Date(2012, 4, 23), new Date(2012, 4, 23)), true);
strictEqual(_.isEqual(new Date(2012, 4, 23), new Date(2013, 3, 25)), false);
strictEqual(_.isEqual(new Date(2012, 4, 23), { 'getTime': _.constant(1337756400000) }), false);
strictEqual(_.isEqual(new Date('a'), new Date('a')), false);
});
test('should perform comparisons between error objects', 1, function() {
var pairs = _.map([
'Error',
'EvalError',
'RangeError',
'ReferenceError',
'SyntaxError',
'TypeError',
'URIError'
], function(type, index, errorTypes) {
var otherType = errorTypes[++index % errorTypes.length],
CtorA = root[type],
CtorB = root[otherType];
return [new CtorA('a'), new CtorA('a'), new CtorB('a'), new CtorB('b')];
});
var expected = _.times(pairs.length, _.constant([true, false, false]));
var actual = _.map(pairs, function(pair) {
return [_.isEqual(pair[0], pair[1]), _.isEqual(pair[0], pair[2]), _.isEqual(pair[2], pair[3])];
});
deepEqual(actual, expected);
});
test('should perform comparisons between functions', 2, function() {
function a() { return 1 + 2; }
function b() { return 1 + 2; }
strictEqual(_.isEqual(a, a), true);
strictEqual(_.isEqual(a, b), false);
});
test('should perform comparisons between regexes', 5, function() {
strictEqual(_.isEqual(/x/gim, /x/gim), true);
strictEqual(_.isEqual(/x/gim, /x/mgi), true);
strictEqual(_.isEqual(/x/gi, /x/g), false);
strictEqual(_.isEqual(/x/, /y/), false);
strictEqual(_.isEqual(/x/g, { 'global': true, 'ignoreCase': false, 'multiline': false, 'source': 'x' }), false);
});
test('should perform comparisons between typed arrays', 1, function() {
var pairs = _.map(typedArrays, function(type, index) {
var otherType = typedArrays[(index + 1) % typedArrays.length],
CtorA = root[type] || function(n) { this.n = n; },
CtorB = root[otherType] || function(n) { this.n = n; },
bufferA = root[type] ? new ArrayBuffer(8) : 8,
bufferB = root[otherType] ? new ArrayBuffer(8) : 8,
bufferC = root[otherType] ? new ArrayBuffer(16) : 16;
return [new CtorA(bufferA), new CtorA(bufferA), new CtorB(bufferB), new CtorB(bufferC)];
});
var expected = _.times(pairs.length, _.constant([true, false, false]));
var actual = _.map(pairs, function(pair) {
return [_.isEqual(pair[0], pair[1]), _.isEqual(pair[0], pair[2]), _.isEqual(pair[2], pair[3])];
});
deepEqual(actual, expected);
});
test('should avoid common type coercions', 9, function() {
strictEqual(_.isEqual(true, Object(false)), false);
strictEqual(_.isEqual(Object(false), Object(0)), false);
strictEqual(_.isEqual(false, Object('')), false);
strictEqual(_.isEqual(Object(36), Object('36')), false);
strictEqual(_.isEqual(0, ''), false);
strictEqual(_.isEqual(1, true), false);
strictEqual(_.isEqual(1337756400000, new Date(2012, 4, 23)), false);
strictEqual(_.isEqual('36', 36), false);
strictEqual(_.isEqual(36, '36'), false);
});
test('fixes the JScript `[[DontEnum]]` bug (test in IE < 9)', 1, function() {
strictEqual(_.isEqual(shadowObject, {}), false);
});
test('should return `false` for objects with custom `toString` methods', 1, function() {
var primitive,
object = { 'toString': function() { return primitive; } },
values = [true, null, 1, 'a', undefined],
expected = _.map(values, _.constant(false));
var actual = _.map(values, function(value) {
primitive = value;
return _.isEqual(object, value);
});
deepEqual(actual, expected);
});
test('should provide the correct `customizer` arguments', 1, function() {
var argsList = [],
object1 = { 'a': [1, 2], 'b': null },
object2 = { 'a': [1, 2], 'b': null };
object1.b = object2;
object2.b = object1;
var expected = [
[object1, object2],
[object1.a, object2.a, 'a'],
[object1.a[0], object2.a[0], 0],
[object1.a[1], object2.a[1], 1],
[object1.b, object2.b, 'b'],
[object1.b.a, object2.b.a, 'a'],
[object1.b.a[0], object2.b.a[0], 0],
[object1.b.a[1], object2.b.a[1], 1],
[object1.b.b, object2.b.b, 'b']
];
_.isEqual(object1, object2, function() {
argsList.push(slice.call(arguments));
});
deepEqual(argsList, expected);
});
test('should set the `this` binding', 1, function() {
var actual = _.isEqual('a', 'b', function(a, b) {
return this[a] == this[b];
}, { 'a': 1, 'b': 1 });
strictEqual(actual, true);
});
test('should handle comparisons if `customizer` returns `undefined`', 1, function() {
strictEqual(_.isEqual('a', 'a', _.noop), true);
});
test('should not handle comparisons if `customizer` returns `true`', 3, function() {
var customizer = function(value) {
return _.isString(value) || undefined;
};
strictEqual(_.isEqual('a', 'b', customizer), true);
strictEqual(_.isEqual(['a'], ['b'], customizer), true);
strictEqual(_.isEqual({ '0': 'a' }, { '0': 'b' }, customizer), true);
});
test('should return a boolean value even if `customizer` does not', 2, function() {
var actual = _.isEqual('a', 'b', _.constant('c'));
strictEqual(actual, true);
var values = _.without(falsey, undefined),
expected = _.map(values, _.constant(false));
actual = [];
_.each(values, function(value) {
actual.push(_.isEqual('a', 'a', _.constant(value)));
});
deepEqual(actual, expected);
});
test('should ensure `customizer` is a function', 1, function() {
var array = [1, 2, 3],
eq = _.partial(_.isEqual, array),
actual = _.map([array, [1, 0, 3]], eq);
deepEqual(actual, [true, false]);
});
test('should work as an iteratee for `_.every`', 1, function() {
var actual = _.every([1, 1, 1], _.partial(_.isEqual, 1));
ok(actual);
});
test('should treat objects created by `Object.create(null)` like any other plain object', 2, function() {
function Foo() { this.a = 1; }
Foo.prototype.constructor = null;
var object2 = { 'a': 1 };
strictEqual(_.isEqual(new Foo, object2), false);
if (create) {
var object1 = create(null);
object1.a = 1;
strictEqual(_.isEqual(object1, object2), true);
}
else {
skipTest();
}
});
test('should return `true` for like-objects from different documents', 4, function() {
// Ensure `_._object` is assigned (unassigned in Opera 10.00).
if (_._object) {
strictEqual(_.isEqual({ 'a': 1, 'b': 2, 'c': 3 }, _._object), true);
strictEqual(_.isEqual({ 'a': 1, 'b': 2, 'c': 2 }, _._object), false);
strictEqual(_.isEqual([1, 2, 3], _._array), true);
strictEqual(_.isEqual([1, 2, 2], _._array), false);
}
else {
skipTest(4);
}
});
test('should not error on DOM elements', 1, function() {
if (document) {
var element1 = document.createElement('div'),
element2 = element1.cloneNode(true);
try {
strictEqual(_.isEqual(element1, element2), false);
} catch(e) {
ok(false, e.message);
}
}
else {
skipTest();
}
});
test('should perform comparisons between wrapped values', 32, function() {
var stamp = +new Date;
var values = [
[[1, 2], [1, 2], [1, 2, 3]],
[true, true, false],
[new Date(stamp), new Date(stamp), new Date(stamp - 100)],
[{ 'a': 1, 'b': 2 }, { 'a': 1, 'b': 2 }, { 'a': 1, 'b': 1 }],
[1, 1, 2],
[NaN, NaN, Infinity],
[/x/, /x/, /x/i],
['a', 'a', 'A']
];
_.each(values, function(vals) {
if (!isNpm) {
var wrapped1 = _(vals[0]),
wrapped2 = _(vals[1]),
actual = wrapped1.isEqual(wrapped2);
strictEqual(actual, true);
strictEqual(_.isEqual(_(actual), _(true)), true);
wrapped1 = _(vals[0]);
wrapped2 = _(vals[2]);
actual = wrapped1.isEqual(wrapped2);
strictEqual(actual, false);
strictEqual(_.isEqual(_(actual), _(false)), true);
}
else {
skipTest(4);
}
});
});
test('should perform comparisons between wrapped and non-wrapped values', 4, function() {
if (!isNpm) {
var object1 = _({ 'a': 1, 'b': 2 }),
object2 = { 'a': 1, 'b': 2 };
strictEqual(object1.isEqual(object2), true);
strictEqual(_.isEqual(object1, object2), true);
object1 = _({ 'a': 1, 'b': 2 });
object2 = { 'a': 1, 'b': 1 };
strictEqual(object1.isEqual(object2), false);
strictEqual(_.isEqual(object1, object2), false);
}
else {
skipTest(4);
}
});
test('should return an unwrapped value when implicitly chaining', 1, function() {
if (!isNpm) {
strictEqual(_('a').isEqual('a'), true);
}
else {
skipTest();
}
});
test('should return a wrapped value when explicitly chaining', 1, function() {
if (!isNpm) {
ok(_('a').chain().isEqual('a') instanceof _);
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isError');
(function() {
var args = arguments;
test('should return `true` for error objects', 1, function() {
var expected = _.map(errors, _.constant(true));
var actual = _.map(errors, function(error) {
return _.isError(error) === true;
});
deepEqual(actual, expected);
});
test('should return `false` for non error objects', 12, function() {
var expected = _.map(falsey, _.constant(false));
var actual = _.map(falsey, function(value, index) {
return index ? _.isError(value) : _.isError();
});
deepEqual(actual, expected);
strictEqual(_.isError(args), false);
strictEqual(_.isError([1, 2, 3]), false);
strictEqual(_.isError(true), false);
strictEqual(_.isError(new Date), false);
strictEqual(_.isError(_), false);
strictEqual(_.isError(slice), false);
strictEqual(_.isError({ 'a': 1 }), false);
strictEqual(_.isError(1), false);
strictEqual(_.isError(NaN), false);
strictEqual(_.isError(/x/), false);
strictEqual(_.isError('a'), false);
});
test('should work with an error object from another realm', 1, function() {
if (_._object) {
var expected = _.map(_._errors, _.constant(true));
var actual = _.map(_._errors, function(error) {
return _.isError(error) === true;
});
deepEqual(actual, expected);
}
else {
skipTest();
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isFinite');
(function() {
var args = arguments;
test('should return `true` for finite values', 1, function() {
var values = [0, 1, 3.14, -1],
expected = _.map(values, _.constant(true));
var actual = _.map(values, function(value) {
return _.isFinite(value);
});
deepEqual(actual, expected);
});
test('should return `false` for non-finite values', 9, function() {
var values = [NaN, Infinity, -Infinity, Object(1)],
expected = _.map(values, _.constant(false));
var actual = _.map(values, function(value) {
return _.isFinite(value);
});
deepEqual(actual, expected);
strictEqual(_.isFinite(args), false);
strictEqual(_.isFinite([1, 2, 3]), false);
strictEqual(_.isFinite(true), false);
strictEqual(_.isFinite(new Date), false);
strictEqual(_.isFinite(new Error), false);
strictEqual(_.isFinite({ 'a': 1 }), false);
strictEqual(_.isFinite(/x/), false);
strictEqual(_.isFinite('a'), false);
});
test('should return `false` for non-numeric values', 1, function() {
var values = [undefined, [], true, new Date, new Error, '', ' ', '2px'],
expected = _.map(values, _.constant(false));
var actual = _.map(values, function(value) {
return _.isFinite(value);
});
deepEqual(actual, expected);
});
test('should return `false` for numeric string values', 1, function() {
var values = ['2', '0', '08'],
expected = _.map(values, _.constant(false));
var actual = _.map(values, function(value) {
return _.isFinite(value);
});
deepEqual(actual, expected);
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isFunction');
(function() {
var args = arguments;
test('should return `true` for functions', 2, function() {
strictEqual(_.isFunction(_), true);
strictEqual(_.isFunction(slice), true);
});
test('should return `true` for typed array constructors', 1, function() {
var expected = _.map(typedArrays, function(type) {
return objToString.call(root[type]) == funcTag;
});
var actual = _.map(typedArrays, function(type) {
return _.isFunction(root[type]);
});
deepEqual(actual, expected);
});
test('should return `false` for non-functions', 11, function() {
var expected = _.map(falsey, _.constant(false));
var actual = _.map(falsey, function(value, index) {
return index ? _.isFunction(value) : _.isFunction();
});
deepEqual(actual, expected);
strictEqual(_.isFunction(args), false);
strictEqual(_.isFunction([1, 2, 3]), false);
strictEqual(_.isFunction(true), false);
strictEqual(_.isFunction(new Date), false);
strictEqual(_.isFunction(new Error), false);
strictEqual(_.isFunction({ 'a': 1 }), false);
strictEqual(_.isFunction(1), false);
strictEqual(_.isFunction(NaN), false);
strictEqual(_.isFunction(/x/), false);
strictEqual(_.isFunction('a'), false);
});
test('should work using its fallback', 3, function() {
if (!isModularize) {
// Simulate native `Uint8Array` constructor with a `toStringTag` of
// 'Function' and a `typeof` result of 'object'.
var lodash = _.runInContext({
'Function': {
'prototype': {
'toString': function() {
return _.has(this, 'toString') ? this.toString() : fnToString.call(this);
}
}
},
'Object': _.assign(function(value) {
return Object(value);
}, {
'prototype': {
'toString': _.assign(function() {
return _.has(this, '@@toStringTag') ? this['@@toStringTag'] : objToString.call(this);
}, {
'toString': function() {
return String(toString);
}
})
}
}),
'Uint8Array': {
'@@toStringTag': funcTag,
'toString': function() {
return String(Uint8Array || Array);
}
}
});
strictEqual(lodash.isFunction(slice), true);
strictEqual(lodash.isFunction(/x/), false);
strictEqual(lodash.isFunction(Uint8Array), objToString.call(Uint8Array) == funcTag);
}
else {
skipTest(3);
}
});
test('should work with host objects in IE 8 document mode (test in IE 11)', 2, function() {
// Trigger a Chakra JIT bug.
// See https://github.com/jashkenas/underscore/issues/1621.
_.each([body, xml], function(object) {
if (object) {
_.times(100, _.isFunction);
strictEqual(_.isFunction(object), false);
}
else {
skipTest();
}
});
});
test('should work with a function from another realm', 1, function() {
if (_._object) {
strictEqual(_.isFunction(_._function), true);
}
else {
skipTest();
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isMatch');
(function() {
test('should perform a deep comparison between `object` and `source`', 5, function() {
var object = { 'a': 1, 'b': 2, 'c': 3 };
strictEqual(_.isMatch(object, { 'a': 1 }), true);
strictEqual(_.isMatch(object, { 'b': 1 }), false);
strictEqual(_.isMatch(object, { 'a': 1, 'c': 3 }), true);
strictEqual(_.isMatch(object, { 'c': 3, 'd': 4 }), false);
object = { 'a': { 'b': { 'c': 1, 'd': 2 }, 'e': 3 }, 'f': 4 };
strictEqual(_.isMatch(object, { 'a': { 'b': { 'c': 1 } } }), true);
});
test('should match inherited `object` properties', 1, function() {
function Foo() { this.a = 1; }
Foo.prototype.b = 2;
strictEqual(_.isMatch({ 'a': new Foo }, { 'a': { 'b': 2 } }), true);
});
test('should match `-0` as `0`', 2, function() {
var object1 = { 'a': -0 },
object2 = { 'a': 0 };
strictEqual(_.isMatch(object1, object2), true);
strictEqual(_.isMatch(object2, object1), true);
});
test('should not match by inherited `source` properties', 1, function() {
function Foo() { this.a = 1; }
Foo.prototype.b = 2;
var objects = [{ 'a': 1 }, { 'a': 1, 'b': 2 }],
source = new Foo,
expected = _.map(objects, _.constant(true));
var actual = _.map(objects, function(object) {
return _.isMatch(object, source);
});
deepEqual(actual, expected);
});
test('should return `false` when `object` is nullish', 1, function() {
var values = [null, undefined],
expected = _.map(values, _.constant(false)),
source = { 'a': 1 };
var actual = _.map(values, function(value) {
try {
return _.isMatch(value, source);
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should return `true` when comparing an empty `source`', 1, function() {
var object = { 'a': 1 },
expected = _.map(empties, _.constant(true));
var actual = _.map(empties, function(value) {
return _.isMatch(object, value);
});
deepEqual(actual, expected);
});
test('should compare a variety of `source` values', 2, function() {
var object1 = { 'a': false, 'b': true, 'c': '3', 'd': 4, 'e': [5], 'f': { 'g': 6 } },
object2 = { 'a': 0, 'b': 1, 'c': 3, 'd': '4', 'e': ['5'], 'f': { 'g': '6' } };
strictEqual(_.isMatch(object1, object1), true);
strictEqual(_.isMatch(object1, object2), false);
});
test('should work with a function for `source`', 1, function() {
function source() {}
source.a = 1;
source.b = function() {};
source.c = 3;
var objects = [{ 'a': 1 }, { 'a': 1, 'b': source.b, 'c': 3 }];
var actual = _.map(objects, function(object) {
return _.isMatch(object, source);
});
deepEqual(actual, [false, true]);
});
test('should return `true` when comparing a `source` of empty arrays and objects', 1, function() {
var objects = [{ 'a': [1], 'b': { 'c': 1 } }, { 'a': [2, 3], 'b': { 'd': 2 } }],
source = { 'a': [], 'b': {} };
var actual = _.filter(objects, function(object) {
return _.isMatch(object, source);
});
deepEqual(actual, objects);
});
test('should return `true` when comparing an empty `source` to a nullish `object`', 1, function() {
var values = [null, undefined],
expected = _.map(values, _.constant(true)),
source = {};
var actual = _.map(values, function(value) {
try {
return _.isMatch(value, source);
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should search arrays of `source` for values', 3, function() {
var objects = [{ 'a': ['b'] }, { 'a': ['c', 'd'] }],
source = { 'a': ['d'] },
predicate = function(object) { return _.isMatch(object, source); },
actual = _.filter(objects, predicate);
deepEqual(actual, [objects[1]]);
source = { 'a': ['b', 'd'] };
actual = _.filter(objects, predicate);
deepEqual(actual, []);
source = { 'a': ['d', 'b'] };
actual = _.filter(objects, predicate);
deepEqual(actual, []);
});
test('should perform a partial comparison of all objects within arrays of `source`', 1, function() {
var source = { 'a': [{ 'b': 1 }, { 'b': 4, 'c': 5 }] };
var objects = [
{ 'a': [{ 'b': 1, 'c': 2 }, { 'b': 4, 'c': 5, 'd': 6 }] },
{ 'a': [{ 'b': 1, 'c': 2 }, { 'b': 4, 'c': 6, 'd': 7 }] }
];
var actual = _.filter(objects, function(object) {
return _.isMatch(object, source);
});
deepEqual(actual, [objects[0]]);
});
test('should handle a `source` with `undefined` values', 2, function() {
var objects = [{ 'a': 1 }, { 'a': 1, 'b': 1 }, { 'a': 1, 'b': undefined }],
source = { 'b': undefined },
predicate = function(object) { return _.isMatch(object, source); },
actual = _.map(objects, predicate),
expected = [false, false, true];
deepEqual(actual, expected);
source = { 'a': { 'c': undefined } };
objects = [{ 'a': { 'b': 1 } }, { 'a':{ 'b':1, 'c': 1 } }, { 'a': { 'b': 1, 'c': undefined } }];
actual = _.map(objects, predicate);
deepEqual(actual, expected);
});
test('should handle a `source` with `undefined` values', 2, function() {
var matches = _.matches({ 'b': undefined }),
objects = [{ 'a': 1 }, { 'a': 1, 'b': 1 }, { 'a': 1, 'b': undefined }],
actual = _.map(objects, matches),
expected = [false, false, true];
deepEqual(actual, expected);
matches = _.matches({ 'a': { 'c': undefined } });
objects = [{ 'a': { 'b': 1 } }, { 'a': { 'b':1, 'c': 1 } }, { 'a': { 'b': 1, 'c': undefined } }];
actual = _.map(objects, matches);
deepEqual(actual, expected);
});
test('should match properties when `value` is a function', 1, function() {
function Foo() {}
Foo.a = { 'b': 1, 'c': 2 };
var matches = _.matches({ 'a': { 'b': 1 } });
strictEqual(matches(Foo), true);
});
test('should match properties when `value` is not a plain object', 1, function() {
function Foo(object) { _.assign(this, object); }
var object = new Foo({ 'a': new Foo({ 'b': 1, 'c': 2 }) }),
matches = _.matches({ 'a': { 'b': 1 } });
strictEqual(matches(object), true);
});
test('should work with a function for `source`', 1, function() {
function source() {}
source.a = 1;
source.b = function() {};
source.c = 3;
var matches = _.matches(source),
objects = [{ 'a': 1 }, { 'a': 1, 'b': source.b, 'c': 3 }],
actual = _.map(objects, matches);
deepEqual(actual, [false, true]);
});
test('should match problem JScript properties (test in IE < 9)', 1, function() {
var objects = [{}, shadowObject];
var actual = _.map(objects, function(object) {
return _.isMatch(object, shadowObject);
});
deepEqual(actual, [false, true]);
});
test('should provide the correct `customizer` arguments', 1, function() {
var argsList = [],
object1 = { 'a': [1, 2], 'b': null },
object2 = { 'a': [1, 2], 'b': null };
object1.b = object2;
object2.b = object1;
var expected = [
[object1.a, object2.a, 'a'],
[object1.a[0], object2.a[0], 0],
[object1.a[1], object2.a[1], 1],
[object1.b, object2.b, 'b'],
[object1.b.a, object2.b.a, 'a'],
[object1.b.a[0], object2.b.a[0], 0],
[object1.b.a[1], object2.b.a[1], 1],
[object1.b.b, object2.b.b, 'b'],
[object1.b.b.a, object2.b.b.a, 'a'],
[object1.b.b.a[0], object2.b.b.a[0], 0],
[object1.b.b.a[1], object2.b.b.a[1], 1],
[object1.b.b.b, object2.b.b.b, 'b']
];
_.isMatch(object1, object2, function() {
argsList.push(slice.call(arguments));
});
deepEqual(argsList, expected);
});
test('should set the `this` binding', 1, function() {
var actual = _.isMatch({ 'a': 1 }, { 'a': 2 }, function(a, b) {
return this[a] == this[b];
}, { 'a': 1, 'b': 1 });
strictEqual(actual, true);
});
test('should handle comparisons if `customizer` returns `undefined`', 1, function() {
strictEqual(_.isMatch({ 'a': 1 }, { 'a': 1 }, _.noop), true);
});
test('should return a boolean value even if `customizer` does not', 2, function() {
var object = { 'a': 1 },
actual = _.isMatch(object, { 'a': 1 }, _.constant('a'));
strictEqual(actual, true);
var expected = _.map(falsey, _.constant(false));
actual = [];
_.each(falsey, function(value) {
actual.push(_.isMatch(object, { 'a': 2 }, _.constant(value)));
});
deepEqual(actual, expected);
});
test('should ensure `customizer` is a function', 1, function() {
var object = { 'a': 1 },
matches = _.partial(_.isMatch, object),
actual = _.map([object, { 'a': 2 }], matches);
deepEqual(actual, [true, false]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isNaN');
(function() {
var args = arguments;
test('should return `true` for NaNs', 2, function() {
strictEqual(_.isNaN(NaN), true);
strictEqual(_.isNaN(Object(NaN)), true);
});
test('should return `false` for non-NaNs', 12, function() {
var expected = _.map(falsey, function(value) { return value !== value; });
var actual = _.map(falsey, function(value, index) {
return index ? _.isNaN(value) : _.isNaN();
});
deepEqual(actual, expected);
strictEqual(_.isNaN(args), false);
strictEqual(_.isNaN([1, 2, 3]), false);
strictEqual(_.isNaN(true), false);
strictEqual(_.isNaN(new Date), false);
strictEqual(_.isNaN(new Error), false);
strictEqual(_.isNaN(_), false);
strictEqual(_.isNaN(slice), false);
strictEqual(_.isNaN({ 'a': 1 }), false);
strictEqual(_.isNaN(1), false);
strictEqual(_.isNaN(/x/), false);
strictEqual(_.isNaN('a'), false);
});
test('should work with `NaN` from another realm', 1, function() {
if (_._object) {
strictEqual(_.isNaN(_._nan), true);
}
else {
skipTest();
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isNative');
(function() {
var args = arguments;
test('should return `true` for native methods', 6, function() {
_.each([Array, create, root.encodeURI, slice, Uint8Array], function(func) {
if (func) {
strictEqual(_.isNative(func), true);
}
else {
skipTest();
}
});
if (body) {
strictEqual(_.isNative(body.cloneNode), true);
}
else {
skipTest();
}
});
test('should return `false` for non-native methods', 12, function() {
var expected = _.map(falsey, _.constant(false));
var actual = _.map(falsey, function(value, index) {
return index ? _.isNative(value) : _.isNative();
});
deepEqual(actual, expected);
strictEqual(_.isNative(args), false);
strictEqual(_.isNative([1, 2, 3]), false);
strictEqual(_.isNative(true), false);
strictEqual(_.isNative(new Date), false);
strictEqual(_.isNative(new Error), false);
strictEqual(_.isNative(_), false);
strictEqual(_.isNative({ 'a': 1 }), false);
strictEqual(_.isNative(1), false);
strictEqual(_.isNative(NaN), false);
strictEqual(_.isNative(/x/), false);
strictEqual(_.isNative('a'), false);
});
test('should work with native functions from another realm', 2, function() {
if (_._element) {
strictEqual(_.isNative(_._element.cloneNode), true);
}
else {
skipTest();
}
if (_._object) {
strictEqual(_.isNative(_._object.valueOf), true);
}
else {
skipTest();
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isNull');
(function() {
var args = arguments;
test('should return `true` for nulls', 1, function() {
strictEqual(_.isNull(null), true);
});
test('should return `false` for non-nulls', 13, function() {
var expected = _.map(falsey, function(value) { return value === null; });
var actual = _.map(falsey, function(value, index) {
return index ? _.isNull(value) : _.isNull();
});
deepEqual(actual, expected);
strictEqual(_.isNull(args), false);
strictEqual(_.isNull([1, 2, 3]), false);
strictEqual(_.isNull(true), false);
strictEqual(_.isNull(new Date), false);
strictEqual(_.isNull(new Error), false);
strictEqual(_.isNull(_), false);
strictEqual(_.isNull(slice), false);
strictEqual(_.isNull({ 'a': 1 }), false);
strictEqual(_.isNull(1), false);
strictEqual(_.isNull(NaN), false);
strictEqual(_.isNull(/x/), false);
strictEqual(_.isNull('a'), false);
});
test('should work with nulls from another realm', 1, function() {
if (_._object) {
strictEqual(_.isNull(_._null), true);
}
else {
skipTest();
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isNumber');
(function() {
var args = arguments;
test('should return `true` for numbers', 3, function() {
strictEqual(_.isNumber(0), true);
strictEqual(_.isNumber(Object(0)), true);
strictEqual(_.isNumber(NaN), true);
});
test('should return `false` for non-numbers', 11, function() {
var expected = _.map(falsey, function(value) { return typeof value == 'number'; });
var actual = _.map(falsey, function(value, index) {
return index ? _.isNumber(value) : _.isNumber();
});
deepEqual(actual, expected);
strictEqual(_.isNumber(args), false);
strictEqual(_.isNumber([1, 2, 3]), false);
strictEqual(_.isNumber(true), false);
strictEqual(_.isNumber(new Date), false);
strictEqual(_.isNumber(new Error), false);
strictEqual(_.isNumber(_), false);
strictEqual(_.isNumber(slice), false);
strictEqual(_.isNumber({ 'a': 1 }), false);
strictEqual(_.isNumber(/x/), false);
strictEqual(_.isNumber('a'), false);
});
test('should work with numbers from another realm', 1, function() {
if (_._object) {
strictEqual(_.isNumber(_._number), true);
}
else {
skipTest();
}
});
test('should avoid `[xpconnect wrapped native prototype]` in Firefox', 1, function() {
strictEqual(_.isNumber(+"2"), true);
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isObject');
(function() {
var args = arguments;
test('should return `true` for objects', 12, function() {
strictEqual(_.isObject(args), true);
strictEqual(_.isObject([1, 2, 3]), true);
strictEqual(_.isObject(Object(false)), true);
strictEqual(_.isObject(new Date), true);
strictEqual(_.isObject(new Error), true);
strictEqual(_.isObject(_), true);
strictEqual(_.isObject(slice), true);
strictEqual(_.isObject({ 'a': 1 }), true);
strictEqual(_.isObject(Object(0)), true);
strictEqual(_.isObject(/x/), true);
strictEqual(_.isObject(Object('a')), true);
if (document) {
strictEqual(_.isObject(body), true);
} else {
skipTest();
}
});
test('should return `false` for non-objects', 1, function() {
var symbol = (Symbol || noop)(),
values = falsey.concat(true, 1, 'a', symbol),
expected = _.map(values, _.constant(false));
var actual = _.map(values, function(value, index) {
return index ? _.isObject(value) : _.isObject();
});
deepEqual(actual, expected);
});
test('should work with objects from another realm', 8, function() {
if (_._element) {
strictEqual(_.isObject(_._element), true);
}
else {
skipTest();
}
if (_._object) {
strictEqual(_.isObject(_._object), true);
strictEqual(_.isObject(_._boolean), true);
strictEqual(_.isObject(_._date), true);
strictEqual(_.isObject(_._function), true);
strictEqual(_.isObject(_._number), true);
strictEqual(_.isObject(_._regexp), true);
strictEqual(_.isObject(_._string), true);
}
else {
skipTest(7);
}
});
test('should avoid V8 bug #2291 (test in Chrome 19-20)', 1, function() {
// Trigger a V8 JIT bug.
// See https://code.google.com/p/v8/issues/detail?id=2291.
var object = {};
// 1: Useless comparison statement, this is half the trigger.
object == object;
// 2: Initial check with object, this is the other half of the trigger.
_.isObject(object);
strictEqual(_.isObject('x'), false);
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isPlainObject');
(function() {
var element = document && document.createElement('div');
test('should detect plain objects', 5, function() {
function Foo(a) {
this.a = 1;
}
strictEqual(_.isPlainObject({}), true);
strictEqual(_.isPlainObject({ 'a': 1 }), true);
strictEqual(_.isPlainObject({ 'constructor': Foo }), true);
strictEqual(_.isPlainObject([1, 2, 3]), false);
strictEqual(_.isPlainObject(new Foo(1)), false);
});
test('should return `true` for objects with a `[[Prototype]]` of `null`', 1, function() {
if (create) {
strictEqual(_.isPlainObject(create(null)), true);
} else {
skipTest();
}
});
test('should return `true` for plain objects with a custom `valueOf` property', 2, function() {
strictEqual(_.isPlainObject({ 'valueOf': 0 }), true);
if (element) {
var valueOf = element.valueOf;
element.valueOf = 0;
strictEqual(_.isPlainObject(element), false);
element.valueOf = valueOf;
}
else {
skipTest();
}
});
test('should return `false` for DOM elements', 1, function() {
if (element) {
strictEqual(_.isPlainObject(element), false);
} else {
skipTest();
}
});
test('should return `false` for Object objects without a `toStringTag` of "Object"', 3, function() {
strictEqual(_.isPlainObject(arguments), false);
strictEqual(_.isPlainObject(Error), false);
strictEqual(_.isPlainObject(Math), false);
});
test('should return `false` for non-objects', 3, function() {
var expected = _.map(falsey, _.constant(false));
var actual = _.map(falsey, function(value, index) {
return index ? _.isPlainObject(value) : _.isPlainObject();
});
deepEqual(actual, expected);
strictEqual(_.isPlainObject(true), false);
strictEqual(_.isPlainObject('a'), false);
});
test('should work with objects from another realm', 1, function() {
if (_._object) {
strictEqual(_.isPlainObject(_._object), true);
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isRegExp');
(function() {
var args = arguments;
test('should return `true` for regexes', 2, function() {
strictEqual(_.isRegExp(/x/), true);
strictEqual(_.isRegExp(RegExp('x')), true);
});
test('should return `false` for non-regexes', 12, function() {
var expected = _.map(falsey, _.constant(false));
var actual = _.map(falsey, function(value, index) {
return index ? _.isRegExp(value) : _.isRegExp();
});
deepEqual(actual, expected);
strictEqual(_.isRegExp(args), false);
strictEqual(_.isRegExp([1, 2, 3]), false);
strictEqual(_.isRegExp(true), false);
strictEqual(_.isRegExp(new Date), false);
strictEqual(_.isRegExp(new Error), false);
strictEqual(_.isRegExp(_), false);
strictEqual(_.isRegExp(slice), false);
strictEqual(_.isRegExp({ 'a': 1 }), false);
strictEqual(_.isRegExp(1), false);
strictEqual(_.isRegExp(NaN), false);
strictEqual(_.isRegExp('a'), false);
});
test('should work with regexes from another realm', 1, function() {
if (_._object) {
strictEqual(_.isRegExp(_._regexp), true);
}
else {
skipTest();
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isString');
(function() {
var args = arguments;
test('should return `true` for strings', 2, function() {
strictEqual(_.isString('a'), true);
strictEqual(_.isString(Object('a')), true);
});
test('should return `false` for non-strings', 12, function() {
var expected = _.map(falsey, function(value) { return value === ''; });
var actual = _.map(falsey, function(value, index) {
return index ? _.isString(value) : _.isString();
});
deepEqual(actual, expected);
strictEqual(_.isString(args), false);
strictEqual(_.isString([1, 2, 3]), false);
strictEqual(_.isString(true), false);
strictEqual(_.isString(new Date), false);
strictEqual(_.isString(new Error), false);
strictEqual(_.isString(_), false);
strictEqual(_.isString(slice), false);
strictEqual(_.isString({ '0': 1, 'length': 1 }), false);
strictEqual(_.isString(1), false);
strictEqual(_.isString(NaN), false);
strictEqual(_.isString(/x/), false);
});
test('should work with strings from another realm', 1, function() {
if (_._object) {
strictEqual(_.isString(_._string), true);
}
else {
skipTest();
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isTypedArray');
(function() {
var args = arguments;
test('should return `true` for typed arrays', 1, function() {
var expected = _.map(typedArrays, function(type) {
return type in root;
});
var actual = _.map(typedArrays, function(type) {
var Ctor = root[type];
return Ctor ? _.isTypedArray(new Ctor(new ArrayBuffer(8))) : false;
});
deepEqual(actual, expected);
});
test('should return `false` for non typed arrays', 13, function() {
var expected = _.map(falsey, _.constant(false));
var actual = _.map(falsey, function(value, index) {
return index ? _.isTypedArray(value) : _.isTypedArray();
});
deepEqual(actual, expected);
strictEqual(_.isTypedArray(args), false);
strictEqual(_.isTypedArray([1, 2, 3]), false);
strictEqual(_.isTypedArray(true), false);
strictEqual(_.isTypedArray(new Date), false);
strictEqual(_.isTypedArray(new Error), false);
strictEqual(_.isTypedArray(_), false);
strictEqual(_.isTypedArray(slice), false);
strictEqual(_.isTypedArray({ 'a': 1 }), false);
strictEqual(_.isTypedArray(1), false);
strictEqual(_.isTypedArray(NaN), false);
strictEqual(_.isTypedArray(/x/), false);
strictEqual(_.isTypedArray('a'), false);
});
test('should work with typed arrays from another realm', 1, function() {
if (_._object) {
var props = _.map(typedArrays, function(type) {
return '_' + type.toLowerCase();
});
var expected = _.map(props, function(key) {
return key in _;
});
var actual = _.map(props, function(key) {
var value = _[key];
return value ? _.isTypedArray(value) : false;
});
deepEqual(actual, expected);
}
else {
skipTest();
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isUndefined');
(function() {
var args = arguments;
test('should return `true` for `undefined` values', 2, function() {
strictEqual(_.isUndefined(), true);
strictEqual(_.isUndefined(undefined), true);
});
test('should return `false` for non `undefined` values', 13, function() {
var expected = _.map(falsey, function(value) { return value === undefined; });
var actual = _.map(falsey, function(value, index) {
return index ? _.isUndefined(value) : _.isUndefined();
});
deepEqual(actual, expected);
strictEqual(_.isUndefined(args), false);
strictEqual(_.isUndefined([1, 2, 3]), false);
strictEqual(_.isUndefined(true), false);
strictEqual(_.isUndefined(new Date), false);
strictEqual(_.isUndefined(new Error), false);
strictEqual(_.isUndefined(_), false);
strictEqual(_.isUndefined(slice), false);
strictEqual(_.isUndefined({ 'a': 1 }), false);
strictEqual(_.isUndefined(1), false);
strictEqual(_.isUndefined(NaN), false);
strictEqual(_.isUndefined(/x/), false);
strictEqual(_.isUndefined('a'), false);
});
test('should work with `undefined` from another realm', 1, function() {
if (_._object) {
strictEqual(_.isUndefined(_._undefined), true);
}
else {
skipTest();
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('isType checks');
(function() {
test('should return `false` for subclassed values', 8, function() {
var funcs = [
'isArray', 'isBoolean', 'isDate', 'isError',
'isFunction', 'isNumber', 'isRegExp', 'isString'
];
_.each(funcs, function(methodName) {
function Foo() {}
Foo.prototype = root[methodName.slice(2)].prototype;
var object = new Foo;
if (objToString.call(object) == objectTag) {
strictEqual(_[methodName](object), false, '`_.' + methodName + '` returns `false`');
} else {
skipTest();
}
});
});
test('should not error on host objects (test in IE)', 15, function() {
var funcs = [
'isArguments', 'isArray', 'isBoolean', 'isDate', 'isElement',
'isError', 'isFinite', 'isFunction', 'isNaN', 'isNull', 'isNumber',
'isObject', 'isRegExp', 'isString', 'isUndefined'
];
_.each(funcs, function(methodName) {
if (xml) {
var pass = true;
try {
_[methodName](xml);
} catch(e) {
pass = false;
}
ok(pass, '`_.' + methodName + '` should not error');
}
else {
skipTest();
}
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('keys methods');
_.each(['keys', 'keysIn'], function(methodName) {
var args = arguments,
func = _[methodName],
isKeys = methodName == 'keys';
test('`_.' + methodName + '` should return the keys of an object', 1, function() {
deepEqual(func({ 'a': 1, 'b': 1 }).sort(), ['a', 'b']);
});
test('`_.' + methodName + '` should coerce primitives to objects (test in IE 9)', 2, function() {
deepEqual(func('abc').sort(), ['0', '1', '2']);
if (!isKeys) {
// IE 9 doesn't box numbers in for-in loops.
Number.prototype.a = 1;
deepEqual(func(0).sort(), ['a']);
delete Number.prototype.a;
}
else {
skipTest();
}
});
test('`_.' + methodName + '` should treat sparse arrays as dense', 1, function() {
var array = [1];
array[2] = 3;
deepEqual(func(array).sort(), ['0', '1', '2']);
});
test('`_.' + methodName + '` should return an empty array for nullish values', 2, function() {
objectProto.a = 1;
_.each([null, undefined], function(value) {
deepEqual(func(value), []);
});
delete objectProto.a;
});
test('`_.' + methodName + '` should return keys for custom properties on arrays', 1, function() {
var array = [1];
array.a = 1;
deepEqual(func(array).sort(), ['0', 'a']);
});
test('`_.' + methodName + '` should ' + (isKeys ? 'not' : '') + ' include inherited properties of arrays', 1, function() {
var expected = isKeys ? ['0'] : ['0', 'a'];
arrayProto.a = 1;
deepEqual(func([1]).sort(), expected);
delete arrayProto.a;
});
test('`_.' + methodName + '` should work with `arguments` objects (test in IE < 9)', 1, function() {
if (!isStrict) {
deepEqual(func(args).sort(), ['0', '1', '2']);
}
else {
skipTest();
}
});
test('`_.' + methodName + '` should return keys for custom properties on `arguments` objects', 1, function() {
if (!isStrict) {
args.a = 1;
deepEqual(func(args).sort(), ['0', '1', '2', 'a']);
delete args.a;
}
else {
skipTest();
}
});
test('`_.' + methodName + '` should ' + (isKeys ? 'not' : '') + ' include inherited properties of `arguments` objects', 1, function() {
if (!isStrict) {
var expected = isKeys ? ['0', '1', '2'] : ['0', '1', '2', 'a'];
objectProto.a = 1;
deepEqual(func(args).sort(), expected);
delete objectProto.a;
}
else {
skipTest();
}
});
test('`_.' + methodName + '` should work with string objects (test in IE < 9)', 1, function() {
deepEqual(func(Object('abc')).sort(), ['0', '1', '2']);
});
test('`_.' + methodName + '` should return keys for custom properties on string objects', 1, function() {
var object = Object('a');
object.a = 1;
deepEqual(func(object).sort(), ['0', 'a']);
});
test('`_.' + methodName + '` should ' + (isKeys ? 'not' : '') + ' include inherited properties of string objects', 1, function() {
var expected = isKeys ? ['0'] : ['0', 'a'];
stringProto.a = 1;
deepEqual(func(Object('a')).sort(), expected);
delete stringProto.a;
});
test('`_.' + methodName + '` fixes the JScript `[[DontEnum]]` bug (test in IE < 9)', 3, function() {
function Foo() {}
Foo.prototype = _.create(shadowObject);
deepEqual(func(shadowObject).sort(), shadowProps);
var actual = isKeys ? [] : _.without(shadowProps, 'constructor');
deepEqual(func(new Foo).sort(), actual);
Foo.prototype.constructor = Foo;
deepEqual(func(new Foo).sort(), actual);
});
test('`_.' + methodName + '` skips non-enumerable properties (test in IE < 9)', 50, function() {
_.forOwn({
'Array': arrayProto,
'Boolean': Boolean.prototype,
'Date': Date.prototype,
'Error': errorProto,
'Function': funcProto,
'Object': objectProto,
'Number': Number.prototype,
'TypeError': TypeError.prototype,
'RegExp': RegExp.prototype,
'String': stringProto
},
function(proto, key) {
_.each([proto, _.create(proto)], function(object, index) {
var actual = func(proto),
isErr = _.endsWith(key, 'Error'),
message = 'enumerable properties ' + (index ? 'inherited from' : 'on') + ' `' + key + '.prototype`',
props = isErr ? ['constructor', 'toString'] : ['constructor'];
actual = isErr ? _.difference(props, actual) : actual;
strictEqual(_.isEmpty(actual), !isErr, 'skips non-' + message);
proto.a = 1;
actual = func(object);
delete proto.a;
strictEqual(_.includes(actual, 'a'), !(isKeys && index), 'includes ' + message);
if (index) {
object.constructor = 1;
if (isErr) {
object.toString = 2;
}
actual = func(object);
ok(_.isEmpty(_.difference(props, actual)), 'includes properties on objects that shadow those on `' + key + '.prototype`');
}
});
});
});
test('`_.' + methodName + '` skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', 2, function() {
function Foo() {}
Foo.a = 1;
Foo.prototype.b = 2;
var expected = ['a'];
deepEqual(func(Foo).sort(), expected);
Foo.prototype = { 'b': 2 };
deepEqual(func(Foo).sort(), expected);
});
test('`_.' + methodName + '` skips the `constructor` property on prototype objects', 3, function() {
function Foo() {}
Foo.prototype.a = 1;
var expected = ['a'];
deepEqual(func(Foo.prototype), expected);
Foo.prototype = { 'constructor': Foo, 'a': 1 };
deepEqual(func(Foo.prototype), expected);
var Fake = { 'prototype': {} };
Fake.prototype.constructor = Fake;
deepEqual(func(Fake.prototype), ['constructor']);
});
test('`_.' + methodName + '` should ' + (isKeys ? 'not' : '') + ' include inherited properties', 1, function() {
function Foo() { this.a = 1; }
Foo.prototype.b = 2;
var expected = isKeys ? ['a'] : ['a', 'b'];
deepEqual(func(new Foo).sort(), expected);
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.last');
(function() {
var array = [1, 2, 3];
test('should return the last element', 1, function() {
strictEqual(_.last(array), 3);
});
test('should return `undefined` when querying empty arrays', 1, function() {
var array = [];
array['-1'] = 1;
strictEqual(_.last([]), undefined);
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
actual = _.map(array, _.last);
deepEqual(actual, [3, 6, 9]);
});
test('should return an unwrapped value when implicitly chaining', 1, function() {
if (!isNpm) {
strictEqual(_(array).last(), 3);
}
else {
skipTest();
}
});
test('should return a wrapped value when explicitly chaining', 1, function() {
if (!isNpm) {
ok(_(array).chain().last() instanceof _);
}
else {
skipTest();
}
});
test('should work in a lazy chain sequence', 1, function() {
if (!isNpm) {
var array = [1, 2, 3, 4];
var wrapped = _(array).filter(function(value) {
return value % 2;
});
strictEqual(wrapped.last(), 3);
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.lastIndexOf');
(function() {
var array = [1, 2, 3, 1, 2, 3];
test('should return the index of the last matched value', 1, function() {
strictEqual(_.lastIndexOf(array, 3), 5);
});
test('should work with a positive `fromIndex`', 1, function() {
strictEqual(_.lastIndexOf(array, 1, 2), 0);
});
test('should work with `fromIndex` >= `array.length`', 1, function() {
var values = [6, 8, Math.pow(2, 32), Infinity],
expected = _.map(values, _.constant([-1, 3, -1]));
var actual = _.map(values, function(fromIndex) {
return [
_.lastIndexOf(array, undefined, fromIndex),
_.lastIndexOf(array, 1, fromIndex),
_.lastIndexOf(array, '', fromIndex)
];
});
deepEqual(actual, expected);
});
test('should treat falsey `fromIndex` values, except `0` and `NaN`, as `array.length`', 1, function() {
var expected = _.map(falsey, function(value) {
return typeof value == 'number' ? -1 : 5;
});
var actual = _.map(falsey, function(fromIndex) {
return _.lastIndexOf(array, 3, fromIndex);
});
deepEqual(actual, expected);
});
test('should perform a binary search when `fromIndex` is a non-number truthy value', 1, function() {
var sorted = [4, 4, 5, 5, 6, 6],
values = [true, '1', {}],
expected = _.map(values, _.constant(3));
var actual = _.map(values, function(value) {
return _.lastIndexOf(sorted, 5, value);
});
deepEqual(actual, expected);
});
test('should work with a negative `fromIndex`', 1, function() {
strictEqual(_.lastIndexOf(array, 2, -3), 1);
});
test('should work with a negative `fromIndex` <= `-array.length`', 1, function() {
var values = [-6, -8, -Infinity],
expected = _.map(values, _.constant(0));
var actual = _.map(values, function(fromIndex) {
return _.lastIndexOf(array, 1, fromIndex);
});
deepEqual(actual, expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('indexOf methods');
_.each(['indexOf', 'lastIndexOf'], function(methodName) {
var func = _[methodName],
isIndexOf = methodName == 'indexOf';
test('`_.' + methodName + '` should accept a falsey `array` argument', 1, function() {
var expected = _.map(falsey, _.constant(-1));
var actual = _.map(falsey, function(value, index) {
try {
return index ? func(value) : func();
} catch(e) {}
});
deepEqual(actual, expected);
});
test('`_.' + methodName + '` should return `-1` for an unmatched value', 4, function() {
var array = [1, 2, 3],
empty = [];
strictEqual(func(array, 4), -1);
strictEqual(func(array, 4, true), -1);
strictEqual(func(empty, undefined), -1);
strictEqual(func(empty, undefined, true), -1);
});
test('`_.' + methodName + '` should not match values on empty arrays', 2, function() {
var array = [];
array[-1] = 0;
strictEqual(func(array, undefined), -1);
strictEqual(func(array, 0, true), -1);
});
test('`_.' + methodName + '` should match `NaN`', 4, function() {
var array = [1, NaN, 3, NaN, 5, NaN];
strictEqual(func(array, NaN), isIndexOf ? 1 : 5);
strictEqual(func(array, NaN, 2), isIndexOf ? 3 : 1);
strictEqual(func(array, NaN, -2), isIndexOf ? 5 : 3);
strictEqual(func([1, 2, NaN, NaN], NaN, true), isIndexOf ? 2 : 3);
});
test('`_.' + methodName + '` should match `-0` as `0`', 2, function() {
strictEqual(func([-0], 0), 0);
strictEqual(func([0], -0), 0);
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.map');
(function() {
var array = [1, 2, 3];
test('should map values in `collection` to a new array', 2, function() {
var object = { 'a': 1, 'b': 2, 'c': 3 },
expected = ['1', '2', '3'];
deepEqual(_.map(array, String), expected);
deepEqual(_.map(object, String), expected);
});
test('should provide the correct `iteratee` arguments', 1, function() {
var args;
_.map(array, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [1, 0, array]);
});
test('should support the `thisArg` argument', 2, function() {
var callback = function(num, index) { return this[index] + num; },
actual = _.map([1], callback, [2]);
deepEqual(actual, [3]);
actual = _.map({ 'a': 1 }, callback, { 'a': 2 });
deepEqual(actual, [3]);
});
test('should work with a "_.property" style `iteratee`', 1, function() {
var objects = [{ 'a': 'x' }, { 'a': 'y' }];
deepEqual(_.map(objects, 'a'), ['x', 'y']);
});
test('should iterate over own properties of objects', 1, function() {
function Foo() { this.a = 1; }
Foo.prototype.b = 2;
var actual = _.map(new Foo, function(value, key) { return key; });
deepEqual(actual, ['a']);
});
test('should work on an object with no `iteratee`', 1, function() {
var actual = _.map({ 'a': 1, 'b': 2, 'c': 3 });
deepEqual(actual, array);
});
test('should handle object arguments with non-numeric length properties', 1, function() {
var value = { 'value': 'x' },
object = { 'length': { 'value': 'x' } };
deepEqual(_.map(object, _.identity), [value]);
});
test('should treat a nodelist as an array-like object', 1, function() {
if (document) {
var actual = _.map(document.getElementsByTagName('body'), function(element) {
return element.nodeName.toLowerCase();
});
deepEqual(actual, ['body']);
}
else {
skipTest();
}
});
test('should accept a falsey `collection` argument', 1, function() {
var expected = _.map(falsey, _.constant([]));
var actual = _.map(falsey, function(value, index) {
try {
return index ? _.map(value) : _.map();
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should treat number values for `collection` as empty', 1, function() {
deepEqual(_.map(1), []);
});
test('should return a wrapped value when chaining', 1, function() {
if (!isNpm) {
ok(_(array).map(_.noop) instanceof _);
}
else {
skipTest();
}
});
test('should provide the correct `predicate` arguments in a lazy chain sequence', 5, function() {
if (!isNpm) {
var args,
expected = [1, 0, [1, 4, 9]];
_(array).map(function(value, index, array) {
args || (args = slice.call(arguments));
}).value();
deepEqual(args, [1, 0, array]);
args = null;
_(array).map(square).map(function(value, index, array) {
args || (args = slice.call(arguments));
}).value();
deepEqual(args, expected);
args = null;
_(array).map(square).map(function(value, index) {
args || (args = slice.call(arguments));
}).value();
deepEqual(args, expected);
args = null;
_(array).map(square).map(function(value) {
args || (args = slice.call(arguments));
}).value();
deepEqual(args, [1]);
args = null;
_(array).map(square).map(function() {
args || (args = slice.call(arguments));
}).value();
deepEqual(args, expected);
}
else {
skipTest(5);
}
});
test('should be aliased', 1, function() {
strictEqual(_.collect, _.map);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.mapKeys');
(function() {
var array = [1, 2],
object = { 'a': 1, 'b': 2, 'c': 3 };
test('should map keys in `object` to a new object', 1, function() {
var actual = _.mapKeys(object, String);
deepEqual(actual, { '1': 1, '2': 2, '3': 3 });
});
test('should treat arrays like objects', 1, function() {
var actual = _.mapKeys(array, String);
deepEqual(actual, { '1': 1, '2': 2 });
});
test('should support the `thisArg` argument', 2, function() {
function callback(num, key) {
return this[key] + num
}
var actual = _.mapKeys({ 'a': 1 }, callback, { 'a': 2 });
deepEqual(actual, { '3': 1 });
actual = _.mapKeys([2], callback, [1]);
deepEqual(actual, { '3': 2 });
});
test('should work with a "_.property" style `iteratee`', 1, function() {
var actual = _.mapKeys({ 'a': { 'b': 'c' } }, 'b');
deepEqual(actual, { 'c': { 'b': 'c' } });
});
test('should work on an object with no `iteratee`', 1, function() {
var actual = _.mapKeys({ 'a': 1, 'b': 2, 'c': 3 });
deepEqual(actual, { '1': 1, '2': 2, '3': 3 });
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.mapValues');
(function() {
var array = [1, 2],
object = { 'a': 1, 'b': 2, 'c': 3 };
test('should map values in `object` to a new object', 1, function() {
var actual = _.mapValues(object, String);
deepEqual(actual, { 'a': '1', 'b': '2', 'c': '3' });
});
test('should treat arrays like objects', 1, function() {
var actual = _.mapValues(array, String);
deepEqual(actual, { '0': '1', '1': '2' });
});
test('should support the `thisArg` argument', 2, function() {
function callback(num, key) {
return this[key] + num;
}
var actual = _.mapValues({ 'a': 1 }, callback, { 'a': 2 });
deepEqual(actual, { 'a': 3 });
actual = _.mapValues([2], callback, [1]);
deepEqual(actual, { '0': 3 });
});
test('should work with a "_.property" style `iteratee`', 1, function() {
var actual = _.mapValues({ 'a': { 'b': 1 } }, 'b');
deepEqual(actual, { 'a': 1 });
});
test('should work on an object with no `iteratee`', 2, function() {
var actual = _.mapValues({ 'a': 1, 'b': 2, 'c': 3 });
deepEqual(actual, object);
notStrictEqual(actual, object);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.mapKeys and lodash.mapValues');
_.each(['mapKeys', 'mapValues'], function(methodName) {
var array = [1, 2],
func = _[methodName],
object = { 'a': 1, 'b': 2, 'c': 3 };
test('should iterate over own properties of objects', 1, function() {
function Foo() { this.a = 'a'; }
Foo.prototype.b = 'b';
var actual = func(new Foo, function(value, key) { return key; });
deepEqual(actual, { 'a': 'a' });
});
test('should accept a falsey `object` argument', 1, function() {
var expected = _.map(falsey, _.constant({}));
var actual = _.map(falsey, function(value, index) {
try {
return index ? func(value) : func();
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should provide the correct `iteratee` arguments', 2, function() {
_.each([object, array], function(value, index) {
var args;
_.mapValues(value, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [1, index ? '0' : 'a', value]);
});
});
test('should return a wrapped value when chaining', 1, function() {
if (!isNpm) {
ok(_(object)[methodName](_.noop) instanceof _);
}
else {
skipTest();
}
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.matches');
(function() {
test('should create a function that performs a deep comparison between a given object and `source`', 6, function() {
var object = { 'a': 1, 'b': 2, 'c': 3 },
matches = _.matches({ 'a': 1 });
strictEqual(matches.length, 1);
strictEqual(matches(object), true);
matches = _.matches({ 'b': 1 });
strictEqual(matches(object), false);
matches = _.matches({ 'a': 1, 'c': 3 });
strictEqual(matches(object), true);
matches = _.matches({ 'c': 3, 'd': 4 });
strictEqual(matches(object), false);
object = { 'a': { 'b': { 'c': 1, 'd': 2 }, 'e': 3 }, 'f': 4 };
matches = _.matches({ 'a': { 'b': { 'c': 1 } } });
strictEqual(matches(object), true);
});
test('should match inherited `value` properties', 1, function() {
function Foo() { this.a = 1; }
Foo.prototype.b = 2;
var object = { 'a': new Foo },
matches = _.matches({ 'a': { 'b': 2 } });
strictEqual(matches(object), true);
});
test('should match `-0` as `0`', 2, function() {
var object1 = { 'a': -0 },
object2 = { 'a': 0 },
matches = _.matches(object1);
strictEqual(matches(object2), true);
matches = _.matches(object2);
strictEqual(matches(object1), true);
});
test('should not match by inherited `source` properties', 1, function() {
function Foo() { this.a = 1; }
Foo.prototype.b = 2;
var objects = [{ 'a': 1 }, { 'a': 1, 'b': 2 }],
source = new Foo,
matches = _.matches(source),
actual = _.map(objects, matches),
expected = _.map(objects, _.constant(true));
deepEqual(actual, expected);
});
test('should return `false` when `object` is nullish', 1, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant(false)),
matches = _.matches({ 'a': 1 });
var actual = _.map(values, function(value, index) {
try {
return index ? matches(value) : matches();
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should return `true` when comparing an empty `source`', 1, function() {
var object = { 'a': 1 },
expected = _.map(empties, _.constant(true));
var actual = _.map(empties, function(value) {
var matches = _.matches(value);
return matches(object);
});
deepEqual(actual, expected);
});
test('should compare a variety of `source` values', 2, function() {
var object1 = { 'a': false, 'b': true, 'c': '3', 'd': 4, 'e': [5], 'f': { 'g': 6 } },
object2 = { 'a': 0, 'b': 1, 'c': 3, 'd': '4', 'e': ['5'], 'f': { 'g': '6' } },
matches = _.matches(object1);
strictEqual(matches(object1), true);
strictEqual(matches(object2), false);
});
test('should compare functions by reference', 3, function() {
var object1 = { 'a': _.noop },
object2 = { 'a': noop },
object3 = { 'a': {} },
matches = _.matches(object1);
strictEqual(matches(object1), true);
strictEqual(matches(object2), false);
strictEqual(matches(object3), false);
});
test('should not change match behavior if `source` is augmented', 9, function() {
var sources = [
{ 'a': { 'b': 2, 'c': 3 } },
{ 'a': 1, 'b': 2 },
{ 'a': 1 }
];
_.each(sources, function(source, index) {
var object = _.cloneDeep(source),
matches = _.matches(source);
strictEqual(matches(object), true);
if (index) {
source.a = 2;
source.b = 1;
source.c = 3;
} else {
source.a.b = 1;
source.a.c = 2;
source.a.d = 3;
}
strictEqual(matches(object), true);
strictEqual(matches(source), false);
});
});
test('should return `true` when comparing a `source` of empty arrays and objects', 1, function() {
var objects = [{ 'a': [1], 'b': { 'c': 1 } }, { 'a': [2, 3], 'b': { 'd': 2 } }],
matches = _.matches({ 'a': [], 'b': {} }),
actual = _.filter(objects, matches);
deepEqual(actual, objects);
});
test('should return `true` when comparing an empty `source` to a nullish `object`', 1, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant(true)),
matches = _.matches({});
var actual = _.map(values, function(value, index) {
try {
return index ? matches(value) : matches();
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should search arrays of `source` for values', 3, function() {
var objects = [{ 'a': ['b'] }, { 'a': ['c', 'd'] }],
matches = _.matches({ 'a': ['d'] }),
actual = _.filter(objects, matches);
deepEqual(actual, [objects[1]]);
matches = _.matches({ 'a': ['b', 'd'] });
actual = _.filter(objects, matches);
deepEqual(actual, []);
matches = _.matches({ 'a': ['d', 'b'] });
actual = _.filter(objects, matches);
deepEqual(actual, []);
});
test('should perform a partial comparison of all objects within arrays of `source`', 1, function() {
var objects = [
{ 'a': [{ 'b': 1, 'c': 2 }, { 'b': 4, 'c': 5, 'd': 6 }] },
{ 'a': [{ 'b': 1, 'c': 2 }, { 'b': 4, 'c': 6, 'd': 7 }] }
];
var matches = _.matches({ 'a': [{ 'b': 1 }, { 'b': 4, 'c': 5 }] }),
actual = _.filter(objects, matches);
deepEqual(actual, [objects[0]]);
});
test('should handle a `source` with `undefined` values', 2, function() {
var matches = _.matches({ 'b': undefined }),
objects = [{ 'a': 1 }, { 'a': 1, 'b': 1 }, { 'a': 1, 'b': undefined }],
actual = _.map(objects, matches),
expected = [false, false, true];
deepEqual(actual, expected);
matches = _.matches({ 'a': { 'c': undefined } });
objects = [{ 'a': { 'b': 1 } }, { 'a': { 'b':1, 'c': 1 } }, { 'a': { 'b': 1, 'c': undefined } }];
actual = _.map(objects, matches);
deepEqual(actual, expected);
});
test('should match properties when `value` is a function', 1, function() {
function Foo() {}
Foo.a = { 'b': 1, 'c': 2 };
var matches = _.matches({ 'a': { 'b': 1 } });
strictEqual(matches(Foo), true);
});
test('should match properties when `value` is not a plain object', 1, function() {
function Foo(object) { _.assign(this, object); }
var object = new Foo({ 'a': new Foo({ 'b': 1, 'c': 2 }) }),
matches = _.matches({ 'a': { 'b': 1 } });
strictEqual(matches(object), true);
});
test('should work with a function for `source`', 1, function() {
function source() {}
source.a = 1;
source.b = function() {};
source.c = 3;
var matches = _.matches(source),
objects = [{ 'a': 1 }, { 'a': 1, 'b': source.b, 'c': 3 }],
actual = _.map(objects, matches);
deepEqual(actual, [false, true]);
});
test('should match problem JScript properties (test in IE < 9)', 1, function() {
var matches = _.matches(shadowObject),
objects = [{}, shadowObject],
actual = _.map(objects, matches);
deepEqual(actual, [false, true]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.matchesProperty');
(function() {
test('should create a function that performs a deep comparison between a property value and `value`', 6, function() {
var object = { 'a': 1, 'b': 2, 'c': 3 },
matches = _.matchesProperty('a', 1);
strictEqual(matches.length, 1);
strictEqual(matches(object), true);
matches = _.matchesProperty('b', 3);
strictEqual(matches(object), false);
matches = _.matchesProperty('a', { 'a': 1, 'c': 3 });
strictEqual(matches({ 'a': object }), true);
matches = _.matchesProperty('a', { 'c': 3, 'd': 4 });
strictEqual(matches(object), false);
object = { 'a': { 'b': { 'c': 1, 'd': 2 }, 'e': 3 }, 'f': 4 };
matches = _.matchesProperty('a', { 'b': { 'c': 1 } });
strictEqual(matches(object), true);
});
test('should support deep paths', 2, function() {
var object = { 'a': { 'b': { 'c': 3 } } };
_.each(['a.b.c', ['a', 'b', 'c']], function(path) {
var matches = _.matchesProperty(path, 3);
strictEqual(matches(object), true);
});
});
test('should coerce key to a string', 1, function() {
function fn() {}
fn.toString = _.constant('fn');
var objects = [{ 'null': 1 }, { 'undefined': 2 }, { 'fn': 3 }, { '[object Object]': 4 }],
values = [null, undefined, fn, {}];
var expected = _.transform(values, function(result) {
result.push(true, true);
});
var actual = _.transform(objects, function(result, object, index) {
var key = values[index];
_.each([key, [key]], function(path) {
var matches = _.matchesProperty(path, object[key]);
result.push(matches(object));
});
});
deepEqual(actual, expected);
});
test('should match characters of string indexes (test in IE < 9)', 2, function() {
var matches = _.matchesProperty(1, 'o');
_.each(['xo', Object('xo')], function(string) {
strictEqual(matches(string), true);
});
});
test('should match a key over a path', 2, function() {
var object = { 'a.b.c': 3, 'a': { 'b': { 'c': 4 } } };
_.each(['a.b.c', ['a.b.c']], function(path) {
var matches = _.matchesProperty(path, 3);
strictEqual(matches(object), true);
});
});
test('should work with non-string `path` arguments', 2, function() {
var array = [1, 2, 3];
_.each([1, [1]], function(path) {
var matches = _.matchesProperty(path, 2);
strictEqual(matches(array), true);
});
});
test('should return `false` if parts of `path` are missing', 4, function() {
var object = {};
_.each(['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']], function(path) {
var matches = _.matchesProperty(path, 1);
strictEqual(matches(object), false);
});
});
test('should match inherited `value` properties', 2, function() {
function Foo() {}
Foo.prototype.b = 2;
var object = { 'a': new Foo };
_.each(['a', ['a']], function(path) {
var matches = _.matchesProperty(path, { 'b': 2 });
strictEqual(matches(object), true);
});
});
test('should match `-0` as `0`', 2, function() {
var matches = _.matchesProperty('a', -0);
strictEqual(matches({ 'a': 0 }), true);
matches = _.matchesProperty('a', 0);
strictEqual(matches({ 'a': -0 }), true);
});
test('should not match by inherited `source` properties', 2, function() {
function Foo() { this.a = 1; }
Foo.prototype.b = 2;
var objects = [{ 'a': { 'a': 1 } }, { 'a': { 'a': 1, 'b': 2 } }],
expected = _.map(objects, _.constant(true));
_.each(['a', ['a']], function(path) {
var matches = _.matchesProperty(path, new Foo);
deepEqual(_.map(objects, matches), expected);
});
});
test('should return `false` when `object` is nullish', 2, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant(false));
_.each(['constructor', ['constructor']], function(path) {
var matches = _.matchesProperty(path, 1);
var actual = _.map(values, function(value, index) {
try {
return index ? matches(value) : matches();
} catch(e) {}
});
deepEqual(actual, expected);
});
});
test('should return `false` with deep paths when `object` is nullish', 2, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant(false));
_.each(['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], function(path) {
var matches = _.matchesProperty(path, 1);
var actual = _.map(values, function(value, index) {
try {
return index ? matches(value) : matches();
} catch(e) {}
});
deepEqual(actual, expected);
});
});
test('should compare a variety of values', 2, function() {
var object1 = { 'a': false, 'b': true, 'c': '3', 'd': 4, 'e': [5], 'f': { 'g': 6 } },
object2 = { 'a': 0, 'b': 1, 'c': 3, 'd': '4', 'e': ['5'], 'f': { 'g': '6' } },
matches = _.matchesProperty('a', object1);
strictEqual(matches({ 'a': object1 }), true);
strictEqual(matches({ 'a': object2 }), false);
});
test('should compare functions by reference', 3, function() {
var object1 = { 'a': _.noop },
object2 = { 'a': noop },
object3 = { 'a': {} },
matches = _.matchesProperty('a', object1);
strictEqual(matches({ 'a': object1 }), true);
strictEqual(matches({ 'a': object2 }), false);
strictEqual(matches({ 'a': object3 }), false);
});
test('should not change match behavior if `value` is augmented', 9, function() {
_.each([{ 'a': { 'b': 2, 'c': 3 } }, { 'a': 1, 'b': 2 }, { 'a': 1 }], function(source, index) {
var object = _.cloneDeep(source),
matches = _.matchesProperty('a', source);
strictEqual(matches({ 'a': object }), true);
if (index) {
source.a = 2;
source.b = 1;
source.c = 3;
} else {
source.a.b = 1;
source.a.c = 2;
source.a.d = 3;
}
strictEqual(matches({ 'a': object }), true);
strictEqual(matches({ 'a': source }), false);
});
});
test('should return `true` when comparing a `value` of empty arrays and objects', 1, function() {
var objects = [{ 'a': [1], 'b': { 'c': 1 } }, { 'a': [2, 3], 'b': { 'd': 2 } }],
matches = _.matchesProperty('a', { 'a': [], 'b': {} });
var actual = _.filter(objects, function(object) {
return matches({ 'a': object });
});
deepEqual(actual, objects);
});
test('should search arrays of `value` for values', 3, function() {
var objects = [{ 'a': ['b'] }, { 'a': ['c', 'd'] }],
matches = _.matchesProperty('a', ['d']),
actual = _.filter(objects, matches);
deepEqual(actual, [objects[1]]);
matches = _.matchesProperty('a', ['b', 'd']);
actual = _.filter(objects, matches);
deepEqual(actual, []);
matches = _.matchesProperty('a', ['d', 'b']);
actual = _.filter(objects, matches);
deepEqual(actual, []);
});
test('should perform a partial comparison of all objects within arrays of `value`', 1, function() {
var objects = [
{ 'a': [{ 'a': 1, 'b': 2 }, { 'a': 4, 'b': 5, 'c': 6 }] },
{ 'a': [{ 'a': 1, 'b': 2 }, { 'a': 4, 'b': 6, 'c': 7 }] }
];
var matches = _.matchesProperty('a', [{ 'a': 1 }, { 'a': 4, 'b': 5 }]),
actual = _.filter(objects, matches);
deepEqual(actual, [objects[0]]);
});
test('should handle a `value` with `undefined` values', 2, function() {
var matches = _.matchesProperty('b', undefined),
objects = [{ 'a': 1 }, { 'a': 1, 'b': 1 }, { 'a': 1, 'b': undefined }],
actual = _.map(objects, matches);
deepEqual(actual, [false, false, true]);
matches = _.matchesProperty('a', { 'b': undefined });
objects = [{ 'a': { 'a': 1 } }, { 'a': { 'a': 1, 'b': 1 } }, { 'a': { 'a': 1, 'b': undefined } }];
actual = _.map(objects, matches);
deepEqual(actual, [false, false, true]);
});
test('should work with a function for `value`', 1, function() {
function source() {}
source.a = 1;
source.b = function() {};
source.c = 3;
var matches = _.matchesProperty('a', source),
objects = [{ 'a': { 'a': 1 } }, { 'a': { 'a': 1, 'b': source.b, 'c': 3 } }],
actual = _.map(objects, matches);
deepEqual(actual, [false, true]);
});
test('should match properties when `value` is not a plain object', 1, function() {
function Foo(object) { _.assign(this, object); }
var object = new Foo({ 'a': new Foo({ 'b': 1, 'c': 2 }) }),
matches = _.matchesProperty('a', { 'b': 1 });
strictEqual(matches(object), true);
});
test('should match problem JScript properties (test in IE < 9)', 1, function() {
var matches = _.matchesProperty('a', shadowObject),
objects = [{ 'a': {} }, { 'a': shadowObject }],
actual = _.map(objects, matches);
deepEqual(actual, [false, true]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.max');
(function() {
test('should return the largest value from a collection', 1, function() {
strictEqual(_.max([1, 2, 3]), 3);
});
test('should return `-Infinity` for empty collections', 1, function() {
var expected = _.map(empties, _.constant(-Infinity));
var actual = _.map(empties, function(value) {
try {
return _.max(value);
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should return `-Infinity` for non-numeric collection values', 1, function() {
var collections = [['a', 'b'], { 'a': 'a', 'b': 'b' }],
expected = _.map(collections, _.constant(-Infinity));
var actual = _.map(collections, function(value) {
try {
return _.max(value);
} catch(e) {}
});
deepEqual(actual, expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.memoize');
(function() {
test('should memoize results based on the first argument provided', 2, function() {
var memoized = _.memoize(function(a, b, c) {
return a + b + c;
});
strictEqual(memoized(1, 2, 3), 6);
strictEqual(memoized(1, 3, 5), 6);
});
test('should support a `resolver` argument', 2, function() {
var fn = function(a, b, c) { return a + b + c; },
memoized = _.memoize(fn, fn);
strictEqual(memoized(1, 2, 3), 6);
strictEqual(memoized(1, 3, 5), 9);
});
test('should not set a `this` binding', 2, function() {
var memoized = _.memoize(function(a, b, c) {
return a + this.b + this.c;
});
var object = { 'b': 2, 'c': 3, 'memoized': memoized };
strictEqual(object.memoized(1), 6);
strictEqual(object.memoized(2), 7);
});
test('should throw a TypeError if `resolve` is truthy and not a function', function() {
raises(function() { _.memoize(_.noop, {}); }, TypeError);
});
test('should not error if `resolve` is falsey', function() {
var expected = _.map(falsey, _.constant(true));
var actual = _.map(falsey, function(value, index) {
try {
return _.isFunction(index ? _.memoize(_.noop, value) : _.memoize(_.noop));
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should check cache for own properties', 1, function() {
var memoized = _.memoize(_.identity);
var actual = _.map(shadowProps, function(value) {
return memoized(value);
});
deepEqual(actual, shadowProps);
});
test('should expose a `cache` object on the `memoized` function which implements `Map` interface', 18, function() {
_.times(2, function(index) {
var resolver = index ? _.identity : null;
var memoized = _.memoize(function(value) {
return 'value:' + value;
}, resolver);
var cache = memoized.cache;
memoized('a');
strictEqual(cache.has('a'), true);
strictEqual(cache.get('a'), 'value:a');
strictEqual(cache['delete']('a'), true);
strictEqual(cache['delete']('b'), false);
strictEqual(cache.set('b', 'value:b'), cache);
strictEqual(cache.has('b'), true);
strictEqual(cache.get('b'), 'value:b');
strictEqual(cache['delete']('b'), true);
strictEqual(cache['delete']('a'), false);
});
});
test('should skip the `__proto__` key', 8, function() {
_.times(2, function(index) {
var count = 0,
key = '__proto__',
resolver = index && _.identity;
var memoized = _.memoize(function() {
count++;
return [];
}, resolver);
var cache = memoized.cache;
memoized(key);
memoized(key);
strictEqual(count, 2);
strictEqual(cache.get(key), undefined);
strictEqual(cache['delete'](key), false);
ok(!(cache.__data__ instanceof Array));
});
});
test('should allow `_.memoize.Cache` to be customized', 5, function() {
var oldCache = _.memoize.Cache
function Cache() {
this.__data__ = [];
}
Cache.prototype = {
'delete': function(key) {
var data = this.__data__;
var index = _.findIndex(data, function(entry) {
return key === entry.key;
});
if (index < 0) {
return false;
}
data.splice(index, 1);
return true;
},
'get': function(key) {
return _.find(this.__data__, function(entry) {
return key === entry.key;
}).value;
},
'has': function(key) {
return _.some(this.__data__, function(entry) {
return key === entry.key;
});
},
'set': function(key, value) {
this.__data__.push({ 'key': key, 'value': value });
return this;
}
};
_.memoize.Cache = Cache;
var memoized = _.memoize(function(object) {
return 'value:' + object.id;
});
var cache = memoized.cache,
key1 = { 'id': 'a' },
key2 = { 'id': 'b' };
strictEqual(memoized(key1), 'value:a');
strictEqual(cache.has(key1), true);
strictEqual(memoized(key2), 'value:b');
strictEqual(cache.has(key2), true);
cache['delete'](key2);
strictEqual(cache.has(key2), false);
_.memoize.Cache = oldCache;
});
test('should works with an immutable `_.memoize.Cache` ', 2, function() {
var oldCache = _.memoize.Cache
function Cache() {
this.__data__ = [];
}
Cache.prototype = {
'get': function(key) {
return _.find(this.__data__, function(entry) {
return key === entry.key;
}).value;
},
'has': function(key) {
return _.some(this.__data__, function(entry) {
return key === entry.key;
});
},
'set': function(key, value) {
var result = new Cache;
result.__data__ = this.__data__.concat({ 'key': key, 'value': value });
return result;
}
};
_.memoize.Cache = Cache;
var memoized = _.memoize(function(object) {
return object.id;
});
var key1 = { 'id': 'a' },
key2 = { 'id': 'b' };
memoized(key1);
memoized(key2);
var cache = memoized.cache;
strictEqual(cache.has(key1), true);
strictEqual(cache.has(key2), true);
_.memoize.Cache = oldCache;
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.merge');
(function() {
var args = arguments;
test('should merge `source` into the destination object', 1, function() {
var names = {
'characters': [
{ 'name': 'barney' },
{ 'name': 'fred' }
]
};
var ages = {
'characters': [
{ 'age': 36 },
{ 'age': 40 }
]
};
var heights = {
'characters': [
{ 'height': '5\'4"' },
{ 'height': '5\'5"' }
]
};
var expected = {
'characters': [
{ 'name': 'barney', 'age': 36, 'height': '5\'4"' },
{ 'name': 'fred', 'age': 40, 'height': '5\'5"' }
]
};
deepEqual(_.merge(names, ages, heights), expected);
});
test('should merge sources containing circular references', 1, function() {
var object = {
'foo': { 'a': 1 },
'bar': { 'a': 2 }
};
var source = {
'foo': { 'b': { 'c': { 'd': {} } } },
'bar': {}
};
source.foo.b.c.d = source;
source.bar.b = source.foo.b;
var actual = _.merge(object, source);
ok(actual.bar.b === actual.foo.b && actual.foo.b.c.d === actual.foo.b.c.d.foo.b.c.d);
});
test('should treat sources that are sparse arrays as dense', 2, function() {
var array = Array(3);
array[0] = 1;
array[2] = 3;
var actual = _.merge([], array),
expected = array.slice();
expected[1] = undefined;
ok('1' in actual);
deepEqual(actual, expected);
});
test('should merge `arguments` objects', 3, function() {
var object1 = { 'value': args },
object2 = { 'value': { '3': 4 } },
expected = { '0': 1, '1': 2, '2': 3, '3': 4 },
actual = _.merge(object1, object2);
ok(!_.isArguments(actual.value));
deepEqual(actual.value, expected);
delete object1.value[3];
actual = _.merge(object2, object1);
deepEqual(actual.value, expected);
});
test('should merge typed arrays', 4, function() {
var array1 = [0],
array2 = [0, 0],
array3 = [0, 0, 0, 0],
array4 = _.range(0, 8, 0);
var arrays = [array2, array1, array4, array3, array2, array4, array4, array3, array2],
buffer = ArrayBuffer && new ArrayBuffer(8);
// juggle for `Float64Array` shim
if (root.Float64Array && (new Float64Array(buffer)).length == 8) {
arrays[1] = array4;
}
var expected = _.map(typedArrays, function(type, index) {
var array = arrays[index].slice();
array[0] = 1;
return root[type] ? { 'value': array } : false;
});
var actual = _.map(typedArrays, function(type) {
var Ctor = root[type];
return Ctor ? _.merge({ 'value': new Ctor(buffer) }, { 'value': [1] }) : false;
});
ok(_.isArray(actual));
deepEqual(actual, expected);
expected = _.map(typedArrays, function(type, index) {
var array = arrays[index].slice();
array.push(1);
return root[type] ? { 'value': array } : false;
});
actual = _.map(typedArrays, function(type, index) {
var Ctor = root[type],
array = _.range(arrays[index].length);
array.push(1);
return Ctor ? _.merge({ 'value': array }, { 'value': new Ctor(buffer) }) : false;
});
ok(_.isArray(actual));
deepEqual(actual, expected);
});
test('should work with four arguments', 1, function() {
var expected = { 'a': 4 },
actual = _.merge({ 'a': 1 }, { 'a': 2 }, { 'a': 3 }, expected);
deepEqual(actual, expected);
});
test('should assign `null` values', 1, function() {
var actual = _.merge({ 'a': 1 }, { 'a': null });
strictEqual(actual.a, null);
});
test('should not assign `undefined` values', 1, function() {
var actual = _.merge({ 'a': 1 }, { 'a': undefined, 'b': undefined });
deepEqual(actual, { 'a': 1 });
});
test('should not error on DOM elements', 1, function() {
var object1 = { 'el': document && document.createElement('div') },
object2 = { 'el': document && document.createElement('div') },
pairs = [[{}, object1], [object1, object2]],
expected = _.map(pairs, _.constant(true));
var actual = _.map(pairs, function(pair) {
try {
return _.merge(pair[0], pair[1]).el === pair[1].el;
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should assign non array/plain-object values directly', 1, function() {
function Foo() {}
var values = [new Foo, new Boolean, new Date, Foo, new Number, new String, new RegExp],
expected = _.map(values, _.constant(true));
var actual = _.map(values, function(value) {
var object = _.merge({}, { 'a': value });
return object.a === value;
});
deepEqual(actual, expected);
});
test('should convert values to an array when merging with arrays of `source`', 2, function() {
var object = { 'a': { '1': 'y', 'b': 'z', 'length': 2 } },
actual = _.merge(object, { 'a': ['x'] });
deepEqual(actual, { 'a': ['x', 'y'] });
actual = _.merge({ 'a': {} }, { 'a': [] });
deepEqual(actual, { 'a': [] });
});
test('should work with a function `object` value', 2, function() {
function Foo() {}
var source = { 'a': 1 },
actual = _.merge(Foo, source);
strictEqual(actual, Foo);
strictEqual(Foo.a, 1);
});
test('should work with a non-plain `object` value', 2, function() {
function Foo() {}
var object = new Foo,
source = { 'a': 1 },
actual = _.merge(object, source);
strictEqual(actual, object);
strictEqual(object.a, 1);
});
test('should pass thru primitive `object` values', 1, function() {
var values = [true, 1, '1'];
var actual = _.map(values, function(value) {
return _.merge(value, { 'a': 1 });
});
deepEqual(actual, values);
});
test('should handle merging if `customizer` returns `undefined`', 2, function() {
var actual = _.merge({ 'a': { 'b': [1, 1] } }, { 'a': { 'b': [0] } }, _.noop);
deepEqual(actual, { 'a': { 'b': [0, 1] } });
actual = _.merge([], [undefined], _.identity);
deepEqual(actual, [undefined]);
});
test('should defer to `customizer` when it returns a value other than `undefined`', 1, function() {
var actual = _.merge({ 'a': { 'b': [0, 1] } }, { 'a': { 'b': [2] } }, function(a, b) {
return _.isArray(a) ? a.concat(b) : undefined;
});
deepEqual(actual, { 'a': { 'b': [0, 1, 2] } });
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.method');
(function() {
test('should create a function that calls a method of a given object', 4, function() {
var object = { 'a': _.constant(1) };
_.each(['a', ['a']], function(path) {
var method = _.method(path);
strictEqual(method.length, 1);
strictEqual(method(object), 1);
});
});
test('should work with deep property values', 2, function() {
var object = { 'a': { 'b': { 'c': _.constant(3) } } };
_.each(['a.b.c', ['a', 'b', 'c']], function(path) {
var method = _.method(path);
strictEqual(method(object), 3);
});
});
test('should work with non-string `path` arguments', 2, function() {
var array = _.times(3, _.constant);
_.each([1, [1]], function(path) {
var method = _.method(path);
strictEqual(method(array), 1);
});
});
test('should coerce key to a string', 1, function() {
function fn() {}
fn.toString = _.constant('fn');
var expected = [1, 1, 2, 2, 3, 3, 4, 4],
objects = [{ 'null': _.constant(1) }, { 'undefined': _.constant(2) }, { 'fn': _.constant(3) }, { '[object Object]': _.constant(4) }],
values = [null, undefined, fn, {}];
var actual = _.transform(objects, function(result, object, index) {
var key = values[index];
_.each([key, [key]], function(path) {
var method = _.method(key);
result.push(method(object));
});
});
deepEqual(actual, expected);
});
test('should work with inherited property values', 2, function() {
function Foo() {}
Foo.prototype.a = _.constant(1);
_.each(['a', ['a']], function(path) {
var method = _.method(path);
strictEqual(method(new Foo), 1);
});
});
test('should use a key over a path', 2, function() {
var object = { 'a.b.c': _.constant(3), 'a': { 'b': { 'c': _.constant(4) } } };
_.each(['a.b.c', ['a.b.c']], function(path) {
var method = _.method(path);
strictEqual(method(object), 3);
});
});
test('should return `undefined` when `object` is nullish', 2, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant(undefined));
_.each(['constructor', ['constructor']], function(path) {
var method = _.method(path);
var actual = _.map(values, function(value, index) {
return index ? method(value) : method();
});
deepEqual(actual, expected);
});
});
test('should return `undefined` with deep paths when `object` is nullish', 2, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant(undefined));
_.each(['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], function(path) {
var method = _.method(path);
var actual = _.map(values, function(value, index) {
return index ? method(value) : method();
});
deepEqual(actual, expected);
});
});
test('should return `undefined` if parts of `path` are missing', 4, function() {
var object = {};
_.each(['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']], function(path) {
var method = _.method(path);
strictEqual(method(object), undefined);
});
});
test('should apply partial arguments to function', 2, function() {
var object = {
'fn': function() {
return slice.call(arguments);
}
};
_.each(['fn', ['fn']], function(path) {
var method = _.method(path, 1, 2, 3);
deepEqual(method(object), [1, 2, 3]);
});
});
test('should invoke deep property methods with the correct `this` binding', 2, function() {
var object = { 'a': { 'b': function() { return this.c; }, 'c': 1 } };
_.each(['a.b', ['a', 'b']], function(path) {
var method = _.method(path);
strictEqual(method(object), 1);
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.methodOf');
(function() {
test('hould create a function that calls a method of a given key', 4, function() {
var object = { 'a': _.constant(1) };
_.each(['a', ['a']], function(path) {
var methodOf = _.methodOf(object);
strictEqual(methodOf.length, 1);
strictEqual(methodOf(path), 1);
});
});
test('should work with deep property values', 2, function() {
var object = { 'a': { 'b': { 'c': _.constant(3) } } };
_.each(['a.b.c', ['a', 'b', 'c']], function(path) {
var methodOf = _.methodOf(object);
strictEqual(methodOf(path), 3);
});
});
test('should work with non-string `path` arguments', 2, function() {
var array = _.times(3, _.constant);
_.each([1, [1]], function(path) {
var methodOf = _.methodOf(array);
strictEqual(methodOf(path), 1);
});
});
test('should coerce key to a string', 1, function() {
function fn() {}
fn.toString = _.constant('fn');
var expected = [1, 1, 2, 2, 3, 3, 4, 4],
objects = [{ 'null': _.constant(1) }, { 'undefined': _.constant(2) }, { 'fn': _.constant(3) }, { '[object Object]': _.constant(4) }],
values = [null, undefined, fn, {}];
var actual = _.transform(objects, function(result, object, index) {
var key = values[index];
_.each([key, [key]], function(path) {
var methodOf = _.methodOf(object);
result.push(methodOf(key));
});
});
deepEqual(actual, expected);
});
test('should work with inherited property values', 2, function() {
function Foo() {}
Foo.prototype.a = _.constant(1);
_.each(['a', ['a']], function(path) {
var methodOf = _.methodOf(new Foo);
strictEqual(methodOf(path), 1);
});
});
test('should use a key over a path', 2, function() {
var object = { 'a.b.c': _.constant(3), 'a': { 'b': { 'c': _.constant(4) } } };
_.each(['a.b.c', ['a.b.c']], function(path) {
var methodOf = _.methodOf(object);
strictEqual(methodOf(path), 3);
});
});
test('should return `undefined` when `object` is nullish', 2, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant(undefined));
_.each(['constructor', ['constructor']], function(path) {
var actual = _.map(values, function(value, index) {
var methodOf = index ? _.methodOf() : _.methodOf(value);
return methodOf(path);
});
deepEqual(actual, expected);
});
});
test('should return `undefined` with deep paths when `object` is nullish', 2, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant(undefined));
_.each(['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], function(path) {
var actual = _.map(values, function(value, index) {
var methodOf = index ? _.methodOf() : _.methodOf(value);
return methodOf(path);
});
deepEqual(actual, expected);
});
});
test('should return `undefined` if parts of `path` are missing', 4, function() {
var object = {},
methodOf = _.methodOf(object);
_.each(['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']], function(path) {
strictEqual(methodOf(path), undefined);
});
});
test('should apply partial arguments to function', 2, function() {
var object = {
'fn': function() {
return slice.call(arguments);
}
};
var methodOf = _.methodOf(object, 1, 2, 3);
_.each(['fn', ['fn']], function(path) {
deepEqual(methodOf(path), [1, 2, 3]);
});
});
test('should invoke deep property methods with the correct `this` binding', 2, function() {
var object = { 'a': { 'b': function() { return this.c; }, 'c': 1 } },
methodOf = _.methodOf(object);
_.each(['a.b', ['a', 'b']], function(path) {
strictEqual(methodOf(path), 1);
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.min');
(function() {
test('should return the smallest value from a collection', 1, function() {
strictEqual(_.min([1, 2, 3]), 1);
});
test('should return `Infinity` for empty collections', 1, function() {
var expected = _.map(empties, _.constant(Infinity));
var actual = _.map(empties, function(value) {
try {
return _.min(value);
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should return `Infinity` for non-numeric collection values', 1, function() {
var collections = [['a', 'b'], { 'a': 'a', 'b': 'b' }],
expected = _.map(collections, _.constant(Infinity));
var actual = _.map(collections, function(value) {
try {
return _.min(value);
} catch(e) {}
});
deepEqual(actual, expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('extremum methods');
_.each(['max', 'min'], function(methodName) {
var array = [1, 2, 3],
func = _[methodName],
isMax = methodName == 'max';
test('`_.' + methodName + '` should work with Date objects', 1, function() {
var curr = new Date,
past = new Date(0);
strictEqual(func([curr, past]), isMax ? curr : past);
});
test('`_.' + methodName + '` should work with an `iteratee` argument', 1, function() {
var actual = func(array, function(num) {
return -num;
});
strictEqual(actual, isMax ? 1 : 3);
});
test('`_.' + methodName + '` should provide the correct `iteratee` arguments when iterating an array', 1, function() {
var args;
func(array, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [1, 0, array]);
});
test('`_.' + methodName + '` should provide the correct `iteratee` arguments when iterating an object', 1, function() {
var args,
object = { 'a': 1, 'b': 2 },
firstKey = _.first(_.keys(object));
var expected = firstKey == 'a'
? [1, 'a', object]
: [2, 'b', object];
func(object, function() {
args || (args = slice.call(arguments));
}, 0);
deepEqual(args, expected);
});
test('`_.' + methodName + '` should support the `thisArg` argument', 1, function() {
var actual = func(array, function(num, index) {
return -this[index];
}, array);
strictEqual(actual, isMax ? 1 : 3);
});
test('should work with a "_.property" style `iteratee`', 2, function() {
var objects = [{ 'a': 2 }, { 'a': 3 }, { 'a': 1 }],
actual = func(objects, 'a');
deepEqual(actual, objects[isMax ? 1 : 2]);
var arrays = [[2], [3], [1]];
actual = func(arrays, 0);
deepEqual(actual, arrays[isMax ? 1 : 2]);
});
test('`_.' + methodName + '` should work when `iteratee` returns +/-Infinity', 1, function() {
var value = isMax ? -Infinity : Infinity,
object = { 'a': value };
var actual = func([object, { 'a': value }], function(object) {
return object.a;
});
strictEqual(actual, object);
});
test('`_.' + methodName + '` should iterate an object', 1, function() {
var actual = func({ 'a': 1, 'b': 2, 'c': 3 });
strictEqual(actual, isMax ? 3 : 1);
});
test('`_.' + methodName + '` should work with extremely large arrays', 1, function() {
var array = _.range(0, 5e5);
strictEqual(func(array), isMax ? 499999 : 0);
});
test('`_.' + methodName + '` should work as an iteratee for methods like `_.map`', 2, function() {
var arrays = [[2, 1], [5, 4], [7, 8]],
objects = [{ 'a': 2, 'b': 1 }, { 'a': 5, 'b': 4 }, { 'a': 7, 'b': 8 }],
expected = isMax ? [2, 5, 8] : [1, 4, 7];
_.each([arrays, objects], function(values) {
deepEqual(_.map(values, func), expected);
});
});
test('`_.' + methodName + '` should work when chaining on an array with only one value', 1, function() {
if (!isNpm) {
var actual = _([40])[methodName]();
strictEqual(actual, 40);
}
else {
skipTest();
}
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.mixin');
(function() {
function Wrapper(value) {
if (!(this instanceof Wrapper)) {
return new Wrapper(value);
}
if (_.has(value, '__wrapped__')) {
var actions = _.slice(value.__actions__),
chain = value.__chain__;
value = value.__wrapped__;
}
this.__actions__ = actions || [];
this.__chain__ = chain || false;
this.__wrapped__ = value;
}
Wrapper.prototype.value = function() {
return getUnwrappedValue(this);
};
var array = ['a'],
source = { 'a': function(array) { return array[0]; }, 'b': 'B' };
test('should mixin `source` methods into lodash', 4, function() {
if (!isNpm) {
_.mixin(source);
strictEqual(_.a(array), 'a');
strictEqual(_(array).a().value(), 'a');
delete _.a;
delete _.prototype.a;
ok(!('b' in _));
ok(!('b' in _.prototype));
delete _.b;
delete _.prototype.b;
}
else {
skipTest(4);
}
});
test('should mixin chaining methods by reference', 2, function() {
if (!isNpm) {
_.mixin(source);
_.a = _.constant('b');
strictEqual(_.a(array), 'b');
strictEqual(_(array).a().value(), 'a');
delete _.a;
delete _.prototype.a;
}
else {
skipTest(2);
}
});
test('should use `this` as the default `object` value', 3, function() {
var object = _.create(_);
object.mixin(source);
strictEqual(object.a(array), 'a');
ok(!('a' in _));
ok(!('a' in _.prototype));
delete Wrapper.a;
delete Wrapper.prototype.a;
delete Wrapper.b;
delete Wrapper.prototype.b;
});
test('should accept an `object` argument', 1, function() {
var object = {};
_.mixin(object, source);
strictEqual(object.a(array), 'a');
});
test('should return `object`', 2, function() {
var object = {};
strictEqual(_.mixin(object, source), object);
strictEqual(_.mixin(), _);
});
test('should work with a function for `object`', 2, function() {
_.mixin(Wrapper, source);
var wrapped = Wrapper(array),
actual = wrapped.a();
strictEqual(actual.value(), 'a');
ok(actual instanceof Wrapper);
delete Wrapper.a;
delete Wrapper.prototype.a;
delete Wrapper.b;
delete Wrapper.prototype.b;
});
test('should not assign inherited `source` properties', 1, function() {
function Foo() {}
Foo.prototype.a = _.noop;
deepEqual(_.mixin({}, new Foo, {}), {});
});
test('should accept an `options` argument', 16, function() {
function message(func, chain) {
return (func === _ ? 'lodash' : 'provided') + ' function should ' + (chain ? '' : 'not ') + 'chain';
}
_.each([_, Wrapper], function(func) {
_.each([false, true, { 'chain': false }, { 'chain': true }], function(options) {
if (!isNpm) {
if (func === _) {
_.mixin(source, options);
} else {
_.mixin(func, source, options);
}
var wrapped = func(array),
actual = wrapped.a();
if (options === true || (options && options.chain)) {
strictEqual(actual.value(), 'a', message(func, true));
ok(actual instanceof func, message(func, true));
} else {
strictEqual(actual, 'a', message(func, false));
ok(!(actual instanceof func), message(func, false));
}
delete func.a;
delete func.prototype.a;
delete func.b;
delete func.prototype.b;
}
else {
skipTest(2);
}
});
});
});
test('should not extend lodash when an `object` is provided with an empty `options` object', 1, function() {
_.mixin({ 'a': _.noop }, {});
ok(!('a' in _));
delete _.a;
});
test('should not error for non-object `options` values', 2, function() {
var pass = true;
try {
_.mixin({}, source, 1);
} catch(e) {
pass = false;
}
ok(pass);
pass = true;
try {
_.mixin(source, 1);
} catch(e) {
pass = false;
}
delete _.a;
delete _.prototype.a;
delete _.b;
delete _.prototype.b;
ok(pass);
});
test('should not return the existing wrapped value when chaining', 2, function() {
_.each([_, Wrapper], function(func) {
if (!isNpm) {
if (func === _) {
var wrapped = _(source),
actual = wrapped.mixin();
strictEqual(actual.value(), _);
}
else {
wrapped = _(func);
actual = wrapped.mixin(source);
notStrictEqual(actual, wrapped);
}
delete func.a;
delete func.prototype.a;
delete func.b;
delete func.prototype.b;
}
else {
skipTest();
}
});
});
test('should produce methods that work in a lazy chain sequence', 1, function() {
if (!isNpm) {
_.mixin({ 'a': _.countBy, 'b': _.filter });
var predicate = function(value) { return value > 2; },
actual = _([1, 2, 1, 3]).a(_.identity).map(square).b(predicate).take().value();
deepEqual(actual, [4]);
delete _.a;
delete _.prototype.a;
delete _.b;
delete _.prototype.b;
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.negate');
(function() {
test('should create a function that negates the result of `func`', 2, function() {
var negate = _.negate(function(n) {
return n % 2 == 0;
});
strictEqual(negate(1), true);
strictEqual(negate(2), false);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.noop');
(function() {
test('should return `undefined`', 1, function() {
var values = empties.concat(true, new Date, _, 1, /x/, 'a'),
expected = _.map(values, _.constant());
var actual = _.map(values, function(value, index) {
return index ? _.noop(value) : _.noop();
});
deepEqual(actual, expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.noConflict');
(function() {
test('should return the `lodash` function', 1, function() {
if (!isModularize) {
var oldDash = root._;
strictEqual(_.noConflict(), _);
root._ = oldDash;
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.now');
(function() {
asyncTest('should return the number of milliseconds that have elapsed since the Unix epoch', 2, function() {
var stamp = +new Date,
actual = _.now();
ok(actual >= stamp);
if (!(isRhino && isModularize)) {
setTimeout(function() {
ok(_.now() > actual);
QUnit.start();
}, 32);
}
else {
skipTest();
QUnit.start();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.omit');
(function() {
var args = arguments,
object = { 'a': 1, 'b': 2, 'c': 3 },
expected = { 'b': 2 };
test('should create an object with omitted properties', 2, function() {
deepEqual(_.omit(object, 'a'), { 'b': 2, 'c': 3 });
deepEqual(_.omit(object, 'a', 'c'), expected);
});
test('should support picking an array of properties', 1, function() {
deepEqual(_.omit(object, ['a', 'c']), expected);
});
test('should support picking an array of properties and individual properties', 1, function() {
deepEqual(_.omit(object, ['a'], 'c'), expected);
});
test('should iterate over inherited properties', 1, function() {
function Foo() {}
Foo.prototype = object;
deepEqual(_.omit(new Foo, 'a', 'c'), expected);
});
test('should return an empty object when `object` is nullish', 2, function() {
objectProto.a = 1;
_.each([null, undefined], function(value) {
deepEqual(_.omit(value, 'valueOf'), {});
});
delete objectProto.a;
});
test('should work with `arguments` objects as secondary arguments', 1, function() {
deepEqual(_.omit(object, args), expected);
});
test('should work with an array `object` argument', 1, function() {
deepEqual(_.omit([1, 2, 3], '0', '2'), { '1': 2 });
});
test('should work with a primitive `object` argument', 1, function() {
stringProto.a = 1;
stringProto.b = 2;
deepEqual(_.omit('', 'b'), { 'a': 1 });
delete stringProto.a;
delete stringProto.b;
});
test('should work with a `predicate` argument', 1, function() {
var actual = _.omit(object, function(num) {
return num != 2;
});
deepEqual(actual, expected);
});
test('should provide the correct `predicate` arguments', 1, function() {
var args,
object = { 'a': 1, 'b': 2 },
lastKey = _.keys(object).pop();
var expected = lastKey == 'b'
? [1, 'a', object]
: [2, 'b', object];
_.omit(object, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, expected);
});
test('should set the `this` binding', 1, function() {
var actual = _.omit(object, function(num) {
return num != this.b;
}, { 'b': 2 });
deepEqual(actual, expected);
});
test('should coerce property names to strings', 1, function() {
deepEqual(_.omit({ '0': 'a' }, 0), {});
});
}('a', 'c'));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.once');
(function() {
test('should invoke `func` once', 2, function() {
var count = 0,
once = _.once(function() { return ++count; });
once();
strictEqual(once(), 1);
strictEqual(count, 1);
});
test('should not set a `this` binding', 2, function() {
var once = _.once(function() { return ++this.count; }),
object = { 'count': 0, 'once': once };
object.once();
strictEqual(object.once(), 1);
strictEqual(object.count, 1);
});
test('should ignore recursive calls', 2, function() {
var count = 0;
var once = _.once(function() {
once();
return ++count;
});
strictEqual(once(), 1);
strictEqual(count, 1);
});
test('should not throw more than once', 2, function() {
var pass = true;
var once = _.once(function() {
throw new Error;
});
raises(function() { once(); }, Error);
try {
once();
} catch(e) {
pass = false;
}
ok(pass);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.pad');
(function() {
test('should pad a string to a given length', 1, function() {
strictEqual(_.pad('abc', 9), ' abc ');
});
test('should truncate pad characters to fit the pad length', 2, function() {
strictEqual(_.pad('abc', 8), ' abc ');
strictEqual(_.pad('abc', 8, '_-'), '_-abc_-_');
});
test('should coerce `string` to a string', 2, function() {
strictEqual(_.pad(Object('abc'), 4), 'abc ');
strictEqual(_.pad({ 'toString': _.constant('abc') }, 5), ' abc ');
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.padLeft');
(function() {
test('should pad a string to a given length', 1, function() {
strictEqual(_.padLeft('abc', 6), ' abc');
});
test('should truncate pad characters to fit the pad length', 1, function() {
strictEqual(_.padLeft('abc', 6, '_-'), '_-_abc');
});
test('should coerce `string` to a string', 2, function() {
strictEqual(_.padLeft(Object('abc'), 4), ' abc');
strictEqual(_.padLeft({ 'toString': _.constant('abc') }, 5), ' abc');
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.padRight');
(function() {
test('should pad a string to a given length', 1, function() {
strictEqual(_.padRight('abc', 6), 'abc ');
});
test('should truncate pad characters to fit the pad length', 1, function() {
strictEqual(_.padRight('abc', 6, '_-'), 'abc_-_');
});
test('should coerce `string` to a string', 2, function() {
strictEqual(_.padRight(Object('abc'), 4), 'abc ');
strictEqual(_.padRight({ 'toString': _.constant('abc') }, 5), 'abc ');
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('pad methods');
_.each(['pad', 'padLeft', 'padRight'], function(methodName) {
var func = _[methodName],
isPad = methodName == 'pad',
isPadLeft = methodName == 'padLeft';
test('`_.' + methodName + '` should not pad is string is >= `length`', 2, function() {
strictEqual(func('abc', 2), 'abc');
strictEqual(func('abc', 3), 'abc');
});
test('`_.' + methodName + '` should treat negative `length` as `0`', 2, function() {
_.each([0, -2], function(length) {
strictEqual(func('abc', length), 'abc');
});
});
test('`_.' + methodName + '` should coerce `length` to a number', 2, function() {
_.each(['', '4'], function(length) {
var actual = length ? (isPadLeft ? ' abc' : 'abc ') : 'abc';
strictEqual(func('abc', length), actual);
});
});
test('`_.' + methodName + '` should treat nullish values as empty strings', 6, function() {
_.each([null, '_-'], function(chars) {
var expected = chars ? (isPad ? '__' : chars) : ' ';
strictEqual(func(null, 2, chars), expected);
strictEqual(func(undefined, 2, chars), expected);
strictEqual(func('', 2, chars), expected);
});
});
test('`_.' + methodName + '` should work with nullish or empty string values for `chars`', 3, function() {
notStrictEqual(func('abc', 6, null), 'abc');
notStrictEqual(func('abc', 6, undefined), 'abc');
strictEqual(func('abc', 6, ''), 'abc');
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.pairs');
(function() {
test('should create a two dimensional array of key-value pairs', 1, function() {
var object = { 'a': 1, 'b': 2 };
deepEqual(_.pairs(object), [['a', 1], ['b', 2]]);
});
test('should work with an object that has a `length` property', 1, function() {
var object = { '0': 'a', '1': 'b', 'length': 2 };
deepEqual(_.pairs(object), [['0', 'a'], ['1', 'b'], ['length', 2]]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.parseInt');
(function() {
test('should accept a `radix` argument', 1, function() {
var expected = _.range(2, 37);
var actual = _.map(expected, function(radix) {
return _.parseInt('10', radix);
});
deepEqual(actual, expected);
});
test('should use a radix of `10`, for non-hexadecimals, if `radix` is `undefined` or `0`', 4, function() {
strictEqual(_.parseInt('10'), 10);
strictEqual(_.parseInt('10', 0), 10);
strictEqual(_.parseInt('10', 10), 10);
strictEqual(_.parseInt('10', undefined), 10);
});
test('should use a radix of `16`, for hexadecimals, if `radix` is `undefined` or `0`', 8, function() {
_.each(['0x20', '0X20'], function(string) {
strictEqual(_.parseInt(string), 32);
strictEqual(_.parseInt(string, 0), 32);
strictEqual(_.parseInt(string, 16), 32);
strictEqual(_.parseInt(string, undefined), 32);
});
});
test('should use a radix of `10` for string with leading zeros', 2, function() {
strictEqual(_.parseInt('08'), 8);
strictEqual(_.parseInt('08', 10), 8);
});
test('should parse strings with leading whitespace (test in Chrome, Firefox, and Opera)', 2, function() {
var expected = [8, 8, 10, 10, 32, 32, 32, 32];
_.times(2, function(index) {
var actual = [],
func = (index ? (lodashBizarro || {}) : _).parseInt;
if (func) {
_.times(2, function(otherIndex) {
var string = otherIndex ? '10' : '08';
actual.push(
func(whitespace + string, 10),
func(whitespace + string)
);
});
_.each(['0x20', '0X20'], function(string) {
actual.push(
func(whitespace + string),
func(whitespace + string, 16)
);
});
deepEqual(actual, expected);
}
else {
skipTest();
}
});
});
test('should coerce `radix` to a number', 2, function() {
var object = { 'valueOf': _.constant(0) };
strictEqual(_.parseInt('08', object), 8);
strictEqual(_.parseInt('0x20', object), 32);
});
test('should work as an iteratee for methods like `_.map`', 2, function() {
var strings = _.map(['6', '08', '10'], Object),
actual = _.map(strings, _.parseInt);
deepEqual(actual, [6, 8, 10]);
actual = _.map('123', _.parseInt);
deepEqual(actual, [1, 2, 3]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('partial methods');
_.each(['partial', 'partialRight'], function(methodName) {
var func = _[methodName],
isPartial = methodName == 'partial',
ph = func.placeholder;
test('`_.' + methodName + '` partially applies arguments', 1, function() {
var par = func(_.identity, 'a');
strictEqual(par(), 'a');
});
test('`_.' + methodName + '` creates a function that can be invoked with additional arguments', 1, function() {
var fn = function(a, b) { return [a, b]; },
par = func(fn, 'a'),
expected = ['a', 'b'];
deepEqual(par('b'), isPartial ? expected : expected.reverse());
});
test('`_.' + methodName + '` works when there are no partially applied arguments and the created function is invoked without additional arguments', 1, function() {
var fn = function() { return arguments.length; },
par = func(fn);
strictEqual(par(), 0);
});
test('`_.' + methodName + '` works when there are no partially applied arguments and the created function is invoked with additional arguments', 1, function() {
var par = func(_.identity);
strictEqual(par('a'), 'a');
});
test('`_.' + methodName + '` should support placeholders', 4, function() {
var fn = function() { return slice.call(arguments); },
par = func(fn, ph, 'b', ph);
deepEqual(par('a', 'c'), ['a', 'b', 'c']);
deepEqual(par('a'), ['a', 'b', undefined]);
deepEqual(par(), [undefined, 'b', undefined]);
if (isPartial) {
deepEqual(par('a', 'c', 'd'), ['a', 'b', 'c', 'd']);
} else {
par = func(fn, ph, 'c', ph);
deepEqual(par('a', 'b', 'd'), ['a', 'b', 'c', 'd']);
}
});
test('`_.' + methodName + '` should not set a `this` binding', 3, function() {
var fn = function() { return this.a; },
object = { 'a': 1 };
var par = func(_.bind(fn, object));
strictEqual(par(), object.a);
par = _.bind(func(fn), object);
strictEqual(par(), object.a);
object.par = func(fn);
strictEqual(object.par(), object.a);
});
test('`_.' + methodName + '` creates a function with a `length` of `0`', 1, function() {
var fn = function(a, b, c) {},
par = func(fn, 'a');
strictEqual(par.length, 0);
});
test('`_.' + methodName + '` ensure `new partialed` is an instance of `func`', 2, function() {
function Foo(value) {
return value && object;
}
var object = {},
par = func(Foo);
ok(new par instanceof Foo);
strictEqual(new par(true), object);
});
test('`_.' + methodName + '` should clone metadata for created functions', 3, function() {
function greet(greeting, name) {
return greeting + ' ' + name;
}
var par1 = func(greet, 'hi'),
par2 = func(par1, 'barney'),
par3 = func(par1, 'pebbles');
strictEqual(par1('fred'), isPartial ? 'hi fred' : 'fred hi');
strictEqual(par2(), isPartial ? 'hi barney' : 'barney hi');
strictEqual(par3(), isPartial ? 'hi pebbles' : 'pebbles hi');
});
test('`_.' + methodName + '` should work with curried functions', 2, function() {
var fn = function(a, b, c) { return a + b + c; },
curried = _.curry(func(fn, 1), 2);
strictEqual(curried(2, 3), 6);
strictEqual(curried(2)(3), 6);
});
test('should work with placeholders and curried functions', 1, function() {
var fn = function() { return slice.call(arguments); },
curried = _.curry(fn),
par = func(curried, ph, 'b', ph, 'd');
deepEqual(par('a', 'c'), ['a', 'b', 'c', 'd']);
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.partialRight');
(function() {
test('should work as a deep `_.defaults`', 1, function() {
var object = { 'a': { 'b': 1 } },
source = { 'a': { 'b': 2, 'c': 3 } },
expected = { 'a': { 'b': 1, 'c': 3 } };
var defaultsDeep = _.partialRight(_.merge, function deep(value, other) {
return _.merge(value, other, deep);
});
deepEqual(defaultsDeep(object, source), expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('methods using `createWrapper`');
(function() {
var ph1 = _.bind.placeholder,
ph2 = _.bindKey.placeholder,
ph3 = _.partial.placeholder,
ph4 = _.partialRight.placeholder;
test('combinations of partial functions should work', 1, function() {
function fn() {
return slice.call(arguments);
}
var a = _.partial(fn),
b = _.partialRight(a, 3),
c = _.partial(b, 1);
deepEqual(c(2), [1, 2, 3]);
});
test('combinations of bound and partial functions should work', 3, function() {
function fn() {
var result = [this.a];
push.apply(result, arguments);
return result;
}
var expected = [1, 2, 3, 4],
object = { 'a': 1, 'fn': fn };
var a = _.bindKey(object, 'fn'),
b = _.partialRight(a, 4),
c = _.partial(b, 2);
deepEqual(c(3), expected);
a = _.bind(fn, object);
b = _.partialRight(a, 4);
c = _.partial(b, 2);
deepEqual(c(3), expected);
a = _.partial(fn, 2);
b = _.bind(a, object);
c = _.partialRight(b, 4);
deepEqual(c(3), expected);
});
test('combinations of functions with placeholders should work', 3, function() {
function fn() {
return slice.call(arguments);
}
var expected = [1, 2, 3, 4, 5, 6],
object = { 'fn': fn };
var a = _.bindKey(object, 'fn', ph2, 2),
b = _.partialRight(a, ph4, 6),
c = _.partial(b, 1, ph3, 4);
deepEqual(c(3, 5), expected);
a = _.bind(fn, object, ph1, 2);
b = _.partialRight(a, ph4, 6);
c = _.partial(b, 1, ph3, 4);
deepEqual(c(3, 5), expected);
a = _.partial(fn, ph3, 2);
b = _.bind(a, object, 1, ph1, 4);
c = _.partialRight(b, ph4, 6);
deepEqual(c(3, 5), expected);
});
test('combinations of functions with overlaping placeholders should work', 3, function() {
function fn() {
return slice.call(arguments);
}
var expected = [1, 2, 3, 4],
object = { 'fn': fn };
var a = _.bindKey(object, 'fn', ph2, 2),
b = _.partialRight(a, ph4, 4),
c = _.partial(b, ph3, 3);
deepEqual(c(1), expected);
a = _.bind(fn, object, ph1, 2);
b = _.partialRight(a, ph4, 4);
c = _.partial(b, ph3, 3);
deepEqual(c(1), expected);
a = _.partial(fn, ph3, 2);
b = _.bind(a, object, ph1, 3);
c = _.partialRight(b, ph4, 4);
deepEqual(c(1), expected);
});
test('recursively bound functions should work', 1, function() {
function fn() {
return this.a;
}
var a = _.bind(fn, { 'a': 1 }),
b = _.bind(a, { 'a': 2 }),
c = _.bind(b, { 'a': 3 });
strictEqual(c(), 1);
});
test('should work when hot', 12, function() {
_.times(2, function(index) {
var fn = function() {
var result = [this];
push.apply(result, arguments);
return result;
};
var object = {},
bound1 = index ? _.bind(fn, object, 1) : _.bind(fn, object),
expected = [object, 1, 2, 3];
var actual = _.last(_.times(HOT_COUNT, function() {
var bound2 = index ? _.bind(bound1, null, 2) : _.bind(bound1);
return index ? bound2(3) : bound2(1, 2, 3);
}));
deepEqual(actual, expected);
actual = _.last(_.times(HOT_COUNT, function() {
var bound1 = index ? _.bind(fn, object, 1) : _.bind(fn, object),
bound2 = index ? _.bind(bound1, null, 2) : _.bind(bound1);
return index ? bound2(3) : bound2(1, 2, 3);
}));
deepEqual(actual, expected);
});
_.each(['curry', 'curryRight'], function(methodName, index) {
var fn = function(a, b, c) { return [a, b, c]; },
curried = _[methodName](fn),
expected = index ? [3, 2, 1] : [1, 2, 3];
var actual = _.last(_.times(HOT_COUNT, function() {
return curried(1)(2)(3);
}));
deepEqual(actual, expected);
actual = _.last(_.times(HOT_COUNT, function() {
var curried = _[methodName](fn);
return curried(1)(2)(3);
}));
deepEqual(actual, expected);
});
_.each(['partial', 'partialRight'], function(methodName, index) {
var func = _[methodName],
fn = function() { return slice.call(arguments); },
par1 = func(fn, 1),
expected = index ? [3, 2, 1] : [1, 2, 3];
var actual = _.last(_.times(HOT_COUNT, function() {
var par2 = func(par1, 2);
return par2(3);
}));
deepEqual(actual, expected);
actual = _.last(_.times(HOT_COUNT, function() {
var par1 = func(fn, 1),
par2 = func(par1, 2);
return par2(3);
}));
deepEqual(actual, expected);
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.partition');
(function() {
var array = [1, 0, 1];
test('should return two groups of elements', 3, function() {
deepEqual(_.partition([], _.identity), [[], []]);
deepEqual(_.partition(array, _.constant(true)), [array, []]);
deepEqual(_.partition(array, _.constant(false)), [[], array]);
});
test('should use `_.identity` when `predicate` is nullish', 1, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant([[1, 1], [0]]));
var actual = _.map(values, function(value, index) {
return index ? _.partition(array, value) : _.partition(array);
});
deepEqual(actual, expected);
});
test('should provide the correct `predicate` arguments', 1, function() {
var args;
_.partition(array, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [1, 0, array]);
});
test('should support the `thisArg` argument', 1, function() {
var actual = _.partition([1.1, 0.2, 1.3], function(num) {
return this.floor(num);
}, Math);
deepEqual(actual, [[1.1, 1.3], [0.2]]);
});
test('should work with a "_.property" style `predicate`', 1, function() {
var objects = [{ 'a': 1 }, { 'a': 1 }, { 'b': 2 }],
actual = _.partition(objects, 'a');
deepEqual(actual, [objects.slice(0, 2), objects.slice(2)]);
});
test('should work with a number for `predicate`', 2, function() {
var array = [
[1, 0],
[0, 1],
[1, 0]
];
deepEqual(_.partition(array, 0), [[array[0], array[2]], [array[1]]]);
deepEqual(_.partition(array, 1), [[array[1]], [array[0], array[2]]]);
});
test('should work with an object for `collection`', 1, function() {
var actual = _.partition({ 'a': 1.1, 'b': 0.2, 'c': 1.3 }, function(num) {
return Math.floor(num);
});
deepEqual(actual, [[1.1, 1.3], [0.2]]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.pick');
(function() {
var args = arguments,
object = { 'a': 1, 'b': 2, 'c': 3 },
expected = { 'a': 1, 'c': 3 };
test('should create an object of picked properties', 2, function() {
deepEqual(_.pick(object, 'a'), { 'a': 1 });
deepEqual(_.pick(object, 'a', 'c'), expected);
});
test('should support picking an array of properties', 1, function() {
deepEqual(_.pick(object, ['a', 'c']), expected);
});
test('should support picking an array of properties and individual properties', 1, function() {
deepEqual(_.pick(object, ['a'], 'c'), expected);
});
test('should iterate over inherited properties', 1, function() {
function Foo() {}
Foo.prototype = object;
deepEqual(_.pick(new Foo, 'a', 'c'), expected);
});
test('should return an empty object when `object` is nullish', 2, function() {
_.each([null, undefined], function(value) {
deepEqual(_.pick(value, 'valueOf'), {});
});
});
test('should work with `arguments` objects as secondary arguments', 1, function() {
deepEqual(_.pick(object, args), expected);
});
test('should work with an array `object` argument', 1, function() {
deepEqual(_.pick([1, 2, 3], '1'), { '1': 2 });
});
test('should work with a primitive `object` argument', 1, function() {
deepEqual(_.pick('', 'slice'), { 'slice': ''.slice });
});
test('should work with a `predicate` argument', 1, function() {
var actual = _.pick(object, function(num) {
return num != 2;
});
deepEqual(actual, expected);
});
test('should provide the correct `predicate` arguments', 1, function() {
var args,
object = { 'a': 1, 'b': 2 },
lastKey = _.keys(object).pop();
var expected = lastKey == 'b'
? [1, 'a', object]
: [2, 'b', object];
_.pick(object, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, expected);
});
test('should set the `this` binding', 1, function() {
var actual = _.pick(object, function(num) {
return num != this.b;
}, { 'b': 2 });
deepEqual(actual, expected);
});
test('should coerce property names to strings', 1, function() {
deepEqual(_.pick({ '0': 'a', '1': 'b' }, 0), { '0': 'a' });
});
}('a', 'c'));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.pluck');
(function() {
test('should return an array of property values from each element of a collection', 1, function() {
var objects = [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }];
deepEqual(_.pluck(objects, 'name'), ['barney', 'fred']);
});
test('should pluck inherited property values', 1, function() {
function Foo() { this.a = 1; }
Foo.prototype.b = 2;
deepEqual(_.pluck([new Foo], 'b'), [2]);
});
test('should work with an object for `collection`', 1, function() {
var object = { 'a': [1], 'b': [1, 2], 'c': [1, 2, 3] };
deepEqual(_.pluck(object, 'length'), [1, 2, 3]);
});
test('should return `undefined` for undefined properties', 1, function() {
var array = [{ 'a': 1 }],
actual = [_.pluck(array, 'b'), _.pluck(array, 'c')];
deepEqual(actual, [[undefined], [undefined]]);
});
test('should work with nullish elements', 1, function() {
var objects = [{ 'a': 1 }, null, undefined, { 'a': 4 }];
deepEqual(_.pluck(objects, 'a'), [1, undefined, undefined, 4]);
});
test('should coerce `key` to a string', 1, function() {
function fn() {}
fn.toString = _.constant('fn');
var objects = [{ 'null': 1 }, { 'undefined': 2 }, { 'fn': 3 }, { '[object Object]': 4 }],
values = [null, undefined, fn, {}]
var actual = _.map(objects, function(object, index) {
return _.pluck([object], values[index]);
});
deepEqual(actual, [[1], [2], [3], [4]]);
});
test('should work in a lazy chain sequence', 2, function() {
if (!isNpm) {
var array = [{ 'a': 1 }, null, { 'a': 3 }, { 'a': 4 }],
actual = _(array).pluck('a').value();
deepEqual(actual, [1, undefined, 3, 4]);
actual = _(array).filter(Boolean).pluck('a').value();
deepEqual(actual, [1, 3, 4]);
}
else {
skipTest(2);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.property');
(function() {
test('should create a function that plucks a property value of a given object', 4, function() {
var object = { 'a': 1 };
_.each(['a', ['a']], function(path) {
var prop = _.property(path);
strictEqual(prop.length, 1);
strictEqual(prop(object), 1);
});
});
test('should pluck deep property values', 2, function() {
var object = { 'a': { 'b': { 'c': 3 } } };
_.each(['a.b.c', ['a', 'b', 'c']], function(path) {
var prop = _.property(path);
strictEqual(prop(object), 3);
});
});
test('should work with non-string `path` arguments', 2, function() {
var array = [1, 2, 3];
_.each([1, [1]], function(path) {
var prop = _.property(path);
strictEqual(prop(array), 2);
});
});
test('should coerce key to a string', 1, function() {
function fn() {}
fn.toString = _.constant('fn');
var expected = [1, 1, 2, 2, 3, 3, 4, 4],
objects = [{ 'null': 1 }, { 'undefined': 2 }, { 'fn': 3 }, { '[object Object]': 4 }],
values = [null, undefined, fn, {}];
var actual = _.transform(objects, function(result, object, index) {
var key = values[index];
_.each([key, [key]], function(path) {
var prop = _.property(key);
result.push(prop(object));
});
});
deepEqual(actual, expected);
});
test('should pluck inherited property values', 2, function() {
function Foo() {}
Foo.prototype.a = 1;
_.each(['a', ['a']], function(path) {
var prop = _.property(path);
strictEqual(prop(new Foo), 1);
});
});
test('should pluck string indexes (test in IE < 9)', 2, function() {
_.each([1, [1]], function(path) {
var prop = _.property(path);
strictEqual(prop('xo'), 'o');
});
});
test('should pluck a key over a path', 2, function() {
var object = { 'a.b.c': 3, 'a': { 'b': { 'c': 4 } } };
_.each(['a.b.c', ['a.b.c']], function(path) {
var prop = _.property(path);
strictEqual(prop(object), 3);
});
});
test('should return `undefined` when `object` is nullish', 2, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant(undefined));
_.each(['constructor', ['constructor']], function(path) {
var prop = _.property(path);
var actual = _.map(values, function(value, index) {
return index ? prop(value) : prop();
});
deepEqual(actual, expected);
});
});
test('should return `undefined` with deep paths when `object` is nullish', 2, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant(undefined));
_.each(['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], function(path) {
var prop = _.property(path);
var actual = _.map(values, function(value, index) {
return index ? prop(value) : prop();
});
deepEqual(actual, expected);
});
});
test('should return `undefined` if parts of `path` are missing', 4, function() {
var object = {};
_.each(['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']], function(path) {
var prop = _.property(path);
strictEqual(prop(object), undefined);
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.propertyOf');
(function() {
test('should create a function that plucks a property value of a given key', 3, function() {
var object = { 'a': 1 },
propOf = _.propertyOf(object);
strictEqual(propOf.length, 1);
_.each(['a', ['a']], function(path) {
strictEqual(propOf(path), 1);
});
});
test('should pluck deep property values', 2, function() {
var object = { 'a': { 'b': { 'c': 3 } } },
propOf = _.propertyOf(object);
_.each(['a.b.c', ['a', 'b', 'c']], function(path) {
strictEqual(propOf(path), 3);
});
});
test('should work with non-string `path` arguments', 2, function() {
var array = [1, 2, 3],
propOf = _.propertyOf(array);
_.each([1, [1]], function(path) {
strictEqual(propOf(path), 2);
});
});
test('should coerce key to a string', 1, function() {
function fn() {}
fn.toString = _.constant('fn');
var expected = [1, 1, 2, 2, 3, 3, 4, 4],
objects = [{ 'null': 1 }, { 'undefined': 2 }, { 'fn': 3 }, { '[object Object]': 4 }],
values = [null, undefined, fn, {}];
var actual = _.transform(objects, function(result, object, index) {
var key = values[index];
_.each([key, [key]], function(path) {
var propOf = _.propertyOf(object);
result.push(propOf(key));
});
});
deepEqual(actual, expected);
});
test('should pluck inherited property values', 2, function() {
function Foo() { this.a = 1; }
Foo.prototype.b = 2;
var propOf = _.propertyOf(new Foo);
_.each(['b', ['b']], function(path) {
strictEqual(propOf(path), 2);
});
});
test('should pluck string indexes (test in IE < 9)', 2, function() {
var propOf = _.propertyOf('xo');
_.each([1, [1]], function(path) {
strictEqual(propOf(path), 'o');
});
});
test('should pluck a key over a path', 2, function() {
var object = { 'a.b.c': 3, 'a': { 'b': { 'c': 4 } } },
propOf = _.propertyOf(object);
_.each(['a.b.c', ['a.b.c']], function(path) {
strictEqual(propOf(path), 3);
});
});
test('should return `undefined` when `object` is nullish', 2, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant(undefined));
_.each(['constructor', ['constructor']], function(path) {
var actual = _.map(values, function(value, index) {
var propOf = index ? _.propertyOf(value) : _.propertyOf();
return propOf(path);
});
deepEqual(actual, expected);
});
});
test('should return `undefined` with deep paths when `object` is nullish', 2, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant(undefined));
_.each(['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], function(path) {
var actual = _.map(values, function(value, index) {
var propOf = index ? _.propertyOf(value) : _.propertyOf();
return propOf(path);
});
deepEqual(actual, expected);
});
});
test('should return `undefined` if parts of `path` are missing', 4, function() {
var propOf = _.propertyOf({});
_.each(['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']], function(path) {
strictEqual(propOf(path), undefined);
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.pull');
(function() {
test('should modify and return the array', 2, function() {
var array = [1, 2, 3],
actual = _.pull(array, 1, 3);
deepEqual(array, [2]);
ok(actual === array);
});
test('should preserve holes in arrays', 2, function() {
var array = [1, 2, 3, 4];
delete array[1];
delete array[3];
_.pull(array, 1);
ok(!('0' in array));
ok(!('2' in array));
});
test('should treat holes as `undefined`', 1, function() {
var array = [1, 2, 3];
delete array[1];
_.pull(array, undefined);
deepEqual(array, [1, 3]);
});
test('should match `NaN`', 1, function() {
var array = [1, NaN, 3, NaN];
_.pull(array, NaN);
deepEqual(array, [1, 3]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.pullAt');
(function() {
test('should modify the array and return removed elements', 2, function() {
var array = [1, 2, 3],
actual = _.pullAt(array, [0, 1]);
deepEqual(array, [3]);
deepEqual(actual, [1, 2]);
});
test('should work with unsorted indexes', 2, function() {
var array = [1, 2, 3, 4],
actual = _.pullAt(array, [1, 3, 0]);
deepEqual(array, [3]);
deepEqual(actual, [2, 4, 1]);
});
test('should work with repeated indexes', 2, function() {
var array = [1, 2, 3, 4],
actual = _.pullAt(array, [0, 2, 0, 1, 0, 2]);
deepEqual(array, [4]);
deepEqual(actual, [1, 3, 1, 2, 1, 3]);
});
test('should use `undefined` for nonexistent indexes', 2, function() {
var array = ['a', 'b', 'c'],
actual = _.pullAt(array, [2, 4, 0]);
deepEqual(array, ['b']);
deepEqual(actual, ['c', undefined, 'a']);
});
test('should ignore non-index keys', 2, function() {
var array = ['a', 'b', 'c'],
clone = array.slice();
array['1.1'] = array['-1'] = 1;
var values = _.reject(empties, function(value) {
return value === 0 || _.isArray(value);
}).concat(-1, 1.1, 'pop', 'push');
var expected = _.map(values, _.constant(undefined)),
actual = _.pullAt(array, values);
deepEqual(actual, expected);
deepEqual(array, clone);
});
test('should return an empty array when no indexes are provided', 4, function() {
var array = ['a', 'b', 'c'],
actual = _.pullAt(array);
deepEqual(array, ['a', 'b', 'c']);
deepEqual(actual, []);
actual = _.pullAt(array, [], []);
deepEqual(array, ['a', 'b', 'c']);
deepEqual(actual, []);
});
test('should accept multiple index arguments', 2, function() {
var array = ['a', 'b', 'c', 'd'],
actual = _.pullAt(array, 3, 0, 2);
deepEqual(array, ['b']);
deepEqual(actual, ['d', 'a', 'c']);
});
test('should accept multiple arrays of indexes', 2, function() {
var array = ['a', 'b', 'c', 'd'],
actual = _.pullAt(array, [3], [0, 2]);
deepEqual(array, ['b']);
deepEqual(actual, ['d', 'a', 'c']);
});
test('should work with a falsey `array` argument when keys are provided', 1, function() {
var expected = _.map(falsey, _.constant(Array(4)));
var actual = _.map(falsey, function(value) {
try {
return _.pullAt(value, 0, 1, 'pop', 'push');
} catch(e) {}
});
deepEqual(actual, expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.random');
(function() {
var array = Array(1000);
test('should return `0` or `1` when arguments are not provided', 1, function() {
var actual = _.map(array, function() {
return _.random();
});
deepEqual(_.uniq(actual).sort(), [0, 1]);
});
test('should support a `min` and `max` argument', 1, function() {
var min = 5,
max = 10;
ok(_.some(array, function() {
var result = _.random(min, max);
return result >= min && result <= max;
}));
});
test('should support not providing a `max` argument', 1, function() {
var min = 0,
max = 5;
ok(_.some(array, function() {
var result = _.random(max);
return result >= min && result <= max;
}));
});
test('should support large integer values', 2, function() {
var min = Math.pow(2, 31),
max = Math.pow(2, 62);
ok(_.every(array, function() {
var result = _.random(min, max);
return result >= min && result <= max;
}));
ok(_.some(array, function() {
return _.random(Number.MAX_VALUE) > 0;
}));
});
test('should coerce arguments to numbers', 1, function() {
strictEqual(_.random('1', '1'), 1);
});
test('should support floats', 2, function() {
var min = 1.5,
max = 1.6,
actual = _.random(min, max);
ok(actual % 1);
ok(actual >= min && actual <= max);
});
test('should support providing a `floating` argument', 3, function() {
var actual = _.random(true);
ok(actual % 1 && actual >= 0 && actual <= 1);
actual = _.random(2, true);
ok(actual % 1 && actual >= 0 && actual <= 2);
actual = _.random(2, 4, true);
ok(actual % 1 && actual >= 2 && actual <= 4);
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var array = [1, 2, 3],
expected = _.map(array, _.constant(true)),
randoms = _.map(array, _.random);
var actual = _.map(randoms, function(result, index) {
return result >= 0 && result <= array[index] && (result % 1) == 0;
});
deepEqual(actual, expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.range');
(function() {
test('should work with an `end` argument', 1, function() {
deepEqual(_.range(4), [0, 1, 2, 3]);
});
test('should work with `start` and `end` arguments', 1, function() {
deepEqual(_.range(1, 5), [1, 2, 3, 4]);
});
test('should work with `start`, `end`, and `step` arguments', 1, function() {
deepEqual(_.range(0, 20, 5), [0, 5, 10, 15]);
});
test('should support a `step` of `0`', 1, function() {
deepEqual(_.range(1, 4, 0), [1, 1, 1]);
});
test('should work with a `step` larger than `end`', 1, function() {
deepEqual(_.range(1, 5, 20), [1]);
});
test('should work with a negative `step` argument', 2, function() {
deepEqual(_.range(0, -4, -1), [0, -1, -2, -3]);
deepEqual(_.range(21, 10, -3), [21, 18, 15, 12]);
});
test('should treat falsey `start` arguments as `0`', 13, function() {
_.each(falsey, function(value, index) {
if (index) {
deepEqual(_.range(value), []);
deepEqual(_.range(value, 1), [0]);
} else {
deepEqual(_.range(), []);
}
});
});
test('should coerce arguments to finite numbers', 1, function() {
var actual = [_.range('0', 1), _.range('1'), _.range(0, 1, '1'), _.range(NaN), _.range(NaN, NaN)];
deepEqual(actual, [[0], [0], [0], [], []]);
});
test('should work as an iteratee for methods like `_.map`', 2, function() {
var array = [1, 2, 3],
object = { 'a': 1, 'b': 2, 'c': 3 },
expected = [[0], [0, 1], [0, 1, 2]];
_.each([array, object], function(collection) {
var actual = _.map(collection, _.range);
deepEqual(actual, expected);
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.rearg');
(function() {
function fn() {
return slice.call(arguments);
}
test('should reorder arguments provided to `func`', 1, function() {
var rearged = _.rearg(fn, [2, 0, 1]);
deepEqual(rearged('b', 'c', 'a'), ['a', 'b', 'c']);
});
test('should work with repeated indexes', 1, function() {
var rearged = _.rearg(fn, [1, 1, 1]);
deepEqual(rearged('c', 'a', 'b'), ['a', 'a', 'a']);
});
test('should use `undefined` for nonexistent indexes', 1, function() {
var rearged = _.rearg(fn, [1, 4]);
deepEqual(rearged('b', 'a', 'c'), ['a', undefined, 'c']);
});
test('should use `undefined` for non-index values', 1, function() {
var values = _.reject(empties, function(value) {
return value === 0 || _.isArray(value);
}).concat(-1, 1.1);
var expected = _.map(values, _.constant([undefined, 'b', 'c']));
var actual = _.map(values, function(value) {
var rearged = _.rearg(fn, [value]);
return rearged('a', 'b', 'c');
});
deepEqual(actual, expected);
});
test('should not rearrange arguments when no indexes are provided', 2, function() {
var rearged = _.rearg(fn);
deepEqual(rearged('a', 'b', 'c'), ['a', 'b', 'c']);
rearged = _.rearg(fn, [], []);
deepEqual(rearged('a', 'b', 'c'), ['a', 'b', 'c']);
});
test('should accept multiple index arguments', 1, function() {
var rearged = _.rearg(fn, 2, 0, 1);
deepEqual(rearged('b', 'c', 'a'), ['a', 'b', 'c']);
});
test('should accept multiple arrays of indexes', 1, function() {
var rearged = _.rearg(fn, [2], [0, 1]);
deepEqual(rearged('b', 'c', 'a'), ['a', 'b', 'c']);
});
test('should work with fewer indexes than arguments', 1, function() {
var rearged = _.rearg(fn, [1, 0]);
deepEqual(rearged('b', 'a', 'c'), ['a', 'b', 'c']);
});
test('should work on functions that have been rearged', 1, function() {
var rearged1 = _.rearg(fn, 2, 1, 0),
rearged2 = _.rearg(rearged1, 1, 0, 2);
deepEqual(rearged2('b', 'c', 'a'), ['a', 'b', 'c']);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.reduce');
(function() {
var array = [1, 2, 3];
test('should use the first element of a collection as the default `accumulator`', 1, function() {
strictEqual(_.reduce(array), 1);
});
test('should provide the correct `iteratee` arguments when iterating an array', 2, function() {
var args;
_.reduce(array, function() {
args || (args = slice.call(arguments));
}, 0);
deepEqual(args, [0, 1, 0, array]);
args = null;
_.reduce(array, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [1, 2, 1, array]);
});
test('should provide the correct `iteratee` arguments when iterating an object', 2, function() {
var args,
object = { 'a': 1, 'b': 2 },
firstKey = _.first(_.keys(object));
var expected = firstKey == 'a'
? [0, 1, 'a', object]
: [0, 2, 'b', object];
_.reduce(object, function() {
args || (args = slice.call(arguments));
}, 0);
deepEqual(args, expected);
args = null;
expected = firstKey == 'a'
? [1, 2, 'b', object]
: [2, 1, 'a', object];
_.reduce(object, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, expected);
});
_.each({
'literal': 'abc',
'object': Object('abc')
},
function(collection, key) {
test('should work with a string ' + key + ' for `collection` (test in IE < 9)', 2, function() {
var args;
var actual = _.reduce(collection, function(accumulator, value) {
args || (args = slice.call(arguments));
return accumulator + value;
});
deepEqual(args, ['a', 'b', 1, collection]);
strictEqual(actual, 'abc');
});
});
test('should be aliased', 2, function() {
strictEqual(_.foldl, _.reduce);
strictEqual(_.inject, _.reduce);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.reduceRight');
(function() {
var array = [1, 2, 3];
test('should use the last element of a collection as the default `accumulator`', 1, function() {
strictEqual(_.reduceRight(array), 3);
});
test('should provide the correct `iteratee` arguments when iterating an array', 2, function() {
var args;
_.reduceRight(array, function() {
args || (args = slice.call(arguments));
}, 0);
deepEqual(args, [0, 3, 2, array]);
args = null;
_.reduceRight(array, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [3, 2, 1, array]);
});
test('should provide the correct `iteratee` arguments when iterating an object', 2, function() {
var args,
object = { 'a': 1, 'b': 2 },
lastKey = _.last(_.keys(object));
var expected = lastKey == 'b'
? [0, 2, 'b', object]
: [0, 1, 'a', object];
_.reduceRight(object, function() {
args || (args = slice.call(arguments));
}, 0);
deepEqual(args, expected);
args = null;
expected = lastKey == 'b'
? [2, 1, 'a', object]
: [1, 2, 'b', object];
_.reduceRight(object, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, expected);
});
_.each({
'literal': 'abc',
'object': Object('abc')
},
function(collection, key) {
test('should work with a string ' + key + ' for `collection` (test in IE < 9)', 2, function() {
var args;
var actual = _.reduceRight(collection, function(accumulator, value) {
args || (args = slice.call(arguments));
return accumulator + value;
});
deepEqual(args, ['c', 'b', 1, collection]);
strictEqual(actual, 'cba');
});
});
test('should be aliased', 1, function() {
strictEqual(_.foldr, _.reduceRight);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('reduce methods');
_.each(['reduce', 'reduceRight'], function(methodName) {
var func = _[methodName],
array = [1, 2, 3],
isReduce = methodName == 'reduce';
test('`_.' + methodName + '` should reduce a collection to a single value', 1, function() {
var actual = func(['a', 'b', 'c'], function(accumulator, value) {
return accumulator + value;
}, '');
strictEqual(actual, isReduce ? 'abc' : 'cba');
});
test('`_.' + methodName + '` should support the `thisArg` argument', 1, function() {
var actual = func(array, function(sum, num, index) {
return sum + this[index];
}, 0, array);
deepEqual(actual, 6);
});
test('`_.' + methodName + '` should support empty collections without an initial `accumulator` value', 1, function() {
var actual = [],
expected = _.map(empties, _.constant());
_.each(empties, function(value) {
try {
actual.push(func(value, _.noop));
} catch(e) {}
});
deepEqual(actual, expected);
});
test('`_.' + methodName + '` should support empty collections with an initial `accumulator` value', 1, function() {
var expected = _.map(empties, _.constant('x'));
var actual = _.map(empties, function(value) {
try {
return func(value, _.noop, 'x');
} catch(e) {}
});
deepEqual(actual, expected);
});
test('`_.' + methodName + '` should handle an initial `accumulator` value of `undefined`', 1, function() {
var actual = func([], _.noop, undefined);
strictEqual(actual, undefined);
});
test('`_.' + methodName + '` should return `undefined` for empty collections when no `accumulator` is provided (test in IE > 9 and modern browsers)', 2, function() {
var array = [],
object = { '0': 1, 'length': 0 };
if ('__proto__' in array) {
array.__proto__ = object;
strictEqual(_.reduce(array, _.noop), undefined);
}
else {
skipTest();
}
strictEqual(_.reduce(object, _.noop), undefined);
});
test('`_.' + methodName + '` should return an unwrapped value when implicityly chaining', 1, function() {
if (!isNpm) {
strictEqual(_(array)[methodName](add), 6);
}
else {
skipTest();
}
});
test('`_.' + methodName + '` should return a wrapped value when explicitly chaining', 1, function() {
if (!isNpm) {
ok(_(array).chain()[methodName](add) instanceof _);
}
else {
skipTest();
}
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.reject');
(function() {
var array = [1, 2, 3];
test('should return elements the `predicate` returns falsey for', 1, function() {
var actual = _.reject(array, function(num) {
return num % 2;
});
deepEqual(actual, [2]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('filter methods');
_.each(['filter', 'reject'], function(methodName) {
var array = [1, 2, 3, 4],
func = _[methodName],
isFilter = methodName == 'filter',
objects = [{ 'a': 0 }, { 'a': 1 }];
test('`_.' + methodName + '` should not modify the resulting value from within `predicate`', 1, function() {
var actual = func([0], function(num, index, array) {
array[index] = 1;
return isFilter;
});
deepEqual(actual, [0]);
});
test('`_.' + methodName + '` should work with a "_.property" style `predicate`', 1, function() {
deepEqual(func(objects, 'a'), [objects[isFilter ? 1 : 0]]);
});
test('`_.' + methodName + '` should work with a "_.matches" style `predicate`', 1, function() {
deepEqual(func(objects, objects[1]), [objects[isFilter ? 1 : 0]]);
});
test('`_.' + methodName + '` should not modify wrapped values', 2, function() {
if (!isNpm) {
var wrapped = _(array);
var actual = wrapped[methodName](function(num) {
return num < 3;
});
deepEqual(actual.value(), isFilter ? [1, 2] : [3, 4]);
actual = wrapped[methodName](function(num) {
return num > 2;
});
deepEqual(actual.value(), isFilter ? [3, 4] : [1, 2]);
}
else {
skipTest(2);
}
});
test('`_.' + methodName + '` should work in a lazy chain sequence', 2, function() {
if (!isNpm) {
var object = { 'a': 1, 'b': 2, 'c': 3, 'd': 4 },
predicate = function(value) { return isFilter ? (value > 6) : (value < 6); };
var expected = [9, 16],
actual = _(array).map(square)[methodName](predicate).value();
deepEqual(actual, expected);
actual = _(object).mapValues(square)[methodName](predicate).value();
deepEqual(actual, expected);
}
else {
skipTest(2);
}
});
test('`_.' + methodName + '` should provide the correct `predicate` arguments in a lazy chain sequence', 5, function() {
if (!isNpm) {
var args,
expected = [1, 0, [1, 4, 9, 16]];
_(array)[methodName](function(value, index, array) {
args || (args = slice.call(arguments));
}).value();
deepEqual(args, [1, 0, array]);
args = null;
_(array).map(square)[methodName](function(value, index, array) {
args || (args = slice.call(arguments));
}).value();
deepEqual(args, expected);
args = null;
_(array).map(square)[methodName](function(value, index) {
args || (args = slice.call(arguments));
}).value();
deepEqual(args, expected);
args = null;
_(array).map(square)[methodName](function(value) {
args || (args = slice.call(arguments));
}).value();
deepEqual(args, [1]);
args = null;
_(array).map(square)[methodName](function() {
args || (args = slice.call(arguments));
}).value();
deepEqual(args, expected);
}
else {
skipTest(5);
}
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.remove');
(function() {
test('should modify the array and return removed elements', 2, function() {
var array = [1, 2, 3];
var actual = _.remove(array, function(num) {
return num < 3;
});
deepEqual(array, [3]);
deepEqual(actual, [1, 2]);
});
test('should provide the correct `predicate` arguments', 1, function() {
var argsList = [],
array = [1, 2, 3],
clone = array.slice();
_.remove(array, function(value, index) {
var args = slice.call(arguments);
args[2] = args[2].slice();
argsList.push(args);
return index % 2 == 0;
});
deepEqual(argsList, [[1, 0, clone], [2, 1, clone], [3, 2, clone]]);
});
test('should support the `thisArg` argument', 1, function() {
var array = [1, 2, 3];
var actual = _.remove(array, function(num, index) {
return this[index] < 3;
}, array);
deepEqual(actual, [1, 2]);
});
test('should work with a "_.matches" style `predicate`', 1, function() {
var objects = [{ 'a': 0, 'b': 1 }, { 'a': 1, 'b': 2 }];
_.remove(objects, { 'a': 1 });
deepEqual(objects, [{ 'a': 0, 'b': 1 }]);
});
test('should work with a "_.matchesProperty" style `predicate`', 1, function() {
var objects = [{ 'a': 0, 'b': 1 }, { 'a': 1, 'b': 2 }];
_.remove(objects, 'a', 1);
deepEqual(objects, [{ 'a': 0, 'b': 1 }]);
});
test('should work with a "_.property" style `predicate`', 1, function() {
var objects = [{ 'a': 0 }, { 'a': 1 }];
_.remove(objects, 'a');
deepEqual(objects, [{ 'a': 0 }]);
});
test('should preserve holes in arrays', 2, function() {
var array = [1, 2, 3, 4];
delete array[1];
delete array[3];
_.remove(array, function(num) { return num === 1; });
ok(!('0' in array));
ok(!('2' in array));
});
test('should treat holes as `undefined`', 1, function() {
var array = [1, 2, 3];
delete array[1];
_.remove(array, function(num) { return num == null; });
deepEqual(array, [1, 3]);
});
test('should not mutate the array until all elements to remove are determined', 1, function() {
var array = [1, 2, 3];
_.remove(array, function(num, index) { return index % 2 == 0; });
deepEqual(array, [2]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.repeat');
(function() {
test('should repeat a string `n` times', 2, function() {
strictEqual(_.repeat('*', 3), '***');
strictEqual(_.repeat('abc', 2), 'abcabc');
});
test('should return an empty string for negative `n` or `n` of `0`', 2, function() {
strictEqual(_.repeat('abc', 0), '');
strictEqual(_.repeat('abc', -2), '');
});
test('should coerce `n` to a number', 3, function() {
strictEqual(_.repeat('abc'), '');
strictEqual(_.repeat('abc', '2'), 'abcabc');
strictEqual(_.repeat('*', { 'valueOf': _.constant(3) }), '***');
});
test('should coerce `string` to a string', 2, function() {
strictEqual(_.repeat(Object('abc'), 2), 'abcabc');
strictEqual(_.repeat({ 'toString': _.constant('*') }, 3), '***');
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.result');
(function() {
var object = {
'a': 1,
'b': function() { return this.a; }
};
test('should invoke function values', 1, function() {
strictEqual(_.result(object, 'b'), 1);
});
test('should invoke default function values', 1, function() {
var actual = _.result(object, 'c', object.b);
strictEqual(actual, 1);
});
test('should invoke deep property methods with the correct `this` binding', 2, function() {
var value = { 'a': object };
_.each(['a.b', ['a', 'b']], function(path) {
strictEqual(_.result(value, path), 1);
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.get and lodash.result');
_.each(['get', 'result'], function(methodName) {
var func = _[methodName];
test('`_.' + methodName + '` should get property values', 2, function() {
var object = { 'a': 1 };
_.each(['a', ['a']], function(path) {
strictEqual(func(object, path), 1);
});
});
test('`_.' + methodName + '` should get deep property values', 2, function() {
var object = { 'a': { 'b': { 'c': 3 } } };
_.each(['a.b.c', ['a', 'b', 'c']], function(path) {
strictEqual(func(object, path), 3);
});
});
test('`_.' + methodName + '` should get characters of string indexes (test in IE < 9)', 8, function() {
_.each([1, [1]], function(path) {
_.each(['xo', Object('xo')], function(string) {
strictEqual(func(string, path), 'o');
});
});
_.each([{ 'a': 'xo' }, { 'a': Object('xo') }], function(object) {
_.each(['a[1]', ['a', '1']], function(path) {
strictEqual(func(object, path), 'o');
});
});
});
test('`_.' + methodName + '` should get a key over a path', 2, function() {
var object = { 'a.b.c': 3, 'a': { 'b': { 'c': 4 } } };
_.each(['a.b.c', ['a.b.c']], function(path) {
strictEqual(func(object, path), 3);
});
});
test('`_.' + methodName + '` should coerce array paths to strings', 1, function() {
var object = { 'a,b,c': 3, 'a': { 'b': { 'c': 4 } } };
strictEqual(func(object, ['a', 'b', 'c']), 3);
});
test('`_.' + methodName + '` should ignore empty brackets', 1, function() {
var object = { 'a': 1 };
strictEqual(func(object, 'a[]'), 1);
});
test('`_.' + methodName + '` should handle empty paths', 4, function() {
_.each([['', ''], [[], ['']]], function(pair) {
strictEqual(func({}, pair[0]), undefined);
strictEqual(func({ '': 3 }, pair[1]), 3);
});
});
test('`_.' + methodName + '` should handle complex paths', 2, function() {
var object = { 'a': { '-1.23': { '["b"]': { 'c': { "['d']": { 'e': { 'f': 6 } } } } } } };
var paths = [
'a[-1.23]["[\\"b\\"]"].c[\'[\\\'d\\\']\'][e].f',
['a', '-1.23', '["b"]', 'c', "['d']", 'e', 'f']
];
_.each(paths, function(path) {
strictEqual(func(object, path), 6);
});
});
test('`_.' + methodName + '` should return `undefined` when `object` is nullish', 4, function() {
_.each(['constructor', ['constructor']], function(path) {
strictEqual(func(null, path), undefined);
strictEqual(func(undefined, path), undefined);
});
});
test('`_.' + methodName + '` should return `undefined` with deep paths when `object` is nullish', 2, function() {
var values = [null, undefined],
expected = _.map(values, _.constant(undefined)),
paths = ['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']];
_.each(paths, function(path) {
var actual = _.map(values, function(value) {
return func(value, path);
});
deepEqual(actual, expected);
});
});
test('`_.' + methodName + '` should return `undefined` if parts of `path` are missing', 2, function() {
var object = { 'a': [, null] };
_.each(['a[1].b.c', ['a', '1', 'b', 'c']], function(path) {
strictEqual(func(object, path), undefined);
});
});
test('`_.' + methodName + '` should follow `path` over non-plain objects', 4, function() {
var object = { 'a': '' },
paths = ['constructor.prototype.a', ['constructor', 'prototype', 'a']];
_.each(paths, function(path) {
numberProto.a = 1;
var actual = func(0, path);
strictEqual(actual, 1);
delete numberProto.a;
});
_.each(['a.a.a', ['a', 'a', 'a']], function(path) {
stringProto.a = '_';
var actual = func(object, path);
strictEqual(actual, '_');
delete stringProto.a;
});
});
test('`_.' + methodName + '` should return the specified default value for `undefined` values', 1, function() {
var object = { 'a': {} },
values = empties.concat(true, new Date, 1, /x/, 'a');
var expected = _.transform(values, function(result, value) {
result.push(value, value, value, value);
});
var actual = _.transform(values, function(result, value) {
_.each(['a.b.c', ['a', 'b', 'c']], function(path) {
result.push(
func(object, path, value),
func(null, path, value)
);
});
});
deepEqual(actual, expected);
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.rest');
(function() {
var array = [1, 2, 3];
test('should accept a falsey `array` argument', 1, function() {
var expected = _.map(falsey, _.constant([]));
var actual = _.map(falsey, function(value, index) {
try {
return index ? _.rest(value) : _.rest();
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should exclude the first element', 1, function() {
deepEqual(_.rest(array), [2, 3]);
});
test('should return an empty when querying empty arrays', 1, function() {
deepEqual(_.rest([]), []);
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
actual = _.map(array, _.rest);
deepEqual(actual, [[2, 3], [5, 6], [8, 9]]);
});
test('should work in a lazy chain sequence', 4, function() {
if (!isNpm) {
var array = [1, 2, 3],
values = [];
var actual = _(array).rest().filter(function(value) {
values.push(value);
return false;
})
.value();
deepEqual(actual, []);
deepEqual(values, [2, 3]);
values = [];
actual = _(array).filter(function(value) {
values.push(value);
return value > 1;
})
.rest()
.value();
deepEqual(actual, [3]);
deepEqual(values, array);
}
else {
skipTest(4);
}
});
test('should not execute subsequent iteratees on an empty array in a lazy chain sequence', 4, function() {
if (!isNpm) {
var array = [1],
iteratee = function() { pass = false },
pass = true,
actual = _(array).rest().map(iteratee).value();
ok(pass);
deepEqual(actual, []);
pass = true;
actual = _(array).filter(_.identity).rest().map(iteratee).value();
ok(pass);
deepEqual(actual, []);
}
else {
skipTest(4);
}
});
test('should be aliased', 1, function() {
strictEqual(_.tail, _.rest);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.restParam');
(function() {
function fn(a, b, c) {
return slice.call(arguments);
}
test('should apply a rest parameter to `func`', 1, function() {
var rp = _.restParam(fn);
deepEqual(rp(1, 2, 3, 4), [1, 2, [3, 4]]);
});
test('should work with `start`', 1, function() {
var rp = _.restParam(fn, 1);
deepEqual(rp(1, 2, 3, 4), [1, [2, 3, 4]]);
});
test('should treat `start` as `0` for negative or `NaN` values', 1, function() {
var values = [-1, NaN, 'x'],
expected = _.map(values, _.constant([[1, 2, 3, 4]]));
var actual = _.map(values, function(value) {
var rp = _.restParam(fn, value);
return rp(1, 2, 3, 4);
});
deepEqual(actual, expected);
});
test('should use an empty array when `start` is not reached', 1, function() {
var rp = _.restParam(fn);
deepEqual(rp(1), [1, undefined, []]);
});
test('should work on functions with more than 3 params', 1, function() {
var rp = _.restParam(function(a, b, c, d) {
return slice.call(arguments);
});
deepEqual(rp(1, 2, 3, 4, 5), [1, 2, 3, [4, 5]]);
});
test('should not set a `this` binding', 1, function() {
var rp = _.restParam(function(x, y) {
return this[x] + this[y[0]];
});
var object = { 'rp': rp, 'x': 4, 'y': 2 };
strictEqual(object.rp('x', 'y'), 6);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.runInContext');
(function() {
test('should not require a fully populated `context` object', 1, function() {
if (!isModularize) {
var lodash = _.runInContext({
'setTimeout': function(callback) {
callback();
}
});
var pass = false;
lodash.delay(function() { pass = true; }, 32);
ok(pass);
}
else {
skipTest();
}
});
test('should use a zeroed `_.uniqueId` counter', 3, function() {
if (!isModularize) {
_.times(2, _.uniqueId);
var oldId = Number(_.uniqueId()),
lodash = _.runInContext();
ok(_.uniqueId() > oldId);
var id = lodash.uniqueId();
strictEqual(id, '1');
ok(id < oldId);
}
else {
skipTest(3);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.sample');
(function() {
var array = [1, 2, 3];
test('should return a random element', 1, function() {
var actual = _.sample(array);
ok(_.includes(array, actual));
});
test('should return two random elements', 1, function() {
var actual = _.sample(array, 2);
ok(actual.length == 2 && actual[0] !== actual[1] && _.includes(array, actual[0]) && _.includes(array, actual[1]));
});
test('should contain elements of the collection', 1, function() {
var actual = _.sample(array, array.length);
deepEqual(actual.sort(), array);
});
test('should treat falsey `n` values, except nullish, as `0`', 1, function() {
var expected = _.map(falsey, function(value) {
return value == null ? 1 : [];
});
var actual = _.map(falsey, function(n) {
return _.sample([1], n);
});
deepEqual(actual, expected);
});
test('should return an empty array when `n` < `1` or `NaN`', 3, function() {
_.each([0, -1, -Infinity], function(n) {
deepEqual(_.sample(array, n), []);
});
});
test('should return all elements when `n` >= `array.length`', 4, function() {
_.each([3, 4, Math.pow(2, 32), Infinity], function(n) {
deepEqual(_.sample(array, n).sort(), array);
});
});
test('should return `undefined` when sampling an empty array', 1, function() {
strictEqual(_.sample([]), undefined);
});
test('should return an empty array for empty collections', 1, function() {
var expected = _.transform(empties, function(result) {
result.push(undefined, []);
});
var actual = [];
_.each(empties, function(value) {
try {
actual.push(_.sample(value), _.sample(value, 1));
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should sample an object', 2, function() {
var object = { 'a': 1, 'b': 2, 'c': 3 },
actual = _.sample(object);
ok(_.includes(array, actual));
actual = _.sample(object, 2);
ok(actual.length == 2 && actual[0] !== actual[1] && _.includes(array, actual[0]) && _.includes(array, actual[1]));
});
test('should work as an iteratee for methods like `_.map`', 2, function() {
var array1 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
array2 = ['abc', 'def', 'ghi'];
_.each([array1, array2], function(values) {
var a = values[0],
b = values[1],
c = values[2],
actual = _.map(values, _.sample);
ok(_.includes(a, actual[0]) && _.includes(b, actual[1]) && _.includes(c, actual[2]));
});
});
test('should return a wrapped value when chaining and `n` is provided', 2, function() {
if (!isNpm) {
var wrapped = _(array).sample(2),
actual = wrapped.value();
ok(wrapped instanceof _);
ok(actual.length == 2 && actual[0] !== actual[1] && _.includes(array, actual[0]) && _.includes(array, actual[1]));
}
else {
skipTest(2);
}
});
test('should return an unwrapped value when chaining and `n` is not provided', 1, function() {
if (!isNpm) {
var actual = _(array).sample();
ok(_.includes(array, actual));
}
else {
skipTest();
}
});
test('should return a wrapped value when explicitly chaining', 1, function() {
if (!isNpm) {
ok(_(array).chain().sample() instanceof _);
}
else {
skipTest();
}
});
test('should use a stored reference to `_.sample` when chaining', 2, function() {
if (!isNpm) {
var sample = _.sample;
_.sample = _.noop;
var wrapped = _(array);
notStrictEqual(wrapped.sample(), undefined);
notStrictEqual(wrapped.sample(2).value(), undefined);
_.sample = sample;
}
else {
skipTest(2);
}
});
_.each({
'literal': 'abc',
'object': Object('abc')
},
function(collection, key) {
test('should work with a string ' + key + ' for `collection`', 2, function() {
var actual = _.sample(collection);
ok(_.includes(collection, actual));
actual = _.sample(collection, 2);
ok(actual.length == 2 && actual[0] !== actual[1] && _.includes(collection, actual[0]) && _.includes(collection, actual[1]));
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.set');
(function() {
test('should set property values', 4, function() {
var object = { 'a': 1 };
_.each(['a', ['a']], function(path) {
var actual = _.set(object, path, 2);
strictEqual(actual, object);
strictEqual(object.a, 2);
object.a = 1;
});
});
test('should set deep property values', 4, function() {
var object = { 'a': { 'b': { 'c': 3 } } };
_.each(['a.b.c', ['a', 'b', 'c']], function(path) {
var actual = _.set(object, path, 4);
strictEqual(actual, object);
strictEqual(object.a.b.c, 4);
object.a.b.c = 3;
});
});
test('should set a key over a path', 4, function() {
var object = { 'a.b.c': 3 };
_.each(['a.b.c', ['a.b.c']], function(path) {
var actual = _.set(object, path, 4);
strictEqual(actual, object);
deepEqual(object, { 'a.b.c': 4 });
object['a.b.c'] = 3;
});
});
test('should coerce array paths to strings', 1, function() {
var object = { 'a,b,c': 3, 'a': { 'b': { 'c': 3 } } };
_.set(object, ['a', 'b', 'c'], 4);
deepEqual(object, { 'a,b,c': 4, 'a': { 'b': { 'c': 3 } } });
});
test('should ignore empty brackets', 1, function() {
var object = {};
_.set(object, 'a[]', 1);
deepEqual(object, { 'a': 1 });
});
test('should handle empty paths', 4, function() {
_.each([['', ''], [[], ['']]], function(pair, index) {
var object = {};
_.set(object, pair[0], 1);
deepEqual(object, index ? {} : { '': 1 });
_.set(object, pair[1], 2);
deepEqual(object, { '': 2 });
});
});
test('should handle complex paths', 2, function() {
var object = { 'a': { '1.23': { '["b"]': { 'c': { "['d']": { 'e': { 'f': 6 } } } } } } };
var paths = [
'a[-1.23]["[\\"b\\"]"].c[\'[\\\'d\\\']\'][e].f',
['a', '-1.23', '["b"]', 'c', "['d']", 'e', 'f']
];
_.each(paths, function(path) {
_.set(object, path, 7);
strictEqual(object.a[-1.23]['["b"]'].c["['d']"].e.f, 7);
object.a[-1.23]['["b"]'].c["['d']"].e.f = 6;
});
});
test('should create parts of `path` that are missing', 6, function() {
var object = {};
_.each(['a[1].b.c', ['a', '1', 'b', 'c']], function(path) {
var actual = _.set(object, path, 4);
strictEqual(actual, object);
deepEqual(actual, { 'a': [undefined, { 'b': { 'c': 4 } }] });
ok(!(0 in object.a));
delete object.a;
});
});
test('should not error when `object` is nullish', 1, function() {
var values = [null, undefined],
expected = [[null, null], [undefined, undefined]];
var actual = _.map(values, function(value) {
try {
return [_.set(value, 'a.b', 1), _.set(value, ['a', 'b'], 1)];
} catch(e) {
return e.message;
}
});
deepEqual(actual, expected);
});
test('should follow `path` over non-plain objects', 4, function() {
var object = { 'a': '' },
paths = ['constructor.prototype.a', ['constructor', 'prototype', 'a']];
_.each(paths, function(path) {
_.set(0, path, 1);
strictEqual(0..a, 1);
delete numberProto.a;
});
_.each(['a.replace.b', ['a', 'replace', 'b']], function(path) {
_.set(object, path, 1);
strictEqual(stringProto.replace.b, 1);
delete stringProto.replace.b;
});
});
test('should not error on paths over primitive values in strict mode', 2, function() {
numberProto.a = 0;
_.each(['a', 'a.a.a'], function(path) {
try {
_.set(0, path, 1);
strictEqual(0..a, 0);
} catch(e) {
ok(false, e.message);
}
numberProto.a = 0;
});
delete numberProto.a;
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.shuffle');
(function() {
var array = [1, 2, 3],
object = { 'a': 1, 'b': 2, 'c': 3 };
test('should return a new array', 1, function() {
notStrictEqual(_.shuffle(array), array);
});
test('should contain the same elements after a collection is shuffled', 2, function() {
deepEqual(_.shuffle(array).sort(), array);
deepEqual(_.shuffle(object).sort(), array);
});
test('should shuffle small collections', 1, function() {
var actual = _.times(1000, function() {
return _.shuffle([1, 2]);
});
deepEqual(_.sortBy(_.uniq(actual, String), '0'), [[1, 2], [2, 1]]);
});
test('should treat number values for `collection` as empty', 1, function() {
deepEqual(_.shuffle(1), []);
});
_.each({
'literal': 'abc',
'object': Object('abc')
},
function(collection, key) {
test('should work with a string ' + key + ' for `collection`', 1, function() {
var actual = _.shuffle(collection);
deepEqual(actual.sort(), ['a','b', 'c']);
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.size');
(function() {
var args = arguments,
array = [1, 2, 3];
test('should return the number of own enumerable properties of an object', 1, function() {
strictEqual(_.size({ 'one': 1, 'two': 2, 'three': 3 }), 3);
});
test('should return the length of an array', 1, function() {
strictEqual(_.size(array), 3);
});
test('should accept a falsey `object` argument', 1, function() {
var expected = _.map(falsey, _.constant(0));
var actual = _.map(falsey, function(value, index) {
try {
return index ? _.size(value) : _.size();
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should work with `arguments` objects (test in IE < 9)', 1, function() {
strictEqual(_.size(args), 3);
});
test('should work with jQuery/MooTools DOM query collections', 1, function() {
function Foo(elements) { push.apply(this, elements); }
Foo.prototype = { 'length': 0, 'splice': arrayProto.splice };
strictEqual(_.size(new Foo(array)), 3);
});
test('should not treat objects with negative lengths as array-like', 1, function() {
strictEqual(_.size({ 'length': -1 }), 1);
});
test('should not treat objects with lengths larger than `MAX_SAFE_INTEGER` as array-like', 1, function() {
strictEqual(_.size({ 'length': MAX_SAFE_INTEGER + 1 }), 1);
});
test('should not treat objects with non-number lengths as array-like', 1, function() {
strictEqual(_.size({ 'length': '0' }), 1);
});
test('fixes the JScript `[[DontEnum]]` bug (test in IE < 9)', 1, function() {
strictEqual(_.size(shadowObject), 7);
});
_.each({
'literal': 'abc',
'object': Object('abc')
},
function(collection, key) {
test('should work with a string ' + key + ' for `collection`', 1, function() {
deepEqual(_.size(collection), 3);
});
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.slice');
(function() {
var array = [1, 2, 3];
test('should use a default `start` of `0` and a default `end` of `array.length`', 2, function() {
var actual = _.slice(array);
deepEqual(actual, array);
notStrictEqual(actual, array);
});
test('should work with a positive `start`', 2, function() {
deepEqual(_.slice(array, 1), [2, 3]);
deepEqual(_.slice(array, 1, 3), [2, 3]);
});
test('should work with a `start` >= `array.length`', 4, function() {
_.each([3, 4, Math.pow(2, 32), Infinity], function(start) {
deepEqual(_.slice(array, start), []);
});
});
test('should treat falsey `start` values as `0`', 1, function() {
var expected = _.map(falsey, _.constant(array));
var actual = _.map(falsey, function(start) {
return _.slice(array, start);
});
deepEqual(actual, expected);
});
test('should work with a negative `start`', 1, function() {
deepEqual(_.slice(array, -1), [3]);
});
test('should work with a negative `start` <= negative `array.length`', 3, function() {
_.each([-3, -4, -Infinity], function(start) {
deepEqual(_.slice(array, start), array);
});
});
test('should work with `start` >= `end`', 2, function() {
_.each([2, 3], function(start) {
deepEqual(_.slice(array, start, 2), []);
});
});
test('should work with a positive `end`', 1, function() {
deepEqual(_.slice(array, 0, 1), [1]);
});
test('should work with a `end` >= `array.length`', 4, function() {
_.each([3, 4, Math.pow(2, 32), Infinity], function(end) {
deepEqual(_.slice(array, 0, end), array);
});
});
test('should treat falsey `end` values, except `undefined`, as `0`', 1, function() {
var expected = _.map(falsey, function(value) {
return value === undefined ? array : [];
});
var actual = _.map(falsey, function(end) {
return _.slice(array, 0, end);
});
deepEqual(actual, expected);
});
test('should work with a negative `end`', 1, function() {
deepEqual(_.slice(array, 0, -1), [1, 2]);
});
test('should work with a negative `end` <= negative `array.length`', 3, function() {
_.each([-3, -4, -Infinity], function(end) {
deepEqual(_.slice(array, 0, end), []);
});
});
test('should coerce `start` and `end` to integers', 1, function() {
var positions = [[0.1, 1.1], ['0', 1], [0, '1'], ['1'], [NaN, 1], [1, NaN]];
var actual = _.map(positions, function(pos) {
return _.slice.apply(_, [array].concat(pos));
});
deepEqual(actual, [[1], [1], [1], [2, 3], [1], []]);
});
test('should work as an iteratee for methods like `_.map`', 2, function() {
var array = [[1], [2, 3]],
actual = _.map(array, _.slice);
deepEqual(actual, array);
notStrictEqual(actual, array);
});
test('should work in a lazy chain sequence', 38, function() {
if (!isNpm) {
var wrapped = _(array);
_.each(['map', 'filter'], function(methodName) {
deepEqual(wrapped[methodName]().slice(0, -1).value(), [1, 2]);
deepEqual(wrapped[methodName]().slice(1).value(), [2, 3]);
deepEqual(wrapped[methodName]().slice(1, 3).value(), [2, 3]);
deepEqual(wrapped[methodName]().slice(-1).value(), [3]);
deepEqual(wrapped[methodName]().slice(4).value(), []);
deepEqual(wrapped[methodName]().slice(3, 2).value(), []);
deepEqual(wrapped[methodName]().slice(0, -4).value(), []);
deepEqual(wrapped[methodName]().slice(0, null).value(), []);
deepEqual(wrapped[methodName]().slice(0, 4).value(), array);
deepEqual(wrapped[methodName]().slice(-4).value(), array);
deepEqual(wrapped[methodName]().slice(null).value(), array);
deepEqual(wrapped[methodName]().slice(0, 1).value(), [1]);
deepEqual(wrapped[methodName]().slice(NaN, '1').value(), [1]);
deepEqual(wrapped[methodName]().slice(0.1, 1.1).value(), [1]);
deepEqual(wrapped[methodName]().slice('0', 1).value(), [1]);
deepEqual(wrapped[methodName]().slice(0, '1').value(), [1]);
deepEqual(wrapped[methodName]().slice('1').value(), [2, 3]);
deepEqual(wrapped[methodName]().slice(NaN, 1).value(), [1]);
deepEqual(wrapped[methodName]().slice(1, NaN).value(), []);
});
}
else {
skipTest(38);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.some');
(function() {
test('should return `false` for empty collections', 1, function() {
var expected = _.map(empties, _.constant(false));
var actual = _.map(empties, function(value) {
try {
return _.some(value, _.identity);
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should return `true` if `predicate` returns truthy for any element in the collection', 2, function() {
strictEqual(_.some([false, 1, ''], _.identity), true);
strictEqual(_.some([null, 'x', 0], _.identity), true);
});
test('should return `false` if `predicate` returns falsey for all elements in the collection', 2, function() {
strictEqual(_.some([false, false, false], _.identity), false);
strictEqual(_.some([null, 0, ''], _.identity), false);
});
test('should return `true` as soon as `predicate` returns truthy', 1, function() {
strictEqual(_.some([null, true, null], _.identity), true);
});
test('should work with a "_.property" style `predicate`', 2, function() {
var objects = [{ 'a': 0, 'b': 0 }, { 'a': 0, 'b': 1 }];
strictEqual(_.some(objects, 'a'), false);
strictEqual(_.some(objects, 'b'), true);
});
test('should work with a "_.matches" style `predicate`', 2, function() {
var objects = [{ 'a': 0, 'b': 0 }, { 'a': 1, 'b': 1}];
strictEqual(_.some(objects, { 'a': 0 }), true);
strictEqual(_.some(objects, { 'b': 2 }), false);
});
test('should use `_.identity` when `predicate` is nullish', 2, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant(false));
var actual = _.map(values, function(value, index) {
var array = [0, 0];
return index ? _.some(array, value) : _.some(array);
});
deepEqual(actual, expected);
expected = _.map(values, _.constant(true));
actual = _.map(values, function(value, index) {
var array = [0, 1];
return index ? _.some(array, value) : _.some(array);
});
deepEqual(actual, expected);
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var actual = _.map([[1]], _.some);
deepEqual(actual, [true]);
});
test('should be aliased', 1, function() {
strictEqual(_.any, _.some);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.sortBy');
(function() {
function Pair(a, b, c) {
this.a = a;
this.b = b;
this.c = c;
}
var objects = [
{ 'a': 'x', 'b': 3 },
{ 'a': 'y', 'b': 4 },
{ 'a': 'x', 'b': 1 },
{ 'a': 'y', 'b': 2 }
];
var stableArray = [
new Pair(1, 1, 1), new Pair(1, 2, 1),
new Pair(1, 1, 1), new Pair(1, 2, 1),
new Pair(1, 3, 1), new Pair(1, 4, 1),
new Pair(1, 5, 1), new Pair(1, 6, 1),
new Pair(2, 1, 2), new Pair(2, 2, 2),
new Pair(2, 3, 2), new Pair(2, 4, 2),
new Pair(2, 5, 2), new Pair(2, 6, 2),
new Pair(undefined, 1, 1), new Pair(undefined, 2, 1),
new Pair(undefined, 3, 1), new Pair(undefined, 4, 1),
new Pair(undefined, 5, 1), new Pair(undefined, 6, 1)
];
var stableObject = _.zipObject('abcdefghijklmnopqrst'.split(''), stableArray);
test('should sort in ascending order', 1, function() {
var actual = _.pluck(_.sortBy(objects, function(object) {
return object.b;
}), 'b');
deepEqual(actual, [1, 2, 3, 4]);
});
test('should perform a stable sort (test in V8)', 2, function() {
_.each([stableArray, stableObject], function(value, index) {
var actual = _.sortBy(value, function(pair) {
return pair.a;
});
deepEqual(actual, stableArray, index ? 'object' : 'array');
});
});
test('should provide the correct `iteratee` arguments', 1, function() {
var args;
_.sortBy(objects, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [objects[0], 0, objects]);
});
test('should support the `thisArg` argument', 1, function() {
var actual = _.sortBy([1, 2, 3], function(num) {
return this.sin(num);
}, Math);
deepEqual(actual, [3, 1, 2]);
});
test('should use `_.identity` when `iteratee` is nullish', 1, function() {
var array = [3, 2, 1],
values = [, null, undefined],
expected = _.map(values, _.constant([1, 2, 3]));
var actual = _.map(values, function(value, index) {
return index ? _.sortBy(array, value) : _.sortBy(array);
});
deepEqual(actual, expected);
});
test('should work with a "_.property" style `iteratee`', 1, function() {
var actual = _.pluck(_.sortBy(objects.concat(undefined), 'b'), 'b');
deepEqual(actual, [1, 2, 3, 4, undefined]);
});
test('should work with an object for `collection`', 1, function() {
var actual = _.sortBy({ 'a': 1, 'b': 2, 'c': 3 }, function(num) {
return Math.sin(num);
});
deepEqual(actual, [3, 1, 2]);
});
test('should move `null`, `undefined`, and `NaN` values to the end', 2, function() {
var array = [NaN, undefined, null, 4, null, 1, undefined, 3, NaN, 2];
deepEqual(_.sortBy(array), [1, 2, 3, 4, null, null, undefined, undefined, NaN, NaN]);
array = [NaN, undefined, null, 'd', null, 'a', undefined, 'c', NaN, 'b'];
deepEqual(_.sortBy(array), ['a', 'b', 'c', 'd', null, null, undefined, undefined, NaN, NaN]);
});
test('should treat number values for `collection` as empty', 1, function() {
deepEqual(_.sortBy(1), []);
});
test('should coerce arrays returned from `iteratee`', 1, function() {
var actual = _.sortBy(objects, function(object) {
var result = [object.a, object.b];
result.toString = function() { return String(this[0]); };
return result;
});
deepEqual(actual, [objects[0], objects[2], objects[1], objects[3]]);
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var actual = _.map([[2, 1, 3], [3, 2, 1]], _.sortBy);
deepEqual(actual, [[1, 2, 3], [1, 2, 3]]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.sortByOrder');
(function() {
var objects = [
{ 'a': 'x', 'b': 3 },
{ 'a': 'y', 'b': 4 },
{ 'a': 'x', 'b': 1 },
{ 'a': 'y', 'b': 2 }
];
test('should sort multiple properties by specified orders', 1, function() {
var actual = _.sortByOrder(objects, ['a', 'b'], [false, true]);
deepEqual(actual, [objects[3], objects[1], objects[2], objects[0]]);
});
test('should sort a property in ascending order when its order is not specified', 1, function() {
var actual = _.sortByOrder(objects, ['a', 'b'], [false]);
deepEqual(actual, [objects[3], objects[1], objects[2], objects[0]]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('sortBy methods');
_.each(['sortByAll', 'sortByOrder'], function(methodName) {
var func = _[methodName];
function Pair(a, b, c) {
this.a = a;
this.b = b;
this.c = c;
}
var objects = [
{ 'a': 'x', 'b': 3 },
{ 'a': 'y', 'b': 4 },
{ 'a': 'x', 'b': 1 },
{ 'a': 'y', 'b': 2 }
];
var stableArray = [
new Pair(1, 1, 1), new Pair(1, 2, 1),
new Pair(1, 1, 1), new Pair(1, 2, 1),
new Pair(1, 3, 1), new Pair(1, 4, 1),
new Pair(1, 5, 1), new Pair(1, 6, 1),
new Pair(2, 1, 2), new Pair(2, 2, 2),
new Pair(2, 3, 2), new Pair(2, 4, 2),
new Pair(2, 5, 2), new Pair(2, 6, 2),
new Pair(undefined, 1, 1), new Pair(undefined, 2, 1),
new Pair(undefined, 3, 1), new Pair(undefined, 4, 1),
new Pair(undefined, 5, 1), new Pair(undefined, 6, 1)
];
var stableObject = _.zipObject('abcdefghijklmnopqrst'.split(''), stableArray);
test('`_.' + methodName + '` should sort mutliple properties in ascending order', 1, function() {
var actual = func(objects, ['a', 'b']);
deepEqual(actual, [objects[2], objects[0], objects[3], objects[1]]);
});
test('`_.' + methodName + '` should support iteratees', 1, function() {
var actual = func(objects, ['a', function(object) { return object.b; }]);
deepEqual(actual, [objects[2], objects[0], objects[3], objects[1]]);
});
test('`_.' + methodName + '` should perform a stable sort (test in IE > 8, Opera, and V8)', 2, function() {
_.each([stableArray, stableObject], function(value, index) {
var actual = func(value, ['a', 'c']);
deepEqual(actual, stableArray, index ? 'object' : 'array');
});
});
test('`_.' + methodName + '` should not error on nullish elements', 1, function() {
try {
var actual = func(objects.concat(null, undefined), ['a', 'b']);
} catch(e) {}
deepEqual(actual, [objects[2], objects[0], objects[3], objects[1], null, undefined]);
});
test('`_.' + methodName + '` should work as an iteratee for methods like `_.reduce`', 3, function() {
var objects = [
{ 'a': 'x', '0': 3 },
{ 'a': 'y', '0': 4 },
{ 'a': 'x', '0': 1 },
{ 'a': 'y', '0': 2 }
];
var funcs = [func, _.partialRight(func, 'bogus')];
_.each(['a', 0, [0]], function(props, index) {
var expected = _.map(funcs, _.constant(index
? [objects[2], objects[3], objects[0], objects[1]]
: [objects[0], objects[2], objects[1], objects[3]]
));
var actual = _.map(funcs, function(func) {
return _.reduce([props], func, objects);
});
deepEqual(actual, expected);
});
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('sortedIndex methods');
_.each(['sortedIndex', 'sortedLastIndex'], function(methodName) {
var array = [30, 50],
func = _[methodName],
isSortedIndex = methodName == 'sortedIndex',
objects = [{ 'x': 30 }, { 'x': 50 }];
test('`_.' + methodName + '` should return the insert index', 1, function() {
var values = [30, 40, 50],
expected = isSortedIndex ? [0, 1, 1] : [1, 1, 2];
var actual = _.map(values, function(value) {
return func(array, value);
});
deepEqual(actual, expected);
});
test('`_.' + methodName + '` should work with an array of strings', 1, function() {
var array = ['a', 'c'],
values = ['a', 'b', 'c'],
expected = isSortedIndex ? [0, 1, 1] : [1, 1, 2];
var actual = _.map(values, function(value) {
return func(array, value);
});
deepEqual(actual, expected);
});
test('`_.' + methodName + '` should accept a falsey `array` argument and a `value`', 1, function() {
var expected = _.map(falsey, _.constant([0, 0, 0]));
var actual = _.map(falsey, function(array) {
return [func(array, 1), func(array, undefined), func(array, NaN)];
});
deepEqual(actual, expected);
});
test('`_.' + methodName + '` should provide the correct `iteratee` arguments', 1, function() {
var args;
func(array, 40, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [40]);
});
test('`_.' + methodName + '` should support the `thisArg` argument', 1, function() {
var actual = func(array, 40, function(num) {
return this[num];
}, { '30': 30, '40': 40, '50': 50 });
strictEqual(actual, 1);
});
test('`_.' + methodName + '` should work with a "_.property" style `iteratee`', 1, function() {
var actual = func(objects, { 'x': 40 }, 'x');
strictEqual(actual, 1);
});
test('`_.' + methodName + '` should align with `_.sortBy`', 8, function() {
var expected = [1, '2', {}, null, undefined, NaN, NaN];
_.each([
[NaN, null, 1, '2', {}, NaN, undefined],
['2', null, 1, NaN, {}, NaN, undefined]
], function(array) {
deepEqual(_.sortBy(array), expected);
strictEqual(func(expected, 3), 2);
strictEqual(func(expected, undefined), isSortedIndex ? 4 : 5);
strictEqual(func(expected, NaN), isSortedIndex ? 5 : 7);
});
});
test('`_.' + methodName + '` should support arrays larger than `MAX_ARRAY_LENGTH / 2`', 12, function() {
_.each([Math.ceil(MAX_ARRAY_LENGTH / 2), MAX_ARRAY_LENGTH], function(length) {
var array = [],
values = [MAX_ARRAY_LENGTH, NaN, undefined];
array.length = length;
_.each(values, function(value) {
var steps = 0,
actual = func(array, value, function(value) { steps++; return value; });
var expected = (isSortedIndex ? !_.isNaN(value) : _.isFinite(value))
? 0
: Math.min(length, MAX_ARRAY_INDEX);
// Avoid false fails in older Firefox.
if (array.length == length) {
ok(steps == 32 || steps == 33);
strictEqual(actual, expected);
}
else {
skipTest(2);
}
});
});
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.spread');
(function() {
test('should spread arguments to `func`', 1, function() {
var spread = _.spread(add);
strictEqual(spread([4, 2]), 6);
});
test('should throw a TypeError when receiving a non-array `array` argument', 1, function() {
raises(function() { _.spread(4, 2); }, TypeError);
});
test('should provide the correct `func` arguments', 1, function() {
var args;
var spread = _.spread(function() {
args = slice.call(arguments);
});
spread([4, 2], 'ignored');
deepEqual(args, [4, 2]);
});
test('should not set a `this` binding', 1, function() {
var spread = _.spread(function(x, y) {
return this[x] + this[y];
});
var object = { 'spread': spread, 'x': 4, 'y': 2 };
strictEqual(object.spread(['x', 'y']), 6);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.startsWith');
(function() {
var string = 'abc';
test('should return `true` if a string starts with `target`', 1, function() {
strictEqual(_.startsWith(string, 'a'), true);
});
test('should return `false` if a string does not start with `target`', 1, function() {
strictEqual(_.startsWith(string, 'b'), false);
});
test('should work with a `position` argument', 1, function() {
strictEqual(_.startsWith(string, 'b', 1), true);
});
test('should work with `position` >= `string.length`', 4, function() {
_.each([3, 5, MAX_SAFE_INTEGER, Infinity], function(position) {
strictEqual(_.startsWith(string, 'a', position), false);
});
});
test('should treat falsey `position` values as `0`', 1, function() {
var expected = _.map(falsey, _.constant(true));
var actual = _.map(falsey, function(position) {
return _.startsWith(string, 'a', position);
});
deepEqual(actual, expected);
});
test('should treat a negative `position` as `0`', 6, function() {
_.each([-1, -3, -Infinity], function(position) {
strictEqual(_.startsWith(string, 'a', position), true);
strictEqual(_.startsWith(string, 'b', position), false);
});
});
test('should return `true` when `target` is an empty string regardless of `position`', 1, function() {
ok(_.every([-Infinity, NaN, -3, -1, 0, 1, 2, 3, 5, MAX_SAFE_INTEGER, Infinity], function(position) {
return _.startsWith(string, '', position, true);
}));
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.startsWith and lodash.endsWith');
_.each(['startsWith', 'endsWith'], function(methodName) {
var func = _[methodName],
isStartsWith = methodName == 'startsWith';
var string = 'abc',
chr = isStartsWith ? 'a' : 'c';
test('`_.' + methodName + '` should coerce `string` to a string', 2, function() {
strictEqual(func(Object(string), chr), true);
strictEqual(func({ 'toString': _.constant(string) }, chr), true);
});
test('`_.' + methodName + '` should coerce `target` to a string', 2, function() {
strictEqual(func(string, Object(chr)), true);
strictEqual(func(string, { 'toString': _.constant(chr) }), true);
});
test('`_.' + methodName + '` should coerce `position` to a number', 2, function() {
var position = isStartsWith ? 1 : 2;
strictEqual(func(string, 'b', Object(position)), true);
strictEqual(func(string, 'b', { 'toString': _.constant(String(position)) }), true);
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.sum');
(function() {
var array = [6, 4, 2],
object = { 'a': 2, 'b': 3, 'c': 1 },
objects = [{ 'a': 2 }, { 'a': 3 }, { 'a': 1 }];
test('should return the sum of an array of numbers', 1, function() {
strictEqual(_.sum(array), 12);
});
test('should return `0` when passing empty `array` values', 1, function() {
var expected = _.map(empties, _.constant(0));
var actual = _.map(empties, function(value) {
return _.sum(value);
});
deepEqual(actual, expected);
});
test('should coerce values to numbers and `NaN` to `0`', 1, function() {
strictEqual(_.sum(['1', NaN, '2']), 3);
});
test('should iterate an object', 1, function() {
strictEqual(_.sum(object), 6);
});
test('should iterate a string', 2, function() {
_.each(['123', Object('123')], function(value) {
strictEqual(_.sum(value), 6);
});
});
test('should work with an `iteratee` argument', 1, function() {
var actual = _.sum(objects, function(object) {
return object.a;
});
deepEqual(actual, 6);
});
test('should provide the correct `iteratee` arguments', 2, function() {
var args;
_.sum(array, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [6, 0, array]);
args = null;
_.sum(object, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [2, 'a', object]);
});
test('should support the `thisArg` argument', 1, function() {
var actual = _.sum([6.8, 4.5, 2.6], function(num) {
return this.floor(num);
}, Math);
strictEqual(actual, 12);
});
test('should work with a "_.property" style `iteratee`', 2, function() {
var arrays = [[2], [3], [1]];
strictEqual(_.sum(arrays, 0), 6);
strictEqual(_.sum(objects, 'a'), 6);
});
test('should perform basic sum when used as an iteratee for methods like `_.map`', 1, function() {
var actual = _.map([array, object], _.sum);
deepEqual(actual, [12, 6]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.support');
(function() {
test('should contain properties with boolean values', 1, function() {
ok(_.every(_.values(_.support), function(value) {
return value === true || value === false;
}));
});
test('should not contain minified properties (test production builds)', 1, function() {
var props = [
'argsTag',
'argsObject',
'dom',
'enumErrorProps',
'enumPrototypes',
'fastBind',
'funcDecomp',
'funcNames',
'hostObject',
'nodeTag',
'nonEnumArgs',
'nonEnumShadows',
'nonEnumStrings',
'ownLast',
'spliceObjects',
'unindexedChars'
];
ok(_.isEmpty(_.difference(_.keys(_.support), props)));
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.tap');
(function() {
test('should intercept and return the given value', 2, function() {
if (!isNpm) {
var intercepted,
array = [1, 2, 3];
var actual = _.tap(array, function(value) {
intercepted = value;
});
strictEqual(actual, array);
strictEqual(intercepted, array);
}
else {
skipTest(2);
}
});
test('should intercept unwrapped values and return wrapped values when chaining', 2, function() {
if (!isNpm) {
var intercepted,
array = [1, 2, 3];
var wrapped = _(array).tap(function(value) {
intercepted = value;
value.pop();
});
ok(wrapped instanceof _);
wrapped.value();
strictEqual(intercepted, array);
}
else {
skipTest(2);
}
});
test('should support the `thisArg` argument', 1, function() {
if (!isNpm) {
var array = [1, 2];
var wrapped = _(array.slice()).tap(function(value) {
value.push(this[0]);
}, array);
deepEqual(wrapped.value(), [1, 2, 1]);
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.template');
(function() {
test('should escape values in "escape" delimiters', 1, function() {
var strings = ['<p><%- value %></p>', '<p><%-value%></p>', '<p><%-\nvalue\n%></p>'],
expected = _.map(strings, _.constant('<p>&amp;&lt;&gt;&quot;&#39;&#96;\/</p>')),
data = { 'value': '&<>"\'`\/' };
var actual = _.map(strings, function(string) {
return _.template(string)(data);
});
deepEqual(actual, expected);
});
test('should evaluate JavaScript in "evaluate" delimiters', 1, function() {
var compiled = _.template(
'<ul><%\
for (var key in collection) {\
%><li><%= collection[key] %></li><%\
} %></ul>'
);
var data = { 'collection': { 'a': 'A', 'b': 'B' } },
actual = compiled(data);
strictEqual(actual, '<ul><li>A</li><li>B</li></ul>');
});
test('should interpolate data object properties', 1, function() {
var strings = ['<%= a %>BC', '<%=a%>BC', '<%=\na\n%>BC'],
expected = _.map(strings, _.constant('ABC')),
data = { 'a': 'A' };
var actual = _.map(strings, function(string) {
return _.template(string)(data);
});
deepEqual(actual, expected);
});
test('should support escaped values in "interpolation" delimiters', 1, function() {
var compiled = _.template('<%= a ? "a=\\"A\\"" : "" %>'),
data = { 'a': true };
strictEqual(compiled(data), 'a="A"');
});
test('should work with "interpolate" delimiters containing ternary operators', 1, function() {
var compiled = _.template('<%= value ? value : "b" %>'),
data = { 'value': 'a' };
strictEqual(compiled(data), 'a');
});
test('should work with "interpolate" delimiters containing global values', 1, function() {
var compiled = _.template('<%= typeof Math.abs %>');
try {
var actual = compiled();
} catch(e) {}
strictEqual(actual, 'function');
});
test('should work with complex "interpolate" delimiters', 22, function() {
_.each({
'<%= a + b %>': '3',
'<%= b - a %>': '1',
'<%= a = b %>': '2',
'<%= !a %>': 'false',
'<%= ~a %>': '-2',
'<%= a * b %>': '2',
'<%= a / b %>': '0.5',
'<%= a % b %>': '1',
'<%= a >> b %>': '0',
'<%= a << b %>': '4',
'<%= a & b %>': '0',
'<%= a ^ b %>': '3',
'<%= a | b %>': '3',
'<%= {}.toString.call(0) %>': numberTag,
'<%= a.toFixed(2) %>': '1.00',
'<%= obj["a"] %>': '1',
'<%= delete a %>': 'true',
'<%= "a" in obj %>': 'true',
'<%= obj instanceof Object %>': 'true',
'<%= new Boolean %>': 'false',
'<%= typeof a %>': 'number',
'<%= void a %>': ''
},
function(value, key) {
var compiled = _.template(key),
data = { 'a': 1, 'b': 2 };
strictEqual(compiled(data), value, key);
});
});
test('should parse ES6 template delimiters', 2, function() {
var data = { 'value': 2 };
strictEqual(_.template('1${value}3')(data), '123');
strictEqual(_.template('${"{" + value + "\\}"}')(data), '{2}');
});
test('should not reference `_.escape` when "escape" delimiters are not used', 1, function() {
var compiled = _.template('<%= typeof __e %>');
strictEqual(compiled({}), 'undefined');
});
test('should allow referencing variables declared in "evaluate" delimiters from other delimiters', 1, function() {
var compiled = _.template('<% var b = a; %><%= b.value %>'),
data = { 'a': { 'value': 1 } };
strictEqual(compiled(data), '1');
});
test('should support single line comments in "evaluate" delimiters (test production builds)', 1, function() {
var compiled = _.template('<% // A code comment. %><% if (value) { %>yap<% } else { %>nope<% } %>'),
data = { 'value': true };
strictEqual(compiled(data), 'yap');
});
test('should work with custom delimiters', 2, function() {
_.times(2, function(index) {
var settingsClone = _.clone(_.templateSettings);
var settings = _.assign(index ? _.templateSettings : {}, {
'escape': /\{\{-([\s\S]+?)\}\}/g,
'evaluate': /\{\{([\s\S]+?)\}\}/g,
'interpolate': /\{\{=([\s\S]+?)\}\}/g
});
var compiled = _.template('<ul>{{ _.each(collection, function(value, index) {}}<li>{{= index }}: {{- value }}</li>{{}); }}</ul>', index ? null : settings),
expected = '<ul><li>0: a &amp; A</li><li>1: b &amp; B</li></ul>',
data = { 'collection': ['a & A', 'b & B'] };
strictEqual(compiled(data), expected);
_.assign(_.templateSettings, settingsClone);
});
});
test('should work with custom delimiters containing special characters', 2, function() {
_.times(2, function(index) {
var settingsClone = _.clone(_.templateSettings);
var settings = _.assign(index ? _.templateSettings : {}, {
'escape': /<\?-([\s\S]+?)\?>/g,
'evaluate': /<\?([\s\S]+?)\?>/g,
'interpolate': /<\?=([\s\S]+?)\?>/g
});
var compiled = _.template('<ul><? _.each(collection, function(value, index) { ?><li><?= index ?>: <?- value ?></li><? }); ?></ul>', index ? null : settings),
expected = '<ul><li>0: a &amp; A</li><li>1: b &amp; B</li></ul>',
data = { 'collection': ['a & A', 'b & B'] };
strictEqual(compiled(data), expected);
_.assign(_.templateSettings, settingsClone);
});
});
test('should work with strings without delimiters', 1, function() {
var expected = 'abc';
strictEqual(_.template(expected)({}), expected);
});
test('should support the "imports" option', 1, function() {
var compiled = _.template('<%= a %>', { 'imports': { 'a': 1 } });
strictEqual(compiled({}), '1');
});
test('should support the "variable" options', 1, function() {
var compiled = _.template(
'<% _.each( data.a, function( value ) { %>' +
'<%= value.valueOf() %>' +
'<% }) %>', { 'variable': 'data' }
);
var data = { 'a': [1, 2, 3] };
try {
strictEqual(compiled(data), '123');
} catch(e) {
ok(false, e.message);
}
});
test('should support the legacy `options` param signature', 1, function() {
var compiled = _.template('<%= data.a %>', null, { 'variable': 'data' }),
data = { 'a': 1 };
strictEqual(compiled(data), '1');
});
test('should use a `with` statement by default', 1, function() {
var compiled = _.template('<%= index %><%= collection[index] %><% _.each(collection, function(value, index) { %><%= index %><% }); %>'),
actual = compiled({ 'index': 1, 'collection': ['a', 'b', 'c'] });
strictEqual(actual, '1b012');
});
test('should work with `this` references', 2, function() {
var compiled = _.template('a<%= this.String("b") %>c');
strictEqual(compiled(), 'abc');
var object = { 'b': 'B' };
object.compiled = _.template('A<%= this.b %>C', { 'variable': 'obj' });
strictEqual(object.compiled(), 'ABC');
});
test('should work with backslashes', 1, function() {
var compiled = _.template('<%= a %> \\b'),
data = { 'a': 'A' };
strictEqual(compiled(data), 'A \\b');
});
test('should work with escaped characters in string literals', 2, function() {
var compiled = _.template('<% print("\'\\n\\r\\t\\u2028\\u2029\\\\") %>');
strictEqual(compiled(), "'\n\r\t\u2028\u2029\\");
var data = { 'a': 'A' };
compiled = _.template('\'\n\r\t<%= a %>\u2028\u2029\\"');
strictEqual(compiled(data), '\'\n\r\tA\u2028\u2029\\"');
});
test('should handle \\u2028 & \\u2029 characters', 1, function() {
var compiled = _.template('\u2028<%= "\\u2028\\u2029" %>\u2029');
strictEqual(compiled(), '\u2028\u2028\u2029\u2029');
});
test('should work with statements containing quotes', 1, function() {
var compiled = _.template("<%\
if (a == 'A' || a == \"a\") {\
%>'a',\"A\"<%\
} %>"
);
var data = { 'a': 'A' };
strictEqual(compiled(data), "'a',\"A\"");
});
test('should work with templates containing newlines and comments', 1, function() {
var compiled = _.template('<%\n\
// A code comment.\n\
if (value) { value += 3; }\n\
%><p><%= value %></p>'
);
strictEqual(compiled({ 'value': 3 }), '<p>6</p>');
});
test('should not error with IE conditional comments enabled (test with development build)', 1, function() {
var compiled = _.template(''),
pass = true;
/*@cc_on @*/
try {
compiled();
} catch(e) {
pass = false;
}
ok(pass);
});
test('should tokenize delimiters', 1, function() {
var compiled = _.template('<span class="icon-<%= type %>2"></span>'),
data = { 'type': 1 };
strictEqual(compiled(data), '<span class="icon-12"></span>');
});
test('should evaluate delimiters once', 1, function() {
var actual = [],
compiled = _.template('<%= func("a") %><%- func("b") %><% func("c") %>'),
data = { 'func': function(value) { actual.push(value); } };
compiled(data);
deepEqual(actual, ['a', 'b', 'c']);
});
test('should match delimiters before escaping text', 1, function() {
var compiled = _.template('<<\n a \n>>', { 'evaluate': /<<(.*?)>>/g });
strictEqual(compiled(), '<<\n a \n>>');
});
test('should resolve nullish values to an empty string', 3, function() {
var compiled = _.template('<%= a %><%- a %>'),
data = { 'a': null };
strictEqual(compiled(data), '');
data = { 'a': undefined };
strictEqual(compiled(data), '');
data = { 'a': {} };
compiled = _.template('<%= a.b %><%- a.b %>');
strictEqual(compiled(data), '');
});
test('should parse delimiters without newlines', 1, function() {
var expected = '<<\nprint("<p>" + (value ? "yes" : "no") + "</p>")\n>>',
compiled = _.template(expected, { 'evaluate': /<<(.+?)>>/g }),
data = { 'value': true };
strictEqual(compiled(data), expected);
});
test('should support recursive calls', 1, function() {
var compiled = _.template('<%= a %><% a = _.template(c)(obj) %><%= a %>'),
data = { 'a': 'A', 'b': 'B', 'c': '<%= b %>' };
strictEqual(compiled(data), 'AB');
});
test('should coerce `text` argument to a string', 1, function() {
var object = { 'toString': _.constant('<%= a %>') },
data = { 'a': 1 };
strictEqual(_.template(object)(data), '1');
});
test('should not augment the `options` object', 1, function() {
var options = {};
_.template('', options);
deepEqual(options, {});
});
test('should not modify `_.templateSettings` when `options` are provided', 2, function() {
var data = { 'a': 1 };
ok(!('a' in _.templateSettings));
_.template('', {}, data);
ok(!('a' in _.templateSettings));
delete _.templateSettings.a;
});
test('should not error for non-object `data` and `options` values', 2, function() {
var pass = true;
try {
_.template('')(1);
} catch(e) {
pass = false;
}
ok(pass, '`data` value');
pass = true;
try {
_.template('', 1)(1);
} catch(e) {
pass = false;
}
ok(pass, '`options` value');
});
test('should expose the source for compiled templates', 1, function() {
var compiled = _.template('x'),
values = [String(compiled), compiled.source],
expected = _.map(values, _.constant(true));
var actual = _.map(values, function(value) {
return _.includes(value, '__p');
});
deepEqual(actual, expected);
});
test('should expose the source when a SyntaxError occurs', 1, function() {
try {
_.template('<% if x %>');
} catch(e) {
var source = e.source;
}
ok(_.includes(source, '__p'));
});
test('should not include sourceURLs in the source', 1, function() {
var options = { 'sourceURL': '/a/b/c' },
compiled = _.template('x', options),
values = [compiled.source, undefined];
try {
_.template('<% if x %>', options);
} catch(e) {
values[1] = e.source;
}
var expected = _.map(values, _.constant(false));
var actual = _.map(values, function(value) {
return _.includes(value, 'sourceURL');
});
deepEqual(actual, expected);
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var array = ['<%= a %>', '<%- b %>', '<% print(c) %>'],
compiles = _.map(array, _.template),
data = { 'a': 'one', 'b': '`two`', 'c': 'three' };
var actual = _.map(compiles, function(compiled) {
return compiled(data);
});
deepEqual(actual, ['one', '&#96;two&#96;', 'three']);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.trunc');
(function() {
var string = 'hi-diddly-ho there, neighborino';
test('should truncate to a length of `30` by default', 1, function() {
strictEqual(_.trunc(string), 'hi-diddly-ho there, neighbo...');
});
test('should not truncate if `string` is <= `length`', 2, function() {
strictEqual(_.trunc(string, string.length), string);
strictEqual(_.trunc(string, string.length + 2), string);
});
test('should truncate string the given length', 1, function() {
strictEqual(_.trunc(string, 24), 'hi-diddly-ho there, n...');
});
test('should support a `omission` option', 1, function() {
strictEqual(_.trunc(string, { 'omission': ' [...]' }), 'hi-diddly-ho there, neig [...]');
});
test('should support a `length` option', 1, function() {
strictEqual(_.trunc(string, { 'length': 4 }), 'h...');
});
test('should support a `separator` option', 2, function() {
strictEqual(_.trunc(string, { 'length': 24, 'separator': ' ' }), 'hi-diddly-ho there,...');
strictEqual(_.trunc(string, { 'length': 24, 'separator': /,? +/ }), 'hi-diddly-ho there...');
});
test('should treat negative `length` as `0`', 4, function() {
_.each([0, -2], function(length) {
strictEqual(_.trunc(string, length), '...');
strictEqual(_.trunc(string, { 'length': length }), '...');
});
});
test('should coerce `length` to an integer', 8, function() {
_.each(['', NaN, 4.5, '4'], function(length, index) {
var actual = index > 1 ? 'h...' : '...';
strictEqual(_.trunc(string, length), actual);
strictEqual(_.trunc(string, { 'length': { 'valueOf': _.constant(length) } }), actual);
});
});
test('should coerce `string` to a string', 2, function() {
strictEqual(_.trunc(Object(string), 4), 'h...');
strictEqual(_.trunc({ 'toString': _.constant(string) }, 5), 'hi...');
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var actual = _.map([string, string, string], _.trunc),
truncated = 'hi-diddly-ho there, neighbo...';
deepEqual(actual, [truncated, truncated, truncated]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.throttle');
(function() {
asyncTest('should throttle a function', 2, function() {
if (!(isRhino && isModularize)) {
var callCount = 0,
throttled = _.throttle(function() { callCount++; }, 32);
throttled();
throttled();
throttled();
var lastCount = callCount;
ok(callCount > 0);
setTimeout(function() {
ok(callCount > lastCount);
QUnit.start();
}, 64);
}
else {
skipTest(2);
QUnit.start();
}
});
asyncTest('subsequent calls should return the result of the first call', 5, function() {
if (!(isRhino && isModularize)) {
var throttled = _.throttle(_.identity, 32),
result = [throttled('a'), throttled('b')];
deepEqual(result, ['a', 'a']);
setTimeout(function() {
var result = [throttled('x'), throttled('y')];
notEqual(result[0], 'a');
notStrictEqual(result[0], undefined);
notEqual(result[1], 'y');
notStrictEqual(result[1], undefined);
QUnit.start();
}, 64);
}
else {
skipTest(5);
QUnit.start();
}
});
asyncTest('should clear timeout when `func` is called', 1, function() {
if (!isModularize) {
var callCount = 0,
dateCount = 0;
var getTime = function() {
return ++dateCount == 5 ? Infinity : +new Date;
};
var lodash = _.runInContext(_.assign({}, root, {
'Date': function() {
return { 'getTime': getTime };
}
}));
var throttled = lodash.throttle(function() {
callCount++;
}, 32);
throttled();
throttled();
throttled();
setTimeout(function() {
strictEqual(callCount, 2);
QUnit.start();
}, 64);
}
else {
skipTest();
QUnit.start();
}
});
asyncTest('should not trigger a trailing call when invoked once', 2, function() {
if (!(isRhino && isModularize)) {
var callCount = 0,
throttled = _.throttle(function() { callCount++; }, 32);
throttled();
strictEqual(callCount, 1);
setTimeout(function() {
strictEqual(callCount, 1);
QUnit.start();
}, 64);
}
else {
skipTest(2);
QUnit.start();
}
});
_.times(2, function(index) {
asyncTest('should trigger a call when invoked repeatedly' + (index ? ' and `leading` is `false`' : ''), 1, function() {
if (!(isRhino && isModularize)) {
var callCount = 0,
limit = (argv || isPhantom) ? 1000 : 320,
options = index ? { 'leading': false } : {};
var throttled = _.throttle(function() {
callCount++;
}, 32, options);
var start = +new Date;
while ((new Date - start) < limit) {
throttled();
}
var actual = callCount > 1;
setTimeout(function() {
ok(actual);
QUnit.start();
}, 1);
}
else {
skipTest();
QUnit.start();
}
});
});
asyncTest('should apply default options', 3, function() {
if (!(isRhino && isModularize)) {
var callCount = 0;
var throttled = _.throttle(function(value) {
callCount++;
return value;
}, 32, {});
strictEqual(throttled('a'), 'a');
strictEqual(throttled('b'), 'a');
setTimeout(function() {
strictEqual(callCount, 2);
QUnit.start();
}, 256);
}
else {
skipTest(3);
QUnit.start();
}
});
test('should support a `leading` option', 4, function() {
_.each([true, { 'leading': true }], function(options) {
if (!(isRhino && isModularize)) {
var withLeading = _.throttle(_.identity, 32, options);
strictEqual(withLeading('a'), 'a');
}
else {
skipTest();
}
});
_.each([false, { 'leading': false }], function(options) {
if (!(isRhino && isModularize)) {
var withoutLeading = _.throttle(_.identity, 32, options);
strictEqual(withoutLeading('a'), undefined);
}
else {
skipTest();
}
});
});
asyncTest('should support a `trailing` option', 6, function() {
if (!(isRhino && isModularize)) {
var withCount = 0,
withoutCount = 0;
var withTrailing = _.throttle(function(value) {
withCount++;
return value;
}, 64, { 'trailing': true });
var withoutTrailing = _.throttle(function(value) {
withoutCount++;
return value;
}, 64, { 'trailing': false });
strictEqual(withTrailing('a'), 'a');
strictEqual(withTrailing('b'), 'a');
strictEqual(withoutTrailing('a'), 'a');
strictEqual(withoutTrailing('b'), 'a');
setTimeout(function() {
strictEqual(withCount, 2);
strictEqual(withoutCount, 1);
QUnit.start();
}, 256);
}
else {
skipTest(6);
QUnit.start();
}
});
asyncTest('should not update `lastCalled`, at the end of the timeout, when `trailing` is `false`', 1, function() {
if (!(isRhino && isModularize)) {
var callCount = 0;
var throttled = _.throttle(function() {
callCount++;
}, 64, { 'trailing': false });
throttled();
throttled();
setTimeout(function() {
throttled();
throttled();
}, 96);
setTimeout(function() {
ok(callCount > 1);
QUnit.start();
}, 192);
}
else {
skipTest();
QUnit.start();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.debounce and lodash.throttle');
_.each(['debounce', 'throttle'], function(methodName) {
var func = _[methodName],
isDebounce = methodName == 'debounce';
test('_.' + methodName + ' should not error for non-object `options` values', 1, function() {
var pass = true;
try {
func(_.noop, 32, 1);
} catch(e) {
pass = false;
}
ok(pass);
});
asyncTest('_.' + methodName + ' should have a default `wait` of `0`', 1, function() {
if (!(isRhino && isModularize)) {
var callCount = 0;
var funced = func(function() {
callCount++;
});
funced();
setTimeout(function() {
funced();
strictEqual(callCount, isDebounce ? 1 : 2);
QUnit.start();
}, 32);
}
else {
skipTest();
QUnit.start();
}
});
asyncTest('_.' + methodName + ' should invoke `func` with the correct `this` binding', 1, function() {
if (!(isRhino && isModularize)) {
var object = {
'funced': func(function() { actual.push(this); }, 32)
};
var actual = [],
expected = _.times(isDebounce ? 1 : 2, _.constant(object));
object.funced();
if (!isDebounce) {
object.funced();
}
setTimeout(function() {
deepEqual(actual, expected);
QUnit.start();
}, 64);
}
else {
skipTest();
QUnit.start();
}
});
asyncTest('_.' + methodName + ' supports recursive calls', 2, function() {
if (!(isRhino && isModularize)) {
var actual = [],
args = _.map(['a', 'b', 'c'], function(chr) { return [{}, chr]; }),
length = isDebounce ? 1 : 2,
expected = args.slice(0, length),
queue = args.slice();
var funced = func(function() {
var current = [this];
push.apply(current, arguments);
actual.push(current);
var next = queue.shift();
if (next) {
funced.call(next[0], next[1]);
}
}, 64);
var next = queue.shift();
funced.call(next[0], next[1]);
deepEqual(actual, expected.slice(0, length - 1));
setTimeout(function() {
deepEqual(actual, expected);
QUnit.start();
}, 96);
}
else {
skipTest(2);
QUnit.start();
}
});
asyncTest('_.' + methodName + ' should work if the system time is set backwards', 1, function() {
if (!isModularize) {
var callCount = 0,
dateCount = 0;
var getTime = function() {
return ++dateCount === 4 ? +new Date(2012, 3, 23, 23, 27, 18) : +new Date;
};
var lodash = _.runInContext(_.assign({}, root, {
'Date': function() {
return { 'getTime': getTime, 'valueOf': getTime };
}
}));
var funced = lodash[methodName](function() {
callCount++;
}, 32);
funced();
setTimeout(function() {
funced();
strictEqual(callCount, isDebounce ? 1 : 2);
QUnit.start();
}, 64);
}
else {
skipTest();
QUnit.start();
}
});
asyncTest('_.' + methodName + ' should support cancelling delayed calls', 1, function() {
if (!(isRhino && isModularize)) {
var callCount = 0;
var funced = func(function() {
callCount++;
}, 32, { 'leading': false });
funced();
funced.cancel();
setTimeout(function() {
strictEqual(callCount, 0);
QUnit.start();
}, 64);
}
else {
skipTest();
QUnit.start();
}
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.times');
(function() {
test('should coerce non-finite `n` values to `0`', 3, function() {
_.each([-Infinity, NaN, Infinity], function(n) {
deepEqual(_.times(n), []);
});
});
test('should floor `n` float values', 1, function() {
var actual = _.times(2.4, _.indentify);
deepEqual(actual, [0, 1]);
});
test('should provide the correct `iteratee` arguments', 1, function() {
var args;
_.times(1, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [0]);
});
test('should support the `thisArg` argument', 1, function() {
var expect = [1, 2, 3];
var actual = _.times(3, function(num) {
return this[num];
}, expect);
deepEqual(actual, expect);
});
test('should use `_.identity` when `iteratee` is nullish', 1, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant([0, 1, 2]));
var actual = _.map(values, function(value, index) {
return index ? _.times(3, value) : _.times(3);
});
deepEqual(actual, expected);
});
test('should return an array of the results of each `iteratee` execution', 1, function() {
deepEqual(_.times(3, function(n) { return n * 2; }), [0, 2, 4]);
});
test('should return an empty array for falsey and negative `n` arguments', 1, function() {
var values = falsey.concat(-1, -Infinity),
expected = _.map(values, _.constant([]));
var actual = _.map(values, function(value, index) {
return index ? _.times(value) : _.times();
});
deepEqual(actual, expected);
});
test('should return a wrapped value when chaining', 2, function() {
if (!isNpm) {
var wrapped = _(3).times();
ok(wrapped instanceof _);
deepEqual(wrapped.value(), [0, 1, 2]);
}
else {
skipTest(2);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.toArray');
(function() {
test('should return the values of objects', 1, function() {
var array = [1, 2],
object = { 'a': 1, 'b': 2 };
deepEqual(_.toArray(object), array);
});
test('should work with a string for `collection` (test in Opera < 10.52)', 2, function() {
deepEqual(_.toArray('ab'), ['a', 'b']);
deepEqual(_.toArray(Object('ab')), ['a', 'b']);
});
test('should work in a lazy chain sequence', 2, function() {
if (!isNpm) {
var actual = _([1, 2]).map(String).toArray().value();
deepEqual(actual, ['1', '2']);
actual = _({ 'a': 1, 'b': 2 }).toArray().map(String).value();
deepEqual(actual, ['1', '2']);
}
else {
skipTest(2);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.slice and lodash.toArray');
_.each(['slice', 'toArray'], function(methodName) {
var args = (function() { return arguments; }(1, 2, 3)),
array = [1, 2, 3],
func = _[methodName];
test('should return a dense array', 3, function() {
var sparse = Array(3);
sparse[1] = 2;
var actual = func(sparse);
ok('0' in actual);
ok('2' in actual);
deepEqual(actual, sparse);
});
test('should treat array-like objects like arrays', 2, function() {
var object = { '0': 'a', '1': 'b', '2': 'c', 'length': 3 };
deepEqual(func(object), ['a', 'b', 'c']);
deepEqual(func(args), array);
});
test('should return a shallow clone of arrays', 2, function() {
var actual = func(array);
deepEqual(actual, array);
notStrictEqual(actual, array);
});
test('should work with a node list for `collection` (test in IE < 9)', 1, function() {
if (document) {
try {
var actual = func(document.getElementsByTagName('body'));
} catch(e) {}
deepEqual(actual, [body]);
}
else {
skipTest();
}
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.toPlainObject');
(function() {
var args = arguments;
test('should flatten inherited properties', 1, function() {
function Foo() { this.b = 2; }
Foo.prototype.c = 3;
var actual = _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
deepEqual(actual, { 'a': 1, 'b': 2, 'c': 3 });
});
test('should convert `arguments` objects to plain objects', 1, function() {
var actual = _.toPlainObject(args),
expected = { '0': 1, '1': 2, '2': 3 };
deepEqual(actual, expected);
});
test('should convert arrays to plain objects', 1, function() {
var actual = _.toPlainObject(['a', 'b', 'c']),
expected = { '0': 'a', '1': 'b', '2': 'c' };
deepEqual(actual, expected);
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.transform');
(function() {
function Foo() {
this.a = 1;
this.b = 2;
this.c = 3;
}
test('should create an object with the same `[[Prototype]]` as `object` when `accumulator` is nullish', 4, function() {
var accumulators = [, null, undefined],
expected = _.map(accumulators, _.constant(true)),
object = new Foo;
var iteratee = function(result, value, key) {
result[key] = value * value;
};
var mapper = function(accumulator, index) {
return index ? _.transform(object, iteratee, accumulator) : _.transform(object, iteratee);
};
var results = _.map(accumulators, mapper);
var actual = _.map(results, function(result) {
return result instanceof Foo;
});
deepEqual(actual, expected);
expected = _.map(accumulators, _.constant({ 'a': 1, 'b': 4, 'c': 9 }));
actual = _.map(results, _.clone);
deepEqual(actual, expected);
object = { 'a': 1, 'b': 2, 'c': 3 };
actual = _.map(accumulators, mapper);
deepEqual(actual, expected);
object = [1, 2, 3];
expected = _.map(accumulators, _.constant([1, 4, 9]));
actual = _.map(accumulators, mapper);
deepEqual(actual, expected);
});
test('should support an `accumulator` value', 4, function() {
var values = [new Foo, [1, 2, 3], { 'a': 1, 'b': 2, 'c': 3 }],
expected = _.map(values, _.constant([0, 1, 4, 9]));
var actual = _.map(values, function(value) {
return _.transform(value, function(result, value) {
result.push(value * value);
}, [0]);
});
deepEqual(actual, expected);
var object = { '_': 0, 'a': 1, 'b': 4, 'c': 9 };
expected = [object, { '_': 0, '0': 1, '1': 4, '2': 9 }, object];
actual = _.map(values, function(value) {
return _.transform(value, function(result, value, key) {
result[key] = value * value;
}, { '_': 0 });
});
deepEqual(actual, expected);
object = {};
expected = _.map(values, _.constant(object));
actual = _.map(values, function(value) {
return _.transform(value, _.noop, object);
});
deepEqual(actual, expected);
actual = _.map(values, function(value) {
return _.transform(null, null, object);
});
deepEqual(actual, expected);
});
test('should treat sparse arrays as dense', 1, function() {
var actual = _.transform(Array(1), function(result, value, index) {
result[index] = String(value);
});
deepEqual(actual, ['undefined']);
});
test('should work without an `iteratee` argument', 1, function() {
ok(_.transform(new Foo) instanceof Foo);
});
test('should check that `object` is an object before using its `[[Prototype]]`', 2, function() {
var Ctors = [Boolean, Boolean, Number, Number, Number, String, String],
values = [true, false, 0, 1, NaN, '', 'a'],
expected = _.map(values, _.constant({}));
var results = _.map(values, function(value) {
return _.transform(value);
});
deepEqual(results, expected);
expected = _.map(values, _.constant(false));
var actual = _.map(results, function(value, index) {
return value instanceof Ctors[index];
});
deepEqual(actual, expected);
});
test('should create an empty object when provided a falsey `object` argument', 1, function() {
var expected = _.map(falsey, _.constant({}));
var actual = _.map(falsey, function(value, index) {
return index ? _.transform(value) : _.transform();
});
deepEqual(actual, expected);
});
_.each({
'array': [1, 2, 3],
'object': { 'a': 1, 'b': 2, 'c': 3 }
},
function(object, key) {
test('should provide the correct `iteratee` arguments when transforming an ' + key, 2, function() {
var args;
_.transform(object, function() {
args || (args = slice.call(arguments));
});
var first = args[0];
if (key == 'array') {
ok(first !== object && _.isArray(first));
deepEqual(args, [first, 1, 0, object]);
} else {
ok(first !== object && _.isPlainObject(first));
deepEqual(args, [first, 1, 'a', object]);
}
});
test('should support the `thisArg` argument when transforming an ' + key, 2, function() {
var actual = _.transform(object, function(result, value, key) {
result[key] = this[key];
}, null, object);
deepEqual(actual, object);
notStrictEqual(actual, object);
});
});
test('should create an object from the same realm as `object`', 1, function() {
var objects = _.transform(_, function(result, value, key) {
if (_.startsWith(key, '_') && _.isObject(value) && !_.isElement(value)) {
result.push(value);
}
}, []);
var expected = _.times(objects.length, _.constant(true));
var actual = _.map(objects, function(object) {
var Ctor = object.constructor,
result = _.transform(object);
if (result === object) {
return false;
}
if (_.isTypedArray(object)) {
return result instanceof Array;
}
return result instanceof Ctor || !(new Ctor instanceof Ctor);
});
deepEqual(actual, expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('trim methods');
_.each(['trim', 'trimLeft', 'trimRight'], function(methodName, index) {
var func = _[methodName];
var parts = [];
if (index != 2) {
parts.push('leading');
}
if (index != 1) {
parts.push('trailing');
}
parts = parts.join(' and ');
test('`_.' + methodName + '` should remove ' + parts + ' whitespace', 1, function() {
var string = whitespace + 'a b c' + whitespace,
expected = (index == 2 ? whitespace : '') + 'a b c' + (index == 1 ? whitespace : '');
strictEqual(func(string), expected);
});
test('`_.' + methodName + '` should not remove non-whitespace characters', 1, function() {
// Zero-width space (zws), next line character (nel), and non-character (bom) are not whitespace.
var problemChars = '\x85\u200b\ufffe',
string = problemChars + 'a b c' + problemChars;
strictEqual(func(string), string);
});
test('`_.' + methodName + '` should coerce `string` to a string', 1, function() {
var object = { 'toString': _.constant(whitespace + 'a b c' + whitespace) },
expected = (index == 2 ? whitespace : '') + 'a b c' + (index == 1 ? whitespace : '');
strictEqual(func(object), expected);
});
test('`_.' + methodName + '` should remove ' + parts + ' `chars`', 1, function() {
var string = '-_-a-b-c-_-',
expected = (index == 2 ? '-_-' : '') + 'a-b-c' + (index == 1 ? '-_-' : '');
strictEqual(func(string, '_-'), expected);
});
test('`_.' + methodName + '` should coerce `chars` to a string', 1, function() {
var object = { 'toString': _.constant('_-') },
string = '-_-a-b-c-_-',
expected = (index == 2 ? '-_-' : '') + 'a-b-c' + (index == 1 ? '-_-' : '');
strictEqual(func(string, object), expected);
});
test('`_.' + methodName + '` should return an empty string when provided nullish or empty string values and `chars`', 6, function() {
_.each([null, '_-'], function(chars) {
strictEqual(func(null, chars), '');
strictEqual(func(undefined, chars), '');
strictEqual(func('', chars), '');
});
});
test('`_.' + methodName + '` should work with nullish or empty string values for `chars`', 3, function() {
var string = whitespace + 'a b c' + whitespace,
expected = (index == 2 ? whitespace : '') + 'a b c' + (index == 1 ? whitespace : '');
strictEqual(func(string, null), expected);
strictEqual(func(string, undefined), expected);
strictEqual(func(string, ''), string);
});
test('`_.' + methodName + '` should work as an iteratee for methods like `_.map`', 1, function() {
var string = Object(whitespace + 'a b c' + whitespace),
trimmed = (index == 2 ? whitespace : '') + 'a b c' + (index == 1 ? whitespace : ''),
actual = _.map([string, string, string], func);
deepEqual(actual, [trimmed, trimmed, trimmed]);
});
test('`_.' + methodName + '` should return an unwrapped value when implicitly chaining', 1, function() {
if (!isNpm) {
var string = whitespace + 'a b c' + whitespace,
expected = (index == 2 ? whitespace : '') + 'a b c' + (index == 1 ? whitespace : '');
strictEqual(_(string)[methodName](), expected);
}
else {
skipTest();
}
});
test('`_.' + methodName + '` should return a wrapped value when explicitly chaining', 1, function() {
if (!isNpm) {
var string = whitespace + 'a b c' + whitespace;
ok(_(string).chain()[methodName]() instanceof _);
}
else {
skipTest();
}
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.unescape');
(function() {
var escaped = '&amp;&lt;&gt;&quot;&#39;\/',
unescaped = '&<>"\'\/';
escaped += escaped;
unescaped += unescaped;
test('should unescape entities in order', 1, function() {
strictEqual(_.unescape('&amp;lt;'), '&lt;');
});
test('should unescape the proper entities', 1, function() {
strictEqual(_.unescape(escaped), unescaped);
});
test('should not unescape the "&#x2F;" entity', 1, function() {
strictEqual(_.unescape('&#x2F;'), '&#x2F;');
});
test('should handle strings with nothing to unescape', 1, function() {
strictEqual(_.unescape('abc'), 'abc');
});
test('should unescape the same characters escaped by `_.escape`', 1, function() {
strictEqual(_.unescape(_.escape(unescaped)), unescaped);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.union');
(function() {
var args = arguments;
test('should return the union of the given arrays', 1, function() {
var actual = _.union([1, 3, 2], [5, 2, 1, 4], [2, 1]);
deepEqual(actual, [1, 3, 2, 5, 4]);
});
test('should not flatten nested arrays', 1, function() {
var actual = _.union([1, 3, 2], [1, [5]], [2, [4]]);
deepEqual(actual, [1, 3, 2, [5], [4]]);
});
test('should ignore values that are not arrays or `arguments` objects', 3, function() {
var array = [0];
deepEqual(_.union(array, 3, null, { '0': 1 }), array);
deepEqual(_.union(null, array, null, [2, 1]), [0, 2, 1]);
deepEqual(_.union(array, null, args, null), [0, 1, 2, 3]);
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.uniq');
(function() {
var objects = [{ 'a': 2 }, { 'a': 3 }, { 'a': 1 }, { 'a': 2 }, { 'a': 3 }, { 'a': 1 }];
test('should return unique values of an unsorted array', 1, function() {
var array = [2, 3, 1, 2, 3, 1];
deepEqual(_.uniq(array), [2, 3, 1]);
});
test('should return unique values of a sorted array', 1, function() {
var array = [1, 1, 2, 2, 3];
deepEqual(_.uniq(array), [1, 2, 3]);
});
test('should treat object instances as unique', 1, function() {
deepEqual(_.uniq(objects), objects);
});
test('should not treat `NaN` as unique', 1, function() {
deepEqual(_.uniq([1, NaN, 3, NaN]), [1, NaN, 3]);
});
test('should work with `isSorted`', 3, function() {
var expected = [1, 2, 3];
deepEqual(_.uniq([1, 2, 3], true), expected);
deepEqual(_.uniq([1, 1, 2, 2, 3], true), expected);
deepEqual(_.uniq([1, 2, 3, 3, 3, 3, 3], true), expected);
});
test('should work with an `iteratee` argument', 2, function() {
_.each([objects, _.sortBy(objects, 'a')], function(array, index) {
var isSorted = !!index,
expected = isSorted ? [objects[2], objects[0], objects[1]] : objects.slice(0, 3);
var actual = _.uniq(array, isSorted, function(object) {
return object.a;
});
deepEqual(actual, expected);
});
});
test('should provide the correct `iteratee` arguments', 1, function() {
var args;
_.uniq(objects, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [objects[0], 0, objects]);
});
test('should work with `iteratee` without specifying `isSorted`', 1, function() {
var actual = _.uniq(objects, function(object) {
return object.a;
});
deepEqual(actual, objects.slice(0, 3));
});
test('should support the `thisArg` argument', 1, function() {
var actual = _.uniq([1, 2, 1.5, 3, 2.5], function(num) {
return this.floor(num);
}, Math);
deepEqual(actual, [1, 2, 3]);
});
test('should work with a "_.property" style `iteratee`', 2, function() {
var actual = _.uniq(objects, 'a');
deepEqual(actual, objects.slice(0, 3));
var arrays = [[2], [3], [1], [2], [3], [1]];
actual = _.uniq(arrays, 0);
deepEqual(actual, arrays.slice(0, 3));
});
test('should perform an unsorted uniq when used as an iteratee for methods like `_.map`', 1, function() {
var array = [[2, 1, 2], [1, 2, 1]],
actual = _.map(array, _.uniq);
deepEqual(actual, [[2, 1], [1, 2]]);
});
test('should work with large arrays', 1, function() {
var largeArray = [],
expected = [0, 'a', {}],
count = Math.ceil(LARGE_ARRAY_SIZE / expected.length);
_.times(count, function() {
push.apply(largeArray, expected);
});
deepEqual(_.uniq(largeArray), expected);
});
test('should work with large arrays of boolean, `NaN`, and nullish values', 1, function() {
var largeArray = [],
expected = [true, false, NaN, null, undefined],
count = Math.ceil(LARGE_ARRAY_SIZE / expected.length);
_.times(count, function() {
push.apply(largeArray, expected);
});
deepEqual(_.uniq(largeArray), expected);
});
test('should work with large arrays of symbols', 1, function() {
if (Symbol) {
var largeArray = _.times(LARGE_ARRAY_SIZE, function() {
return Symbol();
});
deepEqual(_.uniq(largeArray), largeArray);
}
else {
skipTest();
}
});
test('should work with large arrays of well-known symbols', 1, function() {
// See https://people.mozilla.org/~jorendorff/es6-draft.html#sec-well-known-symbols.
if (Symbol) {
var expected = [
Symbol.hasInstance, Symbol.isConcatSpreadable, Symbol.iterator,
Symbol.match, Symbol.replace, Symbol.search, Symbol.species,
Symbol.split, Symbol.toPrimitive, Symbol.toStringTag, Symbol.unscopables
];
var largeArray = [],
count = Math.ceil(LARGE_ARRAY_SIZE / expected.length);
expected = _.map(expected, function(symbol) {
return symbol || {};
});
_.times(count, function() {
push.apply(largeArray, expected);
});
deepEqual(_.uniq(largeArray), expected);
}
else {
skipTest();
}
});
test('should distinguish between numbers and numeric strings', 1, function() {
var array = [],
expected = ['2', 2, Object('2'), Object(2)],
count = Math.ceil(LARGE_ARRAY_SIZE / expected.length);
_.times(count, function() {
push.apply(array, expected);
});
deepEqual(_.uniq(array), expected);
});
_.each({
'an object': ['a'],
'a number': 0,
'a string': '0'
},
function(callback, key) {
test('should work with ' + key + ' for `iteratee`', 1, function() {
var actual = _.uniq([['a'], ['b'], ['a']], callback);
deepEqual(actual, [['a'], ['b']]);
});
});
test('should be aliased', 1, function() {
strictEqual(_.unique, _.uniq);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.uniqueId');
(function() {
test('should generate unique ids', 1, function() {
var actual = _.times(1000, function() {
return _.uniqueId();
});
strictEqual(_.uniq(actual).length, actual.length);
});
test('should return a string value when not providing a prefix argument', 1, function() {
strictEqual(typeof _.uniqueId(), 'string');
});
test('should coerce the prefix argument to a string', 1, function() {
var actual = [_.uniqueId(3), _.uniqueId(2), _.uniqueId(1)];
ok(/3\d+,2\d+,1\d+/.test(actual));
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.unzipWith');
(function() {
test('should unzip arrays combining regrouped elements with `iteratee`', 1, function() {
var array = [[1, 4], [2, 5], [3, 6]];
deepEqual(_.unzipWith(array, _.add), [6, 15]);
});
test('should provide the correct `iteratee` arguments', 1, function() {
var args;
_.unzipWith([[1, 3, 5], [2, 4, 6]], function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [1, 2, 1, [1, 2]]);
});
test('should support the `thisArg` argument', 1, function() {
var actual = _.unzipWith([[1.2, 3.4], [2.3, 4.5]], function(a, b) {
return this.floor(a) + this.floor(b);
}, Math);
deepEqual(actual, [3, 7]);
});
test('should perform a basic unzip when `iteratee` is nullish', 1, function() {
var array = [[1, 3], [2, 4]],
values = [, null, undefined],
expected = _.map(values, _.constant(_.unzip(array)));
var actual = _.map(values, function(value, index) {
return index ? _.unzipWith(array, value) : _.unzipWith(array);
});
deepEqual(actual, expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.values');
(function() {
test('should get the values of an object', 1, function() {
var object = { 'a': 1, 'b': 2 };
deepEqual(_.values(object), [1, 2]);
});
test('should work with an object that has a `length` property', 1, function() {
var object = { '0': 'a', '1': 'b', 'length': 2 };
deepEqual(_.values(object), ['a', 'b', 2]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.where');
(function() {
test('should filter by `source` properties', 12, function() {
var objects = [
{ 'a': 1 },
{ 'a': 1 },
{ 'a': 1, 'b': 2 },
{ 'a': 2, 'b': 2 },
{ 'a': 3 }
];
var pairs = [
[{ 'a': 1 }, [{ 'a': 1 }, { 'a': 1 }, { 'a': 1, 'b': 2 }]],
[{ 'a': 2 }, [{ 'a': 2, 'b': 2 }]],
[{ 'a': 3 }, [{ 'a': 3 }]],
[{ 'b': 1 }, []],
[{ 'b': 2 }, [{ 'a': 1, 'b': 2 }, { 'a': 2, 'b': 2 }]],
[{ 'a': 1, 'b': 2 }, [{ 'a': 1, 'b': 2 }]]
];
_.each(pairs, function(pair) {
var actual = _.where(objects, pair[0]);
deepEqual(actual, pair[1]);
ok(_.isEmpty(_.difference(actual, objects)));
});
});
test('should work with an object for `collection`', 1, function() {
var object = {
'x': { 'a': 1 },
'y': { 'a': 3 },
'z': { 'a': 1, 'b': 2 }
};
var actual = _.where(object, { 'a': 1 });
deepEqual(actual, [object.x, object.z]);
});
test('should work in a lazy chain sequence', 1, function() {
if (!isNpm) {
var array = [
{ 'a': 1 },
{ 'a': 3 },
{ 'a': 1, 'b': 2 }
];
var actual = _(array).where({ 'a': 1 }).value();
deepEqual(actual, [array[0], array[2]]);
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.without');
(function() {
test('should use strict equality to determine the values to reject', 2, function() {
var object1 = { 'a': 1 },
object2 = { 'b': 2 },
array = [object1, object2];
deepEqual(_.without(array, { 'a': 1 }), array);
deepEqual(_.without(array, object1), [object2]);
});
test('should remove all occurrences of each value from an array', 1, function() {
var array = [1, 2, 3, 1, 2, 3];
deepEqual(_.without(array, 1, 2), [3, 3]);
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.words');
(function() {
test('should treat latin-1 supplementary letters as words', 1, function() {
var expected = _.map(burredLetters, function(letter) {
return [letter];
});
var actual = _.map(burredLetters, function(letter) {
return _.words(letter);
});
deepEqual(actual, expected);
});
test('should not treat mathematical operators as words', 1, function() {
var operators = ['\xd7', '\xf7'],
expected = _.map(operators, _.constant([])),
actual = _.map(operators, _.words);
deepEqual(actual, expected);
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var strings = _.map(['a', 'b', 'c'], Object),
actual = _.map(strings, _.words);
deepEqual(actual, [['a'], ['b'], ['c']]);
});
test('should work with compound words', 6, function() {
deepEqual(_.words('aeiouAreVowels'), ['aeiou', 'Are', 'Vowels']);
deepEqual(_.words('enable 24h format'), ['enable', '24', 'h', 'format']);
deepEqual(_.words('LETTERSAeiouAreVowels'), ['LETTERS', 'Aeiou', 'Are', 'Vowels']);
deepEqual(_.words('tooLegit2Quit'), ['too', 'Legit', '2', 'Quit']);
deepEqual(_.words('walk500Miles'), ['walk', '500', 'Miles']);
deepEqual(_.words('xhr2Request'), ['xhr', '2', 'Request']);
});
test('should work with compound words containing diacritical marks', 3, function() {
deepEqual(_.words('LETTERSÆiouAreVowels'), ['LETTERS', 'Æiou', 'Are', 'Vowels']);
deepEqual(_.words('æiouAreVowels'), ['æiou', 'Are', 'Vowels']);
deepEqual(_.words('æiou2Consonants'), ['æiou', '2', 'Consonants']);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.wrap');
(function() {
test('should create a wrapped function', 1, function() {
var p = _.wrap(_.escape, function(func, text) {
return '<p>' + func(text) + '</p>';
});
strictEqual(p('fred, barney, & pebbles'), '<p>fred, barney, &amp; pebbles</p>');
});
test('should provide the correct `wrapper` arguments', 1, function() {
var args;
var wrapped = _.wrap(_.noop, function() {
args || (args = slice.call(arguments));
});
wrapped(1, 2, 3);
deepEqual(args, [_.noop, 1, 2, 3]);
});
test('should not set a `this` binding', 1, function() {
var p = _.wrap(_.escape, function(func) {
return '<p>' + func(this.text) + '</p>';
});
var object = { 'p': p, 'text': 'fred, barney, & pebbles' };
strictEqual(object.p(), '<p>fred, barney, &amp; pebbles</p>');
});
test('should use `_.identity` when `wrapper` is nullish', 1, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant('a'));
var actual = _.map(values, function(value, index) {
var wrapped = index ? _.wrap('a', value) : _.wrap('a');
return wrapped('b', 'c');
});
deepEqual(actual, expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.xor');
(function() {
var args = arguments;
test('should return the symmetric difference of the given arrays', 1, function() {
var actual = _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]);
deepEqual(actual, [1, 4, 5]);
});
test('should return an array of unique values', 2, function() {
var actual = _.xor([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]);
deepEqual(actual, [1, 4, 5]);
actual = _.xor([1, 1]);
deepEqual(actual, [1]);
});
test('should return a new array when a single array is provided', 1, function() {
var array = [1];
notStrictEqual(_.xor(array), array);
});
test('should ignore individual secondary arguments', 1, function() {
var array = [0];
deepEqual(_.xor(array, 3, null, { '0': 1 }), array);
});
test('should ignore values that are not arrays or `arguments` objects', 3, function() {
var array = [1, 2];
deepEqual(_.xor(array, 3, null, { '0': 1 }), array);
deepEqual(_.xor(null, array, null, [2, 3]), [1, 3]);
deepEqual(_.xor(array, null, args, null), [3]);
});
test('should return a wrapped value when chaining', 2, function() {
if (!isNpm) {
var wrapped = _([1, 2, 3]).xor([5, 2, 1, 4]);
ok(wrapped instanceof _);
deepEqual(wrapped.value(), [3, 5, 4]);
}
else {
skipTest(2);
}
});
test('should work when in a lazy chain sequence before `first` or `last`', 1, function() {
if (!isNpm) {
var wrapped = _([1, 2]).slice().xor([2, 3]);
var actual = _.map(['first', 'last'], function(methodName) {
return wrapped[methodName]();
});
deepEqual(actual, [1, 3]);
}
else {
skipTest();
}
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.zipObject');
(function() {
var object = { 'barney': 36, 'fred': 40 },
array = [['barney', 36], ['fred', 40]];
test('should skip falsey elements in a given two dimensional array', 1, function() {
var actual = _.zipObject(array.concat(falsey));
deepEqual(actual, object);
});
test('should zip together key/value arrays into an object', 1, function() {
var actual = _.zipObject(['barney', 'fred'], [36, 40]);
deepEqual(actual, object);
});
test('should ignore extra `values`', 1, function() {
deepEqual(_.zipObject(['a'], [1, 2]), { 'a': 1 });
});
test('should accept a two dimensional array', 1, function() {
var actual = _.zipObject(array);
deepEqual(actual, object);
});
test('should not assume `keys` is two dimensional if `values` is not provided', 1, function() {
var actual = _.zipObject(['barney', 'fred']);
deepEqual(actual, { 'barney': undefined, 'fred': undefined });
});
test('should accept a falsey `array` argument', 1, function() {
var expected = _.map(falsey, _.constant({}));
var actual = _.map(falsey, function(value, index) {
try {
return index ? _.zipObject(value) : _.zipObject();
} catch(e) {}
});
deepEqual(actual, expected);
});
test('should support consuming the return value of `_.pairs`', 1, function() {
deepEqual(_.zipObject(_.pairs(object)), object);
});
test('should work in a lazy chain sequence', 1, function() {
if (!isNpm) {
var array = [['a', 1], ['b', 2]],
predicate = function(value) { return value > 2; },
actual = _(array).zipObject().map(square).filter(predicate).take().value();
deepEqual(actual, [4]);
}
else {
skipTest();
}
});
test('should be aliased', 1, function() {
strictEqual(_.object, _.zipObject);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.zipWith');
(function() {
test('should zip arrays combining grouped elements with `iteratee`', 2, function() {
var array1 = [1, 2, 3],
array2 = [4, 5, 6];
deepEqual(_.zipWith(array1, array2, _.add), [5, 7, 9]);
deepEqual(_.zipWith(array1, [], _.add), [1, 2, 3]);
});
test('should provide the correct `iteratee` arguments', 1, function() {
var args;
_.zipWith([1, 2], [3, 4], [5, 6], function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [1, 3, 1, [1, 3, 5]]);
});
test('should support the `thisArg` argument', 1, function() {
var actual = _.zipWith([1.2, 2.3], [3.4, 4.5], function(a, b) {
return this.floor(a) + this.floor(b);
}, Math);
deepEqual(actual, [4, 6]);
});
test('should perform a basic zip when `iteratee` is nullish', 1, function() {
var array1 = [1, 2],
array2 = [3, 4],
values = [, null, undefined],
expected = _.map(values, _.constant(_.zip(array1, array2)));
var actual = _.map(values, function(value, index) {
return index ? _.zipWith(array1, array2, value) : _.zipWith(array1, array2);
});
deepEqual(actual, expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.unzip and lodash.zip');
_.each(['unzip', 'zip'], function(methodName, index) {
var func = _[methodName];
func = _.bind(index ? func.apply : func.call, func, null);
var object = {
'an empty array': [
[],
[]
],
'0-tuples': [
[[], []],
[]
],
'2-tuples': [
[['barney', 'fred'], [36, 40]],
[['barney', 36], ['fred', 40]]
],
'3-tuples': [
[['barney', 'fred'], [36, 40], [true, false]],
[['barney', 36, true], ['fred', 40, false]]
]
};
_.forOwn(object, function(pair, key) {
test('`_.' + methodName + '` should work with ' + key, 2, function() {
var actual = func(pair[0]);
deepEqual(actual, pair[1]);
deepEqual(func(actual), actual.length ? pair[0] : []);
});
});
test('`_.' + methodName + '` should work with tuples of different lengths', 4, function() {
var pair = [
[['barney', 36], ['fred', 40, false]],
[['barney', 'fred'], [36, 40], [undefined, false]]
];
var actual = func(pair[0]);
ok('0' in actual[2]);
deepEqual(actual, pair[1]);
actual = func(actual);
ok('2' in actual[0]);
deepEqual(actual, [['barney', 36, undefined], ['fred', 40, false]]);
});
test('`_.' + methodName + '` should treat falsey values as empty arrays', 1, function() {
var expected = _.map(falsey, _.constant([]));
var actual = _.map(falsey, function(value) {
return func([value, value, value]);
});
deepEqual(actual, expected);
});
test('`_.' + methodName + '` should ignore values that are not arrays or `arguments` objects', 1, function() {
var array = [[1, 2], [3, 4], null, undefined, { '0': 1 }];
deepEqual(func(array), [[1, 3], [2, 4]]);
});
test('`_.' + methodName + '` should support consuming its return value', 1, function() {
var expected = [['barney', 'fred'], [36, 40]];
deepEqual(func(func(func(func(expected)))), expected);
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...).commit');
(function() {
test('should execute the chained sequence and returns the wrapped result', 4, function() {
if (!isNpm) {
var array = [1],
wrapped = _(array).push(2).push(3);
deepEqual(array, [1]);
var otherWrapper = wrapped.commit();
ok(otherWrapper instanceof _);
deepEqual(otherWrapper.value(), [1, 2, 3]);
deepEqual(wrapped.value(), [1, 2, 3, 2, 3]);
}
else {
skipTest(4);
}
});
test('should track the `__chain__` value of a wrapper', 2, function() {
if (!isNpm) {
var wrapper = _([1]).chain().commit().first();
ok(wrapper instanceof _);
strictEqual(wrapper.value(), 1);
}
else {
skipTest(2);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...).concat');
(function() {
test('should return a new wrapped array', 3, function() {
if (!isNpm) {
var array = [1],
wrapped = _(array).concat([2, 3]),
actual = wrapped.value();
deepEqual(array, [1]);
deepEqual(actual, [1, 2, 3]);
notStrictEqual(actual, array);
}
else {
skipTest(3);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...).join');
(function() {
var array = [1, 2, 3];
test('should return join all array elements into a string', 2, function() {
if (!isNpm) {
var wrapped = _(array);
strictEqual(wrapped.join('.'), '1.2.3');
strictEqual(wrapped.value(), array);
}
else {
skipTest(2);
}
});
test('should return a wrapped value when explicitly chaining', 1, function() {
if (!isNpm) {
ok(_(array).chain().join('.') instanceof _);
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...).plant');
(function() {
test('should clone the chained sequence planting `value` as the wrapped value', 2, function() {
if (!isNpm) {
var array1 = [5, null, 3, null, 1],
array2 = [10, null, 8, null, 6],
wrapper1 = _(array1).thru(_.compact).map(square).takeRight(2).sort(),
wrapper2 = wrapper1.plant(array2);
deepEqual(wrapper2.value(), [36, 64]);
deepEqual(wrapper1.value(), [1, 9]);
}
else {
skipTest(2);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...).pop');
(function() {
test('should remove elements from the end of `array`', 5, function() {
if (!isNpm) {
var array = [1, 2],
wrapped = _(array);
strictEqual(wrapped.pop(), 2);
deepEqual(wrapped.value(), [1]);
strictEqual(wrapped.pop(), 1);
var actual = wrapped.value();
deepEqual(actual, []);
strictEqual(actual, array);
}
else {
skipTest(5);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...).push');
(function() {
test('should append elements to `array`', 2, function() {
if (!isNpm) {
var array = [1],
wrapped = _(array).push(2, 3),
actual = wrapped.value();
strictEqual(actual, array);
deepEqual(actual, [1, 2, 3]);
}
else {
skipTest(2);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...).replace');
(function() {
test('should replace the matched pattern', 2, function() {
if (!isNpm) {
var wrapped = _('abcdef');
strictEqual(wrapped.replace('def', '123'), 'abc123');
strictEqual(wrapped.replace(/[bdf]/g, '-'), 'a-c-e-');
}
else {
skipTest(2);
}
});
test('should return a wrapped value when explicitly chaining', 1, function() {
if (!isNpm) {
ok(_('abc').chain().replace('b', '_') instanceof _);
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...).reverse');
(function() {
test('should return the wrapped reversed `array`', 3, function() {
if (!isNpm) {
var array = [1, 2, 3],
wrapped = _(array).reverse(),
actual = wrapped.value();
ok(wrapped instanceof _);
strictEqual(actual, array);
deepEqual(actual, [3, 2, 1]);
}
else {
skipTest(3);
}
});
test('should work in a lazy chain sequence', 1, function() {
if (!isNpm) {
var actual = _([1, 2, 3, null]).map(_.identity).reverse().value();
deepEqual(actual, [null, 3, 2, 1]);
}
else {
skipTest();
}
});
test('should be lazy when in a lazy chain sequence', 2, function() {
if (!isNpm) {
var spy = {
'toString': function() {
throw new Error('spy was revealed');
}
};
try {
var wrapped = _(['a', spy]).map(String).reverse(),
actual = wrapped.last();
} catch(e) {}
ok(wrapped instanceof _);
strictEqual(actual, 'a');
}
else {
skipTest(2);
}
});
test('should work in a hybrid chain sequence', 4, function() {
if (!isNpm) {
var array = [1, 2, 3, null];
_.each(['map', 'filter'], function(methodName) {
var actual = _(array)[methodName](_.identity).thru(_.compact).reverse().value();
deepEqual(actual, [3, 2, 1]);
actual = _(array).thru(_.compact)[methodName](_.identity).pull(1).push(4).reverse().value();
deepEqual(actual, [4, 3, 2]);
});
}
else {
skipTest(4);
}
});
test('should track the `__chain__` value of a wrapper', 2, function() {
if (!isNpm) {
var wrapper = _([1, 2, 3]).chain().reverse().first();
ok(wrapper instanceof _);
strictEqual(wrapper.value(), 3);
}
else {
skipTest(2);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...).shift');
(function() {
test('should remove elements from the front of `array`', 5, function() {
if (!isNpm) {
var array = [1, 2],
wrapped = _(array);
strictEqual(wrapped.shift(), 1);
deepEqual(wrapped.value(), [2]);
strictEqual(wrapped.shift(), 2);
var actual = wrapped.value();
deepEqual(actual, []);
strictEqual(actual, array);
}
else {
skipTest(5);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...).slice');
(function() {
test('should return a slice of `array`', 3, function() {
if (!isNpm) {
var array = [1, 2, 3],
wrapped = _(array).slice(0, 2),
actual = wrapped.value();
deepEqual(array, [1, 2, 3]);
deepEqual(actual, [1, 2]);
notStrictEqual(actual, array);
}
else {
skipTest(3);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...).sort');
(function() {
test('should return the wrapped sorted `array`', 2, function() {
if (!isNpm) {
var array = [3, 1, 2],
wrapped = _(array).sort(),
actual = wrapped.value();
strictEqual(actual, array);
deepEqual(actual, [1, 2, 3]);
}
else {
skipTest(2);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...).splice');
(function() {
test('should support removing and inserting elements', 5, function() {
if (!isNpm) {
var array = [1, 2],
wrapped = _(array);
deepEqual(wrapped.splice(1, 1, 3).value(), [2]);
deepEqual(wrapped.value(), [1, 3]);
deepEqual(wrapped.splice(0, 2).value(), [1, 3]);
var actual = wrapped.value();
deepEqual(actual, []);
strictEqual(actual, array);
}
else {
skipTest(5);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...).split');
(function() {
test('should support string split', 2, function() {
if (!isNpm) {
var wrapped = _('abcde');
deepEqual(wrapped.split('c').value(), ['ab', 'de']);
deepEqual(wrapped.split(/[bd]/).value(), ['a', 'c', 'e']);
}
else {
skipTest(2);
}
});
test('should allow mixed string and array prototype methods', 1, function() {
if (!isNpm) {
var wrapped = _('abc');
strictEqual(wrapped.split('b').join(','), 'a,c');
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...).unshift');
(function() {
test('should prepend elements to `array`', 2, function() {
if (!isNpm) {
var array = [3],
wrapped = _(array).unshift(1, 2),
actual = wrapped.value();
strictEqual(actual, array);
deepEqual(actual, [1, 2, 3]);
}
else {
skipTest(2);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('splice objects');
_.each(['pop', 'shift', 'splice'], function(methodName) {
test('`_(...).' + methodName + '` should remove the value at index `0` when length is `0` (test in IE 8 compatibility mode)', 2, function() {
if (!isNpm) {
var wrapped = _({ '0': 1, 'length': 1 });
if (methodName == 'splice') {
wrapped.splice(0, 1).value();
} else {
wrapped[methodName]();
}
deepEqual(wrapped.keys().value(), ['length']);
strictEqual(wrapped.first(), undefined);
}
else {
skipTest(2);
}
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...).toString');
(function() {
test('should return the `toString` result of the wrapped value', 1, function() {
if (!isNpm) {
var wrapped = _([1, 2, 3]);
strictEqual(String(wrapped), '1,2,3');
}
else {
skipTest();
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...).value');
(function() {
test('should execute the chained sequence and extract the unwrapped value', 4, function() {
if (!isNpm) {
var array = [1],
wrapped = _(array).push(2).push(3);
deepEqual(array, [1]);
deepEqual(wrapped.value(), [1, 2, 3]);
deepEqual(wrapped.value(), [1, 2, 3, 2, 3]);
deepEqual(array, [1, 2, 3, 2, 3]);
}
else {
skipTest(4);
}
});
test('should return the `valueOf` result of the wrapped value', 1, function() {
if (!isNpm) {
var wrapped = _(123);
strictEqual(Number(wrapped), 123);
}
else {
skipTest();
}
});
test('should stringify the wrapped value when used by `JSON.stringify`', 1, function() {
if (!isNpm && JSON) {
var wrapped = _([1, 2, 3]);
strictEqual(JSON.stringify(wrapped), '[1,2,3]');
}
else {
skipTest();
}
});
test('should be aliased', 3, function() {
if (!isNpm) {
var expected = _.prototype.value;
strictEqual(_.prototype.run, expected);
strictEqual(_.prototype.toJSON, expected);
strictEqual(_.prototype.valueOf, expected);
}
else {
skipTest(3);
}
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...) methods that return the wrapped modified array');
(function() {
var funcs = [
'push',
'reverse',
'sort',
'unshift'
];
_.each(funcs, function(methodName) {
test('`_(...).' + methodName + '` should return a new wrapper', 2, function() {
if (!isNpm) {
var array = [1, 2, 3],
wrapped = _(array),
actual = wrapped[methodName]();
ok(actual instanceof _);
notStrictEqual(actual, wrapped);
}
else {
skipTest(2);
}
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...) methods that return new wrapped values');
(function() {
var funcs = [
'concat',
'slice',
'splice'
];
_.each(funcs, function(methodName) {
test('`_(...).' + methodName + '` should return a new wrapped value', 2, function() {
if (!isNpm) {
var array = [1, 2, 3],
wrapped = _(array),
actual = wrapped[methodName]();
ok(actual instanceof _);
notStrictEqual(actual, wrapped);
}
else {
skipTest(2);
}
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...) methods that return unwrapped values');
(function() {
var funcs = [
'clone',
'contains',
'every',
'find',
'first',
'has',
'isArguments',
'isArray',
'isBoolean',
'isDate',
'isElement',
'isEmpty',
'isEqual',
'isError',
'isFinite',
'isFunction',
'isNaN',
'isNull',
'isNumber',
'isObject',
'isPlainObject',
'isRegExp',
'isString',
'isUndefined',
'join',
'last',
'max',
'min',
'parseInt',
'pop',
'shift',
'sum',
'random',
'reduce',
'reduceRight',
'some'
];
_.each(funcs, function(methodName) {
test('`_(...).' + methodName + '` should return an unwrapped value when implicitly chaining', 1, function() {
if (!isNpm) {
var array = [1, 2, 3],
actual = _(array)[methodName]();
ok(!(actual instanceof _));
}
else {
skipTest();
}
});
test('`_(...).' + methodName + '` should return a wrapped value when explicitly chaining', 1, function() {
if (!isNpm) {
var array = [1, 2, 3],
actual = _(array).chain()[methodName]();
ok(actual instanceof _);
}
else {
skipTest();
}
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('"Arrays" category methods');
(function() {
var args = arguments,
array = [1, 2, 3, 4, 5, 6];
test('should work with `arguments` objects', 29, function() {
function message(methodName) {
return '`_.' + methodName + '` should work with `arguments` objects';
}
deepEqual(_.difference(args, [null]), [1, [3], 5], message('difference'));
deepEqual(_.difference(array, args), [2, 3, 4, 6], '_.difference should work with `arguments` objects as secondary arguments');
deepEqual(_.union(args, [null, 6]), [1, null, [3], 5, 6], message('union'));
deepEqual(_.union(array, args), array.concat([null, [3]]), '_.union should work with `arguments` objects as secondary arguments');
deepEqual(_.compact(args), [1, [3], 5], message('compact'));
deepEqual(_.drop(args, 3), [null, 5], message('drop'));
deepEqual(_.dropRight(args, 3), [1, null], message('dropRight'));
deepEqual(_.dropRightWhile(args,_.identity), [1, null, [3], null], message('dropRightWhile'));
deepEqual(_.dropWhile(args,_.identity), [ null, [3], null, 5], message('dropWhile'));
deepEqual(_.findIndex(args, _.identity), 0, message('findIndex'));
deepEqual(_.findLastIndex(args, _.identity), 4, message('findLastIndex'));
deepEqual(_.first(args), 1, message('first'));
deepEqual(_.flatten(args), [1, null, 3, null, 5], message('flatten'));
deepEqual(_.indexOf(args, 5), 4, message('indexOf'));
deepEqual(_.initial(args), [1, null, [3], null], message('initial'));
deepEqual(_.intersection(args, [1]), [1], message('intersection'));
deepEqual(_.last(args), 5, message('last'));
deepEqual(_.lastIndexOf(args, 1), 0, message('lastIndexOf'));
deepEqual(_.rest(args, 4), [null, [3], null, 5], message('rest'));
deepEqual(_.sortedIndex(args, 6), 5, message('sortedIndex'));
deepEqual(_.take(args, 2), [1, null], message('take'));
deepEqual(_.takeRight(args, 1), [5], message('takeRight'));
deepEqual(_.takeRightWhile(args, _.identity), [5], message('takeRightWhile'));
deepEqual(_.takeWhile(args, _.identity), [1], message('takeWhile'));
deepEqual(_.uniq(args), [1, null, [3], 5], message('uniq'));
deepEqual(_.without(args, null), [1, [3], 5], message('without'));
deepEqual(_.zip(args, args), [[1, 1], [null, null], [[3], [3]], [null, null], [5, 5]], message('zip'));
if (_.support.argsTag && _.support.argsObject && !_.support.nonEnumArgs) {
_.pull(args, null);
deepEqual([args[0], args[1], args[2]], [1, [3], 5], message('pull'));
_.remove(args, function(value) { return typeof value == 'number'; });
ok(args.length === 1 && _.isEqual(args[0], [3]), message('remove'));
}
else {
skipTest(2);
}
});
test('should accept falsey primary arguments', 4, function() {
function message(methodName) {
return '`_.' + methodName + '` should accept falsey primary arguments';
}
deepEqual(_.difference(null, array), [], message('difference'));
deepEqual(_.intersection(null, array), [], message('intersection'));
deepEqual(_.union(null, array), array, message('union'));
deepEqual(_.xor(null, array), array, message('xor'));
});
test('should accept falsey secondary arguments', 3, function() {
function message(methodName) {
return '`_.' + methodName + '` should accept falsey secondary arguments';
}
deepEqual(_.difference(array, null), array, message('difference'));
deepEqual(_.intersection(array, null), [], message('intersection'));
deepEqual(_.union(array, null), array, message('union'));
});
}(1, null, [3], null, 5));
/*--------------------------------------------------------------------------*/
QUnit.module('"Strings" category methods');
(function() {
var stringMethods = [
'camelCase',
'capitalize',
'escape',
'escapeRegExp',
'kebabCase',
'pad',
'padLeft',
'padRight',
'repeat',
'snakeCase',
'trim',
'trimLeft',
'trimRight',
'trunc',
'unescape'
];
_.each(stringMethods, function(methodName) {
var func = _[methodName];
test('`_.' + methodName + '` should return an empty string when provided nullish or empty string values', 3, function() {
strictEqual(func(null), '');
strictEqual(func(undefined), '');
strictEqual(func(''), '');
});
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash methods');
(function() {
var allMethods = _.reject(_.functions(_).sort(), function(methodName) {
return _.startsWith(methodName, '_');
});
var checkFuncs = [
'after',
'ary',
'before',
'bind',
'curry',
'curryRight',
'debounce',
'defer',
'delay',
'memoize',
'negate',
'once',
'partial',
'partialRight',
'rearg',
'restParam',
'spread',
'throttle'
];
var rejectFalsey = [
'backflow',
'compose',
'flow',
'flowRight',
'tap',
'thru'
].concat(checkFuncs);
var returnArrays = [
'at',
'chunk',
'compact',
'difference',
'drop',
'filter',
'flatten',
'functions',
'initial',
'intersection',
'invoke',
'keys',
'map',
'pairs',
'pluck',
'pull',
'pullAt',
'range',
'reject',
'remove',
'rest',
'sample',
'shuffle',
'sortBy',
'sortByAll',
'sortByOrder',
'take',
'times',
'toArray',
'union',
'uniq',
'values',
'where',
'without',
'xor',
'zip'
];
var acceptFalsey = _.difference(allMethods, rejectFalsey);
test('should accept falsey arguments', 225, function() {
var emptyArrays = _.map(falsey, _.constant([])),
isExposed = '_' in root,
oldDash = root._;
_.each(acceptFalsey, function(methodName) {
var expected = emptyArrays,
func = _[methodName],
pass = true;
var actual = _.map(falsey, function(value, index) {
try {
return index ? func(value) : func();
} catch(e) {
pass = false;
}
});
if (methodName == 'noConflict') {
if (isExposed) {
root._ = oldDash;
} else {
delete root._;
}
}
else if (methodName == 'pull') {
expected = falsey;
}
if (_.includes(returnArrays, methodName) && methodName != 'sample') {
deepEqual(actual, expected, '_.' + methodName + ' returns an array');
}
ok(pass, '`_.' + methodName + '` accepts falsey arguments');
});
// Skip tests for missing methods of modularized builds.
_.each(['chain', 'noConflict', 'runInContext'], function(methodName) {
if (!_[methodName]) {
skipTest();
}
});
});
test('should return an array', 72, function() {
var array = [1, 2, 3];
_.each(returnArrays, function(methodName) {
var actual,
func = _[methodName];
switch (methodName) {
case 'invoke':
actual = func(array, 'toFixed');
break;
case 'sample':
actual = func(array, 1);
break;
default:
actual = func(array);
}
ok(_.isArray(actual), '_.' + methodName + ' returns an array');
var isPull = methodName == 'pull';
strictEqual(actual === array, isPull, '_.' + methodName + ' should ' + (isPull ? '' : 'not ') + 'return the provided array');
});
});
test('should throw an error for falsey arguments', 24, function() {
_.each(rejectFalsey, function(methodName) {
var expected = _.map(falsey, _.constant(true)),
func = _[methodName];
var actual = _.map(falsey, function(value, index) {
var pass = !index && /^(?:backflow|compose|flow(Right)?)$/.test(methodName);
try {
index ? func(value) : func();
} catch(e) {
pass = _.includes(checkFuncs, methodName)
? e.message == FUNC_ERROR_TEXT
: !pass;
}
return pass;
});
deepEqual(actual, expected, '`_.' + methodName + '` rejects falsey arguments');
});
});
test('should handle `null` `thisArg` arguments', 44, function() {
var expected = (function() { return this; }).call(null);
var funcs = [
'assign',
'clone',
'cloneDeep',
'countBy',
'dropWhile',
'dropRightWhile',
'every',
'filter',
'find',
'findIndex',
'findKey',
'findLast',
'findLastIndex',
'findLastKey',
'forEach',
'forEachRight',
'forIn',
'forInRight',
'forOwn',
'forOwnRight',
'groupBy',
'isEqual',
'map',
'mapValues',
'max',
'merge',
'min',
'omit',
'partition',
'pick',
'reduce',
'reduceRight',
'reject',
'remove',
'some',
'sortBy',
'sortedIndex',
'takeWhile',
'takeRightWhile',
'tap',
'times',
'transform',
'thru',
'uniq'
];
_.each(funcs, function(methodName) {
var actual,
array = ['a'],
callback = function() { actual = this; },
func = _[methodName],
message = '`_.' + methodName + '` handles `null` `thisArg` arguments';
if (func) {
if (_.startsWith(methodName, 'reduce') || methodName == 'transform') {
func(array, callback, 0, null);
} else if (_.includes(['assign', 'merge'], methodName)) {
func(array, array, callback, null);
} else if (_.includes(['isEqual', 'sortedIndex'], methodName)) {
func(array, 'a', callback, null);
} else if (methodName == 'times') {
func(1, callback, null);
} else {
func(array, callback, null);
}
strictEqual(actual, expected, message);
}
else {
skipTest();
}
});
});
test('should not contain minified method names (test production builds)', 1, function() {
var shortNames = ['at', 'eq', 'gt', 'lt'];
ok(_.every(_.functions(_), function(methodName) {
return methodName.length > 2 || _.includes(shortNames, methodName);
}));
});
}());
/*--------------------------------------------------------------------------*/
QUnit.config.asyncRetries = 10;
QUnit.config.hidepassed = true;
if (!document) {
QUnit.config.noglobals = true;
QUnit.load();
}
}.call(this));