import { installDom } from "./dom.ts"; import assert from "node:assert/strict"; import { test } from "node:test"; import { createRequire } from "node:module"; import { createElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; import { TooltipProvider } from "@cloudflare/kumo"; import { judgeFixtureState, judgeFixtureDetail } from "./judge-fixtures.ts"; import { JudgeAdvice, ReviewAdvice } from "../src/judge.tsx"; import { judgePromotion, pushedAfterAdvice } from "../src/judge.ts"; import { stateView, promotionAvailable } from "../src/model.ts"; import { ownerNeeds, copyText } from "../src/needs-you-model.ts"; import { mountDashboard } from "../src/dashboard.ts"; import { createHttpTransport } from "../src/transport.ts"; import type { ViewActions } from "../src/view.tsx"; const { JSDOM } = createRequire(import.meta.url)("jsdom"); const brand = { name: "Fixture product", tagline: "Fixture" }; const actions: ViewActions = { owner: true, section: "work", async action() {}, openReview() {}, setSection() {}, async selectProject() {} }; function render(scenario: string, owner = true) { const state = stateView(judgeFixtureState(scenario)); return renderToStaticMarkup(createElement(TooltipProvider, null, createElement(JudgeAdvice, { race: state.races![0], view: { state, activity: state.recentActivity!, project: scenario, brand, projects: [], fixture: true, busy: false }, actions: { ...actions, owner }, }))); } function renderedText(html: string) { const dom = new JSDOM(html); try { return dom.window.document.body.textContent ?? ""; } finally { dom.window.close(); } } async function until(check: () => boolean) { for (let i = 0; i < 200; i++) { if (check()) return; await new Promise((resolve) => setTimeout(resolve, 5)); } assert.fail("Judge fixture DOM did not settle"); } function setup(scenario = "judge-open", owner = true, failRecheck = false) { const dom = new JSDOM('
', { url: "https://fixture.idiot.tax/#race/race-01" }); installDom(dom.window); const root = dom.window.document.getElementById("app") as HTMLElement; const wire = judgeFixtureState(scenario); 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(wire); if (path.endsWith("/activity")) return Response.json({ activity: [], nextAfter: 0 }); if (init?.method === "POST" && path.endsWith("/promote")) { if (failRecheck) { const review = wire.reviews.find((r) => r.agent === "cedar")!; review.receipt!.status = "failed"; review.receipt!.trigger = "before-promote"; return Response.json({ error: "Checks failed before promote." }, { status: 409 }); } return Response.json({ sha: wire.reviews[0].sha }); } const id = path.split("/").at(-1)!; return wire.reviews.some((r) => r.id === id) ? Response.json(judgeFixtureDetail(wire, id)) : Response.json({}, { status: 404 }); }}); const app = mountDashboard(root, { brand, transport, fixture: true, initialProject: scenario, owner }); return { root, wire, calls, app, dom, close() { app.dispose(); dom.window.close(); } }; } test("judge seam is optional, advice stays plain text, forecast is decorative and judge gets no cloth", () => { assert.equal(render("judge-nojudge"), ""); assert.match(renderedText(render("judge-waiting")), /cfos-1 hasn't sent advice yet\./); const html = render("judge-open"); assert.match(html, /From cfos-1/); assert.match(html, /Pick/); assert.match(html, /2nd choice/); assert.match(html, /3rd choice/); assert.match(renderedText(html), /Judge's ranking: cedar, then birch, then amber\./); const c = new JSDOM(html).window.document; assert.equal(c.querySelector(".judge-forecast")!.getAttribute("aria-hidden"), "true"); assert.equal(c.querySelector(".judge-from .race-saddlecloth"), null); assert.equal(c.querySelectorAll("ol li .race-saddlecloth").length, 3); assert.match(render("judge-long"), /<strong>/); assert.equal(new JSDOM(render("judge-long")).window.document.querySelector("blockquote strong"), null); assert.doesNotMatch(html, /decided|decision|verdict|ruling|ruled|chose the winner|judged|award|\bbet\b|odds|stake|prize|payout|bounty|wager/); }); test("pick promotion shares queue eligibility and suppresses stale, closed, budget and pending cases", () => { for (const scenario of ["judge-open", "judge-unapproved", "judge-pick-push", "judge-pick-review", "judge-over", "judge-unchecked", "judge-main-moved", "judge-pending", "judge-recheck", "judge-closed"]) { const state = stateView(judgeFixtureState(scenario)); const choice = judgePromotion(state, state.races![0]); assert.equal(choice.available, scenario === "judge-open", scenario); if (choice.available) assert.equal(promotionAvailable(choice.review!, state), true); else assert.ok(choice.reason, scenario); } const state = stateView(judgeFixtureState("judge-pick-push")); const choice = judgePromotion(state, state.races![0]); assert.equal(choice.reason, "cedar pushed after this advice."); assert.equal(promotionAvailable(choice.review!, state), true, "ordinary queue still promotes independently of advice age"); assert.equal(promotionAvailable(choice.review!, state, false), false); const wire = judgeFixtureState(); const before = JSON.stringify({ ledger: wire.ledger, activity: wire.activity }); stateView(wire); render("judge-open"); assert.equal(JSON.stringify({ ledger: wire.ledger, activity: wire.activity }), before); }); test("ranking updates and newer push evidence are shared with review captions and Needs you", () => { const state = stateView(judgeFixtureState("judge-entrant-push")); const race = state.races![0]; assert.equal(pushedAfterAdvice(state, race, race.entrants.find((e) => e.agent === "birch")!), true); assert.match(render("judge-entrant-push"), /birch pushed after this advice/); const pick = state.reviews.find((r) => r.agent === "cedar")!; const reviewLine = () => renderToStaticMarkup(createElement(ReviewAdvice, { state, review: pick })); assert.match(renderedText(reviewLine()), /Judge's advice: pick/); const suffix = () => ownerNeeds(state, brand).find((need) => need.id === `promote:${pick.id}`)!; assert.match(copyText(suffix().outcome), /First promote wins the race\. The judge picked this entrant\./); race.verdict!.ranking = ["birch", "cedar", "amber"]; assert.match(reviewLine(), /2nd choice of 3/); assert.doesNotMatch(copyText(suffix().outcome), /The judge picked/); }); test("finished advice persists with pick/other result, no action, and truthful empty history", () => { assert.match(renderedText(render("judge-finish-pick")), /Winner: cedar \(the judge's pick\)\./); assert.match(render("judge-finish-other"), /Winner: cedar\. The judge picked birch\./); assert.match(render("judge-finish-pick"), /before the finish/); assert.match(renderedText(render("judge-finish-none")), /didn't send advice before the finish/); assert.doesNotMatch(render("judge-finish-pick"), /promote-judge-pick/); assert.doesNotMatch(render("judge-open", false), /promote-judge-pick/); assert.match(render("judge-open", false), /This is advice\. The owner promotes\./); }); test("pick opens the existing dialog and only its normal primary sends a promote request", async () => { const c = setup(); try { await until(() => !!c.root.querySelector('[data-action="promote-judge-pick"]')); const block = c.root.querySelector(".judge-advice")!; assert.ok(block.closest(".race-usage")); assert.equal(c.root.querySelectorAll(".judge-pick").length, 1); c.root.querySelector