diff --git a/src/review/inline-comments.ts b/src/review/inline-comments.ts index dbd52c4c88..c86df32396 100644 --- a/src/review/inline-comments.ts +++ b/src/review/inline-comments.ts @@ -92,7 +92,7 @@ export type ReviewInlineComment = { * {@link anchoredSuggestionBlock} for anchor-safety and fence validation (#2140 / #1956). */ /** The inline comment body: a compact severity (+ optional category) label + the finding, plus a one-click GitHub - * suggested-change block when the finding carries a `suggestion` AND the caller has suggestions enabled (#1956). + * suggested-change block when the finding carries a `suggestion` AND the caller has suggestions enabled (#1956 / #2139). * When `categoriesEnabled` (#1958 / #2149), the label carries a title-cased category tag (`Blocker ยท Security`) โ€” * the model's own `category` when it emitted one in the fixed enum, else the deterministic fallback * (`classifyFindingCategory`), so the tag is never sometimes-present. Public-safe by construction โ€” both the body diff --git a/test/unit/inline-comments.test.ts b/test/unit/inline-comments.test.ts index 23335324d7..a6030ca88c 100644 --- a/test/unit/inline-comments.test.ts +++ b/test/unit/inline-comments.test.ts @@ -132,7 +132,7 @@ describe("selectInlineComments (#inline-comments)", () => { expect(selectInlineComments(many, bigFiles)).toHaveLength(10); }); - describe("suggestion blocks (#1956)", () => { + describe("suggestion blocks (#1956 / #2139)", () => { const withSuggestion: InlineFinding = { path: "src/a.ts", line: 2, severity: "nit", body: "Use const.", suggestion: "const x = 1;" }; it("defaults to OFF (backward compatible) โ€” a suggestion is never rendered when the third argument is omitted", () => { @@ -150,6 +150,21 @@ describe("selectInlineComments (#inline-comments)", () => { expect(out[0]?.body).toBe("**Nit:** Use const.\n\n```suggestion\nconst x = 1;\n```"); }); + it("preserves multi-line suggestion text verbatim inside the fence and keeps the severity label first (#2139)", () => { + const multiLine: InlineFinding = { + path: "src/a.ts", + line: 2, + severity: "blocker", + body: "Split this statement.", + suggestion: "const x = 1;\nconst y = 2;", + }; + const out = selectInlineComments([multiLine], files, true); + expect(out[0]?.body).toBe( + "**Blocker:** Split this statement.\n\n```suggestion\nconst x = 1;\nconst y = 2;\n```", + ); + expect(out[0]?.body.startsWith("**Blocker:**")).toBe(true); + }); + it("renders no suggestion block (finding text only) when enabled but the finding has none", () => { const noSuggestion: InlineFinding = { path: "src/a.ts", line: 2, severity: "nit", body: "Use const." }; const out = selectInlineComments([noSuggestion], files, true); diff --git a/test/unit/inline-suggestion-anchor.test.ts b/test/unit/inline-suggestion-anchor.test.ts index d181181e90..462a334ab8 100644 --- a/test/unit/inline-suggestion-anchor.test.ts +++ b/test/unit/inline-suggestion-anchor.test.ts @@ -90,5 +90,10 @@ describe("anchoredSuggestionBlock (#2140)", () => { anchoredSuggestionBlock({ ...withSuggestion, suggestion: "```\nescape\n```" }, true, addedLines), ).toBe(""); expect(safeSuggestionBlock(undefined)).toBe(""); + expect(safeSuggestionBlock("")).toBe(""); + }); + + it("preserves multi-line suggestion text verbatim inside the fence (#2139)", () => { + expect(safeSuggestionBlock("line1\nline2")).toBe("\n\n```suggestion\nline1\nline2\n```"); }); });