]> git.proxmox.com Git - mirror_novnc.git/blame - tests/test.keyboard.js
Merge pull request #1007 from juanjoDiaz/remove_jshints_comments
[mirror_novnc.git] / tests / test.keyboard.js
CommitLineData
f00b6fb6 1var assert = chai.assert;
2var expect = chai.expect;
3
0aaf59c2
SM
4import sinon from '../vendor/sinon.js';
5
c1e2785f 6import Keyboard from '../core/input/keyboard.js';
dfae3209 7
099eb856
PO
8function isIE() {
9 return navigator && !!(/trident/i).exec(navigator.userAgent);
10}
11function isEdge() {
12 return navigator && !!(/edge/i).exec(navigator.userAgent);
13}
14
f7363fd2 15describe('Key Event Handling', function() {
f00b6fb6 16 "use strict";
f00b6fb6 17
f7363fd2
PO
18 // The real KeyboardEvent constructor might not work everywhere we
19 // want to run these tests
20 function keyevent(typeArg, KeyboardEventInit) {
21 var e = { type: typeArg };
22 for (var key in KeyboardEventInit) {
23 e[key] = KeyboardEventInit[key];
24 }
25 e.stopPropagation = sinon.spy();
26 e.preventDefault = sinon.spy();
27 return e;
28 };
f00b6fb6 29
f7363fd2
PO
30 describe('Decode Keyboard Events', function() {
31 it('should decode keydown events', function(done) {
099eb856 32 if (isIE() || isEdge()) this.skip();
747b4623
PO
33 var kbd = new Keyboard(document);
34 kbd.onkeyevent = function(keysym, code, down) {
f7363fd2
PO
35 expect(keysym).to.be.equal(0x61);
36 expect(code).to.be.equal('KeyA');
37 expect(down).to.be.equal(true);
38 done();
747b4623 39 };
f7363fd2
PO
40 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
41 });
42 it('should decode keyup events', function(done) {
099eb856 43 if (isIE() || isEdge()) this.skip();
f7363fd2 44 var calls = 0;
747b4623
PO
45 var kbd = new Keyboard(document);
46 kbd.onkeyevent = function(keysym, code, down) {
f7363fd2
PO
47 expect(keysym).to.be.equal(0x61);
48 expect(code).to.be.equal('KeyA');
49 if (calls++ === 1) {
50 expect(down).to.be.equal(false);
51 done();
52 }
747b4623 53 };
f7363fd2
PO
54 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
55 kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'}));
56 });
57
58 describe('Legacy keypress Events', function() {
59 it('should wait for keypress when needed', function() {
747b4623
PO
60 var kbd = new Keyboard(document);
61 kbd.onkeyevent = sinon.spy();
f7363fd2 62 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
747b4623 63 expect(kbd.onkeyevent).to.not.have.been.called;
f7363fd2
PO
64 });
65 it('should decode keypress events', function(done) {
747b4623
PO
66 var kbd = new Keyboard(document);
67 kbd.onkeyevent = function(keysym, code, down) {
f7363fd2
PO
68 expect(keysym).to.be.equal(0x61);
69 expect(code).to.be.equal('KeyA');
70 expect(down).to.be.equal(true);
71 done();
747b4623 72 };
f7363fd2
PO
73 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
74 kbd._handleKeyPress(keyevent('keypress', {code: 'KeyA', charCode: 0x61}));
f00b6fb6 75 });
9fce233d 76 it('should ignore keypress with different code', function() {
747b4623
PO
77 var kbd = new Keyboard(document);
78 kbd.onkeyevent = sinon.spy();
9fce233d
PO
79 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
80 kbd._handleKeyPress(keyevent('keypress', {code: 'KeyB', charCode: 0x61}));
747b4623 81 expect(kbd.onkeyevent).to.not.have.been.called;
9fce233d
PO
82 });
83 it('should handle keypress with missing code', function(done) {
747b4623
PO
84 var kbd = new Keyboard(document);
85 kbd.onkeyevent = function(keysym, code, down) {
9fce233d
PO
86 expect(keysym).to.be.equal(0x61);
87 expect(code).to.be.equal('KeyA');
88 expect(down).to.be.equal(true);
89 done();
747b4623 90 };
9fce233d
PO
91 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
92 kbd._handleKeyPress(keyevent('keypress', {charCode: 0x61}));
93 });
7cac5c8e 94 it('should guess key if no keypress and numeric key', function(done) {
747b4623
PO
95 var kbd = new Keyboard(document);
96 kbd.onkeyevent = function(keysym, code, down) {
7cac5c8e
PO
97 expect(keysym).to.be.equal(0x32);
98 expect(code).to.be.equal('Digit2');
99 expect(down).to.be.equal(true);
100 done();
747b4623 101 };
7cac5c8e
PO
102 kbd._handleKeyDown(keyevent('keydown', {code: 'Digit2', keyCode: 0x32}));
103 });
104 it('should guess key if no keypress and alpha key', function(done) {
747b4623
PO
105 var kbd = new Keyboard(document);
106 kbd.onkeyevent = function(keysym, code, down) {
7cac5c8e
PO
107 expect(keysym).to.be.equal(0x61);
108 expect(code).to.be.equal('KeyA');
109 expect(down).to.be.equal(true);
110 done();
747b4623 111 };
7cac5c8e
PO
112 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41, shiftKey: false}));
113 });
114 it('should guess key if no keypress and alpha key (with shift)', function(done) {
747b4623
PO
115 var kbd = new Keyboard(document);
116 kbd.onkeyevent = function(keysym, code, down) {
7cac5c8e
PO
117 expect(keysym).to.be.equal(0x41);
118 expect(code).to.be.equal('KeyA');
119 expect(down).to.be.equal(true);
120 done();
747b4623 121 };
7cac5c8e
PO
122 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41, shiftKey: true}));
123 });
124 it('should not guess key if no keypress and unknown key', function(done) {
747b4623
PO
125 var kbd = new Keyboard(document);
126 kbd.onkeyevent = function(keysym, code, down) {
7cac5c8e
PO
127 expect(keysym).to.be.equal(0);
128 expect(code).to.be.equal('KeyA');
129 expect(down).to.be.equal(true);
130 done();
747b4623 131 };
7cac5c8e
PO
132 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x09}));
133 });
f00b6fb6 134 });
f00b6fb6 135
f7363fd2 136 describe('suppress the right events at the right time', function() {
099eb856
PO
137 beforeEach(function () {
138 if (isIE() || isEdge()) this.skip();
139 });
f7363fd2 140 it('should suppress anything with a valid key', function() {
3d7bb020 141 var kbd = new Keyboard(document, {});
f7363fd2
PO
142 var evt = keyevent('keydown', {code: 'KeyA', key: 'a'});
143 kbd._handleKeyDown(evt);
144 expect(evt.preventDefault).to.have.been.called;
145 evt = keyevent('keyup', {code: 'KeyA', key: 'a'});
146 kbd._handleKeyUp(evt);
147 expect(evt.preventDefault).to.have.been.called;
148 });
149 it('should not suppress keys without key', function() {
3d7bb020 150 var kbd = new Keyboard(document, {});
f7363fd2
PO
151 var evt = keyevent('keydown', {code: 'KeyA', keyCode: 0x41});
152 kbd._handleKeyDown(evt);
153 expect(evt.preventDefault).to.not.have.been.called;
154 });
155 it('should suppress the following keypress event', function() {
3d7bb020 156 var kbd = new Keyboard(document, {});
f7363fd2
PO
157 var evt = keyevent('keydown', {code: 'KeyA', keyCode: 0x41});
158 kbd._handleKeyDown(evt);
159 var evt = keyevent('keypress', {code: 'KeyA', charCode: 0x41});
160 kbd._handleKeyPress(evt);
161 expect(evt.preventDefault).to.have.been.called;
f00b6fb6 162 });
163 });
f00b6fb6 164 });
165
9e99ce12
PO
166 describe('Fake keyup', function() {
167 it('should fake keyup events for virtual keyboards', function(done) {
168 if (isIE() || isEdge()) this.skip();
169 var count = 0;
747b4623
PO
170 var kbd = new Keyboard(document);
171 kbd.onkeyevent = function(keysym, code, down) {
9e99ce12
PO
172 switch (count++) {
173 case 0:
174 expect(keysym).to.be.equal(0x61);
175 expect(code).to.be.equal('Unidentified');
176 expect(down).to.be.equal(true);
177 break;
178 case 1:
179 expect(keysym).to.be.equal(0x61);
180 expect(code).to.be.equal('Unidentified');
181 expect(down).to.be.equal(false);
182 done();
183 }
747b4623 184 };
9e99ce12
PO
185 kbd._handleKeyDown(keyevent('keydown', {code: 'Unidentified', key: 'a'}));
186 });
187
188 describe('iOS', function() {
189 var origNavigator;
190 beforeEach(function () {
191 // window.navigator is a protected read-only property in many
192 // environments, so we need to redefine it whilst running these
193 // tests.
194 origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
195 if (origNavigator === undefined) {
196 // Object.getOwnPropertyDescriptor() doesn't work
197 // properly in any version of IE
198 this.skip();
199 }
200
201 Object.defineProperty(window, "navigator", {value: {}});
202 if (window.navigator.platform !== undefined) {
203 // Object.defineProperty() doesn't work properly in old
204 // versions of Chrome
205 this.skip();
206 }
207
208 window.navigator.platform = "iPhone 9.0";
209 });
210 afterEach(function () {
211 Object.defineProperty(window, "navigator", origNavigator);
212 });
213
214 it('should fake keyup events on iOS', function(done) {
215 if (isIE() || isEdge()) this.skip();
216 var count = 0;
747b4623
PO
217 var kbd = new Keyboard(document);
218 kbd.onkeyevent = function(keysym, code, down) {
9e99ce12
PO
219 switch (count++) {
220 case 0:
221 expect(keysym).to.be.equal(0x61);
222 expect(code).to.be.equal('KeyA');
223 expect(down).to.be.equal(true);
224 break;
225 case 1:
226 expect(keysym).to.be.equal(0x61);
227 expect(code).to.be.equal('KeyA');
228 expect(down).to.be.equal(false);
229 done();
230 }
747b4623 231 };
9e99ce12
PO
232 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
233 });
234 });
235 });
236
f00b6fb6 237 describe('Track Key State', function() {
099eb856
PO
238 beforeEach(function () {
239 if (isIE() || isEdge()) this.skip();
240 });
f7363fd2 241 it('should send release using the same keysym as the press', function(done) {
747b4623
PO
242 var kbd = new Keyboard(document);
243 kbd.onkeyevent = function(keysym, code, down) {
f7363fd2
PO
244 expect(keysym).to.be.equal(0x61);
245 expect(code).to.be.equal('KeyA');
246 if (!down) {
247 done();
248 }
747b4623 249 };
f7363fd2
PO
250 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
251 kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'b'}));
f00b6fb6 252 });
ae820533
PO
253 it('should send the same keysym for multiple presses', function() {
254 var count = 0;
747b4623
PO
255 var kbd = new Keyboard(document);
256 kbd.onkeyevent = function(keysym, code, down) {
ae820533
PO
257 expect(keysym).to.be.equal(0x61);
258 expect(code).to.be.equal('KeyA');
259 expect(down).to.be.equal(true);
260 count++;
747b4623 261 };
ae820533
PO
262 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
263 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'b'}));
264 expect(count).to.be.equal(2);
265 });
f7363fd2 266 it('should do nothing on keyup events if no keys are down', function() {
747b4623
PO
267 var kbd = new Keyboard(document);
268 kbd.onkeyevent = sinon.spy();
f7363fd2 269 kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'}));
747b4623 270 expect(kbd.onkeyevent).to.not.have.been.called;
f7363fd2 271 });
7e79dfe4
PO
272
273 describe('Legacy Events', function() {
274 it('should track keys using keyCode if no code', function(done) {
747b4623
PO
275 var kbd = new Keyboard(document);
276 kbd.onkeyevent = function(keysym, code, down) {
7e79dfe4
PO
277 expect(keysym).to.be.equal(0x61);
278 expect(code).to.be.equal('Platform65');
279 if (!down) {
280 done();
281 }
747b4623 282 };
7e79dfe4
PO
283 kbd._handleKeyDown(keyevent('keydown', {keyCode: 65, key: 'a'}));
284 kbd._handleKeyUp(keyevent('keyup', {keyCode: 65, key: 'b'}));
285 });
4093c37f 286 it('should ignore compositing code', function() {
747b4623
PO
287 var kbd = new Keyboard(document);
288 kbd.onkeyevent = function(keysym, code, down) {
4093c37f
PO
289 expect(keysym).to.be.equal(0x61);
290 expect(code).to.be.equal('Unidentified');
747b4623 291 };
4093c37f
PO
292 kbd._handleKeyDown(keyevent('keydown', {keyCode: 229, key: 'a'}));
293 });
7e79dfe4 294 it('should track keys using keyIdentifier if no code', function(done) {
747b4623
PO
295 var kbd = new Keyboard(document);
296 kbd.onkeyevent = function(keysym, code, down) {
7e79dfe4
PO
297 expect(keysym).to.be.equal(0x61);
298 expect(code).to.be.equal('Platform65');
299 if (!down) {
300 done();
301 }
747b4623 302 };
7e79dfe4
PO
303 kbd._handleKeyDown(keyevent('keydown', {keyIdentifier: 'U+0041', key: 'a'}));
304 kbd._handleKeyUp(keyevent('keyup', {keyIdentifier: 'U+0041', key: 'b'}));
305 });
306 });
f7363fd2 307 });
f00b6fb6 308
bf43c263
PO
309 describe('Shuffle modifiers on macOS', function() {
310 var origNavigator;
311 beforeEach(function () {
312 // window.navigator is a protected read-only property in many
313 // environments, so we need to redefine it whilst running these
314 // tests.
315 origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
316 if (origNavigator === undefined) {
317 // Object.getOwnPropertyDescriptor() doesn't work
318 // properly in any version of IE
319 this.skip();
320 }
321
322 Object.defineProperty(window, "navigator", {value: {}});
323 if (window.navigator.platform !== undefined) {
324 // Object.defineProperty() doesn't work properly in old
325 // versions of Chrome
326 this.skip();
327 }
328
329 window.navigator.platform = "Mac x86_64";
330 });
331 afterEach(function () {
332 Object.defineProperty(window, "navigator", origNavigator);
333 });
334
335 it('should change Alt to AltGraph', function() {
336 var count = 0;
747b4623
PO
337 var kbd = new Keyboard(document);
338 kbd.onkeyevent = function(keysym, code, down) {
bf43c263
PO
339 switch (count++) {
340 case 0:
341 expect(keysym).to.be.equal(0xFF7E);
342 expect(code).to.be.equal('AltLeft');
343 break;
344 case 1:
345 expect(keysym).to.be.equal(0xFE03);
346 expect(code).to.be.equal('AltRight');
347 break;
348 }
747b4623 349 };
9782d4a3
PO
350 kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt', location: 1}));
351 kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2}));
bf43c263
PO
352 expect(count).to.be.equal(2);
353 });
354 it('should change left Super to Alt', function(done) {
747b4623
PO
355 var kbd = new Keyboard(document);
356 kbd.onkeyevent = function(keysym, code, down) {
bf43c263
PO
357 expect(keysym).to.be.equal(0xFFE9);
358 expect(code).to.be.equal('MetaLeft');
359 done();
747b4623 360 };
9782d4a3 361 kbd._handleKeyDown(keyevent('keydown', {code: 'MetaLeft', key: 'Meta', location: 1}));
bf43c263
PO
362 });
363 it('should change right Super to left Super', function(done) {
747b4623
PO
364 var kbd = new Keyboard(document);
365 kbd.onkeyevent = function(keysym, code, down) {
bf43c263
PO
366 expect(keysym).to.be.equal(0xFFEB);
367 expect(code).to.be.equal('MetaRight');
368 done();
747b4623 369 };
9782d4a3 370 kbd._handleKeyDown(keyevent('keydown', {code: 'MetaRight', key: 'Meta', location: 2}));
bf43c263
PO
371 });
372 });
373
374 describe('Escape AltGraph on Windows', function() {
f7363fd2
PO
375 var origNavigator;
376 beforeEach(function () {
377 // window.navigator is a protected read-only property in many
378 // environments, so we need to redefine it whilst running these
379 // tests.
380 origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
381 if (origNavigator === undefined) {
382 // Object.getOwnPropertyDescriptor() doesn't work
383 // properly in any version of IE
384 this.skip();
385 }
386
387 Object.defineProperty(window, "navigator", {value: {}});
388 if (window.navigator.platform !== undefined) {
389 // Object.defineProperty() doesn't work properly in old
390 // versions of Chrome
391 this.skip();
392 }
393
394 window.navigator.platform = "Windows x86_64";
395 });
396 afterEach(function () {
397 Object.defineProperty(window, "navigator", origNavigator);
398 });
399
bf43c263 400 it('should generate fake undo/redo events on press when AltGraph is down', function() {
f00b6fb6 401 var times_called = 0;
747b4623
PO
402 var kbd = new Keyboard(document);
403 kbd.onkeyevent = function(keysym, code, down) {
f7363fd2
PO
404 switch(times_called++) {
405 case 0:
406 expect(keysym).to.be.equal(0xFFE3);
407 expect(code).to.be.equal('ControlLeft');
408 expect(down).to.be.equal(true);
409 break;
410 case 1:
bf43c263
PO
411 expect(keysym).to.be.equal(0xFFEA);
412 expect(code).to.be.equal('AltRight');
f7363fd2
PO
413 expect(down).to.be.equal(true);
414 break;
415 case 2:
bf43c263
PO
416 expect(keysym).to.be.equal(0xFFEA);
417 expect(code).to.be.equal('AltRight');
f7363fd2
PO
418 expect(down).to.be.equal(false);
419 break;
420 case 3:
421 expect(keysym).to.be.equal(0xFFE3);
bf43c263 422 expect(code).to.be.equal('ControlLeft');
f7363fd2
PO
423 expect(down).to.be.equal(false);
424 break;
425 case 4:
426 expect(keysym).to.be.equal(0x61);
427 expect(code).to.be.equal('KeyA');
428 expect(down).to.be.equal(true);
429 break;
430 case 5:
bf43c263
PO
431 expect(keysym).to.be.equal(0xFFE3);
432 expect(code).to.be.equal('ControlLeft');
f7363fd2
PO
433 expect(down).to.be.equal(true);
434 break;
435 case 6:
bf43c263
PO
436 expect(keysym).to.be.equal(0xFFEA);
437 expect(code).to.be.equal('AltRight');
f7363fd2
PO
438 expect(down).to.be.equal(true);
439 break;
440 }
747b4623 441 };
f7363fd2 442 // First the modifier combo
9782d4a3
PO
443 kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
444 kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2}));
f7363fd2
PO
445 // Next a normal character
446 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
447 expect(times_called).to.be.equal(7);
448 });
449 it('should no do anything on key release', function() {
f00b6fb6 450 var times_called = 0;
747b4623
PO
451 var kbd = new Keyboard(document);
452 kbd.onkeyevent = function(keysym, code, down) {
f7363fd2
PO
453 switch(times_called++) {
454 case 7:
455 expect(keysym).to.be.equal(0x61);
456 expect(code).to.be.equal('KeyA');
457 expect(down).to.be.equal(false);
458 break;
459 }
747b4623 460 };
f7363fd2 461 // First the modifier combo
9782d4a3
PO
462 kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
463 kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2}));
f7363fd2
PO
464 // Next a normal character
465 kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
466 kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'}));
467 expect(times_called).to.be.equal(8);
468 });
469 it('should not consider a char modifier to be down on the modifier key itself', function() {
f00b6fb6 470 var times_called = 0;
747b4623
PO
471 var kbd = new Keyboard(document);
472 kbd.onkeyevent = function(keysym, code, down) {
f7363fd2
PO
473 switch(times_called++) {
474 case 0:
475 expect(keysym).to.be.equal(0xFFE3);
476 expect(code).to.be.equal('ControlLeft');
477 expect(down).to.be.equal(true);
478 break;
479 case 1:
480 expect(keysym).to.be.equal(0xFFE9);
481 expect(code).to.be.equal('AltLeft');
482 expect(down).to.be.equal(true);
483 break;
484 case 2:
485 expect(keysym).to.be.equal(0xFFE3);
486 expect(code).to.be.equal('ControlLeft');
487 expect(down).to.be.equal(true);
488 break;
489 }
747b4623 490 };
f7363fd2 491 // First the modifier combo
9782d4a3
PO
492 kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
493 kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt', location: 1}));
f7363fd2 494 // Then one of the keys again
9782d4a3 495 kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
f00b6fb6 496 expect(times_called).to.be.equal(3);
f00b6fb6 497 });
498 });
499});