E2E testing and browser automation with qa-use CLI. Use when the user needs to run tests, verify features, automate browser interactions, or debug test failures.
E2E testing and browser automation for AI-driven development workflows.
For AI Harnesses (codex, opencode, etc.):
Plugin commands (slash commands like /qa-use:verify) are convenience shortcuts that wrap CLI workflows. Harnesses with only the Bash tool can access ALL functionality via CLI commands documented below.
Pattern throughout this document:
Before using any qa-use commands, verify configuration is in place:
# Check current configuration (no-op if already configured)
qa-use setup
# Configure with API key (validates against server)
qa-use setup --api-key <key>
# View full configuration details
qa-use info
Environment Variables (alternative to config file):
| Variable | Description |
|----------|-------------|
| QA_USE_API_KEY | API key for authentication |
| QA_USE_REGION | Region: us (default) or auto |
| QA_USE_API_URL | Override API base URL |
Config file: .qa-use.json in the project directory or ~/.qa-use.json in the home directory. Precedence: env vars > project .qa-use.json > ~/.qa-use.json.
If you encounter "API key not configured", 401, or auth errors: Run qa-use setup to check config state. NEVER fabricate or guess API keys.
CLI Workflow:
# Create browser session (returns immediately — runs detached in the background)
qa-use browser create --viewport desktop
# For localhost testing — auto-tunnels when base URL is localhost and API is remote.
# No flag needed for the common case:
qa-use browser create --no-headless http://localhost:3000
# Force a tunnel even in dev mode:
qa-use browser create --tunnel on --no-headless
# Opt out of auto-tunnel (e.g. backend is also local):
qa-use browser create --no-tunnel
# Navigate
qa-use browser goto https://example.com
# Snapshot to get element refs (ALWAYS do this before interacting)
qa-use browser snapshot
# Interact by ref
qa-use browser click e3
qa-use browser fill e5 "text"
# Close
qa-use browser close
Background session management:
qa-use browser create returns immediately — the actual browser + tunnel run in a detached child process so your terminal stays free. Manage sessions with:
qa-use browser status --list # Show all active sessions across processes
qa-use browser status # Details for the auto-resolved session
qa-use browser close <id> # Close a specific session
qa-use doctor # Reap stale sessions/tunnels (dead PIDs)
qa-use doctor --dry-run # Preview what would be cleaned
Tunnel commands:
Cross-process tunnel registry — multiple commands share a single tunnel per target:
qa-use tunnel start <url> # Acquire a tunnel (released immediately unless --hold)
qa-use tunnel start <url> --hold # Keep the tunnel up until Ctrl+C
qa-use tunnel ls # List active tunnels (target, public URL, refcount)
qa-use tunnel status <target> # Detail for a single tunnel
qa-use tunnel close <target> # Force-release a tunnel (kills detached holders)
Plugin Shortcut:
/qa-use:explore https://example.com
(Wraps create + goto + snapshot with autonomous exploration)
Critical: Always run snapshot before your first interaction on a page. Never guess element refs.
Snapshot Diff Feature (use it to avoid unnecessary snapshots): After each action (goto, click, fill, etc.), the browser automatically shows DOM changes:
+ [e54] generic "Thanks for agreeing!" (green)~ [e18] checkbox "I agree..." with +attrs: checked, active (yellow)- [e99] button "Submit" (red)When you can skip a full snapshot: If the diff output from your last action already shows the element ref you need to interact with next, use it directly — no need for an intermediate snapshot. For example, if clicking a button shows + [e54] button "Submit" in the diff, you can click e54 immediately.
When you still need a full snapshot: Run snapshot when you need to find elements that weren't in the diff (e.g., pre-existing elements you haven't interacted with yet), or when the diff was truncated (shows "... and N more changes").
What are blocks?
Blocks are atomic recorded interactions from a browser session. They are:
qa-use browser get-blocksWhy blocks matter:
How blocks work:
# 1. Create session and interact (auto-tunnels localhost)
qa-use browser create --no-headless
qa-use browser goto https://example.com
qa-use browser snapshot # Returns: [ref=e1] button
qa-use browser click e1 # Records as block
qa-use browser fill e5 "text" # Records as block
# 2. Retrieve blocks (JSON array)
qa-use browser get-blocks
# Returns:
# [
# {"type": "goto", "url": "...", "timestamp": "..."},
# {"type": "click", "ref": "e1", "timestamp": "..."},
# {"type": "fill", "ref": "e5", "value": "text", "timestamp": "..."}
# ]
# 3. Generate test YAML from blocks
qa-use browser generate-test -n "my_test" -o qa-tests/my_test.yaml
# 4. Run generated test
qa-use test run my_test
Plugin Shortcut:
/qa-use:record start my_test
# ... perform interactions ...
/qa-use:record stop
(Wraps the interactive workflow with AI-powered test generation)
CLI Workflow:
# Run test by name
qa-use test run login
# Run and save the (non-synced) local test to cloud
qa-use test run login --persist
# Validate syntax
qa-use test validate login
# Show test details
qa-use test info login
# List test runs
qa-use test runs --status failed
Plugin Shortcut:
/qa-use:test-run login
(Convenience shortcut for common test execution)
Quick set/unset for the typed variables: block on a single test —
useful when you don't want to round-trip the full YAML.
CLI Workflow:
# Local YAML file — Document API preserves comments + key order
qa-use test vars list qa-tests/login.yaml
qa-use test vars list qa-tests/login.yaml --json
qa-use test vars set qa-tests/login.yaml --key user --value alice
qa-use test vars set qa-tests/login.yaml --key url --value https://x \
--type url --lifetime all
qa-use test vars set qa-tests/login.yaml --key password --value hunter2 --sensitive
qa-use test vars set qa-tests/login.yaml --key password --sensitive # preserves stored value
qa-use test vars unset qa-tests/login.yaml --key user
# Remote --id fallback — exports YAML, mutates, re-imports (best-effort RMW)
qa-use test vars list --id <uuid>
qa-use test vars set --id <uuid> --key user --value alice
qa-use test vars unset --id <uuid> --key user
Rules:
<file> or --id, not both. Neither → exit 1.--id requires a full UUID (use qa-use test list --query <name> to find one).--key/--value writes simple form (key: value).
Any of --type/--lifetime/--context/--sensitive upgrades to full form
({ value, type, lifetime, context, is_sensitive }).--sensitive without
--value keeps the stored value. On a new key, that combination errors out.--json redaction: sensitive entries omit the value key entirely; the
is_sensitive: true flag is the redaction signal.No Plugin Shortcut - Use CLI commands directly.
CLI Workflow:
# Pull tests from cloud
qa-use test sync pull
# Push all local tests to cloud
qa-use test sync push --all
# Push specific test
qa-use test sync push --id <uuid>
# Force push (overwrite conflicts)
qa-use test sync push --force
# Compare local vs cloud
qa-use test diff login.yaml
Persist vs Sync:
A local test is synced when its YAML has both an id: and a version_hash:
(these are written back by test sync push/pull). --persist on test run
and qa-use test sync are related but not the same:
| Local file state | persist | What happens at run time |
|---|---|---|
| Non-synced | false (default) | Runs locally; nothing saved to cloud. Re-run with --persist or use test sync push to persist. |
| Non-synced | true | Test is uploaded as a new cloud entry after the run. Re-running like this creates duplicates — prefer test sync push once the file tracks the cloud id. |
| Synced | false | Runs against the cloud-tracked definition; no extra write. |
| Synced | true | Cloud definition is upserted (may overwrite newer cloud edits). Use test sync push/pull for explicit version control. |
Rule of thumb: test sync push/pull is the deliberate path. Reserve --persist
(or defaults.persist: true in .qa-use.json) for one-off uploads of brand-new
local tests.
No Plugin Shortcut - Use CLI commands directly
| Command | Description |
|---------|-------------|
| qa-use browser create | Create remote browser session |
| qa-use browser create <url> | Create session and navigate to URL |
| qa-use browser create --tunnel [auto\|on\|off] | Tunnel mode — default auto (localhost + remote API). --no-tunnel is sugar for off. |
| qa-use browser create --no-headless | Show browser window (paired with a local/tunnel browser) |
| qa-use browser create --viewport <size> | Set viewport: desktop, tablet, mobile |
| qa-use browser create --ws-url <url> | Connect to existing WebSocket browser |
| qa-use browser create --after-test-id <uuid> | Run a test first, then become interactive |
| qa-use browser create --var <key=value> | Override app config variables (repeatable) |
| qa-use browser list | List active sessions |
| qa-use browser status | Show current session details (app_url, recording_url, etc.) |
| qa-use browser status --list | Show all active sessions across processes |
| qa-use browser close | Close active session |
| qa-use browser close <id> | Close a specific session by id |
| qa-use doctor | Reap stale sessions/tunnels (dead PIDs) |
| qa-use tunnel ls | List active tunnels in the registry |
| qa-use tunnel start <url> --hold | Hold a public tunnel for external consumers |
Sessions auto-persist in ~/.qa-use.json. One active session = no -s flag needed. browser create returns immediately — the browser + tunnel run in a detached child. Use qa-use doctor if stale state accumulates.
| Command | Description |
|---------|-------------|
| qa-use browser goto <url> | Navigate to URL |
| qa-use browser back | Go back |
| qa-use browser forward | Go forward |
| qa-use browser reload | Reload page |
| Command | Description |
|---------|-------------|
| qa-use browser click <ref> | Click element by ref |
| qa-use browser click --text "Button" | Click by semantic description |
| qa-use browser fill <ref> "value" | Fill input field |
| qa-use browser type <ref> "text" | Type with delays (for autocomplete) |
| qa-use browser press <key> | Press key (e.g., Enter, Tab) |
| qa-use browser check <ref> | Check checkbox |
| qa-use browser uncheck <ref> | Uncheck checkbox |
| qa-use browser select <ref> "option" | Select dropdown option |
| qa-use browser hover <ref> | Hover over element |
| qa-use browser scroll down 500 | Scroll by pixels |
| qa-use browser scroll-into-view <ref> | Scroll element into view |
| qa-use browser drag <ref> --target <ref> | Drag element to target |
| qa-use browser mfa-totp [ref] <secret> | Generate TOTP code (optionally fill) |
| qa-use browser upload <ref> <file>... | Upload file(s) to input (base64-encoded, works remote & tunnel) |
| Command | Description |
|---------|-------------|
| qa-use browser snapshot | Get full ARIA tree with element refs (use only when diff output is insufficient) |
| qa-use browser url | Get current URL |
| qa-use browser screenshot | Save screenshot.png |
| qa-use browser screenshot file.png | Save to custom path |
| qa-use browser screenshot --base64 | Output base64 to stdout |
| qa-use browser evaluate <expression> | Execute JavaScript in browser context |
The snapshot-diff feature automatically displays DOM changes after each browser action:
+ prefix and green color — these refs are immediately usable~ prefix and yellow color, including attribute changes (+attrs: checked)- prefix and red color — do NOT use these refsDownloads: When an action triggers a file download (e.g., clicking a download link), the response includes download info: filename, size, and a presigned URL. Use qa-use browser downloads to list all downloads or --save <dir> to save them locally.
Use diff output to interact with newly appeared elements directly, without running a full snapshot first.
| Command | Description |
|---------|-------------|
| qa-use test run <name> | Run test by name |
| qa-use test run --all | Run all tests |
| qa-use test run <name> --tunnel [mode] | Override tunnel mode (auto, on, off). --no-tunnel is sugar for off. Bare --tunnel is sugar for on. |
| qa-use test run <name> --download | Download assets to /tmp/qa-use/downloads/ |
| qa-use test run <name> --var key=value | Override variable |
| qa-use test validate <name> | Validate test syntax |
| qa-use test list | List available tests |
| qa-use test info <name> | Show test details (steps, tags, description) |
| qa-use test info --id <uuid> | Show cloud test details by ID |
| qa-use test runs [name] | List test run history |
| qa-use test runs --id <uuid> | Filter runs by test ID |
| qa-use test runs --status failed | Filter runs by status |
| qa-use test init | Initialize test directory |
| qa-use test sync pull | Pull tests from cloud |
| qa-use test sync push --all | Push all local tests to cloud |
| qa-use test sync push --id <uuid> | Push specific test |
| qa-use test sync push --force | Push tests, overwriting conflicts |
| qa-use test diff <file> | Compare local vs cloud test |
| qa-use test schema [path] | View test definition schema |
qa-use api dynamically discovers operations from /api/v1/openapi.json and caches metadata locally for offline fallback.
| Command | Description |
|---------|-------------|
| qa-use api | Show help and available subcommands |
| qa-use api ls | List available /api/v1/* routes from OpenAPI |
| qa-use api ls --refresh | Force refresh OpenAPI cache |
| qa-use api ls --offline | Use cached OpenAPI metadata only |
| qa-use api info /api/v1/<route> | Show route details: parameters, request body, responses |
| qa-use api info /api/v1/<route> -X POST | Show info for specific HTTP method |
| qa-use api info /api/v1/<route> --json | Route info as JSON |
| qa-use api examples | Show usage examples |
| qa-use api openapi | Print OpenAPI spec URL |
| qa-use api openapi --raw | Dump full OpenAPI spec as JSON |
| qa-use api /api/v1/tests | Call endpoint (method inferred when possible) |
| qa-use api -X GET /api/v1/test-runs -f limit=5 | GET with query fields |
| qa-use api -X POST /api/v1/tests-actions/run --input body.json | POST with JSON body file |
| qa-use api -X GET /api/v1/test-runs/<id> | Fetch detail endpoint by ID |
No Plugin Shortcut - Use CLI commands directly.
Remote API checks are managed through first-class commands. They call the desplega.ai API directly; they do not run checks locally or create browser sessions.
| Command | Description |
|---------|-------------|
| qa-use check list --app-config <id> | List remote API checks |
| qa-use check info <check-id> | Inspect one check, including dependencies and captures |
| qa-use check create --name <name> --app-config <id> --config check.json | Create a check |
| qa-use check update <check-id> --depends-on <id> --alias <name> | Update dependency-chain metadata |
| qa-use check delete <check-id> --force | Delete a check |
| qa-use check-schedule list --app-config <id> | List remote check schedules |
| qa-use check-schedule create --name <name> --app-config <id> --cron "*/15 * * * *" --check <id> | Create an ordered schedule |
| qa-use check-schedule run <schedule-id> --var env=staging | Run a schedule as a remote variable-passing chain |
Common flow:
qa-use check create \
--name "Auth token" \
--app-config <app-config-id> \
--config auth-check.json \
--alias auth \
--capture token=$.access_token
qa-use check update <claim-check-id> \
--depends-on <auth-check-id> \
--alias claim
qa-use check-schedule create \
--name "Claims chain" \
--app-config <app-config-id> \
--cron "*/15 * * * *" \
--check <auth-check-id> \
--check <claim-check-id>
qa-use check-schedule run <schedule-id> --var env=staging --var claim_id=123
There is no standalone qa-use check run <check-id> command yet because the
public API currently documents schedule runs, not individual check-run actions.
Use qa-use api info /api/v1/checks -X POST for the current check payload
contract, and see references/checks.md for examples.
No Plugin Shortcut - Use CLI commands directly.
| Command | Description |
|---------|-------------|
| qa-use browser logs console | View console logs from session |
| qa-use browser logs console -s <id> | View logs from specific/closed session |
| qa-use browser logs network | View network request logs |
| qa-use browser logs network -s <id> | View network logs from specific session |
| qa-use browser downloads | List downloaded files from session |
| qa-use browser downloads --save <dir> | Save downloaded files to local directory |
| qa-use browser downloads --json | Output download info as JSON |
| Command | Description |
|---------|-------------|
| qa-use browser generate-test | Generate test YAML from recorded session |
| qa-use browser generate-test -s <id> | Generate from specific session |
| qa-use browser generate-test -n <name> | Specify test name |
| qa-use browser generate-test -o <path> | Specify output path |
| qa-use browser get-blocks | Get recorded interaction blocks (JSON) |
| Command | Description |
|---------|-------------|
| qa-use browser wait <ms> | Fixed wait |
| qa-use browser wait-for-selector ".class" | Wait for selector |
| qa-use browser wait-for-load | Wait for page load |
Use --var to override app config variables at runtime. Common variables:
| Variable | Description |
|----------|-------------|
| base_url | Base URL for the app (e.g., preview deployment URL) |
| login_url | Login page URL |
| login_username | Username/email for authentication |
| login_password | Password for authentication |
Example with ephemeral preview URL:
qa-use browser create --after-test-id <login-test-uuid> \
--var base_url=https://preview-123.example.com \
--var login_url=https://preview-123.example.com/auth/login
CLI Workflow:
# 1. Search for existing test
qa-use test list | grep "login"
# 2. Run test
qa-use test run login
# 3. Debug failures
qa-use browser logs console
Plugin Shortcut:
/qa-use:verify "login works with valid credentials"
(Wraps the above CLI workflow with AI-powered test discovery and analysis)
CLI Workflow:
# 1. Create session (auto-tunnels localhost targets)
qa-use browser create --no-headless
# 2. Navigate and interact
qa-use browser goto https://example.com
qa-use browser snapshot
qa-use browser click e1
qa-use browser fill e5 "test"
# 3. Generate test from blocks
qa-use browser get-blocks
qa-use browser generate-test -n "my_test"
# 4. Run test
qa-use test run my_test
Plugin Shortcut:
/qa-use:record start my_test
# ... perform interactions ...
/qa-use:record stop
CLI Workflow:
# Create session that runs login test first
qa-use browser create --after-test-id <login-test-uuid>
# Session now authenticated, explore
qa-use browser goto /dashboard
qa-use browser snapshot
Plugin Shortcut:
/qa-use:explore /dashboard
(Automatically handles auth detection and session creation)
CLI Workflow:
# 1. Open test file in editor
vim qa-tests/login.yaml
# 2. Validate syntax
qa-use test validate login
# 3. Run to verify
qa-use test run login
Plugin Shortcut:
/qa-use:record edit login
(AI-assisted editing with validation)
CLI Workflow:
# Create session and navigate
qa-use browser create --no-headless
qa-use browser goto https://evals.desplega.ai/checkboxes
# goto shows diff — initial page load shows all elements:
# Changes: 45 elements added
# + [e18] checkbox "I agree to the terms and conditions"
# + [e19] generic "I agree to the terms and conditions"
# ✅ Use ref from diff directly — no snapshot needed!
qa-use browser click e18
# Diff shows what changed:
# Changes: 5 elements added, 1 element modified
# + [e54] generic "Thanks for agreeing!"
# + [e55] link "Terms and Conditions"
# ~ [e18] checkbox "I agree to the terms and conditions"
# +attrs: active, checked
# ✅ Can click e55 directly from diff output — no snapshot needed!
qa-use browser click e55
# ❌ Need to find an element NOT in the diff? Now run snapshot:
qa-use browser snapshot
Key principle: Use diff output as your primary source of element refs after actions. Only fall back to snapshot when you need to find elements that weren't in the diff.
Benefits:
No Plugin Shortcut - Automatic feature in all browser commands
Environment Variables:
export QA_USE_API_KEY="your-api-key"
export QA_USE_REGION="us" # Optional: "us" or "auto"
Basic Test Execution:
# Run all tests
qa-use test run --all
# Run specific tag
qa-use test run --tag smoke
# Exit codes: 0 = pass, 1 = fail
name: QA Tests
on: [push, pull_request]
jobs:
test:
runs
<!-- Content truncated for initial SEO render. Open the source file tab for the full file. -->
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer