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