1import { ThreadId, type ThreadPullRequestLink } from "@t3tools/contracts";
2import { renderToStaticMarkup } from "react-dom/server";
3import { describe, expect, it } from "vite-plus/test";
4
5import { ThreadWorktreeIndicator, linkedPullRequestSnapshotStatus } from "./ThreadStatusIndicators";
6
7describe("ThreadWorktreeIndicator", () => {
8 it("renders the worktree folder and branch in an accessible label", () => {
9 const markup = renderToStaticMarkup(
10 <ThreadWorktreeIndicator
11 thread={{
12 id: ThreadId.make("thread-1"),
13 branch: "feature/sidebar-indicator",
14 worktreePath: "/tmp/worktrees/sidebar-indicator",
15 }}
16 />,
17 );
18
19 expect(markup).toContain('role="img"');
20 expect(markup).toContain(
21 'aria-label="Worktree: sidebar-indicator (feature/sidebar-indicator)"',
22 );
23 expect(markup).toContain('data-testid="thread-worktree-thread-1"');
24 });
25
26 it.each([null, "", " "])("renders nothing for an absent worktree path", (worktreePath) => {
27 const markup = renderToStaticMarkup(
28 <ThreadWorktreeIndicator
29 thread={{
30 id: ThreadId.make("thread-1"),
31 branch: "main",
32 worktreePath,
33 }}
34 />,
35 );
36
37 expect(markup).toBe("");
38 });
39});
40
41describe("linked pull request snapshots", () => {
42 const link: ThreadPullRequestLink = {
43 host: "gitlab.example.com",
44 repository: "acme/web",
45 number: 42,
46 url: "https://gitlab.example.com/acme/web/-/merge_requests/42",
47 source: "manual",
48 linkedAt: "2026-01-01T00:00:00Z",
49 stack: null,
50 snapshot: null,
51 };
52 it("keeps unsynced links unknown", () => {
53 expect(linkedPullRequestSnapshotStatus(link)).toBeNull();
54 });
55 it("uses the snapshot state and branches with the linked identity", () => {
56 const result = linkedPullRequestSnapshotStatus({
57 ...link,
58 snapshot: {
59 state: "merged",
60 title: "Change",
61 headBranch: "feature",
62 baseBranch: "main",
63 isDraft: false,
64 updatedAt: "2026-01-02T00:00:00Z",
65 syncedAt: "2026-01-03T00:00:00Z",
66 },
67 });
68 expect(result).toEqual({
69 pr: {
70 number: 42,
71 url: link.url,
72 title: "Change",
73 state: "merged",
74 isDraft: false,
75 headRef: "feature",
76 baseRef: "main",
77 updatedAt: "2026-01-02T00:00:00Z",
78 },
79 sourceControlProvider: { kind: "gitlab", name: "gitlab", baseUrl: "" },
80 });
81 });
82 it.each([
83 ["https://git.cafe/owner/repo/pulls/42", "gitcafe"],
84 ["https://staging.git.cafe/owner/repo/pulls/42", "gitcafe"],
85 ["https://codeberg.org/owner/repo/pulls/42", "forgejo"],
86 ["https://gitlab.example.com/acme/web/-/merge_requests/42", "gitlab"],
87 ["https://github.com/owner/repo/pull/42", "github"],
88 ])("classifies %s as %s", (url, kind) => {
89 const result = linkedPullRequestSnapshotStatus({
90 ...link,
91 url,
92 snapshot: {
93 state: "open",
94 title: "Change",
95 headBranch: "feature",
96 baseBranch: "main",
97 isDraft: false,
98 updatedAt: null,
99 syncedAt: "2026-01-03T00:00:00Z",
100 },
101 });
102 expect(result?.sourceControlProvider.kind).toBe(kind);
103 });
104});