Open History to see the last commit
1import type { ChangeRequestProjectIdentity } from "@t3tools/source-control-core/shared/definition";
2import { recognizeRemoteWith } from "@t3tools/source-control-core/shared/definition";
3import { describe, expect, it } from "vite-plus/test";
4
5import { definition } from "./definition.ts";
6
7const parse = (url: string) => definition.parseChangeRequestUrl(new URL(url))!;
8
9const identity = (
10 remoteUrl: string,
11 fields: Partial<ChangeRequestProjectIdentity> = {},
12): ChangeRequestProjectIdentity => ({
13 canonicalKey: "forge.example/git/team/repo",
14 displayName: "git/team/repo",
15 locator: { source: "git-remote", remoteName: "origin", remoteUrl },
16 ...fields,
17});
18
19const onHost = (project: ChangeRequestProjectIdentity, link: ReturnType<typeof parse>) =>
20 definition.isChangeRequestInRepository(project, link) ||
21 definition.canReadChangeRequestOnHost(project, link);
22
23describe("Forgejo change request URLs", () => {
24 it("reads nested paths and keeps the server's port as the authority", () => {
25 expect(parse("http://git.example.test:3000/Team/Repo/pulls/42/files")).toEqual({
26 host: "git.example.test",
27 authority: "git.example.test:3000",
28 repository: "team/repo",
29 number: 42,
30 repositoryUrl: "http://git.example.test:3000/Team/Repo",
31 });
32 expect(parse("https://github.internal/team/repo/pulls/7")).toMatchObject({
33 host: "github.internal",
34 authority: "github.internal",
35 });
36 });
37
38 it("claims only its own route", () => {
39 for (const url of [
40 "https://forge.example/team/repo/pull/7",
41 "https://forge.example/repo/pulls/7",
42 "https://forge.example/team/repo/pulls/abc",
43 ]) {
44 expect(definition.parseChangeRequestUrl(new URL(url)), url).toBeNull();
45 }
46 });
47
48 it.each([
49 ["http://forge.example:3000/git/owner/repo.git", "http://forge.example:3000"],
50 ["git@forge.example:git/owner/repo.git", "https://forge.example"],
51 ["http://other.example:3000/git/owner/repo.git", "https://forge.example"],
52 ])("keeps the matching web origin from %s", (remoteUrl, origin) => {
53 expect(
54 definition.changeRequestUrl({
55 host: "forge.example",
56 repository: "git/owner/repo",
57 number: 42,
58 remoteUrl,
59 }),
60 ).toBe(`${origin}/git/owner/repo/pulls/42`);
61 });
62
63 it("keeps the origin when the host already carries its port", () => {
64 expect(
65 definition.changeRequestUrl({
66 host: "forge.example:3000",
67 repository: "team/repo",
68 number: 42,
69 remoteUrl: "http://forge.example:3000/team/repo.git",
70 }),
71 ).toBe("http://forge.example:3000/team/repo/pulls/42");
72 });
73
74 it("matches projects by HTTP port at repository and host level", () => {
75 const [atThree, atFour] = [3000, 4000].map((port) =>
76 identity(`http://forge.example:${port}/git/team/repo.git`),
77 );
78 const link = parse("http://forge.example:4000/git/team/repo/pulls/42");
79 expect(definition.isChangeRequestInRepository(atFour!, link)).toBe(true);
80 expect(onHost(atFour!, { ...link, repository: "git/team/other" })).toBe(true);
81 expect(definition.isChangeRequestInRepository(atThree!, link)).toBe(false);
82 expect(onHost(atThree!, link)).toBe(false);
83 });
84
85 it("lets tea resolve the web port for SSH remotes, and honours a resolved web URL", () => {
86 const link = parse("http://forge.example:4000/git/team/repo/pulls/42");
87 expect(
88 definition.isChangeRequestInRepository(identity("git@forge.example:git/team/repo.git"), link),
89 ).toBe(true);
90 const aliased = identity("git@ssh.forge.example:team/repo.git", {
91 canonicalKey: "ssh.forge.example/team/repo",
92 displayName: "team/repo",
93 webUrl: "http://forge.example:4000/git/team/repo",
94 });
95 expect(definition.isChangeRequestInRepository(aliased, link)).toBe(true);
96 expect(onHost(aliased, { ...link, repository: "git/team/other" })).toBe(true);
97 for (const url of [
98 "http://other.example:4000/git/team/repo/pulls/42",
99 "http://forge.example:3000/git/team/repo/pulls/42",
100 "http://forge.example:4000/other/team/repo/pulls/42",
101 ]) {
102 expect(onHost(aliased, parse(url)), url).toBe(false);
103 }
104 });
105});
106
107describe("Forgejo repository pages", () => {
108 it("reads the repository from branch, tag and commit views", () => {
109 const repositoryUrl = (url: string) => definition.repositoryUrlFromPage?.(new URL(url), true);
110 expect(repositoryUrl("https://codeberg.org/forgejo/forgejo/src/branch/forgejo/README.md")).toBe(
111 "https://codeberg.org/forgejo/forgejo",
112 );
113 expect(repositoryUrl("http://git.example.test:3000/git/team/repo/src/tag/v1.0")).toBe(
114 "http://git.example.test:3000/git/team/repo",
115 );
116 expect(repositoryUrl("https://codeberg.org/forgejo/forgejo/src/commit/abc123")).toBe(
117 "https://codeberg.org/forgejo/forgejo",
118 );
119 expect(repositoryUrl("https://codeberg.org/forgejo/forgejo")).toBeNull();
120 });
121});
122
123describe("Forgejo remotes", () => {
124 it("recognises Codeberg and Forgejo or Gitea hosts, keeping an HTTP remote's origin", () => {
125 for (const host of ["codeberg.org", "forgejo.example.test", "gitea.example.test"]) {
126 expect(recognizeRemoteWith(definition, `http://${host}:3000/team/repo.git`)).toEqual({
127 kind: "forgejo",
128 name: "Forgejo",
129 baseUrl: `http://${host}:3000`,
130 });
131 }
132 expect(
133 recognizeRemoteWith(definition, "ssh://git@forgejo.example.test:2222/team/repo.git")?.baseUrl,
134 ).toBe("https://forgejo.example.test");
135 expect(recognizeRemoteWith(definition, "https://git.example.test/team/repo")).toBeNull();
136 });
137});
138
139describe("Forgejo CLI commands", () => {
140 it("recognizes shell merges and closes under every spelling tea accepts", () => {
141 for (const command of [
142 "tea pulls merge 7",
143 "tea pull merge 7",
144 "tea pr m 7",
145 "tea pull close 7",
146 "fj pr close 7",
147 ]) {
148 expect(definition.changeRequestCloseCommand.test(command), command).toBe(true);
149 }
150 expect(definition.changeRequestCloseCommand.test("tea pull checkout 7")).toBe(false);
151 });
152
153 it("reads the change request from every checkout spelling tea accepts", () => {
154 for (const command of [
155 "tea pulls checkout 7",
156 "tea pull checkout 7",
157 "tea pr co 7",
158 "tea pull co 7",
159 ]) {
160 expect(definition.checkoutCommandArgument(command), command).toBe("7");
161 }
162 });
163});
164
165describe("Forgejo change request host", () => {
166 it("keeps an HTTP remote's port, because a server's identity includes it", () => {
167 const project = identity("http://forge.example:3000/git/team/repo.git");
168 expect(definition.changeRequestHost?.(project)).toBe("forge.example:3000");
169 expect(
170 definition.changeRequestHost?.(identity("ssh://git@forge.example:2222/git/team/repo.git")),
171 ).toBe("forge.example");
172 expect(
173 definition.changeRequestHost?.(
174 identity("ssh://git@ssh.forge.example:2222/git/team/repo.git", {
175 webUrl: "http://forge.example:3000/git/team/repo",
176 }),
177 ),
178 ).toBe("forge.example:3000");
179 });
180});