]> git.proxmox.com Git - mirror_novnc.git/blob - tests/test.keyboard.js
Make afterEach() hooks work when skipping tests
[mirror_novnc.git] / tests / test.keyboard.js
1 const expect = chai.expect;
2
3 import Keyboard from '../core/input/keyboard.js';
4 import * as browser from '../core/util/browser.js';
5
6 describe('Key Event Handling', function () {
7 "use strict";
8
9 // The real KeyboardEvent constructor might not work everywhere we
10 // want to run these tests
11 function keyevent(typeArg, KeyboardEventInit) {
12 const e = { type: typeArg };
13 for (let key in KeyboardEventInit) {
14 e[key] = KeyboardEventInit[key];
15 }
16 e.stopPropagation = sinon.spy();
17 e.preventDefault = sinon.spy();
18 return e;
19 }
20
21 describe('Decode Keyboard Events', function () {
22 it('should decode keydown events', function (done) {
23 if (browser.isIE() || browser.isEdge()) this.skip();
24 const kbd = new Keyboard(document);
25 kbd.onkeyevent = (keysym, code, down) => {
26 expect(keysym).to.be.equal(0x61);
27 expect(code).to.be.equal('KeyA');
28 expect(down).to.be.equal(true);
29 done();
30 };
31 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
32 });
33 it('should decode keyup events', function (done) {
34 if (browser.isIE() || browser.isEdge()) this.skip();
35 let calls = 0;
36 const kbd = new Keyboard(document);
37 kbd.onkeyevent = (keysym, code, down) => {
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 }
44 };
45 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
46 kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'}));
47 });
48
49 describe('Legacy keypress Events', function () {
50 it('should wait for keypress when needed', function () {
51 const kbd = new Keyboard(document);
52 kbd.onkeyevent = sinon.spy();
53 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
54 expect(kbd.onkeyevent).to.not.have.been.called;
55 });
56 it('should decode keypress events', function (done) {
57 const kbd = new Keyboard(document);
58 kbd.onkeyevent = (keysym, code, down) => {
59 expect(keysym).to.be.equal(0x61);
60 expect(code).to.be.equal('KeyA');
61 expect(down).to.be.equal(true);
62 done();
63 };
64 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
65 kbd._handleKeyPress(keyevent('keypress', {code: 'KeyA', charCode: 0x61}));
66 });
67 it('should ignore keypress with different code', function () {
68 const kbd = new Keyboard(document);
69 kbd.onkeyevent = sinon.spy();
70 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
71 kbd._handleKeyPress(keyevent('keypress', {code: 'KeyB', charCode: 0x61}));
72 expect(kbd.onkeyevent).to.not.have.been.called;
73 });
74 it('should handle keypress with missing code', function (done) {
75 const kbd = new Keyboard(document);
76 kbd.onkeyevent = (keysym, code, down) => {
77 expect(keysym).to.be.equal(0x61);
78 expect(code).to.be.equal('KeyA');
79 expect(down).to.be.equal(true);
80 done();
81 };
82 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
83 kbd._handleKeyPress(keyevent('keypress', {charCode: 0x61}));
84 });
85 it('should guess key if no keypress and numeric key', function (done) {
86 const kbd = new Keyboard(document);
87 kbd.onkeyevent = (keysym, code, down) => {
88 expect(keysym).to.be.equal(0x32);
89 expect(code).to.be.equal('Digit2');
90 expect(down).to.be.equal(true);
91 done();
92 };
93 kbd._handleKeyDown(keyevent('keydown', {code: 'Digit2', keyCode: 0x32}));
94 });
95 it('should guess key if no keypress and alpha key', function (done) {
96 const kbd = new Keyboard(document);
97 kbd.onkeyevent = (keysym, code, down) => {
98 expect(keysym).to.be.equal(0x61);
99 expect(code).to.be.equal('KeyA');
100 expect(down).to.be.equal(true);
101 done();
102 };
103 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41, shiftKey: false}));
104 });
105 it('should guess key if no keypress and alpha key (with shift)', function (done) {
106 const kbd = new Keyboard(document);
107 kbd.onkeyevent = (keysym, code, down) => {
108 expect(keysym).to.be.equal(0x41);
109 expect(code).to.be.equal('KeyA');
110 expect(down).to.be.equal(true);
111 done();
112 };
113 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41, shiftKey: true}));
114 });
115 it('should not guess key if no keypress and unknown key', function (done) {
116 const kbd = new Keyboard(document);
117 kbd.onkeyevent = (keysym, code, down) => {
118 expect(keysym).to.be.equal(0);
119 expect(code).to.be.equal('KeyA');
120 expect(down).to.be.equal(true);
121 done();
122 };
123 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x09}));
124 });
125 });
126
127 describe('suppress the right events at the right time', function () {
128 beforeEach(function () {
129 if (browser.isIE() || browser.isEdge()) this.skip();
130 });
131 it('should suppress anything with a valid key', function () {
132 const kbd = new Keyboard(document, {});
133 const evt1 = keyevent('keydown', {code: 'KeyA', key: 'a'});
134 kbd._handleKeyDown(evt1);
135 expect(evt1.preventDefault).to.have.been.called;
136 const evt2 = keyevent('keyup', {code: 'KeyA', key: 'a'});
137 kbd._handleKeyUp(evt2);
138 expect(evt2.preventDefault).to.have.been.called;
139 });
140 it('should not suppress keys without key', function () {
141 const kbd = new Keyboard(document, {});
142 const evt = keyevent('keydown', {code: 'KeyA', keyCode: 0x41});
143 kbd._handleKeyDown(evt);
144 expect(evt.preventDefault).to.not.have.been.called;
145 });
146 it('should suppress the following keypress event', function () {
147 const kbd = new Keyboard(document, {});
148 const evt1 = keyevent('keydown', {code: 'KeyA', keyCode: 0x41});
149 kbd._handleKeyDown(evt1);
150 const evt2 = keyevent('keypress', {code: 'KeyA', charCode: 0x41});
151 kbd._handleKeyPress(evt2);
152 expect(evt2.preventDefault).to.have.been.called;
153 });
154 });
155 });
156
157 describe('Fake keyup', function () {
158 it('should fake keyup events for virtual keyboards', function (done) {
159 if (browser.isIE() || browser.isEdge()) this.skip();
160 let count = 0;
161 const kbd = new Keyboard(document);
162 kbd.onkeyevent = (keysym, code, down) => {
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 }
175 };
176 kbd._handleKeyDown(keyevent('keydown', {code: 'Unidentified', key: 'a'}));
177 });
178 });
179
180 describe('Track Key State', function () {
181 beforeEach(function () {
182 if (browser.isIE() || browser.isEdge()) this.skip();
183 });
184 it('should send release using the same keysym as the press', function (done) {
185 const kbd = new Keyboard(document);
186 kbd.onkeyevent = (keysym, code, down) => {
187 expect(keysym).to.be.equal(0x61);
188 expect(code).to.be.equal('KeyA');
189 if (!down) {
190 done();
191 }
192 };
193 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
194 kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'b'}));
195 });
196 it('should send the same keysym for multiple presses', function () {
197 let count = 0;
198 const kbd = new Keyboard(document);
199 kbd.onkeyevent = (keysym, code, down) => {
200 expect(keysym).to.be.equal(0x61);
201 expect(code).to.be.equal('KeyA');
202 expect(down).to.be.equal(true);
203 count++;
204 };
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 });
209 it('should do nothing on keyup events if no keys are down', function () {
210 const kbd = new Keyboard(document);
211 kbd.onkeyevent = sinon.spy();
212 kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'}));
213 expect(kbd.onkeyevent).to.not.have.been.called;
214 });
215
216 describe('Legacy Events', function () {
217 it('should track keys using keyCode if no code', function (done) {
218 const kbd = new Keyboard(document);
219 kbd.onkeyevent = (keysym, code, down) => {
220 expect(keysym).to.be.equal(0x61);
221 expect(code).to.be.equal('Platform65');
222 if (!down) {
223 done();
224 }
225 };
226 kbd._handleKeyDown(keyevent('keydown', {keyCode: 65, key: 'a'}));
227 kbd._handleKeyUp(keyevent('keyup', {keyCode: 65, key: 'b'}));
228 });
229 it('should ignore compositing code', function () {
230 const kbd = new Keyboard(document);
231 kbd.onkeyevent = (keysym, code, down) => {
232 expect(keysym).to.be.equal(0x61);
233 expect(code).to.be.equal('Unidentified');
234 };
235 kbd._handleKeyDown(keyevent('keydown', {keyCode: 229, key: 'a'}));
236 });
237 it('should track keys using keyIdentifier if no code', function (done) {
238 const kbd = new Keyboard(document);
239 kbd.onkeyevent = (keysym, code, down) => {
240 expect(keysym).to.be.equal(0x61);
241 expect(code).to.be.equal('Platform65');
242 if (!down) {
243 done();
244 }
245 };
246 kbd._handleKeyDown(keyevent('keydown', {keyIdentifier: 'U+0041', key: 'a'}));
247 kbd._handleKeyUp(keyevent('keyup', {keyIdentifier: 'U+0041', key: 'b'}));
248 });
249 });
250 });
251
252 describe('Shuffle modifiers on macOS', function () {
253 let origNavigator;
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 () {
275 if (origNavigator !== undefined) {
276 Object.defineProperty(window, "navigator", origNavigator);
277 }
278 });
279
280 it('should change Alt to AltGraph', function () {
281 let count = 0;
282 const kbd = new Keyboard(document);
283 kbd.onkeyevent = (keysym, code, down) => {
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 }
294 };
295 kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt', location: 1}));
296 kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2}));
297 expect(count).to.be.equal(2);
298 });
299 it('should change left Super to Alt', function (done) {
300 const kbd = new Keyboard(document);
301 kbd.onkeyevent = (keysym, code, down) => {
302 expect(keysym).to.be.equal(0xFFE9);
303 expect(code).to.be.equal('MetaLeft');
304 done();
305 };
306 kbd._handleKeyDown(keyevent('keydown', {code: 'MetaLeft', key: 'Meta', location: 1}));
307 });
308 it('should change right Super to left Super', function (done) {
309 const kbd = new Keyboard(document);
310 kbd.onkeyevent = (keysym, code, down) => {
311 expect(keysym).to.be.equal(0xFFEB);
312 expect(code).to.be.equal('MetaRight');
313 done();
314 };
315 kbd._handleKeyDown(keyevent('keydown', {code: 'MetaRight', key: 'Meta', location: 2}));
316 });
317 });
318
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 () {
341 if (origNavigator !== undefined) {
342 Object.defineProperty(window, "navigator", origNavigator);
343 }
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
395 describe('Escape AltGraph on Windows', function () {
396 let origNavigator;
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";
416
417 this.clock = sinon.useFakeTimers();
418 });
419 afterEach(function () {
420 if (origNavigator !== undefined) {
421 Object.defineProperty(window, "navigator", origNavigator);
422 }
423 if (this.clock !== undefined) {
424 this.clock.restore();
425 }
426 });
427
428 it('should supress ControlLeft until it knows if it is AltGr', function () {
429 const kbd = new Keyboard(document);
430 kbd.onkeyevent = sinon.spy();
431 kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
432 expect(kbd.onkeyevent).to.not.have.been.called;
433 });
434
435 it('should not trigger on repeating ControlLeft', function () {
436 const kbd = new Keyboard(document);
437 kbd.onkeyevent = sinon.spy();
438 kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
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);
443 });
444
445 it('should not supress ControlRight', function () {
446 const kbd = new Keyboard(document);
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 () {
454 const kbd = new Keyboard(document);
455 kbd.onkeyevent = sinon.spy();
456 kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
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 () {
464 const kbd = new Keyboard(document);
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
474 kbd.onkeyevent.resetHistory();
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 () {
480 const kbd = new Keyboard(document);
481 kbd.onkeyevent = sinon.spy();
482 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
483 kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
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
492 kbd.onkeyevent.resetHistory();
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 () {
498 const kbd = new Keyboard(document);
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
507 kbd.onkeyevent.resetHistory();
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 () {
513 const kbd = new Keyboard(document);
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
523 kbd.onkeyevent.resetHistory();
524 this.clock.tick(100);
525 expect(kbd.onkeyevent).to.not.have.been.called;
526 });
527
528 it('should pass through single Alt', function () {
529 const kbd = new Keyboard(document);
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 () {
537 const kbd = new Keyboard(document);
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);
542 });
543 });
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 () {
570 if (origNavigator !== undefined) {
571 Object.defineProperty(window, "navigator", origNavigator);
572 }
573 if (this.clock !== undefined) {
574 this.clock.restore();
575 }
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 });
618 });