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, ApiError } from "../src/transport.ts"; import { renderDashboard } from "../src/render.ts"; import { assertState, reviewGate, stateView } from "../src/model.ts"; import { DIGEST, fixtureActivity, fixtureState, NEXT_HEAD } from "./fixtures.ts"; import { getByRole } from "./roles.ts"; const require = createRequire(import.meta.url); // jsdom is used only for local DOM acceptance; production has no runtime dependency. const { JSDOM } = require("jsdom") as { JSDOM: new ( html: string, options?: { url: string }, ) => { window: { document: Document; MouseEvent: typeof MouseEvent; Event: typeof Event; close(): void; }; }; }; const brand = { name: "Fixture product", tagline: "Fixture collaboration" }; const origin = "https://workspace.idiot.tax"; function apiFixture() { const state = fixtureState(); const calls: { url: URL; options: RequestInit }[] = []; let failedAction = false; let unauthorized = false; const fetcher: typeof fetch = async (input, options = {}) => { const url = new URL(String(input)); calls.push({ url, options }); if (unauthorized) return Response.json({ error: "Unauthorized" }, { status: 403 }); if (url.pathname === "/projects/quiet/state") return Response.json(fixtureState("quiet")); if (url.pathname === "/projects/quiet/activity") return Response.json({ activity: [], nextAfter: 0 }); if (url.pathname === "/projects/demo/state") return Response.json(state); if (url.pathname === "/projects/demo/activity") return Response.json({ activity: fixtureActivity(), nextAfter: 3 }); if (options.method === "POST") { if (failedAction) { state.reviews[0].stale = true; return Response.json({ error: "stale base, rebase" }, { status: 409 }); } const review = state.reviews[0]; if (url.pathname === "/projects/demo/reviews/review-1/approve") { review.approval = { sha: review.sha, receiptDigest: review.receiptDigest }; return Response.json({ review }); } if (url.pathname === "/projects/demo/reviews/review-1/promote") { assert.ok(review.approval); review.promoted = true; state.ledger.push({ id: "merge-1", review: review.id, repoName: state.project.repoName, ref: "refs/heads/main", before: review.base, after: review.sha, receiptDigest: review.receiptDigest, recordedAt: "2026-10-12T00:20:00.000Z", }); return Response.json({ sha: review.sha }); } } return Response.json({ error: "Not found" }, { status: 404 }); }; return { state, calls, fetcher, failAction() { failedAction = true; }, expireSession() { unauthorized = true; }, }; } async function until(check: () => boolean) { for (let attempt = 0; attempt < 200; attempt++) { if (check()) return; await new Promise((resolve) => setTimeout(resolve, 5)); } assert.fail("DOM did not reach expected state"); } function setup() { const dom = new JSDOM('
'); installDom(dom.window as unknown as Window & typeof globalThis); const root = dom.window.document.getElementById("dashboard")!; const fixture = apiFixture(); const app = mountDashboard(root, { brand, fixture: true, projects: [ { id: "demo", label: "Demo" }, { id: "quiet", label: "Quiet" }, ], transport: createHttpTransport({ origin, fetch: fixture.fetcher }), }); return { dom, root, fixture, app, cleanup() { app.dispose(); dom.window.close(); }, }; } const button = (root: HTMLElement, action: string, review = "review-1") => root.querySelector( `button[data-action="${action}"][data-review="${review}"]`, )!; test("fixture DOM renders every packet view with fork-bound claims and evidence", async () => { const context = setup(); try { await until(() => Boolean(context.root.querySelector("#reviews"))); for (const section of ["lanes", "packets", "blockers", "activity"]) assert.ok(context.root.querySelector(`#${section}`)); assert.match(context.root.textContent!, /FIXTURE · OFFLINE/); assert.match(context.root.textContent!, /demo--builder-1/); assert.match(context.root.textContent!, /Passed/); assert.match(context.root.textContent!, /Fork moved/); assert.equal(button(context.root, "approve").disabled, false); assert.equal(button(context.root, "promote"), null); assert.equal(button(context.root, "approve", "review-2"), null); assert.equal(button(context.root, "approve", "review-3"), null); const blocked = Array.from(context.root.querySelectorAll(".review-card")).find((row) => row.textContent!.includes("api-02"), )!; assert.match(blocked.textContent!, /Waiting for checks/); } finally { context.cleanup(); } }); test("review rows explain blocker gates and activity is empty only after a successful snapshot", () => { const state = stateView(fixtureState()); const blockedReview = state.reviews.find((review) => review.packet === "api-02")!; blockedReview.receiptDigest = DIGEST; blockedReview.receipt = state.reviews[0].receipt; const base = { brand, projects: [], project: "demo", fixture: true, activity: [], busy: false }; for (const snapshot of [ { ...base, state }, { ...base, busy: true }, { ...base, error: "Snapshot unavailable" }, ]) { const dom = new JSDOM(renderDashboard(snapshot)); try { const activity = dom.window.document.querySelector("#activity")!; assert.equal(activity.textContent!.includes("No pushes yet."), "state" in snapshot); if ("state" in snapshot) { const blocked = Array.from(dom.window.document.querySelectorAll(".review-card")).find( (row) => row.textContent!.includes("api-02"), )!; assert.match(blocked.textContent!, /Blocked: this fork pushed into api-02's claimed paths/); assert.equal(activity.querySelectorAll("p").length, 1); } else { assert.equal(activity.textContent, ""); } } finally { dom.window.close(); } } }); test("owner DOM Approve calls the review endpoint once; Promote refreshes head and ledger", async () => { const context = setup(); try { await until(() => Boolean(context.root.querySelector("#reviews"))); const approve = button(context.root, "approve"); approve.click(); approve.click(); await until(() => context.dom.window.document.body.textContent!.includes("Approved bbbbbbb")); assert.ok( [...context.dom.window.document.querySelectorAll("code")].some( (code) => code.textContent === "bbbbbbb" && code.parentElement?.textContent === "Approved bbbbbbb", ), "Approved toast renders SHA as code", ); assert.equal(context.fixture.calls.filter((call) => call.options.method === "POST").length, 1); const approvalCall = context.fixture.calls.find((entry) => entry.options.method === "POST")!; assert.equal(approvalCall.url.pathname, "/projects/demo/reviews/review-1/approve"); assert.equal(approvalCall.options.credentials, "same-origin"); assert.equal(approvalCall.options.body, "{}"); assert.deepEqual(approvalCall.options.headers, { "Content-Type": "application/json" }); await until(() => !!button(context.root, "promote")); button(context.root, "promote").click(); await until( () => !!context.dom.window.document.querySelector('[data-action="confirm-promote"]'), ); assert.equal( context.fixture.calls.filter((call) => call.options.method === "POST").length, 1, "opening confirmation performs no write", ); context.dom.window.document .querySelector('[data-action="confirm-promote"]')! .click(); await until(() => context.dom.window.document.body.textContent!.includes("Promoted to main")); assert.equal(context.fixture.state.ledger[0].after, NEXT_HEAD); assert.equal(context.fixture.state.ledger.length, 1); assert.ok(context.fixture.calls.some((entry) => entry.url.pathname.endsWith("/promote"))); assert.equal(button(context.root, "approve"), null); } finally { context.cleanup(); } }); test("a stale-base conflict is surfaced and refreshed without retrying the owner write", async () => { const context = setup(); try { await until(() => Boolean(context.root.querySelector("#reviews"))); context.fixture.failAction(); button(context.root, "approve").click(); await until( () => context.root .querySelector('[role="alert"]') ?.textContent?.includes("stale base, rebase") === true, ); assert.equal(button(context.root, "approve"), null); assert.equal(context.fixture.calls.filter((call) => call.options.method === "POST").length, 1); } finally { context.cleanup(); } }); test("expired session clears old actionable state; project switching shows empty state", async () => { const context = setup(); try { await until(() => Boolean(context.root.querySelector("#reviews"))); const combo = context.root.querySelector('[role="combobox"]')!; combo.focus(); combo.dispatchEvent( new context.root.ownerDocument.defaultView!.KeyboardEvent("keydown", { key: "ArrowDown", bubbles: true, }), ); await until(() => !!context.dom.window.document.querySelector('[role="option"]')); const option = [ ...context.dom.window.document.querySelectorAll('[role="option"]'), ].find((entry) => entry.textContent === "Quiet")!; option.focus(); option.dispatchEvent( new context.root.ownerDocument.defaultView!.MouseEvent("pointerdown", { bubbles: true, button: 0, }), ); option.dispatchEvent( new context.root.ownerDocument.defaultView!.MouseEvent("mouseup", { bubbles: true, button: 0, }), ); option.click(); await until(() => context.root.textContent!.includes("No agents yet")); assert.equal(context.root.querySelectorAll(".review-card").length, 0); context.fixture.expireSession(); await context.app.refresh(); assert.match(context.root.textContent!, /Couldn't refresh\. Last updated/); assert.ok(context.root.querySelector("#reviews"), "the last loaded project stays visible"); } finally { context.cleanup(); } }); test("all API/config text is escaped, including review IDs, paths and error messages", () => { const state = stateView(fixtureState()); state.packets[0].title = ''; state.reviews[0].id = '" onclick="alert(2)'; const html = renderDashboard({ brand, projects: [], project: "demo", fixture: true, state, activity: [], busy: false, error: "", }); const dom = new JSDOM(html); assert.equal(dom.window.document.querySelector("img,script"), null); assert.equal(dom.window.document.querySelector("[onclick]"), null); assert.match(dom.window.document.body.textContent!, / { const state = stateView(fixtureState()); const review = state.reviews[0]; assert.equal(reviewGate(review, state).approve, true); state.forks[0].head = "0".repeat(40); assert.equal(reviewGate(review, state).approve, false); state.forks[0].head = review.sha; review.receiptDigest = null; assert.equal(reviewGate(review, state).approve, false); review.receiptDigest = DIGEST; state.blockers[0].repoName = review.repoName; state.blockers[0].pushingPacket = review.packet; assert.equal(reviewGate(review, state).approve, false); assert.throws(() => assertState({ ...state, reviews: undefined } as never), /contract/); }); test("HTTP transport encodes IDs, uses activity cursor, refuses login HTML and redirects", async () => { const urls: string[] = []; const transport = createHttpTransport({ origin, fetch: async (url, options) => { assert.equal(options?.redirect, "error"); assert.equal(options?.cache, "no-store"); urls.push(String(url)); return Response.json({ activity: [], nextAfter: 7 }); }, }); await transport.getActivity("a/b", 7); assert.equal(urls[0], `${origin}/projects/a%2Fb/activity?after=7&limit=100`); const htmlTransport = createHttpTransport({ origin, fetch: async () => new Response("login", { headers: { "Content-Type": "text/html" } }), }); await assert.rejects( htmlTransport.getState("demo"), (error: unknown) => error instanceof ApiError && error.status === 502, ); }); test("optional WebSocket invalidation is same-origin, receives messages and closes on dispose", () => { const events = new Map void>(); let closed = false; let updates = 0; const socket = { addEventListener(type: string, handler: () => void) { events.set(type, handler); }, removeEventListener(type: string) { events.delete(type); }, close() { closed = true; }, } as unknown as WebSocket; const transport = createHttpTransport({ origin, socketUrl: (id) => `/events/${id}`, openSocket(url) { assert.equal(url, "wss://workspace.idiot.tax/events/demo"); return socket; }, }); const dispose = transport.subscribe!("demo", () => updates++); events.get("message")!(); assert.equal(updates, 1); dispose(); assert.equal(closed, true); assert.equal(events.size, 0); const foreign = createHttpTransport({ origin, socketUrl: () => "wss://foreign.idiot.tax/events", openSocket() { assert.fail("must reject before opening socket"); }, }); assert.throws(() => foreign.subscribe!("demo", () => {}), /origin/); }); test("wire normalization preserves live fork bindings and refuses a changed approval digest", () => { const wire = fixtureState(); wire.reviews[0].approval = { sha: wire.reviews[0].sha, receiptDigest: "e".repeat(64) }; wire.promotionPending = { id: "pending-1", review: "review-1", repoName: "demo", ref: "refs/heads/main", before: wire.reviews[0].base, after: wire.reviews[0].sha, receiptDigest: DIGEST, recordedAt: "2026-10-12T00:20:00.000Z", }; const view = stateView(wire); assert.equal(view.claims[0].repoName, "demo--builder-1"); assert.equal(view.claims[0].active, true); assert.equal(reviewGate(view.reviews[0], view).promote, false); assert.equal(view.reviews[0].status, "stale"); assert.equal(view.canonicalHead, undefined); assert.deepEqual(view.promotionPending, wire.promotionPending); assert.equal(view.reconcilerAlerts.length, 0); }); test("activity request follows the recent snapshot cursor instead of showing the first page", async () => { const context = setup(); try { await until(() => Boolean(context.root.querySelector("#reviews"))); const activity = context.fixture.calls.find((call) => call.url.pathname.endsWith("/activity"))!; assert.equal(activity.url.searchParams.get("after"), "3"); assert.equal(context.root.querySelectorAll(".activity").length, 3); } finally { context.cleanup(); } }); test("top tabs select one visible view and preserve focus across refreshes", async () => { const context = setup(); try { await until(() => Boolean(context.root.querySelector("#reviews"))); const tabs = [ getByRole(context.root, "tab", { name: "Overview" }), getByRole(context.root, "tab", { name: /^Work\s*,\s+\d+$/ }), getByRole(context.root, "tab", { name: /^Review\s*,\s+\d+$/ }), getByRole(context.root, "tab", { name: "Agents" }), getByRole(context.root, "tab", { name: /^Races(\s*,\s+\d+)?$/ }), ]; assert.equal(context.root.querySelectorAll('[role="tab"]').length, tabs.length); tabs[1].focus(); tabs[1].click(); assert.equal(context.root.querySelector("#view-work")!.hidden, false); assert.equal(context.root.querySelector("#view-overview")!.hidden, true); await context.app.refresh(); assert.equal( context.dom.window.document.activeElement, tabs[1], "refresh preserves the same focused Kumo tab", ); } finally { context.cleanup(); } }); test("promotion confirmation stays bound to the displayed head when a fresh snapshot moves the fork", async () => { const context = setup(); try { await until(() => Boolean(context.root.querySelector("#reviews"))); button(context.root, "approve").click(); await until(() => !!button(context.root, "promote")); button(context.root, "promote").click(); await until( () => !!context.dom.window.document.querySelector('[data-action="confirm-promote"]'), ); context.fixture.state.forks[0].head = "f".repeat(40); await context.app.refresh(); const confirm = context.dom.window.document.querySelector( '[data-action="confirm-promote"]', )!; assert.equal(confirm.disabled, true); confirm.click(); assert.equal( context.fixture.calls.filter((entry) => entry.url.pathname.endsWith("/promote")).length, 0, ); } finally { context.cleanup(); } }); test("tabs preserve fragment selection when the sandbox rejects History API URLs", async () => { const context = setup(); try { await until(() => Boolean(context.root.querySelector("#reviews"))); const window = context.root.ownerDocument.defaultView!; window.history.replaceState = () => { throw new window.DOMException("Opaque origin", "SecurityError"); }; context.root.querySelectorAll('[role="tab"]')[1].click(); assert.equal(window.location.hash, "#work"); await context.app.refresh(); assert.equal(context.root.querySelector("#view-work")!.hidden, false); } finally { context.cleanup(); } }); test("packet states follow blocker, promotion, approval, stale review and claim precedence", async () => { const { packetStatus } = await import("../src/view.tsx"); const state = stateView(fixtureState()); const id = state.packets[0].id; const review = state.reviews[0]; assert.equal(packetStatus(id, state).label, "In review"); review.status = "stale"; review.stale = true; assert.equal(packetStatus(id, state).label, "Out of date"); review.stale = false; review.status = "approved"; assert.equal(packetStatus(id, state).label, "Approved"); review.status = "promoted"; assert.equal(packetStatus(id, state).label, "Promoted"); state.blockers[0].pushingPacket = id; assert.equal(packetStatus(id, state).label, "Blocked"); state.blockers = []; state.reviews = []; assert.equal(packetStatus(id, state).label, "In progress"); state.claims = []; assert.equal(packetStatus(id, state).label, "Ready"); }); test("optional events and real claim times are preserved; absent metadata is omitted", () => { const wire = fixtureState(); wire.project.events = "active"; wire.forks[0].events = "pending"; const state = stateView(wire); assert.equal(state.project.events, "active"); assert.equal(state.forks[0].events, "pending"); assert.equal(state.claims[0].createdAt, wire.claims[0].createdAt); assert.equal(state.blockers[0].claimingAgent, wire.blockers[0].claimingAgent); delete state.project.events; for (const fork of state.forks) delete fork.events; const html = renderDashboard({ brand, project: "demo", projects: [], fixture: false, state, activity: [], busy: false, }); assert.doesNotMatch( html, /Events pending|Unavailable|Details pending|Return to agent|Load older|Exact identifiers/, ); }); test("path summaries use true totals, first-push and deletion copy; details limit and expand only supplied paths", async () => { const { createElement } = await import("react"); const { renderToStaticMarkup } = await import("react-dom/server"); const { createRoot } = await import("react-dom/client"); const { flushSync } = await import("react-dom"); const { Paths, PathDetails } = await import("../src/view.tsx"); for (const [scenario, text] of [ ["large", "+449 more"], ["first", "First push · 450 files"], ["partial", "+449 more"], ["deleted", "Deleted"], ]) { const event = fixtureState(scenario).activity[0]; const html = renderToStaticMarkup(createElement(Paths, { paths: event.changedPaths, event })); assert.match(html, new RegExp(text.replace(/[+·]/g, "\\$&"))); } const dom = new JSDOM('
'); installDom(dom.window as unknown as Window & typeof globalThis); const root = dom.window.document.getElementById("dashboard")!; const react = createRoot(root); try { const event = fixtureState("partial").activity[0]; flushSync(() => react.render( createElement(PathDetails, { paths: event.changedPaths, total: event.changedPathCount, partial: true, }), ), ); assert.equal(root.querySelectorAll("code").length, 20); assert.match(root.textContent!, /Show all 30 paths/); assert.match(root.textContent!, /Showing 30 of 450 paths/); root.querySelector("button")!.click(); await until(() => root.querySelectorAll("code").length === 30); assert.equal(root.querySelector("button"), null); } finally { flushSync(() => react.unmount()); dom.window.close(); } }); test("promotion confirmation refuses a changed base even when the fork head and digest stay equal", async () => { const context = setup(); try { await until(() => !!context.root.querySelector("#reviews")); button(context.root, "approve").click(); await until(() => !!button(context.root, "promote")); button(context.root, "promote").click(); await until( () => !!context.dom.window.document.querySelector('[data-action="confirm-promote"]'), ); context.fixture.state.reviews[0].base = "9".repeat(40); await context.app.refresh(); assert.equal( context.dom.window.document.querySelector( '[data-action="confirm-promote"]', )!.disabled, true, ); assert.equal( context.fixture.calls.filter((call) => call.url.pathname.endsWith("/promote")).length, 0, ); } finally { context.cleanup(); } }); test("superseded stale reviews leave the queue; unsuperseded stale rows do not count", async () => { const { reviewQueue } = await import("../src/model.ts"); const state = stateView(fixtureState("superseded")); assert.equal(reviewQueue(state).length, 0); state.reviews[0].status = "pending"; assert.deepEqual( reviewQueue(state).map((r) => r.id), ["review-1"], ); state.reviews[0].requestedAt = state.reviews[1].requestedAt; assert.equal(reviewQueue(state).length, 2); const html = renderDashboard({ brand, project: "demo", projects: [], fixture: true, state, activity: [], busy: false, }); assert.match(html, /Fork moved/); assert.match(html, /In review/); }); test("panel defaults use first frame width and remember only a session toggle", async () => { const dom = new JSDOM('
', { url: origin }); installDom(dom.window as unknown as Window & typeof globalThis); const window = dom.window as unknown as Window & typeof globalThis; const root = window.document.getElementById("dashboard")!; function mount(width: number) { window.innerWidth = width; return mountDashboard(root, { brand, transport: createHttpTransport({ origin, fetch: apiFixture().fetcher }), initialProject: "demo", }); } try { for (const width of [1340, 1340, 1100, 1100]) { const app = mount(width); assert.equal( root.querySelector("[data-action=toggle-activity]")!.getAttribute("aria-expanded"), String(width >= 1280), ); app.dispose(); } const app = mount(1340); root.querySelector("[data-action=toggle-activity]")!.click(); await until( () => root.querySelector("[data-action=toggle-activity]")!.getAttribute("aria-expanded") === "false", ); app.dispose(); const reopened = mount(1340); assert.equal( root.querySelector("[data-action=toggle-activity]")!.getAttribute("aria-expanded"), "false", ); reopened.dispose(); } finally { window.close(); } }); test("live pause preserves the snapshot and tabs; Reconnect clears the warning", async () => { const dom = new JSDOM('
'); installDom(dom.window as unknown as Window & typeof globalThis); const root = dom.window.document.getElementById("dashboard")!; const transport = createHttpTransport({ origin, fetch: apiFixture().fetcher }); let notify!: (paused: boolean) => void; let stopped = false; transport.live = { subscribe(listener) { notify = listener; return () => { stopped = true; }; }, async reconnect() { notify(false); }, }; const app = mountDashboard(root, { brand, transport, initialProject: "demo" }); try { await until(() => !!root.querySelector("#reviews")); notify(true); assert.match( root.querySelector(".live-paused")!.textContent!, /Live updates paused. Last updated/, ); root.querySelectorAll("[role=tab]")[3].click(); await until(() => !root.querySelector("#view-agents")!.hidden); assert.ok(root.querySelector("#lanes table")); root.querySelector(".live-paused button")!.click(); await until(() => !root.querySelector(".live-paused")); } finally { app.dispose(); dom.window.close(); } assert.equal(stopped, true); });