]>
Commit | Line | Data |
---|---|---|
f00b6fb6 | 1 | var assert = chai.assert; |
2 | var expect = chai.expect; | |
3 | ||
f7363fd2 | 4 | import { Keyboard } from '../core/input/devices.js'; |
dfae3209 SR |
5 | import keysyms from '../core/input/keysymdef.js'; |
6 | import * as KeyboardUtil from '../core/input/util.js'; | |
7 | ||
31f169e8 | 8 | /* jshint newcap: false, expr: true */ |
f7363fd2 | 9 | describe('Key Event Handling', function() { |
f00b6fb6 | 10 | "use strict"; |
f00b6fb6 | 11 | |
f7363fd2 PO |
12 | // The real KeyboardEvent constructor might not work everywhere we |
13 | // want to run these tests | |
14 | function keyevent(typeArg, KeyboardEventInit) { | |
15 | var e = { type: typeArg }; | |
16 | for (var key in KeyboardEventInit) { | |
17 | e[key] = KeyboardEventInit[key]; | |
18 | } | |
19 | e.stopPropagation = sinon.spy(); | |
20 | e.preventDefault = sinon.spy(); | |
21 | return e; | |
22 | }; | |
f00b6fb6 | 23 | |
f7363fd2 PO |
24 | describe('Decode Keyboard Events', function() { |
25 | it('should decode keydown events', function(done) { | |
26 | var kbd = new Keyboard({ | |
27 | onKeyEvent: function(keysym, code, down) { | |
28 | expect(keysym).to.be.equal(0x61); | |
29 | expect(code).to.be.equal('KeyA'); | |
30 | expect(down).to.be.equal(true); | |
31 | done(); | |
32 | }}); | |
33 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); | |
34 | }); | |
35 | it('should decode keyup events', function(done) { | |
36 | var calls = 0; | |
37 | var kbd = new Keyboard({ | |
38 | onKeyEvent: function(keysym, code, down) { | |
39 | expect(keysym).to.be.equal(0x61); | |
40 | expect(code).to.be.equal('KeyA'); | |
41 | if (calls++ === 1) { | |
42 | expect(down).to.be.equal(false); | |
43 | done(); | |
44 | } | |
45 | }}); | |
46 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); | |
47 | kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'})); | |
48 | }); | |
49 | ||
50 | describe('Legacy keypress Events', function() { | |
51 | it('should wait for keypress when needed', function() { | |
52 | var callback = sinon.spy(); | |
53 | var kbd = new Keyboard({onKeyEvent: callback}); | |
54 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41})); | |
55 | expect(callback).to.not.have.been.called; | |
56 | }); | |
57 | it('should decode keypress events', function(done) { | |
58 | var kbd = new Keyboard({ | |
59 | onKeyEvent: function(keysym, code, down) { | |
60 | expect(keysym).to.be.equal(0x61); | |
61 | expect(code).to.be.equal('KeyA'); | |
62 | expect(down).to.be.equal(true); | |
63 | done(); | |
64 | }}); | |
65 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41})); | |
66 | kbd._handleKeyPress(keyevent('keypress', {code: 'KeyA', charCode: 0x61})); | |
f00b6fb6 | 67 | }); |
9fce233d PO |
68 | it('should ignore keypress with different code', function() { |
69 | var callback = sinon.spy(); | |
70 | var kbd = new Keyboard({onKeyEvent: callback}); | |
71 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41})); | |
72 | kbd._handleKeyPress(keyevent('keypress', {code: 'KeyB', charCode: 0x61})); | |
73 | expect(callback).to.not.have.been.called; | |
74 | }); | |
75 | it('should handle keypress with missing code', function(done) { | |
76 | var kbd = new Keyboard({ | |
77 | onKeyEvent: function(keysym, code, down) { | |
78 | expect(keysym).to.be.equal(0x61); | |
79 | expect(code).to.be.equal('KeyA'); | |
80 | expect(down).to.be.equal(true); | |
81 | done(); | |
82 | }}); | |
83 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41})); | |
84 | kbd._handleKeyPress(keyevent('keypress', {charCode: 0x61})); | |
85 | }); | |
f00b6fb6 | 86 | }); |
f00b6fb6 | 87 | |
f7363fd2 PO |
88 | describe('suppress the right events at the right time', function() { |
89 | it('should suppress anything with a valid key', function() { | |
90 | var kbd = new Keyboard({}); | |
91 | var evt = keyevent('keydown', {code: 'KeyA', key: 'a'}); | |
92 | kbd._handleKeyDown(evt); | |
93 | expect(evt.preventDefault).to.have.been.called; | |
94 | evt = keyevent('keyup', {code: 'KeyA', key: 'a'}); | |
95 | kbd._handleKeyUp(evt); | |
96 | expect(evt.preventDefault).to.have.been.called; | |
97 | }); | |
98 | it('should not suppress keys without key', function() { | |
99 | var kbd = new Keyboard({}); | |
100 | var evt = keyevent('keydown', {code: 'KeyA', keyCode: 0x41}); | |
101 | kbd._handleKeyDown(evt); | |
102 | expect(evt.preventDefault).to.not.have.been.called; | |
103 | }); | |
104 | it('should suppress the following keypress event', function() { | |
105 | var kbd = new Keyboard({}); | |
106 | var evt = keyevent('keydown', {code: 'KeyA', keyCode: 0x41}); | |
107 | kbd._handleKeyDown(evt); | |
108 | var evt = keyevent('keypress', {code: 'KeyA', charCode: 0x41}); | |
109 | kbd._handleKeyPress(evt); | |
110 | expect(evt.preventDefault).to.have.been.called; | |
f00b6fb6 | 111 | }); |
112 | }); | |
f00b6fb6 | 113 | }); |
114 | ||
f00b6fb6 | 115 | describe('Track Key State', function() { |
f7363fd2 PO |
116 | it('should send release using the same keysym as the press', function(done) { |
117 | var kbd = new Keyboard({ | |
118 | onKeyEvent: function(keysym, code, down) { | |
119 | expect(keysym).to.be.equal(0x61); | |
120 | expect(code).to.be.equal('KeyA'); | |
121 | if (!down) { | |
122 | done(); | |
123 | } | |
124 | }}); | |
125 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); | |
126 | kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'b'})); | |
f00b6fb6 | 127 | }); |
f7363fd2 PO |
128 | it('should do nothing on keyup events if no keys are down', function() { |
129 | var callback = sinon.spy(); | |
130 | var kbd = new Keyboard({onKeyEvent: callback}); | |
131 | kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'})); | |
132 | expect(callback).to.not.have.been.called; | |
133 | }); | |
134 | it('should send a key release for each key press with the same code', function() { | |
135 | var callback = sinon.spy(); | |
136 | var kbd = new Keyboard({onKeyEvent: callback}); | |
137 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); | |
138 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'b'})); | |
139 | kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA'})); | |
140 | expect(callback.callCount).to.be.equal(4); | |
f00b6fb6 | 141 | }); |
f7363fd2 | 142 | }); |
f00b6fb6 | 143 | |
f7363fd2 PO |
144 | describe('Escape Modifiers', function() { |
145 | var origNavigator; | |
146 | beforeEach(function () { | |
147 | // window.navigator is a protected read-only property in many | |
148 | // environments, so we need to redefine it whilst running these | |
149 | // tests. | |
150 | origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); | |
151 | if (origNavigator === undefined) { | |
152 | // Object.getOwnPropertyDescriptor() doesn't work | |
153 | // properly in any version of IE | |
154 | this.skip(); | |
155 | } | |
156 | ||
157 | Object.defineProperty(window, "navigator", {value: {}}); | |
158 | if (window.navigator.platform !== undefined) { | |
159 | // Object.defineProperty() doesn't work properly in old | |
160 | // versions of Chrome | |
161 | this.skip(); | |
162 | } | |
163 | ||
164 | window.navigator.platform = "Windows x86_64"; | |
165 | }); | |
166 | afterEach(function () { | |
167 | Object.defineProperty(window, "navigator", origNavigator); | |
168 | }); | |
169 | ||
170 | it('should generate fake undo/redo events on press when a char modifier is down', function() { | |
f00b6fb6 | 171 | var times_called = 0; |
f7363fd2 PO |
172 | var kbd = new Keyboard({ |
173 | onKeyEvent: function(keysym, code, down) { | |
174 | switch(times_called++) { | |
175 | case 0: | |
176 | expect(keysym).to.be.equal(0xFFE3); | |
177 | expect(code).to.be.equal('ControlLeft'); | |
178 | expect(down).to.be.equal(true); | |
179 | break; | |
180 | case 1: | |
181 | expect(keysym).to.be.equal(0xFFE9); | |
182 | expect(code).to.be.equal('AltLeft'); | |
183 | expect(down).to.be.equal(true); | |
184 | break; | |
185 | case 2: | |
186 | expect(keysym).to.be.equal(0xFFE9); | |
187 | expect(code).to.be.equal('Unidentified'); | |
188 | expect(down).to.be.equal(false); | |
189 | break; | |
190 | case 3: | |
191 | expect(keysym).to.be.equal(0xFFE3); | |
192 | expect(code).to.be.equal('Unidentified'); | |
193 | expect(down).to.be.equal(false); | |
194 | break; | |
195 | case 4: | |
196 | expect(keysym).to.be.equal(0x61); | |
197 | expect(code).to.be.equal('KeyA'); | |
198 | expect(down).to.be.equal(true); | |
199 | break; | |
200 | case 5: | |
201 | expect(keysym).to.be.equal(0xFFE9); | |
202 | expect(code).to.be.equal('Unidentified'); | |
203 | expect(down).to.be.equal(true); | |
204 | break; | |
205 | case 6: | |
206 | expect(keysym).to.be.equal(0xFFE3); | |
207 | expect(code).to.be.equal('Unidentified'); | |
208 | expect(down).to.be.equal(true); | |
209 | break; | |
210 | } | |
211 | }}); | |
212 | // First the modifier combo | |
213 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control'})); | |
214 | kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt'})); | |
215 | // Next a normal character | |
216 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); | |
217 | expect(times_called).to.be.equal(7); | |
218 | }); | |
219 | it('should no do anything on key release', function() { | |
f00b6fb6 | 220 | var times_called = 0; |
f7363fd2 PO |
221 | var kbd = new Keyboard({ |
222 | onKeyEvent: function(keysym, code, down) { | |
223 | switch(times_called++) { | |
224 | case 7: | |
225 | expect(keysym).to.be.equal(0x61); | |
226 | expect(code).to.be.equal('KeyA'); | |
227 | expect(down).to.be.equal(false); | |
228 | break; | |
229 | } | |
230 | }}); | |
231 | // First the modifier combo | |
232 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control'})); | |
233 | kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt'})); | |
234 | // Next a normal character | |
235 | kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); | |
236 | kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'})); | |
237 | expect(times_called).to.be.equal(8); | |
238 | }); | |
239 | it('should not consider a char modifier to be down on the modifier key itself', function() { | |
f00b6fb6 | 240 | var times_called = 0; |
f7363fd2 PO |
241 | var kbd = new Keyboard({ |
242 | onKeyEvent: function(keysym, code, down) { | |
243 | switch(times_called++) { | |
244 | case 0: | |
245 | expect(keysym).to.be.equal(0xFFE3); | |
246 | expect(code).to.be.equal('ControlLeft'); | |
247 | expect(down).to.be.equal(true); | |
248 | break; | |
249 | case 1: | |
250 | expect(keysym).to.be.equal(0xFFE9); | |
251 | expect(code).to.be.equal('AltLeft'); | |
252 | expect(down).to.be.equal(true); | |
253 | break; | |
254 | case 2: | |
255 | expect(keysym).to.be.equal(0xFFE3); | |
256 | expect(code).to.be.equal('ControlLeft'); | |
257 | expect(down).to.be.equal(true); | |
258 | break; | |
259 | } | |
260 | }}); | |
261 | // First the modifier combo | |
262 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control'})); | |
263 | kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt'})); | |
264 | // Then one of the keys again | |
265 | kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control'})); | |
f00b6fb6 | 266 | expect(times_called).to.be.equal(3); |
f00b6fb6 | 267 | }); |
268 | }); | |
269 | }); |