/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { expect, folio } from './fixtures';
import { ElementHandle } from '..';
import type { ServerResponse } from 'http';
const fixtures = folio.extend();
fixtures.page.override(async ({browser}, runTest) => {
const page = await browser.newPage({
hasTouch: true
});
await runTest(page);
await page.close();
});
const { it } = fixtures.build();
it('should send all of the correct events', async ({page}) => {
await page.setContent(`
a
b
`);
await page.tap('#a');
const eventsHandle = await trackEvents(await page.$('#b'));
await page.tap('#b');
// webkit doesnt send pointerenter or pointerleave or mouseout
expect(await eventsHandle.jsonValue()).toEqual([
'pointerover', 'pointerenter',
'pointerdown', 'touchstart',
'pointerup', 'pointerout',
'pointerleave', 'touchend',
'mouseover', 'mouseenter',
'mousemove', 'mousedown',
'mouseup', 'click',
]);
});
it('should not send mouse events touchstart is canceled', async ({page}) => {
await page.setContent('hello world');
await page.evaluate(() => {
// touchstart is not cancelable unless passive is false
document.addEventListener('touchstart', t => t.preventDefault(), {passive: false});
});
const eventsHandle = await trackEvents(await page.$('body'));
await page.tap('body');
expect(await eventsHandle.jsonValue()).toEqual([
'pointerover', 'pointerenter',
'pointerdown', 'touchstart',
'pointerup', 'pointerout',
'pointerleave', 'touchend',
]);
});
it('should not send mouse events when touchend is canceled', async ({page}) => {
await page.setContent('hello world');
await page.evaluate(() => {
document.addEventListener('touchend', t => t.preventDefault());
});
const eventsHandle = await trackEvents(await page.$('body'));
await page.tap('body');
expect(await eventsHandle.jsonValue()).toEqual([
'pointerover', 'pointerenter',
'pointerdown', 'touchstart',
'pointerup', 'pointerout',
'pointerleave', 'touchend',
]);
});
it('should wait for a navigation caused by a tap', async ({server, page}) => {
await page.goto(server.EMPTY_PAGE);
await page.setContent(`
link;
`);
const responsePromise = new Promise(resolve => {
server.setRoute('/intercept-this.html', (handler, response) => {
resolve(response);
});
});
let resolved = false;
const tapPromise = page.tap('a').then(() => resolved = true);
const response = await responsePromise;
// make sure the tap doesnt resolve too early
await new Promise(x => setTimeout(x, 100));
expect(resolved).toBe(false);
response.end('foo');
await tapPromise;
expect(resolved).toBe(true);
});
it('should work with modifiers', async ({page}) => {
await page.setContent('hello world');
const altKeyPromise = page.evaluate(() => new Promise(resolve => {
document.addEventListener('touchstart', event => {
resolve(event.altKey);
}, {passive: false});
}));
// make sure the evals hit the page
await page.evaluate(() => void 0);
await page.tap('body', {
modifiers: ['Alt']
});
expect(await altKeyPromise).toBe(true);
});
it('should send well formed touch points', async ({page}) => {
const promises = Promise.all([
page.evaluate(() => new Promise(resolve => {
document.addEventListener('touchstart', event => {
resolve([...event.touches].map(t => ({
identifier: t.identifier,
clientX: t.clientX,
clientY: t.clientY,
pageX: t.pageX,
pageY: t.pageY,
radiusX: 'radiusX' in t ? t.radiusX : t['webkitRadiusX'],
radiusY: 'radiusY' in t ? t.radiusY : t['webkitRadiusY'],
rotationAngle: 'rotationAngle' in t ? t.rotationAngle : t['webkitRotationAngle'],
force: 'force' in t ? t.force : t['webkitForce'],
})));
}, false);
})),
page.evaluate(() => new Promise(resolve => {
document.addEventListener('touchend', event => {
resolve([...event.touches].map(t => ({
identifier: t.identifier,
clientX: t.clientX,
clientY: t.clientY,
pageX: t.pageX,
pageY: t.pageY,
radiusX: 'radiusX' in t ? t.radiusX : t['webkitRadiusX'],
radiusY: 'radiusY' in t ? t.radiusY : t['webkitRadiusY'],
rotationAngle: 'rotationAngle' in t ? t.rotationAngle : t['webkitRotationAngle'],
force: 'force' in t ? t.force : t['webkitForce'],
})));
}, false);
})),
]);
// make sure the evals hit the page
await page.evaluate(() => void 0);
await page.touchscreen.tap(40, 60);
const [touchstart, touchend] = await promises;
expect(touchstart).toEqual([{
clientX: 40,
clientY: 60,
force: 1,
identifier: 0,
pageX: 40,
pageY: 60,
radiusX: 1,
radiusY: 1,
rotationAngle: 0,
}]);
expect(touchend).toEqual([]);
});
it('should wait until an element is visible to tap it', async ({page}) => {
const div = await page.evaluateHandle(() => {
const button = document.createElement('button');
button.textContent = 'not clicked';
document.body.appendChild(button);
button.style.display = 'none';
return button;
});
const tapPromise = div.tap();
await div.evaluate(div => div.onclick = () => div.textContent = 'clicked');
await div.evaluate(div => div.style.display = 'block');
await tapPromise;
expect(await div.textContent()).toBe('clicked');
});
async function trackEvents(target: ElementHandle) {
const eventsHandle = await target.evaluateHandle(target => {
const events: string[] = [];
for (const event of [
'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'click',
'pointercancel', 'pointerdown', 'pointerenter', 'pointerleave', 'pointermove', 'pointerout', 'pointerover', 'pointerup',
'touchstart', 'touchend', 'touchmove', 'touchcancel',
])
target.addEventListener(event, () => events.push(event), false);
return events;
});
return eventsHandle;
}