3 /** Used as a safe reference for `undefined` in pre-ES5 environments. */
6 /** Used as the size to cover large array optimizations. */
7 var LARGE_ARRAY_SIZE = 200;
9 /** Used as a reference to the global object. */
10 var root = (typeof global == 'object' && global) || this;
12 /** Used for native method references. */
13 var arrayProto = Array.prototype;
15 /** Method and object shortcuts. */
16 var phantom = root.phantom,
17 amd = root.define && define.amd,
18 document = !phantom && root.document,
20 slice = arrayProto.slice,
21 WeakMap = root.WeakMap;
23 /*--------------------------------------------------------------------------*/
25 /** Use a single "load" function. */
26 var load = (!amd && typeof require == 'function')
30 /** The unit testing framework. */
31 var QUnit = root.QUnit || (root.QUnit = (
32 QUnit = load('../node_modules/qunitjs/qunit/qunit.js') || root.QUnit,
33 QUnit = QUnit.QUnit || QUnit
36 /** Load stable Lodash and QUnit Extras. */
37 var _ = root._ || load('../lodash.js');
39 _ = _.runInContext(root);
41 var QUnitExtras = load('../node_modules/qunit-extras/qunit-extras.js');
43 QUnitExtras.runInContext(root);
46 var convert = root.fp || load('../lib/fp/convert.js'),
47 mapping = root.mapping || load('../lib/fp/mapping.js'),
48 fp = convert(_.runInContext());
50 /*--------------------------------------------------------------------------*/
53 * Skips a given number of tests with a passing result.
56 * @param {Object} assert The QUnit assert object.
57 * @param {number} [count=1] The number of tests to skip.
59 function skipTest(assert, count) {
62 assert.ok(true, 'test skipped');
66 /*--------------------------------------------------------------------------*/
68 QUnit.module('method aliases');
71 QUnit.test('should have correct aliases', function(assert) {
74 var actual = _.transform(mapping.aliasMap, function(result, aliases, methodName) {
75 var func = fp[methodName];
76 _.each(aliases, function(alias) {
77 result.push([alias, fp[alias] === func]);
81 assert.deepEqual(_.reject(actual, 1), []);
85 /*--------------------------------------------------------------------------*/
87 QUnit.module('method ary caps');
90 QUnit.test('should have a cap of 1', function(assert) {
94 'curry', 'iteratee', 'memoize', 'over', 'overEvery', 'overSome',
95 'method', 'methodOf', 'restParam', 'runInContext'
98 var exceptions = funcMethods.concat('mixin', 'template'),
99 expected = _.map(mapping.aryMethodMap[1], _.constant(true));
101 var actual = _.map(mapping.aryMethodMap[1], function(methodName) {
102 var arg = _.includes(funcMethods, methodName) ? _.noop : 1,
103 result = _.attempt(function() { return fp[methodName](arg); });
105 if (_.includes(exceptions, methodName)
106 ? typeof result == 'function'
107 : typeof result != 'function'
111 console.log(methodName, result);
115 assert.deepEqual(actual, expected);
118 QUnit.test('should have a cap of 2', function(assert) {
122 'after', 'ary', 'before', 'bind', 'bindKey', 'cloneDeepWith', 'cloneWith',
123 'curryN', 'debounce', 'delay', 'overArgs', 'rearg', 'throttle', 'wrap'
126 var exceptions = _.difference(funcMethods.concat('matchesProperty'), ['cloneDeepWith', 'cloneWith', 'delay']),
127 expected = _.map(mapping.aryMethodMap[2], _.constant(true));
129 var actual = _.map(mapping.aryMethodMap[2], function(methodName) {
130 var args = _.includes(funcMethods, methodName) ? [methodName == 'curryN' ? 1 : _.noop, _.noop] : [1, []],
131 result = _.attempt(function() { return fp[methodName](args[0])(args[1]); });
133 if (_.includes(exceptions, methodName)
134 ? typeof result == 'function'
135 : typeof result != 'function'
139 console.log(methodName, result);
143 assert.deepEqual(actual, expected);
146 QUnit.test('should have a cap of 3', function(assert) {
150 'assignWith', 'extendWith', 'isEqualWith', 'isMatchWith', 'omitBy',
151 'pickBy', 'reduce', 'reduceRight', 'transform', 'zipWith'
154 var expected = _.map(mapping.aryMethodMap[3], _.constant(true));
156 var actual = _.map(mapping.aryMethodMap[3], function(methodName) {
157 var args = _.includes(funcMethods, methodName) ? [_.noop, 0, 1] : [0, 1, []],
158 result = _.attempt(function() { return fp[methodName](args[0])(args[1])(args[2]); });
160 if (typeof result != 'function') {
163 console.log(methodName, result);
167 assert.deepEqual(actual, expected);
171 /*--------------------------------------------------------------------------*/
173 QUnit.module('methods that use `indexOf`');
176 QUnit.test('should work with `fp.indexOf`', function(assert) {
179 var array = ['a', 'b', 'c'],
180 other = ['b', 'b', 'd'],
181 object = { 'a': 1, 'b': 2, 'c': 2 },
182 actual = fp.difference(array, other);
184 assert.deepEqual(actual, ['a', 'c'], 'fp.difference');
186 actual = fp.includes('b', array);
187 assert.strictEqual(actual, true, 'fp.includes');
189 actual = fp.intersection(other, array);
190 assert.deepEqual(actual, ['b'], 'fp.intersection');
192 actual = fp.omit(other, object);
193 assert.deepEqual(actual, { 'a': 1, 'c': 2 }, 'fp.omit');
195 actual = fp.union(other, array);
196 assert.deepEqual(actual, ['a', 'b', 'c', 'd'], 'fp.union');
198 actual = fp.uniq(other);
199 assert.deepEqual(actual, ['b', 'd'], 'fp.uniq');
201 actual = fp.uniqBy(_.identity, other);
202 assert.deepEqual(actual, ['b', 'd'], 'fp.uniqBy');
204 actual = fp.without('b', array);
205 assert.deepEqual(actual, ['a', 'c'], 'fp.without');
207 actual = fp.xor(other, array);
208 assert.deepEqual(actual, ['a', 'c', 'd'], 'fp.xor');
210 actual = fp.pull('b', array);
211 assert.deepEqual(actual, ['a', 'c'], 'fp.pull');
215 /*--------------------------------------------------------------------------*/
217 QUnit.module('cherry-picked methods');
220 QUnit.test('should provide the correct `iteratee` arguments', function(assert) {
226 object = { 'a': 1, 'b': 2 },
227 isFIFO = _.keys(object)[0] == 'a',
228 map = convert('map', _.map),
229 reduce = convert('reduce', _.reduce);
232 args || (args = slice.call(arguments));
235 assert.deepEqual(args, [1]);
239 args || (args = slice.call(arguments));
242 assert.deepEqual(args, isFIFO ? [1] : [2]);
246 args || (args = slice.call(arguments));
249 assert.deepEqual(args, [0, 1]);
253 args || (args = slice.call(arguments));
256 assert.deepEqual(args, isFIFO ? [0, 1] : [0, 2]);
263 QUnit.test('should not support shortcut fusion', function(assert) {
267 var array = fp.range(0, LARGE_ARRAY_SIZE),
271 var iteratee = function(value) {
273 return value * value;
276 var predicate = function(value) {
278 return value % 2 == 0;
281 var map1 = convert('map', _.map),
282 filter1 = convert('filter', _.filter),
283 take1 = convert('take', _.take);
285 var filter2 = filter1(predicate),
286 map2 = map1(iteratee),
289 var combined = fp.flow(map2, filter2, fp.compact, take2);
291 assert.deepEqual(combined(array), [4, 16]);
292 assert.strictEqual(filterCount, 200, 'filterCount');
293 assert.strictEqual(mapCount, 200, 'mapCount');
301 /*--------------------------------------------------------------------------*/
303 QUnit.module('curry methods');
305 _.each(['curry', 'curryRight'], function(methodName) {
306 var func = fp[methodName];
308 QUnit.test('`_.' + methodName + '` should only accept a `func` param', function(assert) {
311 assert.raises(function() { func(1, _.noop); }, TypeError);
315 /*--------------------------------------------------------------------------*/
317 QUnit.module('curryN methods');
319 _.each(['curryN', 'curryRightN'], function(methodName) {
320 var func = fp[methodName];
322 QUnit.test('`_.' + methodName + '` accept an `arity` param', function(assert) {
325 var actual = func(1, function(a, b) { return [a, b]; })('a');
326 assert.deepEqual(actual, ['a', undefined]);
330 /*--------------------------------------------------------------------------*/
332 QUnit.module('fp.difference');
335 QUnit.test('should return the elements of the first array not included in the second array', function(assert) {
338 assert.deepEqual(fp.difference([1, 2])([2, 3]), [1]);
342 /*--------------------------------------------------------------------------*/
344 QUnit.module('fp.fill');
347 QUnit.test('should have an argument order of `start`, `end`, then `value`', function(assert) {
350 var array = [1, 2, 3];
351 assert.deepEqual(fp.fill(1)(2)('*')(array), [1, '*', 3]);
355 /*--------------------------------------------------------------------------*/
357 QUnit.module('fp.flow and fp.flowRight');
359 _.each(['flow', 'flowRight'], function(methodName, index) {
360 var func = fp[methodName],
361 isFlow = methodName == 'flow';
363 QUnit.test('`fp.' + methodName + '` should support shortcut fusion', function(assert) {
368 array = fp.range(0, LARGE_ARRAY_SIZE);
370 var iteratee = function(value) {
372 return value * value;
375 var predicate = function(value) {
377 return value % 2 == 0;
380 var filter = fp.filter(predicate),
381 map = fp.map(iteratee),
384 _.times(2, function(index) {
385 var combined = isFlow
386 ? func(map, filter, fp.compact, take)
387 : func(take, fp.compact, filter, map);
389 filterCount = mapCount = 0;
391 if (WeakMap && WeakMap.name) {
392 assert.deepEqual(combined(array), [4, 16]);
393 assert.strictEqual(filterCount, 5, 'filterCount');
394 assert.strictEqual(mapCount, 5, 'mapCount');
403 /*--------------------------------------------------------------------------*/
405 QUnit.module('fp.inRange');
408 QUnit.test('should have an argument order of `start`, `end`, then `value`', function(assert) {
411 assert.strictEqual(fp.inRange(2)(4)(3), true);
415 /*--------------------------------------------------------------------------*/
417 QUnit.module('fp.iteratee');
420 QUnit.test('should return a iteratee with capped params', function(assert) {
423 var func = fp.iteratee(function(a, b, c) { return [a, b, c]; }, undefined, 3);
424 assert.deepEqual(func(1, 2, 3), [1, undefined, undefined]);
427 QUnit.test('should convert by name', function(assert) {
431 var iteratee = convert('iteratee', _.iteratee),
432 func = iteratee(function(a, b, c) { return [a, b, c]; }, undefined, 3);
434 assert.deepEqual(func(1, 2, 3), [1, undefined, undefined]);
442 /*--------------------------------------------------------------------------*/
444 QUnit.module('fp.maxBy and fp.minBy');
446 _.each(['maxBy', 'minBy'], function(methodName, index) {
447 var array = [1, 2, 3],
448 func = fp[methodName],
451 QUnit.test('`fp.' + methodName + '` should work with an `iteratee` argument', function(assert) {
454 var actual = func(function(num) {
458 assert.strictEqual(actual, isMax ? 1 : 3);
461 QUnit.test('`fp.' + methodName + '` should provide the correct `iteratee` arguments', function(assert) {
467 args || (args = slice.call(arguments));
470 assert.deepEqual(args, [1]);
474 /*--------------------------------------------------------------------------*/
476 QUnit.module('fp.mixin');
479 var source = { 'a': _.noop };
481 QUnit.test('should mixin static methods but not prototype methods', function(assert) {
486 assert.strictEqual(typeof fp.a, 'function');
487 assert.notOk('a' in fp.prototype);
490 delete fp.prototype.a;
493 QUnit.test('should not assign inherited `source` methods', function(assert) {
497 Foo.prototype.a = _.noop;
500 assert.notOk('a' in fp);
501 assert.notOk('a' in fp.prototype);
504 delete fp.prototype.a;
507 QUnit.test('should not remove existing prototype methods', function(assert) {
511 each2 = fp.prototype.each;
513 fp.mixin({ 'each': source.a });
515 assert.strictEqual(fp.each, source.a);
516 assert.strictEqual(fp.prototype.each, each2);
519 fp.prototype.each = each2;
522 QUnit.test('should not export to the global when `source` is not an object', function(assert) {
525 var props = _.without(_.keys(_), '_');
527 _.times(2, function(index) {
528 fp.mixin.apply(fp, index ? [1] : []);
530 assert.ok(_.every(props, function(key) {
531 return root[key] !== fp[key];
534 _.each(props, function(key) {
535 if (root[key] === fp[key]) {
542 QUnit.test('should convert by name', function(assert) {
546 var object = { 'mixin': convert('mixin', _.mixin) };
549 Foo.mixin = object.mixin;
552 assert.strictEqual(typeof Foo.a, 'function');
553 assert.notOk('a' in Foo.prototype);
555 object.mixin(source);
556 assert.strictEqual(typeof object.a, 'function');
564 /*--------------------------------------------------------------------------*/
566 QUnit.module('fp.random');
569 var array = Array(1000);
571 QUnit.test('should support a `min` and `max` argument', function(assert) {
577 assert.ok(_.some(array, function() {
578 var result = fp.random(min)(max);
579 return result >= min && result <= max;
584 /*--------------------------------------------------------------------------*/
586 QUnit.module('fp.range');
589 QUnit.test('should have an argument order of `start` then `end`', function(assert) {
592 assert.deepEqual(fp.range(1)(4), [1, 2, 3]);
596 /*--------------------------------------------------------------------------*/
598 QUnit.module('reduce methods');
600 _.each(['reduce', 'reduceRight'], function(methodName) {
601 var func = fp[methodName],
602 isReduce = methodName == 'reduce';
604 QUnit.test('`_.' + methodName + '` should provide the correct `iteratee` arguments when iterating an array', function(assert) {
611 args || (args = slice.call(arguments));
614 assert.deepEqual(args, isReduce ? [0, 1] : [0, 3]);
617 QUnit.test('`_.' + methodName + '` should provide the correct `iteratee` arguments when iterating an object', function(assert) {
621 object = { 'a': 1, 'b': 2 },
622 isFIFO = _.keys(object)[0] == 'a';
624 var expected = isFIFO
625 ? (isReduce ? [0, 1] : [0, 2])
626 : (isReduce ? [0, 2] : [0, 1]);
629 args || (args = slice.call(arguments));
632 assert.deepEqual(args, expected);
636 /*--------------------------------------------------------------------------*/
638 QUnit.module('fp.runInContext');
641 QUnit.test('should return a converted lodash instance', function(assert) {
644 assert.strictEqual(typeof fp.runInContext({}).curryN, 'function');
647 QUnit.test('should convert by name', function(assert) {
651 var runInContext = convert('runInContext', _.runInContext);
652 assert.strictEqual(typeof runInContext({}).curryN, 'function');
660 /*--------------------------------------------------------------------------*/
662 QUnit.module('fp.uniqBy');
665 var objects = [{ 'a': 2 }, { 'a': 3 }, { 'a': 1 }, { 'a': 2 }, { 'a': 3 }, { 'a': 1 }];
667 QUnit.test('should work with an `iteratee` argument', function(assert) {
670 var expected = objects.slice(0, 3);
672 var actual = fp.uniqBy(function(object) {
676 assert.deepEqual(actual, expected);
679 QUnit.test('should provide the correct `iteratee` arguments', function(assert) {
684 fp.uniqBy(function() {
685 args || (args = slice.call(arguments));
688 assert.deepEqual(args, [objects[0]]);
692 /*--------------------------------------------------------------------------*/
694 QUnit.module('fp.zip');
697 QUnit.test('should zip together two arrays', function(assert) {
700 assert.deepEqual(fp.zip([1, 2], [3, 4]), [[1, 3], [2, 4]]);
704 /*--------------------------------------------------------------------------*/
706 QUnit.module('fp.zipObject');
709 QUnit.test('should zip together key/value arrays into an object', function(assert) {
712 assert.deepEqual(fp.zipObject(['a', 'b'], [1, 2]), { 'a': 1, 'b': 2 });
716 /*--------------------------------------------------------------------------*/
718 QUnit.module('mutation methods');
721 var array = [1, 2, 3],
723 deepObject = { 'a': { 'b': 2, 'c': 3 } };
725 QUnit.test('should not mutate values', function(assert) {
729 Foo.prototype = { 'b': 2 };
731 var value = _.clone(object),
732 actual = fp.assign({ 'b': 2 }, value);
734 assert.deepEqual(value, object, 'fp.assign');
735 assert.deepEqual(actual, { 'a': 1, 'b': 2 }, 'fp.assign');
737 value = _.clone(object);
738 actual = fp.assignWith(function(objValue, srcValue) {
740 }, { 'b': 2 }, value);
742 assert.deepEqual(value, object, 'fp.assignWith');
743 assert.deepEqual(actual, { 'a': 1, 'b': 2 }, 'fp.assignWith');
745 value = _.clone(object);
746 actual = fp.defaults({ 'a': 2, 'b': 2 }, value);
748 assert.deepEqual(value, object, 'fp.defaults');
749 assert.deepEqual(actual, { 'a': 1, 'b': 2 }, 'fp.defaults');
751 value = _.clone(object);
752 value.b = { 'c': 1 };
753 actual = fp.defaultsDeep({ 'b': { 'c': 2, 'd': 2 } }, value);
755 assert.deepEqual(value, { 'a': 1, 'b': { 'c': 1 } } , 'fp.defaultsDeep');
756 assert.deepEqual(actual, { 'a': 1, 'b': { 'c': 1, 'd': 2 } }, 'fp.defaultsDeep');
758 value = _.clone(object);
759 actual = fp.extend(new Foo, value);
761 assert.deepEqual(value, object, 'fp.extend');
762 assert.deepEqual(actual, { 'a': 1, 'b': 2 }, 'fp.extend');
764 value = _.clone(object);
765 actual = fp.extendWith(function(objValue, srcValue) {
769 assert.deepEqual(value, object, 'fp.extendWith');
770 assert.deepEqual(actual, { 'a': 1, 'b': 2 }, 'fp.extendWith');
772 value = _.clone(array);
773 actual = fp.fill(1, 2, '*', value);
775 assert.deepEqual(value, array, 'fp.fill');
776 assert.deepEqual(actual, [1, '*', 3], 'fp.fill');
778 value = { 'a': { 'b': 2 } };
779 actual = fp.merge({ 'a': { 'c': 3 } }, value);
781 assert.deepEqual(value, { 'a': { 'b': 2 } }, 'fp.merge');
782 assert.deepEqual(actual, { 'a': { 'b': 2, 'c': 3 } }, 'fp.merge');
784 value = { 'a': [1] };
785 actual = fp.mergeWith(function(objValue, srcValue) {
786 if (_.isArray(objValue)) {
787 return objValue.concat(srcValue);
789 }, { 'a': [2, 3] }, value);
791 assert.deepEqual(value, { 'a': [1] }, 'fp.mergeWith');
792 assert.deepEqual(actual, { 'a': [1, 2, 3] }, 'fp.mergeWith');
794 value = _.clone(array);
795 actual = fp.pull(2, value);
797 assert.deepEqual(value, array, 'fp.pull');
798 assert.deepEqual(actual, [1, 3], 'fp.pull');
800 value = _.clone(array);
801 actual = fp.pullAll([1, 3], value);
803 assert.deepEqual(value, array, 'fp.pullAll');
804 assert.deepEqual(actual, [2], 'fp.pullAll');
806 value = _.clone(array);
807 actual = fp.pullAt([0, 2], value);
809 assert.deepEqual(value, array, 'fp.pullAt');
810 assert.deepEqual(actual, [2], 'fp.pullAt');
812 value = _.clone(array);
813 actual = fp.remove(function(value) {
817 assert.deepEqual(value, array, 'fp.remove');
818 assert.deepEqual(actual, [1, 3], 'fp.remove');
820 value = _.clone(array);
821 actual = fp.reverse(value);
823 assert.deepEqual(value, array, 'fp.reverse');
824 assert.deepEqual(actual, [3, 2, 1], 'fp.reverse');
826 value = _.cloneDeep(deepObject);
827 actual = fp.set(3, 'a.b', value);
829 assert.deepEqual(value, deepObject, 'fp.set');
830 assert.deepEqual(actual, { 'a': { 'b': 3, 'c': 3 } }, 'fp.set');
832 value = _.cloneDeep(deepObject);
833 actual = fp.setWith(Object, 4, 'd.e', value);
835 assert.deepEqual(value, deepObject, 'fp.setWith');
836 assert.deepEqual(actual, { 'a': { 'b': 2, 'c': 3 }, 'd': { 'e': 4 } }, 'fp.setWith');
840 /*--------------------------------------------------------------------------*/
842 QUnit.module('with methods');
845 var array = [1, 2, 3],
848 QUnit.test('should provide the correct `customizer` arguments', function(assert) {
852 value = _.clone(object);
854 var actual = fp.assignWith(function(objValue, srcValue) {
855 args || (args = _.map(arguments, _.cloneDeep));
857 }, { 'b': 2 }, value);
859 assert.deepEqual(args, [undefined, 2, 'b', { 'a': 1 }, { 'b': 2 }], 'fp.assignWith');
862 value = _.clone(object);
863 actual = fp.extendWith(function(objValue, srcValue) {
864 args || (args = _.map(arguments, _.cloneDeep));
866 }, { 'b': 2 }, value);
868 assert.deepEqual(args, [undefined, 2, 'b', { 'a': 1 }, { 'b': 2 }], 'fp.extendWith');
870 var stack = { '__data__': { 'array': [], 'map': null } },
871 expected = [[1], [2, 3], 'a', { 'a': [ 1 ] }, { 'a': [2, 3] }, stack];
874 value = { 'a': [1] };
875 actual = fp.mergeWith(function(objValue, srcValue) {
876 args || (args = _.map(arguments, _.cloneDeep));
877 if (_.isArray(objValue)) {
878 return objValue.concat(srcValue);
880 }, { 'a': [2, 3] }, value);
882 args[5] = _.omitBy(args[5], _.isFunction);
883 assert.deepEqual(args, expected, 'fp.mergeWith');
887 /*--------------------------------------------------------------------------*/
889 QUnit.config.asyncRetries = 10;
890 QUnit.config.hidepassed = true;
893 QUnit.config.noglobals = true;