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