@qawolf/cli 1.38.0 → 1.40.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 +1 -1
- package/dist/cli.js +873 -38
- package/dist/runner-sdk.js +406 -8
- package/package.json +2 -2
- package/skills/qawolf-cli/SKILL.md +6 -3
- package/skills/qawolf-cli/references/run-results.md +1 -0
- package/skills/qawolf-cli/references/runner.md +1096 -545
|
@@ -1,662 +1,1213 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
An interactive runner is a live pod with a browser in it. You launch one, look
|
|
4
|
-
at it, act on it, run flows on it, and read what it recorded. Everything is a
|
|
5
|
-
plain request to one host, so there is no connection to hold open.
|
|
6
|
-
|
|
7
|
-
## Getting one
|
|
8
|
-
|
|
9
|
-
Runner ids are yours to choose and are scoped to your team, so `agent-1` is a
|
|
10
|
-
fine id. Launching an id that is already running attaches to that runner instead
|
|
11
|
-
of starting and billing a second one, and the answer says which happened: read
|
|
12
|
-
`alreadyRunning`. Reusing one id is therefore the
|
|
13
|
-
cheap and safe pattern, and the same id with a different `--name` is refused
|
|
14
|
-
rather than silently ignored.
|
|
15
|
-
|
|
16
|
-
Either answer carries a `url`, which `qawolf runner launch` prints, as does a command that launched its own runner. It is a QA Wolf page showing what the runner is doing, where a person can also take over with their own mouse and keyboard. Hand it to a person who asks what your runner is up to. The page opens for anyone on the runner's team, however the runner was launched. There is nothing to see until the runner's first run starts its screen, so the page waits until then. You read the screen with `screenshot`, not with the page.
|
|
17
|
-
|
|
18
|
-
Commands that target a runner find one in this order: `--runner`, then
|
|
19
|
-
`QAWOLF_RUNNER_ID`, then the runner stored for the current directory (which
|
|
20
|
-
`qawolf runner launch` sets). Setting the environment variable once is the most
|
|
21
|
-
robust for a harness whose working directory may not be stable, but it comes
|
|
22
|
-
with three catches worth knowing before you rely on it.
|
|
23
|
-
|
|
24
|
-
`qawolf runner launch` is not in that order: it takes its id from `--id` and
|
|
25
|
-
never reads `QAWOLF_RUNNER_ID`. Bare `qawolf runner launch` invents a random id,
|
|
26
|
-
bills a pod under it and stores it, so a harness that exported the variable and
|
|
27
|
-
then launched without `--id` ends up with a pod it is not addressing. Pass
|
|
28
|
-
`--id` whenever you have an id in mind.
|
|
29
|
-
|
|
30
|
-
And a runner id that is set is treated as found, whether or not anything is
|
|
31
|
-
running under it. So exporting `QAWOLF_RUNNER_ID=agent-1` turns off the
|
|
32
|
-
auto-launch described next: instead of starting `agent-1`, commands try to reach
|
|
33
|
-
it and fail with exit code `8`, naming the id and saying the variable is what
|
|
34
|
-
chose it. Launch that id once yourself and the rest follows.
|
|
35
|
-
|
|
36
|
-
And launching an id that differs from `QAWOLF_RUNNER_ID` prints a warning on
|
|
37
|
-
stderr naming both ids: the variable still outranks the directory default, so
|
|
38
|
-
commands that omit `--runner` keep going to whatever it names, not the runner
|
|
39
|
-
you just launched. Expected when you are launching an additional runner on
|
|
40
|
-
purpose — address that one with `--runner` rather than re-exporting the
|
|
41
|
-
variable, which would repoint every other runner-less command too.
|
|
42
|
-
|
|
43
|
-
If nothing names a runner, the commands that change something will launch one
|
|
44
|
-
and say so on stderr, naming it: `run`, `act` and `exec`. **Read that
|
|
45
|
-
announcement.** The browser it just started is fresh: nothing has been run on it,
|
|
46
|
-
nothing is signed in, and no page is open. Acting as though your earlier setup
|
|
47
|
-
survived is the single most likely way to drive the wrong page.
|
|
48
|
-
|
|
49
|
-
No `read` command ever launches a runner. `screenshot`, `events` and `keepalive`
|
|
50
|
-
tell you there is no runner rather than quietly billing one, and so does
|
|
51
|
-
`terminate`, since starting a pod in order to end it would be absurd.
|
|
52
|
-
|
|
53
|
-
`--name` also chooses between a browser and a mobile device:
|
|
54
|
-
`qawolf runner launch --name android` or `--name ios` starts an Appium session
|
|
55
|
-
instead of a browser. A command built for the other kind answers a
|
|
56
|
-
`failureReason` naming the mismatch rather than doing something approximate —
|
|
57
|
-
`runner-is-not-mobile` from `inspect session`/`contexts`/`page-source`/`elements`
|
|
58
|
-
on a browser runner, `runner-is-not-a-browser` from `inspect element-html`/
|
|
59
|
-
`page-html` on a mobile one — so launch the right family up front rather than
|
|
60
|
-
discovering it from a refusal mid-session.
|
|
61
|
-
|
|
62
|
-
## Knowing what you are holding
|
|
63
|
-
|
|
64
|
-
`qawolf runner list` names the runners this directory has launched that are
|
|
65
|
-
still running, and marks the one a command with no `--runner` would reach:
|
|
1
|
+
<!-- Generated by `bun run generate` from `qawolf help ref runner`, do not edit. Edit the runner commands' help under src/commands/runner instead. -->
|
|
66
2
|
|
|
67
|
-
|
|
68
|
-
id family default
|
|
69
|
-
tester-abc-main playwright yes
|
|
70
|
-
tester-abc-checkout playwright
|
|
71
|
-
```
|
|
3
|
+
# qawolf runner
|
|
72
4
|
|
|
73
|
-
|
|
74
|
-
absent rather than reported. The lookup neither starts a runner nor resets an
|
|
75
|
-
inactivity clock, which is what separates `list` from `keepalive`: listing tells
|
|
76
|
-
you what is there and changes nothing, and holding a runner open is still
|
|
77
|
-
`keepalive`'s job. A lookup that cannot be answered fails the command rather
|
|
78
|
-
than returning a shorter list, because a short list reads as the whole truth.
|
|
5
|
+
An interactive runner is a live pod with a browser or a mobile device in it. You launch one, look at it, act on it, run flows on it, and read what it recorded. Everything is a plain request to one host, so there is no connection to hold open. This guide covers what spans commands; each command's own help below covers the rest.
|
|
79
6
|
|
|
80
|
-
|
|
81
|
-
what you are done with.
|
|
7
|
+
## Which runner a command reaches
|
|
82
8
|
|
|
83
|
-
|
|
84
|
-
directory did not launch it, so a harness handed a runner sees it alongside the
|
|
85
|
-
ones it started itself. Use the `id` column with `--runner` to address any of
|
|
86
|
-
them; addressing one does not make it the default.
|
|
9
|
+
Commands that target a runner find one in this order: `--runner`, then `QAWOLF_RUNNER_ID`, then the runner stored for the current directory, which `qawolf runner launch` sets. Setting the environment variable once is the most robust for a harness whose working directory may not be stable, with two catches:
|
|
87
10
|
|
|
88
|
-
|
|
11
|
+
- `qawolf runner launch` is not in that order: it takes its id from `--id` and never reads `QAWOLF_RUNNER_ID`. Pass `--id` whenever you have an id in mind.
|
|
12
|
+
- A runner id that is set is treated as found, whether or not anything is running under it. Exporting `QAWOLF_RUNNER_ID=agent-1` turns off the auto-launch described next: instead of starting `agent-1`, commands try to reach it and fail with exit code `8`, naming the id and saying the variable is what chose it. Launch that id once yourself and the rest follows.
|
|
89
13
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
14
|
+
If nothing names a runner, the commands that change something launch one and say so on stderr, naming it: `run`, `act` and `exec`. Read that announcement. The browser it just started is fresh: nothing has been run on it, nothing is signed in, and no page is open. Acting as though your earlier setup survived is the single most likely way to drive the wrong page.
|
|
15
|
+
|
|
16
|
+
No read command ever launches a runner. `screenshot`, `events` and `keepalive` tell you there is no runner rather than quietly billing one, and so does `terminate`.
|
|
93
17
|
|
|
94
18
|
## The order that matters
|
|
95
19
|
|
|
96
|
-
A freshly launched runner has no screen. The virtual desktop starts with the
|
|
97
|
-
runner's **first run** and nothing else starts it, so until you have run
|
|
98
|
-
something:
|
|
20
|
+
A freshly launched runner has no screen. The virtual desktop starts with the runner's first run and nothing else starts it, so until you have run something:
|
|
99
21
|
|
|
100
|
-
- `screenshot` and `act` fail with exit code `2`, except `navigate`, which
|
|
101
|
-
fails with exit code `1` (`action-failed`): it skips the screen but still
|
|
102
|
-
needs the runner to have run something
|
|
22
|
+
- `screenshot` and `act` fail with exit code `2`, except `navigate`, which fails with exit code `1` (`action-failed`): it skips the screen but still needs the runner to have run something
|
|
103
23
|
- `exec` fails with exit code `2`
|
|
104
24
|
- `events recorder` reads as empty
|
|
105
25
|
|
|
106
|
-
None of that is a fault, and none of it clears on its own.
|
|
107
|
-
`qawolf runner run <flow>` starts the screen.** A bare navigate does not: it
|
|
108
|
-
fails until the first run, however long you wait.
|
|
26
|
+
None of that is a fault, and none of it clears on its own. Only `qawolf runner run <flow>` starts the screen. A bare navigate does not: it fails until the first run, however long you wait.
|
|
109
27
|
|
|
110
|
-
So the first thing you do to a new runner has to put a browser on it. That means
|
|
111
|
-
a flow file and a `package.json` on disk, even if all you want is to drive the
|
|
112
|
-
browser by hand; there is no "just give me a screen" call. Once one run has
|
|
113
|
-
happened, the screenshot-and-act loop below works for the rest of the runner's
|
|
114
|
-
life.
|
|
28
|
+
So the first thing you do to a new runner has to put a browser on it. That means a flow file and a `package.json` on disk, even if all you want is to drive the browser by hand; there is no "just give me a screen" call. Once one run has happened, the screenshot-and-act loop works for the rest of the runner's life. A `--lines` selection is the one call that does not need a run first.
|
|
115
29
|
|
|
116
|
-
|
|
117
|
-
runner with no browser, the runner starts one and runs your lines against it,
|
|
118
|
-
and says so on stderr. Everything else on this page waits for a run.
|
|
30
|
+
## Exit codes
|
|
119
31
|
|
|
120
32
|
Retry on the exit code, not on the message text:
|
|
121
33
|
|
|
122
|
-
- `4` is transient, with one exception. The screen is up but cannot serve this instant: restarting after a display-size change, or busy with another request. Retry in a second or two, and bound the retries. The exception is a command that changes something, where a `4` can instead mean the answer was lost with the work in flight. For `act` and `actions`, take a screenshot first and repeat only what the screen says did not happen. For `run`, poll `run-status` instead of submitting again, since a second submission risks a second billed run
|
|
123
|
-
- `6` means the work ran out of the time it is given.
|
|
124
|
-
- `8` means there is no such runner. It was never launched, or it was
|
|
125
|
-
|
|
126
|
-
launch the id or name one that is running. The message says which runner was
|
|
127
|
-
meant and whether `--runner`, `QAWOLF_RUNNER_ID` or this directory's stored
|
|
128
|
-
default chose it — read that line before you pick an id to launch.
|
|
129
|
-
- `2` will not clear on its own. Nothing has run on this runner yet, so run a flow; or the runner has no browser at all, so launch with `--name playwright` instead; or the action belongs to the other runner family, so send `tap`, `swipe` or `fill` to a mobile runner and the browser actions to a browser runner. A `runner actions` sequence also exits `2` before it sends anything, for an argument that is not a JSON array, an array of more than ten actions, and `--screenshot` flags that contradict each other. The message says which.
|
|
130
|
-
|
|
131
|
-
## Seeing and acting: the loop is yours
|
|
132
|
-
|
|
133
|
-
Two primitives, and you close the loop with your own model. There is no hosted
|
|
134
|
-
vision loop on this surface.
|
|
135
|
-
|
|
136
|
-
`qawolf runner screenshot --out page.jpg` writes a real JPEG to disk, decoded,
|
|
137
|
-
because every coding harness can open an image file. Read it with whatever
|
|
138
|
-
vision you have. `--out -` writes the JPEG bytes to stdout instead, on their own,
|
|
139
|
-
for a caller that is a process rather than an agent: the confirmation, and the
|
|
140
|
-
JSON line under `--json`, goes to stderr so nothing follows the image on stdout.
|
|
141
|
-
A terminal on stdout is refused: redirect or pipe it.
|
|
142
|
-
|
|
143
|
-
`qawolf runner act <action>` performs exactly one action per call, in the
|
|
144
|
-
computer-use tool vocabulary a vision model already emits: `click`,
|
|
145
|
-
`double_click`, `scroll`, `move`, `drag`, `keypress`, `navigate`, `type`. The
|
|
146
|
-
names and the field names are unchanged from that vocabulary on purpose, so you
|
|
147
|
-
can forward a tool call rather than translate it:
|
|
34
|
+
- `4` is transient, with one exception. The screen is up but cannot serve this instant: restarting after a display-size change, or busy with another request. Retry in a second or two, and bound the retries. The exception is a command that changes something, where a `4` can instead mean the answer was lost with the work in flight. For `act` and `actions`, take a screenshot first and repeat only what the screen says did not happen. For `run`, poll `run-status` instead of submitting again, since a second submission risks a second billed run. For `exec`, check the effect the snippet was meant to have before running it again.
|
|
35
|
+
- `6` means the work ran out of the time it is given.
|
|
36
|
+
- `8` means there is no such runner. It was never launched, or it was terminated, or it idled out. Retrying never brings one back, so stop and launch the id or name one that is running. The message says which runner was meant and whether `--runner`, `QAWOLF_RUNNER_ID` or this directory's stored default chose it; read that line before you pick an id to launch.
|
|
37
|
+
- `2` will not clear on its own. Nothing has run on this runner yet, so run a flow; or the runner has no browser at all, so launch with `--name playwright` instead; or the action belongs to the other runner family, so send `tap`, `swipe` or `fill` to a mobile runner and the browser actions to a browser runner. The message says which.
|
|
148
38
|
|
|
149
|
-
|
|
150
|
-
echo '{"type":"click","button":"left","x":480,"y":260}' | qawolf runner act -
|
|
151
|
-
```
|
|
39
|
+
`act`, `actions`, `run` and `exec` are the commands whose lost answer may still have taken effect.
|
|
152
40
|
|
|
153
|
-
|
|
154
|
-
for it in the same call: `act ... --screenshot step-1.jpg` (or `--screenshot -`
|
|
155
|
-
for stdout) performs the action and writes the screen the runner answers with.
|
|
156
|
-
Prefer it over `act` and then `screenshot`: each step is one call instead of
|
|
157
|
-
two, with no delay to guess at between them. The screen the runner answers with
|
|
158
|
-
is taken once it has changed from just before the action or half a second has
|
|
159
|
-
passed, whichever comes first. An action that reached the screen and did not
|
|
160
|
-
take effect answers with one too, so a `1` from `act --screenshot` still leaves
|
|
161
|
-
you a picture of why the click missed. A `4` whose message starts with
|
|
162
|
-
"Performed" means the action happened and only the picture is missing: take a
|
|
163
|
-
`screenshot`, never send the action again to get it. As with
|
|
164
|
-
`screenshot --out -`, a terminal on stdout is refused.
|
|
41
|
+
## End to end
|
|
165
42
|
|
|
166
|
-
|
|
43
|
+
Run from a directory holding a flow and a `package.json`. The run is what starts the screen, so it is not optional even though the goal here is to drive by hand.
|
|
167
44
|
|
|
168
|
-
|
|
45
|
+
```sh
|
|
46
|
+
export QAWOLF_API_KEY=... # the only credential
|
|
47
|
+
export QAWOLF_RUNNER_ID=agent-1 # so no command below needs --runner
|
|
169
48
|
|
|
170
|
-
|
|
49
|
+
qawolf runner launch --id agent-1 --json # --id, not the variable; read .alreadyRunning and .url
|
|
50
|
+
qawolf runner run flows/smoke.flow.ts --follow # starts the screen; exit 1 if it failed
|
|
171
51
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
- `type` types into whatever the last tap focused, the same as on a browser.
|
|
52
|
+
qawolf runner act navigate --url https://example.com/login --screenshot step-1.jpg # then read step-1.jpg yourself
|
|
53
|
+
qawolf runner act click --button left --x 480 --y 260 --screenshot step-2.jpg
|
|
54
|
+
qawolf runner act type --text "someone@example.com" --screenshot step-3.jpg
|
|
176
55
|
|
|
177
|
-
|
|
56
|
+
qawolf runner inspect element-html --selector "#email"
|
|
57
|
+
qawolf runner inspect variable --name cart | jq .total
|
|
178
58
|
|
|
179
|
-
|
|
59
|
+
qawolf runner run flows/smoke.flow.ts --lines 12-40 --follow # just those lines
|
|
60
|
+
qawolf runner events recorder --tail 5 | jq -r '[.locator] + (.alternates // []) | @tsv'
|
|
61
|
+
qawolf runner terminate
|
|
62
|
+
```
|
|
180
63
|
|
|
181
|
-
|
|
64
|
+
On a mobile runner, launch with `--name android` or `--name ios`, and drive it with `tap`, `swipe`, `fill` and `type` instead of the browser actions.
|
|
182
65
|
|
|
183
|
-
```
|
|
184
|
-
qawolf runner
|
|
66
|
+
```text
|
|
67
|
+
Usage: qawolf runner [options] [command]
|
|
68
|
+
|
|
69
|
+
Drive an interactive runner on the QA Wolf platform
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
-h, --help display help for command
|
|
73
|
+
|
|
74
|
+
Commands:
|
|
75
|
+
launch [options] Launch an interactive runner and make it this directory's
|
|
76
|
+
default
|
|
77
|
+
list [options] List the runners running on your team
|
|
78
|
+
terminate [options] End an interactive runner, and the pod it runs on with it
|
|
79
|
+
stop-run [options] Stop what a runner is currently executing, leaving the
|
|
80
|
+
runner up
|
|
81
|
+
keepalive [options] Reset a runner's inactivity clock, for a caller that
|
|
82
|
+
pauses between actions
|
|
83
|
+
run [options] <flowFile> Run a flow on an interactive runner, shipping the flow
|
|
84
|
+
and what it imports
|
|
85
|
+
events [options] <stream> Print a runner's journal, one entry per line. QA Wolf
|
|
86
|
+
writes console, recorder, run-events, run-logs,
|
|
87
|
+
run-status
|
|
88
|
+
screenshot [options] Save a JPEG of an interactive runner's screen to a file,
|
|
89
|
+
or write it to stdout with --out -
|
|
90
|
+
act [options] <action> Perform one raw action on a runner's screen. A browser
|
|
91
|
+
runner takes click, double_click, scroll, move, drag,
|
|
92
|
+
keypress, navigate and type; a mobile runner takes tap,
|
|
93
|
+
swipe, fill and type. A browser runner answers mobile
|
|
94
|
+
actions with action-not-supported-on-browser; a mobile
|
|
95
|
+
runner answers the other browser actions with
|
|
96
|
+
action-not-supported-on-mobile. Use - to read a whole
|
|
97
|
+
action as JSON from stdin
|
|
98
|
+
actions [options] <sequence> Perform a sequence of up to ten raw actions on a runner's
|
|
99
|
+
screen in one request, as a JSON array of the same
|
|
100
|
+
actions `runner act` takes. Use - to read the array from
|
|
101
|
+
stdin. Actions run back to back, so batch only steps
|
|
102
|
+
whose targets are on the screen you last saw, and end the
|
|
103
|
+
sequence at the step that changes the page. Each result
|
|
104
|
+
carries an effect: performed, not-performed, or unknown
|
|
105
|
+
when the runner stopped answering and the action may have
|
|
106
|
+
landed
|
|
107
|
+
exec [options] <file> Evaluate a snippet against a runner's live page. Use - to
|
|
108
|
+
read the snippet from stdin
|
|
109
|
+
inspect Read one thing off a runner's live page (browser) or
|
|
110
|
+
Appium session (mobile)
|
|
111
|
+
import-package [options] <name> Install a package into a runner's live run, so a snippet
|
|
112
|
+
or a selection can import it
|
|
113
|
+
highlight-selector [options] [selector] Highlight what a selector matches on a runner's live
|
|
114
|
+
page, so the next screenshot shows it. Omit the selector
|
|
115
|
+
to clear the highlight
|
|
116
|
+
promote-snapshot [options] Accept a run's screenshot as the new baseline for an
|
|
117
|
+
image diff, on the runner that produced it
|
|
118
|
+
record Control video recording on a Playwright runner
|
|
119
|
+
list-recordings [options] Read a page of published video recordings, including
|
|
120
|
+
after the runner terminates. Platform URLs persist; video
|
|
121
|
+
URLs expire
|
|
122
|
+
help [command] display help for command
|
|
185
123
|
```
|
|
186
124
|
|
|
187
|
-
|
|
125
|
+
## qawolf runner launch
|
|
188
126
|
|
|
189
|
-
|
|
127
|
+
```text
|
|
128
|
+
Usage: qawolf runner launch [options]
|
|
129
|
+
|
|
130
|
+
Launch an interactive runner and make it this directory's default
|
|
131
|
+
|
|
132
|
+
Options:
|
|
133
|
+
--id <id> Id to launch under. Relaunching an id attaches to that runner
|
|
134
|
+
--name <family> Runner family to run, e.g. playwright
|
|
135
|
+
-h, --help display help for command
|
|
136
|
+
|
|
137
|
+
Examples:
|
|
138
|
+
$ qawolf runner launch
|
|
139
|
+
$ qawolf runner launch --name android
|
|
140
|
+
$ qawolf runner launch --id ci
|
|
141
|
+
|
|
142
|
+
Ids:
|
|
143
|
+
Runner ids are yours to choose and are scoped to your team, so agent-1 is a
|
|
144
|
+
fine id. Launching an id that is already running attaches to that runner
|
|
145
|
+
instead of starting and billing a second one, and the answer says which
|
|
146
|
+
happened: read `alreadyRunning`. Reusing one id is therefore the cheap and
|
|
147
|
+
safe pattern. The same id with a different --name is refused rather than
|
|
148
|
+
silently ignored.
|
|
149
|
+
|
|
150
|
+
launch takes its id from --id and never reads QAWOLF_RUNNER_ID. A bare `qawolf
|
|
151
|
+
runner launch` invents a random id, bills a pod under it and stores it, so a
|
|
152
|
+
harness that exported the variable and then launched without --id ends up with
|
|
153
|
+
a pod it is not addressing. Pass --id whenever you have an id in mind.
|
|
154
|
+
|
|
155
|
+
Launching an id that differs from QAWOLF_RUNNER_ID prints a warning on stderr
|
|
156
|
+
naming both ids: the variable still outranks the directory default, so
|
|
157
|
+
commands that omit --runner keep going to whatever it names, not the runner
|
|
158
|
+
you just launched. That is expected when you launch an additional runner on
|
|
159
|
+
purpose. Address that one with --runner rather than re-exporting the variable,
|
|
160
|
+
which would repoint every other runner-less command too.
|
|
161
|
+
|
|
162
|
+
The page:
|
|
163
|
+
Either answer carries a `url`, which launch prints, as does a command that
|
|
164
|
+
launched its own runner. It is a QA Wolf page showing what the runner is
|
|
165
|
+
doing, where a person can also take over with their own mouse and keyboard.
|
|
166
|
+
Hand it to a person who asks what your runner is up to. The page opens for
|
|
167
|
+
anyone on the runner's team, however the runner was launched. There is nothing
|
|
168
|
+
to see until the runner's first run starts its screen, so the page waits until
|
|
169
|
+
then. You read the screen with `screenshot`, not with the page.
|
|
170
|
+
|
|
171
|
+
Browser or mobile:
|
|
172
|
+
--name also chooses between a browser and a mobile device: --name android or
|
|
173
|
+
--name ios starts an Appium session instead of a browser. A command built for
|
|
174
|
+
the other kind answers a failureReason naming the mismatch rather than doing
|
|
175
|
+
something approximate: runner-is-not-mobile from inspect session, contexts,
|
|
176
|
+
page-source and elements on a browser runner, and runner-is-not-a-browser from
|
|
177
|
+
inspect element-html and page-html on a mobile one. Launch the right family up
|
|
178
|
+
front rather than discovering it from a refusal mid-session.
|
|
179
|
+
```
|
|
190
180
|
|
|
191
|
-
|
|
181
|
+
## qawolf runner list
|
|
192
182
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
- `unknown` means the runner stopped answering with the action in flight, or its screen went quiet mid-action, so it may have taken effect. Take a screenshot before repeating anything from that step on, and never send it again blind.
|
|
183
|
+
```text
|
|
184
|
+
Usage: qawolf runner list [options]
|
|
196
185
|
|
|
197
|
-
|
|
186
|
+
List the runners running on your team
|
|
198
187
|
|
|
199
|
-
|
|
188
|
+
Options:
|
|
189
|
+
--here Only the runners this directory launched
|
|
190
|
+
-h, --help display help for command
|
|
200
191
|
|
|
201
|
-
|
|
202
|
-
|
|
192
|
+
Examples:
|
|
193
|
+
$ qawolf runner list
|
|
194
|
+
$ qawolf runner list --here
|
|
195
|
+
$ qawolf runner list --json
|
|
203
196
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
```
|
|
197
|
+
What the list holds:
|
|
198
|
+
The list names the runners this directory has launched that are still running,
|
|
199
|
+
and marks the one a command with no --runner would reach. It also includes the
|
|
200
|
+
runner named by QAWOLF_RUNNER_ID even though this directory did not launch it,
|
|
201
|
+
so a harness handed a runner sees it alongside the ones it started itself. Use
|
|
202
|
+
the id column with --runner to address any of them; addressing one does not
|
|
203
|
+
make it the default.
|
|
212
204
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
it. If a stop publishes a recording with `status: "failed"`, the CLI returns
|
|
220
|
-
exit code 1 and still prints the manifest so callers can inspect it.
|
|
205
|
+
Every runner is looked up before it is listed, so a runner that idled out is
|
|
206
|
+
absent rather than reported. The lookup neither starts a runner nor resets an
|
|
207
|
+
inactivity clock, which is what separates list from keepalive: listing tells
|
|
208
|
+
you what is there and changes nothing. A lookup that cannot be answered fails
|
|
209
|
+
the command rather than returning a shorter list, because a short list reads
|
|
210
|
+
as the whole truth.
|
|
221
211
|
|
|
222
|
-
|
|
212
|
+
Nothing is billed by listing, but everything in the list is billing. Terminate
|
|
213
|
+
what you are done with.
|
|
223
214
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
qawolf runner list
|
|
215
|
+
The table leaves out the page address, which beside a 63-character id outgrows
|
|
216
|
+
a terminal. --json carries it as `url` on every runner:
|
|
217
|
+
|
|
218
|
+
$ qawolf runner list --json | jq -r '.[] | [.id, .url] | @tsv'
|
|
228
219
|
```
|
|
229
220
|
|
|
230
|
-
|
|
231
|
-
Follow that token for more; the order is storage-key order, not newest-first.
|
|
232
|
-
Entries include status, run ids, a stable platform `url`, and an expiring
|
|
233
|
-
`videoUrl` when video is available. An empty lookup means the recording has not
|
|
234
|
-
been published or does not exist. Abrupt runner loss may leave video unavailable.
|
|
235
|
-
Keep the runner id for history lookups after termination clears the local default.
|
|
221
|
+
## qawolf runner terminate
|
|
236
222
|
|
|
237
|
-
|
|
223
|
+
```text
|
|
224
|
+
Usage: qawolf runner terminate [options]
|
|
225
|
+
|
|
226
|
+
End an interactive runner, and the pod it runs on with it
|
|
227
|
+
|
|
228
|
+
Options:
|
|
229
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner
|
|
230
|
+
-h, --help display help for command
|
|
231
|
+
|
|
232
|
+
Stopping a run or ending a runner:
|
|
233
|
+
Two different things, and the names are the only warning you get:
|
|
234
|
+
- stop-run stops what the runner is executing and leaves the runner up, its
|
|
235
|
+
browser on whatever page the run reached. The run settles as stopped rather
|
|
236
|
+
than passed or failed. Use it to abandon a run and keep the browser you were
|
|
237
|
+
working against.
|
|
238
|
+
- terminate ends the runner and the pod with it. Everything on it is gone, and
|
|
239
|
+
the next command under that id launches and bills a new one.
|
|
240
|
+
|
|
241
|
+
Both succeed when there was nothing to do, and say which: `wasRunning` is
|
|
242
|
+
false when no run was going, and when no runner was running. Neither is an
|
|
243
|
+
error, so a retry needs no special handling. terminate never launches a runner
|
|
244
|
+
in order to end it.
|
|
245
|
+
```
|
|
238
246
|
|
|
239
|
-
|
|
240
|
-
screenshot. As you drive the browser, the runner records each interaction and
|
|
241
|
-
publishes `locator` (the real Playwright locator it resolved), `alternates` (the
|
|
242
|
-
others that matched the same element) and `code` (the generated Playwright call),
|
|
243
|
-
alongside `type`, `sourceUrl` and `timestamp`.
|
|
247
|
+
## qawolf runner stop-run
|
|
244
248
|
|
|
245
|
-
```
|
|
246
|
-
qawolf runner
|
|
247
|
-
|
|
249
|
+
```text
|
|
250
|
+
Usage: qawolf runner stop-run [options]
|
|
251
|
+
|
|
252
|
+
Stop what a runner is currently executing, leaving the runner up
|
|
253
|
+
|
|
254
|
+
Options:
|
|
255
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner
|
|
256
|
+
-h, --help display help for command
|
|
257
|
+
|
|
258
|
+
Stopping a run or ending a runner:
|
|
259
|
+
Two different things, and the names are the only warning you get:
|
|
260
|
+
- stop-run stops what the runner is executing and leaves the runner up, its
|
|
261
|
+
browser on whatever page the run reached. The run settles as stopped rather
|
|
262
|
+
than passed or failed. Use it to abandon a run and keep the browser you were
|
|
263
|
+
working against.
|
|
264
|
+
- terminate ends the runner and the pod with it. Everything on it is gone, and
|
|
265
|
+
the next command under that id launches and bills a new one.
|
|
266
|
+
|
|
267
|
+
Both succeed when there was nothing to do, and say which: `wasRunning` is
|
|
268
|
+
false when no run was going, and when no runner was running. Neither is an
|
|
269
|
+
error, so a retry needs no special handling. terminate never launches a runner
|
|
270
|
+
in order to end it.
|
|
248
271
|
```
|
|
249
272
|
|
|
250
|
-
|
|
251
|
-
is why the first line falls back to `type`. Use these to turn a session you drove
|
|
252
|
-
by pixel coordinates into durable selectors, and to check that a click landed on
|
|
253
|
-
the element you meant rather than near it. The stream is empty until the session
|
|
254
|
-
has a browser context, so an early empty answer means "not yet", not "broken".
|
|
255
|
-
Do not add `--json` here: it wraps each line in an envelope and these field paths
|
|
256
|
-
stop matching.
|
|
273
|
+
## qawolf runner keepalive
|
|
257
274
|
|
|
258
|
-
|
|
275
|
+
```text
|
|
276
|
+
Usage: qawolf runner keepalive [options]
|
|
277
|
+
|
|
278
|
+
Reset a runner's inactivity clock, for a caller that pauses between actions
|
|
279
|
+
|
|
280
|
+
Options:
|
|
281
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner
|
|
282
|
+
-h, --help display help for command
|
|
283
|
+
|
|
284
|
+
Examples:
|
|
285
|
+
$ qawolf runner keepalive
|
|
286
|
+
$ qawolf runner keepalive --runner ci
|
|
287
|
+
|
|
288
|
+
When to call it:
|
|
289
|
+
A runner is reaped after a period of inactivity, and every command that talks
|
|
290
|
+
to the runner counts as activity, including a journal read. keepalive exists
|
|
291
|
+
for the gap that creates: a harness that thinks, or waits on a human, for
|
|
292
|
+
minutes between actions would otherwise come back to a pod that is gone. It
|
|
293
|
+
resets the clock and tells you the runner is still there. It never launches a
|
|
294
|
+
runner.
|
|
295
|
+
|
|
296
|
+
It is a read with a cost: keeping the clock reset keeps a billed pod alive.
|
|
297
|
+
Call it while you are genuinely still working, not on a timer you forget, and
|
|
298
|
+
call `qawolf runner terminate` when you are done rather than leaving a pod to
|
|
299
|
+
time out. A loop that keeps a runner alive and never stops it bills until
|
|
300
|
+
someone notices.
|
|
301
|
+
```
|
|
259
302
|
|
|
260
|
-
|
|
261
|
-
answer on stdout by itself, so you can redirect or pipe it:
|
|
303
|
+
## qawolf runner run
|
|
262
304
|
|
|
263
|
-
```
|
|
264
|
-
qawolf runner
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
305
|
+
```text
|
|
306
|
+
Usage: qawolf runner run [options] <flowFile>
|
|
307
|
+
|
|
308
|
+
Run a flow on an interactive runner, shipping the flow and what it imports
|
|
309
|
+
|
|
310
|
+
Options:
|
|
311
|
+
--follow Report the run's status until it settles: in progress, then passed or failed
|
|
312
|
+
(default: false)
|
|
313
|
+
--logs Stream every log line the run produces while following. Implies --follow
|
|
314
|
+
(default: false)
|
|
315
|
+
--run-events Stream the run's progress events as JSON lines while following. Implies
|
|
316
|
+
--follow (default: false)
|
|
317
|
+
--recorder-events Stream the browser actions the runner records as JSON lines while following,
|
|
318
|
+
from an anchor taken just before submission: the recorder is runner-wide, not
|
|
319
|
+
run-scoped. Implies --follow (default: false)
|
|
320
|
+
--env-id <env> QA Wolf environment whose variables the run is given, by id or alias.
|
|
321
|
+
Defaults to QAWOLF_ENVIRONMENT. QA Wolf reads and decrypts them itself, so
|
|
322
|
+
they never leave the server and no size limit applies to them
|
|
323
|
+
--env-file <path> Dotenv file on this machine whose variables the run is given. Pass this or
|
|
324
|
+
--env-id, not both
|
|
325
|
+
--lines <start-end> Run only these 1-indexed lines against the browser as it stands, instead of
|
|
326
|
+
the whole flow from a fresh one
|
|
327
|
+
--lines-file <path> File the --lines range lives in. Defaults to <flowFile>; pass it only when
|
|
328
|
+
the lines are in another file, such as a page object
|
|
329
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored
|
|
330
|
+
runner
|
|
331
|
+
--timeout <seconds> Give up following after this long. Following keeps the runner alive, so a run
|
|
332
|
+
that never settles would otherwise bill until the terminal closed (default:
|
|
333
|
+
"3600")
|
|
334
|
+
-h, --help display help for command
|
|
335
|
+
|
|
336
|
+
Examples:
|
|
337
|
+
$ qawolf runner run flows/checkout.flow.ts
|
|
338
|
+
$ qawolf runner run flows/checkout.flow.ts --follow
|
|
339
|
+
$ qawolf runner run flows/checkout.flow.ts --follow --logs
|
|
340
|
+
$ qawolf runner run flows/checkout.flow.ts --lines 12-40
|
|
341
|
+
$ qawolf runner run flows/checkout.flow.ts --lines 4-9 --lines-file pages/login.ts
|
|
342
|
+
$ qawolf runner run flows/checkout.flow.ts --env-id staging
|
|
343
|
+
|
|
344
|
+
What travels:
|
|
345
|
+
run ships the flow file, everything it imports, and your package.json and
|
|
346
|
+
tsconfig.json. Nothing else travels, so you can run from the root of a large
|
|
347
|
+
project without sending it. The runner holds no copy of your project, so what
|
|
348
|
+
runs is exactly what is on disk at that moment, uncommitted edits included.
|
|
349
|
+
|
|
350
|
+
Imports are followed the same way a run from the QA Wolf app follows them:
|
|
351
|
+
relative paths and tsconfig.json path aliases, resolving .ts and .js. An
|
|
352
|
+
`export ... from` re-export is not followed, and neither is require(), so a
|
|
353
|
+
barrel file does not pull in what it re-exports. A package.json has to be
|
|
354
|
+
there, since the run reads its npm dependencies from it, and the files may
|
|
355
|
+
carry at most 30 MiB in total. A missing file, a missing package.json and
|
|
356
|
+
files over the cap are all refused before any runner is resolved or launched,
|
|
357
|
+
so a typo costs nothing.
|
|
358
|
+
|
|
359
|
+
After the first run on a runner, later runs send only the files whose content
|
|
360
|
+
changed, so iterating on one flow costs a small request rather than the whole
|
|
361
|
+
graph again. --json reports which happened in `fileSync`: delta or full.
|
|
362
|
+
Nothing about this needs managing: the baseline lives in
|
|
363
|
+
.qawolf/runner-files.json, a switch to another runner ignores it, and a runner
|
|
364
|
+
that turns out not to hold what was claimed gets the whole set resent
|
|
365
|
+
automatically.
|
|
366
|
+
|
|
367
|
+
Waiting for the outcome:
|
|
368
|
+
The call answers with a run id as soon as the run is accepted. The outcome is
|
|
369
|
+
not in that answer; it is in the run-status stream, whose entries carry
|
|
370
|
+
`runId`, `status` and an `errorMessage` when there is one.
|
|
371
|
+
|
|
372
|
+
Pass --follow and let run wait for you. It reports the run's status, in
|
|
373
|
+
progress, then passed or failed, and ends on the settled status. Exit 1 means
|
|
374
|
+
the run did not pass. --logs, --run-events and --recorder-events mirror more
|
|
375
|
+
streams into the follow, and each implies --follow on its own. The recorder is
|
|
376
|
+
runner-wide rather than run-scoped, so --recorder-events carries whatever is
|
|
377
|
+
recorded after an anchor taken just before submission. Whatever mirrors are
|
|
378
|
+
on, the follow still ends on the status, never on them, so a run that prints
|
|
379
|
+
nothing still ends the follow and a run that dies mid-sentence still reports
|
|
380
|
+
how. Combining mirror flags interleaves their lines with nothing saying which
|
|
381
|
+
stream a line came from: fine for eyeballs; when parsing, follow one stream at
|
|
382
|
+
a time.
|
|
383
|
+
|
|
384
|
+
To submit and come back later, poll with the same rule the CLI uses: `status`
|
|
385
|
+
is in-progress while the run is going, and any other value means it has
|
|
386
|
+
settled. `events --follow` does not end when the run settles, so it cannot be
|
|
387
|
+
used to wait for one.
|
|
388
|
+
|
|
389
|
+
$ qawolf runner run flows/checkout.flow.ts --json
|
|
390
|
+
$ qawolf runner events run-status --run <runId> --tail 1 | jq -r '.status'
|
|
391
|
+
|
|
392
|
+
When the runner could not be reached:
|
|
393
|
+
If run reports that the runner could not be reached, that does not mean the
|
|
394
|
+
run did not start. The runner may have accepted it and been too slow to
|
|
395
|
+
answer, and resubmitting bills and journals a second run.
|
|
396
|
+
|
|
397
|
+
There is no clean recovery. The journal lives on the same pod, so while the
|
|
398
|
+
runner stays unreachable a run-status read fails the same way. Wait for the
|
|
399
|
+
runner to answer again, then read run-status without --run and look at the
|
|
400
|
+
newest `runId`. Nothing ties that id back to your submission, and a runner
|
|
401
|
+
takes work from anyone addressing it, so treat the newest id as your run only
|
|
402
|
+
if you know nothing else submits to this runner. An empty read is not proof
|
|
403
|
+
the run did not start either: run returns the moment the run is accepted, and
|
|
404
|
+
its first run-status entry may not be written yet. A resubmit always risks a
|
|
405
|
+
second billed run, so prefer polling run-status a while longer, and only
|
|
406
|
+
submit again once you are willing to accept that risk.
|
|
407
|
+
|
|
408
|
+
Running part of a flow:
|
|
409
|
+
--lines 12-40 runs those lines against the browser as it stands, so nothing is
|
|
410
|
+
re-navigated and nothing is signed in again. Use it to iterate on a step
|
|
411
|
+
without paying for the whole flow to reach it again.
|
|
412
|
+
|
|
413
|
+
The two file paths are the thing to get right, because getting them backwards
|
|
414
|
+
runs the wrong code and nothing reports it:
|
|
415
|
+
- the positional is always the flow file. It is the run's entry point, and it
|
|
416
|
+
is required for every run, selection or not.
|
|
417
|
+
- --lines-file is where the lines live. It defaults to the positional, so pass
|
|
418
|
+
it only when the range is in another file, typically a page object whose
|
|
419
|
+
method you want to run against the instance your last run left alive.
|
|
420
|
+
|
|
421
|
+
The lines-file has to be one of the files that travel, so it lives under the
|
|
422
|
+
directory you run from. A range whose file is not collected is refused before
|
|
423
|
+
a runner is addressed, naming the path.
|
|
424
|
+
|
|
425
|
+
A --lines selection is the one call that does not need a run first. If the
|
|
426
|
+
runner had no browser, one is started before your lines run, and the command
|
|
427
|
+
says so on stderr. Those lines then ran against a fresh page rather than the
|
|
428
|
+
one an earlier run left, which is worth reading before you act on what you
|
|
429
|
+
see.
|
|
430
|
+
|
|
431
|
+
Environment variables:
|
|
432
|
+
A run takes one of two. --env-id names a QA Wolf environment by id or alias,
|
|
433
|
+
the same reference `qawolf flows` takes as --env. --env-file .env gives the
|
|
434
|
+
run the variables in a dotenv file, in the format `qawolf flows pull` writes.
|
|
435
|
+
Passing both is refused: each gives the run its whole environment, so there is
|
|
436
|
+
no order in which they would combine.
|
|
437
|
+
|
|
438
|
+
A run with neither flag falls back to QAWOLF_ENVIRONMENT, the same variable
|
|
439
|
+
`qawolf flows` reads, so one export covers both. The run says on stderr which
|
|
440
|
+
environment it picked up, because those variables reach your flow's code and a
|
|
441
|
+
run should never be given an environment silently. --env-id wins over it, and
|
|
442
|
+
--env-file suppresses it, so a run reading a dotenv file is not handed a
|
|
443
|
+
second environment on top.
|
|
444
|
+
|
|
445
|
+
Prefer --env-id. QA Wolf reads and decrypts the environment itself, so the
|
|
446
|
+
values never leave the server, nothing has to be pulled to disk first, and no
|
|
447
|
+
size limit applies to them. It is the only way to run a flow whose environment
|
|
448
|
+
holds something large, such as a session cookie.
|
|
449
|
+
|
|
450
|
+
A run that sends its own variables with --env-file may carry at most 200 of
|
|
451
|
+
them, each value at most 16 KiB. Names follow what a shell accepts, and
|
|
452
|
+
QAWOLF_TEAM_ID is reserved because QA Wolf sets it from the key you
|
|
453
|
+
authenticated with. All of that is refused before a runner is addressed,
|
|
454
|
+
naming the variable at fault.
|
|
268
455
|
```
|
|
269
456
|
|
|
270
|
-
|
|
271
|
-
exact markup. `variable` reads a top-level variable of the running workflow and
|
|
272
|
-
prints it as JSON, which is how you see what your own code computed rather than
|
|
273
|
-
what the page shows.
|
|
457
|
+
## qawolf runner events
|
|
274
458
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
459
|
+
```text
|
|
460
|
+
Usage: qawolf runner events [options] <stream>
|
|
461
|
+
|
|
462
|
+
Print a runner's journal, one entry per line. QA Wolf writes console, recorder, run-events,
|
|
463
|
+
run-logs, run-status
|
|
464
|
+
|
|
465
|
+
Options:
|
|
466
|
+
--follow Keep reading as new entries arrive. Reading counts as activity, so a follow
|
|
467
|
+
left open keeps the runner alive and billing (default: false)
|
|
468
|
+
--run <id> Restrict run-scoped streams to one run
|
|
469
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored
|
|
470
|
+
runner
|
|
471
|
+
--since <sequence> Read entries after this sequence
|
|
472
|
+
--tail <count> Read only the newest <count> entries
|
|
473
|
+
--timeout <seconds> Give up following after this long. Reading keeps the runner alive, so a
|
|
474
|
+
follow left open would otherwise bill until the terminal closed (default:
|
|
475
|
+
"3600")
|
|
476
|
+
-h, --help display help for command
|
|
477
|
+
|
|
478
|
+
Examples:
|
|
479
|
+
$ qawolf runner events recorder --tail 5
|
|
480
|
+
$ qawolf runner events run-logs --run <runId> --follow
|
|
481
|
+
$ qawolf runner events console --since 120 --json
|
|
482
|
+
|
|
483
|
+
Streams:
|
|
484
|
+
Everything observable is an append-only stream on the pod, read by cursor or
|
|
485
|
+
tail rather than subscribed to, so attaching late still gets you the history
|
|
486
|
+
that is still there. QA Wolf writes recorder, console, run-events, run-logs
|
|
487
|
+
and run-status; a stream nobody has written reads as empty rather than as an
|
|
488
|
+
error, and a stream this CLI version does not know about is still readable by
|
|
489
|
+
name. One payload per line, so shell tools compose:
|
|
490
|
+
|
|
491
|
+
$ qawolf runner events console --tail 20 | jq -r '.message'
|
|
492
|
+
$ qawolf runner events run-logs --run <runId> --follow > run.log
|
|
493
|
+
|
|
494
|
+
--tail N takes the newest N, --since <sequence> reads everything after a
|
|
495
|
+
cursor, and --run <id> narrows the run-scoped streams.
|
|
496
|
+
|
|
497
|
+
History is not unbounded: a size cap drops the oldest entries on a long-lived
|
|
498
|
+
runner, and a --tail N read can stop early and hand back fewer than N even
|
|
499
|
+
when more matched. Both are warned about on stderr, dropped entries only once
|
|
500
|
+
a read holds a cursor, a stopped-early read with a pointer at --since. Watch
|
|
501
|
+
stderr, treat a short answer as "at least this" rather than "all there was",
|
|
502
|
+
and read what you care about as you go rather than at the end.
|
|
503
|
+
|
|
504
|
+
The recorder:
|
|
505
|
+
`events recorder` is what has no equivalent in a screenshot. As you drive the
|
|
506
|
+
browser, the runner records each interaction and publishes `locator` (the real
|
|
507
|
+
Playwright locator it resolved), `alternates` (the others that matched the
|
|
508
|
+
same element) and `code` (the generated Playwright call), alongside `type`,
|
|
509
|
+
`sourceUrl` and `timestamp`:
|
|
510
|
+
|
|
511
|
+
$ qawolf runner events recorder --tail 5 | jq -r '.code // .type'
|
|
512
|
+
$ qawolf runner events recorder --tail 5 | jq -r '[.locator] + (.alternates // []) | @tsv'
|
|
513
|
+
|
|
514
|
+
`code` is absent on events with no call of their own, such as a navigation,
|
|
515
|
+
which is why the first line falls back to `type`. Use these to turn a session
|
|
516
|
+
you drove by pixel coordinates into durable selectors, and to check that a
|
|
517
|
+
click landed on the element you meant rather than near it. The stream is empty
|
|
518
|
+
until the runner's first run gives it a browser context, so an early empty
|
|
519
|
+
answer means "not yet", not "broken". Do not add --json here: it wraps each
|
|
520
|
+
line in an envelope and these field paths stop matching.
|
|
521
|
+
|
|
522
|
+
Following:
|
|
523
|
+
--follow polls and prints as entries arrive. It is tail -f with a bound: it
|
|
524
|
+
ends only at its --timeout (an hour by default, exit 6), because reading keeps
|
|
525
|
+
the runner alive and billing. It does not end when a run settles, so it cannot
|
|
526
|
+
be used to wait for a run: use `runner run --follow`, or poll run-status.
|
|
527
|
+
Redirect it to a file and stop it yourself, or use repeated --since reads when
|
|
528
|
+
you need the command to end sooner.
|
|
529
|
+
|
|
530
|
+
Where --follow wins is the cursor. The pod reports how far a read scanned
|
|
531
|
+
rather than how far it matched, and --follow carries that number, so a
|
|
532
|
+
filtered read that matched nothing still moves forward. A caller paging by
|
|
533
|
+
hand cannot see it, because the CLI does not print it, and the best available
|
|
534
|
+
substitute is the highest `sequence` you actually saw. So a narrow --run
|
|
535
|
+
filter over a busy stream stalls: with nothing matching, there is no new
|
|
536
|
+
`sequence` to move on to, and you re-read the same window until something
|
|
537
|
+
matches.
|
|
538
|
+
|
|
539
|
+
events never launches a runner.
|
|
540
|
+
```
|
|
280
541
|
|
|
281
|
-
|
|
282
|
-
means printing it and then fishing it back out of the `console` stream, which is
|
|
283
|
-
two calls and a marker; `inspect variable` is one call and the value.
|
|
542
|
+
## qawolf runner screenshot
|
|
284
543
|
|
|
285
|
-
|
|
544
|
+
```text
|
|
545
|
+
Usage: qawolf runner screenshot [options]
|
|
546
|
+
|
|
547
|
+
Save a JPEG of an interactive runner's screen to a file, or write it to stdout with --out -
|
|
548
|
+
|
|
549
|
+
Options:
|
|
550
|
+
--out <path> File to write the image to. - writes the JPEG bytes to stdout on their own and
|
|
551
|
+
moves the confirmation, JSON included, to stderr (default: "screenshot.jpg")
|
|
552
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner
|
|
553
|
+
-h, --help display help for command
|
|
554
|
+
|
|
555
|
+
Examples:
|
|
556
|
+
$ qawolf runner screenshot
|
|
557
|
+
$ qawolf runner screenshot --out screens/step-3.jpg
|
|
558
|
+
$ qawolf runner screenshot --out - > step-3.jpg
|
|
559
|
+
$ qawolf runner screenshot --out - | my-vision-tool
|
|
560
|
+
|
|
561
|
+
Reading the screen:
|
|
562
|
+
The image is a real JPEG on disk, decoded, because every coding harness can
|
|
563
|
+
open an image file. Read it with whatever vision you have. --out - writes the
|
|
564
|
+
JPEG bytes to stdout instead, on their own, for a caller that is a process
|
|
565
|
+
rather than an agent: the confirmation, and the JSON line under --json, goes
|
|
566
|
+
to stderr so nothing follows the image on stdout. A terminal on stdout is
|
|
567
|
+
refused: redirect or pipe it.
|
|
568
|
+
|
|
569
|
+
A runner has no screen until its first run, so until then screenshot exits 2.
|
|
570
|
+
It never launches a runner. When you are about to act anyway, `act
|
|
571
|
+
--screenshot` performs the action and returns the screen in one call.
|
|
572
|
+
```
|
|
286
573
|
|
|
287
|
-
|
|
288
|
-
runner answers four different ones instead, one subcommand per question:
|
|
574
|
+
## qawolf runner act
|
|
289
575
|
|
|
290
|
-
```
|
|
291
|
-
qawolf runner
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
576
|
+
```text
|
|
577
|
+
Usage: qawolf runner act [options] <action>
|
|
578
|
+
|
|
579
|
+
Perform one raw action on a runner's screen. A browser runner takes click, double_click, scroll,
|
|
580
|
+
move, drag, keypress, navigate and type; a mobile runner takes tap, swipe, fill and type. A browser
|
|
581
|
+
runner answers mobile actions with action-not-supported-on-browser; a mobile runner answers the
|
|
582
|
+
other browser actions with action-not-supported-on-mobile. Use - to read a whole action as JSON from
|
|
583
|
+
stdin
|
|
584
|
+
|
|
585
|
+
Options:
|
|
586
|
+
--button <button> click: left, right, wheel, back or forward
|
|
587
|
+
--duration-ms <ms> swipe: how long it takes, up to 10000. Slow scrolls, fast flings
|
|
588
|
+
--from <x,y> swipe: the point it starts at
|
|
589
|
+
--keys <keys...> keypress: modifiers and the key, e.g. Control a
|
|
590
|
+
--path <json> drag: JSON array of points to drag through
|
|
591
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's
|
|
592
|
+
stored runner
|
|
593
|
+
--screenshot <path> Also save a JPEG of the screen, taken after the action, to this file, in
|
|
594
|
+
place of a separate screenshot. An action that did not take effect answers
|
|
595
|
+
with one too. - writes it to stdout and moves the confirmation, JSON
|
|
596
|
+
included, to stderr
|
|
597
|
+
--scroll-x <delta> scroll: horizontal wheel delta
|
|
598
|
+
--scroll-y <delta> scroll: vertical wheel delta
|
|
599
|
+
--selector <selector> tap or fill: the element to act on, as a screen object would find it. tap
|
|
600
|
+
takes it in place of --x and --y
|
|
601
|
+
--strategy <strategy> how --selector is resolved: xpath (default), ios-predicate or shadow
|
|
602
|
+
--text <text> type: the text to type into what has focus. fill: the field's new value
|
|
603
|
+
--to <x,y> swipe: the point it ends at
|
|
604
|
+
--url <url> navigate: the http or https URL to go to
|
|
605
|
+
--x <pixels> click, tap and the like: x, in screenshot pixels
|
|
606
|
+
--y <pixels> click, tap and the like: y, in screenshot pixels
|
|
607
|
+
-h, --help display help for command
|
|
608
|
+
|
|
609
|
+
Examples:
|
|
610
|
+
$ qawolf runner act click --button left --x 480 --y 260
|
|
611
|
+
$ qawolf runner act type --text "hello@example.com"
|
|
612
|
+
$ qawolf runner act keypress --keys Control a
|
|
613
|
+
$ qawolf runner act navigate --url https://example.com
|
|
614
|
+
$ qawolf runner act drag --path '[{"x":10,"y":20},{"x":80,"y":90}]'
|
|
615
|
+
$ qawolf runner act click --button left --x 480 --y 260 --screenshot step-4.jpg
|
|
616
|
+
$ echo '{"type":"click","button":"left","x":1,"y":2}' | qawolf runner act -
|
|
617
|
+
$ echo '{"type":"click","button":"left","x":1,"y":2}' | qawolf runner act - --screenshot - > step-5.jpg
|
|
618
|
+
|
|
619
|
+
Mobile:
|
|
620
|
+
$ qawolf runner act tap --x 540 --y 1200
|
|
621
|
+
$ qawolf runner act tap --selector '//*[@content-desc="Continue"]'
|
|
622
|
+
$ qawolf runner act fill --selector 'name == "Postal code"' --strategy ios-predicate --text 94107
|
|
623
|
+
$ qawolf runner act swipe --from 540,1600 --to 540,600 --duration-ms 1500
|
|
624
|
+
|
|
625
|
+
Seeing and acting:
|
|
626
|
+
There is no hosted vision loop: you look with screenshot, decide with your own
|
|
627
|
+
model, and act. act performs exactly one action per call, in the computer-use
|
|
628
|
+
tool vocabulary a vision model already emits. The names and the field names
|
|
629
|
+
are unchanged from that vocabulary on purpose, so you can forward a tool call
|
|
630
|
+
rather than translate it with `act -`.
|
|
631
|
+
|
|
632
|
+
Every action in a see-and-act loop is followed by a look at the result, so ask
|
|
633
|
+
for it in the same call with --screenshot. Prefer it over act and then
|
|
634
|
+
screenshot: each step is one call instead of two, with no delay to guess at
|
|
635
|
+
between them. The screen the runner answers with is taken once it has changed
|
|
636
|
+
from just before the action or half a second has passed, whichever comes
|
|
637
|
+
first. An action that reached the screen and did not take effect answers with
|
|
638
|
+
one too, so an exit 1 still leaves you a picture of why the click missed. A 4
|
|
639
|
+
whose message starts with "Performed" means the action happened and only the
|
|
640
|
+
picture is missing: take a screenshot, never send the action again to get it.
|
|
641
|
+
As with `screenshot --out -`, a terminal on stdout is refused.
|
|
642
|
+
|
|
643
|
+
Coordinates are pixels on the same screenshot you just read. The runner serves
|
|
644
|
+
one see-or-act request at a time, so never have two in flight; when the next
|
|
645
|
+
few steps are already known, send them as one `runner actions` sequence
|
|
646
|
+
instead. Bounds are checked before anything is sent, so an over-long --text or
|
|
647
|
+
an out-of-range coordinate comes back immediately naming the limit.
|
|
648
|
+
|
|
649
|
+
On a 4, the answer may have been lost with the action in flight: take a
|
|
650
|
+
screenshot before repeating a click, and repeat only what the screen says did
|
|
651
|
+
not happen.
|
|
652
|
+
|
|
653
|
+
Mobile:
|
|
654
|
+
A mobile runner has a touchscreen, not a mouse, so it has actions of its own:
|
|
655
|
+
- tap touches a point (--x, --y) or the element --selector names. Check the
|
|
656
|
+
selector first with `inspect elements --selector`, and pass --strategy
|
|
657
|
+
ios-predicate or shadow when it is not XPath.
|
|
658
|
+
- swipe --from x,y --to x,y moves in a straight line between two points.
|
|
659
|
+
--duration-ms sets how long it takes, up to 10000: a slow swipe scrolls, a
|
|
660
|
+
fast one flings.
|
|
661
|
+
- fill --selector ... --text ... replaces the value of that field, and --text
|
|
662
|
+
"" clears it. To add to what a field already holds, tap it and then type.
|
|
663
|
+
- type types into whatever the last tap focused, the same as on a browser.
|
|
664
|
+
|
|
665
|
+
On mobile, send tap and swipe, never click or drag. The other browser actions,
|
|
666
|
+
double_click, scroll, move, keypress and navigate, answer
|
|
667
|
+
action-not-supported-on-mobile rather than doing something approximate.
|
|
668
|
+
navigate is the one to watch for, since it works on a browser runner without a
|
|
669
|
+
run first but has no meaning on mobile at all. A browser runner answers tap,
|
|
670
|
+
swipe and fill with action-not-supported-on-browser.
|
|
671
|
+
|
|
672
|
+
Before the first run:
|
|
673
|
+
A runner has no screen until its first run, so act exits 2 until then, except
|
|
674
|
+
navigate, which exits 1 (action-failed): it skips the screen but still needs
|
|
675
|
+
the runner to have run something. With no runner named at all, act launches
|
|
676
|
+
one and says so on stderr.
|
|
299
677
|
```
|
|
300
678
|
|
|
301
|
-
|
|
302
|
-
the whole answer. `contexts`, `page-source` and `elements` instead print their
|
|
303
|
-
answer as JSON on stdout, on its own like `inspect element-html`, so you can
|
|
304
|
-
redirect or pipe it (`| jq .current`, `| jq .matches`, `| jq .pageSource`)
|
|
305
|
-
rather than getting a count with no way to see what was actually found.
|
|
306
|
-
|
|
307
|
-
`session` is also the one subcommand that never answers `screen-needs-a-run`:
|
|
308
|
-
readiness is the question it exists to answer, so it reports one of `ready`
|
|
309
|
-
(with the platform, device and session id), `unreachable`, `ambiguous` (more
|
|
310
|
-
than one session is somehow live) or `no-session`, rather than refusing until a
|
|
311
|
-
run has happened. The other three need a live session first and share the same
|
|
312
|
-
readiness contract `act` and `screenshot` use on a browser runner:
|
|
313
|
-
`screen-needs-a-run` exits `2` and means no Appium session has started on this
|
|
314
|
-
runner yet — run a flow that opens one, then inspect again; `screen-not-ready`
|
|
315
|
-
exits `4` and means the session exists but did not answer this instant, or more
|
|
316
|
-
than one is somehow live — retry once, and relaunch the runner if it persists.
|
|
317
|
-
|
|
318
|
-
`elements` takes one of three ways to search: whole-pixel `--x`/`--y` on the
|
|
319
|
-
device's own screen, the same coordinates a screenshot is measured in;
|
|
320
|
-
`--text`, which matches exactly unless `--partial` is passed; or `--selector`,
|
|
321
|
-
resolved the same way a screen object's own selector is, with `--strategy`
|
|
322
|
-
naming how (`xpath`, `ios-predicate` or `shadow`; defaults to `xpath`). Prefer
|
|
323
|
-
`--selector` when checking a selector you are about to write: it answers what
|
|
324
|
-
that exact string resolves to, where `--text`/`--x`/`--y` only approximate it.
|
|
325
|
-
The three do not mix — passing flags from more than one at once is refused
|
|
326
|
-
before a runner is addressed rather than silently searching by whichever one
|
|
327
|
-
it picked. An unparseable selector answers `invalid-selector`, exit `2`,
|
|
328
|
-
distinctly from a selector that parsed fine but matched nothing, which answers
|
|
329
|
-
an empty `matches` list, exit `0` — the same distinction `highlight-selector`
|
|
330
|
-
draws on a browser runner. `--context` on `page-source` or `elements` reads a
|
|
331
|
-
context other than the current one — useful once `contexts` has told you which
|
|
332
|
-
are available.
|
|
333
|
-
|
|
334
|
-
A browser runner answers `runner-is-not-mobile` to all four, exit `2`, since
|
|
335
|
-
retrying never helps: launch with `--name android` or `--name ios` instead.
|
|
336
|
-
`runner-unreachable` exits `4` and is worth retrying, same as everywhere else
|
|
337
|
-
on this surface.
|
|
338
|
-
|
|
339
|
-
## Checking a selector: `highlight-selector`
|
|
340
|
-
|
|
341
|
-
`inspect element-html` tells you what a selector matched. `highlight-selector`
|
|
342
|
-
shows you _where_ it is, by drawing on the page itself:
|
|
679
|
+
## qawolf runner actions
|
|
343
680
|
|
|
344
|
-
```
|
|
345
|
-
qawolf runner
|
|
346
|
-
|
|
347
|
-
|
|
681
|
+
```text
|
|
682
|
+
Usage: qawolf runner actions [options] <sequence>
|
|
683
|
+
|
|
684
|
+
Perform a sequence of up to ten raw actions on a runner's screen in one request, as a JSON array of
|
|
685
|
+
the same actions `runner act` takes. Use - to read the array from stdin. Actions run back to back,
|
|
686
|
+
so batch only steps whose targets are on the screen you last saw, and end the sequence at the step
|
|
687
|
+
that changes the page. Each result carries an effect: performed, not-performed, or unknown when the
|
|
688
|
+
runner stopped answering and the action may have landed
|
|
689
|
+
|
|
690
|
+
Options:
|
|
691
|
+
--continue-on-failure Carry on past an action that reached the runner and did not take effect;
|
|
692
|
+
a runner that cannot be reached or a screen that cannot serve still ends
|
|
693
|
+
the sequence
|
|
694
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's
|
|
695
|
+
stored runner
|
|
696
|
+
--screenshot <path> Save a JPEG of the screen after the last action to this file. With
|
|
697
|
+
--screenshot-mode each, one file per action, with the action's index
|
|
698
|
+
before the extension. - writes the final frame to stdout and moves the
|
|
699
|
+
confirmation to stderr
|
|
700
|
+
--screenshot-mode <mode> Defaults to final when --screenshot is given, none otherwise (choices:
|
|
701
|
+
"none", "final", "each")
|
|
702
|
+
-h, --help display help for command
|
|
703
|
+
|
|
704
|
+
Examples:
|
|
705
|
+
$ qawolf runner actions '[{"type":"click","button":"left","x":480,"y":260},{"type":"type","text":"hello@example.com"},{"type":"keypress","keys":["Enter"]}]' --screenshot after-login.jpg
|
|
706
|
+
$ echo '[{"type":"click","button":"left","x":1,"y":2},{"type":"type","text":"hi"}]' | qawolf runner actions -
|
|
707
|
+
$ qawolf runner actions '[{"type":"click","button":"left","x":480,"y":260},{"type":"type","text":"hello"}]' --screenshot-mode each --screenshot step.jpg
|
|
708
|
+
$ qawolf runner actions '[{"type":"scroll","x":480,"y":260,"scroll_x":0,"scroll_y":600},{"type":"click","button":"left","x":120,"y":700}]' --continue-on-failure
|
|
709
|
+
|
|
710
|
+
Mobile:
|
|
711
|
+
$ qawolf runner actions '[{"type":"tap","selector":"//*[@content-desc=\"Email\"]"},{"type":"type","text":"hello@example.com"},{"type":"tap","x":540,"y":1650}]' --screenshot after-login.jpg
|
|
712
|
+
$ qawolf runner actions '[{"type":"fill","selector":"name == \"Postal code\"","strategy":"ios-predicate","text":"94107"},{"type":"swipe","from":{"x":540,"y":1600},"to":{"x":540,"y":600}}]'
|
|
713
|
+
|
|
714
|
+
Batching:
|
|
715
|
+
Use a sequence for the steps you already know: click the field, type into it,
|
|
716
|
+
press Enter. One round trip instead of three, with no delay to guess at
|
|
717
|
+
between them. On a mobile runner the steps are tap, swipe, fill and type, the
|
|
718
|
+
same as act, and a step the runner does not take fails with the same reason
|
|
719
|
+
act would give, action-not-supported-on-mobile or
|
|
720
|
+
action-not-supported-on-browser.
|
|
721
|
+
|
|
722
|
+
Batch only steps whose targets are all on the screen you last saw and are not
|
|
723
|
+
moved by the steps before them, and make the step that changes the page, a
|
|
724
|
+
submit, a navigation, opening a menu, the last one. Then read the frame and
|
|
725
|
+
decide the next batch from it.
|
|
726
|
+
|
|
727
|
+
Screenshots:
|
|
728
|
+
--screenshot after.jpg writes the screen after the last action, as `act
|
|
729
|
+
--screenshot` does, and - puts those bytes on stdout with the confirmation on
|
|
730
|
+
stderr. Stdout then carries the image and nothing else, so - trades the
|
|
731
|
+
per-action results for the frame: give --screenshot a file path whenever you
|
|
732
|
+
need to read `effect` per action. --screenshot-mode each --screenshot step.jpg
|
|
733
|
+
writes one frame per action instead, step-0.jpg, step-1.jpg and so on; each is
|
|
734
|
+
a full image, so keep those sequences short.
|
|
735
|
+
|
|
736
|
+
Reading the answer:
|
|
737
|
+
The answer holds one entry per action reached, and the field to read on each
|
|
738
|
+
is `effect`:
|
|
739
|
+
- performed means the runner did it.
|
|
740
|
+
- not-performed means the runner answered that it did not take effect.
|
|
741
|
+
- unknown means the runner stopped answering with the action in flight, or its
|
|
742
|
+
screen went quiet mid-action, so it may have taken effect. Take a screenshot
|
|
743
|
+
before repeating anything from that step on, and never send it again blind.
|
|
744
|
+
|
|
745
|
+
The sequence stops at the first action that fails. --continue-on-failure
|
|
746
|
+
carries on past one that reached the runner and did not take effect, which is
|
|
747
|
+
only safe for actions that do not depend on each other: a type after a failed
|
|
748
|
+
click goes to whatever has focus. A runner that cannot be reached, a screen
|
|
749
|
+
that cannot serve, or running out of time ends the sequence either way, and
|
|
750
|
+
the message names every action that did not succeed.
|
|
751
|
+
|
|
752
|
+
Exit codes:
|
|
753
|
+
A sequence exits by the worst thing that happened to any one of its actions:
|
|
754
|
+
an effect nobody can confirm (4) first, then running out of time (6), then a
|
|
755
|
+
refusal (2), then an action that plainly did not happen (1). Whatever the
|
|
756
|
+
code, the message names the last action that took effect, and only what
|
|
757
|
+
follows it goes in a new request. A sequence also exits 2 before it sends
|
|
758
|
+
anything for an argument that is not a JSON array, an array of more than ten
|
|
759
|
+
actions, and --screenshot flags that contradict each other.
|
|
348
760
|
```
|
|
349
761
|
|
|
350
|
-
|
|
351
|
-
cannot see the runner's screen, so the only way to read the result is the next
|
|
352
|
-
screenshot.
|
|
762
|
+
## qawolf runner exec
|
|
353
763
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
764
|
+
```text
|
|
765
|
+
Usage: qawolf runner exec [options] <file>
|
|
766
|
+
|
|
767
|
+
Evaluate a snippet against a runner's live page. Use - to read the snippet from stdin
|
|
768
|
+
|
|
769
|
+
Options:
|
|
770
|
+
--file <path> File whose scope the snippet is evaluated in; it and the directory's other files
|
|
771
|
+
travel with it
|
|
772
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner
|
|
773
|
+
-h, --help display help for command
|
|
774
|
+
|
|
775
|
+
Examples:
|
|
776
|
+
$ qawolf runner exec snippet.ts
|
|
777
|
+
$ echo 'console.log(await page.title())' | qawolf runner exec -
|
|
778
|
+
$ qawolf runner exec snippet.ts --file flows/checkout.flow.ts
|
|
779
|
+
|
|
780
|
+
Getting a value back:
|
|
781
|
+
exec does not return what the snippet evaluated to, only whether it ran. To
|
|
782
|
+
get a value back, print it and read the `console` stream with `qawolf runner
|
|
783
|
+
events console`. Print it behind a marker you chose, and match on that rather
|
|
784
|
+
than taking the newest line: the page logs to the same stream, so anything it
|
|
785
|
+
prints after your snippet would be what --tail 1 hands back. Entries carry
|
|
786
|
+
`source`, which is `serverConsole` for your snippet and `browserConsole` for
|
|
787
|
+
the page, so filtering on both is what pins the value down:
|
|
788
|
+
|
|
789
|
+
$ echo 'console.log("qw-title:", await page.title())' | qawolf runner exec -
|
|
790
|
+
$ qawolf runner events console --tail 20 | jq -r 'select(.source == "serverConsole" and (.message | contains("qw-title:"))) | .message'
|
|
791
|
+
|
|
792
|
+
To read a top-level variable of the running workflow, `qawolf runner inspect
|
|
793
|
+
variable` is one call and the value, where exec is two calls and a marker.
|
|
794
|
+
|
|
795
|
+
Scope:
|
|
796
|
+
The snippet imports nothing of yours by default. Pass --file <path> to
|
|
797
|
+
evaluate it in that file's scope, which also ships the directory's other
|
|
798
|
+
files, so the snippet can use your own page objects and helpers.
|
|
799
|
+
|
|
800
|
+
Failures:
|
|
801
|
+
exec exits 2 until the runner's first run, since there is no page to evaluate
|
|
802
|
+
against. On a 4 the message says the snippet could not be evaluated, but a
|
|
803
|
+
lost answer looks the same from outside, so treat a 4 from a snippet that
|
|
804
|
+
changes something as "may have run" rather than "did not run", and check its
|
|
805
|
+
effect before running it again. With no runner named at all, exec launches one
|
|
806
|
+
and says so on stderr.
|
|
807
|
+
```
|
|
360
808
|
|
|
361
|
-
|
|
362
|
-
browser to draw on. `no-answer` exits `4`: a highlight runs inside the page, so
|
|
363
|
-
a page that is gone or mid-navigation does not answer at all rather than
|
|
364
|
-
answering slowly.
|
|
809
|
+
## qawolf runner inspect
|
|
365
810
|
|
|
366
|
-
|
|
811
|
+
```text
|
|
812
|
+
Usage: qawolf runner inspect [options] [command]
|
|
813
|
+
|
|
814
|
+
Read one thing off a runner's live page (browser) or Appium session (mobile)
|
|
815
|
+
|
|
816
|
+
Options:
|
|
817
|
+
-h, --help display help for command
|
|
818
|
+
|
|
819
|
+
Commands:
|
|
820
|
+
element-html [options] Print the HTML of the first element a selector matches
|
|
821
|
+
page-html [options] Print the page's HTML, simplified for a model to read
|
|
822
|
+
variable [options] Print a top-level variable's value from the running workflow
|
|
823
|
+
session [options] Print the Appium session's status: ready, or why not
|
|
824
|
+
contexts [options] List the WebView contexts available, and which is current
|
|
825
|
+
page-source [options] Print the current context's page source, as a tree
|
|
826
|
+
elements [options] Find elements at a screen point, carrying some text, or matching a
|
|
827
|
+
selector
|
|
828
|
+
help [command] display help for command
|
|
829
|
+
|
|
830
|
+
Examples:
|
|
831
|
+
$ qawolf runner inspect element-html --selector "#email"
|
|
832
|
+
$ qawolf runner inspect page-html > page.html
|
|
833
|
+
$ qawolf runner inspect variable --name cart | jq .total
|
|
834
|
+
$ qawolf runner inspect session
|
|
835
|
+
$ qawolf runner inspect contexts
|
|
836
|
+
$ qawolf runner inspect page-source --context WEBVIEW_1
|
|
837
|
+
$ qawolf runner inspect elements --x 200 --y 400
|
|
838
|
+
$ qawolf runner inspect elements --text "Sign in" --partial
|
|
839
|
+
$ qawolf runner inspect elements --selector "//android.widget.Button[@text='Sign in']"
|
|
840
|
+
|
|
841
|
+
Browser:
|
|
842
|
+
inspect answers one question about the live page and prints the answer on
|
|
843
|
+
stdout by itself, so you can redirect or pipe it. `page-html` is simplified
|
|
844
|
+
for a model to read rather than being the browser's exact markup, and
|
|
845
|
+
--selector narrows it to one subtree. `variable` reads a top-level variable of
|
|
846
|
+
the running workflow and prints it as JSON, which is how you see what your own
|
|
847
|
+
code computed rather than what the page shows. Use it before reaching for
|
|
848
|
+
exec: reading a value through a snippet means printing it and then fishing it
|
|
849
|
+
back out of the console stream, which is two calls and a marker.
|
|
850
|
+
|
|
851
|
+
One failure covers three causes, because a runner cannot tell them apart: no
|
|
852
|
+
live page, no element matching the selector, no variable under that name. All
|
|
853
|
+
three exit 2 and none clears by waiting, so read the message, which carries
|
|
854
|
+
whatever the runner said. An unreachable runner exits 4 and is worth retrying;
|
|
855
|
+
a runner that is not running at all exits 8 and is not.
|
|
856
|
+
|
|
857
|
+
Mobile:
|
|
858
|
+
element-html and page-html are a browser's shapes, and a mobile runner answers
|
|
859
|
+
them with runner-is-not-a-browser. It answers session, contexts, page-source
|
|
860
|
+
and elements instead, and variable works on both.
|
|
861
|
+
|
|
862
|
+
session prints one summary line, ready or why not, because that line is the
|
|
863
|
+
whole answer. contexts, page-source and elements print their answer as JSON on
|
|
864
|
+
stdout, on its own, so you can pipe it (| jq .current, | jq .matches, | jq
|
|
865
|
+
.pageSource) rather than getting a count with no way to see what was found.
|
|
866
|
+
|
|
867
|
+
session is the one subcommand that never answers screen-needs-a-run: readiness
|
|
868
|
+
is the question it exists to answer, so it reports ready (with the platform,
|
|
869
|
+
device and session id), unreachable, ambiguous (more than one session is
|
|
870
|
+
somehow live) or no-session. The other three need a live session first.
|
|
871
|
+
screen-needs-a-run exits 2 and means no Appium session has started on this
|
|
872
|
+
runner yet: run a flow that opens one, then inspect again. screen-not-ready
|
|
873
|
+
exits 4 and means the session exists but did not answer this instant, or more
|
|
874
|
+
than one is somehow live: retry once, and relaunch the runner if it persists.
|
|
875
|
+
|
|
876
|
+
elements takes one of three ways to search: whole-pixel --x/--y on the
|
|
877
|
+
device's own screen, the same coordinates a screenshot is measured in; --text,
|
|
878
|
+
which matches exactly unless --partial is passed; or --selector, resolved the
|
|
879
|
+
same way a screen object's own selector is, with --strategy naming how (xpath,
|
|
880
|
+
ios-predicate or shadow; defaults to xpath). Prefer --selector when checking a
|
|
881
|
+
selector you are about to write: it answers what that exact string resolves
|
|
882
|
+
to, where --text and --x/--y only approximate it. The three do not mix: flags
|
|
883
|
+
from more than one are refused before a runner is addressed. An unparseable
|
|
884
|
+
selector answers invalid-selector, exit 2, distinctly from a selector that
|
|
885
|
+
parsed fine but matched nothing, which answers an empty `matches` list, exit
|
|
886
|
+
0. --context on page-source or elements reads a context other than the current
|
|
887
|
+
one, once contexts has told you which are available.
|
|
888
|
+
|
|
889
|
+
A browser runner answers runner-is-not-mobile to session, contexts,
|
|
890
|
+
page-source and elements, exit 2, since retrying never helps: launch with
|
|
891
|
+
--name android or --name ios instead. runner-unreachable exits 4 and is worth
|
|
892
|
+
retrying.
|
|
893
|
+
```
|
|
367
894
|
|
|
368
|
-
|
|
369
|
-
replaces the baseline on the runner that produced it:
|
|
895
|
+
### qawolf runner inspect element-html
|
|
370
896
|
|
|
371
|
-
```
|
|
372
|
-
qawolf runner
|
|
373
|
-
qawolf runner promote-snapshot --screenshot checkout-1-actual.png --baseline checkout-1.png
|
|
374
|
-
```
|
|
897
|
+
```text
|
|
898
|
+
Usage: qawolf runner inspect element-html [options]
|
|
375
899
|
|
|
376
|
-
|
|
377
|
-
positional, because two paths with one unlabelled is easy to get backwards and
|
|
378
|
-
swapping them promotes the wrong image. They are paths inside the run's own
|
|
379
|
-
screenshot storage, not files on your machine.
|
|
900
|
+
Print the HTML of the first element a selector matches
|
|
380
901
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
902
|
+
Options:
|
|
903
|
+
--selector <selector> Playwright selector to inspect
|
|
904
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's
|
|
905
|
+
stored runner
|
|
906
|
+
-h, --help display help for command
|
|
907
|
+
```
|
|
385
908
|
|
|
386
|
-
|
|
909
|
+
### qawolf runner inspect page-html
|
|
387
910
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
dependencies:
|
|
911
|
+
```text
|
|
912
|
+
Usage: qawolf runner inspect page-html [options]
|
|
391
913
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
914
|
+
Print the page's HTML, simplified for a model to read
|
|
915
|
+
|
|
916
|
+
Options:
|
|
917
|
+
--selector <selector> Limit the output to this subtree
|
|
918
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's
|
|
919
|
+
stored runner
|
|
920
|
+
-h, --help display help for command
|
|
395
921
|
```
|
|
396
922
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
923
|
+
### qawolf runner inspect variable
|
|
924
|
+
|
|
925
|
+
```text
|
|
926
|
+
Usage: qawolf runner inspect variable [options]
|
|
927
|
+
|
|
928
|
+
Print a top-level variable's value from the running workflow
|
|
403
929
|
|
|
404
|
-
|
|
930
|
+
Options:
|
|
931
|
+
--name <name> Name of the variable to read
|
|
932
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner
|
|
933
|
+
-h, --help display help for command
|
|
934
|
+
```
|
|
405
935
|
|
|
406
|
-
|
|
407
|
-
browser is showing, which is how you read a value out of the page rather than
|
|
408
|
-
looking at it. Two things to know, because neither is guessable:
|
|
936
|
+
### qawolf runner inspect session
|
|
409
937
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
chose, and match on that rather than taking the newest line: the page logs to the
|
|
413
|
-
same stream, so anything it prints after your snippet would be what `--tail 1`
|
|
414
|
-
hands back. Entries carry `source`, which is `serverConsole` for your snippet and
|
|
415
|
-
`browserConsole` for the page, so filtering on both is what pins the value down.
|
|
938
|
+
```text
|
|
939
|
+
Usage: qawolf runner inspect session [options]
|
|
416
940
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
941
|
+
Print the Appium session's status: ready, or why not
|
|
942
|
+
|
|
943
|
+
Options:
|
|
944
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner
|
|
945
|
+
-h, --help display help for command
|
|
421
946
|
```
|
|
422
947
|
|
|
423
|
-
|
|
424
|
-
evaluate it in that file's scope, which also ships the directory's other files,
|
|
425
|
-
so the snippet can use your own page objects and helpers.
|
|
426
|
-
|
|
427
|
-
## Running a flow
|
|
428
|
-
|
|
429
|
-
`qawolf runner run <flowFile>` ships the flow file, everything it imports, and
|
|
430
|
-
your `package.json` and `tsconfig.json`. Nothing else travels, so you can run
|
|
431
|
-
from the root of a large project without sending it. The runner holds no copy of
|
|
432
|
-
your project, so what runs is exactly what is on disk at that moment,
|
|
433
|
-
uncommitted edits included.
|
|
434
|
-
|
|
435
|
-
Imports are followed the same way a run from the QA Wolf app follows them:
|
|
436
|
-
relative paths and `tsconfig.json` path aliases, resolving `.ts` and `.js`. An
|
|
437
|
-
`export ... from` re-export is not followed, and neither is `require()`, so a
|
|
438
|
-
barrel file does not pull in what it re-exports. A `package.json` has to be
|
|
439
|
-
there, since the run reads its npm dependencies from it, and the files may carry
|
|
440
|
-
at most 30 MiB in total. A missing file, a missing `package.json` and files over
|
|
441
|
-
the cap are all refused before any runner is resolved or launched, so a typo
|
|
442
|
-
costs nothing.
|
|
443
|
-
|
|
444
|
-
After the first run on a runner, later runs send only the files whose content
|
|
445
|
-
changed, so iterating on one flow costs a small request rather than the whole
|
|
446
|
-
graph again. `--json` reports which happened as `fileSync`, `delta` or `full`.
|
|
447
|
-
Nothing about this needs managing: the baseline lives in `.qawolf/runner-files.json`,
|
|
448
|
-
a switch to another runner ignores it, and a runner that turns out not to hold
|
|
449
|
-
what was claimed gets the whole set resent automatically.
|
|
450
|
-
|
|
451
|
-
The call answers with a run id as soon as the run is accepted. **The outcome is
|
|
452
|
-
not in that answer**, it is in the `run-status` stream, whose entries carry
|
|
453
|
-
`runId`, `status` and an `errorMessage` when there is one.
|
|
454
|
-
|
|
455
|
-
### Running part of a flow
|
|
456
|
-
|
|
457
|
-
`--lines 12-40` runs those lines against the browser as it stands, so nothing is
|
|
458
|
-
re-navigated and nothing is signed in again. Use it to iterate on a step without
|
|
459
|
-
paying for the whole flow to reach it again.
|
|
460
|
-
|
|
461
|
-
The two file paths are the thing to get right, because getting them backwards
|
|
462
|
-
runs the wrong code and nothing reports it:
|
|
463
|
-
|
|
464
|
-
- the positional is **always the flow file**. It is the run's entry point, and it
|
|
465
|
-
is required for every run, selection or not.
|
|
466
|
-
- `--lines-file` is **where the lines live**. It defaults to the positional, so
|
|
467
|
-
pass it only when the range is in another file, typically a page object whose
|
|
468
|
-
method you want to run against the instance your last run left alive.
|
|
948
|
+
### qawolf runner inspect contexts
|
|
469
949
|
|
|
470
|
-
```
|
|
471
|
-
qawolf runner
|
|
472
|
-
|
|
473
|
-
|
|
950
|
+
```text
|
|
951
|
+
Usage: qawolf runner inspect contexts [options]
|
|
952
|
+
|
|
953
|
+
List the WebView contexts available, and which is current
|
|
954
|
+
|
|
955
|
+
Options:
|
|
956
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner
|
|
957
|
+
-h, --help display help for command
|
|
474
958
|
```
|
|
475
959
|
|
|
476
|
-
|
|
477
|
-
directory you run from. A range whose file is not collected is refused before a
|
|
478
|
-
runner is addressed, naming the path.
|
|
960
|
+
### qawolf runner inspect page-source
|
|
479
961
|
|
|
480
|
-
|
|
962
|
+
```text
|
|
963
|
+
Usage: qawolf runner inspect page-source [options]
|
|
481
964
|
|
|
482
|
-
|
|
483
|
-
environment by id or alias, the same reference `qawolf flows` takes as `--env`.
|
|
484
|
-
`--env-file .env` gives the run the variables in a dotenv file, in the format
|
|
485
|
-
`qawolf flows pull` writes.
|
|
965
|
+
Print the current context's page source, as a tree
|
|
486
966
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
967
|
+
Options:
|
|
968
|
+
--context <name> Read this context instead of the current one
|
|
969
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored
|
|
970
|
+
runner
|
|
971
|
+
-h, --help display help for command
|
|
490
972
|
```
|
|
491
973
|
|
|
492
|
-
|
|
493
|
-
variable `qawolf flows` reads, so one export covers both. The run says on
|
|
494
|
-
stderr which environment it picked up, because those variables reach your flow's
|
|
495
|
-
code and a run should never be given an environment silently. `--env-id` wins
|
|
496
|
-
over it, and `--env-file` suppresses it, so a run reading a dotenv file is not
|
|
497
|
-
handed a second environment on top.
|
|
974
|
+
### qawolf runner inspect elements
|
|
498
975
|
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
|
|
976
|
+
```text
|
|
977
|
+
Usage: qawolf runner inspect elements [options]
|
|
978
|
+
|
|
979
|
+
Find elements at a screen point, carrying some text, or matching a selector
|
|
980
|
+
|
|
981
|
+
Options:
|
|
982
|
+
--context <name> Read this context instead of the current one
|
|
983
|
+
--partial text: match text containing this, rather than exactly this
|
|
984
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's
|
|
985
|
+
stored runner
|
|
986
|
+
--selector <selector> Resolved the same way a screen object's own selector is
|
|
987
|
+
--strategy <strategy> selector: xpath, ios-predicate, or shadow (defaults to xpath)
|
|
988
|
+
--text <text> text: the text to match
|
|
989
|
+
--x <pixels> point: whole pixels on the device's own screen
|
|
990
|
+
--y <pixels> point: whole pixels on the device's own screen
|
|
991
|
+
-h, --help display help for command
|
|
502
992
|
```
|
|
503
993
|
|
|
504
|
-
|
|
505
|
-
values never leave the server, nothing has to be pulled to disk first, and no
|
|
506
|
-
size limit applies to them. It is the only way to run a flow whose environment
|
|
507
|
-
holds something large, such as a session cookie.
|
|
508
|
-
|
|
509
|
-
A run that sends its own variables with `--env-file` may carry at most 200 of
|
|
510
|
-
them, each value at most 16 KiB. Names follow what a shell accepts, and
|
|
511
|
-
`QAWOLF_TEAM_ID` is reserved because QA Wolf sets it from the key you
|
|
512
|
-
authenticated with. All of that is refused before a runner is addressed, naming
|
|
513
|
-
the variable at fault.
|
|
514
|
-
|
|
515
|
-
Passing both flags is refused. They each give the run its whole environment, so
|
|
516
|
-
there is no order in which they would combine.
|
|
517
|
-
|
|
518
|
-
If the runner had no browser, one is started before your lines run, and the
|
|
519
|
-
command says so on stderr. Those lines then ran against a fresh page rather than
|
|
520
|
-
the one an earlier run left, which is worth reading before you act on what you
|
|
521
|
-
see.
|
|
522
|
-
|
|
523
|
-
**Pass `--follow` to `run` and let it wait for you.** It reports the run's
|
|
524
|
-
status — in progress, then passed or failed — and ends on the settled status.
|
|
525
|
-
Exit code `1` means the run did not pass. Three flags mirror more streams into
|
|
526
|
-
the follow, and each implies `--follow` on its own: `--logs` streams every log
|
|
527
|
-
line the run produces, `--run-events` streams the run's progress events as JSON
|
|
528
|
-
lines, and `--recorder-events` streams the browser actions the runner records
|
|
529
|
-
as JSON lines — the recorder is runner-wide rather than run-scoped, so that one
|
|
530
|
-
carries whatever is recorded after an anchor taken just before submission. Whatever mirrors are on, the
|
|
531
|
-
follow still ends on the status, never on them, so a run that prints nothing
|
|
532
|
-
still terminates the follow and a run that dies mid-sentence still reports how.
|
|
533
|
-
Combining mirror flags interleaves their lines with nothing saying which stream
|
|
534
|
-
a line came from — fine for eyeballs; when parsing, follow one stream at a time.
|
|
994
|
+
## qawolf runner import-package
|
|
535
995
|
|
|
536
|
-
```
|
|
537
|
-
qawolf runner
|
|
538
|
-
|
|
539
|
-
|
|
996
|
+
```text
|
|
997
|
+
Usage: qawolf runner import-package [options] <name>
|
|
998
|
+
|
|
999
|
+
Install a package into a runner's live run, so a snippet or a selection can import it
|
|
1000
|
+
|
|
1001
|
+
Options:
|
|
1002
|
+
--package-version <version> Version to install (default: "latest")
|
|
1003
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's
|
|
1004
|
+
stored runner
|
|
1005
|
+
-h, --help display help for command
|
|
1006
|
+
|
|
1007
|
+
Examples:
|
|
1008
|
+
$ qawolf runner import-package dayjs
|
|
1009
|
+
$ qawolf runner import-package dayjs --package-version 1.11.13
|
|
1010
|
+
|
|
1011
|
+
Installing mid-session:
|
|
1012
|
+
The package goes into the runner's live run, so a snippet or a selection can
|
|
1013
|
+
import it without a whole run to reinstall dependencies. The flag is
|
|
1014
|
+
--package-version because --version belongs to the CLI itself. The install
|
|
1015
|
+
resolves against your project's own dependencies, read from package.json, so
|
|
1016
|
+
it needs a run already going: there is no live run on a runner that has not
|
|
1017
|
+
run anything. npm's own refusal comes back verbatim on an exit 2, which is a
|
|
1018
|
+
name or a version to correct rather than something to retry.
|
|
540
1019
|
```
|
|
541
1020
|
|
|
542
|
-
|
|
543
|
-
does not end when the run settles — it runs until its own `--timeout`, an hour
|
|
544
|
-
by default — so it cannot be used to wait for a run. Poll instead, and decide
|
|
545
|
-
with the same rule the CLI uses: `status` is `in-progress` while the run is
|
|
546
|
-
going, and any other value means it has settled.
|
|
1021
|
+
## qawolf runner highlight-selector
|
|
547
1022
|
|
|
548
|
-
```
|
|
549
|
-
qawolf runner
|
|
550
|
-
|
|
1023
|
+
```text
|
|
1024
|
+
Usage: qawolf runner highlight-selector [options] [selector]
|
|
1025
|
+
|
|
1026
|
+
Highlight what a selector matches on a runner's live page, so the next screenshot shows it. Omit the
|
|
1027
|
+
selector to clear the highlight
|
|
1028
|
+
|
|
1029
|
+
Options:
|
|
1030
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner
|
|
1031
|
+
-h, --help display help for command
|
|
1032
|
+
|
|
1033
|
+
Examples:
|
|
1034
|
+
$ qawolf runner highlight-selector "text=Sign in"
|
|
1035
|
+
$ qawolf runner highlight-selector "#checkout" && qawolf runner screenshot
|
|
1036
|
+
$ qawolf runner highlight-selector
|
|
1037
|
+
|
|
1038
|
+
Reading the result:
|
|
1039
|
+
`inspect element-html` tells you what a selector matched; highlight-selector
|
|
1040
|
+
shows you where it is, by drawing on the page itself. The highlight stays
|
|
1041
|
+
until it is replaced or cleared, which is the point: you cannot see the
|
|
1042
|
+
runner's screen, so the only way to read the result is the next screenshot.
|
|
1043
|
+
|
|
1044
|
+
Three answers are worth telling apart. A selector that matched prints how many
|
|
1045
|
+
elements it hit and exits 0. A selector the page read fine but that matched
|
|
1046
|
+
nothing also exits 0, because the call did what was asked and the count is the
|
|
1047
|
+
answer; the message says the syntax was fine so you look at the page, not the
|
|
1048
|
+
locator. A selector the page could not read at all exits 2, because that one
|
|
1049
|
+
is yours to correct and retrying will not change it.
|
|
1050
|
+
|
|
1051
|
+
runner-cannot-highlight-selectors exits 2 and means the runner has no browser
|
|
1052
|
+
to draw on. no-answer exits 4: a highlight runs inside the page, so a page
|
|
1053
|
+
that is gone or mid-navigation does not answer at all rather than answering
|
|
1054
|
+
slowly.
|
|
551
1055
|
```
|
|
552
1056
|
|
|
553
|
-
|
|
554
|
-
could not be reached, that does not mean the run did not start.** The runner may
|
|
555
|
-
have accepted it and been too slow to answer, and resubmitting bills and journals
|
|
556
|
-
a second run.
|
|
557
|
-
|
|
558
|
-
There is no clean recovery here, so it is worth being plain about it. The journal
|
|
559
|
-
lives on the same pod, so while the runner stays unreachable a `run-status` read
|
|
560
|
-
fails the same way and cannot tell you whether a run is going. Wait for the
|
|
561
|
-
runner to answer again, then read `run-status` without `--run` and look at the
|
|
562
|
-
newest `runId`. Nothing ties that id back to your submission: `run` never
|
|
563
|
-
answered, so you have no id to match it against, and a runner takes work from
|
|
564
|
-
anyone addressing it. Treat the newest id as your run only if you know nothing
|
|
565
|
-
else submits to this runner; otherwise follow it to see what it is before acting
|
|
566
|
-
on it. An empty read is not proof the run did not start, though:
|
|
567
|
-
`run` returns the moment the run is accepted, and its first `run-status` entry
|
|
568
|
-
may not be written yet, so a run accepted just before the runner went quiet can
|
|
569
|
-
still be in flight with nothing to show. `runFlow` has no idempotency key, so a
|
|
570
|
-
resubmit always risks a second billed run. Prefer polling `run-status` a while
|
|
571
|
-
longer over resubmitting; only submit again once you are willing to accept that
|
|
572
|
-
risk.
|
|
573
|
-
|
|
574
|
-
## Reading history
|
|
575
|
-
|
|
576
|
-
Everything observable is an append-only stream on the pod, read by cursor or
|
|
577
|
-
tail rather than subscribed to, so attaching late still gets you the history that
|
|
578
|
-
is still there. It is not unbounded: a size cap drops the oldest entries on a
|
|
579
|
-
long-lived runner, and a `--tail N` read can stop early and hand back fewer than
|
|
580
|
-
N even when more matched. Both are warned about on stderr — dropped entries only
|
|
581
|
-
once a read holds a cursor, a stopped-early read with a pointer at `--since` —
|
|
582
|
-
so watch stderr, treat a short answer as "at least this" rather than "all there
|
|
583
|
-
was", and read what you care about as you go rather than at the end. QA Wolf writes `recorder`, `console`, `run-events`,
|
|
584
|
-
`run-logs` and `run-status`; a stream nobody has written reads as empty rather
|
|
585
|
-
than as an error, and a stream this CLI version does not know about is still
|
|
586
|
-
readable by name.
|
|
587
|
-
|
|
588
|
-
One payload per line, so shell tools compose:
|
|
1057
|
+
## qawolf runner promote-snapshot
|
|
589
1058
|
|
|
590
|
-
```
|
|
591
|
-
qawolf runner
|
|
592
|
-
|
|
1059
|
+
```text
|
|
1060
|
+
Usage: qawolf runner promote-snapshot [options]
|
|
1061
|
+
|
|
1062
|
+
Accept a run's screenshot as the new baseline for an image diff, on the runner that produced it
|
|
1063
|
+
|
|
1064
|
+
Options:
|
|
1065
|
+
--screenshot <path> The screenshot to promote, as the image diff named it
|
|
1066
|
+
--baseline <path> The baseline to replace, as the image diff named it
|
|
1067
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored
|
|
1068
|
+
runner
|
|
1069
|
+
-h, --help display help for command
|
|
1070
|
+
|
|
1071
|
+
Examples:
|
|
1072
|
+
$ qawolf runner promote-snapshot --screenshot checkout-1-actual.png --baseline checkout-1.png
|
|
1073
|
+
$ qawolf runner events run-events --tail 20 | jq 'select(.type == "imageDiffArtifact")'
|
|
1074
|
+
|
|
1075
|
+
Paths:
|
|
1076
|
+
Use it when a run's image diff fails and the new screenshot is the one you
|
|
1077
|
+
want. Both paths are the ones the diff reported in its imageDiffArtifact run
|
|
1078
|
+
event, and both are named rather than positional, because two paths with one
|
|
1079
|
+
unlabelled is easy to get backwards and swapping them promotes the wrong
|
|
1080
|
+
image. They are paths inside the run's own screenshot storage, not files on
|
|
1081
|
+
your machine.
|
|
1082
|
+
|
|
1083
|
+
snapshot-not-found exits 2 and means the run wrote no screenshot at that path,
|
|
1084
|
+
which nearly always means the paths did not come from a diff this run
|
|
1085
|
+
produced. Nothing is changed, so correcting the path and repeating is safe.
|
|
1086
|
+
Promoting twice is also safe, so an unreachable runner is worth retrying.
|
|
593
1087
|
```
|
|
594
1088
|
|
|
595
|
-
|
|
596
|
-
cursor, and `--run <id>` narrows the run-scoped streams.
|
|
1089
|
+
## qawolf runner record
|
|
597
1090
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
1091
|
+
```text
|
|
1092
|
+
Usage: qawolf runner record [options] [command]
|
|
1093
|
+
|
|
1094
|
+
Control video recording on a Playwright runner
|
|
1095
|
+
|
|
1096
|
+
Options:
|
|
1097
|
+
-h, --help display help for command
|
|
1098
|
+
|
|
1099
|
+
Commands:
|
|
1100
|
+
start [options] Start manual video capture across runs. Requires a ready screen and
|
|
1101
|
+
suppresses automatic capture until stopped
|
|
1102
|
+
stop [options] <recording-id> Stop the recording with this UUID and publish it. Retrying cannot
|
|
1103
|
+
stop a later recording
|
|
1104
|
+
status [options] Show the active recording and automatic recording setting
|
|
1105
|
+
auto [options] <setting> Enable or disable automatic recording for subsequent full runs
|
|
1106
|
+
help [command] display help for command
|
|
1107
|
+
|
|
1108
|
+
Recording:
|
|
1109
|
+
Start a flow first so the runner has a screen, then start a manual capture and
|
|
1110
|
+
keep `result.state.active.id` from the `record start --json` answer to stop
|
|
1111
|
+
that capture:
|
|
1112
|
+
|
|
1113
|
+
$ qawolf runner record start --runner ci --json
|
|
1114
|
+
$ qawolf runner record stop <recording-id> --runner ci
|
|
1115
|
+
$ qawolf runner record status --runner ci
|
|
1116
|
+
$ qawolf runner record auto on --runner ci
|
|
1117
|
+
|
|
1118
|
+
Start generates a UUID unless you pass --recording-id <uuid>. If a start
|
|
1119
|
+
response is lost, the error includes that UUID: check `record status` and
|
|
1120
|
+
reuse the UUID when retrying. Stop always names a specific recording, so
|
|
1121
|
+
retrying it cannot stop a later capture. Manual recordings span runs and
|
|
1122
|
+
suppress automatic capture until stopped. The auto setting affects subsequent
|
|
1123
|
+
full runs. An active automatic recording ends with its run; `record stop`
|
|
1124
|
+
cannot interrupt it. If a stop publishes a recording with status "failed", the
|
|
1125
|
+
CLI exits 1 and still prints the manifest so callers can inspect it.
|
|
1126
|
+
|
|
1127
|
+
Published recordings stay readable after the runner terminates, with `qawolf
|
|
1128
|
+
runner list-recordings`.
|
|
1129
|
+
```
|
|
602
1130
|
|
|
603
|
-
|
|
604
|
-
than how far it matched, and `--follow` carries that number, so a filtered read
|
|
605
|
-
that matched nothing still moves forward. A caller paging by hand cannot see it,
|
|
606
|
-
because the CLI does not print it, and the best available substitute is the
|
|
607
|
-
highest `sequence` you actually saw. So a narrow `--run` filter over a busy
|
|
608
|
-
stream stalls: with nothing matching, there is no new `sequence` to move on to,
|
|
609
|
-
and you re-read the same window until something matches (NOVA-1397).
|
|
1131
|
+
### qawolf runner record start
|
|
610
1132
|
|
|
611
|
-
|
|
1133
|
+
```text
|
|
1134
|
+
Usage: qawolf runner record start [options]
|
|
612
1135
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
`qawolf runner keepalive` exists for the gap that creates: a harness that thinks,
|
|
616
|
-
or waits on a human, for minutes between actions would otherwise come back to a
|
|
617
|
-
pod that is gone. It resets the clock and tells you the runner is still there.
|
|
1136
|
+
Start manual video capture across runs. Requires a ready screen and suppresses automatic capture
|
|
1137
|
+
until stopped
|
|
618
1138
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
1139
|
+
Options:
|
|
1140
|
+
--recording-id <uuid> Recording UUID. Generated when omitted; reuse it when retrying this start
|
|
1141
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's
|
|
1142
|
+
stored runner
|
|
1143
|
+
-h, --help display help for command
|
|
1144
|
+
```
|
|
624
1145
|
|
|
625
|
-
|
|
1146
|
+
### qawolf runner record stop
|
|
626
1147
|
|
|
627
|
-
|
|
1148
|
+
```text
|
|
1149
|
+
Usage: qawolf runner record stop [options] <recording-id>
|
|
628
1150
|
|
|
629
|
-
|
|
630
|
-
runner up, its browser on whatever page the run reached. The run settles as
|
|
631
|
-
stopped rather than passed or failed. Use it to abandon a run and keep the
|
|
632
|
-
browser you were working against.
|
|
633
|
-
- `qawolf runner terminate` ends the runner and the pod with it. Everything on
|
|
634
|
-
it is gone, and the next command under that id launches and bills a new one.
|
|
1151
|
+
Stop the recording with this UUID and publish it. Retrying cannot stop a later recording
|
|
635
1152
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
1153
|
+
Options:
|
|
1154
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner
|
|
1155
|
+
-h, --help display help for command
|
|
1156
|
+
```
|
|
639
1157
|
|
|
640
|
-
|
|
1158
|
+
### qawolf runner record status
|
|
641
1159
|
|
|
642
|
-
|
|
643
|
-
|
|
1160
|
+
```text
|
|
1161
|
+
Usage: qawolf runner record status [options]
|
|
644
1162
|
|
|
645
|
-
|
|
646
|
-
export QAWOLF_API_KEY=... # the only credential
|
|
647
|
-
export QAWOLF_RUNNER_ID=agent-1 # so no command below needs --runner
|
|
1163
|
+
Show the active recording and automatic recording setting
|
|
648
1164
|
|
|
649
|
-
|
|
650
|
-
|
|
1165
|
+
Options:
|
|
1166
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner
|
|
1167
|
+
-h, --help display help for command
|
|
1168
|
+
```
|
|
651
1169
|
|
|
652
|
-
qawolf runner
|
|
653
|
-
qawolf runner act click --button left --x 480 --y 260 --screenshot step-2.jpg
|
|
654
|
-
qawolf runner act type --text "someone@example.com" --screenshot step-3.jpg
|
|
1170
|
+
### qawolf runner record auto
|
|
655
1171
|
|
|
656
|
-
|
|
657
|
-
qawolf runner
|
|
1172
|
+
```text
|
|
1173
|
+
Usage: qawolf runner record auto [options] <setting>
|
|
658
1174
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
1175
|
+
Enable or disable automatic recording for subsequent full runs
|
|
1176
|
+
|
|
1177
|
+
Arguments:
|
|
1178
|
+
setting Automatic recording setting (choices: "on", "off")
|
|
1179
|
+
|
|
1180
|
+
Options:
|
|
1181
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner
|
|
1182
|
+
-h, --help display help for command
|
|
1183
|
+
```
|
|
1184
|
+
|
|
1185
|
+
## qawolf runner list-recordings
|
|
1186
|
+
|
|
1187
|
+
```text
|
|
1188
|
+
Usage: qawolf runner list-recordings [options]
|
|
1189
|
+
|
|
1190
|
+
Read a page of published video recordings, including after the runner terminates. Platform URLs
|
|
1191
|
+
persist; video URLs expire
|
|
1192
|
+
|
|
1193
|
+
Options:
|
|
1194
|
+
--runner <id> Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's
|
|
1195
|
+
stored runner
|
|
1196
|
+
--recording-id <uuid> Look up one recording; an empty result means it is not published or does
|
|
1197
|
+
not exist
|
|
1198
|
+
--page-token <token> Continue from nextPageToken returned by the previous page
|
|
1199
|
+
-h, --help display help for command
|
|
1200
|
+
|
|
1201
|
+
Reading pages:
|
|
1202
|
+
$ qawolf runner list-recordings --runner ci --json
|
|
1203
|
+
$ qawolf runner list-recordings --runner ci --recording-id <uuid> --json
|
|
1204
|
+
$ qawolf runner list-recordings --runner ci --page-token '<nextPageToken>' --json
|
|
1205
|
+
|
|
1206
|
+
Each response is one page with `recordings` and an optional `nextPageToken`.
|
|
1207
|
+
Follow that token for more; the order is storage-key order, not newest-first.
|
|
1208
|
+
Entries include status, run ids, a stable platform `url`, and an expiring
|
|
1209
|
+
`videoUrl` when video is available. An empty lookup means the recording has
|
|
1210
|
+
not been published or does not exist. Abrupt runner loss may leave video
|
|
1211
|
+
unavailable. Keep the runner id for history lookups after termination clears
|
|
1212
|
+
the local default.
|
|
662
1213
|
```
|