2
0

settings-keybinds.spec.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. import { test, expect } from "../fixtures"
  2. import { openSettings, closeDialog, waitTerminalFocusIdle, withSession } from "../actions"
  3. import { keybindButtonSelector, terminalSelector } from "../selectors"
  4. import { modKey } from "../utils"
  5. test("changing sidebar toggle keybind works", async ({ page, gotoSession }) => {
  6. await gotoSession()
  7. const dialog = await openSettings(page)
  8. await dialog.getByRole("tab", { name: "Shortcuts" }).click()
  9. const keybindButton = dialog.locator(keybindButtonSelector("sidebar.toggle")).first()
  10. await expect(keybindButton).toBeVisible()
  11. const initialKeybind = await keybindButton.textContent()
  12. expect(initialKeybind).toContain("B")
  13. await keybindButton.click()
  14. await expect(keybindButton).toHaveText(/press/i)
  15. await page.keyboard.press(`${modKey}+Shift+KeyH`)
  16. await page.waitForTimeout(100)
  17. const newKeybind = await keybindButton.textContent()
  18. expect(newKeybind).toContain("H")
  19. const stored = await page.evaluate(() => {
  20. const raw = localStorage.getItem("settings.v3")
  21. return raw ? JSON.parse(raw) : null
  22. })
  23. expect(stored?.keybinds?.["sidebar.toggle"]).toBe("mod+shift+h")
  24. await closeDialog(page, dialog)
  25. const button = page.getByRole("button", { name: /toggle sidebar/i }).first()
  26. const initiallyClosed = (await button.getAttribute("aria-expanded")) !== "true"
  27. await page.keyboard.press(`${modKey}+Shift+H`)
  28. await expect(button).toHaveAttribute("aria-expanded", initiallyClosed ? "true" : "false")
  29. const afterToggleClosed = (await button.getAttribute("aria-expanded")) !== "true"
  30. expect(afterToggleClosed).toBe(!initiallyClosed)
  31. await page.keyboard.press(`${modKey}+Shift+H`)
  32. await expect(button).toHaveAttribute("aria-expanded", initiallyClosed ? "false" : "true")
  33. const finalClosed = (await button.getAttribute("aria-expanded")) !== "true"
  34. expect(finalClosed).toBe(initiallyClosed)
  35. })
  36. test("sidebar toggle keybind guards against shortcut conflicts", async ({ page, gotoSession }) => {
  37. await gotoSession()
  38. const dialog = await openSettings(page)
  39. await dialog.getByRole("tab", { name: "Shortcuts" }).click()
  40. const keybindButton = dialog.locator(keybindButtonSelector("sidebar.toggle"))
  41. await expect(keybindButton).toBeVisible()
  42. const initialKeybind = await keybindButton.textContent()
  43. expect(initialKeybind).toContain("B")
  44. await keybindButton.click()
  45. await expect(keybindButton).toHaveText(/press/i)
  46. await page.keyboard.press(`${modKey}+Shift+KeyP`)
  47. await page.waitForTimeout(100)
  48. const toast = page.locator('[data-component="toast"]').last()
  49. await expect(toast).toBeVisible()
  50. await expect(toast).toContainText(/already/i)
  51. await keybindButton.click()
  52. await expect(keybindButton).toContainText("B")
  53. const stored = await page.evaluate(() => {
  54. const raw = localStorage.getItem("settings.v3")
  55. return raw ? JSON.parse(raw) : null
  56. })
  57. expect(stored?.keybinds?.["sidebar.toggle"]).toBeUndefined()
  58. await closeDialog(page, dialog)
  59. })
  60. test("resetting all keybinds to defaults works", async ({ page, gotoSession }) => {
  61. await page.addInitScript(() => {
  62. localStorage.setItem("settings.v3", JSON.stringify({ keybinds: { "sidebar.toggle": "mod+shift+x" } }))
  63. })
  64. await gotoSession()
  65. const dialog = await openSettings(page)
  66. await dialog.getByRole("tab", { name: "Shortcuts" }).click()
  67. const keybindButton = dialog.locator(keybindButtonSelector("sidebar.toggle"))
  68. await expect(keybindButton).toBeVisible()
  69. const customKeybind = await keybindButton.textContent()
  70. expect(customKeybind).toContain("X")
  71. const resetButton = dialog.getByRole("button", { name: "Reset to defaults" })
  72. await expect(resetButton).toBeVisible()
  73. await expect(resetButton).toBeEnabled()
  74. await resetButton.click()
  75. await page.waitForTimeout(100)
  76. const restoredKeybind = await keybindButton.textContent()
  77. expect(restoredKeybind).toContain("B")
  78. const stored = await page.evaluate(() => {
  79. const raw = localStorage.getItem("settings.v3")
  80. return raw ? JSON.parse(raw) : null
  81. })
  82. expect(stored?.keybinds?.["sidebar.toggle"]).toBeUndefined()
  83. await closeDialog(page, dialog)
  84. })
  85. test("clearing a keybind works", async ({ page, gotoSession }) => {
  86. await gotoSession()
  87. const dialog = await openSettings(page)
  88. await dialog.getByRole("tab", { name: "Shortcuts" }).click()
  89. const keybindButton = dialog.locator(keybindButtonSelector("sidebar.toggle"))
  90. await expect(keybindButton).toBeVisible()
  91. const initialKeybind = await keybindButton.textContent()
  92. expect(initialKeybind).toContain("B")
  93. await keybindButton.click()
  94. await expect(keybindButton).toHaveText(/press/i)
  95. await page.keyboard.press("Delete")
  96. await page.waitForTimeout(100)
  97. const clearedKeybind = await keybindButton.textContent()
  98. expect(clearedKeybind).toMatch(/unassigned|press/i)
  99. const stored = await page.evaluate(() => {
  100. const raw = localStorage.getItem("settings.v3")
  101. return raw ? JSON.parse(raw) : null
  102. })
  103. expect(stored?.keybinds?.["sidebar.toggle"]).toBe("none")
  104. await closeDialog(page, dialog)
  105. await page.keyboard.press(`${modKey}+B`)
  106. await page.waitForTimeout(100)
  107. const stillOnSession = page.url().includes("/session")
  108. expect(stillOnSession).toBe(true)
  109. })
  110. test("changing settings open keybind works", async ({ page, gotoSession }) => {
  111. await gotoSession()
  112. const dialog = await openSettings(page)
  113. await dialog.getByRole("tab", { name: "Shortcuts" }).click()
  114. const keybindButton = dialog.locator(keybindButtonSelector("settings.open"))
  115. await expect(keybindButton).toBeVisible()
  116. const initialKeybind = await keybindButton.textContent()
  117. expect(initialKeybind).toContain(",")
  118. await keybindButton.click()
  119. await expect(keybindButton).toHaveText(/press/i)
  120. await page.keyboard.press(`${modKey}+Slash`)
  121. await page.waitForTimeout(100)
  122. const newKeybind = await keybindButton.textContent()
  123. expect(newKeybind).toContain("/")
  124. const stored = await page.evaluate(() => {
  125. const raw = localStorage.getItem("settings.v3")
  126. return raw ? JSON.parse(raw) : null
  127. })
  128. expect(stored?.keybinds?.["settings.open"]).toBe("mod+/")
  129. await closeDialog(page, dialog)
  130. const settingsDialog = page.getByRole("dialog")
  131. await expect(settingsDialog).toHaveCount(0)
  132. await page.keyboard.press(`${modKey}+Slash`)
  133. await page.waitForTimeout(100)
  134. await expect(settingsDialog).toBeVisible()
  135. await closeDialog(page, settingsDialog)
  136. })
  137. test("changing new session keybind works", async ({ page, sdk, gotoSession }) => {
  138. await withSession(sdk, "test session for keybind", async (session) => {
  139. await gotoSession(session.id)
  140. const initialUrl = page.url()
  141. expect(initialUrl).toContain(`/session/${session.id}`)
  142. const dialog = await openSettings(page)
  143. await dialog.getByRole("tab", { name: "Shortcuts" }).click()
  144. const keybindButton = dialog.locator(keybindButtonSelector("session.new"))
  145. await expect(keybindButton).toBeVisible()
  146. await keybindButton.click()
  147. await expect(keybindButton).toHaveText(/press/i)
  148. await page.keyboard.press(`${modKey}+Shift+KeyN`)
  149. await page.waitForTimeout(100)
  150. const newKeybind = await keybindButton.textContent()
  151. expect(newKeybind).toContain("N")
  152. const stored = await page.evaluate(() => {
  153. const raw = localStorage.getItem("settings.v3")
  154. return raw ? JSON.parse(raw) : null
  155. })
  156. expect(stored?.keybinds?.["session.new"]).toBe("mod+shift+n")
  157. await closeDialog(page, dialog)
  158. await page.keyboard.press(`${modKey}+Shift+N`)
  159. await page.waitForTimeout(200)
  160. const newUrl = page.url()
  161. expect(newUrl).toMatch(/\/session\/?$/)
  162. expect(newUrl).not.toContain(session.id)
  163. })
  164. })
  165. test("changing file open keybind works", async ({ page, gotoSession }) => {
  166. await gotoSession()
  167. const dialog = await openSettings(page)
  168. await dialog.getByRole("tab", { name: "Shortcuts" }).click()
  169. const keybindButton = dialog.locator(keybindButtonSelector("file.open"))
  170. await expect(keybindButton).toBeVisible()
  171. const initialKeybind = await keybindButton.textContent()
  172. expect(initialKeybind).toContain("K")
  173. await keybindButton.click()
  174. await expect(keybindButton).toHaveText(/press/i)
  175. await page.keyboard.press(`${modKey}+Shift+KeyF`)
  176. await page.waitForTimeout(100)
  177. const newKeybind = await keybindButton.textContent()
  178. expect(newKeybind).toContain("F")
  179. const stored = await page.evaluate(() => {
  180. const raw = localStorage.getItem("settings.v3")
  181. return raw ? JSON.parse(raw) : null
  182. })
  183. expect(stored?.keybinds?.["file.open"]).toBe("mod+shift+f")
  184. await closeDialog(page, dialog)
  185. const filePickerDialog = page.getByRole("dialog").filter({ has: page.getByPlaceholder(/search files/i) })
  186. await expect(filePickerDialog).toHaveCount(0)
  187. await page.keyboard.press(`${modKey}+Shift+F`)
  188. await page.waitForTimeout(100)
  189. await expect(filePickerDialog).toBeVisible()
  190. await page.keyboard.press("Escape")
  191. await expect(filePickerDialog).toHaveCount(0)
  192. })
  193. test("changing terminal toggle keybind works", async ({ page, gotoSession }) => {
  194. await gotoSession()
  195. const dialog = await openSettings(page)
  196. await dialog.getByRole("tab", { name: "Shortcuts" }).click()
  197. const keybindButton = dialog.locator(keybindButtonSelector("terminal.toggle"))
  198. await expect(keybindButton).toBeVisible()
  199. await keybindButton.click()
  200. await expect(keybindButton).toHaveText(/press/i)
  201. await page.keyboard.press(`${modKey}+KeyY`)
  202. await page.waitForTimeout(100)
  203. const newKeybind = await keybindButton.textContent()
  204. expect(newKeybind).toContain("Y")
  205. const stored = await page.evaluate(() => {
  206. const raw = localStorage.getItem("settings.v3")
  207. return raw ? JSON.parse(raw) : null
  208. })
  209. expect(stored?.keybinds?.["terminal.toggle"]).toBe("mod+y")
  210. await closeDialog(page, dialog)
  211. const terminal = page.locator(terminalSelector)
  212. await expect(terminal).not.toBeVisible()
  213. await page.keyboard.press(`${modKey}+Y`)
  214. await waitTerminalFocusIdle(page, { term: terminal })
  215. await page.keyboard.press(`${modKey}+Y`)
  216. await expect(terminal).not.toBeVisible()
  217. })
  218. test("terminal toggle keybind persists after reload", async ({ page, gotoSession }) => {
  219. await gotoSession()
  220. const dialog = await openSettings(page)
  221. await dialog.getByRole("tab", { name: "Shortcuts" }).click()
  222. const keybindButton = dialog.locator(keybindButtonSelector("terminal.toggle"))
  223. await expect(keybindButton).toBeVisible()
  224. await keybindButton.click()
  225. await expect(keybindButton).toHaveText(/press/i)
  226. await page.keyboard.press(`${modKey}+Shift+KeyY`)
  227. await page.waitForTimeout(100)
  228. await expect(keybindButton).toContainText("Y")
  229. await closeDialog(page, dialog)
  230. await page.reload()
  231. await expect
  232. .poll(async () => {
  233. return await page.evaluate(() => {
  234. const raw = localStorage.getItem("settings.v3")
  235. if (!raw) return
  236. const parsed = JSON.parse(raw)
  237. return parsed?.keybinds?.["terminal.toggle"]
  238. })
  239. })
  240. .toBe("mod+shift+y")
  241. const reloaded = await openSettings(page)
  242. await reloaded.getByRole("tab", { name: "Shortcuts" }).click()
  243. const reloadedKeybind = reloaded.locator(keybindButtonSelector("terminal.toggle")).first()
  244. await expect(reloadedKeybind).toContainText("Y")
  245. await closeDialog(page, reloaded)
  246. })
  247. test("changing command palette keybind works", async ({ page, gotoSession }) => {
  248. await gotoSession()
  249. const dialog = await openSettings(page)
  250. await dialog.getByRole("tab", { name: "Shortcuts" }).click()
  251. const keybindButton = dialog.locator(keybindButtonSelector("command.palette"))
  252. await expect(keybindButton).toBeVisible()
  253. const initialKeybind = await keybindButton.textContent()
  254. expect(initialKeybind).toContain("P")
  255. await keybindButton.click()
  256. await expect(keybindButton).toHaveText(/press/i)
  257. await page.keyboard.press(`${modKey}+Shift+KeyK`)
  258. await page.waitForTimeout(100)
  259. const newKeybind = await keybindButton.textContent()
  260. expect(newKeybind).toContain("K")
  261. const stored = await page.evaluate(() => {
  262. const raw = localStorage.getItem("settings.v3")
  263. return raw ? JSON.parse(raw) : null
  264. })
  265. expect(stored?.keybinds?.["command.palette"]).toBe("mod+shift+k")
  266. await closeDialog(page, dialog)
  267. const palette = page.getByRole("dialog").filter({ has: page.getByRole("textbox").first() })
  268. await expect(palette).toHaveCount(0)
  269. await page.keyboard.press(`${modKey}+Shift+K`)
  270. await page.waitForTimeout(100)
  271. await expect(palette).toBeVisible()
  272. await expect(palette.getByRole("textbox").first()).toBeVisible()
  273. await page.keyboard.press("Escape")
  274. await expect(palette).toHaveCount(0)
  275. })