@humanjs/playwright 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -4
- package/dist/index.cjs +618 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +283 -4
- package/dist/index.d.ts +283 -4
- package/dist/index.js +588 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -20,15 +20,156 @@ const browser = await chromium.launch();
|
|
|
20
20
|
const page = await browser.newPage();
|
|
21
21
|
|
|
22
22
|
const human = await createHuman(page, {
|
|
23
|
-
personality: 'careful',
|
|
24
|
-
seed: 'session-42',
|
|
25
|
-
speed: 'human',
|
|
23
|
+
personality: 'careful', // careful | fast | distracted | precise
|
|
24
|
+
seed: 'session-42', // deterministic for tests
|
|
25
|
+
speed: 'human', // human | fast | instant
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
await human.goto('https://example.com');
|
|
29
|
+
|
|
30
|
+
// Mouse: real Bezier path, velocity profile, pre-click hover dwell.
|
|
31
|
+
await human.click('button:has-text("Sign in")');
|
|
32
|
+
|
|
33
|
+
// Keyboard: per-key rhythm, optional QWERTY typos, Backspace recovery,
|
|
34
|
+
// occasional mid-word think pauses. The typed string is *not* echoed to
|
|
35
|
+
// plugin params — `params.length` only, by design.
|
|
36
|
+
await human.type('input[name="email"]', 'gonzalo@example.com');
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Speed modes
|
|
40
|
+
|
|
41
|
+
- `'human'` (default) — full humanization on every action.
|
|
42
|
+
- `'fast'` — humanized but accelerated.
|
|
43
|
+
- `'instant'` — bypass humanization entirely; uses Playwright's native methods. Per-key events still fire for `type()`. Right for CI.
|
|
44
|
+
|
|
45
|
+
### Determinism
|
|
46
|
+
|
|
47
|
+
Pass a `seed` and every random decision (path curvature, typo placement, keystroke jitter) becomes reproducible. Same seed + same personality + same value = same keystrokes.
|
|
48
|
+
|
|
49
|
+
### Reading
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
await human.read('p.welcome');
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`human.read()` dwells like a real reader — pause-time scaled by the target's word count and the personality's reading WPM (with personality-controlled jitter).
|
|
56
|
+
|
|
57
|
+
**Target options:**
|
|
58
|
+
|
|
59
|
+
- `string` — Playwright-compatible selector
|
|
60
|
+
- `Locator` — a pre-built Locator
|
|
61
|
+
- `{ text: '...' }` — literal text, no DOM lookup
|
|
62
|
+
- `{ words: 42 }` — pre-counted; skips text extraction entirely
|
|
63
|
+
|
|
64
|
+
**Reading kinds** scale the dwell on top of `personality.reading.wpm`:
|
|
65
|
+
|
|
66
|
+
- `'prose'` (1.0×) — default for non-code targets
|
|
67
|
+
- `'code'` (0.4×) — slower; auto-detected when the target is a `<pre>` or `<code>` element
|
|
68
|
+
- `'scan'` (1.8×) — explicit skim mode
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
await human.read('.article-body'); // prose, default
|
|
72
|
+
await human.read('pre.snippet'); // 'code' auto-detected from <pre>
|
|
73
|
+
await human.read('ul.changelog', { kind: 'scan' }); // explicit skim
|
|
29
74
|
```
|
|
30
75
|
|
|
31
|
-
|
|
76
|
+
Explicit `kind` always wins over auto-detection.
|
|
77
|
+
|
|
78
|
+
**Visible eye-scan motion** during the dwell:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
await human.read('article', { withMotion: true });
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The cursor walks a humanized L→R sweep through every line of rendered text and emits a small return-saccade between lines — same `mousemove` events a real reader would dispatch (so reading-time tooltip / hover handlers fire). Off by default.
|
|
85
|
+
|
|
86
|
+
For demos and screen recordings, pair `withMotion` with `installMouseHelper(page)` to render a visible cursor that follows the synthetic motion:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { createHuman, installMouseHelper } from '@humanjs/playwright';
|
|
90
|
+
|
|
91
|
+
const page = await context.newPage();
|
|
92
|
+
await page.goto('https://example.com/article');
|
|
93
|
+
await installMouseHelper(page);
|
|
94
|
+
|
|
95
|
+
const human = await createHuman(page, { personality: 'careful' });
|
|
96
|
+
await human.read('article', { withMotion: true });
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Returns** a `ReadResult`:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
const { words, durationMs, kind } = await human.read('main');
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Useful for test assertions or surfacing reading metadata in a UI.
|
|
106
|
+
|
|
107
|
+
**Privacy**: the read text is never echoed to plugin params. `read` actions surface only `{ target, kind }` plus inert length metadata — the content itself stays out of telemetry by design, same posture as `human.type()`.
|
|
108
|
+
|
|
109
|
+
### Scrolling
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
await human.scroll(); // ~one viewport down, humanized
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`human.scroll()` produces multi-segment scroll motion with a bell-curve velocity profile (slow start, fast middle, slow end), optional mid-scroll micro-pauses, and — for the `distracted` personality — occasional overshoot + correction. Page scrolls dispatch real `wheel` events; container scrolls advance the element's scroll position directly (more reliable inside nested overflow containers).
|
|
116
|
+
|
|
117
|
+
**Target options:**
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
await human.scroll(); // 'natural' — ~one viewport
|
|
121
|
+
await human.scroll('top'); // to the top
|
|
122
|
+
await human.scroll('end'); // to the bottom
|
|
123
|
+
await human.scroll({ by: 800 }); // relative pixel delta (negative = up)
|
|
124
|
+
await human.scroll({ to: 1500 }); // absolute scroll position on the chosen axis
|
|
125
|
+
await human.scroll('#pricing'); // by selector — scroll until in view
|
|
126
|
+
await human.scroll(locator); // by Locator
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Element-target alignment** matches native `scrollIntoView`:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
await human.scroll('#hero', { block: 'center' }); // 'start' | 'center' | 'end' | 'nearest'
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`'nearest'` is a useful default for "make sure this element is visible without moving more than necessary" — it stays put if the element is already fully in view, otherwise scrolls to the closest edge.
|
|
136
|
+
|
|
137
|
+
**Scroll inside a scrollable container**, not the page:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
await human.scroll('end', { within: '#messages' }); // chat thread to latest
|
|
141
|
+
await human.scroll('#newest-item', { within: '.feed', block: 'end' });
|
|
142
|
+
await human.scroll({ by: -200 }, { within: modalBody }); // scroll up inside a modal
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Every target shape (`'natural'`, `'top'`, `'end'`, selectors, `{ by }`, `{ to }`) applies relative to the container. In humanized mode the cursor parks over the container's center (so an `installMouseHelper` overlay reads as "human hand on the wheel") and each segment advances the container's `scrollLeft` / `scrollTop` directly — more reliable than wheel events inside nested overflow containers. In `'instant'` mode the container's scroll position is set with a single `scrollTo` call.
|
|
146
|
+
|
|
147
|
+
**Horizontal scroll** via `axis: 'x'` — same target shapes apply to the X axis:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
await human.scroll('end', { axis: 'x' }); // to the right edge
|
|
151
|
+
await human.scroll({ by: 400 }, { axis: 'x' }); // 400px right
|
|
152
|
+
await human.scroll('#card-5', { axis: 'x', block: 'center' }); // carousel to a card
|
|
153
|
+
await human.scroll('end', { within: '#kanban', axis: 'x' }); // kanban board to the right end
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Defaults to `'y'`. Combine with `within` for horizontal scrolling inside a container (carousels, kanban boards, sideways galleries).
|
|
157
|
+
|
|
158
|
+
**Force overshoot** even when the personality wouldn't choose one — useful for demos and screen recordings where the humanization signal needs to read clearly:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
await human.scroll('#footer', { overshoot: true });
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Returns** a `ScrollResult`:
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
const { from, to, distance, durationMs } = await human.scroll('end');
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
In `speed: 'instant'`, the page jumps directly via `window.scrollTo` — no wheel events — but the action still fires for observability.
|
|
171
|
+
|
|
172
|
+
See [humanjs.dev](https://humanjs.dev) for the full feature set and personality reference.
|
|
32
173
|
|
|
33
174
|
## License
|
|
34
175
|
|