import { ProjectId, type ThreadPullRequestLink, type VcsStatusResult } from "@t3tools/contracts"; import { describe, expect, it } from "vite-plus/test"; import { presentThreadLinkedPullRequests, presentThreadPr, resolveThreadPrSource, } from "./thread-pr-presentation"; const pullRequest: NonNullable = { number: 3774, title: "Desktop-style pull request indicator", url: "https://github.com/t3tools/t3code/pull/3774", baseRef: "main", headRef: "codex/desktop-style-pr-indicator", state: "merged", }; describe("presentThreadPr", () => { it("uses the compact pull request number label without a hash prefix", () => { expect(presentThreadPr(pullRequest, undefined)).toMatchObject({ label: "3774", accessibilityLabel: "#3774 pull request merged", textClassName: "text-adaptive-violet-600-400", }); }); it("uses merge-request terminology for GitLab", () => { expect( presentThreadPr(pullRequest, { kind: "gitlab", name: "GitLab", baseUrl: "https://gitlab.com", }), ).toMatchObject({ label: "3774", accessibilityLabel: "#3774 merge request merged", }); }); it("uses gray for draft pull requests", () => { expect( presentThreadPr({ ...pullRequest, state: "open", isDraft: true }, undefined), ).toMatchObject({ accessibilityLabel: "#3774 pull request draft", textClassName: "text-foreground-muted", }); }); }); function linkedPr( number: number, overrides: Partial = {}, ): ThreadPullRequestLink { return { host: "github.com", repository: "t3tools/t3code", number, url: `https://github.com/t3tools/t3code/pull/${number}`, source: "manual", linkedAt: "2026-09-08T00:00:00.000Z", stack: null, snapshot: { state: "open", title: `Change ${number}`, headBranch: `change-${number}`, baseBranch: "main", isDraft: false, updatedAt: null, syncedAt: "2026-09-08T00:00:00.000Z", }, ...overrides, }; } describe("presentThreadLinkedPullRequests", () => { it("renders unsynced links with neutral pending status", () => { expect(presentThreadLinkedPullRequests([linkedPr(1, { snapshot: null })])).toMatchObject({ number: 1, label: "1", state: null, textClassName: "text-foreground-muted", accessibilityLabel: "#1 pull request status pending", }); }); it("counts unrelated links without labelling them a stack", () => { expect(presentThreadLinkedPullRequests([linkedPr(1), linkedPr(2)])).toMatchObject({ kind: "pull-request", label: "+2", others: 1, textClassName: "text-foreground-muted", }); }); it("uses the top of a derived stack even when its bottom was linked later", () => { const bottom = linkedPr(1, { linkedAt: "2026-09-09T00:00:00.000Z" }); const top = linkedPr(2); expect( presentThreadLinkedPullRequests([ bottom, { ...top, snapshot: { ...top.snapshot!, baseBranch: "change-1" }, }, ]), ).toMatchObject({ kind: "stack", label: "2", number: 2, url: top.url }); }); it("hides dismissed stack members", () => { expect( presentThreadLinkedPullRequests([linkedPr(1, { source: "stack-dismissed" })]), ).toBeNull(); }); it("retains merged state from the persisted snapshot", () => { const link = linkedPr(1); expect( presentThreadLinkedPullRequests([ { ...link, snapshot: { ...link.snapshot!, state: "merged" } }, ]), ).toMatchObject({ state: "merged", textClassName: "text-adaptive-violet-600-400" }); }); }); describe("resolveThreadPrSource compatibility", () => { const legacyRef = { projectId: ProjectId.make("project"), repository: "t3tools/t3code", number: 1, url: "https://github.com/t3tools/t3code/pull/1", }; const branchRef = { ...legacyRef, number: 2, url: "https://github.com/t3tools/t3code/pull/2" }; it("polls the legacy reference when only the older linking capability exists", () => { expect( resolveThreadPrSource( { pullRequests: [], linkedPullRequest: legacyRef, branchPullRequest: branchRef }, { threadPullRequestLinking: true }, ), ).toEqual({ linkedPresentation: null, pullRequestRef: legacyRef }); }); it("keeps the legacy reference for a server without either capability", () => { expect(resolveThreadPrSource({ pullRequests: [], linkedPullRequest: legacyRef }, {})).toEqual({ linkedPresentation: null, pullRequestRef: legacyRef, }); }); it("ignores stale snapshots after reconnecting to a server without array support", () => { expect( resolveThreadPrSource( { pullRequests: [linkedPr(3)], linkedPullRequest: legacyRef }, { threadPullRequestLinking: true, threadPullRequests: false }, ), ).toEqual({ linkedPresentation: null, pullRequestRef: legacyRef }); }); it("uses snapshots without polling when both capabilities are advertised", () => { expect( resolveThreadPrSource( { pullRequests: [linkedPr(3)], linkedPullRequest: legacyRef, branchPullRequest: branchRef }, { threadPullRequestLinking: true, threadPullRequests: true }, ), ).toMatchObject({ linkedPresentation: { number: 3 }, pullRequestRef: null }); }); it("does not revive a removed modern link from the compatibility reference", () => { expect( resolveThreadPrSource( { pullRequests: [], linkedPullRequest: legacyRef, branchPullRequest: branchRef }, { threadPullRequests: true }, ), ).toEqual({ linkedPresentation: null, pullRequestRef: branchRef }); }); it("does not poll when a modern link is waiting for its first snapshot", () => { expect( resolveThreadPrSource( { pullRequests: [linkedPr(3, { snapshot: null })], linkedPullRequest: legacyRef, branchPullRequest: branchRef, }, { threadPullRequests: true }, ), ).toMatchObject({ linkedPresentation: { number: 3, state: null }, pullRequestRef: null }); }); });