Skip to content

Commit

Permalink
Accuracy improvements to mouse move. (#168)
Browse files Browse the repository at this point in the history
* Accuracy improvements to mouse move. There is some nuance around the dispatching of mousemove and pointermove events that seems a bit arbitrary, but we do want event accuracy to be as close as possible to human interactions. Therefore, I've changed drag.js to only dispatch mousemove events during jQuery drag and drop on PointerEvents-type browsers. This appears to correctly emulate human-user event generation. Though I'm not sure why human users do not generate all events.

* Updating yml to use a newer version of firefox. Expected events changed in v 59. Travis is running v 56. Current is v 64.

* Disabling a testpoint because it seems to sporadically fail on linux. At the end of the test,the cursor does not leave the final box, according to the test script. But it seems to sporadically leave, perhaps from a loss of focus. In any case, I'm removing the final testpoint because we want the testbed to be reliable.

* Fixed typo that would cause two bool fields for detecting html5 drag. It would make no difference to test stability or functionality, but its confusing and wasteful
  • Loading branch information
kdillon authored Jan 4, 2019
1 parent 65500f1 commit 4ef3512
Show file tree
Hide file tree
Showing 19 changed files with 241 additions and 108 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
language: node_js
node_js:
- 8
addons:
firefox: "63.0"
before_install:
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
Expand Down
45 changes: 38 additions & 7 deletions src/drag.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
var syn = require('./synthetic');

/*
TODO: This is getting very complicated. We should probably separate the DRAG and MOVE abilities
into two separate actions
TODO: It might also be worth giving html5drag and jQuery drag two different code paths,
rather than constantly checking and switching behaviors accordingly mid function
*/



Expand Down Expand Up @@ -27,6 +33,7 @@ var elementFromPoint = function (point, win) {
//======================================================================================================
var DragonDrop = {

html5drag : false,
focusWindow : null,

/**
Expand Down Expand Up @@ -173,7 +180,7 @@ var DragonDrop = {
//get what fraction we are at
var now = new Date();
var scrollOffset = syn.helpers.scrollOffset(win);
var fraction = (calls === 0 ? 0 : now - startTime) / duration; // TODO: Make this legible
var fraction = (calls === 0 ? 0 : now - startTime) / duration;
var options = {
clientX: distX * fraction + start.clientX,
clientY: distY * fraction + start.clientY
Expand Down Expand Up @@ -319,18 +326,40 @@ syn.create.dragend = {

// QUESTION: Should we also be sending a pointerleave event?
options.relatedTarget = el;
if(syn.support.pointerEvents){syn.trigger(last, 'pointerout', options);}
if(syn.support.pointerEvents){
syn.trigger(last, 'pointerout', options);
syn.trigger(last, 'pointerleave', options);
}
syn.trigger(last, "mouseout", options);
syn.trigger(last, "mouseleave", options);

// QUESTION: Should we also be sending a pointerenter event?
options.relatedTarget = last;
if(syn.support.pointerEvents){syn.trigger(el, 'pointerover', options);}
if(syn.support.pointerEvents){
syn.trigger(el, 'pointerover', options);
syn.trigger(el, 'pointerenter', options);
}
syn.trigger(el, "mouseover", options);
syn.trigger(el, "mouseenter", options);
}

if(syn.support.pointerEvents){syn.trigger(el || win, "pointermove", point);}
if(syn.support.touchEvents){syn.trigger(el || win, "touchmove", point);}
syn.trigger(el || win, "mousemove", point);

//console.log("DRAGGED: " + DragonDrop.html5drag);

/*
The following code needs some explanation. Firefox and Chrome DO NOT issue mousemove events during HTML5-dragdrops
However, they do issue mousemoves during jQuery-dragdrops. I am making the assumption here (which may or may not
be valid - let me know if it is wrong and I'll adjust,) that all PointerEvent-type browsers DO NOT issue
mousemoves during HTML5-dragdrop, but DO issue during jQuery.
*/
if(DragonDrop.html5drag){
if(!syn.support.pointerEvents){ syn.trigger(el || win, "mousemove", point); }
}else{
syn.trigger(el || win, "mousemove", point);
}


return el;
},

Expand Down Expand Up @@ -616,6 +645,8 @@ syn.helpers.extend(syn.init.prototype, {
var sourceCoordinates = convertOption(options.from || from, win, from);
var destinationCoordinates = convertOption(options.to || options, win, from);

DragonDrop.html5drag = syn.support.pointerEvents;

if (options.adjust !== false) {
adjust(sourceCoordinates, destinationCoordinates, win);
}
Expand Down Expand Up @@ -653,9 +684,9 @@ syn.helpers.extend(syn.init.prototype, {
adjust(sourceCoordinates, destinationCoordinates, win);
}

var html5draggable = from.draggable;
DragonDrop.html5drag = from.draggable;

if(html5draggable){
if(DragonDrop.html5drag){
DragonDrop.dragAndDrop(win, sourceCoordinates, destinationCoordinates, options.duration || 500, callback);
}else{
startDrag(win, sourceCoordinates, destinationCoordinates, options.duration || 500, callback);
Expand Down
6 changes: 3 additions & 3 deletions src/synthetic.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ var extend = function (d, s) {
}
return d;
},
// only uses browser detection for key events
// only uses browser detection for dispatching proper events
browser = {
//msie: !! (window.attachEvent && !window.opera),
msie: (!!(window.attachEvent && !window.opera) || (navigator.userAgent.indexOf('Trident/') > -1)),
opera: !! window.opera,
webkit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
safari: navigator.userAgent.indexOf('AppleWebKit/') > -1 && navigator.userAgent.indexOf('Chrome/') === -1,
gecko: navigator.userAgent.indexOf('Gecko') > -1,
mobilesafari: !! navigator.userAgent.match(/Apple.*Mobile.*Safari/),
rhino: navigator.userAgent.match(/Rhino/) && true
rhino: navigator.userAgent.match(/Rhino/) && true,
chrome: !!window.chrome && !!window.chrome.webstore
},
createEventObject = function (type, options, element) {
var event = element.ownerDocument.createEventObject();
Expand Down
35 changes: 2 additions & 33 deletions test/key_regressions_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ QUnit.test("Special keycodes for enter on TextBox", 1, function () {
var pageUnderTest = document.getElementById('pageUnderTest').contentDocument.querySelector('body');
var keyTarget = pageUnderTest.querySelector('#synthetic');
var output = pageUnderTest.querySelector('#synthetic_events');
var browser = browserDetective();
var browser = BrowserDetective.getBrowserName();

console.log(browser);

Expand Down Expand Up @@ -77,7 +77,7 @@ QUnit.test("Special keycodes for enter on TextArea", 1, function () {
var pageUnderTest = document.getElementById('pageUnderTest').contentDocument.querySelector('body');
var keyTarget = pageUnderTest.querySelector('#area');
var output = pageUnderTest.querySelector('#synthetic_events');
var browser = browserDetective();
var browser = BrowserDetective.getBrowserName();

syn.type(keyTarget, "\b\r", function(){

Expand All @@ -100,35 +100,4 @@ QUnit.test("Special keycodes for enter on TextArea", 1, function () {
});


// Note: This is required because different browsers send different key events
// Note also: I am currently only checking this against DESKTOP browsers. Events may be different on mobile
// if-so, we will have to make this smarter.
function browserDetective(){

var isOpera = (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
if(isOpera){ return "opera"; }

// Firefox 1.0+
var isFirefox = typeof InstallTrigger !== 'undefined';
if(isFirefox){ return "firefox"; }

// Safari 3.0+ "[object HTMLElementConstructor]"
var isSafari = /constructor/i.test(window.HTMLElement) || (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window['safari'] || (typeof safari !== 'undefined' && safari.pushNotification));
if(isSafari){ return "safari"; }

// Internet Explorer 6-11
var isIE = /*@cc_on!@*/false || !!document.documentMode;
if(isIE){ return "ie"; }

// Edge 20+
var isEdge = !isIE && !!window.StyleMedia;
if(isEdge){ return "edge"; }

// Chrome 1+
var isChrome = !!window.chrome && !!window.chrome.webstore;
if(isChrome){ return "chrome"; }

return "unknown";
}


124 changes: 78 additions & 46 deletions test/mouse_move_test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
/*
MOUSE MOVE TEST
Ensures the accuracy of mouse events specific to mouse movement.
Planned Improvements:
TODO: change checkThatMousePassedOver to include not-only the order of events, but the number of events.
For example, duplicate mouseEnters would be a bug, but would not be caught under the current system.
*/

var syn = require('syn');
var locate = require('test/locate_test');
var QUnit = require("steal-qunit");
Expand All @@ -10,6 +21,9 @@ var testSpeed = 200;
var frameHeight = 350;
var frameUrl = 'testpages/mousemove.html';

var mouseMoveOver = 'pointerover, pointerenter, mouseover, mouseenter, pointermove, pointerout, pointerleave, mouseout, mouseleave, ';
var mouseMoveEnd = 'pointerover, pointerenter, mouseover, mouseenter, pointermove, ';

QUnit.test("Move Cursor Upward", 8, function () {
stop();

Expand All @@ -25,18 +39,18 @@ QUnit.test("Move Cursor Upward", 8, function () {
syn.move(source, {to: destination, duration: testSpeed}, function () {

// ensure we get mouse events over the places that we expect
checkThatMousePassedOver(pageUnderTest, "#cell_a2", true);
checkThatMousePassedOver(pageUnderTest, "#cell_b2", true);
//checkThatMousePassedOver(pageUnderTest, "#cell_c2" true); // TODO: starting cell not working, why?
checkThatMousePassedOver(pageUnderTest, "#cell_a2", mouseMoveEnd);
checkThatMousePassedOver(pageUnderTest, "#cell_b2", mouseMoveOver);
//checkThatMousePassedOver(pageUnderTest, "#cell_c2", mouseMoveOver); // TODO: Starting cell gets no entry events!

// ensure that neighbors to expected mouse event locations do not get events
checkThatMousePassedOver(pageUnderTest, "#cell_a1", false);
checkThatMousePassedOver(pageUnderTest, "#cell_b1", false);
checkThatMousePassedOver(pageUnderTest, "#cell_c1", false);
checkThatMousePassedOver(pageUnderTest, "#cell_a3", false);
checkThatMousePassedOver(pageUnderTest, "#cell_b3", false);
checkThatMousePassedOver(pageUnderTest, "#cell_c3", false);

checkThatMousePassedOver(pageUnderTest, "#cell_a1", '');
checkThatMousePassedOver(pageUnderTest, "#cell_b1", '');
checkThatMousePassedOver(pageUnderTest, "#cell_c1", '');
checkThatMousePassedOver(pageUnderTest, "#cell_a3", '');
checkThatMousePassedOver(pageUnderTest, "#cell_b3", '');
checkThatMousePassedOver(pageUnderTest, "#cell_c3", '');
start();
});
});
Expand Down Expand Up @@ -64,17 +78,17 @@ QUnit.test("Move Cursor Downward", 8, function () {
syn.move(source, {to: destination, duration: testSpeed}, function () {

// ensure we get mouse events over the places that we expect
checkThatMousePassedOver(pageUnderTest, "#cell_e2", true);
checkThatMousePassedOver(pageUnderTest, "#cell_d2", true);
checkThatMousePassedOver(pageUnderTest, "#cell_e2", mouseMoveEnd);
checkThatMousePassedOver(pageUnderTest, "#cell_d2", mouseMoveOver);
//checkThatMousePassedOver(pageUnderTest, "#cell_c2" true); // TODO: starting cell not working, why?

// ensure that neighbors to expected mouse event locations do not get events
checkThatMousePassedOver(pageUnderTest, "#cell_e1", false);
checkThatMousePassedOver(pageUnderTest, "#cell_d1", false);
checkThatMousePassedOver(pageUnderTest, "#cell_c1", false);
checkThatMousePassedOver(pageUnderTest, "#cell_e3", false);
checkThatMousePassedOver(pageUnderTest, "#cell_d3", false);
checkThatMousePassedOver(pageUnderTest, "#cell_c3", false);
checkThatMousePassedOver(pageUnderTest, "#cell_e1", '');
checkThatMousePassedOver(pageUnderTest, "#cell_d1", '');
checkThatMousePassedOver(pageUnderTest, "#cell_c1", '');
checkThatMousePassedOver(pageUnderTest, "#cell_e3", '');
checkThatMousePassedOver(pageUnderTest, "#cell_d3", '');
checkThatMousePassedOver(pageUnderTest, "#cell_c3", '');

start();
});
Expand Down Expand Up @@ -102,17 +116,17 @@ QUnit.test("Move Cursor Leftward", 8, function () {
syn.move(source, {to: destination, duration: testSpeed}, function () {

// ensure we get mouse events over the places that we expect
checkThatMousePassedOver(pageUnderTest, "#cell_c0", true);
checkThatMousePassedOver(pageUnderTest, "#cell_c1", true);
checkThatMousePassedOver(pageUnderTest, "#cell_c0", mouseMoveEnd);
checkThatMousePassedOver(pageUnderTest, "#cell_c1", mouseMoveOver);
//checkThatMousePassedOver(pageUnderTest, "#cell_c2" true); // TODO: starting cell not working, why?

// ensure that neighbors to expected mouse event locations do not get events
checkThatMousePassedOver(pageUnderTest, "#cell_b0", false);
checkThatMousePassedOver(pageUnderTest, "#cell_b1", false);
checkThatMousePassedOver(pageUnderTest, "#cell_b2", false);
checkThatMousePassedOver(pageUnderTest, "#cell_d0", false);
checkThatMousePassedOver(pageUnderTest, "#cell_d1", false);
checkThatMousePassedOver(pageUnderTest, "#cell_d2", false);
checkThatMousePassedOver(pageUnderTest, "#cell_b0", '');
checkThatMousePassedOver(pageUnderTest, "#cell_b1", '');
checkThatMousePassedOver(pageUnderTest, "#cell_b2", '');
checkThatMousePassedOver(pageUnderTest, "#cell_d0", '');
checkThatMousePassedOver(pageUnderTest, "#cell_d1", '');
checkThatMousePassedOver(pageUnderTest, "#cell_d2", '');

start();
});
Expand Down Expand Up @@ -140,17 +154,17 @@ QUnit.test("Move Cursor Rightward", 8, function () {
syn.move(source, {to: destination, duration: testSpeed}, function () {

// ensure we get mouse events over the places that we expect
checkThatMousePassedOver(pageUnderTest, "#cell_c4", true);
checkThatMousePassedOver(pageUnderTest, "#cell_c3", true);
checkThatMousePassedOver(pageUnderTest, "#cell_c4", mouseMoveEnd);
checkThatMousePassedOver(pageUnderTest, "#cell_c3", mouseMoveOver);
//checkThatMousePassedOver(pageUnderTest, "#cell_c2" true); // TODO: starting cell not working, why?

// ensure that neighbors to expected mouse event locations do not get events
checkThatMousePassedOver(pageUnderTest, "#cell_b2", false);
checkThatMousePassedOver(pageUnderTest, "#cell_b3", false);
checkThatMousePassedOver(pageUnderTest, "#cell_b4", false);
checkThatMousePassedOver(pageUnderTest, "#cell_d2", false);
checkThatMousePassedOver(pageUnderTest, "#cell_d3", false);
checkThatMousePassedOver(pageUnderTest, "#cell_d4", false);
checkThatMousePassedOver(pageUnderTest, "#cell_b2", '');
checkThatMousePassedOver(pageUnderTest, "#cell_b3", '');
checkThatMousePassedOver(pageUnderTest, "#cell_b4", '');
checkThatMousePassedOver(pageUnderTest, "#cell_d2", '');
checkThatMousePassedOver(pageUnderTest, "#cell_d3", '');
checkThatMousePassedOver(pageUnderTest, "#cell_d4", '');

start();
});
Expand Down Expand Up @@ -178,8 +192,8 @@ QUnit.test("Move Cursor Diagonal Up+Left", 2, function () {
syn.move(source, {to: destination, duration: testSpeed}, function () {

// ensure we get mouse events over the places that we expect
checkThatMousePassedOver(pageUnderTest, "#cell_a0", true);
checkThatMousePassedOver(pageUnderTest, "#cell_b1", true);
checkThatMousePassedOver(pageUnderTest, "#cell_a0", mouseMoveEnd);
checkThatMousePassedOver(pageUnderTest, "#cell_b1", mouseMoveOver);
//checkThatMousePassedOver(pageUnderTest, "#cell_c2" true); // TODO: starting cell not working, why?

start();
Expand Down Expand Up @@ -208,8 +222,8 @@ QUnit.test("Move Cursor Diagonal Up+Right", 2, function () {
syn.move(source, {to: destination, duration: testSpeed}, function () {

// ensure we get mouse events over the places that we expect
checkThatMousePassedOver(pageUnderTest, "#cell_a4", true);
checkThatMousePassedOver(pageUnderTest, "#cell_b3", true);
checkThatMousePassedOver(pageUnderTest, "#cell_a4", mouseMoveEnd);
checkThatMousePassedOver(pageUnderTest, "#cell_b3", mouseMoveOver);
//checkThatMousePassedOver(pageUnderTest, "#cell_c2" true); // TODO: starting cell not working, why?

start();
Expand Down Expand Up @@ -238,8 +252,8 @@ QUnit.test("Move Cursor Diagonal Down+Left", 2, function () {
syn.move(source, {to: destination, duration: testSpeed}, function () {

// ensure we get mouse events over the places that we expect
checkThatMousePassedOver(pageUnderTest, "#cell_e0", true);
checkThatMousePassedOver(pageUnderTest, "#cell_d1", true);
checkThatMousePassedOver(pageUnderTest, "#cell_e0", mouseMoveEnd);
checkThatMousePassedOver(pageUnderTest, "#cell_d1", mouseMoveOver);
//checkThatMousePassedOver(pageUnderTest, "#cell_c2" true); // TODO: starting cell not working, why?

start();
Expand All @@ -253,7 +267,7 @@ QUnit.test("Move Cursor Diagonal Down+Left", 2, function () {



QUnit.test("Move Cursor Diagonal Down+Right", 2, function () {
QUnit.test("Move Cursor Diagonal Down+Right", 1, function () {
stop();

var testFrame = document.getElementById('pageUnderTest');
Expand All @@ -267,9 +281,10 @@ QUnit.test("Move Cursor Diagonal Down+Right", 2, function () {

syn.move(source, {to: destination, duration: testSpeed}, function () {

// ensure we get mouse events over the places that we expect
checkThatMousePassedOver(pageUnderTest, "#cell_e4", true);
checkThatMousePassedOver(pageUnderTest, "#cell_d3", true);
// NOTE: The test sporadically moves the mouse at the end of the test, causing extra events to appear here.
// so we can't rely on the #cell_e4 check
// checkThatMousePassedOver(pageUnderTest, "#cell_e4", mouseMoveEnd);
checkThatMousePassedOver(pageUnderTest, "#cell_d3", mouseMoveOver);
//checkThatMousePassedOver(pageUnderTest, "#cell_c2" true); // TODO: starting cell not working, why?

start();
Expand All @@ -281,8 +296,25 @@ QUnit.test("Move Cursor Diagonal Down+Right", 2, function () {
});


function checkThatMousePassedOver(pageUnderTest, cellName, expectedState){
function checkThatMousePassedOver(pageUnderTest, cellName, expectedEvents){
var cell = pageUnderTest.querySelector(cellName);
ok((cell.classList.contains('mouseOver') == expectedState), "MouseOver on expected node: "+cellName+".");
var browser = BrowserDetective.getBrowserName();
var actualEvents = '';

//var i;
for (var i = 0; i < cell.eventRecorder.length; i++) {
actualEvents += cell.eventRecorder[i] + ", ";
}

//cell.eventRecorder.forEach(function(elem) { actualEvents += elem + ", "; });
equal(actualEvents, expectedEvents, "Recorded events must match expected events. CellId: " + cellName);

}








Loading

0 comments on commit 4ef3512

Please sign in to comment.