UNPKG

lodash-fantasy

Version:

Fantasy Land compatible types built with lodash.

707 lines (539 loc) 24.2 kB
"use strict"; // Third Party const chai = require("chai"); const first = require("lodash/fp/first"); const map = require("lodash/fp/map"); const nth = require("lodash/fp/nth"); const promiseChai = require("chai-as-promised"); const include = require("include")(__dirname); const sinon = require("sinon"); const sinonChai = require("sinon-chai"); // Third Party Setup chai.use(promiseChai); chai.use(sinonChai); const expect = chai.expect; const second = nth(1); // Project const Applicative = require("./laws/Applicative")(expect); const Apply = require("./laws/Apply")(expect); const Chain = require("./laws/Chain")(expect); const Maybe = include("data/Maybe"); const Extend = require("./laws/Extend")(expect); const Functor = require("./laws/Functor")(expect); const Either = include("data/Either"); const Monad = require("./laws/Monad")(expect); const Setoid = require("./laws/Setoid")(expect); const Validation = include("data/Validation"); // Project Aliases const Left = Either.Left; const Right = Either.Right; describe("Either", () => { const testMessage = "Test error"; const testValue = true; describe(".all", () => { describe("with a Left", () => { const testMessage1 = `${testMessage} 1`; const testMessage2 = `${testMessage} 2`; const testEithers = [Left.from(testMessage1), Left.from(testMessage2), Right.from(testValue)]; const expectedResult = testEithers[0]; let actualResult = null; before(() => actualResult = Either.all(testEithers)); it("should return an instance of Left", () => expect(actualResult).to.be.instanceof(Left)); it("should return the first Left", () => expect(actualResult).to.equal(expectedResult)); }); describe("rights", () => { const testEithers = [Right.from(testValue), Right.from(!testValue)]; const expectedResult = Right.from([testValue, !testValue]); let actualResult = null; before(() => actualResult = Either.all(testEithers)); it("should return an instance of Right", () => expect(actualResult).to.be.instanceof(Right)); it("should return a singular Right of all values", () => expect(actualResult).to.eql(expectedResult)); }); }); describe(".any", () => { describe("nothings", () => { const testEithers = [Left.from(testMessage), Left.from(testMessage)]; const expectedResult = first(testEithers); let actualResult = null; before(() => actualResult = Either.any(testEithers)); it("should return the first Left", () => expect(actualResult).to.equal(expectedResult)); }); describe("justs", () => { const testEithers = [Left.from(testMessage), Right.from(testValue), Right.from(!testValue)]; const expectedResult = second(testEithers); let actualResult = null; before(() => actualResult = Either.any(testEithers)); it("should return the first Right", () => expect(actualResult).to.equal(expectedResult)); }); }); describe(".each", () => { const testCollection = [ Left.from(testMessage), Right.from(testValue) ]; describe("early terminate", () => { it("should iterate over each item in the collection until Left is returned", () => { let testCount = 0; const expectedCount = 1; Either.each(validation => { testCount += 1; return validation; }, testCollection); expect(testCount).to.equal(expectedCount); }); }); describe("no early terminate", () => { it("should iterate over each item in the collection", () => { let testCount = 0; const expectedCount = 2; Either.each(() => testCount += 1, testCollection); expect(testCount).to.equal(expectedCount); }); }); }); describe(".equals", () => { it("should return true for same values but same types", () => expect(Either.equals(Right.from(testValue), Right.from(testValue))).to.be.true ); it("should return false for same values but different types", () => expect(Either.equals(Left.from(testValue), Right.from(testValue))).to.be.false ); }); describe(".isLeft", () => { it("should return true for a Left", () => expect(Either.isLeft(Left.from(testMessage))).to.be.true); it("should return false for a Right", () => expect(Either.isLeft(Right.from(testValue))).to.be.false); it("should return false for an arbitrary value", () => expect(Either.isLeft(testValue)).to.be.false); }); describe(".isRight", () => { it("should return false for a Left", () => expect(Either.isRight(Left.from(testMessage))).to.be.false); it("should return true for a Right", () => expect(Either.isRight(Right.from(testValue))).to.be.true); it("should return false for an arbitrary value", () => expect(Either.isRight(testValue)).to.be.false); }); describe(".mapIn", () => { const testCollection = [ Left.from(testMessage), Right.from(testValue) ]; let actualResult = null; before(() => actualResult = Either.mapIn(value => !value, testCollection)); it("should map each value in the collection", () => { const expectedResult = [testMessage, !testValue]; expect(map(testEither => testEither.value, actualResult)).to.be.eql(expectedResult); }); }); describe(".of", () => { it("should return an instance of Right", () => expect(Either.of()).to.be.instanceof(Right)); }); describe(".toMaybe", () => { const testLeft = Left.from(testMessage); const testRight = Right.from(testValue); const testMaybeImplementation = Maybe; it("should convert the Left to a Nothing", () => expect(Either.toMaybe(testMaybeImplementation, testLeft)).to.be.instanceof(Maybe.Nothing) ); it("should convert the Right to a Just", () => expect(Either.toMaybe(testMaybeImplementation, testRight)).to.be.instanceof(Maybe.Just) ); it("should contain the value of the Right", () => { const expectedJust = Maybe.Just.from(testValue); expect(Either.toMaybe(testMaybeImplementation, testRight)).to.eql(expectedJust); }); }); describe(".toPromise", () => { const testLeft = Left.from(testMessage); const testRight = Right.from(testValue); const testPromiseImplementation = Promise; it("should reject with the value of a Left", () => expect(Either.toPromise(testPromiseImplementation, testLeft)).to.eventually.be.rejectedWith(testValue) ); it("should resolve with the value of a Right", () => expect(Either.toPromise(testPromiseImplementation, testRight)).to.eventually.equal(testValue) ); }); describe(".toValidation", () => { const testLeft = Left.from(testMessage); const testRight = Right.from(testValue); const testValidationImplementation = Validation; it("should convert the Left to a Failure", () => expect(Either.toValidation(testValidationImplementation, testLeft)).to.be.instanceof(Validation.Failure) ); it("should convert the Success to a Success", () => expect(Either.toValidation(testValidationImplementation, testRight)).to.be.instanceof(Validation.Success) ); it("should contain the value of the Success", () => { const expectedJust = Validation.Success.from(testValue); expect(Either.toValidation(testValidationImplementation, testRight)).to.eql(expectedJust); }); }); describe(".try", () => { it("should return a Left for a caught exception", () => { const testFn = () => { throw new Error(testMessage); }; expect(Either.try(testFn)).to.be.instanceof(Left); }); it("should return a Right for a normal execution", () => { const testFn = () => true; expect(Either.try(testFn)).to.be.instanceof(Right); }); }); describe("Left", () => { describe(".from", () => { describe("value", () => { const expectedResult = new Left(testMessage); expect(Left.from(testMessage)).to.eql(expectedResult); }); describe("Left", () => { const testLeft = new Left(testMessage); expect(Left.from(testLeft)).to.equal(testLeft); }); describe("Right", () => { const testRight = new Right(testValue); expect(Left.from(testRight)).to.equal(testRight); }); }); describe("constructor", () => { it("should return a new instance of Left", () => expect(new Left(testMessage)).to.be.instanceof(Left)); }); describe("#ap", () => { const testLeft = new Left(testMessage); const testApplyValue = new Left(testMessage); let actualResult = null; before(() => actualResult = testLeft.ap(testApplyValue)); it("should return the instance", () => expect(actualResult).to.equal(testLeft)); it("should not call #map on the provided apply value", () => expect(actualResult).to.equal(testLeft)); }); describe("#bimap", () => { const testLeft = new Left(testMessage); const testBimapLeft = sinon.spy(value => `${value} bimapped`); const testBimapRight = sinon.spy(value => !value); let actualResult = null; before(() => actualResult = testLeft.bimap(testBimapLeft, testBimapRight)); it("should return a new instance", () => expect(actualResult).to.be.instanceof(Left) .and.to.not.equal(testLeft) ); it("should pass the values to the failure bimap method", () => expect(testBimapLeft).to.be.calledWith(testLeft.value) ); it("should not call the success bimap method", () => expect(testBimapRight).to.not.be.called); it("should contain the mapped value", () => expect(actualResult.value).to.eql(`${testMessage} bimapped`)); }); describe("#chain", () => { const testLeft = new Left(testMessage); const testChain = sinon.spy(() => true); let actualResult = null; before(() => actualResult = testLeft.chain(testChain)); it("should return the instance", () => expect(actualResult).to.equal(testLeft)); it("should not call the provided chain method", () => expect(testChain).to.not.be.called); }); describe("#equals", () => { const testLeft1 = new Left(testMessage); const testLeft2 = new Left(testMessage); it("should return true for instances with the same value", () => expect(testLeft1.equals(testLeft2)).to.be.true); }); describe("#get", () => { it("should return null", () => expect(Left.from(testMessage).get()).to.be.null); }); describe("#ifRight", () => { const testLeft = new Left(testMessage); const testIfRight = sinon.spy(() => true); let actualResult = null; before(() => actualResult = testLeft.ifRight(testIfRight)); it("should return the instance", () => expect(actualResult).to.equal(testLeft)); it("should not call the provided ifRight method", () => expect(testIfRight).to.not.be.called); }); describe("#ifLeft", () => { const testLeft = new Left(testMessage); const testIfLeft = sinon.spy(() => true); let actualResult = null; before(() => actualResult = testLeft.ifLeft(testIfLeft)); it("should return the instance", () => expect(actualResult).to.equal(testLeft)); it("should call the provided ifLeft method", () => expect(testIfLeft).to.be.called); }); describe("#isRight", () => { it("should return false", () => expect(new Left(testMessage).isRight()).to.be.false); }); describe("#isLeft", () => { it("should return true", () => expect(new Left(testMessage).isLeft(testMessage)).to.be.true); }); describe("#map", () => { const testLeft = new Left(testMessage); const testMap = sinon.spy(() => true); let actualResult = null; before(() => actualResult = testLeft.map(testMap)); it("should return the instance", () => expect(actualResult).to.equal(testLeft)); it("should not call the provided map method", () => expect(testMap).to.not.be.called); }); describe("#of", () => { it("should return an instance of Right", () => expect(new Left().of()).to.be.instanceof(Right)); }); describe("#orElse", () => { const testLeft = new Left(testMessage); const expectedResult = testValue; let actualResult = null; before(() => actualResult = testLeft.orElse(testValue)); it("should return the value passed", () => expect(actualResult).to.equal(expectedResult)); }); describe("#orElseGet", () => { const testLeft = new Left(testMessage); const testValueSupplier = () => testValue; const expectedResult = testValue; let actualResult = null; before(() => actualResult = testLeft.orElseGet(testValueSupplier)); it("should return the value supplied by the function passed", () => expect(actualResult).to.equal(expectedResult) ); }); describe("#orElseThrow", () => { const testLeft = new Left(testMessage); const testExceptionSupplier = () => new Error(testMessage); const testFn = () => testLeft.orElseThrow(testExceptionSupplier); const expectedResult = testMessage; it("should throw the supplied error", () => expect(testFn).to.throw(expectedResult)); }); describe("#toMaybe", () => { const testLeft = new Left(testMessage); const testMaybeImplementation = Maybe; it("should return a Maybe instance", () => expect(testLeft.toMaybe(testMaybeImplementation)).to.be.instanceof(Maybe.Nothing) ); }); describe("#toPromise", () => { const testLeft = new Left(testMessage); const testPromiseImplementation = Promise; it("should return a Promise instance", () => expect(testLeft.toPromise(testPromiseImplementation)).to.be.instanceof(Promise) ); describe("Promise instance", () => { it("should have rejected the value", () => expect(testLeft.toPromise(testPromiseImplementation)).to.be.rejectedWith(testLeft.value) ); }); }); describe("#toString", () => { const testLeft = new Left(testMessage); it("should return a string containing the type and the values", () => expect(testLeft.toString()).to.equal(`Either.Left(${testMessage})`) ); }); describe("#toValidation", () => { const testLeft = new Left(testMessage); const testValidationImplementation = Validation; it("should return a Validation instance", () => expect(testLeft.toValidation(testValidationImplementation)).to.be.instanceof(Validation.Failure) ); }); describe("Algebraic Laws", () => { Applicative(Left); Apply(Left); Chain(Left); Extend(Left); Functor(Left); Monad(Left); Setoid(Left); }); }); describe("Right", () => { describe(".from", () => { describe("value", () => { const expectedResult = new Right(testValue); expect(Right.from(testValue)).to.eql(expectedResult); }); describe("Left", () => { const testLeft = new Left(testMessage); expect(Right.from(testLeft)).to.equal(testLeft); }); describe("Right", () => { const testRight = new Right(testValue); expect(Right.from(testRight)).to.equal(testRight); }); }); describe("constructor", () => { const testValue = value => !value; it("should return a new instance of Right", () => expect(new Right(testValue)).to.be.instanceof(Right)); }); describe("#ap", () => { const testArgument = testValue; const testRightFn = value => !value; const testRight = new Right(testRightFn); const testApplyValue = new Right(testArgument); let actualResult = null; before(() => actualResult = testRight.ap(testApplyValue)); it("should return a new instance", () => expect(actualResult).to.not.equal(testRight)); it("should call #map on the provided apply value", () => expect(actualResult.value).to.be.false); }); describe("#bimap", () => { const testRight = new Right(testValue); const testBimapLeft = sinon.spy(value => `${value} bimapped`); const testBimapRight = sinon.spy(value => !value); let actualResult = null; before(() => actualResult = testRight.bimap(testBimapLeft, testBimapRight)); it("should return a new instance", () => expect(actualResult).to.be.instanceof(Right) .and.to.not.equal(testRight) ); it("should not call the failure bimap method", () => expect(testBimapLeft).to.not.be.called); it("should pass the value to the success bimap method", () => expect(testBimapRight).to.be.calledWith(testRight.value) ); it("should contain the mapped value", () => expect(actualResult.value).to.equal(!testValue)); }); describe("#chain", () => { const testRight = new Right(testValue); describe("nothing result", () => { const testChainLeft = sinon.spy(() => new Left(testMessage)); let actualResultLeft = null; before(() => actualResultLeft = testRight.chain(testChainLeft)); it("should return a new instance for a wrapped value", () => expect(actualResultLeft).to.not.equal(testRight) ); it("should return the nothing instance", () => expect(actualResultLeft).to.be.instanceof(Left)); }); describe("just wrapped result", () => { const testChainWrapped = sinon.spy(value => new Right(!value)); let actualResultRight = null; before(() => actualResultRight = testRight.chain(testChainWrapped)); it("should return a new instance for a wrapped value", () => expect(actualResultRight).to.not.equal(testRight) ); it("should return the just instance", () => expect(actualResultRight).to.be.instanceof(Right)); }); describe("unwrapped result", () => { const testChainUnwrapped = sinon.spy(value => !value); let actualResultUnwrapped = null; before(() => actualResultUnwrapped = testRight.chain(testChainUnwrapped)); it("should return a new instance for a wrapped value", () => expect(actualResultUnwrapped).to.not.equal(testRight) ); it("should return a just instance", () => expect(actualResultUnwrapped).to.be.instanceof(Right)); }); }); describe("#equals", () => { const testRight1 = new Right(testValue); const testRight2 = new Right(testValue); const testRight3 = new Right(!testValue); it("should return true for instances with the same value", () => expect(testRight1.equals(testRight2)).to.be.true ); it("should return false for instances with a different value", () => expect(testRight1.equals(testRight3)).to.be.false ); }); describe("#get", () => { it("should return null", () => expect(Right.from(testValue).get()).to.equal(testValue)); }); describe("#ifRight", () => { const testRight = new Right(testValue); const testIfRight = sinon.spy(() => true); let actualResult = null; before(() => actualResult = testRight.ifRight(testIfRight)); it("should return the instance", () => expect(actualResult).to.equal(testRight)); it("should call the provided ifRight method", () => expect(testIfRight).to.be.calledWith(testValue)); }); describe("#ifLeft", () => { const testRight = new Right(testValue); const testIfLeft = sinon.spy(() => true); let actualResult = null; before(() => actualResult = testRight.ifLeft(testIfLeft)); it("should return the instance", () => expect(actualResult).to.equal(testRight)); it("should not call the provided ifLeft method", () => expect(testIfLeft).to.not.be.called); }); describe("#isRight", () => { it("should return true", () => expect(new Right(testValue).isRight()).to.be.true); }); describe("#isLeft", () => { it("should return false", () => expect(new Right(testValue).isLeft(testMessage)).to.be.false); }); describe("#map", () => { const testRight = new Right(testValue); const testMap = sinon.spy(value => !value); let actualResult = null; before(() => actualResult = testRight.map(testMap)); it("should return a new instance", () => expect(actualResult).to.be.instanceof(Right) .and.to.not.equal(testRight) ); it("should call the provided map method", () => expect(testMap).to.be.calledWith(testRight.value)); }); describe("#of", () => { it("should return an instance of Right", () => expect(new Right().of()).to.be.instanceof(Right)); }); describe("#orElse", () => { const testRight = new Right(testValue); const testOrElseValue = false; let actualResult = null; before(() => actualResult = testRight.orElse(testOrElseValue)); it("should return the value", () => expect(actualResult).to.eql(testValue)); }); describe("#orElseGet", () => { const testRight = new Right(testValue); const testValueSupplier = () => !testValue; const expectedResult = testValue; let actualResult = null; before(() => actualResult = testRight.orElseGet(testValueSupplier)); it("should return the value of the instance", () => expect(actualResult).to.equal(expectedResult)); }); describe("#orElseThrow", () => { const testRight = new Right(testValue); const testOrElseThrow = sinon.spy(testValue => new Error(testValue)); it("should not throw the supplied error", () => { const testFn = () => testRight.orElseThrow(testOrElseThrow); expect(testFn).to.not.throw(); }); it("should not call the provided method", () => expect(testOrElseThrow).to.not.be.called); it("should return the value", () => expect(testRight.orElseThrow()).to.eql(testValue)); }); describe("#toMaybe", () => { const testRight = new Right(testValue); const testMaybeImplementation = Maybe; it("should convert the Right to a Just", () => expect(testRight.toMaybe(testMaybeImplementation)).to.be.instanceof(Maybe.Just) ); describe("Maybe instance", () => { it("should contain the value of the Right", () => { const expectedJust = Maybe.Just.from(testValue); expect(testRight.toMaybe(testMaybeImplementation)).to.eql(expectedJust); }); }); }); describe("#toPromise", () => { const testRight = new Right(testValue); const testPromiseImplementation = Promise; it("should return a Promise instance", () => expect(testRight.toPromise(testPromiseImplementation)).to.be.instanceof(Promise) ); describe("Promise instance", () => { it("should have resolved the value", () => expect(testRight.toPromise(testPromiseImplementation)).to.eventually.equal(testRight.value) ); }); }); describe("#toString", () => { const testValues = [true, false]; const testRight = new Right(testValues); it("should return a string containing the type and the values", () => expect(testRight.toString()).to.equal("Either.Right(true,false)") ); }); describe("#toValidation", () => { const testRight = new Right(testValue); const testValidationImplementation = Validation; it("should convert the Right to a Success", () => expect(testRight.toValidation(testValidationImplementation)).to.be.instanceof(Validation.Success) ); describe("Validation instance", () => { it("should contain the value of the Right", () => { const expectedJust = Validation.Success.from(testValue); expect(testRight.toValidation(testValidationImplementation)).to.eql(expectedJust); }); }); }); describe("Algebraic Laws", () => { Applicative(Right); Apply(Right); Chain(Right); Extend(Right); Functor(Right); Monad(Right); Setoid(Right); }); }); });