]>
Commit | Line | Data |
---|---|---|
1 | const expect = chai.expect; | |
2 | ||
3 | import Mouse from '../core/input/mouse.js'; | |
4 | ||
5 | describe('Mouse Event Handling', function () { | |
6 | "use strict"; | |
7 | ||
8 | let target; | |
9 | ||
10 | beforeEach(function () { | |
11 | // For these tests we can assume that the canvas is 100x100 | |
12 | // located at coordinates 10x10 | |
13 | target = document.createElement('canvas'); | |
14 | target.style.position = "absolute"; | |
15 | target.style.top = "10px"; | |
16 | target.style.left = "10px"; | |
17 | target.style.width = "100px"; | |
18 | target.style.height = "100px"; | |
19 | document.body.appendChild(target); | |
20 | }); | |
21 | afterEach(function () { | |
22 | document.body.removeChild(target); | |
23 | target = null; | |
24 | }); | |
25 | ||
26 | // The real constructors might not work everywhere we | |
27 | // want to run these tests | |
28 | const mouseevent = (typeArg, MouseEventInit) => { | |
29 | const e = { type: typeArg }; | |
30 | for (let key in MouseEventInit) { | |
31 | e[key] = MouseEventInit[key]; | |
32 | } | |
33 | e.stopPropagation = sinon.spy(); | |
34 | e.preventDefault = sinon.spy(); | |
35 | return e; | |
36 | }; | |
37 | const touchevent = mouseevent; | |
38 | ||
39 | describe('Decode Mouse Events', function () { | |
40 | it('should decode mousedown events', function (done) { | |
41 | const mouse = new Mouse(target); | |
42 | mouse.onmousebutton = (x, y, down, bmask) => { | |
43 | expect(bmask).to.be.equal(0x01); | |
44 | expect(down).to.be.equal(1); | |
45 | done(); | |
46 | }; | |
47 | mouse._handleMouseDown(mouseevent('mousedown', { button: '0x01' })); | |
48 | }); | |
49 | it('should decode mouseup events', function (done) { | |
50 | let calls = 0; | |
51 | const mouse = new Mouse(target); | |
52 | mouse.onmousebutton = (x, y, down, bmask) => { | |
53 | expect(bmask).to.be.equal(0x01); | |
54 | if (calls++ === 1) { | |
55 | expect(down).to.not.be.equal(1); | |
56 | done(); | |
57 | } | |
58 | }; | |
59 | mouse._handleMouseDown(mouseevent('mousedown', { button: '0x01' })); | |
60 | mouse._handleMouseUp(mouseevent('mouseup', { button: '0x01' })); | |
61 | }); | |
62 | it('should decode mousemove events', function (done) { | |
63 | const mouse = new Mouse(target); | |
64 | mouse.onmousemove = (x, y) => { | |
65 | // Note that target relative coordinates are sent | |
66 | expect(x).to.be.equal(40); | |
67 | expect(y).to.be.equal(10); | |
68 | done(); | |
69 | }; | |
70 | mouse._handleMouseMove(mouseevent('mousemove', | |
71 | { clientX: 50, clientY: 20 })); | |
72 | }); | |
73 | it('should decode mousewheel events', function (done) { | |
74 | let calls = 0; | |
75 | const mouse = new Mouse(target); | |
76 | mouse.onmousebutton = (x, y, down, bmask) => { | |
77 | calls++; | |
78 | expect(bmask).to.be.equal(1<<6); | |
79 | if (calls === 1) { | |
80 | expect(down).to.be.equal(1); | |
81 | } else if (calls === 2) { | |
82 | expect(down).to.not.be.equal(1); | |
83 | done(); | |
84 | } | |
85 | }; | |
86 | mouse._handleMouseWheel(mouseevent('mousewheel', | |
87 | { deltaX: 50, deltaY: 0, | |
88 | deltaMode: 0})); | |
89 | }); | |
90 | }); | |
91 | ||
92 | describe('Double-click for Touch', function () { | |
93 | ||
94 | beforeEach(function () { this.clock = sinon.useFakeTimers(); }); | |
95 | afterEach(function () { this.clock.restore(); }); | |
96 | ||
97 | it('should use same pos for 2nd tap if close enough', function (done) { | |
98 | let calls = 0; | |
99 | const mouse = new Mouse(target); | |
100 | mouse.onmousebutton = (x, y, down, bmask) => { | |
101 | calls++; | |
102 | if (calls === 1) { | |
103 | expect(down).to.be.equal(1); | |
104 | expect(x).to.be.equal(68); | |
105 | expect(y).to.be.equal(36); | |
106 | } else if (calls === 3) { | |
107 | expect(down).to.be.equal(1); | |
108 | expect(x).to.be.equal(68); | |
109 | expect(y).to.be.equal(36); | |
110 | done(); | |
111 | } | |
112 | }; | |
113 | // touch events are sent in an array of events | |
114 | // with one item for each touch point | |
115 | mouse._handleMouseDown(touchevent( | |
116 | 'touchstart', { touches: [{ clientX: 78, clientY: 46 }]})); | |
117 | this.clock.tick(10); | |
118 | mouse._handleMouseUp(touchevent( | |
119 | 'touchend', { touches: [{ clientX: 79, clientY: 45 }]})); | |
120 | this.clock.tick(200); | |
121 | mouse._handleMouseDown(touchevent( | |
122 | 'touchstart', { touches: [{ clientX: 67, clientY: 35 }]})); | |
123 | this.clock.tick(10); | |
124 | mouse._handleMouseUp(touchevent( | |
125 | 'touchend', { touches: [{ clientX: 66, clientY: 36 }]})); | |
126 | }); | |
127 | ||
128 | it('should not modify 2nd tap pos if far apart', function (done) { | |
129 | let calls = 0; | |
130 | const mouse = new Mouse(target); | |
131 | mouse.onmousebutton = (x, y, down, bmask) => { | |
132 | calls++; | |
133 | if (calls === 1) { | |
134 | expect(down).to.be.equal(1); | |
135 | expect(x).to.be.equal(68); | |
136 | expect(y).to.be.equal(36); | |
137 | } else if (calls === 3) { | |
138 | expect(down).to.be.equal(1); | |
139 | expect(x).to.not.be.equal(68); | |
140 | expect(y).to.not.be.equal(36); | |
141 | done(); | |
142 | } | |
143 | }; | |
144 | mouse._handleMouseDown(touchevent( | |
145 | 'touchstart', { touches: [{ clientX: 78, clientY: 46 }]})); | |
146 | this.clock.tick(10); | |
147 | mouse._handleMouseUp(touchevent( | |
148 | 'touchend', { touches: [{ clientX: 79, clientY: 45 }]})); | |
149 | this.clock.tick(200); | |
150 | mouse._handleMouseDown(touchevent( | |
151 | 'touchstart', { touches: [{ clientX: 57, clientY: 35 }]})); | |
152 | this.clock.tick(10); | |
153 | mouse._handleMouseUp(touchevent( | |
154 | 'touchend', { touches: [{ clientX: 56, clientY: 36 }]})); | |
155 | }); | |
156 | ||
157 | it('should not modify 2nd tap pos if not soon enough', function (done) { | |
158 | let calls = 0; | |
159 | const mouse = new Mouse(target); | |
160 | mouse.onmousebutton = (x, y, down, bmask) => { | |
161 | calls++; | |
162 | if (calls === 1) { | |
163 | expect(down).to.be.equal(1); | |
164 | expect(x).to.be.equal(68); | |
165 | expect(y).to.be.equal(36); | |
166 | } else if (calls === 3) { | |
167 | expect(down).to.be.equal(1); | |
168 | expect(x).to.not.be.equal(68); | |
169 | expect(y).to.not.be.equal(36); | |
170 | done(); | |
171 | } | |
172 | }; | |
173 | mouse._handleMouseDown(touchevent( | |
174 | 'touchstart', { touches: [{ clientX: 78, clientY: 46 }]})); | |
175 | this.clock.tick(10); | |
176 | mouse._handleMouseUp(touchevent( | |
177 | 'touchend', { touches: [{ clientX: 79, clientY: 45 }]})); | |
178 | this.clock.tick(500); | |
179 | mouse._handleMouseDown(touchevent( | |
180 | 'touchstart', { touches: [{ clientX: 67, clientY: 35 }]})); | |
181 | this.clock.tick(10); | |
182 | mouse._handleMouseUp(touchevent( | |
183 | 'touchend', { touches: [{ clientX: 66, clientY: 36 }]})); | |
184 | }); | |
185 | ||
186 | it('should not modify 2nd tap pos if not touch', function (done) { | |
187 | let calls = 0; | |
188 | const mouse = new Mouse(target); | |
189 | mouse.onmousebutton = (x, y, down, bmask) => { | |
190 | calls++; | |
191 | if (calls === 1) { | |
192 | expect(down).to.be.equal(1); | |
193 | expect(x).to.be.equal(68); | |
194 | expect(y).to.be.equal(36); | |
195 | } else if (calls === 3) { | |
196 | expect(down).to.be.equal(1); | |
197 | expect(x).to.not.be.equal(68); | |
198 | expect(y).to.not.be.equal(36); | |
199 | done(); | |
200 | } | |
201 | }; | |
202 | mouse._handleMouseDown(mouseevent( | |
203 | 'mousedown', { button: '0x01', clientX: 78, clientY: 46 })); | |
204 | this.clock.tick(10); | |
205 | mouse._handleMouseUp(mouseevent( | |
206 | 'mouseup', { button: '0x01', clientX: 79, clientY: 45 })); | |
207 | this.clock.tick(200); | |
208 | mouse._handleMouseDown(mouseevent( | |
209 | 'mousedown', { button: '0x01', clientX: 67, clientY: 35 })); | |
210 | this.clock.tick(10); | |
211 | mouse._handleMouseUp(mouseevent( | |
212 | 'mouseup', { button: '0x01', clientX: 66, clientY: 36 })); | |
213 | }); | |
214 | ||
215 | }); | |
216 | ||
217 | describe('Accumulate mouse wheel events with small delta', function () { | |
218 | ||
219 | beforeEach(function () { this.clock = sinon.useFakeTimers(); }); | |
220 | afterEach(function () { this.clock.restore(); }); | |
221 | ||
222 | it('should accumulate wheel events if small enough', function () { | |
223 | const mouse = new Mouse(target); | |
224 | mouse.onmousebutton = sinon.spy(); | |
225 | ||
226 | mouse._handleMouseWheel(mouseevent( | |
227 | 'mousewheel', { clientX: 18, clientY: 40, | |
228 | deltaX: 4, deltaY: 0, deltaMode: 0 })); | |
229 | this.clock.tick(10); | |
230 | mouse._handleMouseWheel(mouseevent( | |
231 | 'mousewheel', { clientX: 18, clientY: 40, | |
232 | deltaX: 4, deltaY: 0, deltaMode: 0 })); | |
233 | ||
234 | // threshold is 10 | |
235 | expect(mouse._accumulatedWheelDeltaX).to.be.equal(8); | |
236 | ||
237 | this.clock.tick(10); | |
238 | mouse._handleMouseWheel(mouseevent( | |
239 | 'mousewheel', { clientX: 18, clientY: 40, | |
240 | deltaX: 4, deltaY: 0, deltaMode: 0 })); | |
241 | ||
242 | expect(mouse.onmousebutton).to.have.callCount(2); // mouse down and up | |
243 | ||
244 | this.clock.tick(10); | |
245 | mouse._handleMouseWheel(mouseevent( | |
246 | 'mousewheel', { clientX: 18, clientY: 40, | |
247 | deltaX: 4, deltaY: 9, deltaMode: 0 })); | |
248 | ||
249 | expect(mouse._accumulatedWheelDeltaX).to.be.equal(4); | |
250 | expect(mouse._accumulatedWheelDeltaY).to.be.equal(9); | |
251 | ||
252 | expect(mouse.onmousebutton).to.have.callCount(2); // still | |
253 | }); | |
254 | ||
255 | it('should not accumulate large wheel events', function () { | |
256 | const mouse = new Mouse(target); | |
257 | mouse.onmousebutton = sinon.spy(); | |
258 | ||
259 | mouse._handleMouseWheel(mouseevent( | |
260 | 'mousewheel', { clientX: 18, clientY: 40, | |
261 | deltaX: 11, deltaY: 0, deltaMode: 0 })); | |
262 | this.clock.tick(10); | |
263 | mouse._handleMouseWheel(mouseevent( | |
264 | 'mousewheel', { clientX: 18, clientY: 40, | |
265 | deltaX: 0, deltaY: 70, deltaMode: 0 })); | |
266 | this.clock.tick(10); | |
267 | mouse._handleMouseWheel(mouseevent( | |
268 | 'mousewheel', { clientX: 18, clientY: 40, | |
269 | deltaX: 400, deltaY: 400, deltaMode: 0 })); | |
270 | ||
271 | expect(mouse.onmousebutton).to.have.callCount(8); // mouse down and up | |
272 | }); | |
273 | ||
274 | it('should send even small wheel events after a timeout', function () { | |
275 | const mouse = new Mouse(target); | |
276 | mouse.onmousebutton = sinon.spy(); | |
277 | ||
278 | mouse._handleMouseWheel(mouseevent( | |
279 | 'mousewheel', { clientX: 18, clientY: 40, | |
280 | deltaX: 1, deltaY: 0, deltaMode: 0 })); | |
281 | this.clock.tick(51); // timeout on 50 ms | |
282 | ||
283 | expect(mouse.onmousebutton).to.have.callCount(2); // mouse down and up | |
284 | }); | |
285 | ||
286 | it('should account for non-zero deltaMode', function () { | |
287 | const mouse = new Mouse(target); | |
288 | mouse.onmousebutton = sinon.spy(); | |
289 | ||
290 | mouse._handleMouseWheel(mouseevent( | |
291 | 'mousewheel', { clientX: 18, clientY: 40, | |
292 | deltaX: 0, deltaY: 2, deltaMode: 1 })); | |
293 | ||
294 | this.clock.tick(10); | |
295 | ||
296 | mouse._handleMouseWheel(mouseevent( | |
297 | 'mousewheel', { clientX: 18, clientY: 40, | |
298 | deltaX: 1, deltaY: 0, deltaMode: 2 })); | |
299 | ||
300 | expect(mouse.onmousebutton).to.have.callCount(4); // mouse down and up | |
301 | }); | |
302 | }); | |
303 | }); |