]> git.proxmox.com Git - mirror_novnc.git/blame - tests/test.keyboard.js
Merge branch 'camelcase' of https://github.com/samhed/noVNC
[mirror_novnc.git] / tests / test.keyboard.js
CommitLineData
2b5f94fa 1const expect = chai.expect;
f00b6fb6 2
c1e2785f 3import Keyboard from '../core/input/keyboard.js';
59ef2916 4import * as browser from '../core/util/browser.js';
099eb856 5
2c5491e1 6describe('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});