Fork Horse repository, listed by its owner. Publisher snapshot.
packages/forkhorse-dashboard/test/judge.test.ts
Snapshot at 34e3175
1import { installDom } from "./dom.ts";
2import assert from "node:assert/strict";
3import { test } from "node:test";
4import { createRequire } from "node:module";
5import { createElement } from "react";
6import { renderToStaticMarkup } from "react-dom/server";
7import { TooltipProvider } from "@cloudflare/kumo";
8import { judgeFixtureState, judgeFixtureDetail } from "./judge-fixtures.ts";
9import { JudgeAdvice, ReviewAdvice } from "../src/judge.tsx";
10import { judgePromotion, pushedAfterAdvice } from "../src/judge.ts";
11import { stateView, promotionAvailable } from "../src/model.ts";
12import { ownerNeeds, copyText } from "../src/needs-you-model.ts";
13import { mountDashboard } from "../src/dashboard.ts";
14import { createHttpTransport } from "../src/transport.ts";
15import type { ViewActions } from "../src/view.tsx";
16const { JSDOM } = createRequire(import.meta.url)("jsdom");
17const brand = { name: "Fixture product", tagline: "Fixture" };
18const actions: ViewActions = { owner: true, section: "work", async action() {}, openReview() {}, setSection() {}, async selectProject() {} };
19function render(scenario: string, owner = true) {
20 const state = stateView(judgeFixtureState(scenario));
21 return renderToStaticMarkup(createElement(TooltipProvider, null, createElement(JudgeAdvice, {
22 race: state.races![0], view: { state, activity: state.recentActivity!, project: scenario, brand, projects: [], fixture: true, busy: false },
23 actions: { ...actions, owner },
24 })));
25}
26function renderedText(html: string) {
27 const dom = new JSDOM(html);
28 try { return dom.window.document.body.textContent ?? ""; }
29 finally { dom.window.close(); }
30}
31async function until(check: () => boolean) {
32 for (let i = 0; i < 200; i++) { if (check()) return; await new Promise((resolve) => setTimeout(resolve, 5)); }
33 assert.fail("Judge fixture DOM did not settle");
34}
35function setup(scenario = "judge-open", owner = true, failRecheck = false) {
36 const dom = new JSDOM('<div id="app"></div>', { url: "https://fixture.idiot.tax/#race/race-01" });
37 installDom(dom.window);
38 const root = dom.window.document.getElementById("app") as HTMLElement;
39 const wire = judgeFixtureState(scenario);
40 const calls: string[] = [];
41 const transport = createHttpTransport({ origin: dom.window.location.origin, fetch: async (input, init) => {
42 const path = new URL(String(input)).pathname; calls.push(`${init?.method ?? "GET"} ${path}`);
43 if (path.endsWith("/state")) return Response.json(wire);
44 if (path.endsWith("/activity")) return Response.json({ activity: [], nextAfter: 0 });
45 if (init?.method === "POST" && path.endsWith("/promote")) {
46 if (failRecheck) {
47 const review = wire.reviews.find((r) => r.agent === "cedar")!;
48 review.receipt!.status = "failed"; review.receipt!.trigger = "before-promote";
49 return Response.json({ error: "Checks failed before promote." }, { status: 409 });
50 }
51 return Response.json({ sha: wire.reviews[0].sha });
52 }
53 const id = path.split("/").at(-1)!;
54 return wire.reviews.some((r) => r.id === id) ? Response.json(judgeFixtureDetail(wire, id)) : Response.json({}, { status: 404 });
55 }});
56 const app = mountDashboard(root, { brand, transport, fixture: true, initialProject: scenario, owner });
57 return { root, wire, calls, app, dom, close() { app.dispose(); dom.window.close(); } };
58}
59
60test("judge seam is optional, advice stays plain text, forecast is decorative and judge gets no cloth", () => {
61 assert.equal(render("judge-nojudge"), "");
62 assert.match(renderedText(render("judge-waiting")), /cfos-1 hasn't sent advice yet\./);
63 const html = render("judge-open");
64 assert.match(html, /From cfos-1/);
65 assert.match(html, /Pick/); assert.match(html, /2nd choice/); assert.match(html, /3rd choice/);
66 assert.match(renderedText(html), /Judge's ranking: cedar, then birch, then amber\./);
67 const c = new JSDOM(html).window.document;
68 assert.equal(c.querySelector(".judge-forecast")!.getAttribute("aria-hidden"), "true");
69 assert.equal(c.querySelector(".judge-from .race-saddlecloth"), null);
70 assert.equal(c.querySelectorAll("ol li .race-saddlecloth").length, 3);
71 assert.match(render("judge-long"), /<strong>/);
72 assert.equal(new JSDOM(render("judge-long")).window.document.querySelector("blockquote strong"), null);
73 assert.doesNotMatch(html, /decided|decision|verdict|ruling|ruled|chose the winner|judged|award|\bbet\b|odds|stake|prize|payout|bounty|wager/);
74});
75
76test("pick promotion shares queue eligibility and suppresses stale, closed, budget and pending cases", () => {
77 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"]) {
78 const state = stateView(judgeFixtureState(scenario));
79 const choice = judgePromotion(state, state.races![0]);
80 assert.equal(choice.available, scenario === "judge-open", scenario);
81 if (choice.available) assert.equal(promotionAvailable(choice.review!, state), true);
82 else assert.ok(choice.reason, scenario);
83 }
84 const state = stateView(judgeFixtureState("judge-pick-push"));
85 const choice = judgePromotion(state, state.races![0]);
86 assert.equal(choice.reason, "cedar pushed after this advice.");
87 assert.equal(promotionAvailable(choice.review!, state), true, "ordinary queue still promotes independently of advice age");
88 assert.equal(promotionAvailable(choice.review!, state, false), false);
89 const wire = judgeFixtureState();
90 const before = JSON.stringify({ ledger: wire.ledger, activity: wire.activity });
91 stateView(wire); render("judge-open");
92 assert.equal(JSON.stringify({ ledger: wire.ledger, activity: wire.activity }), before);
93});
94
95test("ranking updates and newer push evidence are shared with review captions and Needs you", () => {
96 const state = stateView(judgeFixtureState("judge-entrant-push"));
97 const race = state.races![0];
98 assert.equal(pushedAfterAdvice(state, race, race.entrants.find((e) => e.agent === "birch")!), true);
99 assert.match(render("judge-entrant-push"), /birch pushed after this advice/);
100 const pick = state.reviews.find((r) => r.agent === "cedar")!;
101 const reviewLine = () => renderToStaticMarkup(createElement(ReviewAdvice, { state, review: pick }));
102 assert.match(renderedText(reviewLine()), /Judge's advice: pick/);
103 const suffix = () => ownerNeeds(state, brand).find((need) => need.id === `promote:${pick.id}`)!;
104 assert.match(copyText(suffix().outcome), /First promote wins the race\. The judge picked this entrant\./);
105 race.verdict!.ranking = ["birch", "cedar", "amber"];
106 assert.match(reviewLine(), /2nd choice of 3/);
107 assert.doesNotMatch(copyText(suffix().outcome), /The judge picked/);
108});
109
110test("finished advice persists with pick/other result, no action, and truthful empty history", () => {
111 assert.match(renderedText(render("judge-finish-pick")), /Winner: cedar \(the judge's pick\)\./);
112 assert.match(render("judge-finish-other"), /Winner: cedar\. The judge picked birch\./);
113 assert.match(render("judge-finish-pick"), /before the finish/);
114 assert.match(renderedText(render("judge-finish-none")), /didn't send advice before the finish/);
115 assert.doesNotMatch(render("judge-finish-pick"), /promote-judge-pick/);
116 assert.doesNotMatch(render("judge-open", false), /promote-judge-pick/);
117 assert.match(render("judge-open", false), /This is advice\. The owner promotes\./);
118});
119
120test("pick opens the existing dialog and only its normal primary sends a promote request", async () => {
121 const c = setup();
122 try {
123 await until(() => !!c.root.querySelector('[data-action="promote-judge-pick"]'));
124 const block = c.root.querySelector(".judge-advice")!;
125 assert.ok(block.closest(".race-usage"));
126 assert.equal(c.root.querySelectorAll(".judge-pick").length, 1);
127 c.root.querySelector<HTMLButtonElement>('[data-action="promote-judge-pick"]')!.click();
128 await until(() => !!c.dom.window.document.querySelector('[data-action="confirm-promote"]'));
129 const dialog = c.dom.window.document.querySelector(".promotion-dialog")!;
130 assert.match(dialog.textContent!, /cfos-1 picked cedar\. This is advice; promoting is your call\./);
131 assert.match(dialog.textContent!, /Promoting this finishes the race\. amber and birch will be closed and placed\./);
132 assert.equal(c.calls.filter((call) => call.startsWith("POST")).length, 0);
133 (c.dom.window.document.querySelector('[data-action="confirm-promote"]') as HTMLButtonElement).click();
134 await until(() => c.calls.some((call) => call.endsWith("/reviews/race-review-cedar/promote")));
135 assert.equal(c.calls.filter((call) => call.startsWith("POST")).length, 1);
136 assert.equal(c.calls.some((call) => /verdict/.test(call)), false);
137 } finally { c.close(); }
138});
139
140test("agents cannot invoke pick promotion; long rationale expands and replacement advice is shown", async () => {
141 const c = setup("judge-long", false);
142 try {
143 await until(() => !!c.root.querySelector(".judge-clamped"));
144 assert.equal(c.root.querySelector('[data-action="promote-judge-pick"]'), null);
145 const show = [...c.root.querySelectorAll<HTMLButtonElement>("button")].find((b) => b.textContent === "Show all")!;
146 assert.equal(show.getAttribute("aria-expanded"), "false");
147 show.click();
148 await until(() => !c.root.querySelector(".judge-clamped"));
149 assert.equal(show.textContent, "Show less");
150 c.wire.races![0].verdict!.ranking = ["birch", "cedar", "amber"];
151 c.wire.races![0].verdict!.at = "2026-10-13T14:16:00Z";
152 await c.app.refresh();
153 await until(() => c.root.querySelector(".judge-ranking li strong")!.textContent === "birch");
154 assert.match(c.root.querySelector('.race-track [data-race-agent="birch"]')!.textContent!, /Judge's pick/);
155 assert.equal(c.root.querySelector('[data-action="promote-judge-pick"]'), null);
156 assert.equal(c.calls.filter((call) => call.startsWith("POST")).length, 0);
157 } finally { c.close(); }
158});
159
160test("a newer review arriving with a pick dialog open cannot promote unseen work", async () => {
161 const c = setup();
162 try {
163 await until(() => !!c.root.querySelector('[data-action="promote-judge-pick"]'));
164 c.root.querySelector<HTMLButtonElement>('[data-action="promote-judge-pick"]')!.click();
165 await until(() => !!c.dom.window.document.querySelector('[data-action="confirm-promote"]'));
166 c.wire.reviews.find((r) => r.agent === "cedar")!.createdAt = "2026-10-13T14:15:00Z";
167 await c.app.refresh();
168 await until(() => !c.root.querySelector('[data-action="promote-judge-pick"]'));
169 const confirm = c.dom.window.document.querySelector('[data-action="confirm-promote"]') as HTMLButtonElement;
170 assert.equal(confirm.disabled, true);
171 assert.match(c.dom.window.document.querySelector('.promotion-dialog').textContent, /Review changed\. Close and review again\./);
172 confirm.click();
173 assert.equal(c.calls.filter((call) => call.startsWith("POST")).length, 0);
174 } finally { c.close(); }
175});
176
177test("the pick shortcut preserves the normal required re-check failure dialog without retrying", async () => {
178 const c = setup("judge-open", true, true);
179 try {
180 await until(() => !!c.root.querySelector('[data-action="promote-judge-pick"]'));
181 c.root.querySelector<HTMLButtonElement>('[data-action="promote-judge-pick"]')!.click();
182 await until(() => !!c.dom.window.document.querySelector('[data-action="confirm-promote"]'));
183 (c.dom.window.document.querySelector('[data-action="confirm-promote"]') as HTMLButtonElement).click();
184 await until(() => c.dom.window.document.querySelector('.promotion-dialog')?.textContent.includes("Checks failed on the re-run."));
185 assert.equal(c.root.querySelector('[data-action="promote-judge-pick"]'), null);
186 const dialog = c.dom.window.document.querySelector('.promotion-dialog') as HTMLElement;
187 assert.match(dialog.textContent!, /Nothing was promoted\./);
188 assert.match(dialog.textContent!, /View receipt/);
189 assert.equal((dialog.querySelector('[data-action="confirm-promote"]') as HTMLButtonElement).disabled, true);
190 assert.equal(c.calls.filter((call) => call.startsWith("POST")).length, 1);
191 [...dialog.querySelectorAll<HTMLButtonElement>("button")].find((button) => button.textContent === "View receipt")!.click();
192 await until(() => c.root.querySelector(".receipt-details")?.textContent?.includes("Failed") === true);
193 assert.equal(c.dom.window.location.hash, "#work");
194 assert.equal(c.calls.some((call) => call.endsWith("/receipt")), false, "receipt summary does not request logs");
195 assert.equal(c.calls.filter((call) => call.startsWith("POST")).length, 1);
196 } finally { c.close(); }
197});
198
199// Regression for the rehearsal's doubled picked-entrant caption.
200test("picked entrant's newer push caption appears once while promotion stays blocked", () => {
201 for (const scenario of ["judge-pick-push", "judge-pick-review"]) {
202 for (const owner of [true, false]) {
203 const text = renderedText(render(scenario, owner));
204 assert.equal(text.split("cedar pushed after this advice").length - 1, 1);
205 assert.doesNotMatch(render(scenario, owner), /data-action="promote-judge-pick"/);
206 }
207 }
208 assert.match(renderedText(render("judge-unapproved")), /cedar's review isn't approved yet/);
209});
210