review-open-file.spec.ts 6.2 KB

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