settings-keybinds.test.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import { describe, expect, test } from "bun:test"
  2. import { createRoot } from "solid-js"
  3. import { createKeybindSettingsController } from "../src/components/settings-keybinds"
  4. function setup(overrides: Record<string, string> = {}) {
  5. const changes: [string, string][] = []
  6. const suppression: boolean[] = []
  7. const notifications: { title: string; description?: string }[] = []
  8. let resets = 0
  9. let controller: ReturnType<typeof createKeybindSettingsController>
  10. const dispose = createRoot((dispose) => {
  11. controller = createKeybindSettingsController(
  12. {
  13. command: {
  14. catalog: [
  15. { id: "session.alpha", title: "Alpha", keybind: "mod+a" },
  16. { id: "session.beta", title: "Beta", keybind: "mod+b" },
  17. ],
  18. options: [],
  19. keybinds: (enabled) => suppression.push(enabled),
  20. },
  21. settings: {
  22. current: { keybinds: overrides },
  23. keybinds: {
  24. get: (id) => overrides[id],
  25. set: (id, value) => {
  26. overrides[id] = value
  27. changes.push([id, value])
  28. },
  29. resetAll: () => {
  30. resets++
  31. },
  32. },
  33. },
  34. notify: (toast) => notifications.push(toast),
  35. },
  36. {
  37. locale: () => "en",
  38. t: (key, params) => {
  39. if (params) return `${key}:${Object.values(params).join("|")}`
  40. if (key === "common.key.alt") return "Alt"
  41. return String(key)
  42. },
  43. },
  44. )
  45. return dispose
  46. })
  47. return {
  48. controller: controller!,
  49. changes,
  50. suppression,
  51. notifications,
  52. resets: () => resets,
  53. dispose,
  54. }
  55. }
  56. function modKey(key: string) {
  57. const mac = /(Mac|iPod|iPhone|iPad)/.test(navigator.platform)
  58. return new KeyboardEvent("keydown", { key, ctrlKey: !mac, metaKey: mac, bubbles: true, cancelable: true })
  59. }
  60. describe("keybind settings controller", () => {
  61. test("derives the catalog, effective bindings, and filtered groups", () => {
  62. const state = setup({ "session.beta": "alt+k" })
  63. expect(state.controller.catalog.title("session.alpha")).toBe("Alpha")
  64. expect(state.controller.catalog.keybind("session.beta")).toBe("Alt+K")
  65. expect(state.controller.catalog.filtered("alt k").get("Session")).toEqual(["session.beta"])
  66. expect(state.controller.settings.hasOverrides()).toBe(true)
  67. state.dispose()
  68. })
  69. test("captures bindings, rejects conflicts, and restores command handling", () => {
  70. const state = setup()
  71. state.controller.capture.toggle("session.beta")
  72. document.dispatchEvent(modKey("a"))
  73. expect(state.changes).toEqual([])
  74. expect(state.notifications).toHaveLength(1)
  75. expect(state.controller.capture.active()).toBe("session.beta")
  76. document.dispatchEvent(modKey("x"))
  77. expect(state.changes).toEqual([["session.beta", "mod+x"]])
  78. expect(state.suppression).toEqual([false, true])
  79. expect(state.controller.capture.active()).toBeNull()
  80. state.controller.capture.toggle("session.alpha")
  81. state.dispose()
  82. expect(state.suppression).toEqual([false, true, false, true])
  83. document.dispatchEvent(modKey("z"))
  84. expect(state.changes).toEqual([["session.beta", "mod+x"]])
  85. })
  86. test("resets persisted overrides and reports success", () => {
  87. const state = setup({ "session.alpha": "none" })
  88. state.controller.settings.reset()
  89. expect(state.resets()).toBe(1)
  90. expect(state.notifications[0]?.title).toBe("settings.shortcuts.reset.toast.title")
  91. state.dispose()
  92. })
  93. })