Touch events (legacy)
When an app handles legacy Touch Events — not Pointer Events — for swipe, pinch, and tap gestures, I dispatch them manually with locator.dispatchEvent('touchstart'/'touchmove'/'touchend'). The key gotcha: dispatchEvent doesn't set Event.isTrusted. If the app gates behavior on that property to detect automation, you'll need to disable that check during tests.
When to use manual touch dispatch
Modern apps typically use Pointer Events, which Playwright handles automatically with locator.tap() when hasTouch: true is set in device emulation. Manual dispatch is only needed for older apps that specifically listen for touchstart, touchmove, and touchend events on DOM elements.
Each touch event needs a list of Touch points with at minimum identifier, clientX, and clientY. Three lists are required: touches (all current touches), changedTouches (touches that changed this event), and targetTouches (touches on the target element).
Pan gesture (swipe)
A pan is one touch point moving across the element. The pattern: touchstart at the center, multiple touchmove events stepping toward the target offset, then touchend. I compute the element center from getBoundingClientRect() and move by deltaX/deltaY across steps increments:
Pinch gesture (zoom)
A pinch uses two touch points. For pinch-in (zoom out), the points start far apart and move toward each other. For pinch-out (zoom in), they start close and move apart. Each step updates both touch points simultaneously in the touchmove event: