]> git.proxmox.com Git - mirror_novnc.git/blame - tests/test.mouse.js
Properly limit mouse moves to once every 17 ms
[mirror_novnc.git] / tests / test.mouse.js
CommitLineData
2b5f94fa 1const expect = chai.expect;
c509e6d9
SM
2
3import Mouse from '../core/input/mouse.js';
c509e6d9 4
2c5491e1 5describe('Mouse Event Handling', function () {
c509e6d9
SM
6 "use strict";
7
7407c1f4
PO
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 });
c509e6d9
SM
25
26 // The real constructors might not work everywhere we
27 // want to run these tests
651c23ec 28 const mouseevent = (typeArg, MouseEventInit) => {
2b5f94fa
JD
29 const e = { type: typeArg };
30 for (let key in MouseEventInit) {
c509e6d9
SM
31 e[key] = MouseEventInit[key];
32 }
33 e.stopPropagation = sinon.spy();
34 e.preventDefault = sinon.spy();
35 return e;
36 };
2b5f94fa 37 const touchevent = mouseevent;
c509e6d9 38
2c5491e1
PO
39 describe('Decode Mouse Events', function () {
40 it('should decode mousedown events', function (done) {
2b5f94fa 41 const mouse = new Mouse(target);
651c23ec 42 mouse.onmousebutton = (x, y, down, bmask) => {
747b4623
PO
43 expect(bmask).to.be.equal(0x01);
44 expect(down).to.be.equal(1);
45 done();
46 };
c509e6d9
SM
47 mouse._handleMouseDown(mouseevent('mousedown', { button: '0x01' }));
48 });
2c5491e1 49 it('should decode mouseup events', function (done) {
2b5f94fa
JD
50 let calls = 0;
51 const mouse = new Mouse(target);
651c23ec 52 mouse.onmousebutton = (x, y, down, bmask) => {
747b4623
PO
53 expect(bmask).to.be.equal(0x01);
54 if (calls++ === 1) {
55 expect(down).to.not.be.equal(1);
56 done();
3d7bb020 57 }
747b4623 58 };
c509e6d9
SM
59 mouse._handleMouseDown(mouseevent('mousedown', { button: '0x01' }));
60 mouse._handleMouseUp(mouseevent('mouseup', { button: '0x01' }));
61 });
2c5491e1 62 it('should decode mousemove events', function (done) {
2b5f94fa 63 const mouse = new Mouse(target);
651c23ec 64 mouse.onmousemove = (x, y) => {
747b4623
PO
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 };
c509e6d9
SM
70 mouse._handleMouseMove(mouseevent('mousemove',
71 { clientX: 50, clientY: 20 }));
72 });
2c5491e1 73 it('should decode mousewheel events', function (done) {
2b5f94fa
JD
74 let calls = 0;
75 const mouse = new Mouse(target);
651c23ec 76 mouse.onmousebutton = (x, y, down, bmask) => {
747b4623
PO
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();
3d7bb020 84 }
747b4623 85 };
c509e6d9
SM
86 mouse._handleMouseWheel(mouseevent('mousewheel',
87 { deltaX: 50, deltaY: 0,
88 deltaMode: 0}));
89 });
90 });
91
2c5491e1 92 describe('Double-click for Touch', function () {
c509e6d9
SM
93
94 beforeEach(function () { this.clock = sinon.useFakeTimers(); });
95 afterEach(function () { this.clock.restore(); });
96
2c5491e1 97 it('should use same pos for 2nd tap if close enough', function (done) {
2b5f94fa
JD
98 let calls = 0;
99 const mouse = new Mouse(target);
651c23ec 100 mouse.onmousebutton = (x, y, down, bmask) => {
747b4623
PO
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();
3d7bb020 111 }
747b4623 112 };
c509e6d9
SM
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
2c5491e1 128 it('should not modify 2nd tap pos if far apart', function (done) {
2b5f94fa
JD
129 let calls = 0;
130 const mouse = new Mouse(target);
651c23ec 131 mouse.onmousebutton = (x, y, down, bmask) => {
747b4623
PO
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();
3d7bb020 142 }
747b4623 143 };
c509e6d9
SM
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
2c5491e1 157 it('should not modify 2nd tap pos if not soon enough', function (done) {
2b5f94fa
JD
158 let calls = 0;
159 const mouse = new Mouse(target);
651c23ec 160 mouse.onmousebutton = (x, y, down, bmask) => {
747b4623
PO
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();
3d7bb020 171 }
747b4623 172 };
c509e6d9
SM
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
2c5491e1 186 it('should not modify 2nd tap pos if not touch', function (done) {
2b5f94fa
JD
187 let calls = 0;
188 const mouse = new Mouse(target);
651c23ec 189 mouse.onmousebutton = (x, y, down, bmask) => {
747b4623
PO
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();
3d7bb020 200 }
747b4623 201 };
c509e6d9
SM
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
2c5491e1 217 describe('Accumulate mouse wheel events with small delta', function () {
28b004fd
SM
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 () {
2b5f94fa 223 const mouse = new Mouse(target);
747b4623 224 mouse.onmousebutton = sinon.spy();
28b004fd
SM
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
747b4623 242 expect(mouse.onmousebutton).to.have.callCount(2); // mouse down and up
28b004fd
SM
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
747b4623 252 expect(mouse.onmousebutton).to.have.callCount(2); // still
28b004fd
SM
253 });
254
255 it('should not accumulate large wheel events', function () {
2b5f94fa 256 const mouse = new Mouse(target);
747b4623 257 mouse.onmousebutton = sinon.spy();
28b004fd
SM
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
747b4623 271 expect(mouse.onmousebutton).to.have.callCount(8); // mouse down and up
28b004fd
SM
272 });
273
274 it('should send even small wheel events after a timeout', function () {
2b5f94fa 275 const mouse = new Mouse(target);
747b4623 276 mouse.onmousebutton = sinon.spy();
28b004fd
SM
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
747b4623 283 expect(mouse.onmousebutton).to.have.callCount(2); // mouse down and up
28b004fd
SM
284 });
285
286 it('should account for non-zero deltaMode', function () {
2b5f94fa 287 const mouse = new Mouse(target);
747b4623 288 mouse.onmousebutton = sinon.spy();
28b004fd
SM
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
747b4623 300 expect(mouse.onmousebutton).to.have.callCount(4); // mouse down and up
28b004fd
SM
301 });
302 });
c509e6d9 303});