]> git.proxmox.com Git - mirror_novnc.git/blame - tests/test.keyboard.js
Peter is no longer part of the noVNC team
[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 () {
275 Object.defineProperty(window, "navigator", origNavigator);
276 });
277
2c5491e1 278 it('should change Alt to AltGraph', function () {
2b5f94fa
JD
279 let count = 0;
280 const kbd = new Keyboard(document);
651c23ec 281 kbd.onkeyevent = (keysym, code, down) => {
bf43c263
PO
282 switch (count++) {
283 case 0:
284 expect(keysym).to.be.equal(0xFF7E);
285 expect(code).to.be.equal('AltLeft');
286 break;
287 case 1:
288 expect(keysym).to.be.equal(0xFE03);
289 expect(code).to.be.equal('AltRight');
290 break;
291 }
747b4623 292 };
9782d4a3
PO
293 kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt', location: 1}));
294 kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2}));
bf43c263
PO
295 expect(count).to.be.equal(2);
296 });
2c5491e1 297 it('should change left Super to Alt', function (done) {
2b5f94fa 298 const kbd = new Keyboard(document);
651c23ec 299 kbd.onkeyevent = (keysym, code, down) => {
bf43c263
PO
300 expect(keysym).to.be.equal(0xFFE9);
301 expect(code).to.be.equal('MetaLeft');
302 done();
747b4623 303 };
9782d4a3 304 kbd._handleKeyDown(keyevent('keydown', {code: 'MetaLeft', key: 'Meta', location: 1}));
bf43c263 305 });
2c5491e1 306 it('should change right Super to left Super', function (done) {
2b5f94fa 307 const kbd = new Keyboard(document);
651c23ec 308 kbd.onkeyevent = (keysym, code, down) => {
bf43c263
PO
309 expect(keysym).to.be.equal(0xFFEB);
310 expect(code).to.be.equal('MetaRight');
311 done();
747b4623 312 };
9782d4a3 313 kbd._handleKeyDown(keyevent('keydown', {code: 'MetaRight', key: 'Meta', location: 2}));
bf43c263
PO
314 });
315 });
316
a6304f91
AT
317 describe('Caps Lock on iOS and macOS', function () {
318 let origNavigator;
319 beforeEach(function () {
320 // window.navigator is a protected read-only property in many
321 // environments, so we need to redefine it whilst running these
322 // tests.
323 origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
324 if (origNavigator === undefined) {
325 // Object.getOwnPropertyDescriptor() doesn't work
326 // properly in any version of IE
327 this.skip();
328 }
329
330 Object.defineProperty(window, "navigator", {value: {}});
331 if (window.navigator.platform !== undefined) {
332 // Object.defineProperty() doesn't work properly in old
333 // versions of Chrome
334 this.skip();
335 }
336 });
337
338 afterEach(function () {
339 Object.defineProperty(window, "navigator", origNavigator);
340 });
341
342 it('should toggle caps lock on key press on iOS', function (done) {
343 window.navigator.platform = "iPad";
344 const kbd = new Keyboard(document);
345 kbd.onkeyevent = sinon.spy();
346 kbd._handleKeyDown(keyevent('keydown', {code: 'CapsLock', key: 'CapsLock'}));
347
348 expect(kbd.onkeyevent).to.have.been.calledTwice;
349 expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true);
350 expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false);
351 done();
352 });
353
354 it('should toggle caps lock on key press on mac', function (done) {
355 window.navigator.platform = "Mac";
356 const kbd = new Keyboard(document);
357 kbd.onkeyevent = sinon.spy();
358 kbd._handleKeyDown(keyevent('keydown', {code: 'CapsLock', key: 'CapsLock'}));
359
360 expect(kbd.onkeyevent).to.have.been.calledTwice;
361 expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true);
362 expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false);
363 done();
364 });
365
366 it('should toggle caps lock on key release on iOS', function (done) {
367 window.navigator.platform = "iPad";
368 const kbd = new Keyboard(document);
369 kbd.onkeyevent = sinon.spy();
370 kbd._handleKeyUp(keyevent('keyup', {code: 'CapsLock', key: 'CapsLock'}));
371
372 expect(kbd.onkeyevent).to.have.been.calledTwice;
373 expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true);
374 expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false);
375 done();
376 });
377
378 it('should toggle caps lock on key release on mac', function (done) {
379 window.navigator.platform = "Mac";
380 const kbd = new Keyboard(document);
381 kbd.onkeyevent = sinon.spy();
382 kbd._handleKeyUp(keyevent('keyup', {code: 'CapsLock', key: 'CapsLock'}));
383
384 expect(kbd.onkeyevent).to.have.been.calledTwice;
385 expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true);
386 expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false);
387 done();
388 });
389 });
390
2c5491e1 391 describe('Escape AltGraph on Windows', function () {
2b5f94fa 392 let origNavigator;
f7363fd2
PO
393 beforeEach(function () {
394 // window.navigator is a protected read-only property in many
395 // environments, so we need to redefine it whilst running these
396 // tests.
397 origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
398 if (origNavigator === undefined) {
399 // Object.getOwnPropertyDescriptor() doesn't work
400 // properly in any version of IE
401 this.skip();
402 }
403
404 Object.defineProperty(window, "navigator", {value: {}});
405 if (window.navigator.platform !== undefined) {
406 // Object.defineProperty() doesn't work properly in old
407 // versions of Chrome
408 this.skip();
409 }
410
411 window.navigator.platform = "Windows x86_64";
b22c9ef9
PO
412
413 this.clock = sinon.useFakeTimers();
f7363fd2
PO
414 });
415 afterEach(function () {
416 Object.defineProperty(window, "navigator", origNavigator);
b22c9ef9 417 this.clock.restore();
f7363fd2
PO
418 });
419
b22c9ef9 420 it('should supress ControlLeft until it knows if it is AltGr', function () {
2b5f94fa 421 const kbd = new Keyboard(document);
b22c9ef9 422 kbd.onkeyevent = sinon.spy();
9782d4a3 423 kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
b22c9ef9 424 expect(kbd.onkeyevent).to.not.have.been.called;
f7363fd2 425 });
b22c9ef9
PO
426
427 it('should not trigger on repeating ControlLeft', function () {
2b5f94fa 428 const kbd = new Keyboard(document);
b22c9ef9 429 kbd.onkeyevent = sinon.spy();
9782d4a3 430 kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
b22c9ef9
PO
431 kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
432 expect(kbd.onkeyevent).to.have.been.calledTwice;
433 expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true);
434 expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffe3, "ControlLeft", true);
f7363fd2 435 });
b22c9ef9
PO
436
437 it('should not supress ControlRight', function () {
2b5f94fa 438 const kbd = new Keyboard(document);
b22c9ef9
PO
439 kbd.onkeyevent = sinon.spy();
440 kbd._handleKeyDown(keyevent('keydown', {code: 'ControlRight', key: 'Control', location: 2}));
441 expect(kbd.onkeyevent).to.have.been.calledOnce;
442 expect(kbd.onkeyevent).to.have.been.calledWith(0xffe4, "ControlRight", true);
443 });
444
445 it('should release ControlLeft after 100 ms', function () {
2b5f94fa 446 const kbd = new Keyboard(document);
b22c9ef9 447 kbd.onkeyevent = sinon.spy();
9782d4a3 448 kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
b22c9ef9
PO
449 expect(kbd.onkeyevent).to.not.have.been.called;
450 this.clock.tick(100);
451 expect(kbd.onkeyevent).to.have.been.calledOnce;
452 expect(kbd.onkeyevent).to.have.been.calledWith(0xffe3, "ControlLeft", true);
453 });
454
455 it('should release ControlLeft on other key press', function () {
2b5f94fa 456 const kbd = new Keyboard(document);
b22c9ef9
PO
457 kbd.onkeyevent = sinon.spy();
458 kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
459 expect(kbd.onkeyevent).to.not.have.been.called;
460 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
461 expect(kbd.onkeyevent).to.have.been.calledTwice;
462 expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true);
463 expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0x61, "KeyA", true);
464
465 // Check that the timer is properly dead
c9765e50 466 kbd.onkeyevent.resetHistory();
b22c9ef9
PO
467 this.clock.tick(100);
468 expect(kbd.onkeyevent).to.not.have.been.called;
469 });
470
471 it('should release ControlLeft on other key release', function () {
2b5f94fa 472 const kbd = new Keyboard(document);
b22c9ef9
PO
473 kbd.onkeyevent = sinon.spy();
474 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
9782d4a3 475 kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
b22c9ef9
PO
476 expect(kbd.onkeyevent).to.have.been.calledOnce;
477 expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0x61, "KeyA", true);
478 kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'}));
479 expect(kbd.onkeyevent).to.have.been.calledThrice;
480 expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffe3, "ControlLeft", true);
481 expect(kbd.onkeyevent.thirdCall).to.have.been.calledWith(0x61, "KeyA", false);
482
483 // Check that the timer is properly dead
c9765e50 484 kbd.onkeyevent.resetHistory();
b22c9ef9
PO
485 this.clock.tick(100);
486 expect(kbd.onkeyevent).to.not.have.been.called;
487 });
488
489 it('should generate AltGraph for quick Ctrl+Alt sequence', function () {
2b5f94fa 490 const kbd = new Keyboard(document);
b22c9ef9
PO
491 kbd.onkeyevent = sinon.spy();
492 kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1, timeStamp: Date.now()}));
493 this.clock.tick(20);
494 kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2, timeStamp: Date.now()}));
495 expect(kbd.onkeyevent).to.have.been.calledOnce;
496 expect(kbd.onkeyevent).to.have.been.calledWith(0xfe03, 'AltRight', true);
497
498 // Check that the timer is properly dead
c9765e50 499 kbd.onkeyevent.resetHistory();
b22c9ef9
PO
500 this.clock.tick(100);
501 expect(kbd.onkeyevent).to.not.have.been.called;
502 });
503
504 it('should generate Ctrl, Alt for slow Ctrl+Alt sequence', function () {
2b5f94fa 505 const kbd = new Keyboard(document);
b22c9ef9
PO
506 kbd.onkeyevent = sinon.spy();
507 kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1, timeStamp: Date.now()}));
508 this.clock.tick(60);
509 kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2, timeStamp: Date.now()}));
510 expect(kbd.onkeyevent).to.have.been.calledTwice;
511 expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true);
512 expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffea, "AltRight", true);
513
514 // Check that the timer is properly dead
c9765e50 515 kbd.onkeyevent.resetHistory();
b22c9ef9
PO
516 this.clock.tick(100);
517 expect(kbd.onkeyevent).to.not.have.been.called;
518 });
519
520 it('should pass through single Alt', function () {
2b5f94fa 521 const kbd = new Keyboard(document);
b22c9ef9
PO
522 kbd.onkeyevent = sinon.spy();
523 kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2}));
524 expect(kbd.onkeyevent).to.have.been.calledOnce;
525 expect(kbd.onkeyevent).to.have.been.calledWith(0xffea, 'AltRight', true);
526 });
527
528 it('should pass through single AltGr', function () {
2b5f94fa 529 const kbd = new Keyboard(document);
b22c9ef9
PO
530 kbd.onkeyevent = sinon.spy();
531 kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'AltGraph', location: 2}));
532 expect(kbd.onkeyevent).to.have.been.calledOnce;
533 expect(kbd.onkeyevent).to.have.been.calledWith(0xfe03, 'AltRight', true);
f00b6fb6 534 });
535 });
ccb511a5
PO
536
537 describe('Missing Shift keyup on Windows', function () {
538 let origNavigator;
539 beforeEach(function () {
540 // window.navigator is a protected read-only property in many
541 // environments, so we need to redefine it whilst running these
542 // tests.
543 origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
544 if (origNavigator === undefined) {
545 // Object.getOwnPropertyDescriptor() doesn't work
546 // properly in any version of IE
547 this.skip();
548 }
549
550 Object.defineProperty(window, "navigator", {value: {}});
551 if (window.navigator.platform !== undefined) {
552 // Object.defineProperty() doesn't work properly in old
553 // versions of Chrome
554 this.skip();
555 }
556
557 window.navigator.platform = "Windows x86_64";
558
559 this.clock = sinon.useFakeTimers();
560 });
561 afterEach(function () {
562 Object.defineProperty(window, "navigator", origNavigator);
563 this.clock.restore();
564 });
565
566 it('should fake a left Shift keyup', function () {
567 const kbd = new Keyboard(document);
568 kbd.onkeyevent = sinon.spy();
569
570 kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftLeft', key: 'Shift', location: 1}));
571 expect(kbd.onkeyevent).to.have.been.calledOnce;
572 expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', true);
573 kbd.onkeyevent.resetHistory();
574
575 kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftRight', key: 'Shift', location: 2}));
576 expect(kbd.onkeyevent).to.have.been.calledOnce;
577 expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', true);
578 kbd.onkeyevent.resetHistory();
579
580 kbd._handleKeyUp(keyevent('keyup', {code: 'ShiftLeft', key: 'Shift', location: 1}));
581 expect(kbd.onkeyevent).to.have.been.calledTwice;
582 expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', false);
583 expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', false);
584 });
585
586 it('should fake a right Shift keyup', function () {
587 const kbd = new Keyboard(document);
588 kbd.onkeyevent = sinon.spy();
589
590 kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftLeft', key: 'Shift', location: 1}));
591 expect(kbd.onkeyevent).to.have.been.calledOnce;
592 expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', true);
593 kbd.onkeyevent.resetHistory();
594
595 kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftRight', key: 'Shift', location: 2}));
596 expect(kbd.onkeyevent).to.have.been.calledOnce;
597 expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', true);
598 kbd.onkeyevent.resetHistory();
599
600 kbd._handleKeyUp(keyevent('keyup', {code: 'ShiftRight', key: 'Shift', location: 2}));
601 expect(kbd.onkeyevent).to.have.been.calledTwice;
602 expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', false);
603 expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', false);
604 });
605 });
f00b6fb6 606});