1import type { ThreadPullRequestLink } from "@t3tools/contracts";
2import { describe, expect, it } from "vite-plus/test";
3
4import { resolveThreadReferenceCopyTarget } from "./threadReference.ts";
5
6const crossRepositoryPullRequest: ThreadPullRequestLink = {
7 host: "github.com",
8 repository: "other/repo",
9 number: 42,
10 url: "https://github.com/other/repo/pull/42",
11 source: "manual",
12 linkedAt: "2026-01-01T00:00:00.000Z",
13 snapshot: null,
14 stack: null,
15};
16
17describe("resolveThreadReferenceCopyTarget", () => {
18 it("does not copy another reference while the open panel URL is unavailable", () => {
19 expect(
20 resolveThreadReferenceCopyTarget({
21 threadId: "thread-1",
22 openPanelPullRequestUrl: null,
23 pullRequests: [crossRepositoryPullRequest],
24 linkedPullRequestUrl: "https://github.com/t3/pr/12",
25 }),
26 ).toBeNull();
27 });
28
29 it("prefers the open panel pull request over the thread pull request", () => {
30 expect(
31 resolveThreadReferenceCopyTarget({
32 threadId: "thread-1",
33 openPanelPullRequestUrl: "https://github.com/t3/pr/14",
34 pullRequests: [crossRepositoryPullRequest],
35 linkedPullRequestUrl: "https://github.com/t3/pr/12",
36 }),
37 ).toMatchObject({
38 kind: "pull-request",
39 value: "https://github.com/t3/pr/14",
40 successTitle: "PR link copied",
41 });
42 });
43
44 it.each([null, "https://github.com/t3/pr/12"])(
45 "copies a native cross-repository link before the fallback URL %s",
46 (linkedPullRequestUrl) => {
47 expect(
48 resolveThreadReferenceCopyTarget({
49 threadId: "thread-1",
50 pullRequests: [crossRepositoryPullRequest],
51 linkedPullRequestUrl,
52 }),
53 ).toMatchObject({
54 kind: "pull-request",
55 value: crossRepositoryPullRequest.url,
56 });
57 },
58 );
59
60 it("copies the highest open stack layer instead of the first link", () => {
61 const stack: NonNullable<ThreadPullRequestLink["stack"]> = {
62 kind: "native",
63 id: "stack-1",
64 number: 1,
65 url: "https://github.com/other/repo/stacks/1",
66 base: "main",
67 layers: [
68 { number: 42, headBranch: "first", state: "open" },
69 { number: 43, headBranch: "second", state: "open" },
70 ],
71 };
72 const top = {
73 ...crossRepositoryPullRequest,
74 number: 43,
75 url: "https://github.com/other/repo/pull/43",
76 stack,
77 };
78 expect(
79 resolveThreadReferenceCopyTarget({
80 threadId: "thread-1",
81 pullRequests: [{ ...crossRepositoryPullRequest, stack }, top],
82 }),
83 ).toMatchObject({ kind: "pull-request", value: top.url });
84 });
85
86 it("uses the fallback URL when native links are dismissed", () => {
87 expect(
88 resolveThreadReferenceCopyTarget({
89 threadId: "thread-1",
90 pullRequests: [{ ...crossRepositoryPullRequest, source: "stack-dismissed" }],
91 linkedPullRequestUrl: "https://github.com/t3/pr/12",
92 }),
93 ).toMatchObject({ kind: "pull-request", value: "https://github.com/t3/pr/12" });
94 });
95
96 it("uses the thread pull request when no panel is open", () => {
97 expect(
98 resolveThreadReferenceCopyTarget({
99 threadId: "thread-1",
100 linkedPullRequestUrl: "https://github.com/t3/pr/12",
101 }),
102 ).toMatchObject({
103 kind: "pull-request",
104 value: "https://github.com/t3/pr/12",
105 successTitle: "PR link copied",
106 });
107 });
108
109 it("falls back to the thread ID", () => {
110 expect(resolveThreadReferenceCopyTarget({ threadId: "thread-1" })).toEqual({
111 kind: "thread",
112 value: "thread-1",
113 clipboardTarget: "thread ID",
114 successTitle: "Thread ID copied",
115 failureTitle: "Failed to copy thread ID",
116 });
117 });
118});