]>
Commit | Line | Data |
---|---|---|
2b5f94fa | 1 | const expect = chai.expect; |
f00b6fb6 | 2 | |
c1e2785f | 3 | import Keyboard from '../core/input/keyboard.js'; |
099eb856 | 4 | |
2c5491e1 | 5 | describe('Key Event Handling', function () { |
f00b6fb6 | 6 | "use strict"; |
f00b6fb6 | 7 | |
f7363fd2 PO |
8 | // The real KeyboardEvent constructor might not work everywhere we |
9 | // want to run these tests | |
10 | function keyevent(typeArg, KeyboardEventInit) { | |
2b5f94fa JD |
11 | const e = { type: typeArg }; |
12 | for (let key in KeyboardEventInit) { | |
f7363fd2 PO |
13 | e[key] = KeyboardEventInit[key]; |
14 | } | |
15 | e.stopPropagation = sinon.spy(); | |
16 | e.preventDefault = sinon.spy(); | |
17 | return e; | |
8727f598 | 18 | } |
f00b6fb6 | 19 | |
2c5491e1 PO |
20 | describe('Decode Keyboard Events', function () { |
21 | it('should decode keydown events', function (done) { | |
2b5f94fa | 22 | const kbd = new Keyboard(document); |
651c23ec | 23 | kbd.onkeyevent = (keysym, code, down) => { |
f7363fd2 PO |
24 | expect(keysym).to.be.equal(0x61); |
25 | expect(code).to.be.equal('KeyA'); | |
26 | expect(down).to.be.equal(true); | |
27 | done(); | |
747b4623 | 28 | }; |
f7363fd2 PO |
29 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); |
30 | }); | |
2c5491e1 | 31 | it('should decode keyup events', function (done) { |
2b5f94fa JD |
32 | let calls = 0; |
33 | const kbd = new Keyboard(document); | |
651c23ec | 34 | kbd.onkeyevent = (keysym, code, down) => { |
f7363fd2 PO |
35 | expect(keysym).to.be.equal(0x61); |
36 | expect(code).to.be.equal('KeyA'); | |
37 | if (calls++ === 1) { | |
38 | expect(down).to.be.equal(false); | |
39 | done(); | |
40 | } | |
747b4623 | 41 | }; |
f7363fd2 PO |
42 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); |
43 | kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'})); | |
44 | }); | |
f00b6fb6 | 45 | }); |
46 | ||
2c5491e1 PO |
47 | describe('Fake keyup', function () { |
48 | it('should fake keyup events for virtual keyboards', function (done) { | |
2b5f94fa JD |
49 | let count = 0; |
50 | const kbd = new Keyboard(document); | |
651c23ec | 51 | kbd.onkeyevent = (keysym, code, down) => { |
9e99ce12 PO |
52 | switch (count++) { |
53 | case 0: | |
54 | expect(keysym).to.be.equal(0x61); | |
55 | expect(code).to.be.equal('Unidentified'); | |
56 | expect(down).to.be.equal(true); | |
57 | break; | |
58 | case 1: | |
59 | expect(keysym).to.be.equal(0x61); | |
60 | expect(code).to.be.equal('Unidentified'); | |
61 | expect(down).to.be.equal(false); | |
62 | done(); | |
63 | } | |
747b4623 | 64 | }; |
9e99ce12 PO |
65 | kbd._handleKeyDown(keyevent('keydown', {code: 'Unidentified', key: 'a'})); |
66 | }); | |
9e99ce12 PO |
67 | }); |
68 | ||
2c5491e1 | 69 | describe('Track Key State', function () { |
2c5491e1 | 70 | it('should send release using the same keysym as the press', function (done) { |
2b5f94fa | 71 | const kbd = new Keyboard(document); |
651c23ec | 72 | kbd.onkeyevent = (keysym, code, down) => { |
f7363fd2 PO |
73 | expect(keysym).to.be.equal(0x61); |
74 | expect(code).to.be.equal('KeyA'); | |
75 | if (!down) { | |
76 | done(); | |
77 | } | |
747b4623 | 78 | }; |
f7363fd2 PO |
79 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); |
80 | kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'b'})); | |
f00b6fb6 | 81 | }); |
2c5491e1 | 82 | it('should send the same keysym for multiple presses', function () { |
2b5f94fa JD |
83 | let count = 0; |
84 | const kbd = new Keyboard(document); | |
651c23ec | 85 | kbd.onkeyevent = (keysym, code, down) => { |
ae820533 PO |
86 | expect(keysym).to.be.equal(0x61); |
87 | expect(code).to.be.equal('KeyA'); | |
88 | expect(down).to.be.equal(true); | |
89 | count++; | |
747b4623 | 90 | }; |
ae820533 PO |
91 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); |
92 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'b'})); | |
93 | expect(count).to.be.equal(2); | |
94 | }); | |
2c5491e1 | 95 | it('should do nothing on keyup events if no keys are down', function () { |
2b5f94fa | 96 | const kbd = new Keyboard(document); |
747b4623 | 97 | kbd.onkeyevent = sinon.spy(); |
f7363fd2 | 98 | kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'})); |
747b4623 | 99 | expect(kbd.onkeyevent).to.not.have.been.called; |
f7363fd2 | 100 | }); |
7e79dfe4 | 101 | |
2c5491e1 PO |
102 | describe('Legacy Events', function () { |
103 | it('should track keys using keyCode if no code', function (done) { | |
2b5f94fa | 104 | const kbd = new Keyboard(document); |
651c23ec | 105 | kbd.onkeyevent = (keysym, code, down) => { |
7e79dfe4 PO |
106 | expect(keysym).to.be.equal(0x61); |
107 | expect(code).to.be.equal('Platform65'); | |
108 | if (!down) { | |
109 | done(); | |
110 | } | |
747b4623 | 111 | }; |
7e79dfe4 PO |
112 | kbd._handleKeyDown(keyevent('keydown', {keyCode: 65, key: 'a'})); |
113 | kbd._handleKeyUp(keyevent('keyup', {keyCode: 65, key: 'b'})); | |
114 | }); | |
2c5491e1 | 115 | it('should ignore compositing code', function () { |
2b5f94fa | 116 | const kbd = new Keyboard(document); |
651c23ec | 117 | kbd.onkeyevent = (keysym, code, down) => { |
4093c37f PO |
118 | expect(keysym).to.be.equal(0x61); |
119 | expect(code).to.be.equal('Unidentified'); | |
747b4623 | 120 | }; |
4093c37f PO |
121 | kbd._handleKeyDown(keyevent('keydown', {keyCode: 229, key: 'a'})); |
122 | }); | |
2c5491e1 | 123 | it('should track keys using keyIdentifier if no code', function (done) { |
2b5f94fa | 124 | const kbd = new Keyboard(document); |
651c23ec | 125 | kbd.onkeyevent = (keysym, code, down) => { |
7e79dfe4 PO |
126 | expect(keysym).to.be.equal(0x61); |
127 | expect(code).to.be.equal('Platform65'); | |
128 | if (!down) { | |
129 | done(); | |
130 | } | |
747b4623 | 131 | }; |
7e79dfe4 PO |
132 | kbd._handleKeyDown(keyevent('keydown', {keyIdentifier: 'U+0041', key: 'a'})); |
133 | kbd._handleKeyUp(keyevent('keyup', {keyIdentifier: 'U+0041', key: 'b'})); | |
134 | }); | |
135 | }); | |
f7363fd2 | 136 | }); |
f00b6fb6 | 137 | |
2c5491e1 | 138 | describe('Shuffle modifiers on macOS', function () { |
2b5f94fa | 139 | let origNavigator; |
bf43c263 PO |
140 | beforeEach(function () { |
141 | // window.navigator is a protected read-only property in many | |
142 | // environments, so we need to redefine it whilst running these | |
143 | // tests. | |
144 | origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); | |
bf43c263 PO |
145 | |
146 | Object.defineProperty(window, "navigator", {value: {}}); | |
147 | if (window.navigator.platform !== undefined) { | |
148 | // Object.defineProperty() doesn't work properly in old | |
149 | // versions of Chrome | |
150 | this.skip(); | |
151 | } | |
152 | ||
153 | window.navigator.platform = "Mac x86_64"; | |
154 | }); | |
155 | afterEach(function () { | |
eb05b45b PO |
156 | if (origNavigator !== undefined) { |
157 | Object.defineProperty(window, "navigator", origNavigator); | |
158 | } | |
bf43c263 PO |
159 | }); |
160 | ||
2c5491e1 | 161 | it('should change Alt to AltGraph', function () { |
2b5f94fa JD |
162 | let count = 0; |
163 | const kbd = new Keyboard(document); | |
651c23ec | 164 | kbd.onkeyevent = (keysym, code, down) => { |
bf43c263 PO |
165 | switch (count++) { |
166 | case 0: | |
167 | expect(keysym).to.be.equal(0xFF7E); | |
168 | expect(code).to.be.equal('AltLeft'); | |
169 | break; | |
170 | case 1: | |
171 | expect(keysym).to.be.equal(0xFE03); | |
172 | expect(code).to.be.equal('AltRight'); | |
173 | break; | |
174 | } | |
747b4623 | 175 | }; |
9782d4a3 PO |
176 | kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt', location: 1})); |
177 | kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2})); | |
bf43c263 PO |
178 | expect(count).to.be.equal(2); |
179 | }); | |
2c5491e1 | 180 | it('should change left Super to Alt', function (done) { |
2b5f94fa | 181 | const kbd = new Keyboard(document); |
651c23ec | 182 | kbd.onkeyevent = (keysym, code, down) => { |
bf43c263 PO |
183 | expect(keysym).to.be.equal(0xFFE9); |
184 | expect(code).to.be.equal('MetaLeft'); | |
185 | done(); | |
747b4623 | 186 | }; |
9782d4a3 | 187 | kbd._handleKeyDown(keyevent('keydown', {code: 'MetaLeft', key: 'Meta', location: 1})); |
bf43c263 | 188 | }); |
2c5491e1 | 189 | it('should change right Super to left Super', function (done) { |
2b5f94fa | 190 | const kbd = new Keyboard(document); |
651c23ec | 191 | kbd.onkeyevent = (keysym, code, down) => { |
bf43c263 PO |
192 | expect(keysym).to.be.equal(0xFFEB); |
193 | expect(code).to.be.equal('MetaRight'); | |
194 | done(); | |
747b4623 | 195 | }; |
9782d4a3 | 196 | kbd._handleKeyDown(keyevent('keydown', {code: 'MetaRight', key: 'Meta', location: 2})); |
bf43c263 PO |
197 | }); |
198 | }); | |
199 | ||
a6304f91 AT |
200 | describe('Caps Lock on iOS and macOS', function () { |
201 | let origNavigator; | |
202 | beforeEach(function () { | |
203 | // window.navigator is a protected read-only property in many | |
204 | // environments, so we need to redefine it whilst running these | |
205 | // tests. | |
206 | origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); | |
a6304f91 AT |
207 | |
208 | Object.defineProperty(window, "navigator", {value: {}}); | |
209 | if (window.navigator.platform !== undefined) { | |
210 | // Object.defineProperty() doesn't work properly in old | |
211 | // versions of Chrome | |
212 | this.skip(); | |
213 | } | |
214 | }); | |
215 | ||
216 | afterEach(function () { | |
eb05b45b PO |
217 | if (origNavigator !== undefined) { |
218 | Object.defineProperty(window, "navigator", origNavigator); | |
219 | } | |
a6304f91 AT |
220 | }); |
221 | ||
4ae9d3e7 | 222 | it('should toggle caps lock on key press on iOS', function () { |
a6304f91 AT |
223 | window.navigator.platform = "iPad"; |
224 | const kbd = new Keyboard(document); | |
225 | kbd.onkeyevent = sinon.spy(); | |
226 | kbd._handleKeyDown(keyevent('keydown', {code: 'CapsLock', key: 'CapsLock'})); | |
227 | ||
228 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
229 | expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); | |
230 | expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); | |
a6304f91 AT |
231 | }); |
232 | ||
4ae9d3e7 | 233 | it('should toggle caps lock on key press on mac', function () { |
a6304f91 AT |
234 | window.navigator.platform = "Mac"; |
235 | const kbd = new Keyboard(document); | |
236 | kbd.onkeyevent = sinon.spy(); | |
237 | kbd._handleKeyDown(keyevent('keydown', {code: 'CapsLock', key: 'CapsLock'})); | |
238 | ||
239 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
240 | expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); | |
241 | expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); | |
a6304f91 AT |
242 | }); |
243 | ||
4ae9d3e7 | 244 | it('should toggle caps lock on key release on iOS', function () { |
a6304f91 AT |
245 | window.navigator.platform = "iPad"; |
246 | const kbd = new Keyboard(document); | |
247 | kbd.onkeyevent = sinon.spy(); | |
248 | kbd._handleKeyUp(keyevent('keyup', {code: 'CapsLock', key: 'CapsLock'})); | |
249 | ||
250 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
251 | expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); | |
252 | expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); | |
a6304f91 AT |
253 | }); |
254 | ||
4ae9d3e7 | 255 | it('should toggle caps lock on key release on mac', function () { |
a6304f91 AT |
256 | window.navigator.platform = "Mac"; |
257 | const kbd = new Keyboard(document); | |
258 | kbd.onkeyevent = sinon.spy(); | |
259 | kbd._handleKeyUp(keyevent('keyup', {code: 'CapsLock', key: 'CapsLock'})); | |
260 | ||
261 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
262 | expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); | |
263 | expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); | |
a6304f91 AT |
264 | }); |
265 | }); | |
266 | ||
0cdf2962 PO |
267 | describe('Japanese IM keys on Windows', function () { |
268 | let origNavigator; | |
269 | beforeEach(function () { | |
270 | // window.navigator is a protected read-only property in many | |
271 | // environments, so we need to redefine it whilst running these | |
272 | // tests. | |
273 | origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); | |
274 | ||
275 | Object.defineProperty(window, "navigator", {value: {}}); | |
276 | if (window.navigator.platform !== undefined) { | |
277 | // Object.defineProperty() doesn't work properly in old | |
278 | // versions of Chrome | |
279 | this.skip(); | |
280 | } | |
281 | ||
282 | window.navigator.platform = "Windows"; | |
283 | }); | |
284 | ||
285 | afterEach(function () { | |
286 | if (origNavigator !== undefined) { | |
287 | Object.defineProperty(window, "navigator", origNavigator); | |
288 | } | |
289 | }); | |
290 | ||
291 | const keys = { 'Zenkaku': 0xff2a, 'Hankaku': 0xff2a, | |
292 | 'Alphanumeric': 0xff30, 'Katakana': 0xff26, | |
293 | 'Hiragana': 0xff25, 'Romaji': 0xff24, | |
294 | 'KanaMode': 0xff24 }; | |
295 | for (let [key, keysym] of Object.entries(keys)) { | |
296 | it(`should fake key release for ${key} on Windows`, function () { | |
297 | let kbd = new Keyboard(document); | |
298 | kbd.onkeyevent = sinon.spy(); | |
299 | kbd._handleKeyDown(keyevent('keydown', {code: 'FakeIM', key: key})); | |
300 | ||
301 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
302 | expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(keysym, "FakeIM", true); | |
303 | expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(keysym, "FakeIM", false); | |
304 | }); | |
305 | } | |
306 | }); | |
307 | ||
2c5491e1 | 308 | describe('Escape AltGraph on Windows', function () { |
2b5f94fa | 309 | let origNavigator; |
f7363fd2 PO |
310 | beforeEach(function () { |
311 | // window.navigator is a protected read-only property in many | |
312 | // environments, so we need to redefine it whilst running these | |
313 | // tests. | |
314 | origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); | |
f7363fd2 PO |
315 | |
316 | Object.defineProperty(window, "navigator", {value: {}}); | |
317 | if (window.navigator.platform !== undefined) { | |
318 | // Object.defineProperty() doesn't work properly in old | |
319 | // versions of Chrome | |
320 | this.skip(); | |
321 | } | |
322 | ||
323 | window.navigator.platform = "Windows x86_64"; | |
b22c9ef9 PO |
324 | |
325 | this.clock = sinon.useFakeTimers(); | |
f7363fd2 PO |
326 | }); |
327 | afterEach(function () { | |
eb05b45b PO |
328 | if (origNavigator !== undefined) { |
329 | Object.defineProperty(window, "navigator", origNavigator); | |
330 | } | |
331 | if (this.clock !== undefined) { | |
332 | this.clock.restore(); | |
333 | } | |
f7363fd2 PO |
334 | }); |
335 | ||
b22c9ef9 | 336 | it('should supress ControlLeft until it knows if it is AltGr', function () { |
2b5f94fa | 337 | const kbd = new Keyboard(document); |
b22c9ef9 | 338 | kbd.onkeyevent = sinon.spy(); |
9782d4a3 | 339 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); |
b22c9ef9 | 340 | expect(kbd.onkeyevent).to.not.have.been.called; |
f7363fd2 | 341 | }); |
b22c9ef9 PO |
342 | |
343 | it('should not trigger on repeating ControlLeft', function () { | |
2b5f94fa | 344 | const kbd = new Keyboard(document); |
b22c9ef9 | 345 | kbd.onkeyevent = sinon.spy(); |
9782d4a3 | 346 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); |
b22c9ef9 PO |
347 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); |
348 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
349 | expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true); | |
350 | expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffe3, "ControlLeft", true); | |
f7363fd2 | 351 | }); |
b22c9ef9 PO |
352 | |
353 | it('should not supress ControlRight', function () { | |
2b5f94fa | 354 | const kbd = new Keyboard(document); |
b22c9ef9 PO |
355 | kbd.onkeyevent = sinon.spy(); |
356 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlRight', key: 'Control', location: 2})); | |
357 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
358 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe4, "ControlRight", true); | |
359 | }); | |
360 | ||
361 | it('should release ControlLeft after 100 ms', function () { | |
2b5f94fa | 362 | const kbd = new Keyboard(document); |
b22c9ef9 | 363 | kbd.onkeyevent = sinon.spy(); |
9782d4a3 | 364 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); |
b22c9ef9 PO |
365 | expect(kbd.onkeyevent).to.not.have.been.called; |
366 | this.clock.tick(100); | |
367 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
368 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe3, "ControlLeft", true); | |
369 | }); | |
370 | ||
371 | it('should release ControlLeft on other key press', function () { | |
2b5f94fa | 372 | const kbd = new Keyboard(document); |
b22c9ef9 PO |
373 | kbd.onkeyevent = sinon.spy(); |
374 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); | |
375 | expect(kbd.onkeyevent).to.not.have.been.called; | |
376 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); | |
377 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
378 | expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true); | |
379 | expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0x61, "KeyA", true); | |
380 | ||
381 | // Check that the timer is properly dead | |
c9765e50 | 382 | kbd.onkeyevent.resetHistory(); |
b22c9ef9 PO |
383 | this.clock.tick(100); |
384 | expect(kbd.onkeyevent).to.not.have.been.called; | |
385 | }); | |
386 | ||
387 | it('should release ControlLeft on other key release', function () { | |
2b5f94fa | 388 | const kbd = new Keyboard(document); |
b22c9ef9 PO |
389 | kbd.onkeyevent = sinon.spy(); |
390 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); | |
9782d4a3 | 391 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); |
b22c9ef9 PO |
392 | expect(kbd.onkeyevent).to.have.been.calledOnce; |
393 | expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0x61, "KeyA", true); | |
394 | kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'})); | |
395 | expect(kbd.onkeyevent).to.have.been.calledThrice; | |
396 | expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffe3, "ControlLeft", true); | |
397 | expect(kbd.onkeyevent.thirdCall).to.have.been.calledWith(0x61, "KeyA", false); | |
398 | ||
399 | // Check that the timer is properly dead | |
c9765e50 | 400 | kbd.onkeyevent.resetHistory(); |
b22c9ef9 PO |
401 | this.clock.tick(100); |
402 | expect(kbd.onkeyevent).to.not.have.been.called; | |
403 | }); | |
404 | ||
405 | it('should generate AltGraph for quick Ctrl+Alt sequence', function () { | |
2b5f94fa | 406 | const kbd = new Keyboard(document); |
b22c9ef9 PO |
407 | kbd.onkeyevent = sinon.spy(); |
408 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1, timeStamp: Date.now()})); | |
409 | this.clock.tick(20); | |
410 | kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2, timeStamp: Date.now()})); | |
411 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
412 | expect(kbd.onkeyevent).to.have.been.calledWith(0xfe03, 'AltRight', true); | |
413 | ||
414 | // Check that the timer is properly dead | |
c9765e50 | 415 | kbd.onkeyevent.resetHistory(); |
b22c9ef9 PO |
416 | this.clock.tick(100); |
417 | expect(kbd.onkeyevent).to.not.have.been.called; | |
418 | }); | |
419 | ||
420 | it('should generate Ctrl, Alt for slow Ctrl+Alt sequence', function () { | |
2b5f94fa | 421 | const kbd = new Keyboard(document); |
b22c9ef9 PO |
422 | kbd.onkeyevent = sinon.spy(); |
423 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1, timeStamp: Date.now()})); | |
424 | this.clock.tick(60); | |
425 | kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2, timeStamp: Date.now()})); | |
426 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
427 | expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true); | |
428 | expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffea, "AltRight", true); | |
429 | ||
430 | // Check that the timer is properly dead | |
c9765e50 | 431 | kbd.onkeyevent.resetHistory(); |
b22c9ef9 PO |
432 | this.clock.tick(100); |
433 | expect(kbd.onkeyevent).to.not.have.been.called; | |
434 | }); | |
435 | ||
436 | it('should pass through single Alt', function () { | |
2b5f94fa | 437 | const kbd = new Keyboard(document); |
b22c9ef9 PO |
438 | kbd.onkeyevent = sinon.spy(); |
439 | kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2})); | |
440 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
441 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffea, 'AltRight', true); | |
442 | }); | |
443 | ||
444 | it('should pass through single AltGr', function () { | |
2b5f94fa | 445 | const kbd = new Keyboard(document); |
b22c9ef9 PO |
446 | kbd.onkeyevent = sinon.spy(); |
447 | kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'AltGraph', location: 2})); | |
448 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
449 | expect(kbd.onkeyevent).to.have.been.calledWith(0xfe03, 'AltRight', true); | |
f00b6fb6 | 450 | }); |
451 | }); | |
ccb511a5 PO |
452 | |
453 | describe('Missing Shift keyup on Windows', function () { | |
454 | let origNavigator; | |
455 | beforeEach(function () { | |
456 | // window.navigator is a protected read-only property in many | |
457 | // environments, so we need to redefine it whilst running these | |
458 | // tests. | |
459 | origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); | |
ccb511a5 PO |
460 | |
461 | Object.defineProperty(window, "navigator", {value: {}}); | |
462 | if (window.navigator.platform !== undefined) { | |
463 | // Object.defineProperty() doesn't work properly in old | |
464 | // versions of Chrome | |
465 | this.skip(); | |
466 | } | |
467 | ||
468 | window.navigator.platform = "Windows x86_64"; | |
469 | ||
470 | this.clock = sinon.useFakeTimers(); | |
471 | }); | |
472 | afterEach(function () { | |
eb05b45b PO |
473 | if (origNavigator !== undefined) { |
474 | Object.defineProperty(window, "navigator", origNavigator); | |
475 | } | |
476 | if (this.clock !== undefined) { | |
477 | this.clock.restore(); | |
478 | } | |
ccb511a5 PO |
479 | }); |
480 | ||
481 | it('should fake a left Shift keyup', function () { | |
482 | const kbd = new Keyboard(document); | |
483 | kbd.onkeyevent = sinon.spy(); | |
484 | ||
485 | kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftLeft', key: 'Shift', location: 1})); | |
486 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
487 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', true); | |
488 | kbd.onkeyevent.resetHistory(); | |
489 | ||
490 | kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftRight', key: 'Shift', location: 2})); | |
491 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
492 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', true); | |
493 | kbd.onkeyevent.resetHistory(); | |
494 | ||
495 | kbd._handleKeyUp(keyevent('keyup', {code: 'ShiftLeft', key: 'Shift', location: 1})); | |
496 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
497 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', false); | |
498 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', false); | |
499 | }); | |
500 | ||
501 | it('should fake a right Shift keyup', function () { | |
502 | const kbd = new Keyboard(document); | |
503 | kbd.onkeyevent = sinon.spy(); | |
504 | ||
505 | kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftLeft', key: 'Shift', location: 1})); | |
506 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
507 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', true); | |
508 | kbd.onkeyevent.resetHistory(); | |
509 | ||
510 | kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftRight', key: 'Shift', location: 2})); | |
511 | expect(kbd.onkeyevent).to.have.been.calledOnce; | |
512 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', true); | |
513 | kbd.onkeyevent.resetHistory(); | |
514 | ||
515 | kbd._handleKeyUp(keyevent('keyup', {code: 'ShiftRight', key: 'Shift', location: 2})); | |
516 | expect(kbd.onkeyevent).to.have.been.calledTwice; | |
517 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', false); | |
518 | expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', false); | |
519 | }); | |
520 | }); | |
f00b6fb6 | 521 | }); |