@humanjs/playwright 0.5.0 → 0.6.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 +42 -0
- package/dist/index.cjs +60 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +112 -8
- package/dist/index.d.ts +112 -8
- package/dist/index.js +60 -8
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @humanjs/playwright
|
|
2
2
|
|
|
3
|
+
<p>
|
|
4
|
+
<a href="https://www.npmjs.com/package/@humanjs/playwright"><img alt="npm" src="https://img.shields.io/npm/v/@humanjs/playwright"></a>
|
|
5
|
+
<a href="https://www.npmjs.com/package/@humanjs/playwright"><img alt="downloads" src="https://img.shields.io/npm/dt/@humanjs/playwright"></a>
|
|
6
|
+
<a href="https://github.com/totigm/humanjs"><img alt="GitHub" src="https://img.shields.io/badge/GitHub-totigm%2Fhumanjs-181717?logo=github"></a>
|
|
7
|
+
<a href="https://github.com/totigm/humanjs/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/totigm/humanjs/actions/workflows/ci.yml/badge.svg"></a>
|
|
8
|
+
<a href="https://github.com/totigm/humanjs/blob/main/LICENSE"><img alt="license" src="https://img.shields.io/npm/l/@humanjs/playwright"></a>
|
|
9
|
+
<a href="https://humanjs.dev"><img alt="docs" src="https://img.shields.io/badge/docs-humanjs.dev-emerald"></a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
3
12
|
Humanize Playwright sessions for AI agents, QA tests, and demos. Drop-in adapter for an existing Playwright `Page`.
|
|
4
13
|
|
|
5
14
|
## Install
|
|
@@ -364,6 +373,39 @@ await rec.toTimeline('session.json'); // works
|
|
|
364
373
|
|
|
365
374
|
Every recording is a regular plugin action — `beforeAction` and `afterAction` observe `{ type: 'record' }` exactly like `'click'` or `'scroll'`.
|
|
366
375
|
|
|
376
|
+
## Using your own browser or a persistent profile
|
|
377
|
+
|
|
378
|
+
`createHuman(page)` wraps **any** Playwright `Page` — so reusing a saved login, your installed Chrome, or an already-running browser is just a matter of how you create that page. HumanJS adds nothing special here; these are standard Playwright entry points, collected so you don't have to hunt for them.
|
|
379
|
+
|
|
380
|
+
**Persistent profile** — keep cookies, local storage, and logins across runs. The first run signs in; later runs are already authenticated:
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
import { chromium, createHuman } from '@humanjs/playwright';
|
|
384
|
+
|
|
385
|
+
const context = await chromium.launchPersistentContext('./.humanjs-profile', {
|
|
386
|
+
headless: false,
|
|
387
|
+
channel: 'chrome', // optional: use installed Google Chrome instead of bundled Chromium
|
|
388
|
+
});
|
|
389
|
+
const page = context.pages()[0] ?? (await context.newPage());
|
|
390
|
+
|
|
391
|
+
const human = await createHuman(page, { personality: 'careful' });
|
|
392
|
+
// …drive the page; state persists in ./.humanjs-profile for next time
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
**Attach to an already-running browser** — drive a Chrome you started yourself, with all its existing tabs, extensions, and sessions. Launch Chrome with a debugging port first (`chrome --remote-debugging-port=9222`), then:
|
|
396
|
+
|
|
397
|
+
```ts
|
|
398
|
+
import { chromium, createHuman } from '@humanjs/playwright';
|
|
399
|
+
|
|
400
|
+
const browser = await chromium.connectOverCDP('http://localhost:9222');
|
|
401
|
+
const context = browser.contexts()[0];
|
|
402
|
+
const page = context.pages()[0] ?? (await context.newPage());
|
|
403
|
+
|
|
404
|
+
const human = await createHuman(page, { personality: 'careful' });
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
> **Heads up:** a persistent profile or a connected real browser carries whatever you're signed into. Driving it means the automation can act with those sessions' privileges — keep that in mind for anything sensitive, and be wary of pages that try to manipulate an agent into actions while logged in.
|
|
408
|
+
|
|
367
409
|
## License
|
|
368
410
|
|
|
369
411
|
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -325,15 +325,22 @@ function clamp(value, min, max) {
|
|
|
325
325
|
// src/mouse/index.ts
|
|
326
326
|
async function executeClick(target, ctx, options = {}) {
|
|
327
327
|
const button = options.button ?? "left";
|
|
328
|
-
const locator = typeof target === "string" ? ctx.page.locator(target) : target;
|
|
329
328
|
if (ctx.speed === "instant") {
|
|
330
|
-
|
|
329
|
+
if (isPoint(target)) {
|
|
330
|
+
await ctx.page.mouse.click(target.x, target.y, { button });
|
|
331
|
+
ctx.setMousePosition(target);
|
|
332
|
+
return { target };
|
|
333
|
+
}
|
|
334
|
+
const locator = typeof target === "string" ? ctx.page.locator(target) : target;
|
|
335
|
+
const box2 = await locator.boundingBox();
|
|
331
336
|
await locator.click({ button });
|
|
332
|
-
const center =
|
|
337
|
+
const center = box2 ? { x: box2.x + box2.width / 2, y: box2.y + box2.height / 2 } : ctx.getMousePosition();
|
|
333
338
|
ctx.setMousePosition(center);
|
|
334
339
|
return { target: center };
|
|
335
340
|
}
|
|
336
|
-
const targetPoint = await
|
|
341
|
+
const { point: targetPoint, box } = await resolveTargetPointAndBox(target, ctx, "click");
|
|
342
|
+
await maybeMisclickBeat(ctx, box, targetPoint);
|
|
343
|
+
await walkBezierTo(targetPoint, ctx);
|
|
337
344
|
const preClickMs = computeDwellTime(
|
|
338
345
|
ctx.personality.dwell.preClickMs,
|
|
339
346
|
ctx.personality.dwell.preClickJitter,
|
|
@@ -362,7 +369,7 @@ async function executeHover(target, ctx) {
|
|
|
362
369
|
ctx.setMousePosition(center);
|
|
363
370
|
return { target: center };
|
|
364
371
|
}
|
|
365
|
-
const targetPoint = await moveToTarget(target, ctx
|
|
372
|
+
const targetPoint = await moveToTarget(target, ctx);
|
|
366
373
|
const dwellMs = computeDwellTime(
|
|
367
374
|
ctx.personality.dwell.preClickMs,
|
|
368
375
|
ctx.personality.dwell.preClickJitter,
|
|
@@ -445,10 +452,9 @@ async function executeMove(target, ctx) {
|
|
|
445
452
|
ctx.setMousePosition(point);
|
|
446
453
|
return { target: point };
|
|
447
454
|
}
|
|
448
|
-
async function moveToTarget(target, ctx
|
|
449
|
-
const box = await readBoxWithAutoScroll(target, ctx,
|
|
455
|
+
async function moveToTarget(target, ctx) {
|
|
456
|
+
const box = await readBoxWithAutoScroll(target, ctx, "hover");
|
|
450
457
|
const targetPoint = pickClickPoint(box, ctx.rng, ctx.personality.mouse.clickSpread);
|
|
451
|
-
if (action === "click") await maybeMisclickBeat(ctx, box, targetPoint);
|
|
452
458
|
await walkBezierTo(targetPoint, ctx);
|
|
453
459
|
return targetPoint;
|
|
454
460
|
}
|
|
@@ -1433,6 +1439,52 @@ async function createHuman(page, options = {}) {
|
|
|
1433
1439
|
speed,
|
|
1434
1440
|
events
|
|
1435
1441
|
});
|
|
1442
|
+
},
|
|
1443
|
+
// ────────────────────────────────────────────────────────────────────
|
|
1444
|
+
// Thin re-exports of common Playwright `Page` methods. See the `Human`
|
|
1445
|
+
// interface for the rationale; implementations forward unchanged.
|
|
1446
|
+
// ────────────────────────────────────────────────────────────────────
|
|
1447
|
+
screenshot(opts) {
|
|
1448
|
+
return page.screenshot(opts);
|
|
1449
|
+
},
|
|
1450
|
+
pageText() {
|
|
1451
|
+
return page.innerText("body");
|
|
1452
|
+
},
|
|
1453
|
+
content() {
|
|
1454
|
+
return page.content();
|
|
1455
|
+
},
|
|
1456
|
+
url() {
|
|
1457
|
+
return page.url();
|
|
1458
|
+
},
|
|
1459
|
+
title() {
|
|
1460
|
+
return page.title();
|
|
1461
|
+
},
|
|
1462
|
+
async reload(opts) {
|
|
1463
|
+
await performAction({ type: "reload", params: {} }, async () => {
|
|
1464
|
+
await page.reload(opts);
|
|
1465
|
+
});
|
|
1466
|
+
},
|
|
1467
|
+
async goBack(opts) {
|
|
1468
|
+
await performAction({ type: "goBack", params: {} }, async () => {
|
|
1469
|
+
await page.goBack(opts);
|
|
1470
|
+
});
|
|
1471
|
+
},
|
|
1472
|
+
async goForward(opts) {
|
|
1473
|
+
await performAction({ type: "goForward", params: {} }, async () => {
|
|
1474
|
+
await page.goForward(opts);
|
|
1475
|
+
});
|
|
1476
|
+
},
|
|
1477
|
+
waitForLoadState(state, opts) {
|
|
1478
|
+
return page.waitForLoadState(state, opts);
|
|
1479
|
+
},
|
|
1480
|
+
waitForURL(url, opts) {
|
|
1481
|
+
return page.waitForURL(url, opts);
|
|
1482
|
+
},
|
|
1483
|
+
setViewportSize(size) {
|
|
1484
|
+
return page.setViewportSize(size);
|
|
1485
|
+
},
|
|
1486
|
+
pdf(opts) {
|
|
1487
|
+
return page.pdf(opts);
|
|
1436
1488
|
}
|
|
1437
1489
|
};
|
|
1438
1490
|
}
|