diff --git a/.changeset/runner-act-mobile-actions.md b/.changeset/runner-act-mobile-actions.md new file mode 100644 index 000000000..8e58ce1c4 --- /dev/null +++ b/.changeset/runner-act-mobile-actions.md @@ -0,0 +1,5 @@ +--- +"@qawolf/cli": minor +--- + +`qawolf runner act` has mobile actions of its own: `tap` (at `--x`/`--y` or on a `--selector`), `swipe` (`--from x,y --to x,y`, optional `--duration-ms`), and `fill` (replaces the value of the `--selector` field with `--text`). `click` and `drag` still tap and swipe on a mobile runner, but are deprecated there. A browser runner refuses the mobile actions with `action-not-supported-on-browser`. Pins `@qawolf/api-contracts` 0.73.0. diff --git a/bun.lock b/bun.lock index 9201156c9..87b3061b0 100644 --- a/bun.lock +++ b/bun.lock @@ -8,7 +8,7 @@ "@clack/prompts": "1.5.1", "@napi-rs/keyring": "1.3.0", "@oxc-node/core": "0.1.0", - "@qawolf/api-contracts": "0.70.0", + "@qawolf/api-contracts": "0.73.0", "@qawolf/emails": "1.1.1", "@qawolf/flow-targets": "1.0.0", "@qawolf/flows": "0.1.4", @@ -462,7 +462,7 @@ "@puppeteer/browsers": ["@puppeteer/browsers@2.13.2", "", { "dependencies": { "debug": "^4.4.3", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.5.0", "semver": "^7.7.4", "tar-fs": "^3.1.1", "yargs": "^17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" } }, "sha512-5EUZSUIc37H6aIXyWO0Z4y8NlF8NnjgmqeQgOGiswAU7pY0HOo16ho4+alIWmSfdZnjqBRawMsP3I5YqLSn6kw=="], - "@qawolf/api-contracts": ["@qawolf/api-contracts@0.70.0", "", { "dependencies": { "tslib": "^2.5.3" }, "peerDependencies": { "zod": "^4.1.11" } }, "sha512-kOu1kH47/r0zP/OEG7Z6U8ZbZ4tDQOCV4M76nBCj9XPoLNgloFl3REp92qNlYAGMJX9p7SBgJhIG3mdfmL49wQ=="], + "@qawolf/api-contracts": ["@qawolf/api-contracts@0.73.0", "", { "dependencies": { "tslib": "^2.5.3" }, "peerDependencies": { "zod": "^4.1.11" } }, "sha512-T5w/Iv18MjBTyDO5wyPKRx/NPVBqDzWzSXD2tSJ5MqtR4BsRasvCmL5teaiz1HRp/gjW15PnoI3YjICerysVPQ=="], "@qawolf/emailer-types": ["@qawolf/emailer-types@1.1.0", "", { "dependencies": { "email-addresses": "^5.0.0", "tslib": "^2.5.3", "zod": "^4.1.11", "zod-form-data": "^3.0.1" } }, "sha512-7cX+e31L9dpO8fntQ2cfSMt/1Lla/yUjaLUr/KLpXuscBSajy7CgNzjAGURa1KsCcxz7n+9PGQw6cM3RlC1bSg=="], diff --git a/package.json b/package.json index 839c3ecb6..9c9eaf54a 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "@clack/prompts": "1.5.1", "@napi-rs/keyring": "1.3.0", "@oxc-node/core": "0.1.0", - "@qawolf/api-contracts": "0.70.0", + "@qawolf/api-contracts": "0.73.0", "@qawolf/emails": "1.1.1", "@qawolf/flow-targets": "1.0.0", "@qawolf/flows": "0.1.4", diff --git a/skills/qawolf-cli/SKILL.md b/skills/qawolf-cli/SKILL.md index f7be762c3..77d1d12e1 100644 --- a/skills/qawolf-cli/SKILL.md +++ b/skills/qawolf-cli/SKILL.md @@ -168,7 +168,7 @@ that `url`; never guess a route and never send a repository link in its place. | `qawolf install clear` | local | Remove the managed runtime cache (all installed runtime versions) | | `qawolf investigation get` | read | Read what the QA Wolf AI investigating a run has concluded: one finding per cause of its failures, with the question waiting for a person and any answer or dispute the investigation hasn't acted on yet. | | `qawolf issue addFlows` | write | Add flows to a coverage request owned by the caller's team. Flows already covered stay covered. Bug and maintenance reports link to flows through the runs that reproduce them; use run.diagnose to record one. | -| `qawolf issue create` | write | Create a bug or coverage request issue for the caller's team. Maintenance issues cannot be created through the public API. | +| `qawolf issue create` | write | Create a bug report, maintenance report, or coverage request issue for the caller's team. A user can create a maintenance report only when they can see the workspace's maintenance reports; a team or organization API key credits it to the workspace's automation user. Link its failed flows afterwards with run.diagnose. | | `qawolf issue find` | read | List the team's bug reports, maintenance reports, or coverage requests, newest first. | | `qawolf issue get` | read | Get an issue by id. | | `qawolf issue removeFlows` | write | Remove flows from a coverage request owned by the caller's team. Flows the request does not cover are left alone. Bug and maintenance reports link to flows through the runs that reproduce them, so their flows cannot be set directly. | @@ -182,8 +182,7 @@ that `url`; never guess a route and never send a repository link in its place. | `qawolf run get` | read | Get a run's status, per-flow results, links, and how many of its bugs are blocking. | | `qawolf run reattempt` | write | Request new attempts for a run's flows, in the same run. A flow is eligible once its result is failed or canceled and QA Wolf's automatic retries have finished. A fully investigated run no longer accepts reattempts. Attempts run with the latest flow code. Poll run.get for results. | | `qawolf run stop` | write | Stop a run, including its queued flows and automatic retries. Stopping is asynchronous and can update run-status messages and commit statuses in connected integrations. Repeated requests are safe, and finished runs keep their results. A run that is still being created returns not found; retry once run.get returns the run. If run.get returns a different runId, use that ID. Poll run.get for results. | -| `qawolf run triage` | read | Prototype. Ask Jev, a TypeSafe System One model, whether a flow failure in a run is a bug in the product or a test that needs maintenance. Compares the failing attempt with the flow's last passing run in the same environment. Records nothing. | -| `qawolf runner act` | write | Perform one raw action on a runner's screen: click, double_click, scroll, move, drag, keypress, navigate or type. Use - to read a whole action as JSON from stdin. On a mobile runner only click (button left), drag and type have a touchscreen equivalent; the rest answer action-not-supported-on-mobile | +| `qawolf runner act` | write | Perform one raw action on a runner's screen. A browser runner takes click, double_click, scroll, move, drag, keypress, navigate and type; a mobile runner takes tap, swipe, fill and type. A browser runner answers mobile actions with action-not-supported-on-browser; a mobile runner answers the other browser actions with action-not-supported-on-mobile. Use - to read a whole action as JSON from stdin | | `qawolf runner actions` | write | Perform a sequence of up to ten raw actions on a runner's screen in one request, as a JSON array of the same actions `runner act` takes. Use - to read the array from stdin. Actions run back to back, so batch only steps whose targets are on the screen you last saw, and end the sequence at the step that changes the page. Each result carries an effect: performed, not-performed, or unknown when the runner stopped answering and the action may have landed | | `qawolf runner events` | read | Print a runner's journal, one entry per line. QA Wolf writes console, recorder, run-events, run-logs, run-status | | `qawolf runner exec` | write | Evaluate a snippet against a runner's live page. Use - to read the snippet from stdin | diff --git a/skills/qawolf-cli/references/run-results.md b/skills/qawolf-cli/references/run-results.md index cef5e3084..cbab82c18 100644 --- a/skills/qawolf-cli/references/run-results.md +++ b/skills/qawolf-cli/references/run-results.md @@ -183,6 +183,7 @@ Every documented field of the `run.get` response. `[]` marks an array, so - `completedAt` — When the run finished executing. Absent while queued or running, and also absent for a terminal run that never completed execution (e.g. every flow was canceled or skipped). - `git` — The branch and commit under test. The fields are present when a deploy notification started the run, and absent for runs started another way, for example manually or with run.create. - `git.commitUrl` — Link to the commit on the code host. +- `needsReview` — True once every flow has finished its attempts and at least one failed flow still has no diagnosis, including one whose investigation carried over to a later run. `status` stays `running` until that failure is diagnosed, so a caller that is not waiting for a diagnosis can stop polling here and read the run's failed flows. - `runId` — The run this response describes. Treat it as canonical: it can differ from the id you asked for. A deploy notification returns a run id before the run exists, and if a second notification for the same commit is folded into an earlier run, that id resolves to the earlier run instead. - `status` — One of: queued, running, passed, failed, canceled - `blockingBugCount` — How many bugs this run found are blocking: still open, and priority urgent, high, or unprioritized — unprioritized counts because nobody has ruled it out yet. This is what `failed` is derived from, so it explains a failure rather than adding a second verdict: gate on `status`, then read this to say how many bugs are holding the build. It counts a bug filed against a later run by an investigation that carried over from this one, and it drops a bug once that bug is resolved. diff --git a/skills/qawolf-cli/references/runner.md b/skills/qawolf-cli/references/runner.md index 50bbb1718..0c112b002 100644 --- a/skills/qawolf-cli/references/runner.md +++ b/skills/qawolf-cli/references/runner.md @@ -126,7 +126,7 @@ Retry on the exit code, not on the message text: 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. -- `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 has no touchscreen equivalent on a mobile runner, so reach for one that has. 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. +- `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. ## Seeing and acting: the loop is yours @@ -167,17 +167,18 @@ Coordinates are pixels on the same screenshot you just read. The runner serves o `act`, `actions`, `run` and `exec` are the commands whose lost answer may still have taken effect. On a `4` from `act` or `actions`, take a screenshot before repeating a click. `exec`'s message says the snippet could not be evaluated, but a lost answer looks the same from outside, so treat a `4` from a snippet that changes something as "may have run" rather than "did not run". -A mobile runner has a touchscreen, not a mouse, so only three of the eight -actions have a touchscreen equivalent and go through: `click` with -`button: "left"` taps, `drag` swipes, and `type` types into whatever the last -tap focused. The rest — `double_click`, `scroll`, `move`, `keypress`, -`navigate` — answer `action-not-supported-on-mobile` rather than doing -something approximate. `navigate` is the one to watch for, since it works on a -browser runner without a run first but has no meaning on mobile at all. +A mobile runner has a touchscreen, not a mouse, so it has actions of its own: + +- `tap` touches a point (`--x`, `--y`) or the element `--selector` names. Check the selector first with `inspect elements --selector`, and pass `--strategy ios-predicate` or `shadow` when it is not XPath. +- `swipe --from x,y --to x,y` moves in a straight line between two points. `--duration-ms` sets how long it takes, up to 10000: a slow swipe scrolls, a fast one flings. +- `fill --selector ... --text ...` replaces the value of that field, and `--text ""` clears it. To add to what a field already holds, `tap` it and then `type`. +- `type` types into whatever the last tap focused, the same as on a browser. + +The browser actions `double_click`, `scroll`, `move`, `keypress` and `navigate` answer `action-not-supported-on-mobile` rather than doing something approximate. `navigate` is the one to watch for, since it works on a browser runner without a run first but has no meaning on mobile at all. `click` with `button: "left"` and `drag` still tap and swipe on mobile, but they are deprecated there, so send `tap` and `swipe`. A browser runner answers `tap`, `swipe` and `fill` with `action-not-supported-on-browser`. ### Several steps in one request: `runner actions` -`qawolf runner actions ''` performs up to ten of those same actions back to back in one request, for the steps you already know: click the field, type into it, press Enter. One round trip instead of three, with no delay to guess at between them, and `-` reads the array from stdin the way `act -` reads one action. +`qawolf runner actions ''` performs up to ten of the browser actions back to back in one request (not `tap`, `swipe` or `fill`), for the steps you already know: click the field, type into it, press Enter. One round trip instead of three, with no delay to guess at between them, and `-` reads the array from stdin the way `act -` reads one action. ```sh qawolf runner actions '[{"type":"click","button":"left","x":480,"y":260},{"type":"type","text":"me@example.com"},{"type":"keypress","keys":["Enter"]}]' --screenshot after-login.jpg diff --git a/src/commands/__snapshots__/help.test.ts.snap b/src/commands/__snapshots__/help.test.ts.snap index b048cfab5..879d1f4ef 100644 --- a/src/commands/__snapshots__/help.test.ts.snap +++ b/src/commands/__snapshots__/help.test.ts.snap @@ -293,7 +293,7 @@ Commands: run [options] Run a flow on an interactive runner, shipping the flow and what it imports events [options] Print a runner's journal, one entry per line. QA Wolf writes console, recorder, run-events, run-logs, run-status screenshot [options] Save a JPEG of an interactive runner's screen to a file, or write it to stdout with --out - - act [options] Perform one raw action on a runner's screen: click, double_click, scroll, move, drag, keypress, navigate or type. Use - to read a whole action as JSON from stdin. On a mobile runner only click (button left), drag and type have a touchscreen equivalent; the rest answer action-not-supported-on-mobile + act [options] Perform one raw action on a runner's screen. A browser runner takes click, double_click, scroll, move, drag, keypress, navigate and type; a mobile runner takes tap, swipe, fill and type. A browser runner answers mobile actions with action-not-supported-on-browser; a mobile runner answers the other browser actions with action-not-supported-on-mobile. Use - to read a whole action as JSON from stdin actions [options] Perform a sequence of up to ten raw actions on a runner's screen in one request, as a JSON array of the same actions \`runner act\` takes. Use - to read the array from stdin. Actions run back to back, so batch only steps whose targets are on the screen you last saw, and end the sequence at the step that changes the page. Each result carries an effect: performed, not-performed, or unknown when the runner stopped answering and the action may have landed exec [options] Evaluate a snippet against a runner's live page. Use - to read the snippet from stdin inspect Read one thing off a runner's live page (browser) or Appium session (mobile) @@ -553,31 +553,40 @@ Examples: exports[`--help output qawolf runner act 1`] = ` "Usage: qawolf runner act [options] -Perform one raw action on a runner's screen: click, double_click, scroll, move, -drag, keypress, navigate or type. Use - to read a whole action as JSON from -stdin. On a mobile runner only click (button left), drag and type have a -touchscreen equivalent; the rest answer action-not-supported-on-mobile +Perform one raw action on a runner's screen. A browser runner takes click, +double_click, scroll, move, drag, keypress, navigate and type; a mobile runner +takes tap, swipe, fill and type. A browser runner answers mobile actions with +action-not-supported-on-browser; a mobile runner answers the other browser +actions with action-not-supported-on-mobile. Use - to read a whole action as +JSON from stdin Options: - --button