]> git.proxmox.com Git - mirror_novnc.git/blob - tests/test.keyboard.js
Remove keysym names from keysymdef.js
[mirror_novnc.git] / tests / test.keyboard.js
1 var assert = chai.assert;
2 var expect = chai.expect;
3
4 import keysyms from '../core/input/keysymdef.js';
5 import * as KeyboardUtil from '../core/input/util.js';
6
7 /* jshint newcap: false, expr: true */
8 describe('Key Event Pipeline Stages', function() {
9 "use strict";
10 describe('Decode Keyboard Events', function() {
11 it('should pass events to the next stage', function(done) {
12 KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
13 expect(evt).to.be.an.object;
14 done();
15 }).keydown({keyCode: 0x41});
16 });
17 it('should pass the right keysym through', function(done) {
18 KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
19 expect(evt.keysym).to.be.deep.equal(0x61);
20 done();
21 }).keypress({keyCode: 0x41});
22 });
23 it('should pass the right keyid through', function(done) {
24 KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
25 expect(evt).to.have.property('keyId', 0x41);
26 done();
27 }).keydown({keyCode: 0x41});
28 });
29 it('should not sync modifiers on a keypress', function() {
30 // Firefox provides unreliable modifier state on keypress events
31 var count = 0;
32 KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
33 ++count;
34 }).keypress({keyCode: 0x41, ctrlKey: true});
35 expect(count).to.be.equal(1);
36 });
37 it('should sync modifiers if necessary', function(done) {
38 var count = 0;
39 KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
40 switch (count) {
41 case 0: // fake a ctrl keydown
42 expect(evt).to.be.deep.equal({keysym: 0xffe3, type: 'keydown'});
43 ++count;
44 break;
45 case 1:
46 expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown', keysym: 0x61});
47 done();
48 break;
49 }
50 }).keydown({keyCode: 0x41, ctrlKey: true});
51 });
52 it('should forward keydown events with the right type', function(done) {
53 KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
54 expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown'});
55 done();
56 }).keydown({keyCode: 0x41});
57 });
58 it('should forward keyup events with the right type', function(done) {
59 KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
60 expect(evt).to.be.deep.equal({keyId: 0x41, keysym: 0x61, type: 'keyup'});
61 done();
62 }).keyup({keyCode: 0x41});
63 });
64 it('should forward keypress events with the right type', function(done) {
65 KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
66 expect(evt).to.be.deep.equal({keyId: 0x41, keysym: 0x61, type: 'keypress'});
67 done();
68 }).keypress({keyCode: 0x41});
69 });
70 it('should generate stalls if a char modifier is down while a key is pressed', function(done) {
71 var count = 0;
72 KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) {
73 switch (count) {
74 case 0: // fake altgr
75 expect(evt).to.be.deep.equal({keysym: 0xfe03, type: 'keydown'});
76 ++count;
77 break;
78 case 1: // stall before processing the 'a' keydown
79 expect(evt).to.be.deep.equal({type: 'stall'});
80 ++count;
81 break;
82 case 2: // 'a'
83 expect(evt).to.be.deep.equal({
84 type: 'keydown',
85 keyId: 0x41,
86 keysym: 0x61
87 });
88
89 done();
90 break;
91 }
92 }).keydown({keyCode: 0x41, altGraphKey: true});
93
94 });
95 describe('suppress the right events at the right time', function() {
96 it('should suppress anything while a shortcut modifier is down', function() {
97 var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
98
99 obj.keydown({keyCode: 0x11}); // press ctrl
100 expect(obj.keydown({keyCode: 'A'.charCodeAt()})).to.be.true;
101 expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.true;
102 expect(obj.keydown({keyCode: '1'.charCodeAt()})).to.be.true;
103 expect(obj.keydown({keyCode: 0x3c})).to.be.true; // < key on DK Windows
104 expect(obj.keydown({keyCode: 0xde})).to.be.true; // Ø key on DK
105 });
106 it('should suppress non-character keys', function() {
107 var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
108
109 expect(obj.keydown({keyCode: 0x08}), 'a').to.be.true;
110 expect(obj.keydown({keyCode: 0x09}), 'b').to.be.true;
111 expect(obj.keydown({keyCode: 0x11}), 'd').to.be.true;
112 expect(obj.keydown({keyCode: 0x12}), 'e').to.be.true;
113 });
114 it('should not suppress shift', function() {
115 var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
116
117 expect(obj.keydown({keyCode: 0x10}), 'd').to.be.false;
118 });
119 it('should generate event for shift keydown', function() {
120 var called = false;
121 var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
122 expect(evt).to.have.property('keysym');
123 called = true;
124 }).keydown({keyCode: 0x10});
125 expect(called).to.be.true;
126 });
127 it('should not suppress character keys', function() {
128 var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
129
130 expect(obj.keydown({keyCode: 'A'.charCodeAt()})).to.be.false;
131 expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.false;
132 expect(obj.keydown({keyCode: '1'.charCodeAt()})).to.be.false;
133 expect(obj.keydown({keyCode: 0x3c})).to.be.false; // < key on DK Windows
134 expect(obj.keydown({keyCode: 0xde})).to.be.false; // Ø key on DK
135 });
136 it('should not suppress if a char modifier is down', function() {
137 var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) {});
138
139 obj.keydown({keyCode: 0xe1}); // press altgr
140 expect(obj.keydown({keyCode: 'A'.charCodeAt()})).to.be.false;
141 expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.false;
142 expect(obj.keydown({keyCode: '1'.charCodeAt()})).to.be.false;
143 expect(obj.keydown({keyCode: 0x3c})).to.be.false; // < key on DK Windows
144 expect(obj.keydown({keyCode: 0xde})).to.be.false; // Ø key on DK
145 });
146 });
147 describe('Keypress and keyup events', function() {
148 it('should always suppress event propagation', function() {
149 var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
150
151 expect(obj.keypress({keyCode: 'A'.charCodeAt()})).to.be.true;
152 expect(obj.keypress({keyCode: 0x3c})).to.be.true; // < key on DK Windows
153 expect(obj.keypress({keyCode: 0x11})).to.be.true;
154
155 expect(obj.keyup({keyCode: 'A'.charCodeAt()})).to.be.true;
156 expect(obj.keyup({keyCode: 0x3c})).to.be.true; // < key on DK Windows
157 expect(obj.keyup({keyCode: 0x11})).to.be.true;
158 });
159 it('should never generate stalls', function() {
160 var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
161 expect(evt.type).to.not.be.equal('stall');
162 });
163
164 obj.keypress({keyCode: 'A'.charCodeAt()});
165 obj.keypress({keyCode: 0x3c});
166 obj.keypress({keyCode: 0x11});
167
168 obj.keyup({keyCode: 'A'.charCodeAt()});
169 obj.keyup({keyCode: 0x3c});
170 obj.keyup({keyCode: 0x11});
171 });
172 });
173 describe('mark events if a char modifier is down', function() {
174 it('should not mark modifiers on a keydown event', function() {
175 var times_called = 0;
176 var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) {
177 switch (times_called++) {
178 case 0: //altgr
179 break;
180 case 1: // 'a'
181 expect(evt).to.not.have.property('escape');
182 break;
183 }
184 });
185
186 obj.keydown({keyCode: 0xe1}); // press altgr
187 obj.keydown({keyCode: 'A'.charCodeAt()});
188 });
189
190 it('should indicate on events if a single-key char modifier is down', function(done) {
191 var times_called = 0;
192 var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) {
193 switch (times_called++) {
194 case 0: //altgr
195 break;
196 case 1: // 'a'
197 expect(evt).to.be.deep.equal({
198 type: 'keypress',
199 keyId: 'A'.charCodeAt(),
200 keysym: 'a'.charCodeAt(),
201 escape: [0xfe03]
202 });
203 done();
204 return;
205 }
206 });
207
208 obj.keydown({keyCode: 0xe1}); // press altgr
209 obj.keypress({keyCode: 'A'.charCodeAt()});
210 });
211 it('should indicate on events if a multi-key char modifier is down', function(done) {
212 var times_called = 0;
213 var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xffe9, 0xffe3]), function(evt) {
214 switch (times_called++) {
215 case 0: //ctrl
216 break;
217 case 1: //alt
218 break;
219 case 2: // 'a'
220 expect(evt).to.be.deep.equal({
221 type: 'keypress',
222 keyId: 'A'.charCodeAt(),
223 keysym: 'a'.charCodeAt(),
224 escape: [0xffe9, 0xffe3]
225 });
226 done();
227 return;
228 }
229 });
230
231 obj.keydown({keyCode: 0x11}); // press ctrl
232 obj.keydown({keyCode: 0x12}); // press alt
233 obj.keypress({keyCode: 'A'.charCodeAt()});
234 });
235 it('should not consider a char modifier to be down on the modifier key itself', function() {
236 var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) {
237 expect(evt).to.not.have.property('escape');
238 });
239
240 obj.keydown({keyCode: 0xe1}); // press altgr
241
242 });
243 });
244 describe('add/remove keysym', function() {
245 it('should remove keysym from keydown if a char key and no modifier', function() {
246 KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
247 expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown'});
248 }).keydown({keyCode: 0x41});
249 });
250 it('should not remove keysym from keydown if a shortcut modifier is down', function() {
251 var times_called = 0;
252 KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
253 switch (times_called++) {
254 case 1:
255 expect(evt).to.be.deep.equal({keyId: 0x41, keysym: 0x61, type: 'keydown'});
256 break;
257 }
258 }).keydown({keyCode: 0x41, ctrlKey: true});
259 expect(times_called).to.be.equal(2);
260 });
261 it('should not remove keysym from keydown if a char modifier is down', function() {
262 var times_called = 0;
263 KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) {
264 switch (times_called++) {
265 case 2:
266 expect(evt).to.be.deep.equal({keyId: 0x41, keysym: 0x61, type: 'keydown'});
267 break;
268 }
269 }).keydown({keyCode: 0x41, altGraphKey: true});
270 expect(times_called).to.be.equal(3);
271 });
272 it('should not remove keysym from keydown if key is noncharacter', function() {
273 KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
274 expect(evt, 'bacobjpace').to.be.deep.equal({keyId: 0x09, keysym: 0xff09, type: 'keydown'});
275 }).keydown({keyCode: 0x09});
276
277 KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
278 expect(evt, 'ctrl').to.be.deep.equal({keyId: 0x11, keysym: 0xffe3, type: 'keydown'});
279 }).keydown({keyCode: 0x11});
280 });
281 it('should never remove keysym from keypress', function() {
282 KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
283 expect(evt).to.be.deep.equal({keyId: 0x41, keysym: 0x61, type: 'keypress'});
284 }).keypress({keyCode: 0x41});
285 });
286 it('should never remove keysym from keyup', function() {
287 KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
288 expect(evt).to.be.deep.equal({keyId: 0x41, keysym: 0x61, type: 'keyup'});
289 }).keyup({keyCode: 0x41});
290 });
291 });
292 // on keypress, keyup(?), always set keysym
293 // on keydown, only do it if we don't expect a keypress: if noncharacter OR modifier is down
294 });
295
296 describe('Verify that char modifiers are active', function() {
297 it('should pass keydown events through if there is no stall', function(done) {
298 var obj = KeyboardUtil.VerifyCharModifier(function(evt){
299 expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: 0x41});
300 done();
301 })({type: 'keydown', keyId: 0x41, keysym: 0x41});
302 });
303 it('should pass keyup events through if there is no stall', function(done) {
304 var obj = KeyboardUtil.VerifyCharModifier(function(evt){
305 expect(evt).to.deep.equal({type: 'keyup', keyId: 0x41, keysym: 0x41});
306 done();
307 })({type: 'keyup', keyId: 0x41, keysym: 0x41});
308 });
309 it('should pass keypress events through if there is no stall', function(done) {
310 var obj = KeyboardUtil.VerifyCharModifier(function(evt){
311 expect(evt).to.deep.equal({type: 'keypress', keyId: 0x41, keysym: 0x41});
312 done();
313 })({type: 'keypress', keyId: 0x41, keysym: 0x41});
314 });
315 it('should not pass stall events through', function(done){
316 var obj = KeyboardUtil.VerifyCharModifier(function(evt){
317 // should only be called once, for the keydown
318 expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: 0x41});
319 done();
320 });
321
322 obj({type: 'stall'});
323 obj({type: 'keydown', keyId: 0x41, keysym: 0x41});
324 });
325 it('should merge keydown and keypress events if they come after a stall', function(done) {
326 var next_called = false;
327 var obj = KeyboardUtil.VerifyCharModifier(function(evt){
328 // should only be called once, for the keydown
329 expect(next_called).to.be.false;
330 next_called = true;
331 expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: 0x44});
332 done();
333 });
334
335 obj({type: 'stall'});
336 obj({type: 'keydown', keyId: 0x41, keysym: 0x42});
337 obj({type: 'keypress', keyId: 0x43, keysym: 0x44});
338 expect(next_called).to.be.false;
339 });
340 it('should preserve modifier attribute when merging if keysyms differ', function(done) {
341 var next_called = false;
342 var obj = KeyboardUtil.VerifyCharModifier(function(evt){
343 // should only be called once, for the keydown
344 expect(next_called).to.be.false;
345 next_called = true;
346 expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: 0x44, escape: [0xffe3]});
347 done();
348 });
349
350 obj({type: 'stall'});
351 obj({type: 'keydown', keyId: 0x41, keysym: 0x42});
352 obj({type: 'keypress', keyId: 0x43, keysym: 0x44, escape: [0xffe3]});
353 expect(next_called).to.be.false;
354 });
355 it('should not preserve modifier attribute when merging if keysyms are the same', function() {
356 var obj = KeyboardUtil.VerifyCharModifier(function(evt){
357 expect(evt).to.not.have.property('escape');
358 });
359
360 obj({type: 'stall'});
361 obj({type: 'keydown', keyId: 0x41, keysym: 0x42});
362 obj({type: 'keypress', keyId: 0x43, keysym: 0x42, escape: [0xffe3]});
363 });
364 it('should not merge keydown and keypress events if there is no stall', function(done) {
365 var times_called = 0;
366 var obj = KeyboardUtil.VerifyCharModifier(function(evt){
367 switch(times_called) {
368 case 0:
369 expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: 0x42});
370 break;
371 case 1:
372 expect(evt).to.deep.equal({type: 'keypress', keyId: 0x43, keysym: 0x44});
373 done();
374 break;
375 }
376
377 ++times_called;
378 });
379
380 obj({type: 'keydown', keyId: 0x41, keysym: 0x42});
381 obj({type: 'keypress', keyId: 0x43, keysym: 0x44});
382 });
383 it('should not merge keydown and keypress events if separated by another event', function(done) {
384 var times_called = 0;
385 var obj = KeyboardUtil.VerifyCharModifier(function(evt){
386 switch(times_called) {
387 case 0:
388 expect(evt,1).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: 0x42});
389 break;
390 case 1:
391 expect(evt,2).to.deep.equal({type: 'keyup', keyId: 0x43, keysym: 0x44});
392 break;
393 case 2:
394 expect(evt,3).to.deep.equal({type: 'keypress', keyId: 0x45, keysym: 0x46});
395 done();
396 break;
397 }
398
399 ++times_called;
400 });
401
402 obj({type: 'stall'});
403 obj({type: 'keydown', keyId: 0x41, keysym: 0x42});
404 obj({type: 'keyup', keyId: 0x43, keysym: 0x44});
405 obj({type: 'keypress', keyId: 0x45, keysym: 0x46});
406 });
407 });
408
409 describe('Track Key State', function() {
410 it('should do nothing on keyup events if no keys are down', function() {
411 var obj = KeyboardUtil.TrackKeyState(function(evt) {
412 expect(true).to.be.false;
413 });
414 obj({type: 'keyup', keyId: 0x41});
415 });
416 it('should insert into the queue on keydown if no keys are down', function() {
417 var times_called = 0;
418 var elem = null;
419 var keysymsdown = {};
420 var obj = KeyboardUtil.TrackKeyState(function(evt) {
421 ++times_called;
422 if (elem.type == 'keyup') {
423 expect(evt).to.have.property('keysym');
424 expect (keysymsdown[evt.keysym]).to.not.be.undefined;
425 delete keysymsdown[evt.keysym];
426 }
427 else {
428 expect(evt).to.be.deep.equal(elem);
429 expect (keysymsdown[evt.keysym]).to.not.be.undefined;
430 }
431 elem = null;
432 });
433
434 expect(elem).to.be.null;
435 elem = {type: 'keydown', keyId: 0x41, keysym: 0x42};
436 keysymsdown[0x42] = true;
437 obj(elem);
438 expect(elem).to.be.null;
439 elem = {type: 'keyup', keyId: 0x41};
440 obj(elem);
441 expect(elem).to.be.null;
442 expect(times_called).to.be.equal(2);
443 });
444 it('should insert into the queue on keypress if no keys are down', function() {
445 var times_called = 0;
446 var elem = null;
447 var keysymsdown = {};
448 var obj = KeyboardUtil.TrackKeyState(function(evt) {
449 ++times_called;
450 if (elem.type == 'keyup') {
451 expect(evt).to.have.property('keysym');
452 expect (keysymsdown[evt.keysym]).to.not.be.undefined;
453 delete keysymsdown[evt.keysym];
454 }
455 else {
456 expect(evt).to.be.deep.equal(elem);
457 expect (keysymsdown[evt.keysym]).to.not.be.undefined;
458 }
459 elem = null;
460 });
461
462 expect(elem).to.be.null;
463 elem = {type: 'keypress', keyId: 0x41, keysym: 0x42};
464 keysymsdown[0x42] = true;
465 obj(elem);
466 expect(elem).to.be.null;
467 elem = {type: 'keyup', keyId: 0x41};
468 obj(elem);
469 expect(elem).to.be.null;
470 expect(times_called).to.be.equal(2);
471 });
472 it('should add keysym to last key entry if keyId matches', function() {
473 // this implies that a single keyup will release both keysyms
474 var times_called = 0;
475 var elem = null;
476 var keysymsdown = {};
477 var obj = KeyboardUtil.TrackKeyState(function(evt) {
478 ++times_called;
479 if (elem.type == 'keyup') {
480 expect(evt).to.have.property('keysym');
481 expect (keysymsdown[evt.keysym]).to.not.be.undefined;
482 delete keysymsdown[evt.keysym];
483 }
484 else {
485 expect(evt).to.be.deep.equal(elem);
486 expect (keysymsdown[evt.keysym]).to.not.be.undefined;
487 elem = null;
488 }
489 });
490
491 expect(elem).to.be.null;
492 elem = {type: 'keypress', keyId: 0x41, keysym: 0x42};
493 keysymsdown[0x42] = true;
494 obj(elem);
495 expect(elem).to.be.null;
496 elem = {type: 'keypress', keyId: 0x41, keysym: 0x43};
497 keysymsdown[0x43] = true;
498 obj(elem);
499 expect(elem).to.be.null;
500 elem = {type: 'keyup', keyId: 0x41};
501 obj(elem);
502 expect(times_called).to.be.equal(4);
503 });
504 it('should create new key entry if keyId matches and keysym does not', function() {
505 // this implies that a single keyup will release both keysyms
506 var times_called = 0;
507 var elem = null;
508 var keysymsdown = {};
509 var obj = KeyboardUtil.TrackKeyState(function(evt) {
510 ++times_called;
511 if (elem.type == 'keyup') {
512 expect(evt).to.have.property('keysym');
513 expect (keysymsdown[evt.keysym]).to.not.be.undefined;
514 delete keysymsdown[evt.keysym];
515 }
516 else {
517 expect(evt).to.be.deep.equal(elem);
518 expect (keysymsdown[evt.keysym]).to.not.be.undefined;
519 elem = null;
520 }
521 });
522
523 expect(elem).to.be.null;
524 elem = {type: 'keydown', keyId: 0, keysym: 0x42};
525 keysymsdown[0x42] = true;
526 obj(elem);
527 expect(elem).to.be.null;
528 elem = {type: 'keydown', keyId: 0, keysym: 0x43};
529 keysymsdown[0x43] = true;
530 obj(elem);
531 expect(times_called).to.be.equal(2);
532 expect(elem).to.be.null;
533 elem = {type: 'keyup', keyId: 0};
534 obj(elem);
535 expect(times_called).to.be.equal(3);
536 elem = {type: 'keyup', keyId: 0};
537 obj(elem);
538 expect(times_called).to.be.equal(4);
539 });
540 it('should merge key entry if keyIds are zero and keysyms match', function() {
541 // this implies that a single keyup will release both keysyms
542 var times_called = 0;
543 var elem = null;
544 var keysymsdown = {};
545 var obj = KeyboardUtil.TrackKeyState(function(evt) {
546 ++times_called;
547 if (elem.type == 'keyup') {
548 expect(evt).to.have.property('keysym');
549 expect (keysymsdown[evt.keysym]).to.not.be.undefined;
550 delete keysymsdown[evt.keysym];
551 }
552 else {
553 expect(evt).to.be.deep.equal(elem);
554 expect (keysymsdown[evt.keysym]).to.not.be.undefined;
555 elem = null;
556 }
557 });
558
559 expect(elem).to.be.null;
560 elem = {type: 'keydown', keyId: 0, keysym: 0x42};
561 keysymsdown[0x42] = true;
562 obj(elem);
563 expect(elem).to.be.null;
564 elem = {type: 'keydown', keyId: 0, keysym: 0x42};
565 keysymsdown[0x42] = true;
566 obj(elem);
567 expect(times_called).to.be.equal(2);
568 expect(elem).to.be.null;
569 elem = {type: 'keyup', keyId: 0};
570 obj(elem);
571 expect(times_called).to.be.equal(3);
572 });
573 it('should add keysym as separate entry if keyId does not match last event', function() {
574 // this implies that separate keyups are required
575 var times_called = 0;
576 var elem = null;
577 var keysymsdown = {};
578 var obj = KeyboardUtil.TrackKeyState(function(evt) {
579 ++times_called;
580 if (elem.type == 'keyup') {
581 expect(evt).to.have.property('keysym');
582 expect (keysymsdown[evt.keysym]).to.not.be.undefined;
583 delete keysymsdown[evt.keysym];
584 }
585 else {
586 expect(evt).to.be.deep.equal(elem);
587 expect (keysymsdown[evt.keysym]).to.not.be.undefined;
588 elem = null;
589 }
590 });
591
592 expect(elem).to.be.null;
593 elem = {type: 'keypress', keyId: 0x41, keysym: 0x42};
594 keysymsdown[0x42] = true;
595 obj(elem);
596 expect(elem).to.be.null;
597 elem = {type: 'keypress', keyId: 0x42, keysym: 0x43};
598 keysymsdown[0x43] = true;
599 obj(elem);
600 expect(elem).to.be.null;
601 elem = {type: 'keyup', keyId: 0x41};
602 obj(elem);
603 expect(times_called).to.be.equal(4);
604 elem = {type: 'keyup', keyId: 0x42};
605 obj(elem);
606 expect(times_called).to.be.equal(4);
607 });
608 it('should add keysym as separate entry if keyId does not match last event and first is zero', function() {
609 // this implies that separate keyups are required
610 var times_called = 0;
611 var elem = null;
612 var keysymsdown = {};
613 var obj = KeyboardUtil.TrackKeyState(function(evt) {
614 ++times_called;
615 if (elem.type == 'keyup') {
616 expect(evt).to.have.property('keysym');
617 expect (keysymsdown[evt.keysym]).to.not.be.undefined;
618 delete keysymsdown[evt.keysym];
619 }
620 else {
621 expect(evt).to.be.deep.equal(elem);
622 expect (keysymsdown[evt.keysym]).to.not.be.undefined;
623 elem = null;
624 }
625 });
626
627 expect(elem).to.be.null;
628 elem = {type: 'keydown', keyId: 0, keysym: 0x42};
629 keysymsdown[0x42] = true;
630 obj(elem);
631 expect(elem).to.be.null;
632 elem = {type: 'keydown', keyId: 0x42, keysym: 0x43};
633 keysymsdown[0x43] = true;
634 obj(elem);
635 expect(elem).to.be.null;
636 expect(times_called).to.be.equal(2);
637 elem = {type: 'keyup', keyId: 0};
638 obj(elem);
639 expect(times_called).to.be.equal(3);
640 elem = {type: 'keyup', keyId: 0x42};
641 obj(elem);
642 expect(times_called).to.be.equal(4);
643 });
644 it('should add keysym as separate entry if keyId does not match last event and second is zero', function() {
645 // this implies that a separate keyups are required
646 var times_called = 0;
647 var elem = null;
648 var keysymsdown = {};
649 var obj = KeyboardUtil.TrackKeyState(function(evt) {
650 ++times_called;
651 if (elem.type == 'keyup') {
652 expect(evt).to.have.property('keysym');
653 expect (keysymsdown[evt.keysym]).to.not.be.undefined;
654 delete keysymsdown[evt.keysym];
655 }
656 else {
657 expect(evt).to.be.deep.equal(elem);
658 expect (keysymsdown[evt.keysym]).to.not.be.undefined;
659 elem = null;
660 }
661 });
662
663 expect(elem).to.be.null;
664 elem = {type: 'keydown', keyId: 0x41, keysym: 0x42};
665 keysymsdown[0x42] = true;
666 obj(elem);
667 expect(elem).to.be.null;
668 elem = {type: 'keydown', keyId: 0, keysym: 0x43};
669 keysymsdown[0x43] = true;
670 obj(elem);
671 expect(elem).to.be.null;
672 elem = {type: 'keyup', keyId: 0x41};
673 obj(elem);
674 expect(times_called).to.be.equal(3);
675 elem = {type: 'keyup', keyId: 0};
676 obj(elem);
677 expect(times_called).to.be.equal(4);
678 });
679 it('should pop matching key event on keyup', function() {
680 var times_called = 0;
681 var obj = KeyboardUtil.TrackKeyState(function(evt) {
682 switch (times_called++) {
683 case 0:
684 case 1:
685 case 2:
686 expect(evt.type).to.be.equal('keydown');
687 break;
688 case 3:
689 expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x42, keysym: 0x62});
690 break;
691 }
692 });
693
694 obj({type: 'keydown', keyId: 0x41, keysym: 0x61});
695 obj({type: 'keydown', keyId: 0x42, keysym: 0x62});
696 obj({type: 'keydown', keyId: 0x43, keysym: 0x63});
697 obj({type: 'keyup', keyId: 0x42});
698 expect(times_called).to.equal(4);
699 });
700 it('should pop the first zero keyevent on keyup with zero keyId', function() {
701 var times_called = 0;
702 var obj = KeyboardUtil.TrackKeyState(function(evt) {
703 switch (times_called++) {
704 case 0:
705 case 1:
706 case 2:
707 expect(evt.type).to.be.equal('keydown');
708 break;
709 case 3:
710 expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: 0x61});
711 break;
712 }
713 });
714
715 obj({type: 'keydown', keyId: 0, keysym: 0x61});
716 obj({type: 'keydown', keyId: 0, keysym: 0x62});
717 obj({type: 'keydown', keyId: 0x41, keysym: 0x63});
718 obj({type: 'keyup', keyId: 0x0});
719 expect(times_called).to.equal(4);
720 });
721 it('should pop the last keyevents keysym if no match is found for keyId', function() {
722 var times_called = 0;
723 var obj = KeyboardUtil.TrackKeyState(function(evt) {
724 switch (times_called++) {
725 case 0:
726 case 1:
727 case 2:
728 expect(evt.type).to.be.equal('keydown');
729 break;
730 case 3:
731 expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x44, keysym: 0x63});
732 break;
733 }
734 });
735
736 obj({type: 'keydown', keyId: 0x41, keysym: 0x61});
737 obj({type: 'keydown', keyId: 0x42, keysym: 0x62});
738 obj({type: 'keydown', keyId: 0x43, keysym: 0x63});
739 obj({type: 'keyup', keyId: 0x44});
740 expect(times_called).to.equal(4);
741 });
742 describe('Firefox sends keypress even when keydown is suppressed', function() {
743 it('should discard the keypress', function() {
744 var times_called = 0;
745 var obj = KeyboardUtil.TrackKeyState(function(evt) {
746 expect(times_called).to.be.equal(0);
747 ++times_called;
748 });
749
750 obj({type: 'keydown', keyId: 0x41, keysym: 0x42});
751 expect(times_called).to.be.equal(1);
752 obj({type: 'keypress', keyId: 0x41, keysym: 0x43});
753 });
754 });
755 describe('releaseAll', function() {
756 it('should do nothing if no keys have been pressed', function() {
757 var times_called = 0;
758 var obj = KeyboardUtil.TrackKeyState(function(evt) {
759 ++times_called;
760 });
761 obj({type: 'releaseall'});
762 expect(times_called).to.be.equal(0);
763 });
764 it('should release the keys that have been pressed', function() {
765 var times_called = 0;
766 var obj = KeyboardUtil.TrackKeyState(function(evt) {
767 switch (times_called++) {
768 case 2:
769 expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: 0x41});
770 break;
771 case 3:
772 expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: 0x42});
773 break;
774 }
775 });
776 obj({type: 'keydown', keyId: 0x41, keysym: 0x41});
777 obj({type: 'keydown', keyId: 0x42, keysym: 0x42});
778 expect(times_called).to.be.equal(2);
779 obj({type: 'releaseall'});
780 expect(times_called).to.be.equal(4);
781 obj({type: 'releaseall'});
782 expect(times_called).to.be.equal(4);
783 });
784 });
785
786 });
787
788 describe('Escape Modifiers', function() {
789 describe('Keydown', function() {
790 it('should pass through when a char modifier is not down', function() {
791 var times_called = 0;
792 KeyboardUtil.EscapeModifiers(function(evt) {
793 expect(times_called).to.be.equal(0);
794 ++times_called;
795 expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0x41, keysym: 0x42});
796 })({type: 'keydown', keyId: 0x41, keysym: 0x42});
797 expect(times_called).to.be.equal(1);
798 });
799 it('should generate fake undo/redo events when a char modifier is down', function() {
800 var times_called = 0;
801 KeyboardUtil.EscapeModifiers(function(evt) {
802 switch(times_called++) {
803 case 0:
804 expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: 0xffe9});
805 break;
806 case 1:
807 expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: 0xffe3});
808 break;
809 case 2:
810 expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0x41, keysym: 0x42, escape: [0xffe9, 0xffe3]});
811 break;
812 case 3:
813 expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0, keysym: 0xffe9});
814 break;
815 case 4:
816 expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0, keysym: 0xffe3});
817 break;
818 }
819 })({type: 'keydown', keyId: 0x41, keysym: 0x42, escape: [0xffe9, 0xffe3]});
820 expect(times_called).to.be.equal(5);
821 });
822 });
823 describe('Keyup', function() {
824 it('should pass through when a char modifier is down', function() {
825 var times_called = 0;
826 KeyboardUtil.EscapeModifiers(function(evt) {
827 expect(times_called).to.be.equal(0);
828 ++times_called;
829 expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x41, keysym: 0x42, escape: [0xfe03]});
830 })({type: 'keyup', keyId: 0x41, keysym: 0x42, escape: [0xfe03]});
831 expect(times_called).to.be.equal(1);
832 });
833 it('should pass through when a char modifier is not down', function() {
834 var times_called = 0;
835 KeyboardUtil.EscapeModifiers(function(evt) {
836 expect(times_called).to.be.equal(0);
837 ++times_called;
838 expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x41, keysym: 0x42});
839 })({type: 'keyup', keyId: 0x41, keysym: 0x42});
840 expect(times_called).to.be.equal(1);
841 });
842 });
843 });
844 });