feat: surface inline-comment anchor fields on pr view --reviews (task 0034)
All checks were successful
CI / test (pull_request) Successful in 53s

Each inline review comment under `pr view <n> --reviews` now renders its
`id` (the handle a reply targets), `resolved` (`yes`/`no`, from whether
Gitea populated the comment's `resolver`), and `diff_hunk`. The hunk is
structurally trimmed to its `@@` header line plus its last two lines by
default (hunks of three lines or fewer are left whole) and emitted
verbatim under `--full`, so the trim never touches the char-based body
truncation path. The raw `position`/`original_position` diff offsets stay
unsurfaced. The fields ride the existing reviews-plus-per-review-comments
fetch — no extra API calls.
This commit is contained in:
2026-07-18 15:46:30 -04:00
parent a557745e59
commit 237c10e38b
5 changed files with 423 additions and 4 deletions

View File

@@ -169,7 +169,17 @@ describe("pr view", () => {
{
method: "GET",
path: "/api/v1/repos/testowner/testrepo/pulls/7/reviews/11/comments",
body: [{ user: { login: "alice" }, path: "src/x.ts", body: "nit here" }],
body: [
{
id: 501,
user: { login: "alice" },
path: "src/x.ts",
body: "nit here",
diff_hunk: "@@ -10,6 +10,7 @@ func main\n a := 1\n b := 2\n c := 3\n d := 4\n+e := 5",
position: 7,
original_position: 4,
},
],
},
{
method: "GET",
@@ -194,12 +204,253 @@ describe("pr view", () => {
expect(stdout).toContain(" official: no");
expect(stdout).toContain(" stale: no");
expect(stdout).toContain(" stale: yes");
expect(stdout).toContain(" comments[1]{author,path,body}:");
expect(stdout).toContain(" alice,src/x.ts,nit here");
expect(stdout).toContain(" comments[1]{id,author,path,resolved,diff_hunk,body}:");
expect(stdout).toContain(
' 501,alice,src/x.ts,no,"@@ -10,6 +10,7 @@ func main\\n d := 4\\n+e := 5",nit here',
);
expect(stdout).not.toContain("original_position");
expect(stdout).not.toContain("position:");
expect(stdout).not.toContain("review_count");
expect(stdout).toContain("comment_count");
});
it("renders resolved as yes when an inline comment's resolver is populated", async () => {
server = await startFixtureServer([
{
method: "GET",
path: "/api/v1/repos/testowner/testrepo/pulls/8",
body: {
number: 8,
title: "T",
state: "open",
user: { login: "alexion" },
draft: false,
merged: false,
comments: 0,
body: "b",
head: { sha: "sha8", ref: "f" },
},
},
{
method: "GET",
path: "/api/v1/repos/testowner/testrepo/pulls/8/reviews",
body: [
{
id: 21,
state: "COMMENT",
official: false,
stale: false,
dismissed: false,
user: { login: "dave" },
body: "",
},
],
},
{
method: "GET",
path: "/api/v1/repos/testowner/testrepo/pulls/8/reviews/21/comments",
body: [
{
id: 777,
user: { login: "dave" },
path: "a.ts",
resolver: { login: "carol" },
diff_hunk: "@@ -1,4 +1,5 @@\n a\n b\n c\n+d",
body: "ok",
},
],
},
{
method: "GET",
path: "/api/v1/repos/testowner/testrepo/commits/sha8/status",
body: { sha: "sha8", total_count: 0, statuses: [] },
},
]);
const { stdout, exitCode } = await runCliTest(["pr", "view", "8", "--reviews"], {
env: testModeEnv(server.url),
});
expect(exitCode).toBe(0);
expect(stdout).toContain('777,dave,a.ts,yes,"@@ -1,4 +1,5 @@\\n c\\n+d",ok');
});
it("emits each inline comment's diff_hunk verbatim under --full, with no trimming", async () => {
server = await startFixtureServer([
{
method: "GET",
path: "/api/v1/repos/testowner/testrepo/pulls/10",
body: {
number: 10,
title: "T",
state: "open",
user: { login: "alexion" },
draft: false,
merged: false,
comments: 0,
body: "b",
head: { sha: "sha10", ref: "f" },
},
},
{
method: "GET",
path: "/api/v1/repos/testowner/testrepo/pulls/10/reviews",
body: [
{
id: 41,
state: "COMMENT",
official: false,
stale: false,
dismissed: false,
user: { login: "frank" },
body: "",
},
],
},
{
method: "GET",
path: "/api/v1/repos/testowner/testrepo/pulls/10/reviews/41/comments",
body: [
{
id: 999,
user: { login: "frank" },
path: "big.ts",
diff_hunk: "@@ -1,4 +1,5 @@\n a\n b\n c\n+d",
body: "hi",
},
],
},
{
method: "GET",
path: "/api/v1/repos/testowner/testrepo/commits/sha10/status",
body: { sha: "sha10", total_count: 0, statuses: [] },
},
]);
const { stdout, exitCode } = await runCliTest(["pr", "view", "10", "--reviews", "--full"], {
env: testModeEnv(server.url),
});
expect(exitCode).toBe(0);
expect(stdout).toContain('999,frank,big.ts,no,"@@ -1,4 +1,5 @@\\n a\\n b\\n c\\n+d",hi');
});
it("renders a diff_hunk of three lines or fewer in full by default, leaving short hunks untrimmed", async () => {
server = await startFixtureServer([
{
method: "GET",
path: "/api/v1/repos/testowner/testrepo/pulls/9",
body: {
number: 9,
title: "T",
state: "open",
user: { login: "alexion" },
draft: false,
merged: false,
comments: 0,
body: "b",
head: { sha: "sha9", ref: "f" },
},
},
{
method: "GET",
path: "/api/v1/repos/testowner/testrepo/pulls/9/reviews",
body: [
{
id: 31,
state: "COMMENT",
official: false,
stale: false,
dismissed: false,
user: { login: "eve" },
body: "",
},
],
},
{
method: "GET",
path: "/api/v1/repos/testowner/testrepo/pulls/9/reviews/31/comments",
body: [
{
id: 888,
user: { login: "eve" },
path: "b.ts",
diff_hunk: "@@ -5 +5 @@\n-x\n+y",
body: "short",
},
],
},
{
method: "GET",
path: "/api/v1/repos/testowner/testrepo/commits/sha9/status",
body: { sha: "sha9", total_count: 0, statuses: [] },
},
]);
const { stdout, exitCode } = await runCliTest(["pr", "view", "9", "--reviews"], {
env: testModeEnv(server.url),
});
expect(exitCode).toBe(0);
expect(stdout).toContain('888,eve,b.ts,no,"@@ -5 +5 @@\\n-x\\n+y",short');
});
it("trims a 4-line diff_hunk to its header plus last two lines by default, dropping the one middle line", async () => {
server = await startFixtureServer([
{
method: "GET",
path: "/api/v1/repos/testowner/testrepo/pulls/11",
body: {
number: 11,
title: "T",
state: "open",
user: { login: "alexion" },
draft: false,
merged: false,
comments: 0,
body: "b",
head: { sha: "sha11", ref: "f" },
},
},
{
method: "GET",
path: "/api/v1/repos/testowner/testrepo/pulls/11/reviews",
body: [
{
id: 51,
state: "COMMENT",
official: false,
stale: false,
dismissed: false,
user: { login: "grace" },
body: "",
},
],
},
{
method: "GET",
path: "/api/v1/repos/testowner/testrepo/pulls/11/reviews/51/comments",
body: [
{
id: 1010,
user: { login: "grace" },
path: "c.ts",
diff_hunk: "@@ -2,3 +2,3 @@\n keep1\n drop_me\n+keep2",
body: "boundary",
},
],
},
{
method: "GET",
path: "/api/v1/repos/testowner/testrepo/commits/sha11/status",
body: { sha: "sha11", total_count: 0, statuses: [] },
},
]);
const { stdout, exitCode } = await runCliTest(["pr", "view", "11", "--reviews"], {
env: testModeEnv(server.url),
});
expect(exitCode).toBe(0);
expect(stdout).toContain('1010,grace,c.ts,no,"@@ -2,3 +2,3 @@\\n drop_me\\n+keep2",boundary');
});
it("reports a nonexistent PR as PR_NOT_FOUND with exit 1", async () => {
server = await startFixtureServer([
{