import { installDom } from "./dom.ts"; import assert from "node:assert/strict"; import { createRequire } from "node:module"; import { test } from "node:test"; import { mountDashboard } from "../src/dashboard.ts"; import { createHttpTransport } from "../src/transport.ts"; import { canArchive, recentReceipts, reviewQueue, reviewState, stateView, workPackets, } from "../src/model.ts"; import { ownerNeeds } from "../src/needs-you-model.ts"; import { archiveFixtureState, archiveRace } from "./archive-fixtures.ts"; import { reviewFixtureDetail } from "./review-fixtures.ts"; const { JSDOM } = createRequire(import.meta.url)("jsdom"); const brand = { name: "Fixture product", tagline: "Fixture" }; async function until(check: () => boolean) { for (let i = 0; i < 200; i++) { if (check()) return; await new Promise((resolve) => setTimeout(resolve, 5)); } assert.fail("Archive DOM did not settle"); } function setup(id = "archive-ready", owner = true, failure = 0, hash = "#work") { const dom = new JSDOM('
', { url: `https://fixture.idiot.tax/${hash}` }); installDom(dom.window); const root = dom.window.document.getElementById("dashboard") as HTMLElement; const state = archiveFixtureState(id); const calls: string[] = []; const transport = createHttpTransport({ origin: dom.window.location.origin, fetch: async (input, init) => { const path = new URL(String(input)).pathname; calls.push(`${init?.method ?? "GET"} ${path}`); if (path.endsWith("/state")) return Response.json(state); if (path.endsWith("/activity")) return Response.json({ activity: state.activity, nextAfter: 0 }); if (path.endsWith("/reviews/review-1")) return Response.json(reviewFixtureDetail(state)); if (/\/(unarchive|archive)$/.test(path)) { if (failure === 409) state.races = [archiveRace()]; if (failure === 404) state.packets = state.packets.filter((p) => p.id !== "FH-040"); if (failure) return Response.json({ error: "Raw server error forbidden" }, { status: failure }); const packet = state.packets[0]; packet.archived = path.endsWith("/unarchive") ? null : { at: "2026-10-13T15:00:00Z", by: "owner" }; return Response.json({ packet }); } return Response.json({ error: "Missing fixture" }, { status: 404 }); }, }); const app = mountDashboard(root, { brand, transport, initialProject: id, fixture: true, owner }); const button = (action: string) => root.querySelector(`button[data-action="${action}"]`); async function open() { await until(() => !!root.querySelector("#packets .packet-link")); root.querySelector("#packets .packet-link")!.click(); if (state.packets[0].mode === "race" && !state.packets[0].archived) { await until(() => [...root.querySelectorAll("button")] .some((entry) => entry.textContent === "Packet details")); [...root.querySelectorAll("button")] .find((entry) => entry.textContent === "Packet details")!.click(); } await until(() => !!root.querySelector(".archive-heading")); } return { dom, root, state, calls, app, button, open, close() { app.dispose(); dom.window.close(); }, }; } test("archive predicate mirrors raw core review, claim, race and both blocker identities", () => { for (const id of [ "archive-claim", "archive-race", "archive-review", "archive-main-moved", "archive-blocker", ]) { const state = stateView(archiveFixtureState(id)); assert.equal(canArchive(state, "FH-040"), false, id); if (id === "archive-main-moved") assert.equal(reviewState(state.reviews[0], state).kind, "main-moved"); } const wire = archiveFixtureState(); const r = wire.reviews[0]; const projected = stateView(wire); delete projected.reviews[0].promoted; assert.equal(projected.reviews[0].status, "promoted"); assert.equal(canArchive(projected, "FH-040"), false, "display promoted cannot close a raw-open review"); r.promoted = false; r.approval = { sha: "9".repeat(40), receiptDigest: null }; assert.equal(stateView(wire).reviews[0].status, "stale"); assert.equal(canArchive(stateView(wire), "FH-040"), false, "display stale is still raw-open"); r.stale = true; assert.equal(canArchive(stateView(wire), "FH-040"), true); r.stale = false; r.returned = { reason: "Try again", at: "2026-10-13T14:00:00Z" }; assert.equal(canArchive(stateView(wire), "FH-040"), true); wire.races = [archiveRace("2026-10-13T14:00:00Z")]; assert.equal(canArchive(stateView(wire), "FH-040"), true); assert.equal(canArchive(stateView(wire), "FH-READY"), true, "never started"); const blockerWire = archiveFixtureState("archive-blocker"); assert.equal(canArchive(stateView(blockerWire), "FH-READY"), false); delete blockerWire.blockers[0].resolved; assert.equal(canArchive(stateView(blockerWire), "FH-040"), false, "legacy blocker stays open"); blockerWire.blockers[0].resolved = { at: "2026-10-13T14:00:00Z", by: "owner" }; assert.equal(canArchive(stateView(blockerWire), "FH-040"), true); }); test("archive filters Work, Review and Needs you but preserves receipts, ledger and activity", () => { const wire = archiveFixtureState("archive-list"); const review = wire.reviews[0]; review.promoted = false; review.returned = { reason: "History", at: "2026-10-13T14:00:00Z" }; const state = stateView(wire); assert.equal(reviewQueue(state).length, 0); assert.equal(ownerNeeds(state, brand).length, 0); assert.deepEqual( workPackets(state).map((p) => p.id), ["FH-READY"], ); assert.equal(recentReceipts(state).length, 1); assert.deepEqual( state.ledger.map((entry) => entry.sha), wire.ledger.map((entry) => entry.after), ); assert.deepEqual(state.recentActivity, wire.activity); state.packets.push({ ...state.packets[0], id: "older", archived: { at: "2026-10-12T14:00:00Z", by: "owner" }, }); assert.deepEqual( workPackets(state, true).map((p) => p.id), ["FH-READY", "FH-040", "older"], ); delete wire.packets[0].archived; assert.equal(stateView(wire).packets[0].archived, null); }); test("owner archives in the panel, retains focus, toggles neutral rows and undoes once", async () => { const c = setup(); try { await c.open(); const history = structuredClone({ reviews: c.state.reviews, ledger: c.state.ledger, activity: c.state.activity, }); c.button("archive")!.focus(); c.button("archive")!.click(); c.button("archive")?.click(); await until(() => !!c.button("unarchive")); assert.equal(c.dom.window.document.activeElement, c.button("unarchive")); await until(() => c.dom.window.document.body.textContent.includes("Archived FH-040")); assert.match(c.root.querySelector("#activity-panel")!.textContent!, /Archived .* by you/); assert.equal(c.root.querySelectorAll("#packets tbody tr").length, 1); assert.deepEqual( { reviews: c.state.reviews, ledger: c.state.ledger, activity: c.state.activity }, history, ); const packetsMetric = c.root.querySelector(".metrics p.metric-value")!; assert.equal(packetsMetric.textContent, "1"); assert.equal(c.root.querySelector(".metrics h3"), null); assert.equal(c.calls.filter((p) => p.startsWith("POST")).length, 1); c.button("toggle-archived")!.click(); await until(() => c.root.querySelectorAll("#packets tbody tr").length === 2); assert.equal(c.button("toggle-archived")!.getAttribute("aria-pressed"), "true"); const archivedRow = [...c.root.querySelectorAll("#packets tbody tr")].at(-1)!; assert.match(archivedRow.textContent!, /FH-040.*Archived/); assert.equal(archivedRow.querySelector(".status-dot"), null); assert.match(c.root.textContent!, /1 archived packets shown/); c.button("toggle-archived")!.click(); await until(() => c.root.querySelectorAll("#packets tbody tr").length === 1); c.button("unarchive")!.click(); await until(() => !!c.button("archive")); assert.equal(c.dom.window.document.activeElement, c.button("archive")); assert.equal(c.root.querySelectorAll("#packets tbody tr").length, 2); assert.equal(c.button("toggle-archived"), null); await until(() => c.dom.window.document.body.textContent.includes("Unarchived FH-040")); } finally { c.close(); } }); test("agents get the archived toggle and caption without archive or unarchive controls", async () => { for (const id of ["archive-ready", "archive-list"]) { const c = setup(id, false); try { if (id === "archive-list") { await until(() => !!c.button("toggle-archived")); c.button("toggle-archived")!.click(); await until(() => c.root.querySelectorAll("#packets .packet-link").length === 2); [...c.root.querySelectorAll("#packets .packet-link")].at(-1)!.click(); await until(() => !!c.root.querySelector(".archive-heading")); assert.match(c.root.querySelector("#activity-panel")!.textContent!, /by the owner/); } else await c.open(); assert.equal(c.button("archive"), null); assert.equal(c.button("unarchive"), null); assert.equal(c.calls.filter((p) => p.startsWith("POST")).length, 0); } finally { c.close(); } } }); test("all in-flight states hide panel Archive", async () => { for (const id of [ "archive-claim", "archive-race", "archive-review", "archive-main-moved", "archive-blocker", ]) { const c = setup(id); try { await c.open(); assert.equal(c.button("archive"), null, id); } finally { c.close(); } } }); test("archive errors refresh once, sanitize copy, hide after conflict and close on missing", async () => { for (const failure of [409, 404, 503]) { const c = setup("archive-ready", true, failure); try { await c.open(); c.button("archive")!.click(); await until(() => !!c.root.querySelector('[role="alert"]')); const message = c.root.querySelector('[role="alert"]')!.textContent!; assert.doesNotMatch(message, /Raw server/); assert.match( message, failure === 409 ? /FH-040 has work in flight/ : failure === 404 ? /This packet doesn't exist anymore/ : /Couldn't archive FH-040. Try again/, ); assert.equal(c.calls.filter((p) => p.startsWith("POST")).length, 1); assert.equal(c.calls.filter((p) => p.endsWith("/state")).length, 2); if (failure === 409) assert.equal(c.button("archive"), null); if (failure === 404) assert.equal(c.root.querySelector(".archive-heading"), null); } finally { c.close(); } } }); test("archived promoted review bookmark stays open with its neutral badge and evidence", async () => { const c = setup("archive-list", true, 0, "#review/review-1"); try { await until(() => !!c.root.querySelector(".review-header")); assert.match(c.root.querySelector(".review-header")!.textContent!, /FH-040.*Archived/); assert.match(c.root.querySelector(".review-detail")!.textContent!, /Promoted to main/); } finally { c.close(); } }); test("archive transport encodes packet routes and uses the existing same-origin session once", async () => { const calls: { url: string; init?: RequestInit }[] = []; const transport = createHttpTransport({ origin: "https://fixture.idiot.tax", fetch: async (input, init) => { calls.push({ url: String(input), init }); return Response.json({ packet: archiveFixtureState().packets[0] }); }, }); await transport.archivePacket!("project/one", "packet/one"); await transport.unarchivePacket!("project/one", "packet/one"); assert.deepEqual( calls.map((call) => new URL(call.url).pathname), [ "/projects/project%2Fone/packets/packet%2Fone/archive", "/projects/project%2Fone/packets/packet%2Fone/unarchive", ], ); for (const { init } of calls) { assert.equal(init?.method, "POST"); assert.equal(init?.credentials, "same-origin"); assert.equal(init?.cache, "no-store"); assert.equal(init?.redirect, "error"); assert.equal(init?.body, "{}"); assert.equal(new Headers(init?.headers).has("Authorization"), false); } }); test("uncertain unarchive failure stays in the panel with sanitized copy and no retry", async () => { const c = setup("archive-list", true, 503); try { await until(() => !!c.button("toggle-archived")); c.button("toggle-archived")!.click(); await c.open(); // The active row opened by setup is the next packet; select archived history instead. [...c.root.querySelectorAll("#packets .packet-link")].at(-1)!.click(); await until(() => !!c.button("unarchive")); c.button("unarchive")!.click(); await until(() => !!c.root.querySelector('[role="alert"]')); assert.match( c.root.querySelector('[role="alert"]')!.textContent!, /Couldn't unarchive FH-040. Try again./, ); assert.doesNotMatch(c.root.textContent!, /Raw server error/); assert.equal(c.calls.filter((call) => call.startsWith("POST")).length, 1); assert.ok(c.button("unarchive")); } finally { c.close(); } }); test("finished race keeps its track and opens packet details for archive and undo", async () => { const c = setup("archive-finished-race", true, 0, "#race/FH-040"); try { await until(() => [...c.root.querySelectorAll("button")] .some((button) => button.textContent === "Packet details")); const details = [...c.root.querySelectorAll("button")] .find((button) => button.textContent === "Packet details")!; assert.ok(details); details.click(); await until(() => !!c.button("archive")); assert.equal(c.dom.window.location.hash, "#work"); c.button("archive")!.click(); await until(() => !!c.button("unarchive")); c.button("toggle-archived")!.click(); await until(() => c.root.querySelectorAll("#packets tbody tr").length === 2); [...c.root.querySelectorAll("#packets .packet-link")].at(-1)!.click(); assert.ok(c.button("unarchive"), "archived race row opens details directly"); assert.equal(c.root.querySelector(".race-page"), null); c.button("unarchive")!.click(); await until(() => !!c.button("archive")); } finally { c.close(); } }); test("open race Work row routes both roles through the track to read-only live packet details", async () => { for (const owner of [true, false]) { const c = setup("archive-race", owner); try { await until(() => !!c.root.querySelector('#packets [data-packet-id="FH-040"]')); const row = c.root.querySelector('#packets [data-packet-id="FH-040"]')!; assert.match(row.textContent!, /Race/); assert.match(row.textContent!, /Racing/); assert.match(row.textContent!, /1 entrant/); row.querySelector(".packet-link")!.click(); await until(() => !!c.root.querySelector(".race-navigation")); assert.equal(c.dom.window.location.hash, "#race/FH-040"); const buttons = c.root.querySelectorAll(".race-navigation button"); assert.equal(buttons.length, 2); assert.match(buttons[0].textContent!, /Work/); assert.equal(buttons[1].textContent, "Packet details"); buttons[1].click(); await until(() => !!c.root.querySelector(".archive-heading")); assert.equal(c.button("archive"), null); assert.equal(c.button("unarchive"), null); assert.equal(c.calls.filter((call) => call.startsWith("POST")).length, 0); } finally { c.close(); } } }); test("finished archive race fixture records a promoted winner and closed other entrants", () => { const wire = archiveFixtureState("archive-finished-race"); const race = wire.races![0]; const winner = race.entrants.find((entrant) => entrant.agent === race.winner)!; assert.equal(wire.packets[0].mode, "race"); assert.equal(winner.promotedAt, race.finishedAt); assert.equal(winner.place, 1); assert.equal(winner.closedAt, race.finishedAt); assert.equal(winner.closedBy, null); assert.equal(race.ledgerEntryId, wire.ledger[0].id); assert.equal(wire.reviews[0].agent, winner.agent); assert.ok(wire.agents.some((agent) => agent.name === winner.agent)); assert.ok(race.entrants.filter((entrant) => entrant !== winner) .every((entrant) => entrant.closedBy === "race-finished" && entrant.closedAt === race.finishedAt)); assert.equal(canArchive(stateView(wire), "FH-040"), true); });