| 1 | import type { MergebotPullRequestLiveStatus } from "@t3tools/contracts"; |
| 2 | import { describe, expect, it } from "vite-plus/test"; |
| 3 | import { mergebotStatusPill } from "./mergebotStatusPill.ts"; |
| 4 | |
| 5 | const eta = "2026-10-15T12:00:00Z"; |
| 6 | const 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 | }; |
| 24 | const merged = (deploy: MergebotPullRequestLiveStatus["deploy"]) => ({ |
| 25 | ...open, |
| 26 | lifecycle: "merged" as const, |
| 27 | deploy, |
| 28 | }); |
| 29 | const queued = { ...open, autoMerge: true, queueStatus: "queued" as const, mergeEtaAt: eta }; |
| 30 | |
| 31 | describe("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 | }); |