import type { EnvironmentId, ProjectId, PullRequestCheck } from "@t3tools/contracts"; import { Children, isValidElement, type ReactNode } from "react"; import { describe, expect, it } from "vite-plus/test"; import { PullRequestChecksPopover } from "./PullRequestChecksPopover"; import type { EnvironmentPullRequestEntry } from "./pullRequestList.logic"; import { PullRequestRow } from "./PullRequestRow"; import { pullRequestChecksState, pullRequestCheckStatusLabel, summarizePullRequestChecks, } from "./pullRequestPresentation"; function check( status: PullRequestCheck["status"], overrides: Partial = {}, ): PullRequestCheck { return { name: `check-${status}`, status, description: null, url: null, ...overrides }; } describe("pullRequestChecksState", () => { it("lets a failure outrank a run still going, and reports nothing without checks", () => { expect(pullRequestChecksState([check("success"), check("pending"), check("failure")])).toBe( "failing", ); expect(pullRequestChecksState([check("success"), check("cancelled")])).toBe("failing"); expect(pullRequestChecksState([check("success"), check("pending")])).toBe("pending"); expect(pullRequestChecksState([check("success"), check("action-required")])).toBe("pending"); expect(pullRequestChecksState([check("success")])).toBe("passing"); // Skipped and neutral are neither a pass nor a failure, so they are no verdict at all. expect(pullRequestChecksState([check("skipped"), check("neutral")])).toBe(null); expect(pullRequestChecksState([])).toBe(null); }); it("names workflow approval instead of claiming every check passed", () => { const workflow = check("action-required", { url: "https://github.com/acme/web/actions/runs/42/job/7", }); const manualGate = check("action-required", { url: "https://example.com/manual-gate" }); expect(pullRequestCheckStatusLabel(workflow)).toBe("Awaiting approval"); expect(pullRequestCheckStatusLabel(manualGate)).toBe("Awaiting action"); expect(summarizePullRequestChecks([check("success"), workflow])).toBe( "1 workflow awaiting approval", ); expect(summarizePullRequestChecks([check("failure"), workflow])).toBe("1 of 2 failing"); expect(summarizePullRequestChecks([check("success"), manualGate])).toBe( "1 check awaiting action", ); expect(summarizePullRequestChecks([workflow, manualGate])).toBe( "1 workflow and 1 check awaiting action", ); }); }); /** Every element of the tree the row returned, so a nested indicator can be looked for. */ function flatten(node: ReactNode): ReadonlyArray> { const found: unknown[] = []; for (const child of Children.toArray(node)) { if (!isValidElement(child)) continue; found.push(child); found.push(...flatten((child.props as { readonly children?: ReactNode }).children)); } return found as ReadonlyArray>; } function entry(overrides: Partial): EnvironmentPullRequestEntry { return { environmentId: "env-1" as EnvironmentId, projectId: "project-1" as ProjectId, provider: "github", repository: "pingdotgg/t3code", number: 1, title: "Add the pull requests page", url: "https://github.com/pingdotgg/t3code/pull/1", author: null, headBranch: "feat/page", baseBranch: "main", state: "open", isDraft: false, mergeability: "mergeable", additions: 1, deletions: 0, createdAt: "2026-07-01T00:00:00Z", updatedAt: "2026-07-02T00:00:00Z", viewerReviewRequested: false, labels: [], ...overrides, } as EnvironmentPullRequestEntry; } function row(overrides: Partial): ReactNode { return PullRequestRow.type({ entry: entry(overrides), selected: false, showProjectTitle: false, showProvider: false, onSelect: () => {}, }); } describe("PullRequestRow checks indicator", () => { function indicators(node: ReactNode): number { return flatten(node).filter( (element) => (element as { type?: unknown }).type === PullRequestChecksPopover, ).length; } it("shows the indicator only for a row the host reported a rollup for", () => { expect(indicators(row({ checksState: "failing" }))).toBe(1); expect(indicators(row({}))).toBe(0); }); });