Fork Horse repository, listed by its owner. Publisher snapshot.

packages/forkhorse-dashboard/test/fork-graph.test.ts

Snapshot at 34e3175

Containing directory

18,513 bytes · typescriptView raw
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