]>
Commit | Line | Data |
---|---|---|
2b5f94fa | 1 | const expect = chai.expect; |
f00b6fb6 | 2 | |
c1e2785f | 3 | import Keyboard from '../core/input/keyboard.js'; |
59ef2916 | 4 | import * as browser from '../core/util/browser.js'; |
099eb856 | 5 | |
2c5491e1 | 6 | describe('Key Event Handling', function () { |
f00b6fb6 | 7 | "use strict"; |
f00b6fb6 | 8 | |
f7363fd2 PO |
9 | // The real KeyboardEvent constructor might not work everywhere we |
10 | // want to run these tests | |
11 | function keyevent(typeArg, KeyboardEventInit) { | |
2b5f94fa JD |
12 | const e = { type: typeArg }; |
13 | for (let key in KeyboardEventInit) { | |
f7363fd2 PO |
14 | e[key] = KeyboardEventInit[key]; |
15 | } | |
16 | e.stopPropagation = sinon.spy(); | |
17 | e.preventDefault = sinon.spy(); | |
18 | return e; | |
8727f598 | 19 | } |
f00b6fb6 | 20 | |
2c5491e1 PO |
21 | describe('Decode Keyboard Events', function () { |
22 | it('should decode keydown events', function (done) { | |
59ef2916 | 23 | if (browser.isIE() || browser.isEdge()) this.skip(); |
2b5f94fa | 24 | const kbd = new Keyboard(document); |
651c23ec | 25 | kbd.onkeyevent = (keysym, code, down) => { |
f7363fd2 PO |
26 | expect(keysym).to.be.equal(0x61); |
27 | expect(code).to.be.equal('KeyA'); | |
28 | expect(down).to.be.equal(true); | |
29 | done(); | |
747b4623 | 30 | }; |
f7363fd2 PO |
31 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); |
32 | }); | |
2c5491e1 | 33 | it('should decode keyup events', function (done) { |
59ef2916 | 34 | if (browser.isIE() || browser.isEdge()) this.skip(); |
2b5f94fa JD |
35 | let calls = 0; |
36 | const kbd = new Keyboard(document); | |
651c23ec | 37 | kbd.onkeyevent = (keysym, code, down) => { |
f7363fd2 PO |
38 | expect(keysym).to.be.equal(0x61); |
39 | expect(code).to.be.equal('KeyA'); | |
40 | if (calls++ === 1) { | |
41 | expect(down).to.be.equal(false); | |
42 | done(); | |
43 | } | |
747b4623 | 44 | }; |
f7363fd2 PO |
45 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); |
46 | kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'})); | |
47 | }); | |
48 | ||
2c5491e1 PO |
49 | describe('Legacy keypress Events', function () { |
50 | it('should wait for keypress when needed', function () { | |
2b5f94fa | 51 | const kbd = new Keyboard(document); |
747b4623 | 52 | kbd.onkeyevent = sinon.spy(); |
f7363fd2 | 53 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41})); |
747b4623 | 54 | expect(kbd.onkeyevent).to.not.have.been.called; |
f7363fd2 | 55 | }); |
2c5491e1 | 56 | it('should decode keypress events', function (done) { |
2b5f94fa | 57 | const kbd = new Keyboard(document); |
651c23ec | 58 | kbd.onkeyevent = (keysym, code, down) => { |
f7363fd2 PO |
59 | expect(keysym).to.be.equal(0x61); |
60 | expect(code).to.be.equal('KeyA'); | |
61 | expect(down).to.be.equal(true); | |
62 | done(); | |
747b4623 | 63 | }; |
f7363fd2 PO |
64 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41})); |
65 | kbd._handleKeyPress(keyevent('keypress', {code: 'KeyA', charCode: 0x61})); | |
f00b6fb6 | 66 | }); |
2c5491e1 | 67 | it('should ignore keypress with different code', function () { |
2b5f94fa | 68 | const kbd = new Keyboard(document); |
747b4623 | 69 | kbd.onkeyevent = sinon.spy(); |
9fce233d PO |
70 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41})); |
71 | kbd._handleKeyPress(keyevent('keypress', {code: 'KeyB', charCode: 0x61})); | |
747b4623 | 72 | expect(kbd.onkeyevent).to.not.have.been.called; |
9fce233d | 73 | }); |
2c5491e1 | 74 | it('should handle keypress with missing code', function (done) { |
2b5f94fa | 75 | const kbd = new Keyboard(document); |
651c23ec | 76 | kbd.onkeyevent = (keysym, code, down) => { |
9fce233d PO |
77 | expect(keysym).to.be.equal(0x61); |
78 | expect(code).to.be.equal('KeyA'); | |
79 | expect(down).to.be.equal(true); | |
80 | done(); | |
747b4623 | 81 | }; |
9fce233d PO |
82 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41})); |
83 | kbd._handleKeyPress(keyevent('keypress', {charCode: 0x61})); | |
84 | }); | |
2c5491e1 | 85 | it('should guess key if no keypress and numeric key', function (done) { |
2b5f94fa | 86 | const kbd = new Keyboard(document); |
651c23ec | 87 | kbd.onkeyevent = (keysym, code, down) => { |
7cac5c8e PO |
88 | expect(keysym).to.be.equal(0x32); |
89 | expect(code).to.be.equal('Digit2'); | |
90 | expect(down).to.be.equal(true); | |
91 | done(); | |
747b4623 | 92 | }; |
7cac5c8e PO |
93 | kbd._handleKeyDown(keyevent('keydown', {code: 'Digit2', keyCode: 0x32})); |
94 | }); | |
2c5491e1 | 95 | it('should guess key if no keypress and alpha key', function (done) { |
2b5f94fa | 96 | const kbd = new Keyboard(document); |
651c23ec | 97 | kbd.onkeyevent = (keysym, code, down) => { |
7cac5c8e PO |
98 | expect(keysym).to.be.equal(0x61); |
99 | expect(code).to.be.equal('KeyA'); | |
100 | expect(down).to.be.equal(true); | |
101 | done(); | |
747b4623 | 102 | }; |
7cac5c8e PO |
103 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41, shiftKey: false})); |
104 | }); | |
2c5491e1 | 105 | it('should guess key if no keypress and alpha key (with shift)', function (done) { |
2b5f94fa | 106 | const kbd = new Keyboard(document); |
651c23ec | 107 | kbd.onkeyevent = (keysym, code, down) => { |
7cac5c8e PO |
108 | expect(keysym).to.be.equal(0x41); |
109 | expect(code).to.be.equal('KeyA'); | |
110 | expect(down).to.be.equal(true); | |
111 | done(); | |
747b4623 | 112 | }; |
7cac5c8e PO |
113 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41, shiftKey: true})); |
114 | }); | |
2c5491e1 | 115 | it('should not guess key if no keypress and unknown key', function (done) { |
2b5f94fa | 116 | const kbd = new Keyboard(document); |
651c23ec | 117 | kbd.onkeyevent = (keysym, code, down) => { |
7cac5c8e PO |
118 | expect(keysym).to.be.equal(0); |
119 | expect(code).to.be.equal('KeyA'); | |
120 | expect(down).to.be.equal(true); | |
121 | done(); | |
747b4623 | 122 | }; |
7cac5c8e PO |
123 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x09})); |
124 | }); | |
f00b6fb6 | 125 | }); |
f00b6fb6 | 126 | |
2c5491e1 | 127 | describe('suppress the right events at the right time', function () { |
099eb856 | 128 | beforeEach(function () { |
59ef2916 | 129 | if (browser.isIE() || browser.isEdge()) this.skip(); |
099eb856 | 130 | }); |
2c5491e1 | 131 | it('should suppress anything with a valid key', function () { |
2b5f94fa JD |
132 | const kbd = new Keyboard(document, {}); |
133 | const evt1 = keyevent('keydown', {code: 'KeyA', key: 'a'}); | |
8727f598 JD |
134 | kbd._handleKeyDown(evt1); |
135 | expect(evt1.preventDefault).to.have.been.called; | |
2b5f94fa | 136 | const evt2 = keyevent('keyup', {code: 'KeyA', key: 'a'}); |
8727f598 JD |
137 | kbd._handleKeyUp(evt2); |
138 | expect(evt2.preventDefault).to.have.been.called; | |
f7363fd2 | 139 | }); |
2c5491e1 | 140 | it('should not suppress keys without key', function () { |
2b5f94fa JD |
141 | const kbd = new Keyboard(document, {}); |
142 | const evt = keyevent('keydown', {code: 'KeyA', keyCode: 0x41}); | |
f7363fd2 PO |
143 | kbd._handleKeyDown(evt); |
144 | expect(evt.preventDefault).to.not.have.been.called; | |
145 | }); | |
2c5491e1 | 146 | it('should suppress the following keypress event', function () { |
2b5f94fa JD |
147 | const kbd = new Keyboard(document, {}); |
148 | const evt1 = keyevent('keydown', {code: 'KeyA', keyCode: 0x41}); | |
8727f598 | 149 | kbd._handleKeyDown(evt1); |
2b5f94fa | 150 | const evt2 = keyevent('keypress', {code: 'KeyA', charCode: 0x41}); |
8727f598 JD |
151 | kbd._handleKeyPress(evt2); |
152 | expect(evt2.preventDefault).to.have.been.called; | |
f00b6fb6 | 153 | }); |
154 | }); | |
f00b6fb6 | 155 | }); |
156 | ||
2c5491e1 PO |
157 | describe('Fake keyup', function () { |
158 | it('should fake keyup events for virtual keyboards', function (done) { | |
59ef2916 | 159 | if (browser.isIE() || browser.isEdge()) this.skip(); |
2b5f94fa JD |
160 | let count = 0; |
161 | const kbd = new Keyboard(document); | |
651c23ec | 162 | kbd.onkeyevent = (keysym, code, down) => { |
9e99ce12 PO |
163 | switch (count++) { |
164 | case 0: | |
165 | expect(keysym).to.be.equal(0x61); | |
166 | expect(code).to.be.equal('Unidentified'); | |
167 | expect(down).to.be.equal(true); | |
168 | break; | |
169 | case 1: | |
170 | expect(keysym).to.be.equal(0x61); | |
171 | expect(code).to.be.equal('Unidentified'); | |
172 | expect(down).to.be.equal(false); | |
173 | done(); | |
174 | } | |
747b4623 | 175 | }; |
9e99ce12 PO |
176 | kbd._handleKeyDown(keyevent('keydown', {code: 'Unidentified', key: 'a'})); |
177 | }); | |
9e99ce12 PO |
178 | }); |
179 | ||
2c5491e1 | 180 | describe('Track Key State', function () { |
099eb856 | 181 | beforeEach(function () { |
59ef2916 | 182 | if (browser.isIE() || browser.isEdge()) this.skip(); |
099eb856 | 183 | }); |
2c5491e1 | 184 | it('should send release using the same keysym as the press', function (done) { |
2b5f94fa | 185 | const kbd = new Keyboard(document); |
651c23ec | 186 | kbd.onkeyevent = (keysym, code, down) => { |
f7363fd2 PO |
187 | expect(keysym).to.be.equal(0x61); |
188 | expect(code).to.be.equal('KeyA'); | |
189 | if (!down) { | |
190 | done(); | |
191 | } | |
747b4623 | 192 | }; |
f7363fd2 PO |
193 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); |
194 | kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'b'})); | |
f00b6fb6 | 195 | }); |
2c5491e1 | 196 | it('should send the same keysym for multiple presses', function () { |
2b5f94fa JD |
197 | let count = 0; |
198 | const kbd = new Keyboard(document); | |
651c23ec | 199 | kbd.onkeyevent = (keysym, code, down) => { |
ae820533 PO |
200 | expect(keysym).to.be.equal(0x61); |
201 | expect(code).to.be.equal('KeyA'); | |
202 | expect(down).to.be.equal(true); | |
203 | count++; | |
747b4623 | 204 | }; |
ae820533 PO |
205 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); |
206 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'b'})); | |
207 | expect(count).to.be.equal(2); | |
208 | }); | |
2c5491e1 | 209 | it('should do nothing on keyup events if no keys are down', function () { |
2b5f94fa | 210 | const kbd = new Keyboard(document); |
747b4623 | 211 | kbd.onkeyevent = sinon.spy(); |
f7363fd2 | 212 | kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'})); |
747b4623 | 213 | expect(kbd.onkeyevent).to.not.have.been.called; |
f7363fd2 | 214 | }); |
7e79dfe4 | 215 | |
2c5491e1 PO |
216 | describe('Legacy Events', function () { |
217 | it('should track keys using keyCode if no code', function (done) { | |
2b5f94fa | 218 | const kbd = new Keyboard(document); |
651c23ec | 219 | kbd.onkeyevent = (keysym, code, down) => { |
7e79dfe4 PO |
220 | expect(keysym).to.be.equal(0x61); |
221 | expect(code).to.be.equal('Platform65'); | |
222 | if (!down) { | |
223 | done(); | |
224 | } | |
747b4623 | 225 | }; |
7e79dfe4 PO |
226 | kbd._handleKeyDown(keyevent('keydown', {keyCode: 65, key: 'a'})); |
227 | kbd._handleKeyUp(keyevent('keyup', {keyCode: 65, key: 'b'})); | |
228 | }); | |
2c5491e1 | 229 | it('should ignore compositing code', function () { |
2b5f94fa | 230 | const kbd = new Keyboard(document); |
651c23ec | 231 | kbd.onkeyevent = (keysym, code, down) => { |
4093c37f PO |
232 | expect(keysym).to.be.equal(0x61); |
233 | expect(code).to.be.equal('Unidentified'); | |
747b4623 | 234 | }; |
4093c37f PO |
235 | kbd._handleKeyDown(keyevent('keydown', {keyCode: 229, key: 'a'})); |
236 | }); | |
2c5491e1 | 237 | it('should track keys using keyIdentifier if no code', function (done) { |
2b5f94fa | 238 | const kbd = new Keyboard(document); |
651c23ec | 239 | kbd.onkeyevent = (keysym, code, down) => { |
7e79dfe4 PO |
240 | expect(keysym).to.be.equal(0x61); |
241 | expect(code).to.be.equal('Platform65'); | |
242 | if (!down) { | |
243 | done(); | |
244 | } | |
747b4623 | 245 | }; |
7e79dfe4 PO |
246 | kbd._handleKeyDown(keyevent('keydown', {keyIdentifier: 'U+0041', key: 'a'})); |
247 | kbd._handleKeyUp(keyevent('keyup', {keyIdentifier: 'U+0041', key: 'b'})); | |
248 | }); | |
249 | }); | |
f7363fd2 | 250 | }); |
f00b6fb6 | 251 | |
2c5491e1 | 252 | describe('Shuffle modifiers on macOS', function () { |
2b5f94fa | 253 | let origNavigator; |
bf43c263 PO |
254 | beforeEach(function () { |
255 | // window.navigator is a protected read-only property in many | |
256 | // environments, so we need to redefine it whilst running these | |
257 | // tests. | |
258 | origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); | |
259 | if (origNavigator === undefined) { | |
260 | // Object.getOwnPropertyDescriptor() doesn't work | |
261 | // properly in any version of IE | |
262 | this.skip(); | |
263 | } | |
264 | ||
265 | Object.defineProperty(window, "navigator", {value: {}}); | |
266 | if (window.navigator.platform !== undefined) { | |
267 | // Object.defineProperty() doesn't work properly in old | |
268 | // versions of Chrome | |
269 | this.skip(); | |
270 | } | |
271 | ||
272 | window.navigator.platform = "Mac x86_64"; | |
273 | }); | |
274 | afterEach(function () { | |
eb05b45b PO |
275 | if (origNavigator !== undefined) { |
276 | Object.defineProperty(window, "navigator", origNavigator); | |
277 | } | |
bf43c263 PO |
278 | }); |
279 | ||
2c5491e1 | 280 | it('should change Alt to AltGraph', function () { |
2b5f94fa JD |
281 | let count = 0; |
282 | const kbd = new Keyboard(document); | |
651c23ec | 283 | kbd.onkeyevent = (keysym, code, down) => { |
bf43c263 PO |
284 | switch (count++) { |
285 | case 0: | |
286 | expect(keysym).to.be.equal(0xFF7E); | |
287 | expect(code).to.be.equal('AltLeft'); | |
288 | break; | |
289 | case 1: | |
290 | expect(keysym).to.be.equal(0xFE03); | |
291 | expect(code).to.be.equal('AltRight'); | |
292 | break; | |
293 | } | |
747b4623 | 294 | }; |
9782d4a3 PO |
295 | kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt', location: 1})); |
296 | kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2})); | |
bf43c263 PO |
297 | expect(count).to.be.equal(2); |
298 | }); | |
2c5491e1 | 299 | it('should change left Super to Alt', function (done) { |
2b5f94fa | 300 | const kbd = new Keyboard(document); |
651c23ec | 301 | kbd.onkeyevent = (keysym, code, down) => { |
bf43c263 PO |
302 | expect(keysym).to.be.equal(0xFFE9); |
303 | expect(code).to.be.equal('MetaLeft'); | |
304 | done(); | |
747b4623 | 305 | }; |
9782d4a3 | 306 | kbd._handleKeyDown(keyevent('keydown', {code: 'MetaLeft', key: 'Meta', location: 1})); |
bf43c263 | 307 | }); |
2c5491e1 | 308 | it('should change right Super to left Super', function (done) { |
2b5f94fa | 309 | const kbd = new Keyboard(document); |
651c23ec | 310 | kbd.onkeyevent = (keysym, code, down) => { |
bf43c263 PO |
311 | expect(keysym).to.be.equal(0xFFEB); |
312 | expect(code).to.be.equal('MetaRight'); | |
313 | done(); | |
747b4623 | 314 | }; |
9782d4a3 | 315 | kbd._handleKeyDown(keyevent('keydown', {code: 'MetaRight', key: 'Meta', location: 2})); |
bf43c263 PO |
316 | }); |
317 | }); | |
318 | ||
a6304f91 AT |
319 | describe('Caps Lock on iOS and macOS', function () { |
320 | let origNavigator; | |
321 | beforeEach(function () { | |
322 | // window.navigator is a protected read-only property in many | |
323 | // environments, so we need to redefine it whilst running these | |
324 | // tests. | |
325 | origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); | |
326 | if (origNavigator === undefined) { | |
327 | // Object.getOwnPropertyDescriptor() doesn't work | |
328 | // properly in any version of IE | |
329 | this.skip(); | |
330 | } | |
331 | ||
332 | Object.defineProperty(window, "navigator", {value: {}}); | |
333 | if (window.navigator.platform !== undefined) { | |
334 | // Object.defineProperty() doesn't work properly in old | |
335 | // versions of Chrome | |
336 | this.skip(); | |
337 | } | |
338 | }); | |
339 | ||
340 | afterEach(function () { | |
eb05b45b PO |
341 | if (origNavigator !== undefined) { |
342 | Object.defineProperty(window, "navigator", origNavigator); | |
343 | } | |
a6304f91 AT |
344 | }); |
345 | ||
346 | it('should toggle caps lock on key press on iOS', function (done) { | |
347 | window.navigator.platform = "iPad"; | |
348 | const kbd = new Keyboard(document); | |
349 | kbd.onkeyevent = sinon.spy(); | |
350 | kbd._handleKeyDown(keyevent('keydown', {code: 'CapsLock', key: 'CapsLock'})); | |
351 | ||
352 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
353 | expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); | |
354 | expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); | |
355 | done(); | |
356 | }); | |
357 | ||
358 | it('should toggle caps lock on key press on mac', function (done) { | |
359 | window.navigator.platform = "Mac"; | |
360 | const kbd = new Keyboard(document); | |
361 | kbd.onkeyevent = sinon.spy(); | |
362 | kbd._handleKeyDown(keyevent('keydown', {code: 'CapsLock', key: 'CapsLock'})); | |
363 | ||
364 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
365 | expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); | |
366 | expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); | |
367 | done(); | |
368 | }); | |
369 | ||
370 | it('should toggle caps lock on key release on iOS', function (done) { | |
371 | window.navigator.platform = "iPad"; | |
372 | const kbd = new Keyboard(document); | |
373 | kbd.onkeyevent = sinon.spy(); | |
374 | kbd._handleKeyUp(keyevent('keyup', {code: 'CapsLock', key: 'CapsLock'})); | |
375 | ||
376 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
377 | expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); | |
378 | expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); | |
379 | done(); | |
380 | }); | |
381 | ||
382 | it('should toggle caps lock on key release on mac', function (done) { | |
383 | window.navigator.platform = "Mac"; | |
384 | const kbd = new Keyboard(document); | |
385 | kbd.onkeyevent = sinon.spy(); | |
386 | kbd._handleKeyUp(keyevent('keyup', {code: 'CapsLock', key: 'CapsLock'})); | |
387 | ||
388 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
389 | expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); | |
390 | expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); | |
391 | done(); | |
392 | }); | |
393 | }); | |
394 | ||
2c5491e1 | 395 | describe('Escape AltGraph on Windows', function () { |
2b5f94fa | 396 | let origNavigator; |
f7363fd2 PO |
397 | beforeEach(function () { |
398 | // window.navigator is a protected read-only property in many | |
399 | // environments, so we need to redefine it whilst running these | |
400 | // tests. | |
401 | origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); | |
402 | if (origNavigator === undefined) { | |
403 | // Object.getOwnPropertyDescriptor() doesn't work | |
404 | // properly in any version of IE | |
405 | this.skip(); | |
406 | } | |
407 | ||
408 | Object.defineProperty(window, "navigator", {value: {}}); | |
409 | if (window.navigator.platform !== undefined) { | |
410 | // Object.defineProperty() doesn't work properly in old | |
411 | // versions of Chrome | |
412 | this.skip(); | |
413 | } | |
414 | ||
415 | window.navigator.platform = "Windows x86_64"; | |
b22c9ef9 PO |
416 | |
417 | this.clock = sinon.useFakeTimers(); | |
f7363fd2 PO |
418 | }); |
419 | afterEach(function () { | |
eb05b45b PO |
420 | if (origNavigator !== undefined) { |
421 | Object.defineProperty(window, "navigator", origNavigator); | |
422 | } | |
423 | if (this.clock !== undefined) { | |
424 | this.clock.restore(); | |
425 | } | |
f7363fd2 PO |
426 | }); |
427 | ||
b22c9ef9 | 428 | it('should supress ControlLeft until it knows if it is AltGr', function () { |
2b5f94fa | 429 | const kbd = new Keyboard(document); |
b22c9ef9 | 430 | kbd.onkeyevent = sinon.spy(); |
9782d4a3 | 431 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); |
b22c9ef9 | 432 | expect(kbd.onkeyevent).to.not.have.been.called; |
f7363fd2 | 433 | }); |
b22c9ef9 PO |
434 | |
435 | it('should not trigger on repeating ControlLeft', function () { | |
2b5f94fa | 436 | const kbd = new Keyboard(document); |
b22c9ef9 | 437 | kbd.onkeyevent = sinon.spy(); |
9782d4a3 | 438 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); |
b22c9ef9 PO |
439 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); |
440 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
441 | expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true); | |
442 | expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffe3, "ControlLeft", true); | |
f7363fd2 | 443 | }); |
b22c9ef9 PO |
444 | |
445 | it('should not supress ControlRight', function () { | |
2b5f94fa | 446 | const kbd = new Keyboard(document); |
b22c9ef9 PO |
447 | kbd.onkeyevent = sinon.spy(); |
448 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlRight', key: 'Control', location: 2})); | |
449 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
450 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe4, "ControlRight", true); | |
451 | }); | |
452 | ||
453 | it('should release ControlLeft after 100 ms', function () { | |
2b5f94fa | 454 | const kbd = new Keyboard(document); |
b22c9ef9 | 455 | kbd.onkeyevent = sinon.spy(); |
9782d4a3 | 456 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); |
b22c9ef9 PO |
457 | expect(kbd.onkeyevent).to.not.have.been.called; |
458 | this.clock.tick(100); | |
459 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
460 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe3, "ControlLeft", true); | |
461 | }); | |
462 | ||
463 | it('should release ControlLeft on other key press', function () { | |
2b5f94fa | 464 | const kbd = new Keyboard(document); |
b22c9ef9 PO |
465 | kbd.onkeyevent = sinon.spy(); |
466 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); | |
467 | expect(kbd.onkeyevent).to.not.have.been.called; | |
468 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); | |
469 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
470 | expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true); | |
471 | expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0x61, "KeyA", true); | |
472 | ||
473 | // Check that the timer is properly dead | |
c9765e50 | 474 | kbd.onkeyevent.resetHistory(); |
b22c9ef9 PO |
475 | this.clock.tick(100); |
476 | expect(kbd.onkeyevent).to.not.have.been.called; | |
477 | }); | |
478 | ||
479 | it('should release ControlLeft on other key release', function () { | |
2b5f94fa | 480 | const kbd = new Keyboard(document); |
b22c9ef9 PO |
481 | kbd.onkeyevent = sinon.spy(); |
482 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); | |
9782d4a3 | 483 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); |
b22c9ef9 PO |
484 | expect(kbd.onkeyevent).to.have.been.calledOnce; |
485 | expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0x61, "KeyA", true); | |
486 | kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'})); | |
487 | expect(kbd.onkeyevent).to.have.been.calledThrice; | |
488 | expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffe3, "ControlLeft", true); | |
489 | expect(kbd.onkeyevent.thirdCall).to.have.been.calledWith(0x61, "KeyA", false); | |
490 | ||
491 | // Check that the timer is properly dead | |
c9765e50 | 492 | kbd.onkeyevent.resetHistory(); |
b22c9ef9 PO |
493 | this.clock.tick(100); |
494 | expect(kbd.onkeyevent).to.not.have.been.called; | |
495 | }); | |
496 | ||
497 | it('should generate AltGraph for quick Ctrl+Alt sequence', function () { | |
2b5f94fa | 498 | const kbd = new Keyboard(document); |
b22c9ef9 PO |
499 | kbd.onkeyevent = sinon.spy(); |
500 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1, timeStamp: Date.now()})); | |
501 | this.clock.tick(20); | |
502 | kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2, timeStamp: Date.now()})); | |
503 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
504 | expect(kbd.onkeyevent).to.have.been.calledWith(0xfe03, 'AltRight', true); | |
505 | ||
506 | // Check that the timer is properly dead | |
c9765e50 | 507 | kbd.onkeyevent.resetHistory(); |
b22c9ef9 PO |
508 | this.clock.tick(100); |
509 | expect(kbd.onkeyevent).to.not.have.been.called; | |
510 | }); | |
511 | ||
512 | it('should generate Ctrl, Alt for slow Ctrl+Alt sequence', function () { | |
2b5f94fa | 513 | const kbd = new Keyboard(document); |
b22c9ef9 PO |
514 | kbd.onkeyevent = sinon.spy(); |
515 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1, timeStamp: Date.now()})); | |
516 | this.clock.tick(60); | |
517 | kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2, timeStamp: Date.now()})); | |
518 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
519 | expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true); | |
520 | expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffea, "AltRight", true); | |
521 | ||
522 | // Check that the timer is properly dead | |
c9765e50 | 523 | kbd.onkeyevent.resetHistory(); |
b22c9ef9 PO |
524 | this.clock.tick(100); |
525 | expect(kbd.onkeyevent).to.not.have.been.called; | |
526 | }); | |
527 | ||
528 | it('should pass through single Alt', function () { | |
2b5f94fa | 529 | const kbd = new Keyboard(document); |
b22c9ef9 PO |
530 | kbd.onkeyevent = sinon.spy(); |
531 | kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2})); | |
532 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
533 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffea, 'AltRight', true); | |
534 | }); | |
535 | ||
536 | it('should pass through single AltGr', function () { | |
2b5f94fa | 537 | const kbd = new Keyboard(document); |
b22c9ef9 PO |
538 | kbd.onkeyevent = sinon.spy(); |
539 | kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'AltGraph', location: 2})); | |
540 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
541 | expect(kbd.onkeyevent).to.have.been.calledWith(0xfe03, 'AltRight', true); | |
f00b6fb6 | 542 | }); |
543 | }); | |
ccb511a5 PO |
544 | |
545 | describe('Missing Shift keyup on Windows', function () { | |
546 | let origNavigator; | |
547 | beforeEach(function () { | |
548 | // window.navigator is a protected read-only property in many | |
549 | // environments, so we need to redefine it whilst running these | |
550 | // tests. | |
551 | origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); | |
552 | if (origNavigator === undefined) { | |
553 | // Object.getOwnPropertyDescriptor() doesn't work | |
554 | // properly in any version of IE | |
555 | this.skip(); | |
556 | } | |
557 | ||
558 | Object.defineProperty(window, "navigator", {value: {}}); | |
559 | if (window.navigator.platform !== undefined) { | |
560 | // Object.defineProperty() doesn't work properly in old | |
561 | // versions of Chrome | |
562 | this.skip(); | |
563 | } | |
564 | ||
565 | window.navigator.platform = "Windows x86_64"; | |
566 | ||
567 | this.clock = sinon.useFakeTimers(); | |
568 | }); | |
569 | afterEach(function () { | |
eb05b45b PO |
570 | if (origNavigator !== undefined) { |
571 | Object.defineProperty(window, "navigator", origNavigator); | |
572 | } | |
573 | if (this.clock !== undefined) { | |
574 | this.clock.restore(); | |
575 | } | |
ccb511a5 PO |
576 | }); |
577 | ||
578 | it('should fake a left Shift keyup', function () { | |
579 | const kbd = new Keyboard(document); | |
580 | kbd.onkeyevent = sinon.spy(); | |
581 | ||
582 | kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftLeft', key: 'Shift', location: 1})); | |
583 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
584 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', true); | |
585 | kbd.onkeyevent.resetHistory(); | |
586 | ||
587 | kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftRight', key: 'Shift', location: 2})); | |
588 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
589 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', true); | |
590 | kbd.onkeyevent.resetHistory(); | |
591 | ||
592 | kbd._handleKeyUp(keyevent('keyup', {code: 'ShiftLeft', key: 'Shift', location: 1})); | |
593 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
594 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', false); | |
595 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', false); | |
596 | }); | |
597 | ||
598 | it('should fake a right Shift keyup', function () { | |
599 | const kbd = new Keyboard(document); | |
600 | kbd.onkeyevent = sinon.spy(); | |
601 | ||
602 | kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftLeft', key: 'Shift', location: 1})); | |
603 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
604 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', true); | |
605 | kbd.onkeyevent.resetHistory(); | |
606 | ||
607 | kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftRight', key: 'Shift', location: 2})); | |
608 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
609 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', true); | |
610 | kbd.onkeyevent.resetHistory(); | |
611 | ||
612 | kbd._handleKeyUp(keyevent('keyup', {code: 'ShiftRight', key: 'Shift', location: 2})); | |
613 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
614 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', false); | |
615 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', false); | |
616 | }); | |
617 | }); | |
f00b6fb6 | 618 | }); |