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