review-line-comment.spec.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import { expect, test, type Page } from "@playwright/test"
  2. import { base64Encode } from "@opencode-ai/core/util/encode"
  3. import { mockOpenCodeServer } from "../utils/mock-server"
  4. import { expectAppVisible, expectSessionTitle } from "../utils/waits"
  5. const directory = "C:/OpenCode/ReviewLineCommentRegression"
  6. const sessionID = "ses_review_line_comment_regression"
  7. const title = "Review line comment regression"
  8. test.beforeEach(async ({ page }) => {
  9. await openReview(page)
  10. })
  11. test("opens the comment editor when code is clicked", async ({ page }) => {
  12. const review = page.locator('[data-component="session-review"]')
  13. const line = review.getByText("export const value = 'after'", { exact: true })
  14. await expectAppVisible(line)
  15. await line.click()
  16. await expect(review.getByRole("textbox")).toBeVisible()
  17. })
  18. test("opens the comment editor when a line number is clicked", async ({ page }) => {
  19. const review = page.locator('[data-component="session-review"]')
  20. const lineNumber = review.locator('[data-column-number="1"]').last()
  21. await expectAppVisible(lineNumber)
  22. await lineNumber.click()
  23. await expect(review.getByRole("textbox")).toBeVisible()
  24. })
  25. test("opens the comment editor for a line number range", async ({ page }) => {
  26. const review = page.locator('[data-component="session-review"]')
  27. const start = review.locator('[data-column-number="1"]').last()
  28. const end = review.locator('[data-column-number="3"]').last()
  29. await expectAppVisible(start)
  30. await expectAppVisible(end)
  31. const from = await start.boundingBox()
  32. const to = await end.boundingBox()
  33. if (!from || !to) throw new Error("Missing line number bounds")
  34. await page.mouse.move(from.x + from.width / 2, from.y + from.height / 2)
  35. await page.mouse.down()
  36. await page.mouse.move(to.x + to.width / 2, to.y + to.height / 2)
  37. await page.mouse.up()
  38. await expect(review.getByRole("textbox")).toBeVisible()
  39. })
  40. test("shows a comment button when a line number is hovered", async ({ page }) => {
  41. const review = page.locator('[data-component="session-review"]')
  42. const lineNumber = review.locator('[data-column-number="1"]').last()
  43. await expectAppVisible(lineNumber)
  44. const comment = review.getByRole("button", { name: "Comment", exact: true })
  45. await expect(async () => {
  46. await page.mouse.move(0, 0)
  47. await lineNumber.hover()
  48. await expect(comment).toBeVisible({ timeout: 500 })
  49. await comment.click({ timeout: 500 })
  50. }).toPass()
  51. await expect(review.getByRole("textbox")).toBeVisible()
  52. })
  53. test("stages a submitted line comment in the prompt context", async ({ page }) => {
  54. const requests: string[] = []
  55. page.on("request", (request) => {
  56. if (request.method() !== "GET") requests.push(`${request.method()} ${new URL(request.url()).pathname}`)
  57. })
  58. const review = page.locator('[data-component="session-review"]')
  59. await review.getByText("export const value = 'after'", { exact: true }).click()
  60. await review.getByRole("textbox").fill("Use the existing value instead")
  61. await review.locator('[data-slot="line-comment-action"][data-variant="primary"]').click()
  62. await expect(review.getByText("Use the existing value instead", { exact: true })).toBeVisible()
  63. await page.getByRole("tab", { name: "Session" }).click()
  64. const context = page.getByText("Use the existing value instead", { exact: true }).last()
  65. await expect(context).toBeVisible()
  66. await expect(context.locator("..")).toContainText("review.ts:2")
  67. expect(requests).toEqual([])
  68. })
  69. async function openReview(page: Page) {
  70. await page.setViewportSize({ width: 700, height: 900 })
  71. await mockOpenCodeServer(page, {
  72. directory,
  73. project: {
  74. id: "proj_review_line_comment_regression",
  75. worktree: directory,
  76. vcs: "git",
  77. name: "review-line-comment-regression",
  78. time: { created: 1700000000000, updated: 1700000000000 },
  79. sandboxes: [],
  80. },
  81. provider: { all: [], connected: [], default: {} },
  82. sessions: [
  83. {
  84. id: sessionID,
  85. slug: "review-line-comment-regression",
  86. projectID: "proj_review_line_comment_regression",
  87. directory,
  88. title,
  89. version: "dev",
  90. time: { created: 1700000000000, updated: 1700000000000 },
  91. },
  92. ],
  93. vcsDiff: [
  94. {
  95. file: "src/review.ts",
  96. additions: 1,
  97. deletions: 1,
  98. status: "modified",
  99. patch:
  100. "diff --git a/src/review.ts b/src/review.ts\n--- a/src/review.ts\n+++ b/src/review.ts\n@@ -1,3 +1,3 @@\n export const first = 1\n-export const value = 'before'\n+export const value = 'after'\n export const last = 3\n",
  101. },
  102. ],
  103. pageMessages: () => ({
  104. items: [
  105. {
  106. info: {
  107. id: "msg_review_line_comment_regression",
  108. sessionID,
  109. role: "user",
  110. time: { created: 1700000000000 },
  111. summary: { diffs: [] },
  112. agent: "build",
  113. model: { providerID: "opencode", modelID: "test" },
  114. },
  115. parts: [
  116. {
  117. id: "prt_review_line_comment_regression",
  118. sessionID,
  119. messageID: "msg_review_line_comment_regression",
  120. type: "text",
  121. text: "Review this change.",
  122. },
  123. ],
  124. },
  125. ],
  126. }),
  127. })
  128. await page.goto(`/${base64Encode(directory)}/session/${sessionID}`)
  129. await expectSessionTitle(page, title)
  130. const diffResponse = page.waitForResponse((response) => new URL(response.url()).pathname === "/vcs/diff")
  131. await page.getByRole("tab", { name: "Changes" }).click()
  132. expect(await (await diffResponse).json()).toHaveLength(1)
  133. const review = page.locator('[data-component="session-review"]')
  134. await expectAppVisible(review)
  135. await review
  136. .getByRole("heading", { name: /review\.ts/ })
  137. .getByRole("button")
  138. .first()
  139. .click()
  140. }