Skip to content

Repository files navigation

pointerdriver

test

Simulate human maneuvers across devices on gesture-heavy web apps.
For usage as an automated testing utility.

Abstracts idiosyncrasies across various pointer devices;
e.g: "Apple Pencil", "touchscreen", "mouse" etc...
using an ergonomic and uniform API.

Note

LLM agents/assistants must read the skill.

Usage

Import in your target app's DevTools/Web Inspector/Console:

const{ DragMotion }=awaitimport('https://theprofs.github.io/pointerdriver/pointerdriver.js')awaitnewDragMotion(document.querySelector('#el'),[[30,50,0],[60,80,16],]).perform()

Local usage

Run a local server instead of the CDN:

npx github:TheProfs/pointerdriver
const{ DragMotion }=awaitimport('http://127.0.0.1:5619/pointerdriver.js')awaitnewDragMotion(document.querySelector('#el'),[[30,50,0],[60,80,16],]).perform()

If the target page is HTTPS, the HTTP import is blocked as mixed content.
Use a Cloudflare Tunnel to wrap it in HTTPS:

cloudflared tunnel --url http://127.0.0.1:5619

Programmatic usage

You can also import and use it to drive automated tests:

npm i github:TheProfs/pointerdriver
import{test,before,after}from'node:test'import{pointerdriver}from'pointerdriver'importpuppeteerfrom'puppeteer'letserver,browser,pagebefore(async()=>{server=pointerdriver()browser=awaitpuppeteer.launch({headless: false})page=awaitbrowser.newPage()awaitpage.goto('http://localhost:3000')})after(()=>(server.close(),browser.close()))test('#mousedrag',asynct=>{t.beforeEach(()=>page.evaluate(async()=>{const{ DragMotion }=awaitimport('http://127.0.0.1:5619/pointerdriver.js')awaitnewDragMotion(document.querySelector('#el'),[[30,50,0],[60,80,16],]).perform()}))awaitt.test('creates a PathItem',asynct=>{// assertions...})awaitt.test('closely following the cursor path',asynct=>{// assertions...})awaitt.test('with selected attributes',asynct=>{// assertions...})})

pointerdriver(port) starts the module server. Defaults to port 5619.

Motions

A Motion encodes the manner in which the actual device it represents,
translates inputs into events and constucts a near-identical
event stream and dispatches it to the passed element:

"swipe across an element using 1 finger, and draw a square":

awaitnewDragMotion(document.querySelector('#board'),[[30,50,0],[60,80,16],[120,140,32],]).perform()

"put 2 fingers down and twist 45 degrees at pivot: x: 100, y: 100,
then lift up":

awaitnewTwistMotion(document.querySelector('#board'),45,{x: 100,y: 100}).perform()

Element targeting

The passed element should be the container of the
actual element you're targeting.

For example:

<divid="board"><canvas></canvas></div>
awaitnewDragMotion(document.querySelector('#board'),[[30,50,0],[60,80,16],]).perform()

There are 2 types of Motions;

  • Drawing motions
  • Gesture motions

Drawing

Single-pointer interactions, commonly used for drawing on a surface.

  • DragMotion — mouse
  • GlideMotion — touch
  • StrokeMotion — pen

All three take (el, points):

  • points is [[x, y, ms], ...]
  • ms is monotonic and >= 0
awaitnewDragMotion(document.querySelector('#el'),[[30,50,0],[60,80,16],]).perform()

Gestures

Multi-pointer interactions, commonly used for navigation (zoom, pan etc.)

PinchMotion(el, scale, { x, y, distance, steps })

Two-finger pinch together or apart.

  • scale — target scale factor
  • x, y — gesture center
  • distance — initial finger gap in px (default 100)
  • steps — interpolation frames (default 20)
awaitnewPinchMotion(document.querySelector('#el'),2,{x: 60,y: 80}).perform()

TwistMotion(el, degrees, { x, y, radius, steps })

Two-finger rotation around a center point.

  • degrees — rotation angle (default 45)
  • x, y — gesture center
  • radius — finger distance from center (default 80)
  • steps — interpolation frames (default 20)
awaitnewTwistMotion(document.querySelector('#el'),45,{x: 60,y: 80}).perform()

SwipeMotion(el, distance, { x, y, angle, separation, steps })

Two-finger parallel drag.

  • distance — travel distance in px
  • x, y — gesture center
  • angle — direction in degrees (default 0)
  • separation — gap between fingers in px (default 40)
  • steps — interpolation frames (default 20)
awaitnewSwipeMotion(document.querySelector('#el'),200,{x: 60,y: 80}).perform()

Add a motion

Motions are extensible, allowing support for more
devices and interaction types.

Motion (base)
├── PathMotion (drawing)
│ ├── DragMotion
│ ├── GlideMotion
│ └── StrokeMotion
└── GestureMotion (gestures)
├── PinchMotion
├── TwistMotion
└── SwipeMotion
  1. Create motions/<name>/index.js
  2. Extend PathMotion or GestureMotion
  3. Export from motions/index.js

Note

Co-locate tests in motions/<name>/test/.

Publish

Pushes to developmentbundle and deploy to GitHub Pages:

https://theprofs.github.io/pointerdriver/pointerdriver.js

Run tests

npm test

License

MIT

About

Synthesize and dispatch pointer, touch and gesture events on a DOM element

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages