keybind.test.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. import { describe, test, expect } from "bun:test"
  2. import { Keybind } from "../src/util/keybind"
  3. describe("Keybind.toString", () => {
  4. test("should convert simple key to string", () => {
  5. const info: Keybind.Info = { ctrl: false, meta: false, shift: false, leader: false, name: "f" }
  6. expect(Keybind.toString(info)).toBe("f")
  7. })
  8. test("should convert ctrl modifier to string", () => {
  9. const info: Keybind.Info = { ctrl: true, meta: false, shift: false, leader: false, name: "x" }
  10. expect(Keybind.toString(info)).toBe("ctrl+x")
  11. })
  12. test("should convert leader key to string", () => {
  13. const info: Keybind.Info = { ctrl: false, meta: false, shift: false, leader: true, name: "f" }
  14. expect(Keybind.toString(info)).toBe("<leader> f")
  15. })
  16. test("should convert multiple modifiers to string", () => {
  17. const info: Keybind.Info = { ctrl: true, meta: true, shift: false, leader: false, name: "g" }
  18. expect(Keybind.toString(info)).toBe("ctrl+alt+g")
  19. })
  20. test("should convert all modifiers to string", () => {
  21. const info: Keybind.Info = { ctrl: true, meta: true, shift: true, leader: true, name: "h" }
  22. expect(Keybind.toString(info)).toBe("<leader> ctrl+alt+shift+h")
  23. })
  24. test("should convert shift modifier to string", () => {
  25. const info: Keybind.Info = {
  26. ctrl: false,
  27. meta: false,
  28. shift: true,
  29. leader: false,
  30. name: "return",
  31. }
  32. expect(Keybind.toString(info)).toBe("shift+return")
  33. })
  34. test("should convert function key to string", () => {
  35. const info: Keybind.Info = { ctrl: false, meta: false, shift: false, leader: false, name: "f2" }
  36. expect(Keybind.toString(info)).toBe("f2")
  37. })
  38. test("should convert special key to string", () => {
  39. const info: Keybind.Info = {
  40. ctrl: false,
  41. meta: false,
  42. shift: false,
  43. leader: false,
  44. name: "pgup",
  45. }
  46. expect(Keybind.toString(info)).toBe("pgup")
  47. })
  48. test("should handle empty name", () => {
  49. const info: Keybind.Info = { ctrl: true, meta: false, shift: false, leader: false, name: "" }
  50. expect(Keybind.toString(info)).toBe("ctrl")
  51. })
  52. test("should handle only modifiers", () => {
  53. const info: Keybind.Info = { ctrl: true, meta: true, shift: true, leader: true, name: "" }
  54. expect(Keybind.toString(info)).toBe("<leader> ctrl+alt+shift")
  55. })
  56. test("should handle only leader with no other parts", () => {
  57. const info: Keybind.Info = { ctrl: false, meta: false, shift: false, leader: true, name: "" }
  58. expect(Keybind.toString(info)).toBe("<leader>")
  59. })
  60. test("should convert super modifier to string", () => {
  61. const info: Keybind.Info = { ctrl: false, meta: false, shift: false, super: true, leader: false, name: "z" }
  62. expect(Keybind.toString(info)).toBe("super+z")
  63. })
  64. test("should convert super+shift modifier to string", () => {
  65. const info: Keybind.Info = { ctrl: false, meta: false, shift: true, super: true, leader: false, name: "z" }
  66. expect(Keybind.toString(info)).toBe("super+shift+z")
  67. })
  68. test("should handle super with ctrl modifier", () => {
  69. const info: Keybind.Info = { ctrl: true, meta: false, shift: false, super: true, leader: false, name: "a" }
  70. expect(Keybind.toString(info)).toBe("ctrl+super+a")
  71. })
  72. test("should handle super with all modifiers", () => {
  73. const info: Keybind.Info = { ctrl: true, meta: true, shift: true, super: true, leader: false, name: "x" }
  74. expect(Keybind.toString(info)).toBe("ctrl+alt+super+shift+x")
  75. })
  76. test("should handle undefined super field (omitted)", () => {
  77. const info: Keybind.Info = { ctrl: true, meta: false, shift: false, leader: false, name: "c" }
  78. expect(Keybind.toString(info)).toBe("ctrl+c")
  79. })
  80. })
  81. describe("Keybind.match", () => {
  82. test("should match identical keybinds", () => {
  83. const a: Keybind.Info = { ctrl: true, meta: false, shift: false, leader: false, name: "x" }
  84. const b: Keybind.Info = { ctrl: true, meta: false, shift: false, leader: false, name: "x" }
  85. expect(Keybind.match(a, b)).toBe(true)
  86. })
  87. test("should not match different key names", () => {
  88. const a: Keybind.Info = { ctrl: true, meta: false, shift: false, leader: false, name: "x" }
  89. const b: Keybind.Info = { ctrl: true, meta: false, shift: false, leader: false, name: "y" }
  90. expect(Keybind.match(a, b)).toBe(false)
  91. })
  92. test("should not match different modifiers", () => {
  93. const a: Keybind.Info = { ctrl: true, meta: false, shift: false, leader: false, name: "x" }
  94. const b: Keybind.Info = { ctrl: false, meta: false, shift: false, leader: false, name: "x" }
  95. expect(Keybind.match(a, b)).toBe(false)
  96. })
  97. test("should match leader keybinds", () => {
  98. const a: Keybind.Info = { ctrl: false, meta: false, shift: false, leader: true, name: "f" }
  99. const b: Keybind.Info = { ctrl: false, meta: false, shift: false, leader: true, name: "f" }
  100. expect(Keybind.match(a, b)).toBe(true)
  101. })
  102. test("should not match leader vs non-leader", () => {
  103. const a: Keybind.Info = { ctrl: false, meta: false, shift: false, leader: true, name: "f" }
  104. const b: Keybind.Info = { ctrl: false, meta: false, shift: false, leader: false, name: "f" }
  105. expect(Keybind.match(a, b)).toBe(false)
  106. })
  107. test("should match complex keybinds", () => {
  108. const a: Keybind.Info = { ctrl: true, meta: true, shift: false, leader: false, name: "g" }
  109. const b: Keybind.Info = { ctrl: true, meta: true, shift: false, leader: false, name: "g" }
  110. expect(Keybind.match(a, b)).toBe(true)
  111. })
  112. test("should not match with one modifier different", () => {
  113. const a: Keybind.Info = { ctrl: true, meta: true, shift: false, leader: false, name: "g" }
  114. const b: Keybind.Info = { ctrl: true, meta: true, shift: true, leader: false, name: "g" }
  115. expect(Keybind.match(a, b)).toBe(false)
  116. })
  117. test("should match simple key without modifiers", () => {
  118. const a: Keybind.Info = { ctrl: false, meta: false, shift: false, leader: false, name: "a" }
  119. const b: Keybind.Info = { ctrl: false, meta: false, shift: false, leader: false, name: "a" }
  120. expect(Keybind.match(a, b)).toBe(true)
  121. })
  122. test("should match super modifier keybinds", () => {
  123. const a: Keybind.Info = { ctrl: false, meta: false, shift: false, super: true, leader: false, name: "z" }
  124. const b: Keybind.Info = { ctrl: false, meta: false, shift: false, super: true, leader: false, name: "z" }
  125. expect(Keybind.match(a, b)).toBe(true)
  126. })
  127. test("should not match super vs non-super", () => {
  128. const a: Keybind.Info = { ctrl: false, meta: false, shift: false, super: true, leader: false, name: "z" }
  129. const b: Keybind.Info = { ctrl: false, meta: false, shift: false, super: false, leader: false, name: "z" }
  130. expect(Keybind.match(a, b)).toBe(false)
  131. })
  132. test("should match undefined super with false super", () => {
  133. const a: Keybind.Info = { ctrl: true, meta: false, shift: false, leader: false, name: "c" }
  134. const b: Keybind.Info = { ctrl: true, meta: false, shift: false, super: false, leader: false, name: "c" }
  135. expect(Keybind.match(a, b)).toBe(true)
  136. })
  137. test("should match super+shift combination", () => {
  138. const a: Keybind.Info = { ctrl: false, meta: false, shift: true, super: true, leader: false, name: "z" }
  139. const b: Keybind.Info = { ctrl: false, meta: false, shift: true, super: true, leader: false, name: "z" }
  140. expect(Keybind.match(a, b)).toBe(true)
  141. })
  142. test("should not match when only super differs", () => {
  143. const a: Keybind.Info = { ctrl: true, meta: true, shift: true, super: true, leader: false, name: "a" }
  144. const b: Keybind.Info = { ctrl: true, meta: true, shift: true, super: false, leader: false, name: "a" }
  145. expect(Keybind.match(a, b)).toBe(false)
  146. })
  147. })
  148. describe("Keybind.parse", () => {
  149. test("should parse simple key", () => {
  150. const result = Keybind.parse("f")
  151. expect(result).toEqual([
  152. {
  153. ctrl: false,
  154. meta: false,
  155. shift: false,
  156. leader: false,
  157. name: "f",
  158. },
  159. ])
  160. })
  161. test("should parse leader key syntax", () => {
  162. const result = Keybind.parse("<leader>f")
  163. expect(result).toEqual([
  164. {
  165. ctrl: false,
  166. meta: false,
  167. shift: false,
  168. leader: true,
  169. name: "f",
  170. },
  171. ])
  172. })
  173. test("should parse ctrl modifier", () => {
  174. const result = Keybind.parse("ctrl+x")
  175. expect(result).toEqual([
  176. {
  177. ctrl: true,
  178. meta: false,
  179. shift: false,
  180. leader: false,
  181. name: "x",
  182. },
  183. ])
  184. })
  185. test("should parse multiple modifiers", () => {
  186. const result = Keybind.parse("ctrl+alt+u")
  187. expect(result).toEqual([
  188. {
  189. ctrl: true,
  190. meta: true,
  191. shift: false,
  192. leader: false,
  193. name: "u",
  194. },
  195. ])
  196. })
  197. test("should parse shift modifier", () => {
  198. const result = Keybind.parse("shift+f2")
  199. expect(result).toEqual([
  200. {
  201. ctrl: false,
  202. meta: false,
  203. shift: true,
  204. leader: false,
  205. name: "f2",
  206. },
  207. ])
  208. })
  209. test("should parse meta/alt modifier", () => {
  210. const result = Keybind.parse("meta+g")
  211. expect(result).toEqual([
  212. {
  213. ctrl: false,
  214. meta: true,
  215. shift: false,
  216. leader: false,
  217. name: "g",
  218. },
  219. ])
  220. })
  221. test("should parse leader with modifier", () => {
  222. const result = Keybind.parse("<leader>h")
  223. expect(result).toEqual([
  224. {
  225. ctrl: false,
  226. meta: false,
  227. shift: false,
  228. leader: true,
  229. name: "h",
  230. },
  231. ])
  232. })
  233. test("should parse multiple keybinds separated by comma", () => {
  234. const result = Keybind.parse("ctrl+c,<leader>q")
  235. expect(result).toEqual([
  236. {
  237. ctrl: true,
  238. meta: false,
  239. shift: false,
  240. leader: false,
  241. name: "c",
  242. },
  243. {
  244. ctrl: false,
  245. meta: false,
  246. shift: false,
  247. leader: true,
  248. name: "q",
  249. },
  250. ])
  251. })
  252. test("should parse shift+return combination", () => {
  253. const result = Keybind.parse("shift+return")
  254. expect(result).toEqual([
  255. {
  256. ctrl: false,
  257. meta: false,
  258. shift: true,
  259. leader: false,
  260. name: "return",
  261. },
  262. ])
  263. })
  264. test("should parse ctrl+j combination", () => {
  265. const result = Keybind.parse("ctrl+j")
  266. expect(result).toEqual([
  267. {
  268. ctrl: true,
  269. meta: false,
  270. shift: false,
  271. leader: false,
  272. name: "j",
  273. },
  274. ])
  275. })
  276. test("should handle 'none' value", () => {
  277. const result = Keybind.parse("none")
  278. expect(result).toEqual([])
  279. })
  280. test("should handle special keys", () => {
  281. const result = Keybind.parse("pgup")
  282. expect(result).toEqual([
  283. {
  284. ctrl: false,
  285. meta: false,
  286. shift: false,
  287. leader: false,
  288. name: "pgup",
  289. },
  290. ])
  291. })
  292. test("should handle function keys", () => {
  293. const result = Keybind.parse("f2")
  294. expect(result).toEqual([
  295. {
  296. ctrl: false,
  297. meta: false,
  298. shift: false,
  299. leader: false,
  300. name: "f2",
  301. },
  302. ])
  303. })
  304. test("should handle complex multi-modifier combination", () => {
  305. const result = Keybind.parse("ctrl+alt+g")
  306. expect(result).toEqual([
  307. {
  308. ctrl: true,
  309. meta: true,
  310. shift: false,
  311. leader: false,
  312. name: "g",
  313. },
  314. ])
  315. })
  316. test("should be case insensitive", () => {
  317. const result = Keybind.parse("CTRL+X")
  318. expect(result).toEqual([
  319. {
  320. ctrl: true,
  321. meta: false,
  322. shift: false,
  323. leader: false,
  324. name: "x",
  325. },
  326. ])
  327. })
  328. test("should parse super modifier", () => {
  329. const result = Keybind.parse("super+z")
  330. expect(result).toEqual([
  331. {
  332. ctrl: false,
  333. meta: false,
  334. shift: false,
  335. super: true,
  336. leader: false,
  337. name: "z",
  338. },
  339. ])
  340. })
  341. test("should parse super with shift modifier", () => {
  342. const result = Keybind.parse("super+shift+z")
  343. expect(result).toEqual([
  344. {
  345. ctrl: false,
  346. meta: false,
  347. shift: true,
  348. super: true,
  349. leader: false,
  350. name: "z",
  351. },
  352. ])
  353. })
  354. test("should parse multiple keybinds with super", () => {
  355. const result = Keybind.parse("ctrl+-,super+z")
  356. expect(result).toEqual([
  357. {
  358. ctrl: true,
  359. meta: false,
  360. shift: false,
  361. leader: false,
  362. name: "-",
  363. },
  364. {
  365. ctrl: false,
  366. meta: false,
  367. shift: false,
  368. super: true,
  369. leader: false,
  370. name: "z",
  371. },
  372. ])
  373. })
  374. })