review-open-file.spec.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import { base64Encode } from "@kirincode-ai/core/util/encode"
  2. import { expect, test } from "@playwright/test"
  3. import { mockKirinCodeServer } from "../utils/mock-server"
  4. import { expectSessionTitle } from "../utils/waits"
  5. const directory = "C:/KirinCode/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 mockKirinCodeServer(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: "kirincode",
  27. name: "KirinCode",
  28. models: { test: { id: "test", name: "Test", limit: { context: 200_000 } } },
  29. },
  30. ],
  31. connected: ["kirincode"],
  32. default: { providerID: "kirincode", 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 contextButton = page.getByRole("button", { name: "View context usage" })
  90. await contextButton.click()
  91. await expect(panel.getByRole("tab", { name: "Context" })).toHaveAttribute("data-selected", "")
  92. await panel.getByRole("button", { name: "Open file" }).click()
  93. await expect(panel.getByRole("tab", { name: "Open file" })).toHaveAttribute("data-selected", "")
  94. await expect(sidebar).toBeVisible()
  95. await contextButton.click()
  96. await expect(panel.getByRole("tab", { name: "Context" })).toHaveAttribute("data-selected", "")
  97. await expect(sidebar).toHaveCount(0)
  98. await panel.getByRole("button", { name: "Open file" }).click()
  99. const filter = panel.getByRole("combobox", { name: "Filter files" })
  100. await expect(filter).toBeFocused()
  101. await expect(panel.getByRole("tab", { name: "Open file" })).toHaveAttribute("data-selected", "")
  102. await expect(panel.getByText("open-file-project", { exact: true })).toBeVisible()
  103. await panel.getByRole("button", { name: "README.md" }).click()
  104. await expect(panel.getByRole("tab", { name: "README.md" })).toHaveAttribute("data-selected", "")
  105. await expect(panel.getByText("contents:README.md", { exact: true })).toBeVisible()
  106. await expect(sidebar).toHaveCount(0)
  107. await panel.getByRole("button", { name: "Open file" }).click()
  108. await expect(panel.getByRole("tab", { name: "README.md" })).toHaveCount(0)
  109. await expect(sidebar).toBeVisible()
  110. await filter.fill("nested")
  111. const result = panel.getByRole("option", { name: /nested\.ts/ })
  112. await expect(result).toBeVisible()
  113. const resultID = await result.getAttribute("id")
  114. expect(resultID).toBeTruthy()
  115. await expect(filter).toHaveAttribute("aria-activedescendant", resultID!)
  116. await filter.press("Enter")
  117. await expect(panel.getByRole("tab", { name: "nested.ts" })).toHaveAttribute("data-selected", "")
  118. await expect(panel.getByText("contents:src/nested.ts", { exact: true })).toBeVisible()
  119. expect(searches).toContainEqual({ query: "nested", dirs: "false", limit: 200 })
  120. await panel.getByRole("button", { name: "Open file" }).click()
  121. await expect(panel.getByRole("tab", { name: "nested.ts" })).toHaveCount(1)
  122. await expect(panel.getByRole("tab", { name: "Open file" })).toHaveAttribute("data-selected", "")
  123. await page.keyboard.press("Control+w")
  124. await expect(panel.getByRole("tab", { name: "Open file" })).toHaveCount(0)
  125. await expect(panel.getByRole("tab", { name: "nested.ts" })).toHaveAttribute("data-selected", "")
  126. })
  127. function fileNode(path: string) {
  128. return {
  129. name: path,
  130. path,
  131. absolute: `${directory}/${path}`,
  132. type: "file",
  133. ignored: false,
  134. }
  135. }
  136. function fileDiff(file: string) {
  137. return {
  138. file,
  139. before: "before\n",
  140. after: "after\n",
  141. additions: 1,
  142. deletions: 1,
  143. status: "modified",
  144. }
  145. }