import { describe, expect, test } from "bun:test"; import { addTag, applyEntryPatch, orderEngagements, removeTag, setCustomValue } from "../src/edits.ts"; describe("entry edit builders", () => { test("two quick tag adds built from the latest cache keep both", () => { // Each edit reads the cache as left by the previous (optimistic) edit, not the render's props. let cache = { tags: [] as string[], rating: null as number | null }; const edit = (build: (cur: typeof cache) => Partial) => (cache = applyEntryPatch(cache, build(cache))); edit((cur) => ({ tags: addTag(cur.tags, "cosmic horror") })); edit((cur) => ({ tags: addTag(cur.tags, "re-read") })); expect(cache.tags).toEqual(["cosmic horror", "re-read"]); }); test("addTag trims and ignores case duplicates; removeTag removes one", () => { expect(addTag(["Horror"], " horror ")).toEqual(["Horror"]); expect(addTag(["a"], " ")).toEqual(["a"]); expect(removeTag(["a", "b"], "a")).toEqual(["b"]); }); test("setCustomValue sets, and removes empty values", () => { expect(setCustomValue({ a: 1 }, "b", "x")).toEqual({ a: 1, b: "x" }); expect(setCustomValue({ a: 1, b: "x" }, "b", "")).toEqual({ a: 1 }); expect(setCustomValue({ a: [1] }, "a", [])).toEqual({}); expect(setCustomValue({ a: 1 }, "a", null)).toEqual({}); }); }); describe("orderEngagements", () => { const g = (id: string, createdAt: string, finished?: string, started?: string) => ({ id, createdAt, finishedAt: finished ? { value: finished, precision: "day" as const } : null, startedAt: started ? { value: started, precision: "day" as const } : null, }); test("sorts by finishedAt ?? startedAt, then createdAt; undated first", () => { const list = [ g("late", "2026-01-01T00:00:00Z", "2026-09-14"), g("early-logged-later", "2026-10-01T00:00:00Z", "2025-03"), g("started-only", "2026-02-01T00:00:00Z", undefined, "2025-06-01"), g("undated", "2026-03-01T00:00:00Z"), ]; expect(orderEngagements(list).map((x) => x.id)).toEqual(["undated", "early-logged-later", "started-only", "late"]); }); });