| const expect = chai.expect; |
| |
| import Mouse from '../core/input/mouse.js'; |
| |
| describe('Mouse Event Handling', function () { |
| "use strict"; |
| |
| let target; |
| |
| beforeEach(function () { |
| // For these tests we can assume that the canvas is 100x100 |
| // located at coordinates 10x10 |
| target = document.createElement('canvas'); |
| target.style.position = "absolute"; |
| target.style.top = "10px"; |
| target.style.left = "10px"; |
| target.style.width = "100px"; |
| target.style.height = "100px"; |
| document.body.appendChild(target); |
| }); |
| afterEach(function () { |
| document.body.removeChild(target); |
| target = null; |
| }); |
| |
| // The real constructors might not work everywhere we |
| // want to run these tests |
| const mouseevent = (typeArg, MouseEventInit) => { |
| const e = { type: typeArg }; |
| for (let key in MouseEventInit) { |
| e[key] = MouseEventInit[key]; |
| } |
| e.stopPropagation = sinon.spy(); |
| e.preventDefault = sinon.spy(); |
| return e; |
| }; |
| const touchevent = mouseevent; |
| |
| describe('Decode Mouse Events', function () { |
| it('should decode mousedown events', function (done) { |
| const mouse = new Mouse(target); |
| mouse.onmousebutton = (x, y, down, bmask) => { |
| expect(bmask).to.be.equal(0x01); |
| expect(down).to.be.equal(1); |
| done(); |
| }; |
| mouse._handleMouseDown(mouseevent('mousedown', { button: '0x01' })); |
| }); |
| it('should decode mouseup events', function (done) { |
| let calls = 0; |
| const mouse = new Mouse(target); |
| mouse.onmousebutton = (x, y, down, bmask) => { |
| expect(bmask).to.be.equal(0x01); |
| if (calls++ === 1) { |
| expect(down).to.not.be.equal(1); |
| done(); |
| } |
| }; |
| mouse._handleMouseDown(mouseevent('mousedown', { button: '0x01' })); |
| mouse._handleMouseUp(mouseevent('mouseup', { button: '0x01' })); |
| }); |
| it('should decode mousemove events', function (done) { |
| const mouse = new Mouse(target); |
| mouse.onmousemove = (x, y) => { |
| // Note that target relative coordinates are sent |
| expect(x).to.be.equal(40); |
| expect(y).to.be.equal(10); |
| done(); |
| }; |
| mouse._handleMouseMove(mouseevent('mousemove', |
| { clientX: 50, clientY: 20 })); |
| }); |
| it('should decode mousewheel events', function (done) { |
| let calls = 0; |
| const mouse = new Mouse(target); |
| mouse.onmousebutton = (x, y, down, bmask) => { |
| calls++; |
| expect(bmask).to.be.equal(1<<6); |
| if (calls === 1) { |
| expect(down).to.be.equal(1); |
| } else if (calls === 2) { |
| expect(down).to.not.be.equal(1); |
| done(); |
| } |
| }; |
| mouse._handleMouseWheel(mouseevent('mousewheel', |
| { deltaX: 50, deltaY: 0, |
| deltaMode: 0})); |
| }); |
| }); |
| |
| describe('Double-click for Touch', function () { |
| |
| beforeEach(function () { this.clock = sinon.useFakeTimers(); }); |
| afterEach(function () { this.clock.restore(); }); |
| |
| it('should use same pos for 2nd tap if close enough', function (done) { |
| let calls = 0; |
| const mouse = new Mouse(target); |
| mouse.onmousebutton = (x, y, down, bmask) => { |
| calls++; |
| if (calls === 1) { |
| expect(down).to.be.equal(1); |
| expect(x).to.be.equal(68); |
| expect(y).to.be.equal(36); |
| } else if (calls === 3) { |
| expect(down).to.be.equal(1); |
| expect(x).to.be.equal(68); |
| expect(y).to.be.equal(36); |
| done(); |
| } |
| }; |
| // touch events are sent in an array of events |
| // with one item for each touch point |
| mouse._handleMouseDown(touchevent( |
| 'touchstart', { touches: [{ clientX: 78, clientY: 46 }]})); |
| this.clock.tick(10); |
| mouse._handleMouseUp(touchevent( |
| 'touchend', { touches: [{ clientX: 79, clientY: 45 }]})); |
| this.clock.tick(200); |
| mouse._handleMouseDown(touchevent( |
| 'touchstart', { touches: [{ clientX: 67, clientY: 35 }]})); |
| this.clock.tick(10); |
| mouse._handleMouseUp(touchevent( |
| 'touchend', { touches: [{ clientX: 66, clientY: 36 }]})); |
| }); |
| |
| it('should not modify 2nd tap pos if far apart', function (done) { |
| let calls = 0; |
| const mouse = new Mouse(target); |
| mouse.onmousebutton = (x, y, down, bmask) => { |
| calls++; |
| if (calls === 1) { |
| expect(down).to.be.equal(1); |
| expect(x).to.be.equal(68); |
| expect(y).to.be.equal(36); |
| } else if (calls === 3) { |
| expect(down).to.be.equal(1); |
| expect(x).to.not.be.equal(68); |
| expect(y).to.not.be.equal(36); |
| done(); |
| } |
| }; |
| mouse._handleMouseDown(touchevent( |
| 'touchstart', { touches: [{ clientX: 78, clientY: 46 }]})); |
| this.clock.tick(10); |
| mouse._handleMouseUp(touchevent( |
| 'touchend', { touches: [{ clientX: 79, clientY: 45 }]})); |
| this.clock.tick(200); |
| mouse._handleMouseDown(touchevent( |
| 'touchstart', { touches: [{ clientX: 57, clientY: 35 }]})); |
| this.clock.tick(10); |
| mouse._handleMouseUp(touchevent( |
| 'touchend', { touches: [{ clientX: 56, clientY: 36 }]})); |
| }); |
| |
| it('should not modify 2nd tap pos if not soon enough', function (done) { |
| let calls = 0; |
| const mouse = new Mouse(target); |
| mouse.onmousebutton = (x, y, down, bmask) => { |
| calls++; |
| if (calls === 1) { |
| expect(down).to.be.equal(1); |
| expect(x).to.be.equal(68); |
| expect(y).to.be.equal(36); |
| } else if (calls === 3) { |
| expect(down).to.be.equal(1); |
| expect(x).to.not.be.equal(68); |
| expect(y).to.not.be.equal(36); |
| done(); |
| } |
| }; |
| mouse._handleMouseDown(touchevent( |
| 'touchstart', { touches: [{ clientX: 78, clientY: 46 }]})); |
| this.clock.tick(10); |
| mouse._handleMouseUp(touchevent( |
| 'touchend', { touches: [{ clientX: 79, clientY: 45 }]})); |
| this.clock.tick(500); |
| mouse._handleMouseDown(touchevent( |
| 'touchstart', { touches: [{ clientX: 67, clientY: 35 }]})); |
| this.clock.tick(10); |
| mouse._handleMouseUp(touchevent( |
| 'touchend', { touches: [{ clientX: 66, clientY: 36 }]})); |
| }); |
| |
| it('should not modify 2nd tap pos if not touch', function (done) { |
| let calls = 0; |
| const mouse = new Mouse(target); |
| mouse.onmousebutton = (x, y, down, bmask) => { |
| calls++; |
| if (calls === 1) { |
| expect(down).to.be.equal(1); |
| expect(x).to.be.equal(68); |
| expect(y).to.be.equal(36); |
| } else if (calls === 3) { |
| expect(down).to.be.equal(1); |
| expect(x).to.not.be.equal(68); |
| expect(y).to.not.be.equal(36); |
| done(); |
| } |
| }; |
| mouse._handleMouseDown(mouseevent( |
| 'mousedown', { button: '0x01', clientX: 78, clientY: 46 })); |
| this.clock.tick(10); |
| mouse._handleMouseUp(mouseevent( |
| 'mouseup', { button: '0x01', clientX: 79, clientY: 45 })); |
| this.clock.tick(200); |
| mouse._handleMouseDown(mouseevent( |
| 'mousedown', { button: '0x01', clientX: 67, clientY: 35 })); |
| this.clock.tick(10); |
| mouse._handleMouseUp(mouseevent( |
| 'mouseup', { button: '0x01', clientX: 66, clientY: 36 })); |
| }); |
| |
| }); |
| |
| describe('Accumulate mouse wheel events with small delta', function () { |
| |
| beforeEach(function () { this.clock = sinon.useFakeTimers(); }); |
| afterEach(function () { this.clock.restore(); }); |
| |
| it('should accumulate wheel events if small enough', function () { |
| const mouse = new Mouse(target); |
| mouse.onmousebutton = sinon.spy(); |
| |
| mouse._handleMouseWheel(mouseevent( |
| 'mousewheel', { clientX: 18, clientY: 40, |
| deltaX: 4, deltaY: 0, deltaMode: 0 })); |
| this.clock.tick(10); |
| mouse._handleMouseWheel(mouseevent( |
| 'mousewheel', { clientX: 18, clientY: 40, |
| deltaX: 4, deltaY: 0, deltaMode: 0 })); |
| |
| // threshold is 10 |
| expect(mouse._accumulatedWheelDeltaX).to.be.equal(8); |
| |
| this.clock.tick(10); |
| mouse._handleMouseWheel(mouseevent( |
| 'mousewheel', { clientX: 18, clientY: 40, |
| deltaX: 4, deltaY: 0, deltaMode: 0 })); |
| |
| expect(mouse.onmousebutton).to.have.callCount(2); // mouse down and up |
| |
| this.clock.tick(10); |
| mouse._handleMouseWheel(mouseevent( |
| 'mousewheel', { clientX: 18, clientY: 40, |
| deltaX: 4, deltaY: 9, deltaMode: 0 })); |
| |
| expect(mouse._accumulatedWheelDeltaX).to.be.equal(4); |
| expect(mouse._accumulatedWheelDeltaY).to.be.equal(9); |
| |
| expect(mouse.onmousebutton).to.have.callCount(2); // still |
| }); |
| |
| it('should not accumulate large wheel events', function () { |
| const mouse = new Mouse(target); |
| mouse.onmousebutton = sinon.spy(); |
| |
| mouse._handleMouseWheel(mouseevent( |
| 'mousewheel', { clientX: 18, clientY: 40, |
| deltaX: 11, deltaY: 0, deltaMode: 0 })); |
| this.clock.tick(10); |
| mouse._handleMouseWheel(mouseevent( |
| 'mousewheel', { clientX: 18, clientY: 40, |
| deltaX: 0, deltaY: 70, deltaMode: 0 })); |
| this.clock.tick(10); |
| mouse._handleMouseWheel(mouseevent( |
| 'mousewheel', { clientX: 18, clientY: 40, |
| deltaX: 400, deltaY: 400, deltaMode: 0 })); |
| |
| expect(mouse.onmousebutton).to.have.callCount(8); // mouse down and up |
| }); |
| |
| it('should send even small wheel events after a timeout', function () { |
| const mouse = new Mouse(target); |
| mouse.onmousebutton = sinon.spy(); |
| |
| mouse._handleMouseWheel(mouseevent( |
| 'mousewheel', { clientX: 18, clientY: 40, |
| deltaX: 1, deltaY: 0, deltaMode: 0 })); |
| this.clock.tick(51); // timeout on 50 ms |
| |
| expect(mouse.onmousebutton).to.have.callCount(2); // mouse down and up |
| }); |
| |
| it('should account for non-zero deltaMode', function () { |
| const mouse = new Mouse(target); |
| mouse.onmousebutton = sinon.spy(); |
| |
| mouse._handleMouseWheel(mouseevent( |
| 'mousewheel', { clientX: 18, clientY: 40, |
| deltaX: 0, deltaY: 2, deltaMode: 1 })); |
| |
| this.clock.tick(10); |
| |
| mouse._handleMouseWheel(mouseevent( |
| 'mousewheel', { clientX: 18, clientY: 40, |
| deltaX: 1, deltaY: 0, deltaMode: 2 })); |
| |
| expect(mouse.onmousebutton).to.have.callCount(4); // mouse down and up |
| }); |
| }); |
| |
| }); |