UNPKG

pragmatic-fp-ts

Version:

Opinionated functional programming library with easy use in mind

246 lines (211 loc) 8.87 kB
import { futureMaybe } from "../Future.ts"; import * as l from "../main.ts"; const even = (n: number) => n % 2 === 0; const odd = (n: number) => n % 2 === 1; const asyncEven = (n: number) => Promise.resolve(n % 2 === 0); const asyncOdd = (n: number) => Promise.resolve(n % 2 === 1); describe("Future", () => { describe("eitherFuture", () => { it("should lift values", async () => { const num = l.futureEither(1); expect(await num.getValue()).toBe(1); }); it("should provide default values", async () => { expect(await l.futureEither<number>(null as any).getValueOr(1)).toBe(1); await expect(l.futureEither(null as any).getValue()).rejects.toBeTruthy(); }); it("should lift promises", async () => { const num = l.futureEither(Promise.resolve(1)); expect(await num.getValue()).toBe(1); }); it("should chain bind functions", async () => { const num = l.futureEither(1)._(l.add(1)); expect(await num.getValue()).toBe(2); }); it("should chain bind promise generating functions", async () => { const incAsync = (n: number) => Promise.resolve(n + 1); const num = l.futureEither(1)._(incAsync); expect(await num.getValue()).toBe(2); }); it("should filter values with predicates", async () => { const num = l.futureEither(1); expect(await num.filter(odd).getMonad()).toBeInstanceOf(l.Right); expect(await num.filter(even).getMonad()).toBeInstanceOf(l.Left); expect(await num.filter(even).getMonad()).toBeInstanceOf(l.Left); }); it("should filter values with predicate promises", async () => { const num = l.futureEither(1); expect(await num.filter(asyncOdd).getMonad()).toBeInstanceOf(l.Right); expect(await num.filter(asyncEven).getMonad()).toBeInstanceOf(l.Left); expect(await num.filter(asyncEven)._(l.add(1)).getMonad()).toBeInstanceOf(l.Left); }); it("should bind effect", async () => { let value = 1; const num = l.futureEither("foo"); const updateValue = () => (value += 1); expect(await num.effect(updateValue).getValue()).toEqual("foo"); expect(value).toBe(2); }); it("should bind async effects", async () => { let value = 1; const num = l.futureEither("foo"); let p: Promise<any> = null as any; const updateAsync = () => { p = new Promise<void>((resolve) => { setTimeout(() => { value += 1; resolve(); }, 500); }); return p; }; expect(await num.effect(updateAsync).getValue()).toEqual("foo"); expect(value).toBe(2); }); it("should execute and await effects in order", async () => { const results: any[] = []; await futureMaybe(1) .effect(async () => { await new Promise((resolve) => setTimeout(resolve, 500)); results.push(1); }) .effect(async () => { results.push(2); }) .effect(async () => { await new Promise((resolve) => setTimeout(resolve, 100)); results.push(3); }) .effect(async () => { results.push(4); }) .getValue(); expect(results).toEqual([1, 2, 3, 4]); }); it("should catch errors", async () => { const num = l.futureEither(1); const boom1 = () => Promise.reject("boom") as any; const boom2 = () => { throw "boom"; }; expect(await num._(boom1).getMonad()).toBeInstanceOf(l.Left); expect(await num.filter(boom1).getMonad()).toBeInstanceOf(l.Left); expect(await num._(boom2).getMonad()).toBeInstanceOf(l.Left); expect(await num.filter(boom2).getMonad()).toBeInstanceOf(l.Left); const error1 = await num._(boom1, "bad stuff!").getMonad(); const error2 = await num._(boom2, "bad stuff!").getMonad(); const error3 = await num._(boom1).getMonad(); const error4 = await num._(boom2).getMonad(); expect(error1.getReason()).toEqual("bad stuff!"); expect(error2.getReason()).toEqual("bad stuff!"); expect(error3.getReason()).toEqual("boom"); expect(error4.getReason()).toEqual("boom"); }); const syncMatcher = { right: l.add(1), left: () => 0 }; const asyncMatcher = { right: (n: number) => Promise.resolve(n + 1), left: () => 0 }; it("should match success types", async () => { const num = l.futureEither(1).match(syncMatcher); expect(await num.getValue()).toEqual(2); }); it("should match success types with promises", async () => { const num = l.futureEither(1).match(asyncMatcher); expect(await num.getValue()).toEqual(2); }); it("should rethrow errors", async () => { await expect( l.futureEither(l.left<number>(new Error())).match(l.throwLeftAsError).getValue() ).rejects.toBeInstanceOf(Error); }); }); describe("maybeFuture", () => { it("should lift values", async () => { const num = l.futureMaybe(1); expect(await num.getValue()).toBe(1); }); it("should lift promises", async () => { const num = l.futureMaybe(Promise.resolve(1)); expect(await num.getValue()).toBe(1); }); it("should provide default values", async () => { expect(await l.futureMaybe<number>(null as any).getValueOr(1)).toBe(1); await expect(l.futureMaybe(null as any).getValue()).rejects.toBeTruthy(); }); it("should chain bind functions", async () => { const num = l.futureMaybe(1)._(l.add(1)); expect(await num.getValue()).toBe(2); }); it("should chain bind promise generating functions", async () => { const incAsync = (n: number) => Promise.resolve(n + 1); const num = l.futureMaybe(1)._(incAsync); expect(await num.getValue()).toBe(2); }); it("should filter values with predicates", async () => { const num = l.futureMaybe(1); expect(await num.filter(odd).getMonad()).toBeInstanceOf(l.Just); expect(await num.filter(even).getMonad()).toBeInstanceOf(l.Nothing); expect(await num.filter(even)._(l.add(1)).getMonad()).toBeInstanceOf(l.Nothing); }); it("should filter values with predicate promises", async () => { const num = l.futureMaybe(1); expect(await num.filter(asyncOdd).getMonad()).toBeInstanceOf(l.Just); expect(await num.filter(asyncEven).getMonad()).toBeInstanceOf(l.Nothing); expect(await num.filter(asyncEven)._(l.add(1)).getMonad()).toBeInstanceOf(l.Nothing); }); it("should bind effect", async () => { let value = 1; const num = l.futureMaybe("foo"); const updateValue = () => (value += 1); expect(await num.effect(updateValue).getValue()).toEqual("foo"); expect(value).toBe(2); }); it("should bind async effects", async () => { let value = 1; const num = l.futureMaybe("foo"); let p: Promise<any> = null as any; const updateAsync = () => { p = new Promise<void>((resolve) => { setTimeout(() => { value += 1; resolve(); }, 500); }); return p; }; expect(await num.effect(updateAsync).getValue()).toEqual("foo"); expect(value).toBe(2); }); it("should become Nothing if effects throw", async () => { const causeError = () => Promise.reject("error"); await expect(l.futureMaybe("test").effect(causeError).getValue()).rejects.toBeDefined(); }); it("should catch errors", async () => { const num = l.futureMaybe(1); const boom1 = () => Promise.reject("boom") as any; const boom2 = () => { throw new Error("boom"); }; expect(await num._(boom1).getMonad()).toBeInstanceOf(l.Nothing); expect(await num.filter(boom1).getMonad()).toBeInstanceOf(l.Nothing); expect(await num._(boom2).getMonad()).toBeInstanceOf(l.Nothing); expect(await num.filter(boom2).getMonad()).toBeInstanceOf(l.Nothing); }); const syncMatcher = { just: l.add(1), nothing: () => 0 }; const asyncMatcher = { just: (n: number) => Promise.resolve(n + 1), nothing: () => 0 }; it("should match success types", async () => { const num = l.futureMaybe(1).match(syncMatcher); expect(await num.getValue()).toEqual(2); }); it("should match success types with promises", async () => { const num = l.futureMaybe(1).match(asyncMatcher); expect(await num.getValue()).toEqual(2); }); it("should match error types", async () => { const num = l.futureMaybe(l.nothing<number>()).match(syncMatcher); expect(await num.getValue()).toEqual(0); }); it("should match error types with promises", async () => { const num = l.futureMaybe(l.nothing<number>()).match(asyncMatcher); expect(await num.getValue()).toEqual(0); }); }); });