diff --git a/docs/src/touch-events.md b/docs/src/touch-events.md index a9b140afd7..a1d394dc62 100644 --- a/docs/src/touch-events.md +++ b/docs/src/touch-events.md @@ -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. diff --git a/packages/playwright-core/src/server/injected/injectedScript.ts b/packages/playwright-core/src/server/injected/injectedScript.ts index 0acc9bc76c..ed963f2b3f 100644 --- a/packages/playwright-core/src/server/injected/injectedScript.ts +++ b/packages/playwright-core/src/server/injected/injectedScript.ts @@ -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; diff --git a/tests/library/locator-dispatchevent-touch.spec.ts b/tests/library/locator-dispatchevent-touch.spec.ts index c811c847f0..fdf9c2c7e2 100644 --- a/tests/library/locator-dispatchevent-touch.spec.ts +++ b/tests/library/locator-dispatchevent-touch.spec.ts @@ -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(`