import { describe, it, expect } from "vitest"; import { describeActivity } from "@/lib/activity/format"; import type { ActivityRecord } from "@/lib/pb/types"; function row(overrides: Partial): ActivityRecord { return { id: "a1", created: "2026-09-16 10:00:00.000Z", project: "p1", card: "c1", actor: "u1", action: "card_updated", field: "", old_value: "", new_value: "", ...overrides, } as ActivityRecord; } describe("describeActivity", () => { it("describes a priority change in words, not codes", () => { expect( describeActivity( row({ action: "priority_changed", field: "priority", old_value: "medium", new_value: "high" }) ) ).toBe("changed priority from Medium to High"); }); it("describes setting a target date that had none", () => { expect( describeActivity( row({ action: "date_changed", field: "target_date", old_value: "", new_value: "2026-10-01" }) ) ).toBe("set the target date to 1 Oct 2026"); }); it("describes moving a target date", () => { expect( describeActivity( row({ action: "date_changed", field: "target_date", old_value: "2026-10-01 00:00:00.000Z", new_value: "2026-11-15", }) ) ).toBe("moved the target date from 1 Oct 2026 to 15 Nov 2026"); }); it("describes clearing a target date", () => { expect( describeActivity( row({ action: "date_changed", field: "target_date", old_value: "2026-10-01", new_value: "" }) ) ).toBe("cleared the target date"); }); it("describes card creation, acceptance and archiving", () => { expect(describeActivity(row({ action: "card_created", new_value: "Add a POS system" }))) .toBe("added this card"); expect(describeActivity(row({ action: "card_accepted" }))).toBe("accepted this request"); expect(describeActivity(row({ action: "card_archived" }))).toBe("archived this card"); }); it("names the destination when a card moves", () => { expect(describeActivity(row({ action: "card_moved", new_value: "In Progress" }))) .toBe("moved this card to In Progress"); }); it("falls back gracefully when a move has no destination recorded", () => { expect(describeActivity(row({ action: "card_moved", new_value: "" }))) .toBe("moved this card"); }); it("describes comments and subtasks", () => { expect(describeActivity(row({ action: "comment_added" }))).toBe("commented"); expect( describeActivity(row({ action: "subtask_toggled", new_value: "true", field: "Wire up the till" })) ).toBe("completed “Wire up the till”"); expect( describeActivity(row({ action: "subtask_toggled", new_value: "false", field: "Wire up the till" })) ).toBe("reopened “Wire up the till”"); }); it("describes a column move with the new column name", () => { expect( describeActivity( row({ action: "column_changed", field: "column", old_value: "col_done", new_value: "Backlog" }) ) ).toBe("moved this card to Backlog"); }); it("describes a column move out of a column when only the old name is known", () => { expect( describeActivity( row({ action: "column_changed", field: "column", old_value: "Done", new_value: "" }) ) ).toBe("moved this card out of Done"); }); it("describes a client proposal in human terms", () => { expect( describeActivity( row({ action: "date_proposed", field: "proposed_target_date", old_value: "", new_value: "2026-11-15", }) ) ).toBe("proposed moving this card to 15 Nov 2026"); }); it("describes an accepted proposal with its date", () => { expect( describeActivity( row({ action: "date_proposal_resolved", field: "proposed_target_date", old_value: "2026-11-15", new_value: "accepted", }) ) ).toBe("accepted the proposal for 15 Nov 2026"); }); it("describes a dismissed proposal with its date", () => { expect( describeActivity( row({ action: "date_proposal_resolved", field: "proposed_target_date", old_value: "2026-11-15", new_value: "dismissed", }) ) ).toBe("dismissed the proposal for 15 Nov 2026"); }); it("falls back gracefully when a resolved proposal has no date", () => { expect( describeActivity( row({ action: "date_proposal_resolved", field: "proposed_target_date", old_value: "", new_value: "accepted", }) ) ).toBe("accepted a proposal"); }); it("describes a generic update", () => { expect(describeActivity(row({ action: "card_updated", field: "description" }))) .toBe("updated the description"); expect(describeActivity(row({ action: "card_updated", field: "title" }))) .toBe("renamed this card"); }); it("never returns an empty string for an unknown action", () => { // Double assertion on purpose: the point is to simulate an action value // added by a later phase that this switch has not learned yet. const unknown = "something_new" as unknown as ActivityRecord["action"]; const result = describeActivity(row({ action: unknown })); expect(result.length).toBeGreaterThan(0); }); });