settings-keybinds.spec.ts 12 KB

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