Public: anyone can read this; the crew can change it.
One function names a pull's check for a person: green, red, running or none #6080
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/6071-one-function-names-a-pull-s-check-for-a → main 2 commits fork 2 threads · 2 unresolved
4 files
changed +64
−5
src/apps/check_word.ts added +25
| @@ -0,0 +1,25 @@ | ||
| 1 | /** | |
| 2 | * A pull's check, named for a person (#6071): green, red, running or none. | |
| 3 | * Only a run of this very head counts; a cancelled run says nothing. | |
| 4 | */ | |
| 5 | import type { RunState } from "../core/runs.ts"; | |
| 6 | ||
| 7 | export type CheckWord = "green" | "red" | "running" | "none"; | |
| 8 | ||
| 9 | export function checkWord( | |
| 10 | run: { state: RunState; commitOid: string } | undefined, | |
| 11 | headOid: string, | |
| 12 | ): CheckWord { | |
| 13 | if (run === undefined || run.commitOid !== headOid) return "none"; | |
| 14 | switch (run.state) { | |
| 15 | case "ok": | |
| 16 | return "green"; | |
| 17 | case "failed": | |
| 18 | return "red"; | |
| 19 | case "queued": | |
| 20 | case "running": | |
| 21 | return "running"; | |
| 22 | default: | |
| 23 | return "none"; | |
| 24 | } | |
| 25 | } |
src/web/studio/review_page.ts +11 −3
| @@ -12,6 +12,7 @@ import { appsForRepos } from "../../core/apps.ts"; | ||
| 12 | 12 | import { lookUrl, looksForApps } from "../../core/looks.ts"; |
| 13 | 13 | import { latestRunsForIssues, type Run } from "../../core/runs.ts"; |
| 14 | 14 | import type { Ctx } from "../../http/context.ts"; |
| 15 | import { checkWord } from "../../apps/check_word.ts"; | |
| 15 | 16 | import { repoBase, signInFirst } from "../chrome.ts"; |
| 16 | 17 | import { html, type Html } from "../html.ts"; |
| 17 | 18 | import { page } from "../layout.ts"; |
| @@ -22,9 +23,16 @@ import { diffStatFor, sizeLine } from "./review_evidence.ts"; | ||
| 22 | 23 | |
| 23 | 24 | /** The check on a pull's head, as the pull page wears it; only a run of this very head counts. */ |
| 24 | 25 | function verdict(run: Run | undefined, headOid: string): Html { |
| 25 | return run === undefined || run.commitOid !== headOid | |
| 26 | ? badge("No check", { variant: "outline" }) | |
| 27 | : runStateBadge(run.state, "check"); | |
| 26 | switch (checkWord(run, headOid)) { | |
Next: the pull page and the terminal still pick a run without checking its head. Route them through checkWord so the door parity holds. |
||
| 27 | case "green": | |
| 28 | return runStateBadge("ok", "check"); | |
| 29 | case "red": | |
| 30 | return runStateBadge("failed", "check"); | |
| 31 | case "running": | |
| 32 | return runStateBadge("running", "check"); | |
| 33 | case "none": | |
| 34 | return badge("No check", { variant: "outline" }); | |
| 35 | } | |
| 28 | 36 | } |
| 29 | 37 | |
| 30 | 38 | /** |
test/check-word.test.ts added +20
| @@ -0,0 +1,20 @@ | ||
| 1 | import { expect, test } from "bun:test"; | |
| 2 | import { checkWord } from "../src/apps/check_word.ts"; | |
| 3 | ||
| 4 | const run = (state: "queued" | "running" | "ok" | "failed" | "cancelled", commitOid = "h1") => ({ | |
| 5 | state, | |
| 6 | commitOid, | |
| 7 | }); | |
| 8 | ||
| 9 | test("an ok run of the head is green", () => expect(checkWord(run("ok"), "h1")).toBe("green")); | |
| 10 | test("a failed run of the head is red", () => expect(checkWord(run("failed"), "h1")).toBe("red")); | |
| 11 | test("queued and running are running", () => { | |
| 12 | expect(checkWord(run("queued"), "h1")).toBe("running"); | |
| 13 | expect(checkWord(run("running"), "h1")).toBe("running"); | |
| 14 | }); | |
| 15 | test("a cancelled run is none", () => expect(checkWord(run("cancelled"), "h1")).toBe("none")); | |
| 16 | test("no run is none", () => expect(checkWord(undefined, "h1")).toBe("none")); | |
| 17 | test("a run of an older head is none, whatever its state", () => { | |
| 18 | for (const s of ["queued", "running", "ok", "failed", "cancelled"] as const) | |
| 19 | expect(checkWord(run(s, "old"), "h1")).toBe("none"); | |
| 20 | }); |
test/review-page.test.ts +8 −2
| @@ -93,6 +93,10 @@ beforeAll(async () => { | ||
| 93 | 93 | .query(`INSERT INTO run (id, repo_id, kind, issue_id, commit_oid, state) VALUES (?1, ?2, 'check', ?3, ?4, ?5)`) |
| 94 | 94 | .run(id(), repo.id, fresh, ZERO, state); |
| 95 | 95 | } |
| 96 | const older = stale; | |
| 97 | app.db | |
| 98 | .query(`INSERT INTO run (id, repo_id, kind, issue_id, commit_oid, state) VALUES (?1, ?2, 'check', ?3, ?4, 'running')`) | |
| 99 | .run(id(), repo.id, older, "1".repeat(40)); | |
| 96 | 100 | const made2 = createAppRow(app.db, { repoId: repo.id, slug: "wait", access: "public" }); |
| 97 | 101 | if (!made2.ok) throw new Error(String(made2.error)); |
| 98 | 102 | const record = made2.value; |
| @@ -178,8 +182,10 @@ test("a pull's row shows its check verdict; one with no check says none", async | ||
| 178 | 182 | expect(find("Stale pull")).toContain("No check"); |
| 179 | 183 | expect(find("Fresh ok pull")).toContain("Passed"); |
| 180 | 184 | expect(find("Fresh failed pull")).toContain("Failed"); |
| 181 | expect(find("Fresh cancelled pull")).toContain("Cancelled"); | |
| 182 | expect(find("Fresh cancelled pull")).not.toContain("No check"); | |
| 185 | expect(find("Fresh cancelled pull")).toContain("No check"); | |
| 186 | expect(find("Fresh cancelled pull")).not.toContain("Cancelled"); | |
| 187 | expect(find("Stale pull")).toContain("No check"); | |
| 188 | expect(find("Stale pull")).not.toContain("Running"); | |
| 183 | 189 | }); |
| 184 | 190 | |
| 185 | 191 | test("a pull of an app links its preview beside the row, not inside it; an issue row has neither", async () => { |
new above
offline
Exported but never imported anywhere in src/. Wire it into review_page.ts
verdictand add a test through that caller.