keyboard.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. /**
  2. * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. import env from '../src/env';
  6. import {
  7. keyCodes,
  8. getCode,
  9. parseKeystroke,
  10. getEnvKeystrokeText,
  11. isArrowKeyCode,
  12. isForwardArrowKeyCode,
  13. getLocalizedArrowKeyCodeDirection
  14. } from '../src/keyboard';
  15. import { expectToThrowCKEditorError } from './_utils/utils';
  16. describe( 'Keyboard', () => {
  17. describe( 'keyCodes', () => {
  18. it( 'contains numbers', () => {
  19. expect( keyCodes[ '0' ] ).to.equal( 48 );
  20. expect( keyCodes[ '9' ] ).to.equal( 57 );
  21. } );
  22. it( 'contains letters', () => {
  23. expect( keyCodes.a ).to.equal( 65 );
  24. expect( keyCodes.z ).to.equal( 90 );
  25. } );
  26. it( 'modifiers and other keys', () => {
  27. expect( keyCodes.delete ).to.equal( 46 );
  28. expect( keyCodes.ctrl ).to.equal( 0x110000 );
  29. expect( keyCodes.cmd ).to.equal( 0x110000 );
  30. expect( keyCodes.f1 ).to.equal( 112 );
  31. expect( keyCodes.f12 ).to.equal( 123 );
  32. expect( keyCodes ).to.include.keys(
  33. 'ctrl', 'cmd', 'shift', 'alt',
  34. 'arrowleft', 'arrowup', 'arrowright', 'arrowdown',
  35. 'backspace', 'delete', 'enter', 'space', 'esc', 'tab',
  36. 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12'
  37. );
  38. } );
  39. } );
  40. describe( 'getCode', () => {
  41. it( 'gets code of a number', () => {
  42. expect( getCode( '0' ) ).to.equal( 48 );
  43. } );
  44. it( 'gets code of a letter', () => {
  45. expect( getCode( 'a' ) ).to.equal( 65 );
  46. } );
  47. it( 'gets code of a function key', () => {
  48. expect( getCode( 'f6' ) ).to.equal( 117 );
  49. } );
  50. it( 'is case insensitive', () => {
  51. expect( getCode( 'A' ) ).to.equal( 65 );
  52. expect( getCode( 'Ctrl' ) ).to.equal( 0x110000 );
  53. expect( getCode( 'ENTER' ) ).to.equal( 13 );
  54. } );
  55. it( 'throws when passed unknown key name', () => {
  56. expectToThrowCKEditorError( () => {
  57. getCode( 'foo' );
  58. }, 'keyboard-unknown-key', null );
  59. } );
  60. it( 'gets code of a keystroke info', () => {
  61. expect( getCode( { keyCode: 48 } ) ).to.equal( 48 );
  62. } );
  63. it( 'adds modifiers to the keystroke code', () => {
  64. expect( getCode( { keyCode: 48, altKey: true, ctrlKey: true, shiftKey: true } ) )
  65. .to.equal( 48 + 0x110000 + 0x220000 + 0x440000 );
  66. } );
  67. } );
  68. describe( 'parseKeystroke', () => {
  69. it( 'parses string', () => {
  70. expect( parseKeystroke( 'ctrl+a' ) ).to.equal( 0x110000 + 65 );
  71. } );
  72. it( 'allows spacing', () => {
  73. expect( parseKeystroke( 'ctrl + a' ) ).to.equal( 0x110000 + 65 );
  74. } );
  75. it( 'is case-insensitive', () => {
  76. expect( parseKeystroke( 'Ctrl+A' ) ).to.equal( 0x110000 + 65 );
  77. } );
  78. it( 'works with an array', () => {
  79. expect( parseKeystroke( [ 'ctrl', 'a' ] ) ).to.equal( 0x110000 + 65 );
  80. } );
  81. it( 'works with an array which contains numbers', () => {
  82. expect( parseKeystroke( [ 'shift', 33 ] ) ).to.equal( 0x220000 + 33 );
  83. } );
  84. it( 'works with two modifiers', () => {
  85. expect( parseKeystroke( 'ctrl+shift+a' ) ).to.equal( 0x110000 + 0x220000 + 65 );
  86. } );
  87. it( 'throws on unknown name', () => {
  88. expectToThrowCKEditorError( () => {
  89. parseKeystroke( 'foo' );
  90. }, 'keyboard-unknown-key', null );
  91. } );
  92. } );
  93. describe( 'getEnvKeystrokeText', () => {
  94. const initialEnvMac = env.isMac;
  95. afterEach( () => {
  96. env.isMac = initialEnvMac;
  97. } );
  98. describe( 'on Macintosh', () => {
  99. beforeEach( () => {
  100. env.isMac = true;
  101. } );
  102. it( 'replaces CTRL with ⌘', () => {
  103. expect( getEnvKeystrokeText( 'CTRL' ) ).to.equal( '⌘' );
  104. expect( getEnvKeystrokeText( 'CTRL+A' ) ).to.equal( '⌘A' );
  105. expect( getEnvKeystrokeText( 'ctrl+A' ) ).to.equal( '⌘A' );
  106. } );
  107. it( 'replaces SHIFT with ⇧', () => {
  108. expect( getEnvKeystrokeText( 'SHIFT' ) ).to.equal( '⇧' );
  109. expect( getEnvKeystrokeText( 'SHIFT+A' ) ).to.equal( '⇧A' );
  110. expect( getEnvKeystrokeText( 'shift+A' ) ).to.equal( '⇧A' );
  111. } );
  112. it( 'replaces ALT with ⌥', () => {
  113. expect( getEnvKeystrokeText( 'ALT' ) ).to.equal( '⌥' );
  114. expect( getEnvKeystrokeText( 'ALT+A' ) ).to.equal( '⌥A' );
  115. expect( getEnvKeystrokeText( 'alt+A' ) ).to.equal( '⌥A' );
  116. } );
  117. it( 'work for multiple modifiers', () => {
  118. expect( getEnvKeystrokeText( 'CTRL+SHIFT+X' ) ).to.equal( '⌘⇧X' );
  119. expect( getEnvKeystrokeText( 'ALT+SHIFT+X' ) ).to.equal( '⌥⇧X' );
  120. } );
  121. it( 'does not touch other keys', () => {
  122. expect( getEnvKeystrokeText( 'ESC+A' ) ).to.equal( 'ESC+A' );
  123. expect( getEnvKeystrokeText( 'TAB' ) ).to.equal( 'TAB' );
  124. expect( getEnvKeystrokeText( 'A' ) ).to.equal( 'A' );
  125. expect( getEnvKeystrokeText( 'A+CTRL+B' ) ).to.equal( 'A+⌘B' );
  126. } );
  127. } );
  128. describe( 'on non–Macintosh', () => {
  129. beforeEach( () => {
  130. env.isMac = false;
  131. } );
  132. it( 'does not touch anything', () => {
  133. expect( getEnvKeystrokeText( 'CTRL+A' ) ).to.equal( 'CTRL+A' );
  134. expect( getEnvKeystrokeText( 'ctrl+A' ) ).to.equal( 'ctrl+A' );
  135. expect( getEnvKeystrokeText( 'SHIFT+A' ) ).to.equal( 'SHIFT+A' );
  136. expect( getEnvKeystrokeText( 'alt+A' ) ).to.equal( 'alt+A' );
  137. expect( getEnvKeystrokeText( 'CTRL+SHIFT+A' ) ).to.equal( 'CTRL+SHIFT+A' );
  138. expect( getEnvKeystrokeText( 'A' ) ).to.equal( 'A' );
  139. } );
  140. } );
  141. } );
  142. describe( 'isArrowKeyCode()', () => {
  143. it( 'should return "true" for right arrow', () => {
  144. expect( isArrowKeyCode( keyCodes.arrowright ) ).to.be.true;
  145. } );
  146. it( 'should return "true" for left arrow', () => {
  147. expect( isArrowKeyCode( keyCodes.arrowleft ) ).to.be.true;
  148. } );
  149. it( 'should return "true" for up arrow', () => {
  150. expect( isArrowKeyCode( keyCodes.arrowup ) ).to.be.true;
  151. } );
  152. it( 'should return "true" for down arrow', () => {
  153. expect( isArrowKeyCode( keyCodes.arrowdown ) ).to.be.true;
  154. } );
  155. it( 'should return "false" for non-arrow keystrokes', () => {
  156. expect( isArrowKeyCode( keyCodes.a ) ).to.be.false;
  157. expect( isArrowKeyCode( keyCodes.ctrl ) ).to.be.false;
  158. } );
  159. } );
  160. describe( 'getLocalizedArrowKeyCodeDirection()', () => {
  161. describe( 'for a left–to–right content language direction', () => {
  162. it( 'should return "left" for left arrow', () => {
  163. expect( getLocalizedArrowKeyCodeDirection( keyCodes.arrowleft, 'ltr' ) ).to.equal( 'left' );
  164. } );
  165. it( 'should return "right" for right arrow', () => {
  166. expect( getLocalizedArrowKeyCodeDirection( keyCodes.arrowright, 'ltr' ) ).to.equal( 'right' );
  167. } );
  168. it( 'should return "up" for up arrow', () => {
  169. expect( getLocalizedArrowKeyCodeDirection( keyCodes.arrowup, 'ltr' ) ).to.equal( 'up' );
  170. } );
  171. it( 'should return "down" for down arrow', () => {
  172. expect( getLocalizedArrowKeyCodeDirection( keyCodes.arrowdown, 'ltr' ) ).to.equal( 'down' );
  173. } );
  174. } );
  175. describe( 'for a right-to-left content language direction', () => {
  176. it( 'should return "right" for left arrow', () => {
  177. expect( getLocalizedArrowKeyCodeDirection( keyCodes.arrowleft, 'rtl' ) ).to.equal( 'right' );
  178. } );
  179. it( 'should return "left" for right arrow', () => {
  180. expect( getLocalizedArrowKeyCodeDirection( keyCodes.arrowright, 'rtl' ) ).to.equal( 'left' );
  181. } );
  182. it( 'should return "up" for up arrow', () => {
  183. expect( getLocalizedArrowKeyCodeDirection( keyCodes.arrowup, 'rtl' ) ).to.equal( 'up' );
  184. } );
  185. it( 'should return "down" for down arrow', () => {
  186. expect( getLocalizedArrowKeyCodeDirection( keyCodes.arrowdown, 'rtl' ) ).to.equal( 'down' );
  187. } );
  188. } );
  189. } );
  190. describe( 'isForwardArrowKeyCode()', () => {
  191. describe( 'for a left–to–right content language direction', () => {
  192. it( 'should return "true" for down arrow', () => {
  193. expect( isForwardArrowKeyCode( keyCodes.arrowdown, 'ltr' ) ).to.be.true;
  194. } );
  195. it( 'should return "true" for right arrow', () => {
  196. expect( isForwardArrowKeyCode( keyCodes.arrowright, 'ltr' ) ).to.be.true;
  197. } );
  198. it( 'should return "false" for up arrow', () => {
  199. expect( isForwardArrowKeyCode( keyCodes.arrowup, 'ltr' ) ).to.be.false;
  200. } );
  201. it( 'should return "false" for left arrow', () => {
  202. expect( isForwardArrowKeyCode( keyCodes.arrowleft, 'ltr' ) ).to.be.false;
  203. } );
  204. } );
  205. describe( 'for a right-to-left content language direction', () => {
  206. it( 'should return "true" for down arrow', () => {
  207. expect( isForwardArrowKeyCode( keyCodes.arrowdown, 'rtl' ) ).to.be.true;
  208. } );
  209. it( 'should return "true" for left arrow', () => {
  210. expect( isForwardArrowKeyCode( keyCodes.arrowleft, 'rtl' ) ).to.be.true;
  211. } );
  212. it( 'should return "false" for up arrow', () => {
  213. expect( isForwardArrowKeyCode( keyCodes.arrowup, 'rtl' ) ).to.be.false;
  214. } );
  215. it( 'should return "false" for right arrow', () => {
  216. expect( isForwardArrowKeyCode( keyCodes.arrowright, 'rtl' ) ).to.be.false;
  217. } );
  218. } );
  219. } );
  220. } );