1import type { MergebotPullRequestLiveStatus } from "@t3tools/contracts";
2import { describe, expect, it } from "vite-plus/test";
3import { mergebotStatusPill } from "./mergebotStatusPill.ts";
4
5const eta = "2026-10-15T12:00:00Z";
6const open: MergebotPullRequestLiveStatus = {
7 number: 12,
8 url: "https://mergebot.example/work/app/pull/12",
9 title: "Ship change",
10 body: null,
11 headSha: "head",
12 headBranch: "feature",
13 lifecycle: "open",
14 draft: false,
15 autoMerge: false,
16 ciStatus: "passed",
17 checks: { completed: 14, total: 14, failed: 0 },
18 hasConflicts: false,
19 queueStatus: "not_queued",
20 position: null,
21 mergeEtaAt: null,
22 deploy: null,
23};
24const merged = (deploy: MergebotPullRequestLiveStatus["deploy"]) => ({
25 ...open,
26 lifecycle: "merged" as const,
27 deploy,
28});
29const queued = { ...open, autoMerge: true, queueStatus: "queued" as const, mergeEtaAt: eta };
30
31describe("mergebotStatusPill", () => {
32 it.each<[string, MergebotPullRequestLiveStatus, ReturnType<typeof mergebotStatusPill>]>([
33 [
34 "running CI with counts and a queue ETA",
35 {
36 ...queued,
37 ciStatus: "pending",
38 queueStatus: "waiting_ci",
39 position: 3,
40 checks: { completed: 3, total: 14, failed: 0 },
41 },
42 { kind: "running-ci", label: "Running CI (3/14)", tone: "yellow", etaAt: eta },
43 ],
44 [
45 "running CI before checks load",
46 { ...open, ciStatus: "pending", checks: null },
47 { kind: "running-ci", label: "Running CI", tone: "yellow", etaAt: null },
48 ],
49 [
50 "rebasing",
51 { ...queued, queueStatus: "updating_branch", position: 1 },
52 { kind: "rebasing", label: "Rebasing", tone: "yellow", etaAt: eta },
53 ],
54 [
55 "CI failed",
56 { ...open, ciStatus: "failed", checks: { completed: 14, total: 14, failed: 2 } },
57 { kind: "ci-failed", label: "CI Failed", tone: "red", etaAt: null },
58 ],
59 [
60 "CI failed in the queue",
61 { ...queued, queueStatus: "ci_failed", position: 2 },
62 { kind: "ci-failed", label: "CI Failed", tone: "red", etaAt: null },
63 ],
64 [
65 "merge conflicts outrank failing CI",
66 { ...open, ciStatus: "failed", hasConflicts: true },
67 { kind: "merge-conflicts", label: "Merge Conflicts", tone: "orange", etaAt: null },
68 ],
69 [
70 "draft passing CI",
71 { ...open, draft: true },
72 { kind: "draft-passes-ci", label: "Draft Passes CI", tone: "blue", etaAt: null },
73 ],
74 ["ready to merge", open, { kind: "ready", label: "Ready to merge", tone: "blue", etaAt: null }],
75 [
76 "unknown conflicts still read as ready",
77 { ...open, hasConflicts: null },
78 { kind: "ready", label: "Ready to merge", tone: "blue", etaAt: null },
79 ],
80 [
81 "queued behind another PR",
82 { ...queued, position: 2 },
83 { kind: "queued", label: "Queued #2", tone: "blue", etaAt: eta },
84 ],
85 [
86 "first in the queue with passing CI",
87 { ...queued, position: 1 },
88 { kind: "merging", label: "Merging", tone: "green", etaAt: eta },
89 ],
90 [
91 "merging",
92 { ...queued, queueStatus: "merging", position: 1 },
93 { kind: "merging", label: "Merging", tone: "green", etaAt: eta },
94 ],
95 [
96 "merged, waiting for the next deploy",
97 merged({ state: "waiting", environment: "staging", etaAt: null }),
98 { kind: "merged", label: "Merged", tone: "green", etaAt: null },
99 ],
100 [
101 "merged without deploy data",
102 merged(null),
103 { kind: "merged", label: "Merged", tone: "green", etaAt: null },
104 ],
105 [
106 "deploying",
107 merged({ state: "deploying", environment: "staging", etaAt: eta }),
108 { kind: "deploying", label: "Deploying", tone: "green", etaAt: eta },
109 ],
110 [
111 "deploy failed",
112 merged({ state: "failed", environment: "production", etaAt: null }),
113 { kind: "deploy-failed", label: "Deploy Failed", tone: "red", etaAt: null },
114 ],
115 [
116 "on staging",
117 merged({ state: "staging", environment: "staging", etaAt: null }),
118 { kind: "on-staging", label: "On Staging", tone: "green", etaAt: null },
119 ],
120 [
121 "shipped to production",
122 merged({ state: "production", environment: "production", etaAt: null }),
123 { kind: "shipped", label: "Shipped to Prod", tone: "muted", etaAt: null },
124 ],
125 ["closed", { ...open, lifecycle: "closed" }, null],
126 ["unknown CI outside the queue", { ...open, ciStatus: null }, null],
127 ])("%s", (_, status, expected) => {
128 expect(mergebotStatusPill(status)).toEqual(expected);
129 });
130});