Skip to content

Commit

Permalink
feat(touchEvents): complete touch event support for Chrome only, and …
Browse files Browse the repository at this point in the history
…subsequently remove usage of Ph
  • Loading branch information
Mitchal committed Jan 11, 2018
1 parent ee39595 commit 730916b
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 62 deletions.
133 changes: 95 additions & 38 deletions src/acceptance.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ let history;
*/
export function scaleWindowWidth(scale) {
andThen(() => {
var $root = $(root);
let $root = $(root);
const currentWidth = $root.width();
const newWidth = currentWidth * scale;
$root.css('width', `${newWidth}px`);
Expand Down Expand Up @@ -162,20 +162,71 @@ export function mouseMove(selector, options) {
triggerMouseEvent(mouseMove, selector, options);
}

/**
* Waits for an element to show up, and then simulates a user touch start by triggering a touch event on that element.
* @param {string|jQuery} selector The jQuery selector or jQuery object to simulate touch on.
* Note that the selector or jQuery object must represent exactly one (1) element in the app, or the call will fail.
* @param {object} [options] Any options to pass along to the simulated touch event.
* @example
* touchStart('.element-to-touch', {touches: [{ clientX: 1337, clientY: 1338 }], changedTouches: [{ clientX: 1337, clientY: 1338}]});
*/
export function touchStart(selector, options) {
triggerTouchEvent(touchStart, selector, options);
}

/**
* Waits for an element to show up, and then simulates a user touch move by triggering a touch event on that element.
* @param {string|jQuery} selector The jQuery selector or jQuery object to simulate touch on.
* Note that the selector or jQuery object must represent exactly one (1) element in the app, or the call will fail.
* @param {object} [options] Any options to pass along to the simulated touch event.
* @example
* touchMove('.element-to-touch', {touches: [{ clientX: 1337, clientY: 1338 }], changedTouches: [{ clientX: 1337, clientY: 1338}]});
*/
export function touchMove(selector, options) {
triggerTouchEvent(touchMove, selector, options);
}

/**
* Waits for an element to show up, and then simulates a user touch cancel by triggering a touch event on that element.
* @param {string|jQuery} selector The jQuery selector or jQuery object to simulate touch on.
* Note that the selector or jQuery object must represent exactly one (1) element in the app, or the call will fail.
* @param {object} [options] Any options to pass along to the simulated touch event.
* @example
* touchCancel('.element-to-touch', {touches: [{ clientX: 1337, clientY: 1338 }], changedTouches: [{ clientX: 1337, clientY: 1338}]});
*/
export function touchCancel(selector, options) {
triggerTouchEvent(touchCancel, selector, options);
}

/**
* Waits for an element to show up, and then simulates a user touch end by triggering a touch event on that element.
* @param {string|jQuery} selector The jQuery selector or jQuery object to simulate touch on.
* Note that the selector or jQuery object must represent exactly one (1) element in the app, or the call will fail.
* @param {object} [options] Any options to pass along to the simulated touch event.
* @example
* touchEnd('.element-to-touch', {touches: [{ clientX: 1337, clientY: 1338 }], changedTouches: [{ clientX: 1337, clientY: 1338}]});
*/
export function touchEnd(selector, options) {
triggerTouchEvent(touchEnd, selector, options);
}

function triggerMouseEvent(
exportedFunction,
selector,
options,
eventsToTriggerFirst = []) {
triggerEvent(exportedFunction, selector, options, createMouseEvent, eventsToTriggerFirst)
eventsToTriggerFirst = []
) {
triggerEvent(
exportedFunction,
selector,
options,
createMouseEvent,
eventsToTriggerFirst
);
}

function triggerTouchEvent(
exportedFunction,
selector,
options,
eventsToTriggerFirst = []) {
triggerEvent(exportedFunction, selector, options, createTouchEvent, eventsToTriggerFirst)
function triggerTouchEvent(exportedFunction, selector, options) {
triggerEvent(exportedFunction, selector, options, createTouchEvent);
}

function triggerEvent(
Expand All @@ -202,8 +253,9 @@ function triggerEvent(
: options;

function triggerEvent(eventName) {
const event = createEvent(eventName, evaluatedOptions);
jqueryElement[0].dispatchEvent(event);
const target = jqueryElement[0];
const event = createEvent(eventName, evaluatedOptions, target);
target.dispatchEvent(event);
}

const eventsToTrigger = eventsToTriggerFirst.concat([eventName]);
Expand All @@ -214,18 +266,6 @@ function triggerEvent(
});
}

/**
* Waits for an element to show up, and then simulates a user touch start by triggering a mouse event on that element.
* @param {string|jQuery} selector The jQuery selector or jQuery object to simulate touch start on.
* Note that the selector or jQuery object must represent exactly one (1) element in the app, or the call will fail.
* @param {object} [options] Any options to pass along to the simulated mouse event.
* @example
* mouseDown('.element-to-touch-start-on', {touches: [{ clientX: 1337, clientY: 1338 }]});
*/
export function touchStart(selector, options) {
triggerTouchEvent(touchStart, selector, options);
}

/**
* Waits for an input element to show up, and then simulates a user filling in the value of that input.
* @param {string|jQuery} selector The jQuery selector or jQuery object to fill in.
Expand Down Expand Up @@ -364,7 +404,7 @@ function createMouseEvent(
relatedTarget = document.body.parentNode,
} = {}
) {
var result;
let result;

try {
result = new MouseEvent(type, options);
Expand Down Expand Up @@ -392,24 +432,41 @@ function createMouseEvent(
return result;
}

function createTouchEvent(
type,
{
touches = [{
clientX: 0,
clientY: 0,
}],
} = {}
) {
var result;
function createTouchEvent(type, options = {}, target) {
let result;

try {
result = new TouchEvent(type, {touches});
const touches = mapTouchesToRealTouchInstances(
options.touches || [],
target
);

const changedTouches = mapTouchesToRealTouchInstances(
options.changedTouches || [],
target
);

const targetTouches = mapTouchesToRealTouchInstances(
options.targetTouches || [],
target
);
result = new TouchEvent(type, {
touches,
changedTouches,
targetTouches,
});
} catch (e) {
console.log(touches)
result = document.createEvent('TouchEvent');
result.initTouchEvent(type, {touches});
throw new Error(
'acast-test-helpers: Touch event helpers are currently only supported in Chrome.'
);
}

return result;
}

function mapTouchesToRealTouchInstances(touches, target) {
let nextIdentifier = 0;
return touches.map(
touch => new Touch({ target, identifier: nextIdentifier++, ...touch })
);
}
2 changes: 1 addition & 1 deletion testem/testem-dev.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const baseConfig = require('./testem');

module.exports = Object.assign({}, baseConfig, {
launch_in_dev: ['PhantomJS', 'Chrome'],
launch_in_dev: ['Chrome'],
on_start: './node_modules/.bin/webpack --watch'
});
5 changes: 4 additions & 1 deletion testem/testem.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ module.exports = {
'tmp/tests-bundle.js'
]),
framework: 'mocha',
launch_in_ci: ['PhantomJS'],
launch_in_ci: ['Chrome'],
browser_args: {
'Chrome': [ '--headless', '--disable-gpu', '--remote-debugging-port=9222' ],
},
};
12 changes: 6 additions & 6 deletions tests/acceptance/mouse-events.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ describe('Mouse Events', () => {
describeMouseEventHelper(mouseUp, 'mouseup');
describeMouseEventHelper(mouseMove, 'mousemove');

function describeMouseEventHelper(func, eventName, extraTests = ()=> {
function describeMouseEventHelper(helperToTest, eventName, extraTests = ()=> {
}) {
describe(func.name, () => {
describe(helperToTest.name, () => {
setupAsync();

let elementToInteractWith;
Expand All @@ -56,7 +56,7 @@ describe('Mouse Events', () => {
attachElementToBody();
const spy = sinon.spy();
$(elementToInteractWith).on(eventName, spy);
func('.element-to-interact-with');
helperToTest('.element-to-interact-with');
andThen(() => {
expect(spy).to.have.been.calledOnce();
});
Expand All @@ -65,7 +65,7 @@ describe('Mouse Events', () => {
it('waits until element shows up before trying to interact with it', () => {
const spy = sinon.spy();
$(elementToInteractWith).on(eventName, spy);
func('.element-to-interact-with');
helperToTest('.element-to-interact-with');
andThen(() => {
expect(spy).to.have.been.calledOnce();
});
Expand All @@ -80,7 +80,7 @@ describe('Mouse Events', () => {
expect(e.clientY).to.equal(1338);
done();
});
func('.element-to-interact-with', { clientX: 1337, clientY: 1338 });
helperToTest('.element-to-interact-with', { clientX: 1337, clientY: 1338 });
});

it('evaluates options lazily if passed as function', (done) => {
Expand All @@ -90,7 +90,7 @@ describe('Mouse Events', () => {
screenX = 1337;
});

func('.element-to-interact-with', () => ({ screenX }));
helperToTest('.element-to-interact-with', () => ({ screenX }));

const element = attachElementToBody();
$(element).on(eventName, e => {
Expand Down
63 changes: 47 additions & 16 deletions tests/acceptance/touch-events.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import {touchStart, andThen, setupAsync, jQuery as $} from '../../src'
import {
touchStart,
touchMove,
touchCancel,
touchEnd,
andThen,
setupAsync,
jQuery as $
} from '../../src'

describe('Touch Events', () => {
describeMouseEventHelper(touchStart, 'touchstart');
describeTouchEventHelper(touchStart, 'touchstart');
describeTouchEventHelper(touchMove, 'touchmove');
describeTouchEventHelper(touchCancel, 'touchcancel');
describeTouchEventHelper(touchEnd, 'touchend');

function describeMouseEventHelper(func, eventName, extraTests = ()=> {}) {
describe(func.name, () => {
function describeTouchEventHelper(helperToTest, eventName) {
describe(helperToTest.name, () => {
setupAsync();

let elementToInteractWith;
Expand All @@ -26,8 +37,8 @@ describe('Touch Events', () => {
it(`triggers ${eventName} event on selected element`, () => {
attachElementToBody();
const spy = sinon.spy();
$(elementToInteractWith).on(eventName, spy);
func('.element-to-interact-with');
elementToInteractWith.addEventListener(eventName, spy);
helperToTest('.element-to-interact-with');
andThen(() => {
expect(spy).to.have.been.calledOnce();
});
Expand All @@ -36,7 +47,7 @@ describe('Touch Events', () => {
it('waits until element shows up before trying to interact with it', () => {
const spy = sinon.spy();
$(elementToInteractWith).on(eventName, spy);
func('.element-to-interact-with');
helperToTest('.element-to-interact-with');
andThen(() => {
expect(spy).to.have.been.calledOnce();
});
Expand All @@ -46,12 +57,32 @@ describe('Touch Events', () => {

it('takes extra options as parameters', (done) => {
const element = attachElementToBody();
$(element).on(eventName, e => {
const handleEvent = e => {
expect(e.touches[0].clientX).to.equal(1337);
expect(e.touches[0].clientY).to.equal(1338);
expect(e.touches[0].target).to.equal(element);
expect(e.touches[0].identifier).to.equal(0);

expect(e.changedTouches[0].clientX).to.equal(1337);
expect(e.changedTouches[0].clientY).to.equal(1338);
expect(e.changedTouches[0].target).to.equal(element);
expect(e.changedTouches[0].identifier).to.equal(42);

expect(e.targetTouches[0].clientX).to.equal(1337);
expect(e.targetTouches[0].clientY).to.equal(1338);
expect(e.targetTouches[0].target).to.equal(element);
expect(e.targetTouches[0].identifier).to.equal(0);

element.removeEventListener(eventName, handleEvent);
done();
};

element.addEventListener(eventName, handleEvent);
helperToTest('.element-to-interact-with', {
touches: [{ clientX: 1337, clientY: 1338 }],
changedTouches: [{ target: element, clientX: 1337, clientY: 1338, identifier: 42 }],
targetTouches: [{ target: element, clientX: 1337, clientY: 1338 }],
});
func('.element-to-interact-with', {touches: [{ clientX: 1337, clientY: 1338 }]});
});

it('evaluates options lazily if passed as function', (done) => {
Expand All @@ -60,17 +91,17 @@ describe('Touch Events', () => {
andThen(() => {
screenX = 1337;
});
func('.element-to-interact-with', () => ({ screenX }));

helperToTest('.element-to-interact-with', () => ({ touches: [{ screenX }] }));

const element = attachElementToBody();
$(element).on(eventName, e => {
expect(e.screenX).to.equal(1337);
const handleEvent = e => {
expect(e.touches[0].screenX).to.equal(1337);
element.removeEventListener(eventName, handleEvent);
done();
});
};
element.addEventListener(eventName, handleEvent);
});

extraTests(attachElementToBody);
});
}
})

0 comments on commit 730916b

Please sign in to comment.