import { describe, it, expect } from "vitest"; import { parseBoardLine, parseBoardText, suggestShot } from "./board-parse"; const shots = [ { id: "s1", shotCode: "DH2_VFX_027_090" }, { id: "s2", shotCode: "DH2_VFX_005_010" }, { id: "s3", shotCode: "DH2_VFX_004_100" }, { id: "s4", shotCode: "UNG_117_023_010", clientCode: "EP17_VFX_003" }, ]; const users = [ { id: "u1", name: "Chris Pinto", email: "chris@example.com" }, { id: "u2", name: "Michelle Pinto", email: "michelle@example.com" }, ]; const ctx = { shots, users }; describe("parseBoardLine", () => { it("links the shot and assigns from (NAME)", () => { expect(parseBoardLine("DH2_VFX_027_090 finalise (CHRIS)", ctx)).toEqual({ text: "finalise", code: "DH2_VFX_027_090", shotId: "s1", assigneeId: "u1", waitingOn: null, group: "", }); }); it("matches codes case-insensitively and by client alias", () => { expect(parseBoardLine("dh2_vfx_027_090 x", ctx).shotId).toBe("s1"); const alias = parseBoardLine("EP17_VFX_003 warp fix", ctx); expect(alias.shotId).toBe("s4"); expect(alias.code).toBe("UNG_117_023_010"); }); it("keeps an unknown code as typed for flagging", () => { const r = parseBoardLine("DH2_004_100 not on our tracker?", ctx); expect(r.code).toBe("DH2_004_100"); expect(r.shotId).toBeNull(); expect(r.text).toBe("not on our tracker?"); }); it("assigns from @name and keeps the rest of a parenthesised group", () => { expect(parseBoardLine("DH2_VFX_005_010 gfx @michelle", ctx).assigneeId).toBe("u2"); const r = parseBoardLine("(MICHELLE ROTO) Maagie shot", ctx); expect(r.assigneeId).toBe("u2"); expect(r.text).toBe("ROTO Maagie shot"); }); it("leaves parentheses that are not a user alone", () => { const r = parseBoardLine("DH2_VFX_005_010 track (90% done)", ctx); expect(r.assigneeId).toBeNull(); expect(r.text).toBe("track (90% done)"); }); it("splits off what the line is waiting on", () => { const r = parseBoardLine( "DH2_VFX_005_010 gfx required for update (MICHELLE) wait: client GFX ADH_GFX_005_020", ctx, ); expect(r.shotId).toBe("s2"); expect(r.waitingOn).toBe("client GFX ADH_GFX_005_020"); expect(r.text).toBe("gfx required for update"); expect(parseBoardLine("x waiting on: grade", ctx).waitingOn).toBe("grade"); // no colon → prose expect(parseBoardLine("awaiting gfx", ctx).waitingOn).toBeNull(); }); it("takes a note with no shot code", () => { const r = parseBoardLine("- Deposit received", ctx); expect(r).toMatchObject({ text: "Deposit received", code: null, shotId: null }); }); }); describe("parseBoardText", () => { it("groups lines under headings and skips blanks", () => { const items = parseBoardText( "DH2_VFX_027_090 finalise (CHRIS)\n\nWATER DROPS:\nDH2_VFX_005_010 still do (CHRIS)\nDH2_VFX_004_100\n", ctx, ); expect(items.map((i) => [i.code, i.group, i.text])).toEqual([ ["DH2_VFX_027_090", "", "finalise"], ["DH2_VFX_005_010", "WATER DROPS", "still do"], ["DH2_VFX_004_100", "WATER DROPS", ""], ]); }); it("treats an all-caps line without a code as a heading", () => { const items = parseBoardText("FLAGS\nDH2_004_100 not on our tracker?", ctx); expect(items).toHaveLength(1); expect(items[0].group).toBe("FLAGS"); }); it("never treats a single typed line as a heading", () => { expect(parseBoardText("CALL CLIENT", ctx)).toHaveLength(1); }); }); describe("suggestShot", () => { it("suggests the shot with the same prefix and numbers", () => { expect(suggestShot("DH2_004_100", shots)?.id).toBe("s3"); }); it("does not guess between several candidates", () => { const dup = [...shots, { id: "s5", shotCode: "DH2_GFX_004_100" }]; expect(suggestShot("DH2_004_100", dup)).toBeNull(); }); });