Skip to content

Commit

Permalink
fix: dispatch touch events in webkit (#34250)
Browse files Browse the repository at this point in the history
  • Loading branch information
yury-s authored Jan 8, 2025
1 parent 7ee7e01 commit edfbab2
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 10 deletions.
4 changes: 0 additions & 4 deletions docs/src/touch-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ If your web application relies on [pointer events](https://developer.mozilla.org

You can dispatch touch events to the page using [`method: Locator.dispatchEvent`]. [Touch](https://developer.mozilla.org/en-US/docs/Web/API/Touch) points can be passed as arguments, see examples below.

:::note
The examples work only in Chromium and Firefox, as the [`Touch()`](https://developer.mozilla.org/en-US/docs/Web/API/Touch/Touch) constructor is not supported in WebKit.
:::

#### Emulating pan gesture

In the example below, we emulate pan gesture that is expected to move the map. The app under test only uses `clientX/clientY` coordinates of the touch point, so we initialize just that. In a more complex scenario you may need to also set `pageX/pageY/screenX/screenY`, if your app needs them.
Expand Down
36 changes: 31 additions & 5 deletions packages/playwright-core/src/server/injected/injectedScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1016,11 +1016,37 @@ export class InjectedScript {
case 'mouse': event = new MouseEvent(type, eventInit); break;
case 'keyboard': event = new KeyboardEvent(type, eventInit); break;
case 'touch': {
eventInit.target ??= node;
eventInit.touches = eventInit.touches?.map((t: any) => t instanceof Touch ? t : new Touch({ ...t, target: t.target ?? node }));
eventInit.targetTouches = eventInit.targetTouches?.map((t: any) => t instanceof Touch ? t : new Touch({ ...t, target: t.target ?? node }));
eventInit.changedTouches = eventInit.changedTouches?.map((t: any) => t instanceof Touch ? t : new Touch({ ...t, target: t.target ?? node }));
event = new TouchEvent(type, eventInit);
// WebKit does not support Touch constructor, but has deprecated createTouch and createTouchList methods.
if (this._browserName === 'webkit') {
const createTouch = (t: any) => {
if (t instanceof Touch)
return t;
// createTouch does not accept clientX/clientY, so we have to use pageX/pageY.
let pageX = t.pageX;
if (pageX === undefined && t.clientX !== undefined)
pageX = t.clientX + (this.document.scrollingElement?.scrollLeft || 0);
let pageY = t.pageY;
if (pageY === undefined && t.clientY !== undefined)
pageY = t.clientY + (this.document.scrollingElement?.scrollTop || 0);
return (this.document as any).createTouch(this.window, t.target ?? node, t.identifier, pageX, pageY, t.screenX, t.screenY, t.radiusX, t.radiusY, t.rotationAngle, t.force);
};
const createTouchList = (touches: any) => {
if (touches instanceof TouchList || !touches)
return touches;
return (this.document as any).createTouchList(...touches.map(createTouch));
};
eventInit.target ??= node;
eventInit.touches = createTouchList(eventInit.touches);
eventInit.targetTouches = createTouchList(eventInit.targetTouches);
eventInit.changedTouches = createTouchList(eventInit.changedTouches);
event = new TouchEvent(type, eventInit);
} else {
eventInit.target ??= node;
eventInit.touches = eventInit.touches?.map((t: any) => t instanceof Touch ? t : new Touch({ ...t, target: t.target ?? node }));
eventInit.targetTouches = eventInit.targetTouches?.map((t: any) => t instanceof Touch ? t : new Touch({ ...t, target: t.target ?? node }));
eventInit.changedTouches = eventInit.changedTouches?.map((t: any) => t instanceof Touch ? t : new Touch({ ...t, target: t.target ?? node }));
event = new TouchEvent(type, eventInit);
}
break;
}
case 'pointer': event = new PointerEvent(type, eventInit); break;
Expand Down
1 change: 0 additions & 1 deletion tests/library/locator-dispatchevent-touch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { contextTest as it, expect } from '../config/browserTest';
it.use({ hasTouch: true });

it('should support touch points in touch event arguments', async ({ page, server, browserName }) => {
it.fixme(browserName === 'webkit', 'WebKit does not have Touch constructor');
await page.goto(server.EMPTY_PAGE);
await page.setContent(`
<div data-testid='outer' style="position: absolute; width: 120px; height: 120px; background-color: red;">
Expand Down

0 comments on commit edfbab2

Please sign in to comment.