pw-repl 0.12.7 → 0.13.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/AGENTS.md +13 -7
- package/README.md +76 -74
- package/bin/pw-repl.js +65 -40
- package/lib/background.js +2 -2
- package/lib/capture.js +11 -12
- package/lib/cli-names.js +4 -4
- package/lib/commands.js +3 -0
- package/lib/elements.js +115 -26
- package/lib/emulation.js +2 -1
- package/lib/help.js +166 -73
- package/lib/inspect.js +103 -41
- package/lib/launch.js +3 -1
- package/lib/output.js +10 -3
- package/lib/profile.js +16 -14
- package/lib/record.js +18 -14
- package/lib/routes.js +8 -9
- package/lib/runner.js +39 -9
- package/lib/send.js +28 -6
- package/lib/server.js +4 -1
- package/lib/start.js +7 -2
- package/lib/state.js +26 -9
- package/lib/syntax.js +11 -5
- package/lib/toast.js +1 -6
- package/lib/util.js +25 -1
- package/lib/watch.js +4 -2
- package/lib/written.js +67 -0
- package/package.json +1 -1
- package/skill/SKILL.md +39 -44
package/lib/help.js
CHANGED
|
@@ -22,36 +22,52 @@ function wrap(text, width = WIDTH) {
|
|
|
22
22
|
}).join('\n\n');
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
// The common tasks, as the README's table lists them too (a unit test keeps the two the same).
|
|
26
|
+
const TASKS = [
|
|
27
|
+
['see where I am', 'tab, info'],
|
|
28
|
+
['see what is on the page', 'snapshot, screenshot'],
|
|
29
|
+
['do something on it', 'click, fill, type, press, drag'],
|
|
30
|
+
['click what has no selector (a canvas)', 'mouseclick <x> <y>, mousedblclick <x> <y>'],
|
|
31
|
+
['run some JavaScript in the page', 'eval document.title; eval --file=check.js for a long one'],
|
|
32
|
+
['wait for a page or element', 'wait load, wait <selector> [--gone], wait text <text>'],
|
|
33
|
+
['choose a file in a file input', 'upload <selector> <file>'],
|
|
34
|
+
['see it as a phone, or in dark mode', 'emulate mobile, emulate dark'],
|
|
35
|
+
['see what the page requested', 'requests, then body <#> for what one got back'],
|
|
36
|
+
['see console messages and errors', 'console'],
|
|
37
|
+
['show an agent what I do', 'watch on, click around, then watch'],
|
|
38
|
+
['see each step as I click', 'watch on --live'],
|
|
39
|
+
['watch a tab someone will open', 'watch on --next-tab [url-part]'],
|
|
40
|
+
['replay what I clicked through', 'watch save flow.txt, then pw-repl send --file flow.txt'],
|
|
41
|
+
['record requests and console together', 'capture on, then capture off'],
|
|
42
|
+
['find what makes the page slow', 'profile on, then profile off; profile reload for a load'],
|
|
43
|
+
['fake a backend response', 'route <glob> <status> <json>, route <glob> abort'],
|
|
44
|
+
['change or slow an API response', 'route <glob> patch <json>, route <glob> delay <time>'],
|
|
45
|
+
['cut or slow the whole network', 'network off, network slow, then network on'],
|
|
46
|
+
['record a video of the page', 'record on, then record off (help video for a take)'],
|
|
47
|
+
['show a pointer where the agent acts', 'cursor on, then cursor off'],
|
|
48
|
+
['say what is happening on screen', 'toast <text>, then toast off'],
|
|
49
|
+
['point something out on the page', 'highlight <ref>, then highlight off'],
|
|
50
|
+
['clean up', 'modes off'],
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
// The commands that turn on a mode, as the README lists them too.
|
|
54
|
+
const MODES = 'watch, capture, profile, record, route, network off or slow, emulate, viewport, highlight, cursor and toast';
|
|
55
|
+
|
|
25
56
|
const OVERVIEW = wrap(`pw-repl drives Chromium over CDP. Commands act on the selected tab (\`tab\` shows which).
|
|
26
57
|
The browser may be a shared session, with other users' tabs open in it.
|
|
27
58
|
|
|
28
59
|
Common tasks:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
what did the page request? requests, then body <#> for what one got back
|
|
36
|
-
console messages and errors console
|
|
37
|
-
show an agent what I do watch on, click around, then watch
|
|
38
|
-
see each step as I click watch on --live
|
|
39
|
-
watch a tab someone will open watch on --next-tab [url-part]
|
|
40
|
-
requests and console together capture on, then capture off
|
|
41
|
-
a video of what I do help video
|
|
42
|
-
break the backend on purpose route <glob> <status> <json>, route <glob> abort, network off
|
|
43
|
-
change or slow an API response route <glob> patch <json>, route <glob> delay <secs>
|
|
44
|
-
slow the whole network network slow
|
|
45
|
-
clean up modes off
|
|
46
|
-
|
|
47
|
-
Modes (watch, capture, record, route, network off or slow, emulate, viewport) stay on until turned off;
|
|
48
|
-
the prompt shows the selected tab's: (watch routes:1) pw>. tab, watch, capture, record, route, network,
|
|
49
|
-
emulate and modes on their own show their state and what you can run next.
|
|
60
|
+
${TASKS.map(([task, commands]) => ` ${task.padEnd(38)} ${commands}`).join('\n')}
|
|
61
|
+
|
|
62
|
+
Modes (${MODES}) stay on until turned off; the prompt shows the selected tab's: (watch routes:1) pw>.
|
|
63
|
+
Each of them on its own, and modes, shows its state and what you can run next.
|
|
64
|
+
|
|
65
|
+
Times are in milliseconds, as in Playwright, or in seconds with an s: wait .done 30000, or wait .done 30s.
|
|
50
66
|
|
|
51
67
|
Topics (help <topic>):
|
|
52
68
|
tabs open, select, close, navigate network requests, bodies, console, fakes, network off
|
|
53
69
|
interact click, fill, type, press, select devtools eval, CDP commands, cookie and storage names
|
|
54
|
-
inspect snapshot, watch,
|
|
70
|
+
inspect snapshot, watch, wait, screenshots session modes, server, clients, quitting
|
|
55
71
|
video record a take, with a pointer
|
|
56
72
|
|
|
57
73
|
help <command> for usage and caveats (e.g. help route), help <command> --all for all of it; help --all
|
|
@@ -71,7 +87,7 @@ tab on its own lists them all.`,
|
|
|
71
87
|
Commands use the first match (click, dblclick and hover the first they can use: help click); see help
|
|
72
88
|
fill for selectors with spaces. mousemove and the other mouse commands act at a point instead. An action
|
|
73
89
|
scrolls its element into view first, as Playwright does; profile counts that scroll in the action's step.`,
|
|
74
|
-
commands: ['click', 'dblclick', 'hover', 'fill', 'type', 'press', 'select', 'check', 'uncheck', 'upload', 'mousemove', 'mouseclick', 'mousedown', 'mouseup', 'mousewheel'],
|
|
90
|
+
commands: ['click', 'dblclick', 'hover', 'drag', 'fill', 'type', 'press', 'select', 'check', 'uncheck', 'upload', 'mousemove', 'mouseclick', 'mousedblclick', 'mousedown', 'mouseup', 'mousewheel'],
|
|
75
91
|
},
|
|
76
92
|
inspect: {
|
|
77
93
|
intro: `Output is capped; --all right after the command, or at the end, shows everything (text --all body).`,
|
|
@@ -106,7 +122,7 @@ box to point at something, and toast <text> a line of text saying what is happen
|
|
|
106
122
|
the viewer needs to read it. After a step that loads a page, wait for text only the result shows (wait
|
|
107
123
|
text ...), not a heading the page always has, and not wait load: a single-page app loads nothing.
|
|
108
124
|
|
|
109
|
-
record on --steps also saves when and where each step ran, in the video
|
|
125
|
+
record on --steps also saves when and where each step ran, in seconds into the video and its pixels, for
|
|
110
126
|
editing it afterwards.
|
|
111
127
|
|
|
112
128
|
The user's own screen, with their real pointer, takes a screen recorder they run (OBS, ffmpeg -f
|
|
@@ -121,15 +137,10 @@ clean browser of its own (here, its video-start is record on).`,
|
|
|
121
137
|
devtools: {
|
|
122
138
|
intro: `Cookie and storage listings omit values. eval, cdp, html, screenshots, recordings and URLs can
|
|
123
139
|
still show sensitive data (cdp Network.getCookies shows cookie values).`,
|
|
124
|
-
commands: ['eval', 'cdp', 'cookies', 'storage'],
|
|
140
|
+
commands: ['eval', 'cdp', 'clipboard', 'cookies', 'storage'],
|
|
125
141
|
},
|
|
126
142
|
session: {
|
|
127
|
-
intro: `
|
|
128
|
-
|
|
129
|
-
Unknown outcome: a command that timed out after it began acting on the page may or may not have done
|
|
130
|
-
it. It says so (pw-repl send exits 2), and the REPL carries on.
|
|
131
|
-
|
|
132
|
-
Server: pw-repl serve also takes commands on /tmp/pw-repl.sock; they show here as [server] lines.
|
|
143
|
+
intro: `Server: pw-repl serve also takes commands on /tmp/pw-repl.sock; they show here as [server] lines.
|
|
133
144
|
|
|
134
145
|
Clients: pw-repl send -c <name> (or $PW_CLIENT) sends as a client of its own. It has a selected tab of
|
|
135
146
|
its own, so clients do not move each other's; its commands show as [server:<name>] lines. modes and tab
|
|
@@ -143,7 +154,12 @@ selected tab, if any: (watch network:off routes:2 capture) pw>. modes lists them
|
|
|
143
154
|
|
|
144
155
|
Times (requests, console, watch) are local, with their UTC offset: 18:16:15.721-06:00.
|
|
145
156
|
|
|
146
|
-
Dialogs are reported and never answered on their own; dialog accept or dismiss answers one, from any client, whoever opened it
|
|
157
|
+
Dialogs are reported and never answered on their own; dialog accept or dismiss answers one, from any client, whoever opened it.
|
|
158
|
+
|
|
159
|
+
Unknown outcome: a command that timed out after it began acting on the page may or may not have done
|
|
160
|
+
it. It says so (pw-repl send exits 2), and the REPL carries on.
|
|
161
|
+
|
|
162
|
+
Completion: @<id> <command> makes the REPL print [[pw-done:<id>:ok|error]] when it finishes.`,
|
|
147
163
|
commands: ['modes', 'dialog', 'help', 'quit'],
|
|
148
164
|
},
|
|
149
165
|
};
|
|
@@ -197,6 +213,8 @@ afresh.`,
|
|
|
197
213
|
['[button]', 'left, right or middle, after a ref or a quoted selector: click e5 right. Default: left.'],
|
|
198
214
|
],
|
|
199
215
|
options: [
|
|
216
|
+
['--button=<button>', 'the button, after any selector: click #menu --button=right. Over send, the shell takes the quotes, so pw-repl send click \'#menu\' right clicks a selector "#menu right": use --button, or quote it twice (\'"#menu"\').'],
|
|
217
|
+
['--click-count=<n>', 'clicks this many times at once, as Playwright\'s clickCount: 2 is a double-click (as dblclick), 3 a triple-click, which selects a line of text or all of a field\'s. 1 to 3. Default: 1.'],
|
|
200
218
|
['--modifiers=<keys>', 'keys held down meanwhile, comma-separated: Alt, Control, ControlOrMeta, Meta, Shift.'],
|
|
201
219
|
],
|
|
202
220
|
detail: `Several matches: hidden ones and ones covered by something else (a dialog's overlay) are passed over,
|
|
@@ -208,8 +226,23 @@ it to be ready.
|
|
|
208
226
|
Timed out after it began acting, it says what stopped it (e.g. <div class="overlay"> intercepts pointer
|
|
209
227
|
events) and that the outcome is unknown; Playwright's full call log goes to the REPL's pane or log.`,
|
|
210
228
|
},
|
|
211
|
-
dblclick: {
|
|
229
|
+
dblclick: {
|
|
230
|
+
usage: 'dblclick <selector> [button]',
|
|
231
|
+
summary: 'double-click, as click does, with its options',
|
|
232
|
+
detail: `click --click-count=2, as Playwright's dblclick is. For a triple-click: click --click-count=3.`,
|
|
233
|
+
},
|
|
212
234
|
hover: { usage: 'hover <selector>', summary: 'hover, choosing the match as click does' },
|
|
235
|
+
drag: {
|
|
236
|
+
usage: 'drag <from> <to>',
|
|
237
|
+
summary: 'drag one element onto another',
|
|
238
|
+
args: [
|
|
239
|
+
['<from> <to>', 'selectors or snapshot refs, each quoted if it has spaces: drag e5 e9, drag "text=Item 1" "#done ul".'],
|
|
240
|
+
],
|
|
241
|
+
detail: `As Playwright's dragTo: it hovers the first match of <from>, presses the left button, hovers the
|
|
242
|
+
first match of <to>, and releases, waiting up to 5s in all for each to be ready. An HTML5 drag (a
|
|
243
|
+
draggable element) gets dragstart, dragover and drop; any other gets pointer and mouse events. A drag
|
|
244
|
+
through points of its own: mousemove, mousedown, mousemove, mouseup.`,
|
|
245
|
+
},
|
|
213
246
|
mousemove: {
|
|
214
247
|
usage: 'mousemove <x> <y>',
|
|
215
248
|
summary: 'move the mouse to a point in the viewport',
|
|
@@ -220,7 +253,7 @@ events) and that the outcome is unknown; Playwright's full call log goes to the
|
|
|
220
253
|
eval (r => [r.x + r.width / 2, r.y + r.height / 2])(document.querySelector("#map")
|
|
221
254
|
.getBoundingClientRect())
|
|
222
255
|
|
|
223
|
-
A click at a point: mouseclick <x> <y>. A drag: mousedown, then mousemove to another point, then mouseup.`,
|
|
256
|
+
A click at a point: mouseclick <x> <y>; a double-click: mousedblclick <x> <y>. A drag: mousedown, then mousemove to another point, then mouseup.`,
|
|
224
257
|
},
|
|
225
258
|
mouseclick: {
|
|
226
259
|
usage: 'mouseclick <x> <y> [button]',
|
|
@@ -230,14 +263,30 @@ A click at a point: mouseclick <x> <y>. A drag: mousedown, then mousemove to ano
|
|
|
230
263
|
['[button]', 'left, right or middle. Default: left.'],
|
|
231
264
|
],
|
|
232
265
|
options: [
|
|
266
|
+
['--button=<button>', 'the button, as click\'s: mouseclick 400 300 --button=right.'],
|
|
267
|
+
['--click-count=<n>', 'as click\'s: 2 is a double-click (as mousedblclick), 3 a triple-click. 1 to 3. Default: 1.'],
|
|
233
268
|
['--delay=<ms>', 'holds the button down this long between press and release, as Playwright\'s mouse.click delay. Default: 0.'],
|
|
234
269
|
],
|
|
235
270
|
detail: `For what has no selector, such as a marker drawn on a canvas; anything a selector finds is better
|
|
236
271
|
clicked with click, which waits for it. help mousemove shows how to find a point. The mouse stays at the point, for a mousewheel or mousedown after
|
|
237
272
|
it. The cursor (help cursor) moves there first and shows a ring at the press.`,
|
|
238
273
|
},
|
|
239
|
-
|
|
240
|
-
|
|
274
|
+
mousedblclick: {
|
|
275
|
+
usage: 'mousedblclick <x> <y> [button]',
|
|
276
|
+
summary: 'double-click at a point in the viewport',
|
|
277
|
+
args: [
|
|
278
|
+
['<x> <y>', 'CSS pixels from the top left of the tab\'s viewport, as for mousemove.'],
|
|
279
|
+
['[button]', 'left, right or middle. Default: left.'],
|
|
280
|
+
],
|
|
281
|
+
options: [
|
|
282
|
+
['--button=<button>', 'the button, as click\'s.'],
|
|
283
|
+
],
|
|
284
|
+
detail: `mouseclick --click-count=2, as Playwright's mouse.dblclick is: the page gets two clicks and a
|
|
285
|
+
dblclick. Two mouseclicks are two single clicks, which fire no dblclick. For a triple-click: mouseclick
|
|
286
|
+
<x> <y> --click-count=3.`,
|
|
287
|
+
},
|
|
288
|
+
mousedown: { usage: 'mousedown [button]', summary: 'press a mouse button at the mouse (left, right, middle)', detail: 'The button is a word or --button=<button>, as for mouseclick.' },
|
|
289
|
+
mouseup: { usage: 'mouseup [button]', summary: 'release a mouse button where the mouse is', detail: 'The button is a word or --button=<button>, as for mouseclick.' },
|
|
241
290
|
mousewheel: {
|
|
242
291
|
usage: 'mousewheel <dx> <dy>',
|
|
243
292
|
summary: 'turn the wheel where the mouse is',
|
|
@@ -259,14 +308,19 @@ scrolled or redrawn. While recording, it scrolls in eased steps over up to 1s (h
|
|
|
259
308
|
['--submit', 'at the end: presses Enter in it afterwards.'],
|
|
260
309
|
],
|
|
261
310
|
detail: `Examples: fill #name Ada Lovelace, fill "text=Your name" Ada, fill e7 Ada. It sets the whole value at
|
|
262
|
-
once, even while recording: type shows it being typed
|
|
311
|
+
once, even while recording: type shows it being typed.
|
|
312
|
+
|
|
313
|
+
At the prompt and in a send --file line, \\n, \\t and \\r in double quotes are a newline, a tab and a
|
|
314
|
+
carriage return (fill #notes "line 1\\nline 2"), and \\\\ is a backslash. Over send, the shell's quoting comes
|
|
315
|
+
first, and its "a\\nb" is a literal \\n, typed as a backslash and an n: give it a real newline, as
|
|
316
|
+
$'line 1\\nline 2'.`,
|
|
263
317
|
},
|
|
264
318
|
type: {
|
|
265
319
|
usage: 'type [--delay=<ms>] [<selector>] <text> [--submit]',
|
|
266
320
|
summary: 'type key by key after what the field holds',
|
|
267
321
|
args: [
|
|
268
322
|
['[<selector>]', 'as for fill. Without it (one word, or one quoted), into what has focus: type "garden hose".'],
|
|
269
|
-
['<text>', 'the rest of the line, as fill\'s value.'],
|
|
323
|
+
['<text>', 'the rest of the line, as fill\'s value: a newline in it presses Enter, which in a textarea starts a new line (help fill says how to give one).'],
|
|
270
324
|
],
|
|
271
325
|
options: [
|
|
272
326
|
['--delay=<ms>', 'right after type: the gap between keys. Default: none, or 60 while the tab records.'],
|
|
@@ -320,10 +374,13 @@ all.`,
|
|
|
320
374
|
['--grep <text>', 'only the lines containing text (role, name or a flag such as [disabled], any case), each with the named elements around it.'],
|
|
321
375
|
['--regex <pattern>', '--grep by a regular expression: snapshot --regex "Total: \\d+".'],
|
|
322
376
|
],
|
|
323
|
-
detail: `Refs: a ref (e3,
|
|
377
|
+
detail: `Refs: a ref (e3; f1e3, f2e3 once the page has loaded again, when the old refs stop working) works as a selector in any command: click e3. It belongs
|
|
324
378
|
to the tab whose snapshot gave it, and can change when the page changes (e102 may become f4e98): take a
|
|
325
379
|
new snapshot after it does.
|
|
326
380
|
|
|
381
|
+
Quotes: some names and text are quoted, YAML-style, as one with a quote mark in it is, and their quotes
|
|
382
|
+
are escaped: paragraph: "Say \\"hi\\"" is the text Say "hi" on the page, with no backslashes.
|
|
383
|
+
|
|
327
384
|
--grep: a hit with no named element around it prints without a path. A hit on text (a label) keeps the
|
|
328
385
|
ref of the element holding it, so snapshot <that ref> shows what sits beside it, such as the value next
|
|
329
386
|
to the label.
|
|
@@ -381,7 +438,14 @@ one no selector finds alone) stays a comment saying why, and the save says how m
|
|
|
381
438
|
ends with the last step, not with what it brought: to end on a result that comes later (search results
|
|
382
439
|
loading), add a wait for it at the end (wait text ...).
|
|
383
440
|
|
|
384
|
-
|
|
441
|
+
Watching someone: read a response body over 100 KB that matters as soon as it shows in requests, since
|
|
442
|
+
the browser drops it once the tab navigates. watch leaves out images and other static files, failed ones
|
|
443
|
+
too; console error and requests --all show what failed. Without a url-part, --next-tab takes the first
|
|
444
|
+
tab anyone opens: check tab and info once it fires, since another tab opening later does not show in
|
|
445
|
+
watch.
|
|
446
|
+
|
|
447
|
+
Shared: every client sees the same steps, and watch new's place is shared: when another client watches
|
|
448
|
+
the tab too, read it with watch <n>, since each one's watch new would hide steps from the other.`,
|
|
385
449
|
moreAbout: 'bodies kept, --live timing, what --changes misses, one --next-tab at a time, and what watch save leaves out',
|
|
386
450
|
more: `From its first watch on, a tab keeps response bodies of up to 100 KB, so body <#> still shows one
|
|
387
451
|
after the tab navigates, even when the page navigates as soon as the request answers.
|
|
@@ -465,7 +529,7 @@ circle). That changes the page, so put it back afterwards, as it was if it had a
|
|
|
465
529
|
eval document.querySelector("#rate-3").style.outline = ""`,
|
|
466
530
|
},
|
|
467
531
|
screenshot: {
|
|
468
|
-
usage: 'screenshot [<ref>] [--full] [-d <
|
|
532
|
+
usage: 'screenshot [<ref>] [--full] [-d <time>] [name]',
|
|
469
533
|
summary: 'save a PNG of the viewport, --full page or an element',
|
|
470
534
|
args: [
|
|
471
535
|
['[<ref>]', 'only that element, from a snapshot ref: screenshot e5.'],
|
|
@@ -473,7 +537,7 @@ circle). That changes the page, so put it back afterwards, as it was if it had a
|
|
|
473
537
|
],
|
|
474
538
|
options: [
|
|
475
539
|
['--full', 'the whole page, not only the viewport; --full-page too.'],
|
|
476
|
-
['-d, --delay <
|
|
540
|
+
['-d, --delay <time>', 'counts down out loud first, in seconds, up to 60s, so someone can hold a hover or open a menu.'],
|
|
477
541
|
['--filename=<file>', 'saves it there instead (a JPEG if it ends in .jpg). Relative to the REPL\'s folder; through pw-repl send, the sender\'s. It never replaces a file.'],
|
|
478
542
|
],
|
|
479
543
|
detail: `Where: $PW_SCREENSHOT_DIR (a folder that exists); without it, next to the REPL's socket when it serves on one of its own, or
|
|
@@ -526,9 +590,9 @@ shows in screenshots and recordings. Selectors, snapshot and text don't see it,
|
|
|
526
590
|
],
|
|
527
591
|
detail: `The REPL's clicks and typing don't move a pointer on screen, so a recording shows things happening
|
|
528
592
|
with nothing pointing at them. With the cursor on, these commands move it smoothly to their element or
|
|
529
|
-
point before they act: click, dblclick, hover, fill, type <selector>, press <selector>, select, check,
|
|
530
|
-
uncheck, upload, mousemove and
|
|
531
|
-
records), and the command waits for it. Clicks (click, dblclick, check, uncheck, mouseclick, mousedown, and upload
|
|
593
|
+
point before they act: click, dblclick, hover, drag (to each end), fill, type <selector>, press <selector>, select, check,
|
|
594
|
+
uncheck, upload, mousemove, mouseclick and mousedblclick. Each move takes 150 to 600ms by distance (300 to 1000ms while the tab
|
|
595
|
+
records), and the command waits for it. Clicks (click, dblclick, check, uncheck, mouseclick, mousedblclick, mousedown, and upload
|
|
532
596
|
when it clicks a button) show a ring where they land.
|
|
533
597
|
|
|
534
598
|
Only the drawing moves: no mouse events are sent along the way, so the page gets the same input as with
|
|
@@ -541,19 +605,19 @@ the same spot, and shows in screenshots and recordings. Selectors, snapshot and
|
|
|
541
605
|
takes no clicks, and it points at elements inside frames too.`,
|
|
542
606
|
},
|
|
543
607
|
record: {
|
|
544
|
-
usage: 'record [on [file] [
|
|
608
|
+
usage: 'record [on [file] [time] [options] | off [file]]',
|
|
545
609
|
summary: 'record the selected tab\'s page to a video',
|
|
546
610
|
args: [
|
|
547
611
|
['on | off', 'on starts recording the selected tab; off stops it, saves the file and says its path, length, size and bytes. Neither: says whether it is recording, and how your last recording ended.'],
|
|
548
612
|
['[file]', 'clip.webm or clip.mp4; its ending picks the format. Relative to the REPL\'s folder; through pw-repl send, the sender\'s. Default: recording-<timestamp>.webm where screenshot saves. On record off: saves it under that name instead.'],
|
|
549
|
-
['[
|
|
613
|
+
['[time]', 'stops by itself after it, 1s or more. Default: 1 hour (3600s).'],
|
|
550
614
|
],
|
|
551
615
|
optionsFor: 'record on',
|
|
552
616
|
options: [
|
|
553
617
|
['--steps', 'also save clip.steps.txt next to clip.webm: when and where each command ran (Steps, below). Default: off.'],
|
|
554
618
|
['--pause=<ms>', 'a pause after each action, and each highlight drawn, so the viewer sees it. Default: 750; 0 none.'],
|
|
555
|
-
['--lead=<ms>', 'still page recorded before record on returns. Default: 1000; counts toward
|
|
556
|
-
['--tail=<ms>', 'still page recorded after record off, before it stops. Default: 1000; none when it stops at its
|
|
619
|
+
['--lead=<ms>', 'still page recorded before record on returns. Default: 1000; counts toward its time.'],
|
|
620
|
+
['--tail=<ms>', 'still page recorded after record off, before it stops. Default: 1000; none when it stops at its time.'],
|
|
557
621
|
['--filename=<file>', 'the file, as playwright-cli writes it.'],
|
|
558
622
|
],
|
|
559
623
|
detail: `What it records: the page only, from inside the browser, at the viewport's size in CSS pixels (set
|
|
@@ -594,7 +658,7 @@ height loses a pixel), scrollbars included; a phone's under emulate mobile is it
|
|
|
594
658
|
is 412 wide, less sharp than the phone draws it.
|
|
595
659
|
|
|
596
660
|
How it ended: record on its own, or record off when nothing is recording, says how your last recording
|
|
597
|
-
ended (record off, its
|
|
661
|
+
ended (record off, its time, its tab closing or the REPL stopping) with what record off would have
|
|
598
662
|
said. tab close says what became of a recording it ended. A REPL stopped with pw-repl stop cannot be
|
|
599
663
|
asked: stop prints what became of each recording it ended, and of one that ended by itself that record
|
|
600
664
|
has not shown since.
|
|
@@ -607,13 +671,15 @@ goes on recording. There is no --mp4 or --webm: the file's ending picks the form
|
|
|
607
671
|
summary: 'show, set or unset the viewport size',
|
|
608
672
|
args: [
|
|
609
673
|
['<W>x<H>', 'sets the selected tab\'s page to that size, until viewport off, modes off or the REPL exits. The window is not resized.'],
|
|
610
|
-
['off', 'back to the window\'s
|
|
611
|
-
['(none)', 'shows the size, and whether it was set or is the window\'s.'],
|
|
674
|
+
['off', 'back to the window\'s page area.'],
|
|
675
|
+
['(none)', 'shows the size, and whether it was set or is the window\'s page area.'],
|
|
612
676
|
],
|
|
613
677
|
detail: `emulate mobile sets a phone's size instead, and each refuses while the other is on.
|
|
614
678
|
|
|
615
|
-
The window's
|
|
679
|
+
The window's page area is the page's size without a viewport set. Chromium's --window-size flag sizes the whole
|
|
616
680
|
window, frame included, so the page is smaller: --window-size=1280,900 gives 1280x757 in a headless one.
|
|
681
|
+
--launch starts its Chromium at --window-size=1280,800 (a page of 1280x657 headless), unless the flags
|
|
682
|
+
after -- give one; a profile it was given and has used before may keep its own last size.
|
|
617
683
|
For a page of an exact size, use viewport <W>x<H>.`,
|
|
618
684
|
},
|
|
619
685
|
emulate: {
|
|
@@ -638,14 +704,14 @@ mouse, and sends no touch events. Dark, light and timezone apply at once, but wh
|
|
|
638
704
|
drew with them (times, colors set by its script) changes only when it reloads.`,
|
|
639
705
|
},
|
|
640
706
|
wait: {
|
|
641
|
-
usage: 'wait [text|request] <what> [--gone] [
|
|
707
|
+
usage: 'wait [text|request] <what> [--gone] [time]',
|
|
642
708
|
summary: 'wait for an element, text, a response or a load (10s)',
|
|
643
709
|
args: [
|
|
644
710
|
['<selector>', 'for a matching element to be visible.'],
|
|
645
|
-
['text <text>', 'for text to be visible: any part of an element\'s text, any case. Quote text that ends in a number, or the number is taken as the
|
|
711
|
+
['text <text>', 'for text to be visible: any part of an element\'s text, any case. Quote text that ends in a number, or the number is taken as the time: wait text "Showing 1".'],
|
|
646
712
|
['request <part>', 'for a matching response (a URL part or a glob), counting one that finished since the previous command began, or is still under way: click, then wait request, does not miss it.'],
|
|
647
713
|
['load', 'for the page to finish loading (its load event), counting a navigation that began since the previous command began: click a link, then wait load.'],
|
|
648
|
-
['[
|
|
714
|
+
['[time]', 'how long, with no limit (give send time too: send -t). Default: 10s.'],
|
|
649
715
|
],
|
|
650
716
|
options: [
|
|
651
717
|
['--gone', 'until nothing matching is visible (removed or hidden): wait .spinner --gone. Done at once if nothing matches yet, so wait for it to appear first if it may not have.'],
|
|
@@ -654,8 +720,8 @@ drew with them (times, colors set by its script) changes only when it reloads.`,
|
|
|
654
720
|
something on the new view instead. A wait that times out is an error; the REPL carries on.`,
|
|
655
721
|
},
|
|
656
722
|
sleep: {
|
|
657
|
-
usage: 'sleep <
|
|
658
|
-
summary: 'wait a fixed time,
|
|
723
|
+
usage: 'sleep <time>',
|
|
724
|
+
summary: 'wait a fixed time: sleep 1500, or sleep 1.5s',
|
|
659
725
|
detail: `While recording, each action is already followed by a pause (help record): a sleep adds to it.`,
|
|
660
726
|
},
|
|
661
727
|
requests: {
|
|
@@ -712,11 +778,11 @@ A page brought back from the back/forward cache (back, forward) reports its earl
|
|
|
712
778
|
the time it comes back, though nothing loaded again.`,
|
|
713
779
|
},
|
|
714
780
|
profile: {
|
|
715
|
-
usage: 'profile on [
|
|
781
|
+
usage: 'profile on [time] | off | reload | save <file>',
|
|
716
782
|
summary: 'what slows the tab: long tasks, layouts, input, frames',
|
|
717
783
|
args: [
|
|
718
|
-
['on', 'starts a performance trace of the selected tab, until profile off (at most
|
|
719
|
-
['[
|
|
784
|
+
['on', 'starts a performance trace of the selected tab, until profile off (at most 120s; give it a time for longer).'],
|
|
785
|
+
['[time]', 'records that long (1s or more), then prints. Other commands wait until it ends. Over 20s, give send time too: pw-repl send -t 70s profile on 60s.'],
|
|
720
786
|
['off', 'stops it and prints the summary.'],
|
|
721
787
|
['reload', 'reloads the tab and profiles its load, until its network is idle (at most 10s after load) and half a second more. For work that starts later (a timer): profile on, reload, sleep, profile off.'],
|
|
722
788
|
['save <file>', 'writes the last profile\'s trace, the tab\'s events only, as Chrome trace JSON for DevTools\' Performance panel (Load profile) or ui.perfetto.dev. It refuses a file that exists.'],
|
|
@@ -749,7 +815,7 @@ may time out, but the profile records it all; profile off once the page answers
|
|
|
749
815
|
|
|
750
816
|
Size: a busy page fills Chrome's trace buffer in under a minute (one that restyles many elements as it
|
|
751
817
|
draws, sooner), which stops the profile there, and the summary says so. Stopping a long one takes a
|
|
752
|
-
while, as Chrome writes out its trace: give send time, pw-repl send -t
|
|
818
|
+
while, as Chrome writes out its trace: give send time, pw-repl send -t 60s profile off. If send gives up
|
|
753
819
|
first, the profile still ends, and profile shows it.
|
|
754
820
|
|
|
755
821
|
One profile runs at a time, in a browser: Chrome traces one at a time, the whole browser's, so another
|
|
@@ -780,12 +846,12 @@ the trace even when its page did nothing. The trace is kept in a temporary folde
|
|
|
780
846
|
profile, or until the REPL stops.`,
|
|
781
847
|
},
|
|
782
848
|
capture: {
|
|
783
|
-
usage: 'capture on [requests|console] [
|
|
849
|
+
usage: 'capture on [requests|console] [time] | off',
|
|
784
850
|
summary: 'record requests and console together, in time order',
|
|
785
851
|
args: [
|
|
786
852
|
['on', 'records both until capture off, which prints them.'],
|
|
787
853
|
['requests | console', 'records only one.'],
|
|
788
|
-
['[
|
|
854
|
+
['[time]', 'records that long (1s or more), then prints. Other commands wait until it ends.'],
|
|
789
855
|
['(none)', 'says whether one is running, or shows the last one.'],
|
|
790
856
|
],
|
|
791
857
|
detail: `A timed capture suits recording what someone does in the browser; around your own commands, use
|
|
@@ -798,7 +864,7 @@ and console it keeps more than the last 200, and lists both together.`,
|
|
|
798
864
|
args: [
|
|
799
865
|
['<glob> <status> <json>', 'answers with this status (200-599) and JSON (the rest of the line), or no body. It never reaches the network, so it still answers while the network is off.'],
|
|
800
866
|
['<glob> patch <json>', 'lets it through, then changes its JSON response: a JSON Merge Patch, where objects merge, null removes a key and anything else replaces; one that is not an object (an array) replaces the whole body.'],
|
|
801
|
-
['<glob> delay <
|
|
867
|
+
['<glob> delay <time>', 'holds it that long, then lets it through.'],
|
|
802
868
|
['<glob> abort', 'fails it as if the connection broke.'],
|
|
803
869
|
['off <glob> | --all', 'removes one route, or all.'],
|
|
804
870
|
['(none)', 'lists the selected tab\'s routes.'],
|
|
@@ -824,21 +890,28 @@ replaces it. A glob may be quoted. Example: route **/api/cart patch {"total": 0}
|
|
|
824
890
|
['on', 'restores it.'],
|
|
825
891
|
],
|
|
826
892
|
detail: `Per tab; lasts until network on or the REPL exits. Routes still answer while it is off or slow. To slow
|
|
827
|
-
one API, use route <glob> delay <
|
|
893
|
+
one API, use route <glob> delay <time>. Stopping a service is not the same as network off: a dev proxy
|
|
828
894
|
in front of it usually holds the request open, so the page spins instead of failing.`,
|
|
829
895
|
},
|
|
830
896
|
eval: {
|
|
831
|
-
usage: 'eval [--all] <JavaScript>',
|
|
897
|
+
usage: 'eval [--all] [--timeout=<time>] <JavaScript>',
|
|
832
898
|
summary: 'evaluate JavaScript in the selected tab and print the result',
|
|
833
899
|
args: [
|
|
834
900
|
['<JavaScript>', 'the rest of the line. Promises are awaited, await works at the top level, and a function (() => document.title) is called.'],
|
|
835
|
-
['<function> <ref>', 'calls it with that element: eval "el => el.textContent" e5.'],
|
|
901
|
+
['<function> <ref>', 'calls it with that element: eval "el => el.textContent" e5. Quoted, its \\n is a newline and its \\" a quote: write \\\\n for a JS string\'s \\n.'],
|
|
836
902
|
],
|
|
837
903
|
options: [
|
|
904
|
+
['--file=<path>', 'the JavaScript is the file\'s text: anything the line takes, over many lines and with // comments. With a ref or a selector after the path, it is a function called with that element (the first match). Over send, a relative path is from your shell\'s current folder.'],
|
|
905
|
+
['--timeout=<time>', 'how long the page has to answer, before --file. Default: 15s. Past 20s, give send time too, or it gives up first and cancels the eval: pw-repl send -t 70s eval --timeout=60s ...'],
|
|
838
906
|
['--all', 'the whole result, not capped.'],
|
|
839
907
|
],
|
|
840
908
|
detail: `eval fetch(...) sends a new request, which can change state on the server; it cannot read one that
|
|
841
|
-
already happened (requests lists those)
|
|
909
|
+
already happened (requests lists those).
|
|
910
|
+
|
|
911
|
+
Long work (--timeout): every other command waits while an eval runs, the other clients' too. For work of
|
|
912
|
+
minutes, or in a browser others are using, start it without awaiting it, keep its result on window, and
|
|
913
|
+
read it back: eval "window.__done = null; run().then(r => window.__done = r); 'started'", then eval
|
|
914
|
+
window.__done until it is set.`,
|
|
842
915
|
},
|
|
843
916
|
cdp: {
|
|
844
917
|
usage: 'cdp [--all] <method> <JSON object>',
|
|
@@ -852,6 +925,20 @@ already happened (requests lists those).`,
|
|
|
852
925
|
],
|
|
853
926
|
detail: `The session is detached afterwards, so subscriptions and settings do not persist. Browser.close and
|
|
854
927
|
Target.closeTarget are refused, and so is Page.handleJavaScriptDialog: dialog answers one.`,
|
|
928
|
+
},
|
|
929
|
+
clipboard: {
|
|
930
|
+
usage: 'clipboard [--all] [--shared-clipboard]',
|
|
931
|
+
summary: 'print the text on the clipboard, as a Copy button left it',
|
|
932
|
+
options: [
|
|
933
|
+
['--shared-clipboard', 'reads it in a browser with a window too, whose clipboard is the computer\'s (below).'],
|
|
934
|
+
['--all', 'the whole text, not capped.'],
|
|
935
|
+
],
|
|
936
|
+
detail: `It reads through the selected tab, which must be a secure page (https, or http on localhost), granting
|
|
937
|
+
it clipboard-read only while it reads. Check a Copy button: click it, then clipboard.
|
|
938
|
+
|
|
939
|
+
A headless browser, as --launch starts, has a clipboard of its own, holding only what its pages copied.
|
|
940
|
+
One with a window reads the computer's: whatever its user last copied, a password too. There it refuses
|
|
941
|
+
without --shared-clipboard; ask the user before giving it.`,
|
|
855
942
|
},
|
|
856
943
|
cookies: {
|
|
857
944
|
usage: 'cookies [--all]',
|
|
@@ -908,7 +995,11 @@ at the browser (e.g. headless), it is the only way to answer one.`,
|
|
|
908
995
|
quit: {
|
|
909
996
|
usage: 'quit',
|
|
910
997
|
summary: 'disconnect, leaving Chromium running (one --launch started stops too)',
|
|
911
|
-
detail: `Runs immediately, even while another command is waiting. Only available at the prompt
|
|
998
|
+
detail: `Runs immediately, even while another command is waiting. Only available at the prompt.
|
|
999
|
+
|
|
1000
|
+
As it stops (quit, or pw-repl stop), it says, for each client, how many of the files it wrote are still
|
|
1001
|
+
there, how large, and in which folders: Files left by cart-bug: 3 files, 48 MB (2 screenshots, 1 video)
|
|
1002
|
+
in /tmp/. pw-repl send -c <name> close lists a client's own in full.`,
|
|
912
1003
|
},
|
|
913
1004
|
};
|
|
914
1005
|
|
|
@@ -990,9 +1081,11 @@ Different on purpose, since the browser may be someone else's too:
|
|
|
990
1081
|
if (Object.hasOwn(TOPICS, topicOrCommand)) {
|
|
991
1082
|
const topic = TOPICS[topicOrCommand];
|
|
992
1083
|
const text = [topic.intro, '', ...commandLines(topic.commands)].join('\n');
|
|
993
|
-
// A topic can share its name with a command (network)
|
|
994
|
-
return Object.hasOwn(COMMANDS, topicOrCommand) ? `${text}\n\n${
|
|
1084
|
+
// A topic can share its name with a command (network): the topic, and where the command's help is.
|
|
1085
|
+
return Object.hasOwn(COMMANDS, topicOrCommand) ? `${text}\n\n${topicOrCommand} is also a command: help ${topicOrCommand} --command` : text;
|
|
995
1086
|
}
|
|
1087
|
+
const asCommand = /^(\S+)\s+--command$/.exec(topicOrCommand);
|
|
1088
|
+
if (asCommand && Object.hasOwn(COMMANDS, asCommand[1])) return renderCommand(asCommand[1]);
|
|
996
1089
|
if (Object.hasOwn(COMMANDS, topicOrCommand)) return renderCommand(topicOrCommand);
|
|
997
1090
|
// Not a topic: the mouse commands are in interact, with the others that act on the page.
|
|
998
1091
|
if (topicOrCommand === 'mouse') {
|
|
@@ -1020,4 +1113,4 @@ function notFound(topic) {
|
|
|
1020
1113
|
return `No help for ${topic}. Topics: ${Object.keys(TOPICS).join(', ')}`;
|
|
1021
1114
|
}
|
|
1022
1115
|
|
|
1023
|
-
module.exports = { render, notFound, TOPICS, COMMANDS };
|
|
1116
|
+
module.exports = { render, notFound, TOPICS, COMMANDS, TASKS, MODES };
|