import assert from "node:assert/strict"; import * as runner from "../runner.mjs"; import { keys, difference, assocIn, dissoc, getIn, findFirst, partial, strSortFn, undefinedAsNull, first, rest, butlast, last, take, range, } from "../../src/utils.mjs"; const test_keys = async t => { t.start("keys()"); await t.test("happy paths", () => { assert.deepEqual( { a: 1, b: 2 }, keys(["a", "b"], { a: 1, b: 2, c: 3 }), ); }); await t.test("stress scenarios", () => { assert.deepEqual( {}, keys([], {}), "empty selection of empty object", ); assert.deepEqual( {}, keys([], {a: 1}), "empty selection of non-empty object", ); assert.deepEqual( {}, keys(["a"], {}), "non-empty selection of empty object", ); assert.deepEqual( { a: undefined, b: null }, keys(["a", "b", "c"], { a: undefined, b: null }), "falsy values", ); }); }; const test_difference = async t => { t.start("difference()"); await t.test("empty values", () => { assert.deepEqual( difference(new Set(), new Set()), new Set(), ); assert.deepEqual( difference(new Set(), new Set([1, 2])), new Set(), ); assert.deepEqual( difference(new Set([1, 2]), new Set()), new Set([1, 2]), ); }); await t.test("different subsets", () => { assert.deepEqual( difference(new Set([1, 2]), new Set([3, 4])), new Set([1, 2]), ); assert.deepEqual( difference(new Set([1, 2, 3]), new Set([2, 4, 5])), new Set([1, 3]), ); assert.deepEqual( difference(new Set([1]), new Set([1, 2, 3, 4, 5])), new Set(), ); assert.deepEqual( difference(new Set([1, 2, 3]), new Set([1, 2, 3])), new Set(), ); }); }; const test_assocIn = async t => { t.start("assocIn()"); await t.test("empty values", () => { assert.deepEqual(assocIn({}, [], null), {}); assert.deepEqual(assocIn({ k: "v" }, [], null), { k: "v" }); }); await t.test("adding values", () => { assert.deepEqual(assocIn({}, ["k"], "v"), { k: "v" }); assert.deepEqual(assocIn({}, ["k1", "k2"], "v"), { k1: { k2: "v" }}); assert.deepEqual(assocIn({}, ["k1", "k2", "k3"], "v"), { k1: { k2: { k3: "v" }}}); assert.deepEqual(assocIn({ k: "v" }, ["k1", "k2"], "v"), { k: "v", k1: { k2: "v" }}); }); await t.test("replacing values", () => { assert.deepEqual( assocIn( { k1: { k2: { k3: "before" }}}, ["k1", "k2", "k3"], "after" ), { k1: { k2: { k3: "after" }}} ); }); }; const test_dissoc = async t => { t.start("dissoc()"); await t.test("empty values", () => { assert.deepEqual(dissoc({}, "k"), {}); }); await t.test("noop when key does not exist", () => { assert.deepEqual(dissoc({ a: 1 }, "b"), { a: 1 }); }); await t.test("removes the key", () => { assert.deepEqual(dissoc({ a: 1, b: 2}, "b"), { a: 1 }); }); }; const test_getIn = async t => { t.start("getIn()"); await t.test("empty values", () => { assert.deepEqual(getIn({}, []), {}); assert.deepEqual(getIn({ k: "v" }, []), { k: "v" }); }); await t.test("missing values", () => { assert.deepEqual(getIn({}, ["a", "b", "c"]), undefined); assert.deepEqual(getIn({ a: {}}, ["a", "b", "c"]), undefined); assert.deepEqual(getIn({ a: { b: {}}}, ["a", "b", "c"]), undefined); assert.deepEqual(getIn({ a: { b: {}, c: {}}}, ["a", "b", "c"]), undefined); }); await t.test("nested valeues", () => { assert.deepEqual(getIn({ a: { b: { c: { d: "e" }}}}, ["a", "b", "c", "d"]), "e"); }); }; const test_findFirst = async t => { t.start("findFirst()"); await t.test("empty values", () => { assert.equal(findFirst([], () => {}), null); }); await t.test("when function doesn't transform, it behaves similarly to [].find()", () => { const arr1 = [ 0, null, undefined, "", 1, 2 ]; assert.equal(findFirst(arr1, x => x), 1); assert.equal(arr1.find(x => x), 1); const arr2 = [ 0, null, undefined, "", false ]; assert.equal(findFirst(arr2, x => x), null); assert.equal(arr2.find(x => x), undefined); }); await t.test("when it does transform, we return the transformed value", () => { const arr = [ 1, 3, 5, 6 ]; assert.equal( findFirst(arr, x => x % 2 === 0 && "a brand new value"), "a brand new value", ); }); }; const test_partial = async t => { t.start("partial()"); await t.test("empty values", () => { const adder = (a, b, c) => a + b + c; const adder1 = partial(adder); assert.equal(adder1(1, 2, 3), 6); const adder2 = partial(adder, 4, 5, 6); assert.equal(adder2(), 15); const noargs = () => "static"; assert.equal(partial(noargs)(), noargs()); }); await t.test("too few arguments", () => { const threeArgs = (a, b, c) => { assert.notEqual(c, undefined); return a + b + c }; const add1 = partial(threeArgs, 1); assert.throws( () => add1(2), assert.AssertionError, ); const add1And2 = partial(threeArgs, 1, 2); assert.throws( () => add1And2(), assert.AssertionError, ); const addNothing = partial(threeArgs); assert.throws( () => addNothing(), assert.AssertionError, ); }); await t.test("too many arguments", () => { const twoArgs = (a, b) => a + b; assert.equal(partial(twoArgs, 1)(2, 3), 3); assert.equal(partial(twoArgs, 1, 2)(3), 3); }); await t.test("daily usage", () => { const twoArg = (a, b) => a + b; const numbers = [ 1, 2, 3, 4, 5 ]; assert.deepEqual( numbers.map(partial(twoArg, 2)), [ 3, 4, 5, 6, 7 ], ); }); await t.test("nested partials", () => { const threeArgs = (a, b, c) => a + b + c; const add1 = partial(threeArgs, 1); const add1And2 = partial(add1, 2); assert.equal(add1And2(3), 6); }); }; const test_strSortFn = async t => { t.start("strSortFn()"); await t.test("empty value", () => { assert.equal(strSortFn("", ""), 0); }); await t.test("default sort", () => { const arr = [ "a", "Z" ]; assert.deepEqual( [...arr].sort(strSortFn), [...arr].sort().reverse(), ); }); }; const test_undefinedAsNull = async t => { t.start("undefinedAsNull()"); await t.test("null for undefined or null", () => { assert.equal(undefinedAsNull(undefined), null); assert.equal(undefinedAsNull(null), null); }); await t.test("identity otherwise", () => { const expected = [ " ", "", 0, 1, -1.1, true, false, [], [ "" ], {}, { k: "v" }, ]; const given = expected.map(undefinedAsNull); assert.deepEqual(given, expected); }); }; const test_first = async t => { t.start("first()"); await t.test("undefined for an empty array", () => { assert.equal(undefined, first([])); assert.equal(undefined, first("")); }); await t.test("the first element otherwise", () => { assert.equal(1, first([1, 2, 3])); assert.equal("a", first("abc")); }); }; const test_rest = async t => { t.start("rest()"); await t.test("an empty array when no more elements are available", () => { assert.deepEqual([], rest([])); assert.deepEqual([], rest([1])); assert.equal("", rest("")); assert.equal("bc", rest("abc")); }); await t.test("the rest of the collection otherwise", () => { assert.deepEqual([2, 3], rest([1, 2, 3])); assert.equal("bc", rest("abc")); }); await t.test("combines with first() well", () => { const arr = ["anything", "can", "go", "here"]; assert.deepEqual(arr, [ first(arr), ...rest(arr) ]); }); }; const test_butlast = async t => { t.start("butlast()"); await t.test("empty array when ther are no more elements", () => { assert.deepEqual([], butlast([])); assert.deepEqual([], butlast([1])); assert.equal("", butlast("")); assert.equal("", butlast("c")); }); await t.test("the beginning of the array otherwise", () => { assert.deepEqual([1, 2], butlast([1, 2, 3])); assert.equal("ab", butlast("abc")); }); }; const test_last = async t => { t.start("last()"); await t.test("undefined for an empty array", () => { assert.equal(undefined, last([])); assert.equal(undefined, last("")); }); await t.test("the last element otherwise", () => { assert.equal(3, last([1, 2, 3])); assert.equal("c", last("abc")); }); await t.test("combines with butlast() well", () => { const arr = ["anything", "goes", "here", "too"]; assert.deepEqual(arr, [ ...butlast(arr), last(arr) ]); }); }; const test_take = async t => { t.start("take()"); await t.test("example usage", () => { assert.deepEqual(Array.from(take(3, [1, 2, 3, 4, 5, 6])), [1, 2, 3]); assert.deepEqual([...take(3, [1, 2, 3, 4, 5, 6])], [1, 2, 3]); const gen = function*() { yield* [1, 2, 3, 4, 5, 6]; } assert.deepEqual([...take(3, gen())], [1, 2, 3]); assert.deepEqual([...take(3, [1, 2])], [1, 2]); assert.deepEqual([...take(1, [])], []); assert.deepEqual([...take(0, [1])], []); assert.deepEqual([...take(-1, [1])], []); }); }; const test_range = async t => { t.start("range()"); await t.test("empty values", () => { const [] = range(); const [ a ] = range(); assert.equal(a, 0n); const [ b, c, d, e ] = range(); assert.deepEqual( [ b, c, d, e ], [ 0n, 1n, 2n, 3n ], ); assert.deepEqual( Array.from(take(5, range())), [ 0n, 1n, 2n, 3n, 4n ], ); assert.deepEqual( Array.from(take(1, range())), [ 0n ], ); assert.deepEqual( Array.from(take(0, range())), [], ); }); await t.test("example usage", () => { assert.deepEqual( [...range(-5, 5)], [ -5n, -4n, -3n, -2n, -1n, 0n, 1n, 2n, 3n, 4n ], ); assert.deepEqual( [...range(-100, 100, 10)], [ -100n, -90n, -80n, -70n, -60n, -50n, -40n, -30n, -20n, -10n, 0n, 10n, 20n, 30n, 40n, 50n, 60n, 70n, 80n, 90n, ], ); assert.deepEqual([...range(0, 4, 2)], [0n, 2n]); assert.deepEqual([...range(0, 5, 2)], [0n, 2n, 4n]); assert.deepEqual([...range(0, 6, 2)], [0n, 2n, 4n]); assert.deepEqual([...range(0, 7, 2)], [0n, 2n, 4n, 6n]); assert.deepEqual( [...range(100, 0, -10)], [ 100n, 90n, 80n, 70n, 60n, 50n, 40n, 30n, 20n, 10n ] ); assert.deepEqual( [...range(10, -10, -1)], [ 10n, 9n, 8n, 7n, 6n, 5n, 4n, 3n, 2n, 1n, 0n, -1n, -2n, -3n, -4n, -5n, -6n, -7n, -8n, -9n, ], ); assert.deepEqual( [...take(3, range(1, 10, 0))], [ 1n, 1n, 1n ], ); assert.deepEqual( [...take(3, range(10, 1, 0))], [ 10n, 10n, 10n ], ); }); }; await runner.runTests([ test_keys, test_difference, test_assocIn, test_dissoc, test_getIn, test_findFirst, test_partial, test_strSortFn, test_undefinedAsNull, test_first, test_rest, test_butlast, test_last, test_take, test_range, ]);