review-open-file.spec.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import { base64Encode } from "@opencode-ai/core/util/encode"
  2. import { expect, test } from "@playwright/test"
  3. import { mockOpenCodeServer } from "../utils/mock-server"
  4. import { expectSessionTitle } from "../utils/waits"
  5. const directory = "C:/OpenCode/ReviewOpenFile"
  6. const projectID = "proj_review_open_file"
  7. const sessionID = "ses_review_open_file"
  8. const title = "Review open file"
  9. const server = `http://${process.env.PLAYWRIGHT_SERVER_HOST ?? "127.0.0.1"}:${process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"}`
  10. test.use({ viewport: { width: 1440, height: 900 } })
  11. test("opens and searches project files inline", async ({ page }) => {
  12. const searches: { query: string; dirs?: string; limit?: number }[] = []
  13. await mockOpenCodeServer(page, {
  14. protocol: "v2",
  15. directory,
  16. project: {
  17. id: projectID,
  18. worktree: directory,
  19. vcs: "git",
  20. name: "open-file-project",
  21. time: { created: 1700000000000, updated: 1700000000000 },
  22. sandboxes: [],
  23. },
  24. provider: {
  25. all: [
  26. {
  27. id: "opencode",
  28. name: "OpenCode",
  29. models: { test: { id: "test", name: "Test", limit: { context: 200_000 } } },
  30. },
  31. ],
  32. connected: ["opencode"],
  33. default: { providerID: "opencode", modelID: "test" },
  34. },
  35. sessions: [
  36. {
  37. id: sessionID,
  38. slug: sessionID,
  39. projectID,
  40. directory,
  41. title,
  42. version: "dev",
  43. time: { created: 1700000000000, updated: 1700000000000 },
  44. },
  45. ],
  46. vcsDiff: [fileDiff("src/changed.ts")],
  47. fileList: (path) => {
  48. if (path) return []
  49. return [
  50. fileNode("README.md"),
  51. { name: "src", path: "src", absolute: `${directory}/src`, type: "directory", ignored: false },
  52. ]
  53. },
  54. fileContent: (path) => ({ type: "text", content: `contents:${path}` }),
  55. findFiles: (input) => {
  56. searches.push(input)
  57. return input.query === "nested" ? ["src/nested.ts"] : []
  58. },
  59. pageMessages: () => ({ items: [] }),
  60. })
  61. await page.addInitScript(
  62. ({ directory, server, sessionID }) => {
  63. localStorage.setItem("settings.v3", JSON.stringify({ general: { newLayoutDesigns: true } }))
  64. localStorage.setItem(
  65. "opencode.global.dat:server",
  66. JSON.stringify({
  67. projects: { local: [{ worktree: directory, expanded: true }] },
  68. lastProject: { local: directory },
  69. }),
  70. )
  71. localStorage.setItem(
  72. "opencode.global.dat:layout",
  73. JSON.stringify({ review: { diffStyle: "split", panelOpened: true } }),
  74. )
  75. localStorage.setItem(
  76. "opencode.global.dat:review-panel-v2",
  77. JSON.stringify({ sidebarOpened: false, sidebarWidth: 240, expandMode: "collapse" }),
  78. )
  79. localStorage.setItem(
  80. "opencode.window.browser.dat:tabs",
  81. JSON.stringify([{ type: "session", server, sessionId: sessionID }]),
  82. )
  83. },
  84. { directory, server, sessionID },
  85. )
  86. await page.goto(`/server/${base64Encode(server)}/session/${sessionID}`)
  87. await expectSessionTitle(page, title)
  88. const panel = page.locator("#review-panel")
  89. const sidebar = panel.locator('[data-slot="session-review-v2-sidebar"]')
  90. const sidebarToggle = panel.getByRole("button", { name: "Toggle file tree" })
  91. const contextButton = page.getByRole("button", { name: "View context usage" })
  92. await contextButton.click()
  93. await expect(panel.getByRole("tab", { name: "Context" })).toHaveAttribute("data-selected", "")
  94. await panel.getByRole("button", { name: "Open file" }).click()
  95. await expect(panel.getByRole("tab", { name: "Open file" })).toHaveAttribute("data-selected", "")
  96. await expect(sidebarToggle).toBeDisabled()
  97. await expect(sidebar).toBeVisible()
  98. await contextButton.click()
  99. await expect(panel.getByRole("tab", { name: "Context" })).toHaveAttribute("data-selected", "")
  100. await expect(sidebar).toBeHidden()
  101. await panel.getByRole("button", { name: "Open file" }).click()
  102. const filter = panel.getByRole("combobox", { name: "Filter files" })
  103. await expect(filter).toBeFocused()
  104. await expect(panel.getByRole("tab", { name: "Open file" })).toHaveAttribute("data-selected", "")
  105. await expect(panel.getByText("open-file-project", { exact: true })).toBeVisible()
  106. await panel.getByRole("button", { name: "README.md" }).click()
  107. await expect(panel.getByRole("tab", { name: "README.md" })).toHaveAttribute("data-selected", "")
  108. await expect(sidebarToggle).toBeEnabled()
  109. await expect(panel.getByText("contents:README.md", { exact: true })).toBeVisible()
  110. await expect(sidebar).toHaveCount(0)
  111. await panel.getByRole("button", { name: "Open file" }).click()
  112. await expect(panel.getByRole("tab", { name: "README.md" })).toHaveCount(0)
  113. await expect(sidebar).toBeVisible()
  114. await filter.fill("nested")
  115. const result = panel.getByRole("option", { name: /nested\.ts/ })
  116. await expect(result).toBeVisible()
  117. const resultID = await result.getAttribute("id")
  118. expect(resultID).toBeTruthy()
  119. await expect(filter).toHaveAttribute("aria-activedescendant", resultID!)
  120. await filter.press("Enter")
  121. await expect(panel.getByRole("tab", { name: "nested.ts" })).toHaveAttribute("data-selected", "")
  122. await expect(sidebarToggle).toBeEnabled()
  123. await expect(panel.getByText("contents:src/nested.ts", { exact: true })).toBeVisible()
  124. expect(searches).toContainEqual({ query: "nested", dirs: "file", limit: 200 })
  125. await panel.getByRole("button", { name: "Open file" }).click()
  126. await expect(panel.getByRole("tab", { name: "nested.ts" })).toHaveCount(1)
  127. await expect(panel.getByRole("tab", { name: "Open file" })).toHaveAttribute("data-selected", "")
  128. await expect(sidebarToggle).toBeDisabled()
  129. await panel.locator("#session-side-panel-review-tab").click()
  130. await expect(sidebarToggle).toBeEnabled()
  131. await panel.getByRole("tab", { name: "Open file" }).click()
  132. await page.keyboard.press("Control+w")
  133. await expect(panel.getByRole("tab", { name: "Open file" })).toHaveCount(0)
  134. await expect(panel.getByRole("tab", { name: "nested.ts" })).toHaveAttribute("data-selected", "")
  135. })
  136. function fileNode(path: string) {
  137. return {
  138. name: path,
  139. path,
  140. absolute: `${directory}/${path}`,
  141. type: "file",
  142. ignored: false,
  143. }
  144. }
  145. function fileDiff(file: string) {
  146. return {
  147. file,
  148. before: "before\n",
  149. after: "after\n",
  150. additions: 1,
  151. deletions: 1,
  152. status: "modified",
  153. }
  154. }