import { installDom } from "./dom.ts"; import { createRequire } from "node:module"; import assert from "node:assert/strict"; import { test } from "node:test"; import { mountDashboard } from "../src/dashboard.ts"; import { createHttpTransport } from "../src/transport.ts"; import { reviewGate, stateView } from "../src/model.ts"; import { reviewFixtureState, reviewFixtureDetail, reviewFixtureDiff } from "./review-fixtures.ts"; const { JSDOM } = createRequire(import.meta.url)("jsdom"); const origin = "https://fixture.idiot.tax"; async function until(fn: () => unknown) { for (let i = 0; i < 250; i++) { if (fn()) return; await new Promise((r) => setTimeout(r, 5)); } assert.fail("Review DOM did not settle"); } function setup( scenario = "review-current", owner = true, route = "#review/review-1", overrides: { detail?: () => Promise; diff?: () => Promise; returning?: () => Promise; noDetail?: boolean; noDiff?: boolean; } = {}, ) { const dom = new JSDOM('
', { url: `${origin}/${route}` }); installDom(dom.window); const root = dom.window.document.getElementById("dashboard")! as HTMLElement; const wire = reviewFixtureState(scenario); const calls: { url: URL; init?: RequestInit }[] = []; const transport = createHttpTransport({ origin, fetch: async (input, init) => { const url = new URL(String(input)); calls.push({ url, init }); if (url.pathname.endsWith("/state")) return Response.json(wire); if (url.pathname.endsWith("/activity")) return Response.json({ activity: [], nextAfter: 0 }); if (url.pathname.endsWith("/diff")) return overrides.diff ? overrides.diff() : url.searchParams.get("path") === "missing.txt" ? Response.json({ error: "missing" }, { status: 404 }) : Response.json(reviewFixtureDiff(url.searchParams.get("path")!)); if (url.pathname.endsWith("/return")) { if (overrides.returning) return overrides.returning(); wire.reviews[0].returned = { reason: JSON.parse(String(init!.body)).reason, at: "2026-10-13T08:00:00Z", }; return Response.json({ review: wire.reviews[0] }); } if (url.pathname.endsWith("/approve")) { wire.reviews[0].approval = { sha: wire.reviews[0].sha, receiptDigest: wire.reviews[0].receiptDigest, }; return Response.json({ review: wire.reviews[0] }); } if (url.pathname.endsWith("/receipt")) return Response.json({ error: "fixture logs" }, { status: 404 }); return overrides.detail ? overrides.detail() : scenario === "review-not-found" ? Response.json({ error: "missing" }, { status: 404 }) : Response.json(reviewFixtureDetail(wire)); }, }); if (overrides.noDetail) delete transport.getReview; if (overrides.noDiff) delete transport.getDiff; const app = mountDashboard(root, { brand: { name: "Fixture product", tagline: "Fixture" }, fixture: true, owner, agent: "fixture-agent", initialProject: scenario, transport, }); return { dom, root, wire, calls, app, transport, close() { app.dispose(); dom.window.close(); }, }; } function button(c: ReturnType, text: string, dialog = false): HTMLButtonElement { return [...(dialog ? c.dom.window.document : c.root).querySelectorAll("button")].find( (b: HTMLButtonElement) => b.textContent === text, )!; } function fill(c: ReturnType, value: string) { const element = c.dom.window.document.querySelector("textarea") as HTMLTextAreaElement; Object.getOwnPropertyDescriptor(c.dom.window.HTMLTextAreaElement.prototype, "value")!.set!.call( element, value, ); element.dispatchEvent(new c.dom.window.Event("input", { bubbles: true })); } test("hosts without detail capability keep non-linking queue rows and resolve bookmarked or typed detail hashes to the queue", async () => { for (const route of ["#review", "#review/review-1"]) { const c = setup("review-current", true, route, { noDetail: true }); try { await until(() => c.root.querySelector(".review-card")); assert.equal(c.dom.window.location.hash, "#review"); assert.equal(!!c.root.querySelector(".review-detail"), false); assert.equal(!!c.root.querySelector(".review-card .claim-link"), false); assert.equal(!!c.root.querySelector('.review-card a[href*="#review/"]'), false); const row = c.root.querySelector(".review-card")!; row.click(); row.querySelector("td span")!.click(); assert.equal(c.dom.window.location.hash, "#review"); c.dom.window.location.hash = "#review/review-1"; await until(() => c.dom.window.location.hash === "#review"); assert.equal(!!c.root.querySelector("#reviews"), true); assert.equal( c.calls.some((call) => call.url.pathname.endsWith("/reviews/review-1")), false, ); } finally { c.close(); } } }); test("missing diff capability uses product copy and missing receipt/ledger evidence stays plain", async () => { const c = setup("review-promoted", true, "#review/review-1", { noDiff: true, detail: async () => { const detail = reviewFixtureDetail(c.wire); detail.review.receipt = null; detail.promotion = null; return Response.json(detail); }, }); try { await until(() => c.root.querySelector(".review-file-toggle")); assert.match(c.root.textContent!, /No receipt/); assert.equal( c.root.querySelectorAll(".review-milestone")[0].textContent!.match(/No receipt/g)?.length, 1, ); assert.match(c.root.textContent!, /No ledger entry recorded\./); c.root.querySelector(".review-file-toggle")!.click(); await until(() => c.root.textContent!.includes("Couldn't load this diff.")); assert.doesNotMatch( c.root.querySelector(".review-detail")!.textContent!, /host adapter|unavailable/, ); assert.equal( c.calls.some((call) => call.url.pathname.endsWith("/diff")), false, ); } finally { c.close(); } }); test("detail route replaces queue, retains activity, three independent evidence cells and request list", async () => { const c = setup(); try { await until(() => c.root.querySelector(".review-milestones")); assert.equal(c.root.querySelector("#reviews"), null); assert.equal(c.root.querySelectorAll(".review-milestone").length, 3); assert.match(c.root.textContent!, /Checks passed.*Not approved yet.*Not promoted yet/s); assert.match( c.root.querySelectorAll(".review-milestone")[2].textContent!, /No promotion recorded\./, ); assert.ok(c.root.querySelector(".review-acceptance li")); assert.match( c.root.querySelector(".plain-text")!.textContent!, /request and inspect each claim\.\nKeep/, ); assert.ok(c.root.querySelector("#activity-panel")); assert.ok(button(c, "Approve")); assert.ok(button(c, "Return to agent")); assert.equal(c.root.querySelector('[data-variant="primary"]'), null); button(c, "← Review queue").click(); await until(() => c.root.querySelector("#reviews")); assert.equal(c.dom.window.location.hash, "#review"); c.root.querySelector(".review-card .claim-link")!.click(); await until(() => c.root.querySelector(".review-milestones")); assert.equal(c.dom.window.location.hash, "#review/review-1"); c.dom.window.history.back(); await until(() => c.root.querySelector("#reviews")); } finally { c.close(); } }); test("return required labelled reason, counter, escaped multiline owner and agent view, never approve returned", async () => { const c = setup(); try { await until(() => button(c, "Return to agent")); button(c, "Return to agent").click(); await until(() => c.dom.window.document.querySelector("textarea")); const confirm = c.dom.window.document.querySelector('[data-action="confirm-return"]')!; assert.equal(confirm.disabled, true); assert.equal( c.dom.window.document.querySelector("label[for=return-reason]")!.textContent, "What needs to change?", ); assert.equal( c.dom.window.document.querySelector("textarea")!.hasAttribute("placeholder"), false, ); await until(() => c.dom.window.document.activeElement?.tagName === "TEXTAREA"); fill(c, "Fix \nSecond line"); await until(() => !confirm.disabled); assert.equal( c.dom.window.document.getElementById("return-counter")!.textContent, `${"Fix \nSecond line".length} / 2000`, ); confirm.click(); confirm.click(); await until(() => c.root.querySelector(".returned-reason")); assert.equal(c.calls.filter((r) => r.url.pathname.endsWith("/return")).length, 1); assert.equal(c.root.querySelector(".returned-reason script"), null); assert.equal( c.root.querySelector(".returned-reason .plain-text")!.textContent, "Fix \nSecond line", ); assert.equal(c.root.querySelector('[data-action="approve"]'), null); assert.equal(c.root.querySelector('[data-action="promote"]'), null); assert.equal(c.root.querySelector('[data-action="return"]'), null); button(c, "← Review queue").click(); await until(() => c.root.querySelector(".returned-preview")); assert.match(c.root.querySelector(".review-card")!.textContent!, /Returned/); assert.equal( c.root.querySelector(".returned-preview")!.textContent, "Fix ", ); const state = stateView(c.wire); assert.equal(reviewGate(state.reviews[0], state).approve, false); } finally { c.close(); } const agent = setup("review-returned", false); try { await until(() => agent.root.querySelector(".returned-reason")); assert.equal(agent.root.querySelector('[data-action="return"]'), null); assert.equal(agent.root.querySelector('[data-action="approve"]'), null); assert.match( agent.root.querySelector(".returned-reason")!.textContent!, /