1import { describe, expect, it } from "vite-plus/test";
2
3import { sourceControlClients } from "@t3tools/client-runtime/source-control-clients";
4
5import { resolveLinkPullRequestInput } from "./LinkPullRequestDialog";
6
7const changeRequestWebUrl = (kind: string, host: string, repository: string, number: number) =>
8 sourceControlClients.get(kind).changeRequestUrl({ host, repository, number });
9
10const project = {
11 host: "github.com",
12 repository: "acme/web",
13 webUrl: (number: number) => changeRequestWebUrl("github", "github.com", "acme/web", number),
14};
15
16describe("resolveLinkPullRequestInput", () => {
17 it.each([
18 ["https://bitbucket.org/acme/web/pull-requests/42", "bitbucket.org"],
19 ["https://github.acme.test/acme/web/pull/42", "github.acme.test"],
20 ["https://git.acme.test/acme/web/-/merge_requests/42", "git.acme.test"],
21 ])("links supported host URL %s without a thread project", (url, host) => {
22 expect(
23 resolveLinkPullRequestInput({
24 reference: ` ${url} `,
25 project: null,
26 hasProject: (candidate) => candidate.host === host,
27 }),
28 ).toEqual({ link: { host, repository: "acme/web", number: 42, url } });
29 });
30
31 it("validates the full Azure repository when resolving a browser URL", () => {
32 const hasProject = (reference: { host: string; repository: string }) =>
33 reference.host === "dev.azure.com" && reference.repository === "org-a/project/_git/web";
34 expect(
35 resolveLinkPullRequestInput({
36 reference: "https://dev.azure.com/org-a/project/_git/web/pullrequest/42",
37 project: null,
38 hasProject,
39 }),
40 ).toMatchObject({ link: { repository: "org-a/project/_git/web", number: 42 } });
41 expect(
42 resolveLinkPullRequestInput({
43 reference: "https://dev.azure.com/org-b/project/_git/web/pullrequest/42",
44 project: null,
45 hasProject,
46 }),
47 ).toMatchObject({ error: expect.stringContaining("org-b/project/_git/web") });
48 });
49
50 it("resolves bare Azure numbers into canonical browser URLs", () => {
51 expect(
52 resolveLinkPullRequestInput({
53 reference: "#42",
54 project: {
55 host: "ssh.dev.azure.com",
56 repository: "v3/org/project/web",
57 webUrl: (number) =>
58 changeRequestWebUrl("azure-devops", "ssh.dev.azure.com", "v3/org/project/web", number),
59 },
60 hasProject: () => true,
61 }),
62 ).toMatchObject({
63 link: {
64 host: "dev.azure.com",
65 repository: "org/project/_git/web",
66 number: 42,
67 url: "https://dev.azure.com/org/project/_git/web/pullrequest/42",
68 },
69 });
70 });
71
72 it("returns null for input that is not a reference", () => {
73 expect(
74 resolveLinkPullRequestInput({ reference: "hello", project, hasProject: () => true }),
75 ).toBeNull();
76 });
77
78 it("resolves a bare number against the thread's own repository", () => {
79 expect(
80 resolveLinkPullRequestInput({ reference: "#42", project, hasProject: () => true }),
81 ).toEqual({
82 link: {
83 host: "github.com",
84 repository: "acme/web",
85 number: 42,
86 url: "https://github.com/acme/web/pull/42",
87 },
88 });
89 });
90
91 it("links a URL from another repository on a host with a project", () => {
92 expect(
93 resolveLinkPullRequestInput({
94 reference: "https://github.com/acme/api/pull/7",
95 project,
96 hasProject: (reference) => reference.host === "github.com",
97 }),
98 ).toEqual({
99 link: {
100 host: "github.com",
101 repository: "acme/api",
102 number: 7,
103 url: "https://github.com/acme/api/pull/7",
104 },
105 });
106 });
107
108 it("refuses a URL on a host nothing is checked out from", () => {
109 const result = resolveLinkPullRequestInput({
110 reference: "https://gitlab.com/acme/api/-/merge_requests/7",
111 project,
112 hasProject: () => false,
113 });
114 expect(result).toMatchObject({ error: expect.stringContaining("gitlab.com") });
115 });
116
117 it("asks for a URL when a bare number has no project to resolve against", () => {
118 expect(
119 resolveLinkPullRequestInput({ reference: "12", project: null, hasProject: () => true }),
120 ).toMatchObject({ error: expect.stringContaining("full URL") });
121 });
122
123 it("accepts a checkout command as a reference", () => {
124 expect(
125 resolveLinkPullRequestInput({
126 reference: "gh pr checkout https://github.com/acme/web/pull/3",
127 project,
128 hasProject: () => true,
129 }),
130 ).toMatchObject({ link: { number: 3, repository: "acme/web" } });
131 });
132});
133
134describe("changeRequestWebUrl", () => {
135 it("knows the four hosts and nothing else", () => {
136 expect(changeRequestWebUrl("gitlab", "gitlab.com", "g/sub/repo", 5)).toBe(
137 "https://gitlab.com/g/sub/repo/-/merge_requests/5",
138 );
139 expect(changeRequestWebUrl("unknown", "x", "a/b", 1)).toBeNull();
140 });
141});