test.js 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764
  1. ;(function(window, undefined) {
  2. 'use strict';
  3. /** Use a single load function */
  4. var load = typeof require == 'function' ? require : window.load;
  5. /** The `platform` object to check */
  6. var platform =
  7. window.platform ||
  8. load('../vendor/platform.js/platform.js') ||
  9. window.platform;
  10. /** The unit testing framework */
  11. var QUnit =
  12. window.QUnit || (
  13. window.setTimeout || (window.addEventListener = window.setTimeout = / /),
  14. window.QUnit = load('../vendor/qunit/qunit/qunit' + (platform.name == 'Narwhal' ? '-1.8.0' : '') + '.js') || window.QUnit,
  15. load('../vendor/qunit-clib/qunit-clib.js'),
  16. (window.addEventListener || 0).test && delete window.addEventListener,
  17. window.QUnit
  18. );
  19. /** The `lodash` function to test */
  20. var _ =
  21. window._ || (
  22. _ = load('../lodash.js') || window._,
  23. _._ || _
  24. );
  25. /** Used to pass falsey values to methods */
  26. var falsey = [
  27. ,
  28. '',
  29. 0,
  30. false,
  31. NaN,
  32. null,
  33. undefined
  34. ];
  35. /** Shortcut used to make object properties immutable */
  36. var freeze = Object.freeze;
  37. /** Shortcut used to convert array-like objects to arrays */
  38. var slice = [].slice;
  39. /** Used to check problem JScript properties (a.k.a. the [[DontEnum]] bug) */
  40. var shadowed = {
  41. 'constructor': 1,
  42. 'hasOwnProperty': 2,
  43. 'isPrototypeOf': 3,
  44. 'propertyIsEnumerable': 4,
  45. 'toLocaleString': 5,
  46. 'toString': 6,
  47. 'valueOf': 7
  48. };
  49. /** Used to check problem JScript properties too */
  50. var shadowedKeys = [
  51. 'constructor',
  52. 'hasOwnProperty',
  53. 'isPrototypeOf',
  54. 'propertyIsEnumerable',
  55. 'toLocaleString',
  56. 'toString',
  57. 'valueOf'
  58. ];
  59. /*--------------------------------------------------------------------------*/
  60. /**
  61. * Skips a given number of tests with a passing result.
  62. *
  63. * @private
  64. * @param {Number} [count=1] The number of tests to skip.
  65. */
  66. function skipTest(count) {
  67. count || (count = 1);
  68. while (count--) {
  69. ok(true, 'test skipped');
  70. }
  71. }
  72. /*--------------------------------------------------------------------------*/
  73. // add object from iframe
  74. (function() {
  75. if (!window.document) {
  76. return;
  77. }
  78. var body = document.body,
  79. iframe = document.createElement('iframe');
  80. iframe.frameBorder = iframe.height = iframe.width = 0;
  81. body.appendChild(iframe);
  82. var idoc = (idoc = iframe.contentDocument || iframe.contentWindow).document || idoc;
  83. idoc.write("<script>parent._._object = { 'a': 1, 'b': 2, 'c': 3 };<\/script>");
  84. idoc.close();
  85. }());
  86. /*--------------------------------------------------------------------------*/
  87. // explicitly call `QUnit.module()` instead of `module()`
  88. // in case we are in a CLI environment
  89. QUnit.module('lodash');
  90. (function() {
  91. // ensure this test is executed before any other template tests to avoid false positives
  92. test('should initialize `reEvaluateDelimiter` correctly (test with production build)', function() {
  93. var data = { 'a': [1, 2] },
  94. settings = _.templateSettings;
  95. _.templateSettings = { 'interpolate': /\{\{(.+?)\}\}/g };
  96. equal(_.template('{{ a.join(",") }}', data), '1,2');
  97. _.templateSettings = settings;
  98. });
  99. test('supports loading lodash.js as the "lodash" module', function() {
  100. if (window.document && window.require) {
  101. equal((lodashModule || {}).moduleName, 'lodash');
  102. } else {
  103. skipTest();
  104. }
  105. });
  106. test('supports loading lodash.js as the "underscore" module', function() {
  107. if (window.document && window.require) {
  108. equal((underscoreModule || {}).moduleName, 'underscore');
  109. } else {
  110. skipTest();
  111. }
  112. });
  113. test('avoids overwritten native methods', function() {
  114. if (window.document) {
  115. notDeepEqual(lodashBadShim.keys({ 'a': 1 }), []);
  116. } else {
  117. skipTest();
  118. }
  119. });
  120. }());
  121. /*--------------------------------------------------------------------------*/
  122. QUnit.module('lodash constructor');
  123. (function() {
  124. test('creates a new instance when called without the `new` operator', function() {
  125. ok(_() instanceof _);
  126. });
  127. test('should return passed LoDash instances', function() {
  128. var wrapped = _([]);
  129. equal(_(wrapped), wrapped);
  130. });
  131. }());
  132. /*--------------------------------------------------------------------------*/
  133. QUnit.module('lodash.bind');
  134. (function() {
  135. test('should correctly append array arguments to partially applied arguments (test in IE < 9)', function() {
  136. var args,
  137. bound = _.bind(function() { args = slice.call(arguments); }, {}, 'a');
  138. bound(['b'], 'c');
  139. deepEqual(args, ['a', ['b'], 'c']);
  140. });
  141. test('supports lazy bind', function() {
  142. var object = {
  143. 'name': 'moe',
  144. 'greet': function(greeting) {
  145. return greeting + ': ' + this.name;
  146. }
  147. };
  148. var func = _.bind(object, 'greet', 'hi');
  149. equal(func(), 'hi: moe');
  150. object.greet = function(greeting) {
  151. return greeting + ' ' + this.name + '!';
  152. };
  153. equal(func(), 'hi moe!');
  154. });
  155. }());
  156. /*--------------------------------------------------------------------------*/
  157. QUnit.module('lodash.clone');
  158. (function() {
  159. function Klass() { this.a = 1; }
  160. Klass.prototype = { 'b': 1 };
  161. var nonCloneable = {
  162. 'an arguments object': arguments,
  163. 'an element': window.document && document.body,
  164. 'a function': Klass,
  165. 'a Klass instance': new Klass
  166. };
  167. var objects = {
  168. 'an array': ['a', 'b', 'c', ''],
  169. 'an array-like-object': { '0': 'a', '1': 'b', '2': 'c', '3': '', 'length': 5 },
  170. 'boolean': false,
  171. 'boolean object': Object(false),
  172. 'an object': { 'a': 0, 'b': 1, 'c': 3 },
  173. 'an object with object values': { 'a': /a/, 'b': ['B'], 'c': { 'C': 1 } },
  174. 'an object from another document': _._object || {},
  175. 'null': null,
  176. 'a number': 3,
  177. 'a number object': Object(3),
  178. 'a regexp': /a/gim,
  179. 'a string': 'a',
  180. 'a string object': Object('a'),
  181. 'undefined': undefined
  182. };
  183. objects['an array'].length = 5;
  184. _.forOwn(objects, function(object, key) {
  185. test('should deep clone ' + key + ' correctly', function() {
  186. var clone = _.clone(object, true);
  187. ok(_.isEqual(object, clone));
  188. if (_.isObject(object)) {
  189. ok(clone !== object);
  190. } else {
  191. skipTest();
  192. }
  193. });
  194. });
  195. _.forOwn(nonCloneable, function(object, key) {
  196. test('should not clone ' + key, function() {
  197. ok(_.clone(object) === object);
  198. ok(_.clone(object, true) === object);
  199. });
  200. });
  201. test('should shallow clone when used as `callback` for `_.map`', function() {
  202. var expected = [{ 'a': [0] }, { 'b': [1] }],
  203. actual = _.map(expected, _.clone);
  204. ok(actual != expected && actual.a == expected.a && actual.b == expected.b);
  205. });
  206. test('should deep clone objects with circular references', function() {
  207. var object = {
  208. 'foo': { 'b': { 'foo': { 'c': { } } } },
  209. 'bar': { }
  210. };
  211. object.foo.b.foo.c = object;
  212. object.bar.b = object.foo.b;
  213. var clone = _.clone(object, true);
  214. ok(clone.bar.b === clone.foo.b && clone === clone.foo.b.foo.c && clone !== object);
  215. });
  216. test('should clone using Klass#clone', function() {
  217. var object = new Klass;
  218. Klass.prototype.clone = function() { return new Klass; };
  219. var clone = _.clone(object);
  220. ok(clone !== object && clone instanceof Klass);
  221. clone = _.clone(object, true);
  222. ok(clone !== object && clone instanceof Klass);
  223. delete Klass.prototype.clone;
  224. });
  225. test('should clone problem JScript properties (test in IE < 9)', function() {
  226. deepEqual(_.clone(shadowed), shadowed);
  227. ok(_.clone(shadowed) != shadowed);
  228. deepEqual(_.clone(shadowed, true), shadowed);
  229. ok(_.clone(shadowed, true) != shadowed);
  230. });
  231. }(1, 2, 3));
  232. /*--------------------------------------------------------------------------*/
  233. QUnit.module('lodash.contains');
  234. (function() {
  235. _.each({
  236. 'literal': 'abc',
  237. 'object': Object('abc')
  238. },
  239. function(collection, key) {
  240. test('should work with a string ' + key + ' for `collection`', function() {
  241. equal(_.contains(collection, 'bc'), true);
  242. equal(_.contains(collection, 'd'), false);
  243. });
  244. });
  245. }());
  246. /*--------------------------------------------------------------------------*/
  247. QUnit.module('lodash.countBy');
  248. (function() {
  249. test('should only add values to own, not inherited, properties', function() {
  250. var actual = _.countBy([4.2, 6.1, 6.4], function(num) {
  251. return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor';
  252. });
  253. equal(actual.constructor, 1);
  254. equal(actual.hasOwnProperty, 2);
  255. });
  256. }());
  257. /*--------------------------------------------------------------------------*/
  258. QUnit.module('lodash.debounce');
  259. (function() {
  260. asyncTest('subsequent debounced calls return the last `func` result', function() {
  261. var debounced = _.debounce(function(value) { return value; }, 100);
  262. debounced('x');
  263. setTimeout(function() {
  264. equal(debounced('y'), 'x');
  265. QUnit.start();
  266. }, 220);
  267. });
  268. test('subsequent "immediate" debounced calls return the last `func` result', function() {
  269. var debounced = _.debounce(function(value) { return value; }, 100, true),
  270. result = [debounced('x'), debounced('y')];
  271. deepEqual(result, ['x', 'x']);
  272. });
  273. }());
  274. /*--------------------------------------------------------------------------*/
  275. QUnit.module('lodash.difference');
  276. (function() {
  277. test('should work correctly when using `cachedContains`', function() {
  278. var array1 = _.range(27),
  279. array2 = array1.slice(),
  280. a = {},
  281. b = {},
  282. c = {};
  283. array1.push(a, b, c);
  284. array2.push(b, c, a);
  285. deepEqual(_.difference(array1, array2), []);
  286. });
  287. }());
  288. /*--------------------------------------------------------------------------*/
  289. QUnit.module('lodash.escape');
  290. (function() {
  291. test('should not escape the "/" character', function() {
  292. equal(_.escape('/'), '/');
  293. });
  294. test('should return an empty string when passed `null` or `undefined`', function() {
  295. equal(_.escape(null), '');
  296. equal(_.escape(undefined), '');
  297. });
  298. }());
  299. /*--------------------------------------------------------------------------*/
  300. QUnit.module('lodash.extend');
  301. (function() {
  302. test('should not error on `null` or `undefined` sources (test in IE < 9)', function() {
  303. try {
  304. deepEqual(_.extend({}, null, undefined, { 'a': 1 }), { 'a': 1 });
  305. } catch(e) {
  306. ok(false);
  307. }
  308. });
  309. test('skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', function() {
  310. function Foo() {}
  311. Foo.prototype.c = 3;
  312. Foo.a = 1;
  313. Foo.b = 2;
  314. var expected = { 'a': 1, 'b': 2 };
  315. deepEqual(_.extend({}, Foo), expected);
  316. Foo.prototype = { 'c': 3 };
  317. deepEqual(_.extend({}, Foo), expected);
  318. });
  319. }());
  320. /*--------------------------------------------------------------------------*/
  321. _.each(['bindAll', 'defaults', 'extend'], function(methodName) {
  322. var func = _[methodName];
  323. QUnit.module('lodash.' + methodName + ' strict mode checks');
  324. test('should not throw strict mode errors', function() {
  325. var object = { 'a': null, 'b': function(){} },
  326. pass = true;
  327. if (freeze) {
  328. freeze(object);
  329. try {
  330. if (methodName == 'bindAll') {
  331. func(object);
  332. } else {
  333. func(object, { 'a': 1 });
  334. }
  335. } catch(e) {
  336. pass = false;
  337. }
  338. ok(pass);
  339. }
  340. else {
  341. skipTest();
  342. }
  343. });
  344. });
  345. /*--------------------------------------------------------------------------*/
  346. QUnit.module('lodash.filter');
  347. (function() {
  348. test('should not modify the resulting value from within `callback`', function() {
  349. var actual = _.filter([0], function(value, index, array) {
  350. return (array[index] = 1);
  351. });
  352. deepEqual(actual, [0]);
  353. });
  354. }());
  355. /*--------------------------------------------------------------------------*/
  356. QUnit.module('lodash.find');
  357. (function() {
  358. var array = [1, 2, 3];
  359. test('should return found `value`', function() {
  360. equal(_.find(array, function(n) { return n > 2; }), 3);
  361. });
  362. test('should return `undefined` if `value` is not found', function() {
  363. equal(_.find(array, function(n) { return n == 4; }), undefined);
  364. });
  365. }());
  366. /*--------------------------------------------------------------------------*/
  367. QUnit.module('lodash.flatten');
  368. (function() {
  369. test('should treat sparse arrays as dense', function() {
  370. var array = [[1, 2, 3], Array(3)],
  371. expected = [1, 2, 3],
  372. actual1 = _.flatten(array),
  373. actual2 = _.flatten(array, true);
  374. expected.push(undefined, undefined, undefined);
  375. deepEqual(actual1, expected);
  376. ok('4' in actual1);
  377. deepEqual(actual2, expected);
  378. ok('4' in actual2);
  379. });
  380. }());
  381. /*--------------------------------------------------------------------------*/
  382. QUnit.module('lodash.forEach');
  383. (function() {
  384. test('returns the collection', function() {
  385. var collection = [1, 2, 3];
  386. equal(_.forEach(collection, Boolean), collection);
  387. });
  388. test('should treat array-like object with invalid `length` as a regular object', function() {
  389. var keys = [],
  390. object = { 'length': -1 };
  391. _.forEach(object, function(value, key) { keys.push(key); });
  392. deepEqual(keys, ['length']);
  393. keys = []; object.length = Math.pow(2, 32);
  394. _.forEach(object, function(value, key) { keys.push(key); });
  395. deepEqual(keys, ['length']);
  396. });
  397. _.each({
  398. 'literal': 'abc',
  399. 'object': Object('abc')
  400. },
  401. function(collection, key) {
  402. test('should work with a string ' + key + ' for `collection` (test in IE < 9)', function() {
  403. var args,
  404. values = [];
  405. _.forEach(collection, function(value) {
  406. args || (args = slice.call(arguments));
  407. values.push(value);
  408. });
  409. deepEqual(args, ['a', 0, collection]);
  410. deepEqual(values, ['a', 'b', 'c']);
  411. });
  412. });
  413. }());
  414. /*--------------------------------------------------------------------------*/
  415. QUnit.module('lodash.forIn');
  416. (function() {
  417. test('iterates over inherited properties', function() {
  418. function Dog(name) { this.name = name; }
  419. Dog.prototype.bark = function() { /* Woof, woof! */ };
  420. var keys = [];
  421. _.forIn(new Dog('Dagny'), function(value, key) { keys.push(key); });
  422. deepEqual(keys.sort(), ['bark', 'name']);
  423. });
  424. }());
  425. /*--------------------------------------------------------------------------*/
  426. QUnit.module('lodash.forOwn');
  427. (function() {
  428. test('iterates over the `length` property', function() {
  429. var keys = [],
  430. object = { '0': 'zero', '1': 'one', 'length': 2 };
  431. _.forOwn(object, function(value, key) { keys.push(key); });
  432. deepEqual(keys.sort(), ['0', '1', 'length']);
  433. });
  434. }());
  435. /*--------------------------------------------------------------------------*/
  436. _.each(['forEach', 'forIn', 'forOwn'], function(methodName) {
  437. var func = _[methodName];
  438. QUnit.module('lodash.' + methodName + ' iteration bugs');
  439. test('fixes the JScript [[DontEnum]] bug (test in IE < 9)', function() {
  440. var keys = [];
  441. func(shadowed, function(value, key) { keys.push(key); });
  442. deepEqual(keys.sort(), shadowedKeys);
  443. });
  444. test('skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', function() {
  445. function Foo() {}
  446. Foo.prototype.a = 1;
  447. var keys = [];
  448. function callback(value, key) { keys.push(key); }
  449. func(Foo, callback);
  450. deepEqual(keys, []);
  451. keys.length = 0;
  452. Foo.prototype = { 'a': 1 };
  453. func(Foo, callback);
  454. deepEqual(keys, []);
  455. });
  456. });
  457. _.each(['forEach', 'forIn', 'forOwn'], function(methodName) {
  458. var func = _[methodName];
  459. QUnit.module('lodash.' + methodName + ' can exit early');
  460. test('can exit early when iterating arrays', function() {
  461. var array = [1, 2, 3],
  462. values = [];
  463. func(array, function(value) { values.push(value); return false; });
  464. deepEqual(values, [1]);
  465. });
  466. test('can exit early when iterating objects', function() {
  467. var object = { 'a': 1, 'b': 2, 'c': 3 },
  468. values = [];
  469. func(object, function(value) { values.push(value); return false; });
  470. equal(values.length, 1);
  471. });
  472. });
  473. /*--------------------------------------------------------------------------*/
  474. QUnit.module('lodash.groupBy');
  475. (function() {
  476. test('supports the `thisArg` argument', function() {
  477. var actual = _.groupBy([4.2, 6.1, 6.4], function(num) {
  478. return this.floor(num);
  479. }, Math);
  480. deepEqual(actual, { '4': [4.2], '6': [6.1, 6.4] });
  481. });
  482. test('should only add values to own, not inherited, properties', function() {
  483. var actual = _.groupBy([4.2, 6.1, 6.4], function(num) {
  484. return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor';
  485. });
  486. deepEqual(actual.constructor, [4.2]);
  487. deepEqual(actual.hasOwnProperty, [6.1, 6.4]);
  488. });
  489. test('should work with an object for `collection`', function() {
  490. var actual = _.groupBy({ 'a': 4.2, 'b': 6.1, 'c': 6.4 }, function(num) {
  491. return Math.floor(num);
  492. });
  493. deepEqual(actual, { '4': [4.2], '6': [6.1, 6.4] });
  494. });
  495. }());
  496. /*--------------------------------------------------------------------------*/
  497. QUnit.module('lodash.indexOf');
  498. (function() {
  499. var array = [1, 2, 3, 1, 2, 3];
  500. test('should work with a positive `fromIndex`', function() {
  501. equal(_.indexOf(array, 1, 2), 3);
  502. });
  503. test('should work with `fromIndex` >= `array.length`', function() {
  504. equal(_.indexOf(array, 1, 6), -1);
  505. equal(_.indexOf(array, undefined, 6), -1);
  506. equal(_.indexOf(array, 1, 8), -1);
  507. equal(_.indexOf(array, undefined, 8), -1);
  508. });
  509. test('should work with a negative `fromIndex`', function() {
  510. equal(_.indexOf(array, 2, -3), 4);
  511. });
  512. test('should work with a negative `fromIndex` <= `-array.length`', function() {
  513. equal(_.indexOf(array, 1, -6), 0);
  514. equal(_.indexOf(array, 2, -8), 1);
  515. });
  516. test('should ignore non-number `fromIndex` values', function() {
  517. equal(_.indexOf([1, 2, 3], 1, '1'), 0);
  518. });
  519. test('should work with `isSorted`', function() {
  520. equal(_.indexOf([1, 2, 3], 1, true), 0);
  521. });
  522. }());
  523. /*--------------------------------------------------------------------------*/
  524. QUnit.module('lodash.initial');
  525. (function() {
  526. test('returns an empty collection for `n` of `0`', function() {
  527. var array = [1, 2, 3];
  528. deepEqual(_.initial(array, 0), []);
  529. });
  530. }());
  531. /*--------------------------------------------------------------------------*/
  532. QUnit.module('lodash.invoke');
  533. (function() {
  534. test('should work with an object for `collection`', function() {
  535. var object = { 'a': 1, 'b': 2, 'c': 3 };
  536. deepEqual(_.invoke(object, 'toFixed', 1), ['1.0', '2.0', '3.0']);
  537. });
  538. }());
  539. /*--------------------------------------------------------------------------*/
  540. QUnit.module('lodash.isElement');
  541. (function() {
  542. test('should use strict equality in its duck type check', function() {
  543. var element = window.document ? document.body : { 'nodeType': 1 };
  544. equal(_.isElement(element), true);
  545. equal(_.isElement({ 'nodeType': new Number(1) }), false);
  546. equal(_.isElement({ 'nodeType': true }), false);
  547. equal(_.isElement({ 'nodeType': [1] }), false);
  548. equal(_.isElement({ 'nodeType': '1' }), false);
  549. equal(_.isElement({ 'nodeType': '001' }), false);
  550. });
  551. }());
  552. /*--------------------------------------------------------------------------*/
  553. QUnit.module('lodash.isEmpty');
  554. (function() {
  555. var args = arguments;
  556. test('fixes the JScript [[DontEnum]] bug (test in IE < 9)', function() {
  557. equal(_.isEmpty(shadowed), false);
  558. });
  559. test('skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', function() {
  560. function Foo() {}
  561. Foo.prototype.a = 1;
  562. equal(_.isEmpty(Foo), true);
  563. Foo.prototype = { 'a': 1 };
  564. equal(_.isEmpty(Foo), true);
  565. });
  566. test('should work with an object that has a `length` property', function() {
  567. equal(_.isEmpty({ 'length': 0 }), false);
  568. });
  569. test('should work with jQuery/MooTools DOM query collections', function() {
  570. function Foo(elements) { Array.prototype.push.apply(this, elements); }
  571. Foo.prototype = { 'length': 0, 'splice': Array.prototype.splice };
  572. equal(_.isEmpty(new Foo([])), true);
  573. });
  574. test('should work with `arguments` objects (test in IE < 9)', function() {
  575. equal(_.isEmpty(args), false);
  576. });
  577. }(1, 2, 3));
  578. /*--------------------------------------------------------------------------*/
  579. QUnit.module('lodash.isEqual');
  580. (function() {
  581. test('should work with `arguments` objects (test in IE < 9)', function() {
  582. var args1 = (function() { return arguments; }(1, 2, 3)),
  583. args2 = (function() { return arguments; }(1, 2, 3)),
  584. args3 = (function() { return arguments; }(1, 2));
  585. equal(_.isEqual(args1, args2), true);
  586. equal(_.isEqual(args1, args3), false);
  587. });
  588. test('should respect custom `isEqual` result despite objects strict equaling each other', function() {
  589. var object = { 'isEqual': function() { return false; } };
  590. equal(_.isEqual(object, object), false);
  591. });
  592. test('should use custom `isEqual` methods on primitives', function() {
  593. Boolean.prototype.isEqual = function() { return true; };
  594. equal(_.isEqual(true, false), true);
  595. delete Boolean.prototype.isEqual;
  596. });
  597. test('fixes the JScript [[DontEnum]] bug (test in IE < 9)', function() {
  598. equal(_.isEqual(shadowed, {}), false);
  599. });
  600. test('should return `true` for like-objects from different documents', function() {
  601. // ensure `_._object` is assigned (unassigned in Opera 10.00)
  602. if (_._object) {
  603. var object = { 'a': 1, 'b': 2, 'c': 3 };
  604. equal(_.isEqual(object, _._object), true);
  605. }
  606. else {
  607. skipTest();
  608. }
  609. });
  610. test('should return `false` when comparing values with circular references to unlike values', function() {
  611. var array1 = ['a', null, 'c'],
  612. array2 = ['a', [], 'c'],
  613. object1 = { 'a': 1, 'b': null, 'c': 3 },
  614. object2 = { 'a': 1, 'b': {}, 'c': 3 };
  615. array1[1] = array1;
  616. equal(_.isEqual(array1, array2), false);
  617. object1.b = object1;
  618. equal(_.isEqual(object1, object2), false);
  619. });
  620. }());
  621. /*--------------------------------------------------------------------------*/
  622. QUnit.module('lodash.isObject');
  623. (function() {
  624. test('should avoid V8 bug #2291', function() {
  625. // trigger V8 bug
  626. // http://code.google.com/p/v8/issues/detail?id=2291
  627. var obj = {},
  628. str = 'foo';
  629. // 1: Useless comparison statement, this is half the trigger
  630. obj == obj;
  631. // 2: Initial check with object, this is the other half of the trigger
  632. _.isObject(obj);
  633. equal(_.isObject(str), false);
  634. });
  635. }());
  636. /*--------------------------------------------------------------------------*/
  637. QUnit.module('lodash.isNaN');
  638. (function() {
  639. test('returns `true` for `new Number(NaN)`', function() {
  640. equal(_.isNaN(new Number(NaN)), true)
  641. });
  642. }());
  643. /*--------------------------------------------------------------------------*/
  644. _.each([
  645. 'isArguments',
  646. 'isArray',
  647. 'isBoolean',
  648. 'isDate',
  649. 'isElement',
  650. 'isEmpty',
  651. 'isEqual',
  652. 'isFinite',
  653. 'isFunction',
  654. 'isNaN',
  655. 'isNull',
  656. 'isNumber',
  657. 'isObject',
  658. 'isRegExp',
  659. 'isString',
  660. 'isUndefined'
  661. ], function(methodName) {
  662. var func = _[methodName];
  663. QUnit.module('lodash.' + methodName + ' result');
  664. test('should return a boolean', function() {
  665. var expected = 'boolean';
  666. equal(typeof func(arguments), expected);
  667. equal(typeof func([]), expected);
  668. equal(typeof func(true), expected);
  669. equal(typeof func(false), expected);
  670. equal(typeof func(new Date), expected);
  671. equal(typeof func(window.document && document.body), expected);
  672. equal(typeof func({}), expected);
  673. equal(typeof func(undefined), expected);
  674. equal(typeof func(Infinity), expected);
  675. equal(typeof func(_), expected);
  676. equal(typeof func(NaN), expected);
  677. equal(typeof func(null), expected);
  678. equal(typeof func(0), expected);
  679. equal(typeof func({ 'a': 1 }), expected);
  680. equal(typeof func('a'), expected);
  681. });
  682. });
  683. /*--------------------------------------------------------------------------*/
  684. QUnit.module('lodash.keys');
  685. (function() {
  686. var args = arguments;
  687. test('should work with `arguments` objects (test in IE < 9)', function() {
  688. deepEqual(_.keys(args), ['0', '1', '2']);
  689. });
  690. test('fixes the JScript [[DontEnum]] bug (test in IE < 9)', function() {
  691. function Foo() {}
  692. Foo.prototype.a = 1;
  693. deepEqual(_.keys(Foo.prototype), ['a']);
  694. deepEqual(_.keys(shadowed).sort(), shadowedKeys);
  695. });
  696. test('skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', function() {
  697. function Foo() {}
  698. Foo.prototype.c = 3;
  699. Foo.a = 1;
  700. Foo.b = 2;
  701. var expected = ['a', 'b'];
  702. deepEqual(_.keys(Foo), expected);
  703. Foo.prototype = { 'c': 3 };
  704. deepEqual(_.keys(Foo), expected);
  705. });
  706. }(1, 2, 3));
  707. /*--------------------------------------------------------------------------*/
  708. QUnit.module('lodash.lastIndexOf');
  709. (function() {
  710. var array = [1, 2, 3, 1, 2, 3];
  711. test('should work with a positive `fromIndex`', function() {
  712. equal(_.lastIndexOf(array, 1, 2), 0);
  713. });
  714. test('should work with `fromIndex` >= `array.length`', function() {
  715. equal(_.lastIndexOf(array, undefined, 6), -1);
  716. equal(_.lastIndexOf(array, 1, 6), 3);
  717. equal(_.lastIndexOf(array, undefined, 8), -1);
  718. equal(_.lastIndexOf(array, 1, 8), 3);
  719. });
  720. test('should work with a negative `fromIndex`', function() {
  721. equal(_.lastIndexOf(array, 2, -3), 1);
  722. });
  723. test('should work with a negative `fromIndex` <= `-array.length`', function() {
  724. equal(_.lastIndexOf(array, 1, -6), 0);
  725. equal(_.lastIndexOf(array, 2, -8), -1);
  726. });
  727. test('should ignore non-number `fromIndex` values', function() {
  728. equal(_.lastIndexOf([1, 2, 3], 3, '1'), 2);
  729. equal(_.lastIndexOf([1, 2, 3], 3, true), 2);
  730. });
  731. }());
  732. /*--------------------------------------------------------------------------*/
  733. QUnit.module('lodash.merge');
  734. (function() {
  735. var args = arguments;
  736. test('should merge `source` into the destination object', function() {
  737. var stooges = [
  738. { 'name': 'moe' },
  739. { 'name': 'larry' }
  740. ];
  741. var ages = [
  742. { 'age': 40 },
  743. { 'age': 50 }
  744. ];
  745. var heights = [
  746. { 'height': '5\'4"' },
  747. { 'height': '5\'5"' },
  748. ];
  749. var expected = [
  750. { 'name': 'moe', 'age': 40, 'height': '5\'4"' },
  751. { 'name': 'larry', 'age': 50, 'height': '5\'5"' }
  752. ];
  753. deepEqual(_.merge(stooges, ages, heights), expected);
  754. });
  755. test('should merge sources containing circular references', function() {
  756. var object = {
  757. 'foo': { 'a': 1 },
  758. 'bar': { 'a': 2 }
  759. };
  760. var source = {
  761. 'foo': { 'b': { 'foo': { 'c': { } } } },
  762. 'bar': { }
  763. };
  764. source.foo.b.foo.c = source;
  765. source.bar.b = source.foo.b;
  766. var actual = _.merge(object, source);
  767. ok(actual.bar.b === actual.foo.b && actual.foo.b.foo.c === actual.foo.b.foo.c.foo.b.foo.c);
  768. });
  769. test('should merge problem JScript properties (test in IE < 9)', function() {
  770. var object = [{
  771. 'constructor': 1,
  772. 'hasOwnProperty': 2,
  773. 'isPrototypeOf': 3
  774. }];
  775. var source = [{
  776. 'propertyIsEnumerable': 4,
  777. 'toLocaleString': 5,
  778. 'toString': 6,
  779. 'valueOf': 7
  780. }];
  781. deepEqual(_.merge(object, source), [shadowed]);
  782. });
  783. test('should not treat `arguments` objects as plain objects', function() {
  784. var object = {
  785. 'args': args
  786. };
  787. var source = {
  788. 'args': { '3': 4 }
  789. };
  790. var actual = _.merge(object, source);
  791. equal(_.isArguments(actual.args), false);
  792. });
  793. test('should work with four arguments', function() {
  794. var expected = { 'a': 4 };
  795. deepEqual(_.merge({ 'a': 1 }, { 'a': 2 }, { 'a': 3 }, expected), expected);
  796. });
  797. }(1, 2, 3));
  798. /*--------------------------------------------------------------------------*/
  799. QUnit.module('lodash.omit');
  800. (function() {
  801. var object = { 'a': 1, 'b': 2 },
  802. actual = { 'b': 2 };
  803. test('should accept individual property names', function() {
  804. deepEqual(_.omit(object, 'a'), actual);
  805. });
  806. test('should accept an array of property names', function() {
  807. deepEqual(_.omit(object, ['a', 'c']), actual);
  808. });
  809. test('should accept mixes of individual and arrays of property names', function() {
  810. deepEqual(_.omit(object, ['a'], 'c'), actual);
  811. });
  812. test('should iterate over inherited properties', function() {
  813. function Foo() {}
  814. Foo.prototype = object;
  815. deepEqual(_.omit(new Foo, 'a'), actual);
  816. });
  817. test('should work with a `callback` argument', function() {
  818. var actual = _.omit(object, function(value) {
  819. return value == 1;
  820. });
  821. deepEqual(actual, { 'b': 2 });
  822. });
  823. test('should pass the correct `callback` arguments', function() {
  824. var args,
  825. lastKey = _.keys(object).pop();
  826. var expected = lastKey == 'b'
  827. ? [1, 'a', object]
  828. : [2, 'b', object];
  829. _.omit(object, function() {
  830. args || (args = slice.call(arguments));
  831. });
  832. deepEqual(args, expected);
  833. });
  834. test('should correct set the `this` binding', function() {
  835. var actual = _.omit(object, function(value) {
  836. return value == this.a;
  837. }, { 'a': 1 });
  838. deepEqual(actual, { 'b': 2 });
  839. });
  840. }());
  841. /*--------------------------------------------------------------------------*/
  842. QUnit.module('lodash.partial');
  843. (function() {
  844. test('partially applies an argument, without additional arguments', function() {
  845. var arg = 'catnip',
  846. func = function(x) { return x; };
  847. equal(_.partial(func, arg)(), arg);
  848. });
  849. test('partially applies an argument, with additional arguments', function() {
  850. var arg1 = 'catnip',
  851. arg2 = 'cheese',
  852. func = function(x, y) { return [x, y]; };
  853. deepEqual(_.partial(func, arg1)(arg2), [arg1, arg2]);
  854. });
  855. test('works without partially applying arguments, without additional arguments', function() {
  856. var func = function() { return arguments.length; };
  857. equal(_.partial(func)(), 0);
  858. });
  859. test('works without partially applying arguments, with additional arguments', function() {
  860. var arg = 'catnip',
  861. func = function(x) { return x; };
  862. equal(_.partial(func)(arg), arg);
  863. });
  864. test('should not alter the `this` binding of either function', function() {
  865. var object = { 'cat': 'nip' },
  866. func = function() { return this.cat; };
  867. equal(_.partial(_.bind(func, object))(), object.cat);
  868. equal(_.bind(_.partial(func), object)(), object.cat);
  869. });
  870. }());
  871. /*--------------------------------------------------------------------------*/
  872. QUnit.module('lodash.pick');
  873. (function() {
  874. var object = { 'a': 1, 'b': 2 };
  875. test('should iterate over inherited properties', function() {
  876. function Foo() {}
  877. Foo.prototype = object;
  878. deepEqual(_.pick(new Foo, 'b'), { 'b': 2 });
  879. });
  880. test('should work with a `callback` argument', function() {
  881. var actual = _.pick(object, function(value) {
  882. return value == 2;
  883. });
  884. deepEqual(actual, { 'b': 2 });
  885. });
  886. test('should pass the correct `callback` arguments', function() {
  887. var args,
  888. lastKey = _.keys(object).pop();
  889. var expected = lastKey == 'b'
  890. ? [1, 'a', object]
  891. : [2, 'b', object];
  892. _.pick(object, function() {
  893. args || (args = slice.call(arguments));
  894. });
  895. deepEqual(args, expected);
  896. });
  897. test('should correct set the `this` binding', function() {
  898. var actual = _.pick(object, function(value) {
  899. return value == this.b;
  900. }, { 'b': 2 });
  901. deepEqual(actual, { 'b': 2 });
  902. });
  903. }());
  904. /*--------------------------------------------------------------------------*/
  905. QUnit.module('lodash.pluck');
  906. (function() {
  907. test('should work with an object for `collection`', function() {
  908. var object = { 'a': [1], 'b': [1, 2], 'c': [1, 2, 3] };
  909. deepEqual(_.pluck(object, 'length'), [1, 2, 3]);
  910. });
  911. }());
  912. /*--------------------------------------------------------------------------*/
  913. QUnit.module('lodash.random');
  914. (function() {
  915. test('should work like `Math.random` if no arguments are passed', function() {
  916. var actual = _.random();
  917. ok(actual >= 0 && actual < 1);
  918. });
  919. test('supports not passing a `max` argument', function() {
  920. var actual = _.random(5),
  921. start = new Date;
  922. while ((new Date - start) < 50 && actual == 5) {
  923. actual = _.random(5);
  924. }
  925. ok(actual != 5);
  926. });
  927. }());
  928. /*--------------------------------------------------------------------------*/
  929. QUnit.module('lodash.range');
  930. (function() {
  931. var func = _.range;
  932. test('should treat falsey `start` arguments as `0`', function() {
  933. _.each(falsey, function(value, index) {
  934. if (index) {
  935. deepEqual(_.range(value), []);
  936. deepEqual(_.range(value, 1), [0]);
  937. } else {
  938. deepEqual(_.range(), []);
  939. }
  940. });
  941. });
  942. test('should coerce arguments to numbers', function() {
  943. var actual = [func('0',1), func('1'), func(0, 1, '1')];
  944. deepEqual(actual, [[0], [0], [0]]);
  945. });
  946. }());
  947. /*--------------------------------------------------------------------------*/
  948. QUnit.module('lodash.reduce');
  949. (function() {
  950. _.each({
  951. 'literal': 'abc',
  952. 'object': Object('abc')
  953. },
  954. function(collection, key) {
  955. test('should work with a string ' + key + ' for `collection` (test in IE < 9)', function() {
  956. var args;
  957. var actual = _.reduce(collection, function(accumulator, value) {
  958. args || (args = slice.call(arguments));
  959. return accumulator + value;
  960. });
  961. deepEqual(args, ['a', 'b', 1, collection]);
  962. equal(actual, 'abc');
  963. });
  964. });
  965. }());
  966. /*--------------------------------------------------------------------------*/
  967. QUnit.module('lodash.reduceRight');
  968. (function() {
  969. test('should pass the correct `callback` arguments when iterating an object', function() {
  970. var args,
  971. object = { 'a': 1, 'b': 2 },
  972. lastKey = _.keys(object).pop();
  973. var expected = lastKey == 'b'
  974. ? [2, 1, 'a', object]
  975. : [1, 2, 'b', object];
  976. _.reduceRight(object, function() {
  977. args || (args = slice.call(arguments));
  978. });
  979. deepEqual(args, expected);
  980. });
  981. test('should treat array-like object with invalid `length` as a regular object', function() {
  982. var args,
  983. object = { 'a': 1, 'length': -1 },
  984. lastKey = _.keys(object).pop();
  985. var expected = lastKey == 'length'
  986. ? [-1, 1, 'a', object]
  987. : [1, -1, 'length', object];
  988. _.reduceRight(object, function() {
  989. args || (args = slice.call(arguments));
  990. });
  991. deepEqual(args, expected);
  992. });
  993. _.each({
  994. 'literal': 'abc',
  995. 'object': Object('abc')
  996. },
  997. function(collection, key) {
  998. test('should work with a string ' + key + ' for `collection` (test in IE < 9)', function() {
  999. var args;
  1000. var actual = _.reduceRight(collection, function(accumulator, value) {
  1001. args || (args = slice.call(arguments));
  1002. return accumulator + value;
  1003. });
  1004. deepEqual(args, ['c', 'b', 1, collection]);
  1005. equal(actual, 'cba');
  1006. });
  1007. });
  1008. }());
  1009. /*--------------------------------------------------------------------------*/
  1010. QUnit.module('lodash.size');
  1011. (function() {
  1012. var args = arguments;
  1013. test('should detect the size of a string value', function() {
  1014. equal(_.size('abc'), 3);
  1015. });
  1016. test('should allow a falsey `object` argument', function() {
  1017. _.each(falsey, function(index, value) {
  1018. try {
  1019. var actual = index ? _.size(value) : _.size();
  1020. } catch(e) { }
  1021. equal(actual, 0);
  1022. })
  1023. });
  1024. test('should work with jQuery/MooTools DOM query collections', function() {
  1025. function Foo(elements) { Array.prototype.push.apply(this, elements); }
  1026. Foo.prototype = { 'length': 0, 'splice': Array.prototype.splice };
  1027. equal(_.size(new Foo([1, 2, 3])), 3);
  1028. });
  1029. test('should work with `arguments` objects (test in IE < 9)', function() {
  1030. equal(_.size(args), 3);
  1031. });
  1032. test('fixes the JScript [[DontEnum]] bug (test in IE < 9)', function() {
  1033. equal(_.size(shadowed), 7);
  1034. });
  1035. }(1, 2, 3));
  1036. /*--------------------------------------------------------------------------*/
  1037. QUnit.module('lodash.sortBy');
  1038. (function() {
  1039. test('should perform a stable sort (test in IE > 8, Opera, and V8)', function() {
  1040. function Pair(x, y) {
  1041. this.x = x;
  1042. this.y = y;
  1043. }
  1044. var collection = [
  1045. new Pair(1, 1), new Pair(1, 2),
  1046. new Pair(1, 3), new Pair(1, 4),
  1047. new Pair(1, 5), new Pair(1, 6),
  1048. new Pair(2, 1), new Pair(2, 2),
  1049. new Pair(2, 3), new Pair(2, 4),
  1050. new Pair(2, 5), new Pair(2, 6),
  1051. new Pair(undefined, 1), new Pair(undefined, 2),
  1052. new Pair(undefined, 3), new Pair(undefined, 4),
  1053. new Pair(undefined, 5), new Pair(undefined, 6)
  1054. ];
  1055. var actual = _.sortBy(collection, function(pair) {
  1056. return pair.x;
  1057. });
  1058. deepEqual(actual, collection);
  1059. });
  1060. test('supports the `thisArg` argument', function() {
  1061. var actual = _.sortBy([1, 2, 3], function(num) {
  1062. return this.sin(num);
  1063. }, Math);
  1064. deepEqual(actual, [3, 1, 2]);
  1065. });
  1066. test('should work with an object for `collection`', function() {
  1067. var actual = _.sortBy({ 'a': 1, 'b': 2, 'c': 3 }, function(num) {
  1068. return Math.sin(num);
  1069. });
  1070. deepEqual(actual, [3, 1, 2]);
  1071. });
  1072. }());
  1073. /*--------------------------------------------------------------------------*/
  1074. QUnit.module('lodash.sortedIndex');
  1075. (function() {
  1076. test('supports the `thisArg` argument', function() {
  1077. var actual = _.sortedIndex([1, 2, 3], 4, function(num) {
  1078. return this.sin(num);
  1079. }, Math);
  1080. equal(actual, 0);
  1081. });
  1082. test('supports arrays with lengths larger than `Math.pow(2, 31) - 1`', function() {
  1083. var length = Math.pow(2, 32) - 1,
  1084. index = length - 1,
  1085. array = Array(length),
  1086. steps = 0;
  1087. array[index] = index;
  1088. _.sortedIndex(array, index, function() { steps++; });
  1089. equal(steps, 33);
  1090. });
  1091. }());
  1092. /*--------------------------------------------------------------------------*/
  1093. QUnit.module('lodash.template');
  1094. (function() {
  1095. test('supports recursive calls', function() {
  1096. var compiled = _.template('<%= a %><% a = _.template(c, obj) %><%= a %>'),
  1097. data = { 'a': 'A', 'b': 'B', 'c': '<%= b %>' };
  1098. equal(compiled(data), 'AB');
  1099. });
  1100. test('should not augment the `options` object', function() {
  1101. var options = {};
  1102. _.template('', null, options);
  1103. deepEqual(options, {});
  1104. });
  1105. test('should provide the template source when a SyntaxError occurs', function() {
  1106. try {
  1107. _.template('<% if x %>');
  1108. } catch(e) {
  1109. var source = e.source;
  1110. }
  1111. ok((source + '').indexOf('__p') > -1);
  1112. });
  1113. test('should work with complex "interpolate" delimiters', function() {
  1114. _.each({
  1115. '<%= a + b %>': '3',
  1116. '<%= b - a %>': '1',
  1117. '<%= a = b %>': '2',
  1118. '<%= !a %>': 'false',
  1119. '<%= ~a %>': '-2',
  1120. '<%= a * b %>': '2',
  1121. '<%= a / b %>': '0.5',
  1122. '<%= a % b %>': '1',
  1123. '<%= a >> b %>': '0',
  1124. '<%= a << b %>': '4',
  1125. '<%= a & b %>': '0',
  1126. '<%= a ^ b %>': '3',
  1127. '<%= a | b %>': '3',
  1128. '<%= {}.toString.call(0) %>': '[object Number]',
  1129. '<%= a.toFixed(2) %>': '1.00',
  1130. '<%= obj["a"] %>': '1',
  1131. '<%= delete a %>': 'true',
  1132. '<%= "a" in obj %>': 'true',
  1133. '<%= obj instanceof Object %>': 'true',
  1134. '<%= new Boolean %>': 'false',
  1135. '<%= typeof a %>': 'number',
  1136. '<%= void a %>': ''
  1137. }, function(value, key) {
  1138. var compiled = _.template(key),
  1139. data = { 'a': 1, 'b': 2 };
  1140. equal(compiled(data), value, key);
  1141. });
  1142. });
  1143. test('should allow referencing variables declared in "evaluate" delimiters from other delimiters', function() {
  1144. var compiled = _.template('<% var b = a; %><%= b.value %>'),
  1145. data = { 'a': { 'value': 1 } };
  1146. equal(compiled(data), '1');
  1147. });
  1148. test('should work when passing `options.variable`', function() {
  1149. var compiled = _.template(
  1150. '<% _.forEach( data.a, function( value ) { %>' +
  1151. '<%= value.valueOf() %>' +
  1152. '<% }) %>', null, { 'variable': 'data' }
  1153. );
  1154. var data = { 'a': [1, 2, 3] };
  1155. try {
  1156. equal(compiled(data), '123');
  1157. } catch(e) {
  1158. ok(false);
  1159. }
  1160. });
  1161. test('should not error with IE conditional comments enabled (test with development build)', function() {
  1162. var compiled = _.template(''),
  1163. pass = true;
  1164. /*@cc_on @*/
  1165. try {
  1166. compiled();
  1167. } catch(e) {
  1168. pass = false;
  1169. }
  1170. ok(pass);
  1171. });
  1172. test('should tokenize delimiters correctly', function() {
  1173. var compiled = _.template('<span class="icon-<%= type %>2"></span>');
  1174. equal(compiled({ 'type': 1 }), '<span class="icon-12"></span>');
  1175. });
  1176. test('should work with "interpolate" delimiters containing ternary operators', function() {
  1177. var compiled = _.template('<%= value ? value : "b" %>');
  1178. equal(compiled({ 'value': 'a' }), 'a');
  1179. });
  1180. }());
  1181. /*--------------------------------------------------------------------------*/
  1182. QUnit.module('lodash.throttle');
  1183. (function() {
  1184. test('subsequent calls should return the result of the first call', function() {
  1185. var throttled = _.throttle(function(value) { return value; }, 100),
  1186. result = [throttled('x'), throttled('y')];
  1187. deepEqual(result, ['x', 'x']);
  1188. });
  1189. test('supports calls in a loop', function() {
  1190. var counter = 0,
  1191. throttled = _.throttle(function() { counter++; }, 100),
  1192. start = new Date,
  1193. limit = 220;
  1194. while ((new Date - start) < limit) {
  1195. throttled();
  1196. }
  1197. ok(counter > 1);
  1198. });
  1199. asyncTest('supports recursive calls', function() {
  1200. var counter = 0;
  1201. var throttled = _.throttle(function() {
  1202. counter++;
  1203. if (counter < 4) {
  1204. throttled();
  1205. }
  1206. }, 100);
  1207. setTimeout(function() {
  1208. ok(counter > 1);
  1209. QUnit.start();
  1210. }, 220);
  1211. throttled();
  1212. });
  1213. }());
  1214. /*--------------------------------------------------------------------------*/
  1215. QUnit.module('lodash.toArray');
  1216. (function() {
  1217. var args = arguments;
  1218. _.each({
  1219. 'an array': ['a', 'b', 'c'],
  1220. 'a string': Object('abc')
  1221. }, function(collection, key) {
  1222. test('should call custom `toArray` method of ' + key, function() {
  1223. collection.toArray = function() { return [3, 2, 1]; };
  1224. deepEqual(_.toArray(collection), [3, 2, 1]);
  1225. });
  1226. });
  1227. test('should treat array-like objects like arrays', function() {
  1228. var object = { '0': 'a', '1': 'b', '2': 'c', 'length': 3 };
  1229. deepEqual(_.toArray(object), ['a', 'b', 'c']);
  1230. deepEqual(_.toArray(args), [1, 2, 3]);
  1231. });
  1232. test('should treat array-like object with invalid `length` as a regular object', function() {
  1233. var object = { 'length': -1 };
  1234. deepEqual(_.toArray(object), [-1]);
  1235. });
  1236. test('should work with a string for `collection` (test in Opera < 10.52)', function() {
  1237. deepEqual(_.toArray('abc'), ['a', 'b', 'c']);
  1238. deepEqual(_.toArray(Object('abc')), ['a', 'b', 'c']);
  1239. });
  1240. }(1, 2, 3));
  1241. /*--------------------------------------------------------------------------*/
  1242. QUnit.module('lodash.unescape');
  1243. (function() {
  1244. var escaped = '&lt;h1&gt;Moe&#x27;s famous &quot;death by chocolate&quot; brownies &amp; cake&lt;\/h1&gt;',
  1245. unescaped = '<h1>Moe\'s famous "death by chocolate" brownies & cake<\/h1>';
  1246. test('should unescape entities in the correct order', function() {
  1247. equal(_.unescape('&amp;lt;'), '&lt;');
  1248. });
  1249. test('should unescape the proper entities', function() {
  1250. equal(_.unescape(escaped), unescaped);
  1251. });
  1252. test('should unescape the same characters escaped by `_.escape`', function() {
  1253. equal(_.unescape(_.escape(unescaped)), unescaped);
  1254. });
  1255. test('should return an empty string when passed `null` or `undefined`', function() {
  1256. equal(_.unescape(null), '');
  1257. equal(_.unescape(undefined), '');
  1258. });
  1259. }());
  1260. /*--------------------------------------------------------------------------*/
  1261. QUnit.module('lodash.uniq');
  1262. (function() {
  1263. test('supports the `thisArg` argument', function() {
  1264. var actual = _.uniq([1, 2, 1.5, 3, 2.5], function(num) {
  1265. return this.floor(num);
  1266. }, Math);
  1267. deepEqual(actual, [1, 2, 3]);
  1268. });
  1269. }());
  1270. /*--------------------------------------------------------------------------*/
  1271. QUnit.module('lodash.where');
  1272. (function() {
  1273. var array = [
  1274. { 'a': 1 },
  1275. { 'a': 1 },
  1276. { 'a': 1, 'b': 2 },
  1277. { 'a': 2, 'b': 2 },
  1278. { 'a': 3 }
  1279. ];
  1280. test('should filter by properties', function() {
  1281. deepEqual(_.where(array, { 'a': 1 }), [{ 'a': 1 }, { 'a': 1 }, { 'a': 1, 'b': 2 }]);
  1282. deepEqual(_.where(array, { 'a': 2 }), [{ 'a': 2, 'b': 2 }]);
  1283. deepEqual(_.where(array, { 'a': 3 }), [{ 'a': 3 }]);
  1284. deepEqual(_.where(array, { 'b': 1 }), []);
  1285. deepEqual(_.where(array, { 'b': 2 }), [{ 'a': 1, 'b': 2 }, { 'a': 2, 'b': 2 }]);
  1286. deepEqual(_.where(array, { 'a': 1, 'b': 2 }), [{ 'a': 1, 'b': 2 }]);
  1287. });
  1288. test('should filter by inherited properties', function() {
  1289. function Foo() {}
  1290. Foo.prototype = { 'b': 2 };
  1291. var properties = new Foo;
  1292. properties.a = 1;
  1293. deepEqual(_.where(array, properties), [{ 'a': 1, 'b': 2 }]);
  1294. });
  1295. test('should filter by problem JScript properties (test in IE < 9)', function() {
  1296. var collection = [shadowed];
  1297. deepEqual(_.where(collection, shadowed), [shadowed]);
  1298. });
  1299. test('should work with an object for `collection`', function() {
  1300. var collection = {
  1301. 'x': { 'a': 1 },
  1302. 'y': { 'a': 3 },
  1303. 'z': { 'a': 1, 'b': 2 }
  1304. };
  1305. deepEqual(_.where(collection, { 'a': 1 }), [{ 'a': 1 }, { 'a': 1, 'b': 2 }]);
  1306. });
  1307. }());
  1308. /*--------------------------------------------------------------------------*/
  1309. QUnit.module('lodash(...).shift');
  1310. (function() {
  1311. test('should remove the value at index `0` when length is `0` (test in IE 8 compatibility mode)', function() {
  1312. var wrapped = _({ '0': 1, 'length': 1 });
  1313. wrapped.shift();
  1314. deepEqual(wrapped.keys(), ['length']);
  1315. equal(wrapped.first(), undefined);
  1316. });
  1317. }());
  1318. /*--------------------------------------------------------------------------*/
  1319. QUnit.module('lodash(...).splice');
  1320. (function() {
  1321. test('should remove the value at index `0` when length is `0` (test in IE < 9, and in compatibility mode for IE9)', function() {
  1322. var wrapped = _({ '0': 1, 'length': 1 });
  1323. wrapped.splice(0, 1);
  1324. deepEqual(wrapped.keys(), ['length']);
  1325. equal(wrapped.first(), undefined);
  1326. });
  1327. }());
  1328. /*--------------------------------------------------------------------------*/
  1329. QUnit.module('lodash methods');
  1330. (function() {
  1331. test('should allow falsey arguments', function() {
  1332. var funcs = _.without.apply(_, [_.functions(_)].concat([
  1333. '_',
  1334. '_iteratorTemplate',
  1335. '_shimKeys',
  1336. 'after',
  1337. 'bind',
  1338. 'bindAll',
  1339. 'compose',
  1340. 'debounce',
  1341. 'defer',
  1342. 'delay',
  1343. 'functions',
  1344. 'memoize',
  1345. 'once',
  1346. 'partial',
  1347. 'tap',
  1348. 'throttle',
  1349. 'wrap'
  1350. ]));
  1351. _.each(funcs, function(methodName) {
  1352. var func = _[methodName],
  1353. pass = true;
  1354. _.each(falsey, function(value, index) {
  1355. try {
  1356. index ? func(value) : func();
  1357. } catch(e) {
  1358. pass = false;
  1359. }
  1360. });
  1361. ok(pass, methodName + ' allows falsey arguments');
  1362. });
  1363. });
  1364. }());
  1365. /*--------------------------------------------------------------------------*/
  1366. // explicitly call `QUnit.start()` for Narwhal, Rhino, and RingoJS
  1367. if (!window.document) {
  1368. QUnit.start();
  1369. }
  1370. }(typeof global == 'object' && global || this));