Skip to content
Source
Sign inSign up

pullRequestAnchor.test.ts

154 lines5.6 KB

1import { expect, it } from "vite-plus/test";
2import {
3 parsePullRequestAnchor,
4 pullRequestAnchorMatchesUrl,
5 pullRequestDiffPathMatches,
6 pullRequestLinkCommit,
7 resolvePullRequestReviewAnchor,
8} from "./pullRequestAnchor.ts";
9​
10const url = "https://github.com/owner/repo/pull/7";
11it.each(["discussion_r123", "r123", "discussion-diff-123R5-L9", "discussion-diff-123"])(
12 "resolves review comment alias %s",
13 (hash) => {
14 expect(parsePullRequestAnchor(`${url}#${hash}`)).toEqual({
15 kind: "timeline",
16 anchor: "discussion_r123",
17 });
18 expect(pullRequestAnchorMatchesUrl("discussion_r123", `${url}#${hash}`)).toBe(true);
19 },
20);
21it.each([
22 "pullrequestreview-123",
23 "issuecomment-123",
24 "commitcomment-123",
25 "event-123",
26 "note_123",
27 "comment-123",
28])("preserves timeline anchor %s", (anchor) => {
29 expect(parsePullRequestAnchor(`${url}#${anchor}`)).toEqual({ kind: "timeline", anchor });
30});
31it.each(["a".repeat(32), "b".repeat(64)])(
32 "reads file and cross-side line ranges for %s",
33 (digest) => {
34 expect(parsePullRequestAnchor(`${url}/files#diff-${digest}`)).toEqual({
35 kind: "diff",
36 digest,
37 range: null,
38 });
39 expect(parsePullRequestAnchor(`${url}/changes#diff-${digest}L12-R15`)).toEqual({
40 kind: "diff",
41 digest,
42 range: { start: 12, side: "left", end: 15, endSide: "right" },
43 });
44 expect(parsePullRequestAnchor(`${url}#diff-${digest}R42`)).toEqual({
45 kind: "diff",
46 digest,
47 range: { start: 42, side: "right", end: 42, endSide: "right" },
48 });
49 },
50);
51it.each([
52 [
53 "src/hello world.ts",
54 "bcea8deb9d67b305e28c4a0b0d021615",
55 "066802cf2cf056706e4e4951cb7dff2717a2ae32fd9797c1f1b41062d57db929",
56 ],
57 [
58 "src/日本語.ts",
59 "5c66d5d565666e46924cf80d7bbfdab3",
60 "191b8ebf2241225db999eaf8b6ff44b07934d577f347d0ff684591eecad34944",
61 ],
62 [
63 "old/path.ts",
64 "f5d455478d496859b654884654393a61",
65 "aa38d67014e2c59d51ea456a155e039f4e32f636a4c450714c3b517bb8bde7e7",
66 ],
67])("matches GitHub path digests for %s", (path, legacy, current) => {
68 for (const digest of [legacy, current]) {
69 expect(pullRequestDiffPathMatches(digest, path)).toBe(true);
70 expect(pullRequestDiffPathMatches(digest, "other.ts")).toBe(false);
71 }
72});
73it("handles encoded hashes and rejects invalid line numbers and URLs", () => {
74 expect(parsePullRequestAnchor(`${url}#%64iscussion_r123`)).toEqual({
75 kind: "timeline",
76 anchor: "discussion_r123",
77 });
78 expect(parsePullRequestAnchor(`${url}#diff-abcR0`)).toBeNull();
79 expect(parsePullRequestAnchor(`${url}#diff-abcR9999999999999999999`)).toBeNull();
80 expect(parsePullRequestAnchor("invalid")).toBeNull();
81 expect(parsePullRequestAnchor(url)).toBeNull();
82 expect(parsePullRequestAnchor(`${url}#unknown`)).toEqual({ kind: "summary", anchor: "unknown" });
83 expect(parsePullRequestAnchor(`${url}/files-not-a-tab`)).toBeNull();
84 expect(parsePullRequestAnchor(`${url}/commits/main`)).toBeNull();
85 expect(parsePullRequestAnchor(`${url}/checks/extra`)).toBeNull();
86});
87​
88it("keeps the commit scope of a PR diff permalink", () => {
89 const sha = "a".repeat(40);
90 expect(pullRequestLinkCommit(`${url}/commits/${sha}#diff-abcR5`)).toBe(sha);
91 expect(pullRequestLinkCommit(`${url}/files#diff-abcR5`)).toBeNull();
92});
93​
94const gitlab = "https://gitlab.com/group/repo/-/merge_requests/7";
95it.each([
96 [`${url}/files`, "code"],
97 [`${url}/files/`, "code"],
98 [`${url}/changes`, "code"],
99 [`${url}/commits/${"a".repeat(40)}`, "code"],
100 [`${url}/commits`, "commits"],
101 [`${url}/checks?check_run_id=1`, "checks"],
102 [`${gitlab}/diffs`, "code"],
103 [`${gitlab}/commits`, "commits"],
104 [`${gitlab}/pipelines`, "checks"],
105] as const)("keeps unknown and missing hashes in the tab %s names", (path, kind) => {
106 for (const hash of ["", "#unknown", "#diff-abcR0", "#diff-abcR9999999999999999999", "#%ZZ"]) {
107 expect(parsePullRequestAnchor(`${path}${hash}`)).toEqual({ kind });
108 }
109 expect(parsePullRequestAnchor(`${path}#issuecomment-123`)).toEqual({
110 kind: "timeline",
111 anchor: "issuecomment-123",
112 });
113});
114​
115it("finds a review with no text of its own through the earliest comment it posted", () => {
116 const review = `${url}#pullrequestreview-9`;
117 const threads = [
118 {
119 comments: [
120 { url: `${url}#discussion_r3`, createdAt: "2026-07-01T00:02:00Z", reviewUrl: review },
121 { url: `${url}#discussion_r2`, createdAt: "2026-07-01T00:01:00Z", reviewUrl: review },
122 { url: `${url}#discussion_r1`, createdAt: "2026-07-01T00:00:00Z" },
123 ],
124 },
125 ];
126 expect(resolvePullRequestReviewAnchor("pullrequestreview-9", [], threads)).toBe("discussion_r2");
127 // A review shown in its own right, an unknown review and other anchors are kept.
128 expect(resolvePullRequestReviewAnchor("pullrequestreview-9", [{ url: review }], threads)).toBe(
129 "pullrequestreview-9",
130 );
131 expect(resolvePullRequestReviewAnchor("pullrequestreview-8", [], threads)).toBe(
132 "pullrequestreview-8",
133 );
134 expect(resolvePullRequestReviewAnchor("issuecomment-9", [], threads)).toBe("issuecomment-9");
135});
136​
137it("reads GitLab merge request file and line anchors", () => {
138 const path = "internal/commands/issuable/view/issuable_view_test.go";
139 const digest = "0cd3702f060909d3161d1095b8347d4d07671427";
140 expect(pullRequestDiffPathMatches(digest, path)).toBe(true);
141 expect(parsePullRequestAnchor(`${gitlab}/diffs#${digest}`)).toEqual({
142 kind: "diff",
143 digest,
144 range: null,
145 });
146 expect(parsePullRequestAnchor(`${gitlab}/diffs#${digest}_248_253`)).toEqual({
147 kind: "diff",
148 digest,
149 range: { start: 253, side: "right", end: 253, endSide: "right" },
150 });
151 expect(parsePullRequestAnchor(`${gitlab}/diffs#${digest}_12_0`)).toMatchObject({
152 range: { start: 12, side: "left" },
153 });
154});