"use strict"; var _ = lib("utils/helpers"); describe("Helpers", function() { describe("extend", function() { var extend = _.extend; var base = { fruits: ["apple"], vegetables: ["beet"], thing: null, otherThing: "hello!", data: [{ user: "barney" }, { user: "fred" }] }; var source = { fruits: ["banana"], vegetables: ["carrot"], thing: "hello!", otherThing: null, data: [{ age: 36 }, { age: 40 }] }; var expected = { data: [ { age: 36, user: "barney" }, { age: 40, user: "fred" } ], fruits: [ "apple", "banana" ], vegetables: [ "beet", "carrot" ], thing: "hello!", otherThing: null }; it("extends two objects", function() { var extended = extend(base, source); expect(extended).to.be.eql(expected); }); }); describe("isObject", function() { var fn = _.isObject; it("checks if a value is an Object", function() { var Klass = function() {}, instance = new Klass(); expect(fn({})).to.be.eql(true); expect(fn(instance)).to.be.eql(true); expect(fn([])).to.be.eql(false); expect(fn(function() {})).to.be.eql(false); expect(fn(10)).to.be.eql(false); expect(fn("")).to.be.eql(false); }); }); describe("isFunction", function() { var fn = _.isFunction; it("checks if a value is a Function", function() { expect(fn(function() {})).to.be.eql(true); expect(fn({})).to.be.eql(false); expect(fn([])).to.be.eql(false); expect(fn(10)).to.be.eql(false); expect(fn("")).to.be.eql(false); }); }); describe("isArray", function() { var fn = _.isArray; it("checks if a value is an Array", function() { expect(fn([])).to.be.eql(true); expect(fn(function() {})).to.be.eql(false); expect(fn({})).to.be.eql(false); expect(fn(10)).to.be.eql(false); expect(fn("")).to.be.eql(false); }); }); describe("isNumber", function() { var fn = _.isNumber; it("checks if a value is a Number", function() { expect(fn(10)).to.be.eql(true); expect(fn(function() {})).to.be.eql(false); expect(fn({})).to.be.eql(false); expect(fn([])).to.be.eql(false); expect(fn("")).to.be.eql(false); }); }); describe("isString", function() { var fn = _.isString; it("checks if a value is a String", function() { expect(fn("")).to.be.eql(true); expect(fn(10)).to.be.eql(false); expect(fn(function() {})).to.be.eql(false); expect(fn({})).to.be.eql(false); expect(fn([])).to.be.eql(false); }); }); describe("#pluck", function() { var object = { a: { item: "hello" }, b: { item: "world" } }, array = [ { item: "hello" }, { item: "world" } ]; it("plucks values from a collection", function() { expect(_.pluck(object, "item")).to.be.eql(["hello", "world"]); expect(_.pluck(array, "item")).to.be.eql(["hello", "world"]); }); }); describe("#map", function() { var object = { a: { item: "hello" }, b: { item: "world" } }, array = [ { item: "hello" }, { item: "world" } ]; var fn = function(value, key) { return [value, key]; }; it("runs a function over items in a collection", function() { expect(_.map(object, fn)).to.be.eql([ [{ item: "hello" }, "a"], [{ item: "world" }, "b"] ]); expect(_.map(array, fn)).to.be.eql([ [{ item: "hello" }, 0], [{ item: "world" }, 1] ]); }); it("defaults to the identity function", function() { expect(_.map(array)).to.be.eql(array); expect(_.map(object)).to.be.eql(array); }); }); describe("#invoke", function() { var array = [ { name: "bob", toString: function() { return "Hi from " + this.name; } }, { name: "dave", toString: function() { return "hello from " + this.name; } } ]; var object = { bob: { name: "bob", toString: function() { return "Hi from " + this.name; } }, dave: { name: "dave", toString: function() { return "hello from " + this.name; } } }; it("runs a instance function over items in a collection", function() { expect(_.invoke(object, "toString")).to.be.eql([ "Hi from bob", "hello from dave" ]); expect(_.invoke(array, "toString")).to.be.eql([ "Hi from bob", "hello from dave" ]); expect(_.invoke([1, 2, 3, 4, 5], Number.prototype.toString)).to.be.eql([ "1", "2", "3", "4", "5" ]); }); }); describe("#each", function() { var object = { a: { item: "hello" }, b: { item: "world" } }, array = [ { item: "hello" }, { item: "world" } ]; var fn = function(value, key) { return [value, key]; }; it("runs a function over items in a collection", function() { fn = spy(); _.map(object, fn); expect(fn).to.be.calledWith(object.a, "a"); expect(fn).to.be.calledWith(object.b, "b"); fn = spy(); _.map(array, fn); expect(fn).to.be.calledWith(array[0], 0); expect(fn).to.be.calledWith(array[1], 1); }); }); describe("#reduce", function() { var arr = [1, 2, 3, 4, 5, 6], obj = { a: 1, b: 2 }; function add(sum, n) { return sum + n; } it("reduces over a collection with the provided iteratee", function() { expect(_.reduce(arr, add, 0)).to.be.eql(21); expect(_.reduce(obj, add, 0)).to.be.eql(3); }); it("defaults to the first value for the accumulator", function() { var object = { a: { name: "hello" }, b: { name: "world" } }; expect(_.reduce(arr, add)).to.be.eql(21); expect( _.reduce(object, function(acc, val) { acc.name += " " + val.name; return acc; }) ).to.be.eql({ name: "hello world"}); }); it("supports providing a `this` value", function() { var self = { toString: function(y) { return y.toString(); } }; var fn = function(acc, val) { return acc + this.toString(val); }; expect(_.reduce(arr, fn, 1, self)).to.be.eql("123456"); }); }); describe("#arity", function() { it("creates a function that only takes a certain # of args", function() { var fn = spy(); var one = _.arity(fn, 1); one("one", "two", "three"); expect(fn).to.be.calledWith("one"); }); }); describe("#partial", function() { it("partially applies a function's arguments", function() { var fn = spy(); var one = _.partial(fn, "one", "two"); one("three"); expect(fn).to.be.calledWith("one", "two", "three"); }); }); describe("#partialRight", function() { it("partially applies arguments to the end of a fn call", function() { var fn = spy(); var one = _.partialRight(fn, "two", "three"); one("one"); expect(fn).to.be.calledWith("one", "two", "three"); }); }); describe("#includes", function() { it("checks if an array includes a value", function() { var fn = _.includes; var arr = [1, "2", 3]; expect(fn(arr, 1)).to.be.eql(true); expect(fn(arr, "2")).to.be.eql(true); expect(fn(arr, {})).to.be.eql(false); }); }); describe("#parallel", function() { var fn1, fn2, fn3, callback; beforeEach(function() { fn1 = stub(); fn2 = stub(); fn3 = stub(); callback = stub(); }); it("executes a set of functions in parallel", function() { _.parallel([fn1, fn2, fn3], callback); expect(fn1).to.be.called; expect(fn2).to.be.called; expect(fn3).to.be.called; expect(callback).to.not.be.called; fn1.yield(null, true); expect(callback).to.not.be.called; fn2.yield(null, true); expect(callback).to.not.be.called; fn3.yield(null, true); expect(callback).to.be.calledWith(null, [true, true, true]); }); it("stops immediately if there's an error", function() { _.parallel([fn1, fn2, fn3], callback); fn1.yield(true, null); expect(callback).to.be.calledWith(true); fn2.yields(null, true); fn3.yields(null, true); expect(callback).to.be.calledOnce; }); }); describe("#series", function() { var fn1, fn2, fn3, callback; beforeEach(function() { fn1 = stub(); fn2 = stub(); fn3 = stub(); callback = stub(); }); it("executes a set of functions in series", function() { _.series([fn1, fn2, fn3], callback); expect(fn1).to.be.called; expect(fn2).to.not.be.called; expect(fn3).to.not.be.called; expect(callback).to.not.be.called; fn1.yield(null, true); expect(fn2).to.be.called; expect(fn3).to.not.be.called; expect(callback).to.not.be.called; fn2.yield(null, true); expect(fn3).to.be.called; expect(callback).to.not.be.called; fn3.yield(null, true); expect(callback).to.be.calledWith(null, [true, true, true]); }); it("stops immediately if there's an error", function() { _.series([fn1, fn2, fn3], callback); fn1.yield(true, null); expect(fn2).to.not.be.called; expect(fn3).to.not.be.called; expect(callback).to.be.calledWith(true); }); }); });