Fork Horse repository, listed by its owner. Publisher snapshot.
packages/forkhorse-dashboard/test/fork-graph.test.ts
Snapshot at 34e3175
1import { installDom } from "./dom.ts";
2import { createRequire } from "node:module";
3import assert from "node:assert/strict";
4import { test } from "node:test";
5import { createRoot } from "react-dom/client";
6import { createElement, act } from "react";
7import { TooltipProvider } from "@cloudflare/kumo";
8import {
9 forkGraph,
10 foldMarks,
11 collisionLink,
12 mainShaLabels,
13 clockTime,
14 tableEvents,
15} from "../src/fork-graph-model.ts";
16import { agentIdentities, saddlecloth } from "../src/lane.tsx";
17import { ForkGraph } from "../src/fork-graph.tsx";
18import { stateView } from "../src/model.ts";
19import { forkFixtureState } from "./fork-graph-fixtures.ts";
20const now = Date.parse("2026-10-13T16:00:00Z");
21const fixture = (id = "fork-hero") => stateView(forkFixtureState(id, now));
22test("agent numbers follow enrollment, survive response reordering and repeat silks", () => {
23 const agents = [
24 { name: "legacy-z", createdAt: null },
25 { name: "new-b", createdAt: "2026-10-13T11:00:00Z" },
26 { name: "legacy-a" },
27 { name: "new-a", createdAt: "2026-10-13T11:00:00Z" },
28 ];
29 const identities = agentIdentities(agents);
30 assert.deepEqual([...identities.keys()], ["legacy-a", "legacy-z", "new-a", "new-b"]);
31 assert.deepEqual([...agentIdentities(agents.toReversed())], [...identities]);
32 assert.equal(identities.get("new-b")!.silk, identities.get("legacy-a")!.silk);
33 assert.equal(saddlecloth({ ...fixture(), agents }, "missing"), undefined);
34});
35test("ledger times prove merge ends; missing claims use open start without invented claim or approval", () => {
36 const state = fixture();
37 delete state.reviews.find((r) => r.id === "fixture-review-0")!.approvedAt;
38 const graph = forkGraph(state, now);
39 const merged = graph.lanes.find((l) => l.promoted)!;
40 assert.equal(merged.claimedAt, undefined);
41 assert.equal(merged.start, Date.parse(state.recentActivity![0].recordedAt));
42 assert.equal(merged.end, Date.parse(state.ledger[0].promotedAt));
43 assert.equal(
44 merged.events.some((e) => e.kind === "claim"),
45 false,
46 );
47 assert.equal(
48 merged.events.some((e) => e.kind === "approved"),
49 false,
50 );
51 assert.deepEqual(
52 merged.events.filter((e) => e.kind === "push").map((e) => e.at),
53 state
54 .recentActivity!.filter((a) => a.repoName === merged.repoName)
55 .map((a) => Date.parse(a.recordedAt)),
56 );
57});
58test("race branches only exist with races, remain in claim order and share project identity", () => {
59 const state = fixture();
60 const graph = forkGraph(state, now);
61 const entrants = graph.lanes.filter((l) => l.race);
62 assert.equal(entrants.length, 2);
63 assert.ok(entrants[0].claimedAt! < entrants[1].claimedAt!);
64 assert.equal(
65 entrants[0].identity!.number,
66 graph.lanes.find((l) => !l.race && l.agent === entrants[0].agent)!.identity!.number,
67 );
68 delete state.races;
69 assert.equal(forkGraph(state, now).races, 0);
70});
71test("window is bounded, old lanes retain true claim time and ended lanes expire", () => {
72 const state = fixture("fork-old");
73 const graph = forkGraph(state, now, "four");
74 assert.ok(graph.lanes.some((l) => l.claimedAt! < graph.start));
75 state.claims[0].createdAt = "2020-01-01T00:00:00Z";
76 assert.ok(now - forkGraph(state, now).start <= 24 * 3600000);
77 state.ledger[0].promotedAt = new Date(now - 25 * 3600000).toISOString();
78 assert.equal(
79 forkGraph(state, now, "day").lanes.some((l) => l.promoted),
80 false,
81 );
82});
83test("folding at measured width preserves every event and prioritizes checks over approval/review/push", () => {
84 const lane = forkGraph(fixture("fork-dense"), now, "four").lanes.find(
85 (l) => l.packet === "fixture-02",
86 )!;
87 const folded = foldMarks(lane.events, now - 4 * 3600000, now, 450);
88 const cluster = folded.find((g) => g.events.length >= 3)!;
89 assert.ok(cluster);
90 assert.equal(cluster.event.kind, "passed");
91 assert.ok(cluster.events.some((e) => e.kind === "push"));
92 assert.ok(cluster.events.some((e) => e.kind === "approved"));
93 assert.equal(folded.flatMap((g) => g.events).length, lane.events.length);
94});
95test("main SHA labels favor the newest mark and pack older labels only in remaining space", () => {
96 const marks = [
97 { id: "older", at: 1, x: 110 },
98 { id: "middle", at: 2, x: 185 },
99 { id: "newest", at: 3, x: 195 },
100 ];
101 assert.deepEqual([...mainShaLabels(marks, 300).keys()], ["newest", "older"]);
102 assert.equal(mainShaLabels([{ id: "edge", at: 4, x: 290 }], 300).get("edge"), 210);
103 assert.deepEqual([...mainShaLabels(marks.toReversed(), 300)], [...mainShaLabels(marks, 300)]);
104 assert.equal(mainShaLabels(marks, 90).size, 0);
105});
106test("table cells cap all event kinds to their latest three and retain earlier facts", () => {
107 const events = forkGraph(fixture("fork-history"), now).lanes.find(
108 (l) => l.packet === "fixture-02",
109 )!.events;
110 for (const kinds of [["push"], ["review"], ["passed", "failed"], ["approved"]]) {
111 const matching = events.filter((event) => kinds.includes(event.kind));
112 const { shown, earlier } = tableEvents(events.toReversed(), kinds);
113 assert.equal(shown.length, 3);
114 assert.ok(earlier.length > 0);
115 assert.deepEqual([...earlier, ...shown], matching);
116 }
117});
118test("collision rings sit beside the pushing lane and links break around unrelated lanes", () => {
119 assert.deepEqual(collisionLink(80, 208, [80, 144, 208]), {
120 ringY: 112,
121 segments: [
122 { top: 80, height: 56 },
123 { top: 152, height: 56 },
124 ],
125 });
126 assert.deepEqual(collisionLink(208, 80, [80, 144, 208]), {
127 ringY: 176,
128 segments: [
129 { top: 80, height: 56 },
130 { top: 152, height: 56 },
131 ],
132 });
133 assert.deepEqual(collisionLink(80, 144, [80, 144]), {
134 ringY: 112,
135 segments: [{ top: 80, height: 64 }],
136 });
137 const grouped = collisionLink(80, 238, [80, 174, 238]);
138 assert.equal(grouped.ringY, 112);
139 assert.ok(grouped.segments.every((s) => s.top + s.height <= 166 || s.top >= 182));
140});
141test("near-start hour grid stays while its clipped centered label is omitted", async () => {
142 const { JSDOM } = createRequire(import.meta.url)("jsdom");
143 const dom = new JSDOM("<div id='root'></div>", { url: "https://fixture.idiot.tax" });
144 installDom(dom.window);
145 Object.defineProperty(globalThis, "IS_REACT_ACT_ENVIRONMENT", {
146 value: true,
147 configurable: true,
148 });
149 const originalBounds = dom.window.HTMLElement.prototype.getBoundingClientRect;
150 // Fixture geometry only; the browser capture verifies actual text bounds.
151 dom.window.HTMLElement.prototype.getBoundingClientRect = function () {
152 const bounds = originalBounds.call(this);
153 return this.classList.contains("fork-plot") ? { ...bounds, width: 640 } : bounds;
154 };
155 const root = createRoot(document.getElementById("root")!);
156 await act(async () =>
157 root.render(
158 createElement(
159 TooltipProvider,
160 null,
161 createElement(ForkGraph, {
162 state: fixture("fork-hour-edge"),
163 owner: false,
164 openReview: () => {},
165 openPacket: () => {},
166 openReceipt: () => {},
167 now,
168 }),
169 ),
170 ),
171 );
172 const grid = [...document.querySelectorAll(".fork-grid")];
173 const labels = [...document.querySelectorAll<HTMLElement>(".fork-tick")];
174 assert.equal(grid.length, 4);
175 assert.ok(parseFloat(grid[0].getAttribute("x1")!) < 4, "Near-start grid line is retained");
176 assert.equal(labels.length, 3);
177 assert.ok(!labels.some((label) => label.textContent === clockTime(now - 4 * 3600000)));
178 assert.ok(
179 labels.every((label) => {
180 const center = (parseFloat(label.style.left) * 640) / 100;
181 return center >= 24 && center <= 640 - 24;
182 }),
183 );
184 await act(async () => root.unmount());
185});
186test("lane state labels and dots distinguish failed checks, warnings and review", async () => {
187 const state = fixture("fork-states");
188 const expected = [
189 ["Promoted to main", "success"],
190 ["Approved", "success"],
191 ["Checks running", "info"],
192 ["Checks failed", "danger"],
193 ["Timed out", "warning"],
194 ["Couldn't run", "warning"],
195 ["In review", "info"],
196 ["Racing", "brand"],
197 ];
198 const states = forkGraph(state, now).lanes.map((lane) => lane.state);
199 for (const [word] of expected) assert.ok(states.includes(word), word);
200 const { JSDOM } = createRequire(import.meta.url)("jsdom");
201 const dom = new JSDOM("<div id='root'></div>", { url: "https://fixture.idiot.tax" });
202 installDom(dom.window);
203 Object.defineProperty(globalThis, "IS_REACT_ACT_ENVIRONMENT", {
204 value: true,
205 configurable: true,
206 });
207 const root = createRoot(document.getElementById("root")!);
208 await act(async () =>
209 root.render(
210 createElement(
211 TooltipProvider,
212 null,
213 createElement(ForkGraph, {
214 state,
215 owner: false,
216 openReview: () => {},
217 openPacket: () => {},
218 openReceipt: () => {},
219 now,
220 }),
221 ),
222 ),
223 );
224 for (const [word, tone] of expected) {
225 const labels = [...document.querySelectorAll(".fork-state")].filter(
226 (label) => label.textContent!.trim() === `● ${word}`,
227 );
228 assert.ok(labels.length, word);
229 assert.ok(
230 labels.every((label) => label.classList.contains(`fork-state-${tone}`)),
231 `${word}: ${tone}`,
232 );
233 }
234 const interruptions = [...document.querySelectorAll(".lane-marker-interrupted")];
235 assert.equal(interruptions.length, 2);
236 assert.ok(interruptions.every((marker) => marker.textContent === "!"));
237 assert.equal(document.querySelector(".lane-marker-failed")!.textContent, "✗");
238 assert.match(document.querySelector(".fork-legend")!.textContent!, /! checks didn't finish/);
239 assert.match(document.querySelector(".fork-table")!.textContent!, /timed out/);
240 assert.match(document.querySelector(".fork-table")!.textContent!, /couldn't run/);
241 await act(async () => root.unmount());
242});
243test("a claimed lane without a review shows an In progress brand dot", async () => {
244 const state = fixture("fork-in-progress");
245 const lane = forkGraph(state, now).lanes.find((entry) => entry.packet === "fixture-02")!;
246 assert.equal(lane.state, "In progress");
247 assert.equal(lane.review, undefined);
248 const { JSDOM } = createRequire(import.meta.url)("jsdom");
249 const dom = new JSDOM("<div id='root'></div>", { url: "https://fixture.idiot.tax" });
250 installDom(dom.window);
251 Object.defineProperty(globalThis, "IS_REACT_ACT_ENVIRONMENT", {
252 value: true,
253 configurable: true,
254 });
255 const root = createRoot(document.getElementById("root")!);
256 await act(async () =>
257 root.render(
258 createElement(
259 TooltipProvider,
260 null,
261 createElement(ForkGraph, {
262 state,
263 owner: false,
264 openReview: () => {},
265 openPacket: () => {},
266 openReceipt: () => {},
267 now,
268 }),
269 ),
270 ),
271 );
272 const label = [...document.querySelectorAll(".fork-state")].find(
273 (entry) => entry.textContent!.trim() === "● In progress",
274 )!;
275 assert.ok(label);
276 assert.ok(label.classList.contains("fork-state-brand"));
277 const racing = [...document.querySelectorAll(".fork-state")].filter(
278 (entry) => entry.textContent!.trim() === "● Racing",
279 );
280 assert.ok(racing.length);
281 assert.ok(racing.every((entry) => entry.classList.contains("fork-state-brand")));
282 await act(async () => root.unmount());
283});
284test("fixture DOM leaves closed table in accessibility tree, supports navigation and storage denial", async () => {
285 const { JSDOM } = createRequire(import.meta.url)("jsdom");
286 const dom = new JSDOM("<div id='root'></div>", { url: "https://fixture.idiot.tax" });
287 installDom(dom.window);
288 Object.defineProperty(globalThis, "IS_REACT_ACT_ENVIRONMENT", {
289 value: true,
290 configurable: true,
291 });
292 Object.defineProperty(globalThis, "localStorage", {
293 get() {
294 throw Error("Denied");
295 },
296 configurable: true,
297 });
298 const opened: string[] = [];
299 const root = createRoot(document.getElementById("root")!);
300 await act(async () =>
301 root.render(
302 createElement(
303 TooltipProvider,
304 null,
305 createElement(ForkGraph, {
306 state: fixture(),
307 owner: false,
308 openReview: () => {},
309 openPacket: (id) => opened.push(id),
310 openReceipt: () => {},
311 now,
312 }),
313 ),
314 ),
315 );
316 const unknownStart = document.querySelector<HTMLButtonElement>("button.fork-start")!;
317 assert.equal(unknownStart.textContent, "Fork start");
318 assert.equal(document.querySelectorAll(".fork-lead-in").length, 1);
319 assert.equal(unknownStart.getAttribute("aria-label"), "Started before the loaded history");
320 for (const [word, tone] of [
321 ["Approved", "success"],
322 ["Checks running", "info"],
323 ["Racing", "brand"],
324 ]) {
325 const labels = [...document.querySelectorAll(".fork-state")].filter((el) =>
326 el.textContent!.includes(word),
327 );
328 assert.ok(labels.length);
329 assert.ok(labels.every((el) => el.classList.contains(`fork-state-${tone}`)));
330 }
331 const chips = [...document.querySelectorAll<HTMLButtonElement>(".fork-window-chip")];
332 assert.deepEqual(
333 chips.filter((b) => b.getAttribute("aria-pressed") === "true").map((b) => b.textContent),
334 ["Recent"],
335 );
336 await act(async () => chips.find((b) => b.textContent === "Last 4 h")!.click());
337 assert.deepEqual(
338 chips.filter((b) => b.getAttribute("aria-pressed") === "true").map((b) => b.textContent),
339 ["Last 4 h"],
340 );
341 const group = document.querySelector('[role="group"].fork-picture')!;
342 const table = document.getElementById(group.getAttribute("aria-describedby")!)!;
343 assert.match(
344 group.getAttribute("aria-label")!,
345 /Fork graph: 4 live claims · 1 race · 1 promotion today/,
346 );
347 assert.ok(table.querySelector("table"));
348 assert.equal(table.getAttribute("hidden"), null);
349 assert.equal(table.getAttribute("aria-hidden"), null);
350 const disclosure = document.querySelector<HTMLButtonElement>(".fork-disclosure")!;
351 assert.equal(getComputedStyle(table).position, "absolute");
352 assert.equal(getComputedStyle(table).width, "1px");
353 assert.equal(getComputedStyle(table).clipPath, "inset(50%)");
354 assert.ok([...table.querySelectorAll("button")].every((button) => button.tabIndex === -1));
355 assert.equal(disclosure.getAttribute("aria-expanded"), "false");
356 await act(async () => disclosure.click());
357 assert.equal(disclosure.getAttribute("aria-expanded"), "true");
358 assert.equal(table.style.clipPath, "");
359 assert.ok([...table.querySelectorAll("button")].every((button) => button.tabIndex === 0));
360 await act(async () =>
361 [...document.querySelectorAll<HTMLButtonElement>(".fork-lane-label")]
362 .find((b) => b.textContent!.includes("fixture-01"))!
363 .click(),
364 );
365 assert.equal(opened[0], "fixture-01");
366 assert.match(table.textContent!, /checks failed/);
367 assert.match(table.textContent!, /checks passed/);
368 const runningRow = [...table.querySelectorAll("tbody tr")].find((row) =>
369 row.textContent!.includes("fixture-03"),
370 )!;
371 assert.equal(
372 runningRow.querySelectorAll("td")[4].querySelector(".fork-cell-visible")!.textContent!.trim(),
373 "Running",
374 );
375 const checkedRow = [...table.querySelectorAll("tbody tr")].find((row) =>
376 row.textContent!.includes("fixture-02"),
377 )!;
378 assert.match(
379 checkedRow.querySelectorAll("td")[4].querySelector(".fork-cell-visible")!.textContent!,
380 /failed.*passed/,
381 );
382 await act(async () =>
383 root.render(
384 createElement(
385 TooltipProvider,
386 null,
387 createElement(ForkGraph, {
388 state: fixture("fork-old"),
389 owner: false,
390 openReview: () => {},
391 openPacket: () => {},
392 openReceipt: () => {},
393 now,
394 }),
395 ),
396 ),
397 );
398 const oldHistory = [...document.querySelectorAll(".fork-history")].filter((el) =>
399 el.textContent!.includes("since"),
400 );
401 assert.ok(oldHistory.length);
402 for (const history of oldHistory) {
403 assert.match(history.querySelector(".fork-stub")!.textContent!, /^since \d{2}:\d{2}$/);
404 assert.equal(
405 history.querySelector(".fork-stub")!.nextElementSibling!.textContent,
406 "Earlier pushes not shown",
407 );
408 }
409 await act(async () =>
410 root.render(
411 createElement(
412 TooltipProvider,
413 null,
414 createElement(ForkGraph, {
415 state: fixture("fork-history"),
416 owner: false,
417 openReview: () => {},
418 openPacket: () => {},
419 openReceipt: () => {},
420 now,
421 }),
422 ),
423 ),
424 );
425 const capped = [...document.querySelectorAll(".fork-earlier-events")];
426 assert.ok(capped.length >= 4);
427 for (const hint of capped) {
428 assert.match(hint.textContent!, /\+\d+ earlier/);
429 const visible = hint.parentElement!.querySelector(".fork-cell-visible")!.textContent!;
430 assert.equal((visible.match(/\d{2}:\d{2}/g) ?? []).length, 3);
431 assert.doesNotMatch(visible, /push|review requested|approved|checks/);
432 assert.match(hint.getAttribute("aria-label")!, /earlier events:/);
433 assert.ok(hint.parentElement!.querySelector(".sr-only")!.textContent);
434 assert.equal((hint as HTMLElement).tabIndex, 0);
435 }
436 // The no-layout DOM shim cannot measure SHA-label space; real-bundle captures check geometry.
437 await act(async () => root.unmount());
438 dom.window.close();
439});
440
441test("a reclaimed fork does not inherit its earlier promotion or reviews", () => {
442 const state = fixture();
443 state.claims.push({
444 agent: "fixture-alpha",
445 packet: "fixture-01",
446 repoName: "fixture-fork-1",
447 active: true,
448 paths: [],
449 createdAt: new Date(now - 10 * 60000).toISOString(),
450 });
451 const graph = forkGraph(state, now);
452 const same = graph.lanes.filter((l) => l.packet === "fixture-01");
453 assert.equal(same.length, 2);
454 assert.equal(same.filter((l) => !l.end).length, 1);
455 const current = same.find((l) => !l.end)!;
456 assert.equal(current.review, undefined);
457 assert.equal(current.events.filter((e) => e.kind !== "claim").length, 0);
458});
459test("race promotion remains recorded without a loaded ledger and never invents a merge SHA", () => {
460 const state = fixture();
461 const entrant = state.races![0].entrants[0];
462 entrant.promotedAt = new Date(now - 5 * 60000).toISOString();
463 const lane = forkGraph(state, now).lanes.find((l) => l.race && l.agent === entrant.agent)!;
464 assert.equal(lane.end, Date.parse(entrant.promotedAt));
465 assert.equal(lane.promoted, undefined);
466 assert.ok(lane.events.some((e) => e.kind === "promoted"));
467 assert.equal(lane.state, "Promoted to main");
468});
469