diff --git a/packages/combinator/src/a/a.test.ts b/packages/combinator/src/a/a.test.ts index db209e6..dfcadea 100644 --- a/packages/combinator/src/a/a.test.ts +++ b/packages/combinator/src/a/a.test.ts @@ -1,8 +1,10 @@ -import { expect, it } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { apply } from '.'; const addSix = (a: number) => a + 6; -it('should apply the function to the given value', () => { - expect(apply(addSix)(3)).toEqual(9); +describe('A Combinator', () => { + it('should apply the function to the given value', () => { + expect(apply(addSix)(3)).toEqual(9); + }); }); diff --git a/packages/combinator/src/b/b.test.ts b/packages/combinator/src/b/b.test.ts index d50a792..2b7da2b 100644 --- a/packages/combinator/src/b/b.test.ts +++ b/packages/combinator/src/b/b.test.ts @@ -1,9 +1,11 @@ -import { expect, it } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { compose } from '.'; const addEight = (a: number) => a + 8; const timesThree = (a: number) => a * 3; -it('should correctly compose the functions', () => { - expect(compose(addEight)(timesThree)(4)).toEqual(20); +describe('B Combinator', () => { + it('should correctly compose the functions', () => { + expect(compose(addEight)(timesThree)(4)).toEqual(20); + }); }); diff --git a/packages/combinator/src/bl/bl.test.ts b/packages/combinator/src/bl/bl.test.ts index 5158e33..6535fc1 100644 --- a/packages/combinator/src/bl/bl.test.ts +++ b/packages/combinator/src/bl/bl.test.ts @@ -1,9 +1,11 @@ -import { expect, it } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { BL } from '.'; const add = (a: number) => (b: number) => a + b; const double = (x: number) => x * 2; -it('should compose binary function with unary', () => { - expect(BL(double)(add)(3)(4)).toEqual(14); // double(add(3)(4)) +describe('B1 Combinator', () => { + it('should compose binary function with unary', () => { + expect(BL(double)(add)(3)(4)).toEqual(14); // double(add(3)(4)) + }); }); diff --git a/packages/combinator/src/c/c.test.ts b/packages/combinator/src/c/c.test.ts index dce6381..cc506cf 100644 --- a/packages/combinator/src/c/c.test.ts +++ b/packages/combinator/src/c/c.test.ts @@ -1,10 +1,12 @@ -import { expect, it } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { C } from '.'; const subtract = (a: number) => (b: number) => a - b; -it('should swap argument order', () => { - const flipped = C(subtract); - expect(flipped(3)(10)).toEqual(7); // 10 - 3, not 3 - 10 - expect(subtract(10)(3)).toEqual(7); // Verify original behavior +describe('C Combinator', () => { + it('should swap argument order', () => { + const flipped = C(subtract); + expect(flipped(3)(10)).toEqual(7); // 10 - 3, not 3 - 10 + expect(subtract(10)(3)).toEqual(7); // Verify original behavior + }); }); diff --git a/packages/combinator/src/i/i.test.ts b/packages/combinator/src/i/i.test.ts index 20392f6..64e232b 100644 --- a/packages/combinator/src/i/i.test.ts +++ b/packages/combinator/src/i/i.test.ts @@ -1,10 +1,12 @@ -import { expect, it } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { identity } from '.'; const addThree = (a: number) => a + 3; -it('should always return the value, unchanged', () => { - expect(identity(1)).toEqual(1); - expect(identity(addThree)).toEqual(addThree); - expect(identity('test')).toEqual('test'); +describe('I Combinator', () => { + it('should always return the value, unchanged', () => { + expect(identity(1)).toEqual(1); + expect(identity(addThree)).toEqual(addThree); + expect(identity('test')).toEqual('test'); + }); }); diff --git a/packages/combinator/src/k/k.test.ts b/packages/combinator/src/k/k.test.ts index 451bed2..006abd3 100644 --- a/packages/combinator/src/k/k.test.ts +++ b/packages/combinator/src/k/k.test.ts @@ -1,8 +1,10 @@ -import { expect, it } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { constant } from '.'; -it('should always return the first value', () => { - expect(constant(1)(2)).toEqual(1); - expect(constant(9)(7)).toEqual(9); - expect(constant(4)(8)).toEqual(4); +describe('K Combinator', () => { + it('should always return the first value', () => { + expect(constant(1)(2)).toEqual(1); + expect(constant(9)(7)).toEqual(9); + expect(constant(4)(8)).toEqual(4); + }); }); diff --git a/packages/combinator/src/ki/ki.test.ts b/packages/combinator/src/ki/ki.test.ts index 4bad362..e88c1c6 100644 --- a/packages/combinator/src/ki/ki.test.ts +++ b/packages/combinator/src/ki/ki.test.ts @@ -1,8 +1,10 @@ -import { expect, it } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { second } from '.'; -it('should always return the second value', () => { - expect(second(1)(2)).toEqual(2); - expect(second(9)(7)).toEqual(7); - expect(second(4)(8)).toEqual(8); +describe('KI Combinator', () => { + it('should always return the second value', () => { + expect(second(1)(2)).toEqual(2); + expect(second(9)(7)).toEqual(7); + expect(second(4)(8)).toEqual(8); + }); }); diff --git a/packages/combinator/src/phi/phi.test.ts b/packages/combinator/src/phi/phi.test.ts index 546ba73..b2e750f 100644 --- a/packages/combinator/src/phi/phi.test.ts +++ b/packages/combinator/src/phi/phi.test.ts @@ -1,10 +1,12 @@ -import { expect, it } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { fork } from '.'; const add = (a: number) => (b: number) => a + b; const addThree = (a: number) => a + 3; const minusTwo = (a: number) => a - 2; -it('should correctly compose the two unary functions and the binary function', () => { - expect(fork(add)(addThree)(minusTwo)(9)).toEqual(19); +describe('Phi Combinator', () => { + it('should correctly compose the two unary functions and the binary function', () => { + expect(fork(add)(addThree)(minusTwo)(9)).toEqual(19); + }); }); diff --git a/packages/combinator/src/psi/psi.test.ts b/packages/combinator/src/psi/psi.test.ts index d3c0337..5c49504 100644 --- a/packages/combinator/src/psi/psi.test.ts +++ b/packages/combinator/src/psi/psi.test.ts @@ -1,9 +1,11 @@ -import { expect, it } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { Psi } from '.'; const add = (a: number) => (b: number) => a + b; const square = (x: number) => x * x; -it('should apply function to both transformed arguments', () => { - expect(Psi(add)(square)(3)(4)).toEqual(25); // 3² + 4² = 9 + 16 +describe('Psi Combinator', () => { + it('should apply function to both transformed arguments', () => { + expect(Psi(add)(square)(3)(4)).toEqual(25); // 3² + 4² = 9 + 16 + }); }); diff --git a/packages/combinator/src/q/q.test.ts b/packages/combinator/src/q/q.test.ts index 843c6d7..3d1b288 100644 --- a/packages/combinator/src/q/q.test.ts +++ b/packages/combinator/src/q/q.test.ts @@ -1,9 +1,11 @@ -import { expect, it } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { pipe } from '.'; const double = (x: number) => x * 2; const addThree = (x: number) => x + 3; -it('should compose functions left-to-right', () => { - expect(pipe(double)(addThree)(5)).toEqual(13); // (5 * 2) + 3 = 13 +describe('Q Combinator', () => { + it('should compose functions left-to-right', () => { + expect(pipe(double)(addThree)(5)).toEqual(13); // (5 * 2) + 3 = 13 + }); }); diff --git a/packages/combinator/src/readme.test.ts b/packages/combinator/src/readme.test.ts index fb0b4e4..267a761 100644 --- a/packages/combinator/src/readme.test.ts +++ b/packages/combinator/src/readme.test.ts @@ -22,7 +22,7 @@ import { W, } from '.'; -describe('@certes/combinator - README Examples', () => { +describe('README Examples', () => { describe('Quick Start', () => { it('should compose functions right-to-left', () => { const addThenDouble = compose((x: number) => x * 2)((x: number) => x + 3); diff --git a/packages/combinator/src/s/s.test.ts b/packages/combinator/src/s/s.test.ts index 1eeb381..cf58756 100644 --- a/packages/combinator/src/s/s.test.ts +++ b/packages/combinator/src/s/s.test.ts @@ -1,9 +1,11 @@ -import { expect, it } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { substitution } from '.'; const add = (a: number) => (b: number) => a + b; const double = (x: number) => x * 2; -it('should distribute argument to both functions', () => { - expect(substitution(add)(double)(5)).toEqual(15); // 5 + double(5) = 5 + 10 = 15 +describe('S Combinator', () => { + it('should distribute argument to both functions', () => { + expect(substitution(add)(double)(5)).toEqual(15); // 5 + double(5) = 5 + 10 = 15 + }); }); diff --git a/packages/combinator/src/th/t.test.ts b/packages/combinator/src/th/t.test.ts new file mode 100644 index 0000000..6d3467b --- /dev/null +++ b/packages/combinator/src/th/t.test.ts @@ -0,0 +1,10 @@ +import { describe, expect, it } from 'vitest'; +import { applyTo } from '.'; + +const addSix = (a: number) => a + 6; + +describe('T Combinator', () => { + it('should apply the value the given function', () => { + expect(applyTo(3)(addSix)).toEqual(9); + }); +}); diff --git a/packages/combinator/src/th/th.test.ts b/packages/combinator/src/th/th.test.ts deleted file mode 100644 index 02c1b2f..0000000 --- a/packages/combinator/src/th/th.test.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { expect, it } from 'vitest'; -import { applyTo } from '.'; - -const addSix = (a: number) => a + 6; - -it('should apply the value the given function', () => { - expect(applyTo(3)(addSix)).toEqual(9); -}); diff --git a/packages/combinator/src/v/v.test.ts b/packages/combinator/src/v/v.test.ts index 2f9ac55..bb2d4d0 100644 --- a/packages/combinator/src/v/v.test.ts +++ b/packages/combinator/src/v/v.test.ts @@ -1,8 +1,10 @@ -import { expect, it } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { V } from '.'; const multiply = (a: number) => (b: number) => a * b; -it('should pair arguments for function application', () => { - expect(V(3)(4)(multiply)).toEqual(12); +describe('V Combinator', () => { + it('should pair arguments for function application', () => { + expect(V(3)(4)(multiply)).toEqual(12); + }); }); diff --git a/packages/combinator/src/w/w.test.ts b/packages/combinator/src/w/w.test.ts index f4af7e5..07ad06a 100644 --- a/packages/combinator/src/w/w.test.ts +++ b/packages/combinator/src/w/w.test.ts @@ -1,8 +1,10 @@ -import { expect, it } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { duplication } from '.'; const multiply = (a: number) => (b: number) => a * b; -it('should apply binary function to same argument twice', () => { - expect(duplication(multiply)(7)).toEqual(49); // 7 * 7 = 49 +describe('W Combinator', () => { + it('should apply binary function to same argument twice', () => { + expect(duplication(multiply)(7)).toEqual(49); // 7 * 7 = 49 + }); }); diff --git a/packages/common/src/lookup/lookup.test.ts b/packages/common/src/lookup/lookup.test.ts index c8a4519..3f58aa0 100644 --- a/packages/common/src/lookup/lookup.test.ts +++ b/packages/common/src/lookup/lookup.test.ts @@ -20,7 +20,7 @@ const defaultColor: Color = [128, 128, 128, 155]; const colorLookup = lookup(colorTable, defaultVal(defaultColor)); const undefLookup = lookup(colorTable); -describe('lookup', () => { +describe('Lookup', () => { it('should return the selected value', () => { const actualOne = colorLookup('NOPE'); const actualTwo = colorLookup('BUZZ'); diff --git a/packages/common/src/once/once.test.ts b/packages/common/src/once/once.test.ts index 745fed2..e9caf67 100644 --- a/packages/common/src/once/once.test.ts +++ b/packages/common/src/once/once.test.ts @@ -3,7 +3,7 @@ import { once } from '.'; let globalVal = 10; -describe('once', () => { +describe('Once', () => { beforeEach(() => { globalVal = 10; }); diff --git a/packages/common/src/readme.test.ts b/packages/common/src/readme.test.ts index 2a2c885..ed94ef3 100644 --- a/packages/common/src/readme.test.ts +++ b/packages/common/src/readme.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { lookup, noop, once, tap } from '.'; -describe('@certes/common - README Examples', () => { +describe('README Examples', () => { describe('lookup', () => { it('should lookup status codes with default handler', () => { const statusCodes = { diff --git a/packages/common/src/tap/tap.test.ts b/packages/common/src/tap/tap.test.ts index d647342..baa8c71 100644 --- a/packages/common/src/tap/tap.test.ts +++ b/packages/common/src/tap/tap.test.ts @@ -9,7 +9,7 @@ const tapper = (n: number) => { const tapped = tap(tapper); -describe('tap', () => { +describe('Tap', () => { beforeEach(() => { sideEffect = 5; }); diff --git a/packages/composition/src/compose-async/compose-async.test.ts b/packages/composition/src/compose-async/compose-async.test.ts index 763f0c7..5880019 100644 --- a/packages/composition/src/compose-async/compose-async.test.ts +++ b/packages/composition/src/compose-async/compose-async.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, test } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { composeAsync } from '.'; // Async helpers @@ -31,229 +31,231 @@ const syncThrow = (message: string): never => { throw new Error(message); }; -describe('composeAsync - Basic Functionality', () => { - test('should compose async functions right-to-left', async () => { - const composed = composeAsync(asyncAdd3, asyncMultiply2, asyncSubtract1); +describe('ComposeAsync', () => { + describe('Basic Functionality', () => { + it('should compose async functions right-to-left', async () => { + const composed = composeAsync(asyncAdd3, asyncMultiply2, asyncSubtract1); - const result = await composed(10); + const result = await composed(10); - // subtract1(10) = 9, multiply2(9) = 18, add3(18) = 21 - expect(result).toBe(21); - }); - - test('should handle single async function', async () => { - const composed = composeAsync(asyncAdd3); - - expect(await composed(5)).toBe(8); - }); + // subtract1(10) = 9, multiply2(9) = 18, add3(18) = 21 + expect(result).toBe(21); + }); - test('should handle mixed sync and async functions', async () => { - const composed = composeAsync( - asyncUppercase, // async - stringify, // sync - asyncMultiply2, // async - add3, // sync - ); + it('should handle single async function', async () => { + const composed = composeAsync(asyncAdd3); - const result = await composed(5); + expect(await composed(5)).toBe(8); + }); - // add3(5) = 8, multiply2(8) = 16, stringify(16) = "16", uppercase("16") = "16" - expect(result).toBe('16'); - }); + it('should handle mixed sync and async functions', async () => { + const composed = composeAsync( + asyncUppercase, // async + stringify, // sync + asyncMultiply2, // async + add3, // sync + ); - test('should handle n-ary rightmost function', async () => { - const binaryAdd = async (a: number, b: number): Promise => a + b; + const result = await composed(5); - const composed = composeAsync(asyncMultiply2, asyncAdd3, binaryAdd); + // add3(5) = 8, multiply2(8) = 16, stringify(16) = "16", uppercase("16") = "16" + expect(result).toBe('16'); + }); - const result = await composed(5, 3); + it('should handle n-ary rightmost function', async () => { + const binaryAdd = async (a: number, b: number): Promise => a + b; - // binaryAdd(5, 3) = 8, add3(8) = 11, multiply2(11) = 22 - expect(result).toBe(22); - }); - - test('should handle ternary rightmost function', async () => { - const ternaryAdd = async ( - a: number, - b: number, - c: number, - ): Promise => a + b + c; - - const composed = composeAsync(asyncStringify, asyncMultiply2, ternaryAdd); - - const result = await composed(2, 3, 4); - - // ternaryAdd(2, 3, 4) = 9, multiply2(9) = 18, stringify(18) = "18" - expect(result).toBe('18'); - }); + const composed = composeAsync(asyncMultiply2, asyncAdd3, binaryAdd); - test('should return Promise even for all-sync functions', async () => { - const composed = composeAsync(add3, multiply2); + const result = await composed(5, 3); - const result = composed(5); + // binaryAdd(5, 3) = 8, add3(8) = 11, multiply2(11) = 22 + expect(result).toBe(22); + }); - expect(result).toBeInstanceOf(Promise); - expect(await result).toBe(13); - }); -}); + it('should handle ternary rightmost function', async () => { + const ternaryAdd = async ( + a: number, + b: number, + c: number, + ): Promise => a + b + c; -describe('Error Handling', () => { - test('composeAsync should propagate errors from async functions', async () => { - const composed = composeAsync( - asyncAdd3, - async () => asyncThrow('Test error'), - asyncMultiply2, - ); + const composed = composeAsync(asyncStringify, asyncMultiply2, ternaryAdd); - await expect(composed(5)).rejects.toThrow('Test error'); - }); + const result = await composed(2, 3, 4); - test('composeAsync should propagate errors from sync functions', async () => { - const composed = composeAsync( - asyncAdd3, - () => syncThrow('Sync error'), - asyncMultiply2, - ); + // ternaryAdd(2, 3, 4) = 9, multiply2(9) = 18, stringify(18) = "18" + expect(result).toBe('18'); + }); - await expect(composed(5)).rejects.toThrow('Sync error'); - }); + it('should return Promise even for all-sync functions', async () => { + const composed = composeAsync(add3, multiply2); - test('should throw on excessive composition depth', () => { - const functions = Array(1001).fill(asyncAdd3); + const result = composed(5); - // @ts-expect-error For testing - expect(() => composeAsync(...functions)).toThrow(RangeError); - // @ts-expect-error For testing - expect(() => composeAsync(...functions)).toThrow( - 'Async composition depth exceeds 1000', - ); + expect(result).toBeInstanceOf(Promise); + expect(await result).toBe(13); + }); }); -}); -describe('Async Execution Order', () => { - test('composeAsync should execute in correct order', async () => { - const executionOrder: number[] = []; - - const fn1 = async (x: number) => { - await executionOrder.push(1); - return x + 1; - }; - const fn2 = async (x: number) => { - await executionOrder.push(2); - return x * 2; - }; - const fn3 = async (x: number) => { - await executionOrder.push(3); - return x - 1; - }; - - const composed = composeAsync(fn1, fn2, fn3); - await composed(10); - - // Right-to-left: fn3, fn2, fn1 - expect(executionOrder).toEqual([3, 2, 1]); + describe('Error Handling', () => { + it('should propagate errors from async functions', async () => { + const composed = composeAsync( + asyncAdd3, + async () => asyncThrow('Test error'), + asyncMultiply2, + ); + + await expect(composed(5)).rejects.toThrow('Test error'); + }); + + it('should propagate errors from sync functions', async () => { + const composed = composeAsync( + asyncAdd3, + () => syncThrow('Sync error'), + asyncMultiply2, + ); + + await expect(composed(5)).rejects.toThrow('Sync error'); + }); + + it('should throw on excessive composition depth', () => { + const functions = Array(1001).fill(asyncAdd3); + + // @ts-expect-error For testing + expect(() => composeAsync(...functions)).toThrow(RangeError); + // @ts-expect-error For testing + expect(() => composeAsync(...functions)).toThrow( + 'Async composition depth exceeds 1000', + ); + }); }); - test('should await each function before calling next', async () => { - const results: number[] = []; - - const fn1 = async (x: number) => { - await asyncDelay(30, x); - results.push(1); - return x + 1; - }; - const fn2 = async (x: number) => { - await asyncDelay(20, x); - results.push(2); - return x * 2; - }; - const fn3 = async (x: number) => { - await asyncDelay(10, x); - results.push(3); - return x - 1; - }; - - const composed = composeAsync(fn3, fn2, fn1); - await composed(10); - - // Should execute sequentially, not in parallel - expect(results).toEqual([1, 2, 3]); + describe('Async Execution Order', () => { + it('should execute in correct order', async () => { + const executionOrder: number[] = []; + + const fn1 = async (x: number) => { + await executionOrder.push(1); + return x + 1; + }; + const fn2 = async (x: number) => { + await executionOrder.push(2); + return x * 2; + }; + const fn3 = async (x: number) => { + await executionOrder.push(3); + return x - 1; + }; + + const composed = composeAsync(fn1, fn2, fn3); + await composed(10); + + // Right-to-left: fn3, fn2, fn1 + expect(executionOrder).toEqual([3, 2, 1]); + }); + + it('should await each function before calling next', async () => { + const results: number[] = []; + + const fn1 = async (x: number) => { + await asyncDelay(30, x); + results.push(1); + return x + 1; + }; + const fn2 = async (x: number) => { + await asyncDelay(20, x); + results.push(2); + return x * 2; + }; + const fn3 = async (x: number) => { + await asyncDelay(10, x); + results.push(3); + return x - 1; + }; + + const composed = composeAsync(fn3, fn2, fn1); + await composed(10); + + // Should execute sequentially, not in parallel + expect(results).toEqual([1, 2, 3]); + }); }); -}); -describe('Associativity', () => { - test('compose(f, g, h) ≡ manual groupings', async () => { - const f = asyncAdd3; - const g = asyncMultiply2; - const h = asyncSubtract1; + describe('Associativity Law', () => { + it('compose(f, g, h) ≡ manual groupings', async () => { + const f = asyncAdd3; + const g = asyncMultiply2; + const h = asyncSubtract1; - // Create intermediate compositions with explicit types - const gh: (x: number) => Promise = composeAsync(g, h); - const fg: (x: number) => Promise = composeAsync(f, g); + // Create intermediate compositions with explicit types + const gh: (x: number) => Promise = composeAsync(g, h); + const fg: (x: number) => Promise = composeAsync(f, g); - const left = composeAsync(f, gh); - const right = composeAsync(fg, h); - const direct = composeAsync(f, g, h); + const left = composeAsync(f, gh); + const right = composeAsync(fg, h); + const direct = composeAsync(f, g, h); - const testValue = 10; - const expected = await f(await g(await h(testValue))); + const testValue = 10; + const expected = await f(await g(await h(testValue))); - expect(await left(testValue)).toBe(expected); - expect(await right(testValue)).toBe(expected); - expect(await direct(testValue)).toBe(expected); - }); + expect(await left(testValue)).toBe(expected); + expect(await right(testValue)).toBe(expected); + expect(await direct(testValue)).toBe(expected); + }); - test('associativity holds with mixed sync/async', async () => { - const f = asyncAdd3; - const g = multiply2; // sync - const h = asyncSubtract1; + it('associativity holds with mixed sync/async', async () => { + const f = asyncAdd3; + const g = multiply2; // sync + const h = asyncSubtract1; - const gh: (x: number) => Promise = composeAsync(g, h); - const fg: (x: number) => Promise = composeAsync(f, g); + const gh: (x: number) => Promise = composeAsync(g, h); + const fg: (x: number) => Promise = composeAsync(f, g); - const left = composeAsync(f, gh); - const right = composeAsync(fg, h); + const left = composeAsync(f, gh); + const right = composeAsync(fg, h); - const testValue = 10; + const testValue = 10; - expect(await left(testValue)).toBe(await right(testValue)); + expect(await left(testValue)).toBe(await right(testValue)); + }); }); -}); -describe('Identity', () => { - test('left identity: compose(id, f) ≡ f', async () => { - const f = asyncAdd3; + describe('Identity Law', () => { + it('left identity: compose(id, f) ≡ f', async () => { + const f = asyncAdd3; - const composed = composeAsync(asyncIdentity, f); + const composed = composeAsync(asyncIdentity, f); - const testValue = 10; - expect(await composed(testValue)).toBe(await f(testValue)); - }); + const testValue = 10; + expect(await composed(testValue)).toBe(await f(testValue)); + }); - test('right identity: compose(f, id) ≡ f', async () => { - const f = asyncAdd3; + it('right identity: compose(f, id) ≡ f', async () => { + const f = asyncAdd3; - const composed = composeAsync(f, asyncIdentity); + const composed = composeAsync(f, asyncIdentity); - const testValue = 10; - expect(await composed(testValue)).toBe(await f(testValue)); - }); + const testValue = 10; + expect(await composed(testValue)).toBe(await f(testValue)); + }); - test('composeAsync with sync identity', async () => { - const f = asyncAdd3; + it('associativity holds with mixed sync/async', async () => { + const f = asyncAdd3; - const leftId = composeAsync(identity, f); - const rightId = composeAsync(f, identity); + const leftId = composeAsync(identity, f); + const rightId = composeAsync(f, identity); - const testValue = 10; - expect(await leftId(testValue)).toBe(await f(testValue)); - expect(await rightId(testValue)).toBe(await f(testValue)); - }); + const testValue = 10; + expect(await leftId(testValue)).toBe(await f(testValue)); + expect(await rightId(testValue)).toBe(await f(testValue)); + }); - test('compose(id) ≡ id', async () => { - const composedId = composeAsync(asyncIdentity); + it('compose(id) ≡ id', async () => { + const composedId = composeAsync(asyncIdentity); - const testValue = 42; - expect(await composedId(testValue)).toBe(await asyncIdentity(testValue)); + const testValue = 42; + expect(await composedId(testValue)).toBe(await asyncIdentity(testValue)); + }); }); }); diff --git a/packages/composition/src/compose/compose.test.ts b/packages/composition/src/compose/compose.test.ts index 5d55de8..2f4e382 100644 --- a/packages/composition/src/compose/compose.test.ts +++ b/packages/composition/src/compose/compose.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, test } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { compose } from '.'; const identity = (x: T): T => x; @@ -24,255 +24,257 @@ const stringifyNum = (x: number) => 'nine', ][x] ?? x.toString(); -describe('Compose - Basic Functionality', () => { - test('it should support unary for first fn call and return the correct result', () => { - const strNumValue = compose(uppercase, stringifyNum, add3); - const explodeStrNumVal = compose(upperMap, splitIt, stringifyNum, add3); - - const actualOne = strNumValue(4); - const actualTwo = explodeStrNumVal(4); - - expect(actualOne).toBe('SEVEN'); - expect(actualTwo).toStrictEqual(['S', 'E', 'V', 'E', 'N']); - }); - - test('it should support n-ary for first fn call and return the correct result', () => { - const strNumValue = compose(uppercase, stringifyNum, binaryAdd); - const explodeStrNumVal = compose( - upperMap, - splitIt, - stringifyNum, - binaryAdd, - ); - - const actualOne = strNumValue(3, 4); - const actualTwo = explodeStrNumVal(3, 4); - - expect(actualOne).toBe('SEVEN'); - expect(actualTwo).toStrictEqual(['S', 'E', 'V', 'E', 'N']); - }); -}); - -describe('Composition with n-ary Functions', () => { - test('compose(f, g, h) where h is binary', () => { - const f = add3; - const g = multiply2; - const h = (a: number, b: number): number => a + b; - - const composed = compose(f, g, h); - - // h(5, 3) = 8, g(8) = 16, f(16) = 19 - expect(composed(5, 3)).toBe(19); - expect(composed(5, 3)).toBe(f(g(h(5, 3)))); +describe('Compose', () => { + describe('Basic Functionality', () => { + it('should support unary for first fn call and return the correct result', () => { + const strNumValue = compose(uppercase, stringifyNum, add3); + const explodeStrNumVal = compose(upperMap, splitIt, stringifyNum, add3); + + const actualOne = strNumValue(4); + const actualTwo = explodeStrNumVal(4); + + expect(actualOne).toBe('SEVEN'); + expect(actualTwo).toStrictEqual(['S', 'E', 'V', 'E', 'N']); + }); + + it('should support n-ary for first fn call and return the correct result', () => { + const strNumValue = compose(uppercase, stringifyNum, binaryAdd); + const explodeStrNumVal = compose( + upperMap, + splitIt, + stringifyNum, + binaryAdd, + ); + + const actualOne = strNumValue(3, 4); + const actualTwo = explodeStrNumVal(3, 4); + + expect(actualOne).toBe('SEVEN'); + expect(actualTwo).toStrictEqual(['S', 'E', 'V', 'E', 'N']); + }); }); - test('compose with ternary rightmost function', () => { - const f = stringify; - const g = multiply2; - const h = (a: number, b: number, c: number): number => a + b + c; + describe('Composition with n-ary Functions', () => { + it('should compose(f, g, h) where h is binary', () => { + const f = add3; + const g = multiply2; + const h = (a: number, b: number): number => a + b; - const composed = compose(f, g, h); + const composed = compose(f, g, h); - // h(2, 3, 4) = 9, g(9) = 18, f(18) = '18' - expect(composed(2, 3, 4)).toBe('18'); - expect(composed(2, 3, 4)).toBe(f(g(h(2, 3, 4)))); - }); -}); + // h(5, 3) = 8, g(8) = 16, f(16) = 19 + expect(composed(5, 3)).toBe(19); + expect(composed(5, 3)).toBe(f(g(h(5, 3)))); + }); -/** - * Axiom: (f ∘ g) ∘ h = f ∘ (g ∘ h) - * - * In category theory, composition must be associative. - * This means the grouping of compositions doesn't matter. - */ -describe('Associativity Law', () => { - test('compose(f, compose(g, h)) ≡ compose(compose(f, g), h)', () => { - const f = add3; - const g = multiply2; - const h = subtract1; - - // Create intermediate compositions with explicit types - // in order to shut TS up. - const gh: (x: number) => number = compose(g, h); - const fg: (x: number) => number = compose(f, g); - - // Left association: f ∘ (g ∘ h) - const left = compose(f, gh); - - // Right association: (f ∘ g) ∘ h - const right = compose(fg, h); - - // Direct composition - const direct = compose(f, g, h); - - const testValue = 10; - const expected = f(g(h(testValue))); // 21 - - expect(left(testValue)).toBe(expected); - expect(right(testValue)).toBe(expected); - expect(direct(testValue)).toBe(expected); - expect(left(testValue)).toBe(right(testValue)); - }); + it('should compose with a ternary rightmost function', () => { + const f = stringify; + const g = multiply2; + const h = (a: number, b: number, c: number): number => a + b + c; - test('associativity holds for multiple test values', () => { - const f = add3; - const g = multiply2; - const h = subtract1; + const composed = compose(f, g, h); - // Create intermediate compositions with explicit types - // in order to shut TS up. - const gh: (x: number) => number = compose(g, h); - const fg: (x: number) => number = compose(f, g); - - const left = compose(f, gh); - const right = compose(fg, h); - - const testValues = [-10, -1, 0, 1, 5, 100, 1000]; - - for (const value of testValues) { - expect(left(value)).toBe(right(value)); - expect(left(value)).toBe(f(g(h(value)))); - } + // h(2, 3, 4) = 9, g(9) = 18, f(18) = '18' + expect(composed(2, 3, 4)).toBe('18'); + expect(composed(2, 3, 4)).toBe(f(g(h(2, 3, 4)))); + }); }); - test('associativity holds for type-changing compositions', () => { - const f = uppercase; - const g = stringify; - const h = multiply2; - - // Create intermediate compositions with explicit types - // in order to shut TS up. - const gh: (x: number) => string = compose(g, h); - const fg: (x: number) => string = compose(f, g); - - const left = compose(f, gh); - const right = compose(fg, h); - const direct = compose(f, g, h); - - const expected = f(g(h(5))); // '10' - - expect(left(5)).toBe(expected); - expect(right(5)).toBe(expected); - expect(direct(5)).toBe(expected); - expect(left(5)).toBe(right(5)); + /** + * Axiom: (f ∘ g) ∘ h = f ∘ (g ∘ h) + * + * In category theory, composition must be associative. + * This means the grouping of compositions doesn't matter. + */ + describe('Associativity Law', () => { + it('compose(f, compose(g, h)) ≡ compose(compose(f, g), h)', () => { + const f = add3; + const g = multiply2; + const h = subtract1; + + // Create intermediate compositions with explicit types + // in order to shut TS up. + const gh: (x: number) => number = compose(g, h); + const fg: (x: number) => number = compose(f, g); + + // Left association: f ∘ (g ∘ h) + const left = compose(f, gh); + + // Right association: (f ∘ g) ∘ h + const right = compose(fg, h); + + // Direct composition + const direct = compose(f, g, h); + + const testValue = 10; + const expected = f(g(h(testValue))); // 21 + + expect(left(testValue)).toBe(expected); + expect(right(testValue)).toBe(expected); + expect(direct(testValue)).toBe(expected); + expect(left(testValue)).toBe(right(testValue)); + }); + + it('associativity holds for multiple test values', () => { + const f = add3; + const g = multiply2; + const h = subtract1; + + // Create intermediate compositions with explicit types + // in order to shut TS up. + const gh: (x: number) => number = compose(g, h); + const fg: (x: number) => number = compose(f, g); + + const left = compose(f, gh); + const right = compose(fg, h); + + const testValues = [-10, -1, 0, 1, 5, 100, 1000]; + + for (const value of testValues) { + expect(left(value)).toBe(right(value)); + expect(left(value)).toBe(f(g(h(value)))); + } + }); + + it('associativity holds for type-changing compositions', () => { + const f = uppercase; + const g = stringify; + const h = multiply2; + + // Create intermediate compositions with explicit types + // in order to shut TS up. + const gh: (x: number) => string = compose(g, h); + const fg: (x: number) => string = compose(f, g); + + const left = compose(f, gh); + const right = compose(fg, h); + const direct = compose(f, g, h); + + const expected = f(g(h(5))); // '10' + + expect(left(5)).toBe(expected); + expect(right(5)).toBe(expected); + expect(direct(5)).toBe(expected); + expect(left(5)).toBe(right(5)); + }); + + it('associativity holds for longer composition chains', () => { + const f1 = add3; + const f2 = multiply2; + const f3 = subtract1; + const f4 = add3; + + // Different manual groupings + const f3f4: (x: number) => number = compose(f3, f4); + const f2f3f4: (x: number) => number = compose(f2, f3f4); + const group1 = compose(f1, f2f3f4); + + const f1f2: (x: number) => number = compose(f1, f2); + const group2 = compose(f1f2, f3, f4); + + const direct = compose(f1, f2, f3, f4); + + const testValue = 5; + const expected = f1(f2(f3(f4(testValue)))); + + expect(group1(testValue)).toBe(expected); + expect(group2(testValue)).toBe(expected); + expect(direct(testValue)).toBe(expected); + }); }); - test('associativity holds for longer composition chains', () => { - const f1 = add3; - const f2 = multiply2; - const f3 = subtract1; - const f4 = add3; - - // Different manual groupings - const f3f4: (x: number) => number = compose(f3, f4); - const f2f3f4: (x: number) => number = compose(f2, f3f4); - const group1 = compose(f1, f2f3f4); - - const f1f2: (x: number) => number = compose(f1, f2); - const group2 = compose(f1f2, f3, f4); - - const direct = compose(f1, f2, f3, f4); - - const testValue = 5; - const expected = f1(f2(f3(f4(testValue)))); - - expect(group1(testValue)).toBe(expected); - expect(group2(testValue)).toBe(expected); - expect(direct(testValue)).toBe(expected); - }); -}); + /** + * Axiom: id ∘ f = f = f ∘ id + * + * The identity function must be both a left and right identity + * for composition. Composing with identity doesn't change behavior. + */ + describe('Identity Law', () => { + it('left identity: compose(id, f) ≡ f', () => { + const f = add3; -/** - * Axiom: id ∘ f = f = f ∘ id - * - * The identity function must be both a left and right identity - * for composition. Composing with identity doesn't change behavior. - */ -describe('Identity Laws', () => { - test('Left Identity: compose(id, f) ≡ f', () => { - const f = add3; - - const composedWithId = compose(identity, f); - - const testValue = 10; - expect(composedWithId(testValue)).toBe(f(testValue)); - expect(composedWithId(testValue)).toBe(13); - }); + const composedWithId = compose(identity, f); - test('Right Identity: compose(f, id) ≡ f', () => { - const f = add3; + const testValue = 10; + expect(composedWithId(testValue)).toBe(f(testValue)); + expect(composedWithId(testValue)).toBe(13); + }); - const composedWithId = compose(f, identity); + it('right identity: compose(f, id) ≡ f', () => { + const f = add3; - const testValue = 10; - expect(composedWithId(testValue)).toBe(f(testValue)); - expect(composedWithId(testValue)).toBe(13); - }); + const composedWithId = compose(f, identity); - test('Both identities: compose(id, f, id) ≡ f', () => { - const f = multiply2; + const testValue = 10; + expect(composedWithId(testValue)).toBe(f(testValue)); + expect(composedWithId(testValue)).toBe(13); + }); - const composed = compose(identity, f, identity); + it('both identities: compose(id, f, id) ≡ f', () => { + const f = multiply2; - const testValue = 7; - expect(composed(testValue)).toBe(f(testValue)); - expect(composed(testValue)).toBe(14); - }); + const composed = compose(identity, f, identity); - test('identity laws hold for type-changing functions', () => { - const f = stringify; + const testValue = 7; + expect(composed(testValue)).toBe(f(testValue)); + expect(composed(testValue)).toBe(14); + }); - const leftId = compose(identity, f); - const rightId = compose(f, identity); + it('identity laws hold for type-changing functions', () => { + const f = stringify; - const testValue = 42; - expect(leftId(testValue)).toBe(f(testValue)); - expect(rightId(testValue)).toBe(f(testValue)); - expect(leftId(testValue)).toBe('42'); - }); + const leftId = compose(identity, f); + const rightId = compose(f, identity); - test('compose(id) ≡ id', () => { - const composedId = compose(identity); + const testValue = 42; + expect(leftId(testValue)).toBe(f(testValue)); + expect(rightId(testValue)).toBe(f(testValue)); + expect(leftId(testValue)).toBe('42'); + }); - const testValue = 42; - expect(composedId(testValue)).toBe(identity(testValue)); - expect(composedId(testValue)).toBe(42); - }); -}); + it('compose(id) ≡ id', () => { + const composedId = compose(identity); -/** - * Property: compose(f, g, h)(x) ≡ f(g(h(x))) - * - * Verifies that compose actually performs right-to-left composition. - */ -describe('Composition Equivalence', () => { - test('compose(f, g, h)(x) ≡ f(g(h(x)))', () => { - const f = add3; - const g = multiply2; - const h = subtract1; - - const composed = compose(f, g, h); - - const testValue = 10; - const manualComposition = f(g(h(testValue))); - - expect(composed(testValue)).toBe(manualComposition); - // h(10) = 9, g(9) = 18, f(18) = 21 - expect(composed(testValue)).toBe(21); + const testValue = 42; + expect(composedId(testValue)).toBe(identity(testValue)); + expect(composedId(testValue)).toBe(42); + }); }); - test('compose preserves computation order', () => { - const f = uppercase; - const g = stringify; - const h = multiply2; - - const composed = compose(f, g, h); - - const testValue = 5; - // h(5) = 10, g(10) = '10', f('10') = '10' - expect(composed(testValue)).toBe( - uppercase(stringify(multiply2(testValue))), - ); - expect(composed(testValue)).toBe('10'); + /** + * Property: compose(f, g, h)(x) ≡ f(g(h(x))) + * + * Verifies that compose actually performs right-to-left composition. + */ + describe('Composition Equivalence', () => { + it('compose(f, g, h)(x) ≡ f(g(h(x)))', () => { + const f = add3; + const g = multiply2; + const h = subtract1; + + const composed = compose(f, g, h); + + const testValue = 10; + const manualComposition = f(g(h(testValue))); + + expect(composed(testValue)).toBe(manualComposition); + // h(10) = 9, g(9) = 18, f(18) = 21 + expect(composed(testValue)).toBe(21); + }); + + it('compose preserves computation order', () => { + const f = uppercase; + const g = stringify; + const h = multiply2; + + const composed = compose(f, g, h); + + const testValue = 5; + // h(5) = 10, g(10) = '10', f('10') = '10' + expect(composed(testValue)).toBe( + uppercase(stringify(multiply2(testValue))), + ); + expect(composed(testValue)).toBe('10'); + }); }); }); diff --git a/packages/composition/src/curry/curry.test.ts b/packages/composition/src/curry/curry.test.ts index eb1b686..539644c 100644 --- a/packages/composition/src/curry/curry.test.ts +++ b/packages/composition/src/curry/curry.test.ts @@ -1,12 +1,14 @@ -import { expect, it } from 'vitest'; -import { curry } from './'; +import { describe, expect, it } from 'vitest'; +import { curry } from '.'; const addAndMultiply = (a: number, b: number, c: number) => (a + b) * c; const curriedFn = curry(addAndMultiply); -it('should correctly allow any combination of parameters', () => { - expect(curriedFn(2)(3)(4)).toEqual(20); - expect(curriedFn(2, 3)(4)).toEqual(20); - expect(curriedFn(2)(3, 4)).toEqual(20); - expect(curriedFn(2, 3, 4)).toEqual(20); +describe('Curry', () => { + it('should correctly allow any combination of parameters', () => { + expect(curriedFn(2)(3)(4)).toEqual(20); + expect(curriedFn(2, 3)(4)).toEqual(20); + expect(curriedFn(2)(3, 4)).toEqual(20); + expect(curriedFn(2, 3, 4)).toEqual(20); + }); }); diff --git a/packages/composition/src/pipe-async/pipe-async.test.ts b/packages/composition/src/pipe-async/pipe-async.test.ts index 9977c38..049727d 100644 --- a/packages/composition/src/pipe-async/pipe-async.test.ts +++ b/packages/composition/src/pipe-async/pipe-async.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, test } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { pipeAsync } from '.'; // Async helpers @@ -19,6 +19,7 @@ const stringify = (x: number): string => x.toString(); // Identity functions const asyncIdentity = async (x: T): Promise => x; +const identity = (x: T): T => x; // Error helpers // biome-ignore lint/suspicious/useAwait: This is for testing @@ -30,217 +31,230 @@ const syncThrow = (message: string): never => { throw new Error(message); }; -describe('pipeAsync - Basic Functionality', () => { - test('should pipe async functions left-to-right', async () => { - const piped = pipeAsync(asyncSubtract1, asyncMultiply2, asyncAdd3); +describe('PipeAsync', () => { + describe('Basic Functionality', () => { + it('should pipe async functions left-to-right', async () => { + const piped = pipeAsync(asyncSubtract1, asyncMultiply2, asyncAdd3); - const result = await piped(10); + const result = await piped(10); - // subtract1(10) = 9, multiply2(9) = 18, add3(18) = 21 - expect(result).toBe(21); - }); + // subtract1(10) = 9, multiply2(9) = 18, add3(18) = 21 + expect(result).toBe(21); + }); - test('should handle single async function', async () => { - const piped = pipeAsync(asyncAdd3); + it('should handle single async function', async () => { + const piped = pipeAsync(asyncAdd3); - expect(await piped(5)).toBe(8); - }); + expect(await piped(5)).toBe(8); + }); - test('should handle mixed sync and async functions', async () => { - const piped = pipeAsync( - add3, // sync - asyncMultiply2, // async - stringify, // sync - asyncUppercase, // async - ); + it('should handle mixed sync and async functions', async () => { + const piped = pipeAsync( + add3, // sync + asyncMultiply2, // async + stringify, // sync + asyncUppercase, // async + ); - const result = await piped(5); + const result = await piped(5); - // add3(5) = 8, multiply2(8) = 16, stringify(16) = "16", uppercase("16") = "16" - expect(result).toBe('16'); - }); + // add3(5) = 8, multiply2(8) = 16, stringify(16) = "16", uppercase("16") = "16" + expect(result).toBe('16'); + }); - test('should handle n-ary leftmost function', async () => { - const binaryAdd = async (a: number, b: number): Promise => a + b; + it('should handle n-ary leftmost function', async () => { + const binaryAdd = async (a: number, b: number): Promise => a + b; - const piped = pipeAsync(binaryAdd, asyncAdd3, asyncMultiply2); + const piped = pipeAsync(binaryAdd, asyncAdd3, asyncMultiply2); - const result = await piped(5, 3); + const result = await piped(5, 3); - // binaryAdd(5, 3) = 8, add3(8) = 11, multiply2(11) = 22 - expect(result).toBe(22); - }); + // binaryAdd(5, 3) = 8, add3(8) = 11, multiply2(11) = 22 + expect(result).toBe(22); + }); - test('should handle ternary leftmost function', async () => { - const ternaryAdd = async ( - a: number, - b: number, - c: number, - ): Promise => a + b + c; + it('should handle ternary leftmost function', async () => { + const ternaryAdd = async ( + a: number, + b: number, + c: number, + ): Promise => a + b + c; - const piped = pipeAsync(ternaryAdd, asyncMultiply2, asyncStringify); + const piped = pipeAsync(ternaryAdd, asyncMultiply2, asyncStringify); - const result = await piped(2, 3, 4); + const result = await piped(2, 3, 4); - // ternaryAdd(2, 3, 4) = 9, multiply2(9) = 18, stringify(18) = "18" - expect(result).toBe('18'); - }); + // ternaryAdd(2, 3, 4) = 9, multiply2(9) = 18, stringify(18) = "18" + expect(result).toBe('18'); + }); - test('should return Promise even for all-sync functions', async () => { - const piped = pipeAsync(add3, multiply2); + it('should return Promise even for all-sync functions', async () => { + const piped = pipeAsync(add3, multiply2); - const result = piped(5); + const result = piped(5); - expect(result).toBeInstanceOf(Promise); - expect(await result).toBe(16); + expect(result).toBeInstanceOf(Promise); + expect(await result).toBe(16); + }); }); -}); - -describe('Error Handling', () => { - test('pipeAsync should propagate errors from async functions', async () => { - const piped = pipeAsync( - asyncMultiply2, - async () => asyncThrow('Test error'), - asyncAdd3, - ); - await expect(piped(5)).rejects.toThrow('Test error'); + describe('Error Handling', () => { + it('should propagate errors from async functions', async () => { + const piped = pipeAsync( + asyncMultiply2, + async () => asyncThrow('Test error'), + asyncAdd3, + ); + + await expect(piped(5)).rejects.toThrow('Test error'); + }); + + it('should propagate errors from sync functions', async () => { + const piped = pipeAsync( + asyncMultiply2, + () => syncThrow('Sync error'), + asyncAdd3, + ); + + await expect(piped(5)).rejects.toThrow('Sync error'); + }); + + it('should throw on excessive pipe depth', () => { + const functions = Array(1001).fill(asyncAdd3); + + // @ts-expect-error For testing + expect(() => pipeAsync(...functions)).toThrow(RangeError); + // @ts-expect-error For testing + expect(() => pipeAsync(...functions)).toThrow( + 'Async pipe depth exceeds 1000', + ); + }); }); - test('pipeAsync should propagate errors from sync functions', async () => { - const piped = pipeAsync( - asyncMultiply2, - () => syncThrow('Sync error'), - asyncAdd3, - ); - - await expect(piped(5)).rejects.toThrow('Sync error'); + describe('Async Execution Order', () => { + it('should execute in correct order', async () => { + const executionOrder: number[] = []; + + const fn1 = async (x: number) => { + await executionOrder.push(1); + return x + 1; + }; + const fn2 = async (x: number) => { + await executionOrder.push(2); + return x * 2; + }; + const fn3 = async (x: number) => { + await executionOrder.push(3); + return x - 1; + }; + + const piped = pipeAsync(fn1, fn2, fn3); + await piped(10); + + // Left-to-right: fn1, fn2, fn3 + expect(executionOrder).toEqual([1, 2, 3]); + }); + + it('should await each function before calling next', async () => { + const results: number[] = []; + + const fn1 = async (x: number) => { + await asyncDelay(30, x); + results.push(1); + return x + 1; + }; + const fn2 = async (x: number) => { + await asyncDelay(20, x); + results.push(2); + return x * 2; + }; + const fn3 = async (x: number) => { + await asyncDelay(10, x); + results.push(3); + return x - 1; + }; + + const piped = pipeAsync(fn1, fn2, fn3); + await piped(10); + + // Should execute sequentially, not in parallel + expect(results).toEqual([1, 2, 3]); + }); }); - test('should throw on excessive pipe depth', () => { - const functions = Array(1001).fill(asyncAdd3); - - // @ts-expect-error For testing - expect(() => pipeAsync(...functions)).toThrow(RangeError); - // @ts-expect-error For testing - expect(() => pipeAsync(...functions)).toThrow( - 'Async pipe depth exceeds 1000', - ); - }); -}); + describe('Associativity Law', () => { + it('pipe(f, g, h) ≡ manual groupings', async () => { + const f = asyncAdd3; + const g = asyncMultiply2; + const h = asyncSubtract1; -describe('Async Execution Order', () => { - test('pipeAsync should execute in correct order', async () => { - const executionOrder: number[] = []; - - const fn1 = async (x: number) => { - await executionOrder.push(1); - return x + 1; - }; - const fn2 = async (x: number) => { - await executionOrder.push(2); - return x * 2; - }; - const fn3 = async (x: number) => { - await executionOrder.push(3); - return x - 1; - }; - - const piped = pipeAsync(fn1, fn2, fn3); - await piped(10); - - // Left-to-right: fn1, fn2, fn3 - expect(executionOrder).toEqual([1, 2, 3]); - }); + const fg: (x: number) => Promise = pipeAsync(f, g); + const gh: (x: number) => Promise = pipeAsync(g, h); - test('should await each function before calling next', async () => { - const results: number[] = []; - - const fn1 = async (x: number) => { - await asyncDelay(30, x); - results.push(1); - return x + 1; - }; - const fn2 = async (x: number) => { - await asyncDelay(20, x); - results.push(2); - return x * 2; - }; - const fn3 = async (x: number) => { - await asyncDelay(10, x); - results.push(3); - return x - 1; - }; - - const piped = pipeAsync(fn1, fn2, fn3); - await piped(10); - - // Should execute sequentially, not in parallel - expect(results).toEqual([1, 2, 3]); - }); -}); + const left = pipeAsync(fg, h); + const right = pipeAsync(f, gh); + const direct = pipeAsync(f, g, h); -describe('Associativity', () => { - test('pipe(f, g, h) ≡ manual groupings', async () => { - const f = asyncAdd3; - const g = asyncMultiply2; - const h = asyncSubtract1; + const testValue = 10; + const expected = await h(await g(await f(testValue))); - const fg: (x: number) => Promise = pipeAsync(f, g); - const gh: (x: number) => Promise = pipeAsync(g, h); + expect(await left(testValue)).toBe(expected); + expect(await right(testValue)).toBe(expected); + expect(await direct(testValue)).toBe(expected); + }); - const left = pipeAsync(fg, h); - const right = pipeAsync(f, gh); - const direct = pipeAsync(f, g, h); + it('associativity holds with mixed sync/async', async () => { + const f = asyncAdd3; + const g = multiply2; // sync + const h = asyncSubtract1; - const testValue = 10; - const expected = await h(await g(await f(testValue))); + const fg: (x: number) => Promise = pipeAsync(f, g); + const gh: (x: number) => Promise = pipeAsync(g, h); - expect(await left(testValue)).toBe(expected); - expect(await right(testValue)).toBe(expected); - expect(await direct(testValue)).toBe(expected); - }); + const left = pipeAsync(fg, h); + const right = pipeAsync(f, gh); - test('associativity holds with mixed sync/async', async () => { - const f = asyncAdd3; - const g = multiply2; // sync - const h = asyncSubtract1; + const testValue = 10; - const fg: (x: number) => Promise = pipeAsync(f, g); - const gh: (x: number) => Promise = pipeAsync(g, h); + expect(await left(testValue)).toBe(await right(testValue)); + }); + }); - const left = pipeAsync(fg, h); - const right = pipeAsync(f, gh); + describe('Identity Law', () => { + it('left identity: pipe(id, f) ≡ f', async () => { + const f = asyncAdd3; - const testValue = 10; + const piped = pipeAsync(asyncIdentity, f); - expect(await left(testValue)).toBe(await right(testValue)); - }); -}); + const testValue = 10; + expect(await piped(testValue)).toBe(await f(testValue)); + }); -describe('Mathematical Properties - Identity', () => { - test('left identity: pipe(id, f) ≡ f', async () => { - const f = asyncAdd3; + it('right identity: pipe(f, id) ≡ f', async () => { + const f = asyncAdd3; - const piped = pipeAsync(asyncIdentity, f); + const piped = pipeAsync(f, asyncIdentity); - const testValue = 10; - expect(await piped(testValue)).toBe(await f(testValue)); - }); + const testValue = 10; + expect(await piped(testValue)).toBe(await f(testValue)); + }); - test('right identity: pipe(f, id) ≡ f', async () => { - const f = asyncAdd3; + it('associativity holds with mixed sync/async', async () => { + const f = asyncAdd3; - const piped = pipeAsync(f, asyncIdentity); + const leftId = pipeAsync(identity, f); + const rightId = pipeAsync(f, identity); - const testValue = 10; - expect(await piped(testValue)).toBe(await f(testValue)); - }); + const testValue = 10; + expect(await leftId(testValue)).toBe(await f(testValue)); + expect(await rightId(testValue)).toBe(await f(testValue)); + }); - test('pipe(id) ≡ id', async () => { - const pipedId = pipeAsync(asyncIdentity); + it('pipe(id) ≡ id', async () => { + const pipedId = pipeAsync(asyncIdentity); - const testValue = 42; - expect(await pipedId(testValue)).toBe(await asyncIdentity(testValue)); + const testValue = 42; + expect(await pipedId(testValue)).toBe(await asyncIdentity(testValue)); + }); }); }); diff --git a/packages/composition/src/pipe/pipe.test.ts b/packages/composition/src/pipe/pipe.test.ts index 1812578..62589a6 100644 --- a/packages/composition/src/pipe/pipe.test.ts +++ b/packages/composition/src/pipe/pipe.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, test } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { pipe } from '.'; const identity = (x: T): T => x; @@ -24,239 +24,246 @@ const stringifyNum = (x: number) => 'nine', ][x] ?? x.toString(); -describe('Pipe - Basic Functionality', () => { - test('it should support unary for first fn call and return the correct result', () => { - const explodedStrNumVal = pipe(add3, stringifyNum, uppercase, splitIt); - const upperStrNumVal = pipe( - add3, - stringifyNum, - splitIt, - upperMap, - (a: string[]) => a.join(''), - ); - - const actualOne = explodedStrNumVal(3); - const actualTwo = upperStrNumVal(3); - - expect(actualOne).toStrictEqual(['S', 'I', 'X']); - expect(actualTwo).toBe('SIX'); +describe('Pipe', () => { + describe('Basic Functionality', () => { + it('should support unary for first fn call and return the correct result', () => { + const explodedStrNumVal = pipe(add3, stringifyNum, uppercase, splitIt); + const upperStrNumVal = pipe( + add3, + stringifyNum, + splitIt, + upperMap, + (a: string[]) => a.join(''), + ); + + const actualOne = explodedStrNumVal(3); + const actualTwo = upperStrNumVal(3); + + expect(actualOne).toStrictEqual(['S', 'I', 'X']); + expect(actualTwo).toBe('SIX'); + }); + + it('should support n-ary for first fn call and return the correct result', () => { + const explodedStrNumVal = pipe( + binaryAdd, + stringifyNum, + uppercase, + splitIt, + ); + const upperStrNumVal = pipe( + binaryAdd, + stringifyNum, + splitIt, + upperMap, + (a: string[]) => a.join(''), + ); + + const actualOne = explodedStrNumVal(3, 3); + const actualTwo = upperStrNumVal(3, 3); + + expect(actualOne).toStrictEqual(['S', 'I', 'X']); + expect(actualTwo).toBe('SIX'); + }); }); - test('it should support n-ary for first fn call and return the correct result', () => { - const explodedStrNumVal = pipe(binaryAdd, stringifyNum, uppercase, splitIt); - const upperStrNumVal = pipe( - binaryAdd, - stringifyNum, - splitIt, - upperMap, - (a: string[]) => a.join(''), - ); - - const actualOne = explodedStrNumVal(3, 3); - const actualTwo = upperStrNumVal(3, 3); - - expect(actualOne).toStrictEqual(['S', 'I', 'X']); - expect(actualTwo).toBe('SIX'); - }); -}); - -describe('Pipe with n-ary Functions', () => { - test('pipe(f, g, h) where f is binary', () => { - const f = (a: number, b: number): number => a + b; - const g = multiply2; - const h = add3; - - const piped = pipe(f, g, h); - - // f(5, 3) = 8, g(8) = 16, h(16) = 19 - expect(piped(5, 3)).toBe(19); - expect(piped(5, 3)).toBe(h(g(f(5, 3)))); - }); - - test('pipe with ternary leftmost function', () => { - const f = (a: number, b: number, c: number): number => a + b + c; - const g = multiply2; - const h = stringify; - - const piped = pipe(f, g, h); - - // f(2, 3, 4) = 9, g(9) = 18, h(18) = '18' - expect(piped(2, 3, 4)).toBe('18'); - expect(piped(2, 3, 4)).toBe(h(g(f(2, 3, 4)))); - }); -}); - -/** - * Axiom: (h | g) | f = h | (g | f) - * Where | represents pipe - * - * Pipe must also be associative, just with left-to-right ordering. - */ -describe('Associativity Law', () => { - test('pipe(pipe(f, g), h) ≡ pipe(f, pipe(g, h))', () => { - const f = add3; - const g = multiply2; - const h = subtract1; - - // Left association: (f | g) | h - const left = pipe(pipe(f, g), h); - - // Right association: f | (g | h) - const right = pipe(f, pipe(g, h)); - - // Direct pipe - const direct = pipe(f, g, h); - - const testValue = 10; - const expected = h(g(f(testValue))); // 25 - - expect(left(testValue)).toBe(expected); - expect(right(testValue)).toBe(expected); - expect(direct(testValue)).toBe(expected); - expect(left(testValue)).toBe(right(testValue)); - }); - - test('associativity holds for multiple test values', () => { - const f = add3; - const g = multiply2; - const h = subtract1; - - const left = pipe(pipe(f, g), h); - const right = pipe(f, pipe(g, h)); + describe('Pipe with n-ary Functions', () => { + it('should pipe(f, g, h) where f is binary', () => { + const f = (a: number, b: number): number => a + b; + const g = multiply2; + const h = add3; - const testValues = [-10, -1, 0, 1, 5, 100, 1000]; + const piped = pipe(f, g, h); - for (const value of testValues) { - expect(left(value)).toBe(right(value)); - expect(left(value)).toBe(h(g(f(value)))); - } - }); - - test('associativity holds for type-changing pipes', () => { - const f = multiply2; - const g = stringify; - const h = uppercase; + // f(5, 3) = 8, g(8) = 16, h(16) = 19 + expect(piped(5, 3)).toBe(19); + expect(piped(5, 3)).toBe(h(g(f(5, 3)))); + }); - const left = pipe(pipe(f, g), h); - const right = pipe(f, pipe(g, h)); - const direct = pipe(f, g, h); + it('should pipe with a ternary leftmost function', () => { + const f = (a: number, b: number, c: number): number => a + b + c; + const g = multiply2; + const h = stringify; - const expected = h(g(f(5))); // '10' + const piped = pipe(f, g, h); - expect(left(5)).toBe(expected); - expect(right(5)).toBe(expected); - expect(direct(5)).toBe(expected); - expect(left(5)).toBe(right(5)); + // f(2, 3, 4) = 9, g(9) = 18, h(18) = '18' + expect(piped(2, 3, 4)).toBe('18'); + expect(piped(2, 3, 4)).toBe(h(g(f(2, 3, 4)))); + }); }); - test('associativity holds for longer pipe chains', () => { - const f1 = add3; - const f2 = multiply2; - const f3 = subtract1; - const f4 = add3; - - // Different groupings should produce same result - const group1 = pipe(f1, pipe(f2, pipe(f3, f4))); - const group2 = pipe(pipe(f1, f2), pipe(f3, f4)); - const direct = pipe(f1, f2, f3, f4); - - const testValue = 5; - const expected = f4(f3(f2(f1(testValue)))); - - expect(group1(testValue)).toBe(expected); - expect(group2(testValue)).toBe(expected); - expect(direct(testValue)).toBe(expected); + /** + * Axiom: (h | g) | f = h | (g | f) + * Where | represents pipe + * + * Pipe must also be associative, just with left-to-right ordering. + */ + describe('Associativity Law', () => { + it('pipe(pipe(f, g), h) ≡ pipe(f, pipe(g, h))', () => { + const f = add3; + const g = multiply2; + const h = subtract1; + + // Left association: (f | g) | h + const left = pipe(pipe(f, g), h); + + // Right association: f | (g | h) + const right = pipe(f, pipe(g, h)); + + // Direct pipe + const direct = pipe(f, g, h); + + const testValue = 10; + const expected = h(g(f(testValue))); // 25 + + expect(left(testValue)).toBe(expected); + expect(right(testValue)).toBe(expected); + expect(direct(testValue)).toBe(expected); + expect(left(testValue)).toBe(right(testValue)); + }); + + it('associativity holds for multiple test values', () => { + const f = add3; + const g = multiply2; + const h = subtract1; + + const left = pipe(pipe(f, g), h); + const right = pipe(f, pipe(g, h)); + + const testValues = [-10, -1, 0, 1, 5, 100, 1000]; + + for (const value of testValues) { + expect(left(value)).toBe(right(value)); + expect(left(value)).toBe(h(g(f(value)))); + } + }); + + it('associativity holds for type-changing pipes', () => { + const f = multiply2; + const g = stringify; + const h = uppercase; + + const left = pipe(pipe(f, g), h); + const right = pipe(f, pipe(g, h)); + const direct = pipe(f, g, h); + + const expected = h(g(f(5))); // '10' + + expect(left(5)).toBe(expected); + expect(right(5)).toBe(expected); + expect(direct(5)).toBe(expected); + expect(left(5)).toBe(right(5)); + }); + + it('associativity holds for longer pipe chains', () => { + const f1 = add3; + const f2 = multiply2; + const f3 = subtract1; + const f4 = add3; + + // Different groupings should produce same result + const group1 = pipe(f1, pipe(f2, pipe(f3, f4))); + const group2 = pipe(pipe(f1, f2), pipe(f3, f4)); + const direct = pipe(f1, f2, f3, f4); + + const testValue = 5; + const expected = f4(f3(f2(f1(testValue)))); + + expect(group1(testValue)).toBe(expected); + expect(group2(testValue)).toBe(expected); + expect(direct(testValue)).toBe(expected); + }); }); -}); -/** - * Axiom: id | f = f = f | id - * - * Identity must be both left and right identity for pipe. - */ -describe('Identity Laws', () => { - test('Left Identity: pipe(id, f) ≡ f', () => { - const f = add3; + /** + * Axiom: id | f = f = f | id + * + * Identity must be both left and right identity for pipe. + */ + describe('Identity Law', () => { + it('left identity: pipe(id, f) ≡ f', () => { + const f = add3; - const pipedWithId = pipe(identity, f); + const pipedWithId = pipe(identity, f); - const testValue = 10; - expect(pipedWithId(testValue)).toBe(f(testValue)); - expect(pipedWithId(testValue)).toBe(13); - }); + const testValue = 10; + expect(pipedWithId(testValue)).toBe(f(testValue)); + expect(pipedWithId(testValue)).toBe(13); + }); - test('Right Identity: pipe(f, id) ≡ f', () => { - const f = add3; + it('right identity: pipe(f, id) ≡ f', () => { + const f = add3; - const pipedWithId = pipe(f, identity); + const pipedWithId = pipe(f, identity); - const testValue = 10; - expect(pipedWithId(testValue)).toBe(f(testValue)); - expect(pipedWithId(testValue)).toBe(13); - }); + const testValue = 10; + expect(pipedWithId(testValue)).toBe(f(testValue)); + expect(pipedWithId(testValue)).toBe(13); + }); - test('Both identities: pipe(id, f, id) ≡ f', () => { - const f = multiply2; + it('Both identities: pipe(id, f, id) ≡ f', () => { + const f = multiply2; - const piped = pipe(identity, f, identity); + const piped = pipe(identity, f, identity); - const testValue = 7; - expect(piped(testValue)).toBe(f(testValue)); - expect(piped(testValue)).toBe(14); - }); + const testValue = 7; + expect(piped(testValue)).toBe(f(testValue)); + expect(piped(testValue)).toBe(14); + }); - test('identity laws hold for type-changing functions', () => { - const f = stringify; + it('identity laws hold for type-changing functions', () => { + const f = stringify; - const leftId = pipe(identity, f); - const rightId = pipe(f, identity); + const leftId = pipe(identity, f); + const rightId = pipe(f, identity); - const testValue = 42; - expect(leftId(testValue)).toBe(f(testValue)); - expect(rightId(testValue)).toBe(f(testValue)); - expect(leftId(testValue)).toBe('42'); - }); + const testValue = 42; + expect(leftId(testValue)).toBe(f(testValue)); + expect(rightId(testValue)).toBe(f(testValue)); + expect(leftId(testValue)).toBe('42'); + }); - test('pipe(id) ≡ id', () => { - const pipedId = pipe(identity); + it('pipe(id) ≡ id', () => { + const pipedId = pipe(identity); - const testValue = 42; - expect(pipedId(testValue)).toBe(identity(testValue)); - expect(pipedId(testValue)).toBe(42); + const testValue = 42; + expect(pipedId(testValue)).toBe(identity(testValue)); + expect(pipedId(testValue)).toBe(42); + }); }); -}); - -/** - * Property: pipe(f, g, h)(x) ≡ h(g(f(x))) - * - * Verifies that pipe actually performs left-to-right composition. - */ -describe('Pipe Equivalence', () => { - test('pipe(f, g, h)(x) ≡ h(g(f(x)))', () => { - const f = subtract1; - const g = multiply2; - const h = add3; - - const piped = pipe(f, g, h); - - const testValue = 10; - const manualPipe = h(g(f(testValue))); - - expect(piped(testValue)).toBe(manualPipe); - // f(10) = 9, g(9) = 18, h(18) = 21 - expect(piped(testValue)).toBe(21); - }); - - test('pipe preserves computation order', () => { - const f = multiply2; - const g = stringify; - const h = uppercase; - - const piped = pipe(f, g, h); - const testValue = 5; - // f(5) = 10, g(10) = '10', h('10') = '10' - expect(piped(testValue)).toBe(uppercase(stringify(multiply2(testValue)))); - expect(piped(testValue)).toBe('10'); + /** + * Property: pipe(f, g, h)(x) ≡ h(g(f(x))) + * + * Verifies that pipe actually performs left-to-right composition. + */ + describe('Pipe Equivalence', () => { + it('pipe(f, g, h)(x) ≡ h(g(f(x)))', () => { + const f = subtract1; + const g = multiply2; + const h = add3; + + const piped = pipe(f, g, h); + + const testValue = 10; + const manualPipe = h(g(f(testValue))); + + expect(piped(testValue)).toBe(manualPipe); + // f(10) = 9, g(9) = 18, h(18) = 21 + expect(piped(testValue)).toBe(21); + }); + + it('pipe preserves computation order', () => { + const f = multiply2; + const g = stringify; + const h = uppercase; + + const piped = pipe(f, g, h); + + const testValue = 5; + // f(5) = 10, g(10) = '10', h('10') = '10' + expect(piped(testValue)).toBe(uppercase(stringify(multiply2(testValue)))); + expect(piped(testValue)).toBe('10'); + }); }); }); diff --git a/packages/composition/src/readme.test.ts b/packages/composition/src/readme.test.ts new file mode 100644 index 0000000..5aca1dd --- /dev/null +++ b/packages/composition/src/readme.test.ts @@ -0,0 +1,321 @@ +import { describe, expect, it } from 'vitest'; +import { compose, composeAsync, curry, pipe, pipeAsync } from './'; + +describe('README Examples', () => { + describe('Synchronous Composition', () => { + describe('Compose', () => { + it('should transform number to uppercase string', () => { + const transform = compose( + (x: string) => x.toUpperCase(), + (x: number) => x.toString(), + (x: number) => x + 3, + ); + + expect(transform(4)).toBe('7'); + }); + + it('should work with different input values', () => { + const transform = compose( + (x: string) => x.toUpperCase(), + (x: number) => x.toString(), + (x: number) => x + 3, + ); + + expect(transform(0)).toBe('3'); + expect(transform(7)).toBe('10'); + expect(transform(-3)).toBe('0'); + }); + }); + + describe('Pipe', () => { + it('should transform number to uppercase string', () => { + const process = pipe( + (x: number) => x + 3, + (x: number) => x.toString(), + (x: string) => x.toUpperCase(), + ); + + expect(process(4)).toBe('7'); + }); + + it('should work with different input values', () => { + const process = pipe( + (x: number) => x + 3, + (x: number) => x.toString(), + (x: string) => x.toUpperCase(), + ); + + expect(process(0)).toBe('3'); + expect(process(7)).toBe('10'); + expect(process(-3)).toBe('0'); + }); + }); + + describe('Curry', () => { + it('should curry multiply function - all variations', () => { + const multiply = (a: number, b: number, c: number) => a * b * c; + const curried = curry(multiply); + + expect(curried(2)(3)(4)).toBe(24); + expect(curried(2, 3)(4)).toBe(24); + expect(curried(2)(3, 4)).toBe(24); + }); + + it('should work with all arguments at once', () => { + const multiply = (a: number, b: number, c: number) => a * b * c; + const curried = curry(multiply); + + expect(curried(2, 3, 4)).toBe(24); + }); + + it('should work with different values', () => { + const multiply = (a: number, b: number, c: number) => a * b * c; + const curried = curry(multiply); + + expect(curried(1)(2)(3)).toBe(6); + expect(curried(5, 5)(2)).toBe(50); + expect(curried(10)(1, 1)).toBe(10); + }); + }); + }); + + describe('Asynchronous Composition', () => { + describe('ComposeAsync', () => { + type User = { + id: number; + name: string; + email: string; + }; + + it('should compose async user notification flow', async () => { + const fetchUser = async (id: number): Promise => ({ + id, + name: 'Test User', + email: 'test@example.com', + }); + + // biome-ignore lint/suspicious/useAwait: For testing + const sendEmail = async (email: string): Promise => { + expect(email).toBe('test@example.com'); + return true; + }; + + const notifyUser = composeAsync( + async (email: string) => sendEmail(email), + (user: User) => user.email, + async (id: number) => fetchUser(id), + ); + + const result = await notifyUser(123); + expect(result).toBe(true); + }); + + it('should work with different user IDs', async () => { + const users: Record = { + 1: { id: 1, name: 'Alice', email: 'alice@example.com' }, + 2: { id: 2, name: 'Bob', email: 'bob@example.com' }, + }; + + const fetchUser = async (id: number): Promise => users[id]; + const sendEmail = async (email: string): Promise => + `Sent to ${email}`; + + const notifyUser = composeAsync( + async (email: string) => sendEmail(email), + (user: User) => user.email, + async (id: number) => fetchUser(id), + ); + + expect(await notifyUser(1)).toBe('Sent to alice@example.com'); + expect(await notifyUser(2)).toBe('Sent to bob@example.com'); + }); + }); + + describe('PipeAsync', () => { + type Data = { + value: number; + timestamp: number; + }; + + type Result = { + processed: number; + saved: boolean; + }; + + it('should pipe async URL processing flow', async () => { + // biome-ignore lint/suspicious/useAwait: For testing + const mockFetch = async (url: string): Promise => { + expect(url).toBe('https://api.example.com/data'); + return { + json: async () => ({ value: 42, timestamp: Date.now() }), + } as Response; + }; + + const transform = (data: Data): Result => ({ + processed: data.value * 2, + saved: false, + }); + + const saveToDb = async (result: Result): Promise => ({ + ...result, + saved: true, + }); + + const processUrl = pipeAsync( + async (url: string) => mockFetch(url), + async (response: Response) => response.json(), + (data: Data) => transform(data), + async (result: Result) => saveToDb(result), + ); + + const result = await processUrl('https://api.example.com/data'); + + expect(result.processed).toBe(84); + expect(result.saved).toBe(true); + }); + + it('should handle different data values', async () => { + const mockFetch = async (_url: string): Promise => + ({ + json: async () => ({ value: 10, timestamp: Date.now() }), + }) as Response; + + const transform = (data: Data): number => data.value * 3; + const saveToDb = async (value: number): Promise => + `Saved: ${value}`; + + const processUrl = pipeAsync( + async (url: string) => mockFetch(url), + async (response: Response) => response.json(), + (data: Data) => transform(data), + async (result: number) => saveToDb(result), + ); + + expect(await processUrl('https://api.example.com/data')).toBe( + 'Saved: 30', + ); + }); + }); + + describe('Mixed Sync/Async Example', () => { + type Data = { + value: number; + metadata: string; + }; + + it('should mix sync and async functions', async () => { + const fetchData = async (x: number): Promise => ({ + value: x * 2, + metadata: `Fetched ${x}`, + }); + + // biome-ignore lint/suspicious/useAwait: For testing + const save = async (value: number): Promise => { + expect(value).toBeGreaterThan(0); + return true; + }; + + const pipeline = pipeAsync( + async (x: number) => fetchData(x), + (data: Data) => data.value, + async (value: number) => save(value), + ); + + const result = await pipeline(42); + expect(result).toBe(true); + }); + + it('should work with different input values', async () => { + const fetchData = async (x: number): Promise => ({ + value: x + 10, + metadata: 'test', + }); + + const save = async (value: number): Promise => value * 2; + + const pipeline = pipeAsync( + async (x: number) => fetchData(x), + (data: Data) => data.value, + async (value: number) => save(value), + ); + + expect(await pipeline(5)).toBe(30); // (5 + 10) * 2 + expect(await pipeline(10)).toBe(40); // (10 + 10) * 2 + }); + }); + }); + + describe('API Reference', () => { + describe('compose', () => { + it('compose(f, g, h)(x) === f(g(h(x)))', () => { + const f = (x: number) => x * 2; + const g = (x: number) => x + 5; + const h = (x: number) => x - 1; + + const fn = compose(f, g, h); + + const x = 10; + expect(fn(x)).toBe(f(g(h(x)))); + expect(fn(x)).toBe(28); // (10 - 1 + 5) * 2 + }); + }); + + describe('pipe', () => { + it('pipe(f, g, h)(x) === h(g(f(x)))', () => { + const f = (x: number) => x - 1; + const g = (x: number) => x + 5; + const h = (x: number) => x * 2; + + const fn = pipe(f, g, h); + + const x = 10; + expect(fn(x)).toBe(h(g(f(x)))); + expect(fn(x)).toBe(28); // (10 - 1 + 5) * 2 + }); + }); + + describe('composeAsync', () => { + it('await composeAsync(f, g, h)(x) === await f(await g(await h(x)))', async () => { + const f = async (x: number) => x * 2; + const g = async (x: number) => x + 5; + const h = async (x: number) => x - 1; + + const fn = composeAsync(f, g, h); + + const x = 10; + const result = await fn(x); + const expected = await f(await g(await h(x))); + + expect(result).toBe(expected); + expect(result).toBe(28); // (10 - 1 + 5) * 2 + }); + }); + + describe('pipeAsync', () => { + it('await pipeAsync(f, g, h)(x) === await h(await g(await f(x)))', async () => { + const f = async (x: number) => x - 1; + const g = async (x: number) => x + 5; + const h = async (x: number) => x * 2; + + const fn = pipeAsync(f, g, h); + + const x = 10; + const result = await fn(x); + const expected = await h(await g(await f(x))); + + expect(result).toBe(expected); + expect(result).toBe(28); // (10 - 1 + 5) * 2 + }); + }); + + describe('curry', () => { + it('should curry add function', () => { + const add = (a: number, b: number, c: number) => a + b + c; + const curriedAdd = curry(add); + + const add5 = curriedAdd(5); + expect(add5(3, 2)).toBe(10); + }); + }); + }); +}); diff --git a/packages/lazy/src/generators/generate/generate.test.ts b/packages/lazy/src/generators/generate/generate.test.ts index 635b4bd..2b2584b 100644 --- a/packages/lazy/src/generators/generate/generate.test.ts +++ b/packages/lazy/src/generators/generate/generate.test.ts @@ -3,7 +3,7 @@ import { collect } from '../../helpers/collect'; import { take } from '../../iterators/take'; import { generate } from '.'; -describe('generate', () => { +describe('Generate', () => { it('should yield fn(0), fn(1), fn(2), ...', () => { const result = collect(take(5)(generate((i) => i * i))); diff --git a/packages/lazy/src/generators/iterate/iterate.test.ts b/packages/lazy/src/generators/iterate/iterate.test.ts index d67398d..55ad9a6 100644 --- a/packages/lazy/src/generators/iterate/iterate.test.ts +++ b/packages/lazy/src/generators/iterate/iterate.test.ts @@ -3,7 +3,7 @@ import { collect } from '../../helpers/collect'; import { take } from '../../iterators/take'; import { iterate } from '.'; -describe('iterate', () => { +describe('Iterate', () => { it('should yield seed then successive applications of fn', () => { const result = collect(take(5)(iterate((x: number) => x * 2)(1))); diff --git a/packages/lazy/src/generators/range/range.test.ts b/packages/lazy/src/generators/range/range.test.ts index e80b362..89aed03 100644 --- a/packages/lazy/src/generators/range/range.test.ts +++ b/packages/lazy/src/generators/range/range.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { range } from '.'; -describe('range', () => { +describe('Range', () => { it('should yield integers from start to end inclusive', () => { const result = [...range(1, 5)]; diff --git a/packages/lazy/src/generators/repeat/repeat.test.ts b/packages/lazy/src/generators/repeat/repeat.test.ts index 7fb44f2..a3cb846 100644 --- a/packages/lazy/src/generators/repeat/repeat.test.ts +++ b/packages/lazy/src/generators/repeat/repeat.test.ts @@ -3,7 +3,7 @@ import { collect } from '../../helpers/collect'; import { take } from '../../iterators/take'; import { repeat } from '.'; -describe('repeat', () => { +describe('Repeat', () => { it('should yield the same value indefinitely', () => { const result = collect(take(5)(repeat('x'))); diff --git a/packages/lazy/src/helpers/collect/collect.test.ts b/packages/lazy/src/helpers/collect/collect.test.ts index be3eee7..52563cb 100644 --- a/packages/lazy/src/helpers/collect/collect.test.ts +++ b/packages/lazy/src/helpers/collect/collect.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { collect } from '.'; -describe('collect', () => { +describe('Collect', () => { it('should convert an iterable to an array', () => { const result = collect([1, 2, 3]); diff --git a/packages/lazy/src/helpers/take-eager/take-eager.test.ts b/packages/lazy/src/helpers/take-eager/take-eager.test.ts index 5c58dc8..223225d 100644 --- a/packages/lazy/src/helpers/take-eager/take-eager.test.ts +++ b/packages/lazy/src/helpers/take-eager/take-eager.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { takeEager } from '.'; -describe('takeEager', () => { +describe('TakeEager', () => { describe('positive cases', () => { it('should take n elements and return an array', () => { const result = takeEager(3)([1, 2, 3, 4, 5]); diff --git a/packages/lazy/src/iterators/chunk/chunk.test.ts b/packages/lazy/src/iterators/chunk/chunk.test.ts index a6218ad..b8a309d 100644 --- a/packages/lazy/src/iterators/chunk/chunk.test.ts +++ b/packages/lazy/src/iterators/chunk/chunk.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { collect } from '../../helpers/collect'; import { chunk } from '.'; -describe('chunk', () => { +describe('Chunk', () => { it('should group elements into fixed-size arrays', () => { const result = collect(chunk(2)([1, 2, 3, 4, 5, 6])); diff --git a/packages/lazy/src/iterators/concat/concat.test.ts b/packages/lazy/src/iterators/concat/concat.test.ts index b5e4385..d228e57 100644 --- a/packages/lazy/src/iterators/concat/concat.test.ts +++ b/packages/lazy/src/iterators/concat/concat.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { collect } from '../../helpers/collect'; import { concat } from '.'; -describe('concat', () => { +describe('Concat', () => { it('should append iterables after the source', () => { const result = collect(concat([4, 5], [6])([1, 2, 3])); diff --git a/packages/lazy/src/iterators/drop-while/drop-while.test.ts b/packages/lazy/src/iterators/drop-while/drop-while.test.ts index 95820b4..3842e7f 100644 --- a/packages/lazy/src/iterators/drop-while/drop-while.test.ts +++ b/packages/lazy/src/iterators/drop-while/drop-while.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { collect } from '../../helpers/collect'; import { dropWhile } from '.'; -describe('dropWhile', () => { +describe('DropWhile', () => { it('should skip elements while predicate is true', () => { const result = collect(dropWhile((x: number) => x < 3)([1, 2, 3, 4, 1, 2])); diff --git a/packages/lazy/src/iterators/drop/drop.test.ts b/packages/lazy/src/iterators/drop/drop.test.ts index ef01f9a..70f9069 100644 --- a/packages/lazy/src/iterators/drop/drop.test.ts +++ b/packages/lazy/src/iterators/drop/drop.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { collect } from '../../helpers/collect'; import { drop } from '.'; -describe('drop', () => { +describe('Drop', () => { it('should skip the first n elements', () => { const result = collect(drop(2)([1, 2, 3, 4, 5])); diff --git a/packages/lazy/src/iterators/enumerate/enumerate.test.ts b/packages/lazy/src/iterators/enumerate/enumerate.test.ts index 0218198..4c55018 100644 --- a/packages/lazy/src/iterators/enumerate/enumerate.test.ts +++ b/packages/lazy/src/iterators/enumerate/enumerate.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { collect } from '../../helpers/collect'; import { enumerate } from '.'; -describe('enumerate', () => { +describe('Enumerate', () => { it('should pair each element with its index', () => { const result = collect(enumerate(['a', 'b', 'c'])); diff --git a/packages/lazy/src/iterators/filter/filter.test.ts b/packages/lazy/src/iterators/filter/filter.test.ts index e59c717..81318c2 100644 --- a/packages/lazy/src/iterators/filter/filter.test.ts +++ b/packages/lazy/src/iterators/filter/filter.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it, vi } from 'vitest'; import { collect } from '../../helpers/collect'; import { filter } from '.'; -describe('filter', () => { +describe('Filter', () => { it('should yield only elements satisfying the predicate', () => { const result = collect(filter((x: number) => x % 2 === 0)([1, 2, 3, 4, 5])); diff --git a/packages/lazy/src/iterators/flat-map/flat-map.test.ts b/packages/lazy/src/iterators/flat-map/flat-map.test.ts index 9cde782..dd727f0 100644 --- a/packages/lazy/src/iterators/flat-map/flat-map.test.ts +++ b/packages/lazy/src/iterators/flat-map/flat-map.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { collect } from '../../helpers/collect'; import { flatMap } from '.'; -describe('flatMap', () => { +describe('Flat Map', () => { it('should map and flatten results', () => { const result = collect(flatMap((x: number) => [x, x * 2])([1, 2, 3])); diff --git a/packages/lazy/src/iterators/flatten/flatten.test.ts b/packages/lazy/src/iterators/flatten/flatten.test.ts index a19baa4..c750261 100644 --- a/packages/lazy/src/iterators/flatten/flatten.test.ts +++ b/packages/lazy/src/iterators/flatten/flatten.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { collect } from '../../helpers/collect'; import { flatten } from '.'; -describe('flatten', () => { +describe('Flatten', () => { it('should flatten nested iterables by one level', () => { const result = collect(flatten([[1, 2], [3, 4], [5]])); diff --git a/packages/lazy/src/iterators/interleave/interleave.test.ts b/packages/lazy/src/iterators/interleave/interleave.test.ts index 87c6dba..e6afe30 100644 --- a/packages/lazy/src/iterators/interleave/interleave.test.ts +++ b/packages/lazy/src/iterators/interleave/interleave.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { collect } from '../../helpers/collect'; import { interleave } from '.'; -describe('interleave', () => { +describe('Interleave', () => { it('should alternate elements from two iterables', () => { const result = collect(interleave([4, 5, 6])([1, 2, 3])); diff --git a/packages/lazy/src/iterators/intersperse/intersperse.test.ts b/packages/lazy/src/iterators/intersperse/intersperse.test.ts index 10a8bbc..01d5816 100644 --- a/packages/lazy/src/iterators/intersperse/intersperse.test.ts +++ b/packages/lazy/src/iterators/intersperse/intersperse.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { collect } from '../../helpers/collect'; import { intersperse } from '.'; -describe('intersperse', () => { +describe('Intersperse', () => { it('should insert separator between elements', () => { const result = collect(intersperse(0)([1, 2, 3])); diff --git a/packages/lazy/src/iterators/map/map.test.ts b/packages/lazy/src/iterators/map/map.test.ts index b034bb8..8f382c7 100644 --- a/packages/lazy/src/iterators/map/map.test.ts +++ b/packages/lazy/src/iterators/map/map.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it, vi } from 'vitest'; import { collect } from '../../helpers/collect'; import { map } from '.'; -describe('map', () => { +describe('Map', () => { it('should transform each element using the mapping function', () => { const result = collect(map((x: number) => x * 2)([1, 2, 3])); diff --git a/packages/lazy/src/iterators/prepend/prepend.test.ts b/packages/lazy/src/iterators/prepend/prepend.test.ts index 7662532..d58ba0a 100644 --- a/packages/lazy/src/iterators/prepend/prepend.test.ts +++ b/packages/lazy/src/iterators/prepend/prepend.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { collect } from '../../helpers/collect'; import { prepend } from '.'; -describe('prepend', () => { +describe('Prepend', () => { it('should prepend iterables before the source', () => { const result = collect(prepend([1, 2], [3])([4, 5, 6])); diff --git a/packages/lazy/src/iterators/scan/scan.test.ts b/packages/lazy/src/iterators/scan/scan.test.ts index fec0046..820e52a 100644 --- a/packages/lazy/src/iterators/scan/scan.test.ts +++ b/packages/lazy/src/iterators/scan/scan.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { collect } from '../../helpers/collect'; import { scan } from '.'; -describe('scan', () => { +describe('Scan', () => { it('should yield intermediate accumulator values', () => { const result = collect( scan((acc: number, x: number) => acc + x, 0)([1, 2, 3, 4]), @@ -14,7 +14,7 @@ describe('scan', () => { it('should pass the index as the third argument', () => { const result = collect( scan( - (acc: string, x: number, idx) => `${acc}${idx}:${x},`, + (acc: string, x: number, idx: number) => `${acc}${idx}:${x},`, '', )([10, 20, 30]), ); diff --git a/packages/lazy/src/iterators/slice/slice.test.ts b/packages/lazy/src/iterators/slice/slice.test.ts index 9c416e8..b8cdbd5 100644 --- a/packages/lazy/src/iterators/slice/slice.test.ts +++ b/packages/lazy/src/iterators/slice/slice.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { collect } from '../../helpers/collect'; import { slice } from '.'; -describe('slice', () => { +describe('Slice', () => { it('should yield elements from start to end (exclusive)', () => { const result = collect(slice(1, 4)([0, 1, 2, 3, 4, 5])); diff --git a/packages/lazy/src/iterators/take-while/take-while.test.ts b/packages/lazy/src/iterators/take-while/take-while.test.ts index 8229028..0a5d8f8 100644 --- a/packages/lazy/src/iterators/take-while/take-while.test.ts +++ b/packages/lazy/src/iterators/take-while/take-while.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { collect } from '../../helpers/collect'; import { takeWhile } from '.'; -describe('takeWhile', () => { +describe('TakeWhile', () => { it('should yield elements while predicate is true', () => { const result = collect(takeWhile((x: number) => x < 4)([1, 2, 3, 4, 5])); diff --git a/packages/lazy/src/iterators/take/take.test.ts b/packages/lazy/src/iterators/take/take.test.ts index 4e9eb84..7f4b486 100644 --- a/packages/lazy/src/iterators/take/take.test.ts +++ b/packages/lazy/src/iterators/take/take.test.ts @@ -3,7 +3,7 @@ import { collect } from '../../helpers/collect'; import { map } from '../map'; import { take } from '.'; -describe('take', () => { +describe('Take', () => { it('should yield at most n elements', () => { const result = collect(take(3)([1, 2, 3, 4, 5])); diff --git a/packages/lazy/src/iterators/tap/tap.test.ts b/packages/lazy/src/iterators/tap/tap.test.ts index c27b5f3..db5a01f 100644 --- a/packages/lazy/src/iterators/tap/tap.test.ts +++ b/packages/lazy/src/iterators/tap/tap.test.ts @@ -3,7 +3,7 @@ import { collect } from '../../helpers/collect'; import { take } from '../take'; import { tap } from '.'; -describe('tap', () => { +describe('Tap', () => { it('should call the function for each element without modifying them', () => { const sideEffects: number[] = []; diff --git a/packages/lazy/src/iterators/unique-by/unique-by.test.ts b/packages/lazy/src/iterators/unique-by/unique-by.test.ts index 541f0b3..d214d46 100644 --- a/packages/lazy/src/iterators/unique-by/unique-by.test.ts +++ b/packages/lazy/src/iterators/unique-by/unique-by.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { collect } from '../../helpers/collect'; import { uniqueBy } from '.'; -describe('uniqueBy', () => { +describe('UniqueBy', () => { it('should remove duplicates based on key function', () => { const users = [ { id: 1, name: 'Alice' }, diff --git a/packages/lazy/src/iterators/unique/unique.test.ts b/packages/lazy/src/iterators/unique/unique.test.ts index 7f48622..da360c8 100644 --- a/packages/lazy/src/iterators/unique/unique.test.ts +++ b/packages/lazy/src/iterators/unique/unique.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { collect } from '../../helpers/collect'; import { unique } from '.'; -describe('unique', () => { +describe('Unique', () => { it('should remove duplicate elements preserving first occurrence', () => { const result = collect(unique([1, 2, 1, 3, 2, 4])); diff --git a/packages/lazy/src/iterators/zip-with/zip-with.test.ts b/packages/lazy/src/iterators/zip-with/zip-with.test.ts index 86c5f4b..bb55e38 100644 --- a/packages/lazy/src/iterators/zip-with/zip-with.test.ts +++ b/packages/lazy/src/iterators/zip-with/zip-with.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { collect } from '../../helpers/collect'; import { zipWith } from '.'; -describe('zipWith', () => { +describe('ZipWith', () => { it('should combine elements using the provided function', () => { const result = collect( zipWith([10, 20, 30], (a: number, b: number) => a + b)([1, 2, 3]), diff --git a/packages/lazy/src/iterators/zip/zip.test.ts b/packages/lazy/src/iterators/zip/zip.test.ts index 63e18b7..2327eec 100644 --- a/packages/lazy/src/iterators/zip/zip.test.ts +++ b/packages/lazy/src/iterators/zip/zip.test.ts @@ -4,7 +4,7 @@ import { map } from '../map'; import { take } from '../take'; import { zip } from '.'; -describe('zip', () => { +describe('Zip', () => { it('should pair elements from two iterables', () => { const result = collect(zip(['a', 'b', 'c'])([1, 2, 3])); diff --git a/packages/lazy/src/readme.test.ts b/packages/lazy/src/readme.test.ts index 3a6bf21..243bf98 100644 --- a/packages/lazy/src/readme.test.ts +++ b/packages/lazy/src/readme.test.ts @@ -16,7 +16,7 @@ import { zip, } from '.'; -describe('@certes/lazy - README Examples', () => { +describe('README Examples', () => { describe('Quick Start', () => { it('should process data lazily', () => { const mapFn = vi.fn((x: number) => x * x); diff --git a/packages/list/src/chunk/chunk.test.ts b/packages/list/src/chunk/chunk.test.ts index 2499f01..dad5114 100644 --- a/packages/list/src/chunk/chunk.test.ts +++ b/packages/list/src/chunk/chunk.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { chunk } from '.'; -describe('chunk', () => { +describe('Chunk', () => { it('should chunk array into fixed-size groups', () => { const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; const result = chunk(3)(arr); diff --git a/packages/list/src/concat/concat.test.ts b/packages/list/src/concat/concat.test.ts index e67dfc6..de85572 100644 --- a/packages/list/src/concat/concat.test.ts +++ b/packages/list/src/concat/concat.test.ts @@ -5,7 +5,7 @@ const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; const expected = [1, 2, 3, 4, 5, 6]; -describe('concat', () => { +describe('Concat', () => { it('should concatenate two arrays together', () => { expect(concat(arr1)(arr2)).toStrictEqual(expected); }); diff --git a/packages/list/src/every/every.test-d.ts b/packages/list/src/every/every.test-d.ts index 539ea59..544be3a 100644 --- a/packages/list/src/every/every.test-d.ts +++ b/packages/list/src/every/every.test-d.ts @@ -1,19 +1,21 @@ -import { expectTypeOf, test } from 'vitest'; +import { describe, expectTypeOf, it } from 'vitest'; import { every } from '.'; const isEven = (x: number) => !(x & 1); const arr = [1, 2, 3, 4]; -test('it should have the correct curried types', () => { - expectTypeOf(every).toBeFunction(); - expectTypeOf(every).toBeCallableWith(Boolean); - expectTypeOf(every).toBeCallableWith(isEven); +describe('Every', () => { + it('should have the correct curried types', () => { + expectTypeOf(every).toBeFunction(); + expectTypeOf(every).toBeCallableWith(Boolean); + expectTypeOf(every).toBeCallableWith(isEven); - expectTypeOf(every(isEven)).toBeFunction(); - expectTypeOf(every(isEven)).toBeCallableWith([]); - expectTypeOf(every(isEven)).toBeCallableWith(arr); -}); + expectTypeOf(every(isEven)).toBeFunction(); + expectTypeOf(every(isEven)).toBeCallableWith([]); + expectTypeOf(every(isEven)).toBeCallableWith(arr); + }); -test('it should have the correct return types', () => { - expectTypeOf(every(isEven)(arr)).toBeBoolean(); + it('should have the correct return types', () => { + expectTypeOf(every(isEven)(arr)).toBeBoolean(); + }); }); diff --git a/packages/list/src/every/every.test.ts b/packages/list/src/every/every.test.ts index b02f0e5..4e3516a 100644 --- a/packages/list/src/every/every.test.ts +++ b/packages/list/src/every/every.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, test } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { every } from '.'; const isEven = (x: number) => !(x & 1); @@ -6,8 +6,8 @@ const emptyArr: number[] = []; const evenArr = [2, 4, 6, 8, 10]; const oddArr = [0, 3, 5, 7, 9, 11]; -describe('every', () => { - test('it should return true for an empty array', () => { +describe('Every', () => { + it('should return true for an empty array', () => { const everyBool = every(Boolean); const everyMod = every(isEven); @@ -15,7 +15,7 @@ describe('every', () => { expect(everyMod(emptyArr)).toBeTruthy(); }); - test('it should return true for truthy predicates', () => { + it('should return true for truthy predicates', () => { const everyBool = every(Boolean); const everyMod = every(isEven); @@ -23,7 +23,7 @@ describe('every', () => { expect(everyMod(evenArr)).toBeTruthy(); }); - test('it should return false for falsey predicates', () => { + it('should return false for falsey predicates', () => { const everyBool = every(Boolean); const everyMod = every(isEven); diff --git a/packages/list/src/filter/filter.test-d.ts b/packages/list/src/filter/filter.test-d.ts new file mode 100644 index 0000000..38fb37b --- /dev/null +++ b/packages/list/src/filter/filter.test-d.ts @@ -0,0 +1,20 @@ +import { describe, expectTypeOf, it } from 'vitest'; +import { filter } from '.'; + +const isEven = (x: number) => !(x & 1); +const arr = [0, 1, 2, 3, 4, 5, 6]; + +describe('Filter', () => { + it('should have the correct curried types', () => { + expectTypeOf(filter).toBeFunction(); + expectTypeOf(filter).toBeCallableWith(isEven); + + expectTypeOf(filter(isEven)).toBeFunction(); + expectTypeOf(filter(isEven)).toBeCallableWith([]); + expectTypeOf(filter(isEven)).toBeCallableWith(arr); + }); + + it('should have the correct return types', () => { + expectTypeOf(filter(isEven)(arr)).toBeArray(); + }); +}); diff --git a/packages/list/src/filter/index.test.ts b/packages/list/src/filter/filter.test.ts similarity index 69% rename from packages/list/src/filter/index.test.ts rename to packages/list/src/filter/filter.test.ts index 54e1fe0..69a6cf9 100644 --- a/packages/list/src/filter/index.test.ts +++ b/packages/list/src/filter/filter.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, test } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { filter } from '.'; const isEven = (x: number) => !(x & 1); @@ -7,8 +7,8 @@ const arr = [1, 2, 3, 4, 5, 6]; const expectedEven = [2, 4, 6]; const expectedOdd = [1, 3, 5]; -describe('filter', () => { - test('it should return the correct filtered array', () => { +describe('Filter', () => { + it('it should return the correct filtered array', () => { const filterEven = filter(isEven); const filterOdd = filter(isOdd); @@ -16,13 +16,13 @@ describe('filter', () => { expect(filterOdd(arr)).toStrictEqual(expectedOdd); }); - test('it should return empty array when filtering empty array', () => { + it('it should return empty array when filtering empty array', () => { const filterEven = filter(isEven); expect(filterEven([])).toStrictEqual([]); }); - test('it should return empty array when no matches', () => { + it('it should return empty array when no matches', () => { const filterNegative = filter((x: number) => x < 0); expect(filterNegative(arr)).toStrictEqual([]); diff --git a/packages/list/src/filter/index.test-d.ts b/packages/list/src/filter/index.test-d.ts deleted file mode 100644 index 0930baa..0000000 --- a/packages/list/src/filter/index.test-d.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { expectTypeOf, test } from 'vitest'; -import { filter } from '.'; - -const isEven = (x: number) => !(x & 1); -const arr = [0, 1, 2, 3, 4, 5, 6]; - -test('it should have the correct curried types', () => { - expectTypeOf(filter).toBeFunction(); - expectTypeOf(filter).toBeCallableWith(isEven); - - expectTypeOf(filter(isEven)).toBeFunction(); - expectTypeOf(filter(isEven)).toBeCallableWith([]); - expectTypeOf(filter(isEven)).toBeCallableWith(arr); -}); - -test('it should have the correct return types', () => { - expectTypeOf(filter(isEven)(arr)).toBeArray(); -}); diff --git a/packages/list/src/find-index/index.test.ts b/packages/list/src/find-index/find-index.test.ts similarity index 93% rename from packages/list/src/find-index/index.test.ts rename to packages/list/src/find-index/find-index.test.ts index 94d508d..d5b6d91 100644 --- a/packages/list/src/find-index/index.test.ts +++ b/packages/list/src/find-index/find-index.test.ts @@ -5,7 +5,7 @@ const isEven = (x: number) => !(x & 1); const is100 = (x: number) => x === 100; const arr = [1, 2, 3, 4, 5]; -describe('findIndex', () => { +describe('FindIndex', () => { it('should return the first match of the predicate', () => { expect(findIndex(isEven)(arr)).toEqual(1); }); diff --git a/packages/list/src/find-last-index/index.test.ts b/packages/list/src/find-last-index/find-last-index.test.ts similarity index 92% rename from packages/list/src/find-last-index/index.test.ts rename to packages/list/src/find-last-index/find-last-index.test.ts index 0f4d368..c6bd994 100644 --- a/packages/list/src/find-last-index/index.test.ts +++ b/packages/list/src/find-last-index/find-last-index.test.ts @@ -5,7 +5,7 @@ const isEven = (x: number) => !(x & 1); const is100 = (x: number) => x === 100; const arr = [1, 2, 3, 4, 5]; -describe('findLastIndex', () => { +describe('FindLastIndex', () => { it('should return the first match of the predicate', () => { expect(findLastIndex(isEven)(arr)).toEqual(3); }); diff --git a/packages/list/src/find-last/index.test.ts b/packages/list/src/find-last/find-last.test.ts similarity index 93% rename from packages/list/src/find-last/index.test.ts rename to packages/list/src/find-last/find-last.test.ts index 046e850..2808663 100644 --- a/packages/list/src/find-last/index.test.ts +++ b/packages/list/src/find-last/find-last.test.ts @@ -5,7 +5,7 @@ const isEven = (x: number) => !(x & 1); const is100 = (x: number) => x === 100; const arr = [1, 2, 3, 4, 5]; -describe('findLast', () => { +describe('FindLast', () => { it('should return the first match of the predicate', () => { expect(findLast(isEven)(arr)).toEqual(4); }); diff --git a/packages/list/src/find/index.test.ts b/packages/list/src/find/find.test.ts similarity index 94% rename from packages/list/src/find/index.test.ts rename to packages/list/src/find/find.test.ts index 86fad16..6e3f853 100644 --- a/packages/list/src/find/index.test.ts +++ b/packages/list/src/find/find.test.ts @@ -5,7 +5,7 @@ const isEven = (x: number) => !(x & 1); const is100 = (x: number) => x === 100; const arr = [1, 2, 3, 4, 5]; -describe('find', () => { +describe('Find', () => { it('should return the first match of the predicate', () => { expect(find(isEven)(arr)).toEqual(2); }); diff --git a/packages/list/src/flat-map/flat-map.test.ts b/packages/list/src/flat-map/flat-map.test.ts index 357ad39..db50d3f 100644 --- a/packages/list/src/flat-map/flat-map.test.ts +++ b/packages/list/src/flat-map/flat-map.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { flatMap } from '.'; -describe('flatMap', () => { +describe('FlatMap', () => { it('shoudl map and flatten in one operation', () => { const duplicate = (x: number) => [x, x]; const arr = [1, 2, 3]; diff --git a/packages/list/src/flatten/flatten.test.ts b/packages/list/src/flatten/flatten.test.ts index a441df6..6606e85 100644 --- a/packages/list/src/flatten/flatten.test.ts +++ b/packages/list/src/flatten/flatten.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { flatten } from '.'; -describe('flatten', () => { +describe('Flatten', () => { it('should flatten an array of arrays', () => { const arr = [[1, 2], [3, 4], [5]]; const result = flatten(arr); diff --git a/packages/list/src/includes/index.test.ts b/packages/list/src/includes/includes.test.ts similarity index 92% rename from packages/list/src/includes/index.test.ts rename to packages/list/src/includes/includes.test.ts index fcb74f8..008e4a1 100644 --- a/packages/list/src/includes/index.test.ts +++ b/packages/list/src/includes/includes.test.ts @@ -3,7 +3,7 @@ import { includes } from '.'; const arr = [1, 2, 3, 4, 5]; -describe('includes', () => { +describe('Includes', () => { it('should return true if the array contains the value', () => { expect(includes(3)(arr)).toBeTruthy(); }); diff --git a/packages/list/src/index-of/index.test.ts b/packages/list/src/index-of/index-of.test.ts similarity index 92% rename from packages/list/src/index-of/index.test.ts rename to packages/list/src/index-of/index-of.test.ts index ad9bd4b..477c251 100644 --- a/packages/list/src/index-of/index.test.ts +++ b/packages/list/src/index-of/index-of.test.ts @@ -3,7 +3,7 @@ import { indexOf } from '.'; const arr = [1, 2, 3, 4, 5]; -describe('indexOf', () => { +describe('IndexOf', () => { it('should return the index of the first match of the predicate', () => { expect(indexOf(3)(arr)).toEqual(2); }); diff --git a/packages/list/src/map/index.test.ts b/packages/list/src/map/map.test.ts similarity index 61% rename from packages/list/src/map/index.test.ts rename to packages/list/src/map/map.test.ts index fa23890..688304c 100644 --- a/packages/list/src/map/index.test.ts +++ b/packages/list/src/map/map.test.ts @@ -1,17 +1,17 @@ -import { describe, expect, test } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { map } from '.'; const double = (x: number) => x * 2; const arr = [1, 2, 3, 4, 5]; -describe('map', () => { - test('it should return the correct filtered array', () => { +describe('Map', () => { + it('should return the correct filtered array', () => { const doubleItems = map(double); expect(doubleItems(arr)).toStrictEqual([2, 4, 6, 8, 10]); }); - test('it should handle empty array', () => { + it('should handle empty array', () => { const doubleItems = map(double); expect(doubleItems([])).toStrictEqual([]); diff --git a/packages/list/src/push/index.test.ts b/packages/list/src/push/push.test.ts similarity index 95% rename from packages/list/src/push/index.test.ts rename to packages/list/src/push/push.test.ts index 883692a..b7fc957 100644 --- a/packages/list/src/push/index.test.ts +++ b/packages/list/src/push/push.test.ts @@ -4,7 +4,7 @@ import { push } from '.'; const input = [1, 2, 3, 4]; const expected = [1, 2, 3, 4, 5]; -describe('push', () => { +describe('Push', () => { it('should push the item onto the end of the array', () => { expect(push(input)(5)).toEqual(expected); }); diff --git a/packages/list/src/readme.test.ts b/packages/list/src/readme.test.ts index 66b6a6b..aa115e1 100644 --- a/packages/list/src/readme.test.ts +++ b/packages/list/src/readme.test.ts @@ -22,7 +22,7 @@ import { unshift, } from '.'; -describe('@certes/list - README Examples', () => { +describe('README Examples', () => { describe('Usage Section', () => { it('should double numbers with map', () => { const nums = [1, 2, 3, 4, 5]; diff --git a/packages/list/src/reduce-right/index.test.ts b/packages/list/src/reduce-right/reduce-right.test.ts similarity index 62% rename from packages/list/src/reduce-right/index.test.ts rename to packages/list/src/reduce-right/reduce-right.test.ts index e6a2c1d..82c6181 100644 --- a/packages/list/src/reduce-right/index.test.ts +++ b/packages/list/src/reduce-right/reduce-right.test.ts @@ -1,17 +1,17 @@ -import { describe, expect, test } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { reduceRight } from '.'; const concatStr = (base: string, add: string) => `${base}${add}`; const arr = ['a', 'b', 'c', 'd', 'e']; -describe('reduceRight', () => { - test('it should correctly reduce the array from the right', () => { +describe('ReduceRight', () => { + it('should correctly reduce the array from the right', () => { const sumItems = reduceRight(concatStr)(''); expect(sumItems(arr)).toStrictEqual('edcba'); }); - test('it should return initial value for empty array', () => { + it('should return initial value for empty array', () => { const concatItems = reduceRight(concatStr)(''); expect(concatItems([])).toStrictEqual(''); diff --git a/packages/list/src/reduce/index.test.ts b/packages/list/src/reduce/reduce.test.ts similarity index 59% rename from packages/list/src/reduce/index.test.ts rename to packages/list/src/reduce/reduce.test.ts index 4ffd81b..3d2562c 100644 --- a/packages/list/src/reduce/index.test.ts +++ b/packages/list/src/reduce/reduce.test.ts @@ -1,17 +1,17 @@ -import { describe, expect, test } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { reduce } from '.'; const add = (a: number, b: number) => a + b; const arr = [1, 2, 3, 4, 5]; -describe('reduce', () => { - test('it should correctly reduce the array', () => { +describe('Reduce', () => { + it('should correctly reduce the array', () => { const sumItems = reduce(add)(0); expect(sumItems(arr)).toStrictEqual(15); }); - test('it should return initial value for empty array', () => { + it('should return initial value for empty array', () => { const sumItems = reduce(add)(0); expect(sumItems([])).toStrictEqual(0); diff --git a/packages/list/src/reverse/index.test.ts b/packages/list/src/reverse/reverse.test.ts similarity index 56% rename from packages/list/src/reverse/index.test.ts rename to packages/list/src/reverse/reverse.test.ts index d20b1f2..8ee02a7 100644 --- a/packages/list/src/reverse/index.test.ts +++ b/packages/list/src/reverse/reverse.test.ts @@ -1,23 +1,23 @@ -import { describe, expect, test } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { reverse } from '.'; const input = [1, 2, 3, 4, 5]; const expected = [5, 4, 3, 2, 1]; -describe('reverse', () => { - test('it should correctly reverse the array', () => { +describe('Reverse', () => { + it('should correctly reverse the array', () => { expect(reverse(input)).toStrictEqual(expected); }); - test('it should handle empty array', () => { + it('should handle empty array', () => { expect(reverse([])).toStrictEqual([]); }); - test('it should handle single element', () => { + it('should handle single element', () => { expect(reverse([1])).toStrictEqual([1]); }); - test('it should not mutate original array', () => { + it('should not mutate original array', () => { const original = [1, 2, 3]; reverse(original); diff --git a/packages/list/src/scan/scan.test.ts b/packages/list/src/scan/scan.test.ts index e752d74..127294e 100644 --- a/packages/list/src/scan/scan.test.ts +++ b/packages/list/src/scan/scan.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { scan } from '.'; -describe('scan', () => { +describe('Scan', () => { it('should return all intermediate accumulator values', () => { const add = (acc: number, x: number) => acc + x; const arr = [1, 2, 3, 4]; diff --git a/packages/list/src/shift/index.test.ts b/packages/list/src/shift/shift.test.ts similarity index 54% rename from packages/list/src/shift/index.test.ts rename to packages/list/src/shift/shift.test.ts index 262b172..7ce3155 100644 --- a/packages/list/src/shift/index.test.ts +++ b/packages/list/src/shift/shift.test.ts @@ -1,15 +1,15 @@ -import { describe, expect, test } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { shift } from '.'; const input = [1, 2, 3, 4, 5]; const expected = [1, [2, 3, 4, 5]]; -describe('shift', () => { - test('it should return the head and tail of the array', () => { +describe('Shift', () => { + it('should return the head and tail of the array', () => { expect(shift(input)).toStrictEqual(expected); }); - test('it should handle single element array', () => { + it('should handle single element array', () => { const result = shift([1]); expect(result).toStrictEqual([1, []]); diff --git a/packages/list/src/slice/index.test.ts b/packages/list/src/slice/slice.test.ts similarity index 52% rename from packages/list/src/slice/index.test.ts rename to packages/list/src/slice/slice.test.ts index 661fd93..12c63e4 100644 --- a/packages/list/src/slice/index.test.ts +++ b/packages/list/src/slice/slice.test.ts @@ -1,23 +1,23 @@ -import { describe, expect, test } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { slice } from '.'; const input = [1, 2, 3, 4, 5]; const expected = [1, 2, 3, 4]; -describe('slice', () => { - test('it should return the head and tail of the array', () => { +describe('Slice', () => { + it('should return the head and tail of the array', () => { expect(slice(0)(4)(input)).toStrictEqual(expected); }); - test('it should handle empty array', () => { + it('should handle empty array', () => { expect(slice(0)(2)([])).toStrictEqual([]); }); - test('it should handle end beyond array length', () => { + it('should handle end beyond array length', () => { expect(slice(0)(100)(input)).toStrictEqual(input); }); - test('it should handle start equal to end', () => { + it('should handle start equal to end', () => { expect(slice(2)(2)(input)).toStrictEqual([]); }); }); diff --git a/packages/list/src/some/index.test.ts b/packages/list/src/some/some.test.ts similarity index 75% rename from packages/list/src/some/index.test.ts rename to packages/list/src/some/some.test.ts index fb14501..108ea0b 100644 --- a/packages/list/src/some/index.test.ts +++ b/packages/list/src/some/some.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, test } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { some } from '.'; const emptyArr: number[] = []; @@ -7,8 +7,8 @@ const mostlyZeroArr = [0, 0, 6, 0, 0]; const oddArr = [1, 3, 5, 7, 9, 11]; const zeroArr = [0, 0, 0, 0, 0]; -describe('some', () => { - test('it should return false for empty array', () => { +describe('Some', () => { + it('should return false for empty array', () => { const someBool = some(Boolean); const someMod = some((x: number) => x % 2 === 0); @@ -16,7 +16,7 @@ describe('some', () => { expect(someMod(emptyArr)).toBe(false); }); - test('it should return true for truthy predicate', () => { + it('should return true for truthy predicate', () => { const someBool = some(Boolean); const someMod = some((x: number) => x % 2 === 0); @@ -24,7 +24,7 @@ describe('some', () => { expect(someBool(mostlyZeroArr)).toBe(true); }); - test('it should return false for falsey predicate', () => { + it('should return false for falsey predicate', () => { const someBool = some(Boolean); const someMod = some((x: number) => x % 2 === 0); diff --git a/packages/list/src/unique-by/unique-by.test.ts b/packages/list/src/unique-by/unique-by.test.ts index 8d0dd43..f6e58a7 100644 --- a/packages/list/src/unique-by/unique-by.test.ts +++ b/packages/list/src/unique-by/unique-by.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { uniqueBy } from '.'; -describe('uniqueBy', () => { +describe('UniqueBy', () => { it('should remove duplicates by key function', () => { const users = [ { id: 1, name: 'Alice' }, @@ -20,13 +20,14 @@ describe('uniqueBy', () => { }); it('should use first occurrence when duplicates found', () => { - const items = [ + type Result = { id: number; value: string }; + const items: Result[] = [ { id: 1, value: 'first' }, { id: 2, value: 'second' }, { id: 1, value: 'duplicate' }, ]; - // Act - const result = uniqueBy((item: { id: number }) => item.id)(items); + + const result = uniqueBy((item) => item.id)(items); expect(result[0].value).toBe('first'); expect(result.length).toBe(2); diff --git a/packages/list/src/unique/unique.test.ts b/packages/list/src/unique/unique.test.ts index f4588e4..d3dd3e1 100644 --- a/packages/list/src/unique/unique.test.ts +++ b/packages/list/src/unique/unique.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { unique } from '.'; -describe('unique', () => { +describe('Unique', () => { it('should remove duplicate numbers', () => { const arr = [1, 2, 2, 3, 3, 3, 4]; const result = unique(arr); diff --git a/packages/list/src/unshift/index.test.ts b/packages/list/src/unshift/unshift.test.ts similarity index 95% rename from packages/list/src/unshift/index.test.ts rename to packages/list/src/unshift/unshift.test.ts index 0bc2293..dfcdb57 100644 --- a/packages/list/src/unshift/index.test.ts +++ b/packages/list/src/unshift/unshift.test.ts @@ -4,7 +4,7 @@ import { unshift } from '.'; const input = [1, 2, 3, 4]; const expected = [0, 1, 2, 3, 4]; -describe('unshift', () => { +describe('Unshift', () => { it('should push the item onto the start of the array', () => { expect(unshift(input)(0)).toEqual(expected); }); diff --git a/packages/list/src/zip-with/zip-with.test.ts b/packages/list/src/zip-with/zip-with.test.ts index 2cb657f..67d02c4 100644 --- a/packages/list/src/zip-with/zip-with.test.ts +++ b/packages/list/src/zip-with/zip-with.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { zipWith } from '.'; -describe('zipWith', () => { +describe('ZipWith', () => { it('should combine elements with addition', () => { const add = (a: number, b: number) => a + b; const arr1 = [1, 2, 3]; diff --git a/packages/list/src/zip/zip.test.ts b/packages/list/src/zip/zip.test.ts index fbccad8..8a28c6f 100644 --- a/packages/list/src/zip/zip.test.ts +++ b/packages/list/src/zip/zip.test.ts @@ -1,16 +1,12 @@ import { describe, expect, it } from 'vitest'; import { zip } from '.'; -describe('zip', () => { +describe('Zip', () => { it('should pair corresponding elements', () => { - // Arrange const arr1 = [1, 2, 3]; const arr2 = ['a', 'b', 'c']; - - // Act const result = zip(arr2)(arr1); - // Assert expect(result).toEqual([ [1, 'a'], [2, 'b'], @@ -19,14 +15,10 @@ describe('zip', () => { }); it('should pair numbers with strings', () => { - // Arrange const names = ['Alice', 'Bob', 'Charlie']; const scores = [95, 87, 92]; - - // Act const result = zip(scores)(names); - // Assert expect(result).toEqual([ ['Alice', 95], ['Bob', 87], @@ -35,14 +27,10 @@ describe('zip', () => { }); it('should pair arrays of equal length', () => { - // Arrange const arr1 = [1, 2]; const arr2 = [3, 4]; - - // Act const result = zip(arr2)(arr1); - // Assert expect(result).toEqual([ [1, 3], [2, 4], @@ -50,14 +38,10 @@ describe('zip', () => { }); it('should stop at shorter array when first is shorter', () => { - // Arrange const arr1 = [1, 2]; const arr2 = ['a', 'b', 'c', 'd']; - - // Act const result = zip(arr2)(arr1); - // Assert expect(result).toEqual([ [1, 'a'], [2, 'b'], @@ -65,14 +49,10 @@ describe('zip', () => { }); it('should stop at shorter array when second is shorter', () => { - // Arrange const arr1 = [1, 2, 3, 4]; const arr2 = ['a', 'b']; - - // Act const result = zip(arr2)(arr1); - // Assert expect(result).toEqual([ [1, 'a'], [2, 'b'], @@ -80,74 +60,50 @@ describe('zip', () => { }); it('should handle significantly different lengths', () => { - // Arrange const arr1 = [1]; const arr2 = ['a', 'b', 'c', 'd', 'e']; - - // Act const result = zip(arr2)(arr1); - // Assert expect(result).toEqual([[1, 'a']]); }); it('should handle both arrays empty', () => { - // Arrange const arr1: number[] = []; const arr2: string[] = []; - - // Act const result = zip(arr2)(arr1); - // Assert expect(result).toEqual([]); }); it('should handle first array empty', () => { - // Arrange const arr1: number[] = []; const arr2 = ['a', 'b', 'c']; - - // Act const result = zip(arr2)(arr1); - // Assert expect(result).toEqual([]); }); it('should handle second array empty', () => { - // Arrange const arr1 = [1, 2, 3]; const arr2: string[] = []; - - // Act const result = zip(arr2)(arr1); - // Assert expect(result).toEqual([]); }); it('should handle a single element arrays', () => { - // Arrange const arr1 = [42]; const arr2 = ['x']; - - // Act const result = zip(arr2)(arr1); - // Assert expect(result).toEqual([[42, 'x']]); }); it('should handle null and undefined values', () => { - // Arrange const arr1 = [1, null, undefined]; const arr2 = ['a', 'b', 'c']; - - // Act const result = zip(arr2)(arr1); - // Assert expect(result).toEqual([ [1, 'a'], [null, 'b'], diff --git a/packages/logic/src/and/and.test.ts b/packages/logic/src/and/and.test.ts index 5be7788..1b7dbec 100644 --- a/packages/logic/src/and/and.test.ts +++ b/packages/logic/src/and/and.test.ts @@ -4,59 +4,55 @@ import { and, andFn } from '.'; const isEven = (x: number) => !(x & 1); const isOdd = (x: number) => !isEven(x); -describe('and', () => { - describe('basic functionality', () => { - it('should return true for true && true', () => { - expect(and(true)(true)).toEqual(true); - }); +describe('And', () => { + it('should return true for true && true', () => { + expect(and(true)(true)).toEqual(true); + }); - it('should return false for true && false', () => { - expect(and(true)(false)).toEqual(false); - }); + it('should return false for true && false', () => { + expect(and(true)(false)).toEqual(false); + }); - it('should return false for false && true', () => { - expect(and(false)(true)).toEqual(false); - }); + it('should return false for false && true', () => { + expect(and(false)(true)).toEqual(false); + }); - it('should return false for false && false', () => { - expect(and(false)(false)).toEqual(false); - }); + it('should return false for false && false', () => { + expect(and(false)(false)).toEqual(false); }); - describe('edge cases', () => { - it('should treat 0 as false', () => { - const actual = and(0)(1); - expect(actual).toBe(false); - }); + it('should treat 0 as false', () => { + const actual = and(0)(1); + expect(actual).toBe(false); + }); - it('should treat empty string as false', () => { - const actual = and('')(1); - expect(actual).toBe(false); - }); + it('should treat empty string as false', () => { + const actual = and('')(1); + expect(actual).toBe(false); + }); - it('should treat undefined as false', () => { - const actual = and(undefined)(1); - expect(actual).toBe(false); - }); + it('should treat undefined as false', () => { + const actual = and(undefined)(1); + expect(actual).toBe(false); + }); - it('should treat null as false', () => { - const actual = and(null)(1); - expect(actual).toBe(false); - }); + it('should treat null as false', () => { + const actual = and(null)(1); + expect(actual).toBe(false); + }); - it('should treat non-empty string as true', () => { - const actual = and('hello')('world'); - expect(actual).toBe(true); - }); + it('should treat non-empty string as true', () => { + const actual = and('hello')('world'); + expect(actual).toBe(true); + }); - it('should treat non-zero number as true', () => { - const actual = and(42)(7); - expect(actual).toBe(true); - }); + it('should treat non-zero number as true', () => { + const actual = and(42)(7); + expect(actual).toBe(true); }); }); -describe('andFn', () => { +describe('AndFn', () => { it('should return true for true && true', () => { expect(andFn(isEven)(isEven)(6)).toEqual(true); }); diff --git a/packages/logic/src/equality/equality.test.ts b/packages/logic/src/equality/equality.test.ts index 3c2e6f4..6774881 100644 --- a/packages/logic/src/equality/equality.test.ts +++ b/packages/logic/src/equality/equality.test.ts @@ -4,60 +4,56 @@ import { equality, equalityFn } from '.'; const isEven = (x: number) => !(x & 1); const isOdd = (x: number) => !isEven(x); -describe('equality', () => { - describe('basic functionality', () => { - it('should return true for true === true', () => { - expect(equality(true)(true)).toEqual(true); - }); - - it('should return false for true === false', () => { - expect(equality(true)(false)).toEqual(false); - }); - - it('should return false for false === true', () => { - expect(equality(false)(true)).toEqual(false); - }); - - it('should return true for false === false', () => { - expect(equality(false)(false)).toEqual(true); - }); +describe('Equality', () => { + it('should return true for true === true', () => { + expect(equality(true)(true)).toEqual(true); }); - describe('edge cases', () => { - it('should handle NaN correctly', () => { - const a = Number.NaN; - const b = Number.NaN; - const actual = equality(a)(b); + it('should return false for true === false', () => { + expect(equality(true)(false)).toEqual(false); + }); - expect(actual).toBe(false); // NaN !== NaN in JavaScript - }); + it('should return false for false === true', () => { + expect(equality(false)(true)).toEqual(false); + }); - it('should differentiate +0 and -0', () => { - const a = +0; - const b = -0; - const actual = equality(a)(b); + it('should return true for false === false', () => { + expect(equality(false)(false)).toEqual(true); + }); - expect(actual).toBe(true); // +0 === -0 in JavaScript - }); + it('should handle NaN correctly', () => { + const a = Number.NaN; + const b = Number.NaN; + const actual = equality(a)(b); - it('should handle object reference equality', () => { - const obj = { x: 1 }; - const actual = equality(obj)(obj); + expect(actual).toBe(false); // NaN !== NaN in JavaScript + }); + + it('should differentiate +0 and -0', () => { + const a = +0; + const b = -0; + const actual = equality(a)(b); + + expect(actual).toBe(true); // +0 === -0 in JavaScript + }); + + it('should handle object reference equality', () => { + const obj = { x: 1 }; + const actual = equality(obj)(obj); - expect(actual).toBe(true); - }); + expect(actual).toBe(true); + }); - it('should fail for different object instances', () => { - const a = { x: 1 }; - const b = { x: 1 }; - const actual = equality(a)(b); + it('should fail for different object instances', () => { + const a = { x: 1 }; + const b = { x: 1 }; + const actual = equality(a)(b); - expect(actual).toBe(false); - }); + expect(actual).toBe(false); }); }); -describe('equalityFn', () => { +describe('EqualityFn', () => { it('should return true for true === true', () => { expect(equalityFn(isEven)(isEven)(6)).toEqual(true); }); diff --git a/packages/logic/src/nand/nand.test.ts b/packages/logic/src/nand/nand.test.ts index c921ea7..a92ea1e 100644 --- a/packages/logic/src/nand/nand.test.ts +++ b/packages/logic/src/nand/nand.test.ts @@ -4,7 +4,7 @@ import { nand, nandFn } from '.'; const isEven = (x: number) => !(x & 1); const isOdd = (x: number) => !isEven(x); -describe('nand', () => { +describe('Nand', () => { it('should return false for true && true', () => { expect(nand(true)(true)).toEqual(false); }); @@ -22,7 +22,7 @@ describe('nand', () => { }); }); -describe('nandFn', () => { +describe('NandFn', () => { it('should return false for true && true', () => { expect(nandFn(isEven)(isEven)(6)).toEqual(false); }); diff --git a/packages/logic/src/nor/nor.test.ts b/packages/logic/src/nor/nor.test.ts index 419e2ce..8818b98 100644 --- a/packages/logic/src/nor/nor.test.ts +++ b/packages/logic/src/nor/nor.test.ts @@ -4,7 +4,7 @@ import { nor, norFn } from '.'; const isEven = (x: number) => !(x & 1); const isOdd = (x: number) => !isEven(x); -describe('nor', () => { +describe('Nor', () => { it('should return false for true || true', () => { expect(nor(true)(true)).toEqual(false); }); @@ -22,7 +22,7 @@ describe('nor', () => { }); }); -describe('norFn', () => { +describe('NorFn', () => { it('should return false for true || true', () => { expect(norFn(isEven)(isEven)(6)).toEqual(false); }); diff --git a/packages/logic/src/not/not.test.ts b/packages/logic/src/not/not.test.ts index eac0065..1dfcb72 100644 --- a/packages/logic/src/not/not.test.ts +++ b/packages/logic/src/not/not.test.ts @@ -4,37 +4,33 @@ import { not, notFn } from '.'; const isEven = (x: number) => !(x & 1); const isOdd = (x: number) => !isEven(x); -describe('not', () => { - describe('basic functionality', () => { - it('should return false for !true', () => { - expect(not(true)).toEqual(false); - }); +describe('Not', () => { + it('should return false for !true', () => { + expect(not(true)).toEqual(false); + }); - it('should return true for !false', () => { - expect(not(false)).toEqual(true); - }); + it('should return true for !false', () => { + expect(not(false)).toEqual(true); }); - describe('edge cases', () => { - it('should convert truthy values correctly', () => { - const values = [1, 'hello', {}, [], -1]; + it('should convert truthy values correctly', () => { + const values = [1, 'hello', {}, [], -1]; - values.forEach((val) => { - expect(not(val)).toBe(false); - }); + values.forEach((val) => { + expect(not(val)).toBe(false); }); + }); - it('should convert falsy values correctly', () => { - const values = [0, '', null, undefined, Number.NaN]; + it('should convert falsy values correctly', () => { + const values = [0, '', null, undefined, Number.NaN]; - values.forEach((val) => { - expect(not(val)).toBe(true); - }); + values.forEach((val) => { + expect(not(val)).toBe(true); }); }); }); -describe('notFn', () => { +describe('NotFn', () => { it('should return false for !true', () => { expect(notFn(isEven)(6)).toEqual(false); }); diff --git a/packages/logic/src/nullish-or/null-or.test.ts b/packages/logic/src/nullish-or/null-or.test.ts index 0174417..7ec018b 100644 --- a/packages/logic/src/nullish-or/null-or.test.ts +++ b/packages/logic/src/nullish-or/null-or.test.ts @@ -11,61 +11,57 @@ const isEven = (x: number) => !(x & 1); // biome-ignore lint/suspicious/noExplicitAny: For testing const nullVal = (_x: any) => null; -describe('nullishOr', () => { - describe('basic functionality', () => { - it('should return true for true ?? true', () => { - expect(nullishOr(true)(true)).toEqual(true); - }); +describe('NullishOr', () => { + it('should return true for true ?? true', () => { + expect(nullishOr(true)(true)).toEqual(true); + }); - it('should return true for true ?? null', () => { - expect(nullishOr(true)(null)).toEqual(true); - }); + it('should return true for true ?? null', () => { + expect(nullishOr(true)(null)).toEqual(true); + }); - it('should return true for null ?? true', () => { - expect(nullishOr(null)(true)).toEqual(true); - }); + it('should return true for null ?? true', () => { + expect(nullishOr(null)(true)).toEqual(true); + }); - it('should return null for null ?? null', () => { - expect(nullishOr(null)(null)).toBeNull(); - }); + it('should return null for null ?? null', () => { + expect(nullishOr(null)(null)).toBeNull(); }); - describe('edge cases', () => { - it('should preserve 0 as non-nullish', () => { - const a = 0; - const b = 100; - const actual = nullishOr(a)(b); + it('should preserve 0 as non-nullish', () => { + const a = 0; + const b = 100; + const actual = nullishOr(a)(b); - expect(actual).toBe(0); - }); + expect(actual).toBe(0); + }); - it('should preserve false as non-nullish', () => { - const a = false; - const b = true; - const actual = nullishOr(a)(b); + it('should preserve false as non-nullish', () => { + const a = false; + const b = true; + const actual = nullishOr(a)(b); - expect(actual).toBe(false); - }); + expect(actual).toBe(false); + }); - it('should preserve empty string as non-nullish', () => { - const a = ''; - const b = 'fallback'; - const actual = nullishOr(a)(b); + it('should preserve empty string as non-nullish', () => { + const a = ''; + const b = 'fallback'; + const actual = nullishOr(a)(b); - expect(actual).toBe(''); - }); + expect(actual).toBe(''); + }); - it('should treat undefined as nullish', () => { - const a = undefined; - const b = 'fallback'; - const actual = nullishOr(a)(b); + it('should treat undefined as nullish', () => { + const a = undefined; + const b = 'fallback'; + const actual = nullishOr(a)(b); - expect(actual).toBe('fallback'); - }); + expect(actual).toBe('fallback'); }); }); -describe('nullishOrFn', () => { +describe('NullishOrFn', () => { it('should return true for true ?? true', () => { expect(nullishOrFn(isEven)(isEven)(6)).toEqual(true); }); @@ -83,7 +79,7 @@ describe('nullishOrFn', () => { }); }); -describe('swappedNullishOr', () => { +describe('SwappedNullishOr', () => { it('should return true for true ?? true', () => { expect(swappedNullishOr(true)(true)).toEqual(true); }); @@ -101,7 +97,7 @@ describe('swappedNullishOr', () => { }); }); -describe('swappedNullishOrFn', () => { +describe('SwappedNullishOrFn', () => { it('should return true for true ?? true', () => { expect(swappedNullishOrFn(isEven)(isEven)(6)).toEqual(true); }); diff --git a/packages/logic/src/or/or.test.ts b/packages/logic/src/or/or.test.ts index 4d1a51f..a3ef7a9 100644 --- a/packages/logic/src/or/or.test.ts +++ b/packages/logic/src/or/or.test.ts @@ -4,45 +4,41 @@ import { or, orFn, swappedOr, swappedOrFn } from '.'; const isEven = (x: number) => !(x & 1); const isOdd = (x: number) => !isEven(x); -describe('or', () => { - describe('basic functionality', () => { - it('should return true for true || true', () => { - expect(or(true)(true)).toEqual(true); - }); +describe('Or', () => { + it('should return true for true || true', () => { + expect(or(true)(true)).toEqual(true); + }); - it('should return true for true || false', () => { - expect(or(true)(false)).toEqual(true); - }); + it('should return true for true || false', () => { + expect(or(true)(false)).toEqual(true); + }); - it('should return true for false || true', () => { - expect(or(false)(true)).toEqual(true); - }); + it('should return true for false || true', () => { + expect(or(false)(true)).toEqual(true); + }); - it('should return false for false || false', () => { - expect(or(false)(false)).toEqual(false); - }); + it('should return false for false || false', () => { + expect(or(false)(false)).toEqual(false); }); - describe('edge cases', () => { - it('should treat 0 as false', () => { - const a = 0; - const b = 0; - const actual = or(a)(b); + it('should treat 0 as false', () => { + const a = 0; + const b = 0; + const actual = or(a)(b); - expect(actual).toBe(false); - }); + expect(actual).toBe(false); + }); - it('should treat non-zero as true with false', () => { - const a = 42; - const b = false; - const actual = or(a)(b); + it('should treat non-zero as true with false', () => { + const a = 42; + const b = false; + const actual = or(a)(b); - expect(actual).toBe(true); - }); + expect(actual).toBe(true); }); }); -describe('orFn', () => { +describe('OrFn', () => { it('should return true for true || true', () => { expect(orFn(isEven)(isEven)(6)).toEqual(true); }); @@ -60,7 +56,7 @@ describe('orFn', () => { }); }); -describe('swappedOr', () => { +describe('SwappedOr', () => { it('should return true for true || true', () => { expect(swappedOr(true)(true)).toEqual(true); }); @@ -78,7 +74,7 @@ describe('swappedOr', () => { }); }); -describe('swappedOrFn', () => { +describe('SwappedOrFn', () => { it('should return true for true || true', () => { expect(swappedOrFn(isEven)(isEven)(6)).toEqual(true); }); diff --git a/packages/logic/src/readme.test.ts b/packages/logic/src/readme.test.ts index b168a11..f598e8a 100644 --- a/packages/logic/src/readme.test.ts +++ b/packages/logic/src/readme.test.ts @@ -34,7 +34,7 @@ const filter = return res; }; -describe('@certes/logic - README Examples', () => { +describe('README Examples', () => { describe('Quick Start', () => { it('should perform basic Boolean operations', () => { expect(and(true)(false)).toBe(false); diff --git a/packages/logic/src/xor/xor.test-d.ts b/packages/logic/src/xor/xor.test-d.ts index 45431d5..f6cff81 100644 --- a/packages/logic/src/xor/xor.test-d.ts +++ b/packages/logic/src/xor/xor.test-d.ts @@ -4,7 +4,7 @@ import { xor, xorFn } from '.'; const isEven = (x: number) => !(x & 1); const isOdd = (x: number) => !isEven(x); -describe('xor', () => { +describe('Xor', () => { it('should have the correct curried types', () => { expectTypeOf(xor).toBeFunction(); expectTypeOf(xor).toBeCallableWith(0); @@ -24,7 +24,7 @@ describe('xor', () => { }); }); -describe('xorFn', () => { +describe('XorFn', () => { it('should have the correct curried types', () => { expectTypeOf(xorFn).toBeFunction(); expectTypeOf(xorFn).toBeCallableWith(isEven); diff --git a/packages/logic/src/xor/xor.test.ts b/packages/logic/src/xor/xor.test.ts index f636f92..e33147f 100644 --- a/packages/logic/src/xor/xor.test.ts +++ b/packages/logic/src/xor/xor.test.ts @@ -4,53 +4,49 @@ import { xor, xorFn } from '.'; const isEven = (x: number) => !(x & 1); const isOdd = (x: number) => !isEven(x); -describe('xor', () => { - describe('basic functionality', () => { - it('should return true for true | false', () => { - const actual = xor(true)(false); +describe('Xor', () => { + it('should return true for true | false', () => { + const actual = xor(true)(false); - expect(actual).toBe(true); - }); + expect(actual).toBe(true); + }); - it('should return true for false | true', () => { - const actual = xor(false)(true); + it('should return true for false | true', () => { + const actual = xor(false)(true); - expect(actual).toBe(true); - }); + expect(actual).toBe(true); + }); - it('should return false for true | true', () => { - const actual = xor(true)(true); + it('should return false for true | true', () => { + const actual = xor(true)(true); - expect(actual).toBe(false); - }); + expect(actual).toBe(false); + }); - it('should return false for false | false', () => { - const actual = xor(false)(false); + it('should return false for false | false', () => { + const actual = xor(false)(false); - expect(actual).toBe(false); - }); + expect(actual).toBe(false); }); - describe('edge cases', () => { - it('should handle truthy non-boolean values', () => { - const a = 'hello'; - const b = 42; - const actual = xor(a)(b); + it('should handle truthy non-boolean values', () => { + const a = 'hello'; + const b = 42; + const actual = xor(a)(b); - expect(actual).toBe(false); // Both truthy - }); + expect(actual).toBe(false); // Both truthy + }); - it('should handle mixed truthy/falsy', () => { - const a = 0; - const b = 'hello'; - const actual = xor(a)(b); + it('should handle mixed truthy/falsy', () => { + const a = 0; + const b = 'hello'; + const actual = xor(a)(b); - expect(actual).toBe(true); // Exactly one truthy - }); + expect(actual).toBe(true); // Exactly one truthy }); }); -describe('xorFn', () => { +describe('XorFn', () => { it('should return true for true | false', () => { const actual = xorFn(isEven)(isOdd)(4); diff --git a/packages/ordo/src/readme.test.ts b/packages/ordo/src/readme.test.ts index ef8c78f..a099a89 100644 --- a/packages/ordo/src/readme.test.ts +++ b/packages/ordo/src/readme.test.ts @@ -15,7 +15,7 @@ import { uint16, uint32, utf8, -} from './'; +} from '.'; describe('README Examples', () => { describe('Quick Start', () => { @@ -691,10 +691,8 @@ describe('README Examples', () => { return { transformIdx, renderableIdx, physicsIdx }; }; - // Act const entity = createEntity(); - // Assert expect(transforms.length).toBe(1); expect(renderables.length).toBe(1); expect(physics.length).toBe(1); diff --git a/packages/ordo/src/struct/dynamic-struct-array.test.ts b/packages/ordo/src/struct/dynamic-struct-array.test.ts index df1ed92..4209476 100644 --- a/packages/ordo/src/struct/dynamic-struct-array.test.ts +++ b/packages/ordo/src/struct/dynamic-struct-array.test.ts @@ -21,7 +21,7 @@ describe('DynamicStructArray', () => { }); }); - describe('get/set after #resize', () => { + describe('get/set After #resize', () => { it('should correctly get primitive fields after resize', () => { const def = struct({ id: uint32, value: float32 }); const arr = dynamicStructArray(def, 2); diff --git a/packages/ordo/src/struct/struct-array.test.ts b/packages/ordo/src/struct/struct-array.test.ts index e6bd3a9..9cefb08 100644 --- a/packages/ordo/src/struct/struct-array.test.ts +++ b/packages/ordo/src/struct/struct-array.test.ts @@ -69,7 +69,7 @@ describe('StructArray', () => { }); }); - describe('get/set primitives', () => { + describe('get/set Primitives', () => { it('should get a primitive field directly', () => { const def = struct({ id: uint32, value: float32 }); const arr = structArray(def, 10); diff --git a/packages/ordo/src/struct/struct-view.test.ts b/packages/ordo/src/struct/struct-view.test.ts index 33c0b1d..68ffdbb 100644 --- a/packages/ordo/src/struct/struct-view.test.ts +++ b/packages/ordo/src/struct/struct-view.test.ts @@ -15,7 +15,7 @@ import { } from './fields'; describe('StructView', () => { - describe('get/set primitives', () => { + describe('get/set Primitives', () => { it('should get and set primitive fields', () => { const def = struct({ id: uint32, @@ -65,7 +65,7 @@ describe('StructView', () => { }); }); - describe('get arrays', () => { + describe('get Arrays', () => { it('should return a TypedArray view for array fields', () => { const def = struct({ position: array('f32', 3), @@ -108,7 +108,7 @@ describe('StructView', () => { }); }); - describe('get/set utf8', () => { + describe('get/set Utf8', () => { it('should get and set utf8 fields', () => { const def = struct({ name: utf8(32), @@ -163,7 +163,7 @@ describe('StructView', () => { }); }); - describe('get circular buffer', () => { + describe('get CircularBuffer', () => { it('should get circular buffer field', () => { const def = struct({ history: circular('f32', 5), @@ -197,7 +197,7 @@ describe('StructView', () => { }); }); - describe('set on complex fields', () => { + describe('Set on Complex Fields', () => { it('should throw when trying to set an array field', () => { const def = struct({ position: array('f32', 3),