1+ # pointerdriver
2+
13[ ![ test] [ test-badge ]] [ test ]
24
3- # pointerdriver
5+ Simulate human maneuvers across devices on gesture-heavy web apps.
6+ For usage as an automated testing utility.
7+
8+ Abstracts the idiosyncrasies across many different pointer
9+ devices; e.g: "Apple Pencil", "touchscreen", "mouse" etc...
10+ behind an ergonomic and uniform API.
11+
12+ > [ !NOTE]
13+ > LLM agents/assistants ** must** read the [ skill] [ skill-md ] .
14+
15+ ## Usage
416
5- Synthesize pointer, touch, and gesture events on any page.
17+ Start the local server, then import in your target app's
18+ DevTools/Web Inspector/Console:
619
720``` bash
821npx github:TheProfs/pointerdriver
922```
1023
11- > [ !NOTE]
12- > LLM Agents ** must** read the ` pointerdriver ` skill first.
13- > Ask your LLM to run: ` pointerdriver --skill ` and study it carefully.
24+ ``` js
25+ const { DragMotion } = await import (' http://127.0.0.1:5619/pointerdriver.js' )
26+
27+ await new DragMotion (document .querySelector (' #el' ), [
28+ [30 , 50 , 0 ],
29+ [60 , 80 , 16 ],
30+ ]).perform ()
31+ ```
32+
33+ ### Programmatic usage
1434
15- ## How it works :
35+ You can also ` import ` and use it to drive automated tests :
1636
17- 1 . Run your project however you want.
18- We're assuming it's running on ` localhost:3000 `
19- 2 . Run a pointerdriver server with ` npx github:TheProfs/pointerdriver `
20- 3 . Go in your browser console and run:
37+ ``` bash
38+ npm i github:TheProfs/pointerdriver
39+ ```
2140
2241``` js
23- const {
24- DragMotion , GlideMotion , StrokeMotion ,
25- PinchMotion , TwistMotion , SwipeMotion
26- } = await import (' http://127.0.0.1:5619/pointerdriver.js' )
42+ import { test } from ' node:test'
43+ import puppeteer from ' puppeteer'
44+
45+ const browser = await puppeteer .launch ({ headless: false })
46+ const page = await browser .newPage ()
47+ await page .goto (' http://localhost:3000' )
48+
49+ test (' #mousedrag' , async t => {
50+ t .beforeEach (() => page .evaluate (async () => {
51+ const { DragMotion } = await import (
52+ ' http://127.0.0.1:5619/pointerdriver.js'
53+ )
54+
55+ await new DragMotion (document .querySelector (' #el' ), [
56+ [30 , 50 , 0 ],
57+ [60 , 80 , 16 ],
58+ ]).perform ()
59+ }))
60+
61+ await t .test (' creates a PathItem' , async t => {
62+ // assertions...
63+ })
64+
65+ await t .test (' closely following the cursor path' , async t => {
66+ // assertions...
67+ })
68+
69+ await t .test (' with selected attributes' , async t => {
70+ // assertions...
71+ })
72+ })
2773```
2874
29- This loads ` pointerdriver ` and allows executing any of the following motions:
30-
3175## Motions
3276
33- Create a motion by running: ` new Motion(arguments).perform() ` .
34-
35- A motion is a * human-maneuver* ;
36- Regardless of the arguments you use for it, a motion eventually
37- generates the exact sequence of events that would be generated
38- from a real device.
77+ A ` Motion ` is a unit that represents a particular maneuver.
78+ For example:
79+
80+ - ` "swipe across an element using 1 finger, following this <path>" `
81+ - ` "put 2 fingers down and twist <amount> of degrees and lift up" `
3982
83+ The ` Motion ` is then executed on the passed ` element ` ,
84+ which starts dispatching appropriate events at appropriate timings.
4085
41- ### ` DragMotion(el, points) `
86+ Run any motion with ` new Motion(args).perform() ` .
4287
43- Mouse drag across a surface.
88+ There are 2 types of Motions; both explained below:
4489
45- | Param | Description |
46- | ----------| --------------------------------------------|
47- | ` el ` | target element |
48- | ` points ` | ` [x, y, ms][] ` ; ` ms ` monotonic and ` >= 0 ` |
90+ ### Drawing
91+
92+ Motions used for drawing on a surface.
93+
94+ - ` DragMotion ` — mouse
95+ - ` GlideMotion ` — touch
96+ - ` StrokeMotion ` — pen
97+
98+ All three take ` (el, points) ` :
99+
100+ - ` points ` is ` [[x, y, ms], ...] `
101+ - ` ms ` is monotonic and ` >= 0 `
49102
50103``` js
51104await new DragMotion (document .querySelector (' #el' ), [
@@ -54,104 +107,117 @@ await new DragMotion(document.querySelector('#el'), [
54107]).perform ()
55108```
56109
57- ### ` GlideMotion(el, points) `
58-
59- Finger draw on a surface.
60-
61- | Param | Description |
62- | ----------| --------------------------------------------|
63- | ` el ` | target element |
64- | ` points ` | ` [x, y, ms][] ` ; ` ms ` monotonic and ` >= 0 ` |
65-
66110``` js
67111await new GlideMotion (document .querySelector (' #el' ), [
68112 [30 , 50 , 0 ],
69113 [60 , 80 , 16 ],
70114]).perform ()
71115```
72116
73- ### ` StrokeMotion(el, points) `
74-
75- Pen stylus stroke on a surface.
76-
77- | Param | Description |
78- | ----------| --------------------------------------------|
79- | ` el ` | target element |
80- | ` points ` | ` [x, y, ms][] ` ; ` ms ` monotonic and ` >= 0 ` |
81-
82117``` js
83118await new StrokeMotion (document .querySelector (' #el' ), [
84119 [30 , 50 , 0 ],
85120 [60 , 80 , 16 ],
86121]).perform ()
87122```
88123
89- ### ` PinchMotion(el, scale, { x, y, distance, steps }) `
124+ ### Gestures
125+
126+ Motions used for interaction (zooming, panning, etc).
127+
128+ #### ` PinchMotion(el, scale, { x, y, distance, steps }) `
90129
91130Two-finger pinch together or apart.
92131
93- | Param | Default | Description |
94- | ------------| ---------| --------------------------|
95- | ` el ` | | target element |
96- | ` scale ` | | target scale factor |
97- | ` x ` , ` y ` | | gesture center |
98- | ` distance ` | ` 100 ` | initial finger gap in px |
99- | ` steps ` | ` 20 ` | interpolation frames |
132+ - ` scale ` — target scale factor
133+ - ` x ` , ` y ` — gesture center
134+ - ` distance ` — initial finger gap in px (default ` 100 ` )
135+ - ` steps ` — interpolation frames (default ` 20 ` )
100136
101137``` js
102138await new PinchMotion (document .querySelector (' #el' ), 2 , {
103139 x: 60 , y: 80
104140}).perform ()
105141```
106142
107- ### ` TwistMotion(el, degrees, { x, y, radius, steps }) `
143+ #### ` TwistMotion(el, degrees, { x, y, radius, steps }) `
108144
109145Two-finger rotation around a center point.
110146
111- | Param | Default | Description |
112- | -----------| ---------| -----------------------------|
113- | ` el ` | | target element |
114- | ` degrees ` | ` 45 ` | rotation angle |
115- | ` x ` , ` y ` | | gesture center |
116- | ` radius ` | ` 80 ` | finger distance from center |
117- | ` steps ` | ` 20 ` | interpolation frames |
147+ - ` degrees ` — rotation angle (default ` 45 ` )
148+ - ` x ` , ` y ` — gesture center
149+ - ` radius ` — finger distance from center (default ` 80 ` )
150+ - ` steps ` — interpolation frames (default ` 20 ` )
118151
119152``` js
120153await new TwistMotion (document .querySelector (' #el' ), 45 , {
121154 x: 60 , y: 80
122155}).perform ()
123156```
124157
125- ### ` SwipeMotion(el, distance, { x, y, angle, separation, steps }) `
158+ #### ` SwipeMotion(el, distance, { x, y, angle, separation, steps }) `
126159
127160Two-finger parallel drag.
128161
129- | Param | Default | Description |
130- | --------------| ---------| ---------------------------|
131- | ` el ` | | target element |
132- | ` distance ` | | travel distance in px |
133- | ` x ` , ` y ` | | gesture center |
134- | ` angle ` | ` 0 ` | direction in degrees |
135- | ` separation ` | ` 40 ` | gap between fingers in px |
136- | ` steps ` | ` 20 ` | interpolation frames |
162+ - ` distance ` — travel distance in px
163+ - ` x ` , ` y ` — gesture center
164+ - ` angle ` — direction in degrees (default ` 0 ` )
165+ - ` separation ` — gap between fingers in px (default ` 40 ` )
166+ - ` steps ` — interpolation frames (default ` 20 ` )
137167
138168``` js
139169await new SwipeMotion (document .querySelector (' #el' ), 200 , {
140170 x: 60 , y: 80
141171}).perform ()
142172```
143173
144- ## Notes:
174+ ## Add a motion
175+
176+ Motions are extensible, allowing support for more
177+ devices and interaction types.
178+
179+ ```
180+ Motion (base)
181+ ├── PathMotion (drawing)
182+ │ ├── DragMotion
183+ │ ├── GlideMotion
184+ │ └── StrokeMotion
185+ └── GestureMotion (gestures)
186+ ├── PinchMotion
187+ ├── TwistMotion
188+ └── SwipeMotion
189+ ```
190+
191+ 1 . Create ` motions/<name>/index.js `
192+ 2 . Extend ` PathMotion ` or ` GestureMotion `
193+ 3 . Export from ` motions/index.js `
194+
195+ ## Local server
196+
197+ For local development:
198+
199+ ``` bash
200+ npx github:TheProfs/pointerdriver
201+ ```
202+
203+ then in your target app Web Console:
145204
146- - The module server only serves ` /pointerdriver.js ` and ` /src/*/index.js ` .
147- - It sets permissive CORS headers so you can import it from any page.
205+ ``` js
206+ const { DragMotion } = await import (' http://127.0.0.1:5619/pointerdriver.js' )
207+
208+ // execute motion ...
209+ ```
148210
149- ## HTTPS pages
211+ ### Use HTTPS tunnels for https local dev
150212
151- If the target page is HTTPS,
152- importing pointerdriver from HTTP is blocked as mixed content.
153- Serve pointerdriver over HTTPS (see ` bin/skill.md ` for one approach).
213+ If the target page is HTTPS, the HTTP import is blocked as mixed content.
214+ Use a Cloudflare Tunnel to wrap it in HTTPS:
215+
216+ ``` bash
217+ cloudflared tunnel --url http://127.0.0.1:5619
218+ ```
154219
220+ Install via [ Cloudflare Tunnel downloads] [ cftunnel ] .
155221
156222## Run tests
157223
@@ -166,3 +232,5 @@ npm test
166232[ test-badge ] : https://github.com/TheProfs/pointerdriver/actions/workflows/test.yml/badge.svg
167233[ test ] : https://github.com/TheProfs/pointerdriver/actions/workflows/test.yml
168234[ license ] : https://opensource.org/licenses/MIT
235+ [ skill-md ] : bin/skill.md
236+ [ cftunnel ] : https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/
0 commit comments