This documentation is available as Markdown. For the complete index, see llms.txt. Skip to content

CLI — web

For the complete documentation index, see llms.txt

The unotest-web CLI. Run any command with npx @unotest/web <command> — works with npm, pnpm, yarn or bun.

init

Bootstrap the unotest/ layout, config, and MCP wiring for your editor. Re-run anytime — it never overwrites your edits.

Terminal window
npx @unotest/web init [target] [--browser system|bundled|none]
  • --browser — Skip the interactive browser prompt: use system Chrome/Edge, bundled Chromium, or none.

Example

Terminal window
npx @unotest/web init --browser system

e2e

Run a single scenario. name resolves to unotest/e2e/<name>.js. Unknown names suggest close matches from unotest/e2e/**.

Terminal window
npx @unotest/web e2e <name> [--env <name>] [--debug] [--break line:col]
  • --env <name> — Named environment: layers unotest/.env.<name> / .secrets.<name> over the base files (same as UNOTEST_ENV=<name>).
  • --debug — Pause on breakpoints (.debugger.json) and on failure.
  • --break line:col[,line:col] — Override breakpoints (comma-separated, no spaces).
  • --scheduled — Run the test the way its schedules entry does: in the entry’s env (unless you set one) and with its prepare command first, inside the same run slot — the per-test mirror of collection --scheduled. A failed prepare exits 94. --scheduled=<index> pins the entry (zero-based position in npx @unotest/web schedules).
  • UNOTEST_STEP_SCREENSHOTS=1 — Env: capture a PNG after every step into the run’s screenshot/ directory; the viewer shows each one inline when you click its step row.
  • UNOTEST_BROWSER=<engine> — Env: run on chromium / firefox / webkit, overriding the first entry of browsers in the config — the knob a CI browser matrix sets. Any other value is an error.
  • UNOTEST_BROWSER_CHANNEL=<channel> — Env: chrome / msedge / chrome-beta / bundled, overriding channel in the config. The config travels with the suite; this stays on the machine — a box sets bundled for every run.

Example

Terminal window
npx @unotest/web e2e auth/login --env staging

collection

Run a collection (a named set of scenarios), e.g. smoke or regress.

Terminal window
npx @unotest/web collection <name> [--env <name>] [--workers=N] [--bail] [--headed]
  • --env <name> — Named environment: layers unotest/.env.<name> / .secrets.<name> over the base files (same as UNOTEST_ENV=<name>).
  • --workers=N — Run N scenarios in parallel. Overrides the manifest’s workers: for this run; without either, runs are serial.
  • --bail — Stop after the first failure.
  • --headed — Run with a visible browser.
  • --scheduled — Run it the way its schedules entry does: in the entry’s env (unless you set one) and with its prepare command first, inside the same run slot. A failed prepare exits 94 — the environment was not prepared and nothing was tested, which is a different alert from a red suite (exit 1). A collection listed in schedules more than once needs --scheduled=<index> (zero-based).
  • --no-prepare — Skip the collection’s prepare: for this run. For a run against an environment somebody else already prepared — a CI check of a deployment, where reseeding would test the seed rather than the deployment.
  • UNOTEST_RUNS_RETENTION_DAYS=N — Env: how long finished runs are kept, in whole days (default 180). Older runs are swept at the end of a collection run; a failed sweep never changes the exit code.

Example

Terminal window
npx @unotest/web collection smoke --workers=4

author

Open a live authoring session: a headed browser stays open with no test running, and the agent attaches to it over MCP. You and the agent build the draft scenario together on the same page. Stop with abort or Ctrl-C.

Terminal window
npx @unotest/web author <feature>/<name>

Example

Terminal window
npx @unotest/web author checkout/apply-coupon

viewer

Open the local IDE-style viewer (HTTP + WebSocket). No cloud, no account.

Terminal window
npx @unotest/web viewer
  • UNOTEST_VIEWER_NO_OPEN=1 — Env: start the server without auto-opening a browser.
  • UNOTEST_VIEWER_HOST=… — Env: bind host (default localhost).
  • UNOTEST_VIEWER_PORT=… — Env: bind port (default: the OS picks a free one).

Example

Terminal window
npx @unotest/web viewer

app-server

Start the app under test — the webServer block of your config — and hold it until Ctrl-C. Runs then reuse it instead of each spawning their own copy, which is what you want while iterating locally.

Terminal window
npx @unotest/web app-server

Example

Terminal window
npx @unotest/web app-server

runs migrate

Retire history left in the old flat unotest/.runs/<runId>/ layout. It is deleted by default — one layout, one frame format, no second resolver kept forever. This runs automatically on the first run after upgrading, so the command exists to let you look (and choose) BEFORE that happens. Set UNOTEST_RUNS_LEGACY=keep to re-file the old runs into date shards instead; either way the amount is printed before anything is touched.

Terminal window
npx @unotest/web runs migrate [--dry-run]
  • --dry-run — Report how many runs and how many GB are affected, change nothing.
  • UNOTEST_RUNS_LEGACY=keep — Env: re-file old runs into date shards instead of deleting them. The price is a mixed frame format on that machine.

Example

Terminal window
npx @unotest/web runs migrate --dry-run

bundle push

Pack this project’s suite — the unotest/ package, with its config, its package.json and its lockfile — into one bundle and upload it to a box. Your application’s own manifest and dependencies stay behind: a box installs the suite alone, which is why it can run tests for a project whose dependency graph it could not resolve in the first place. This is how tests reach a box: a box holds no credential to your repository and never pulls anything, so nothing is on it that you did not push. Uncommitted work is included and recorded as dirty, which the box’s viewer labels wip — pushing a fix you have not committed yet is the fast loop this exists for. What would fail on the box is refused here instead: a scenario that reads a file outside unotest/ (MODULE_NOT_FOUND at 03:00 otherwise), a missing unotest/package-lock.json (npm ci refuses to run without one), a missing @unotest/web pin, a symlink. Secrets never travel — unotest/.env* and .secrets* are excluded, because environment values belong to the environment and are injected over the bundle when it runs. The bundle’s id is a hash of its content, so re-pushing an unchanged suite is a no-op the box answers instantly.

Terminal window
npx @unotest/web bundle push [--box <url>] [--token <token>] [--out <file>] [--dry-run] [--json] [--run --env <name> [--collection <name>] [--pr <number>]]
  • --box <url> — Box address, e.g. https://tests.example.com. Defaults to $UNOTEST_BOX_URL.
  • --token <token> — Upload token, issued per project on the box. Defaults to $UNOTEST_BOX_TOKEN.
  • --out <file> — Also write the packed bundle to a file — a CI artifact, or something to look inside with tar -tzf.
  • --dry-run — Pack and check, upload nothing. Exits 2 if anything would be refused.
  • --json — Print { bundleId, status, runs? } instead of prose, for a CI job that wants the run ids.
  • --run — Ask the box to run this bundle once it is stored — what a CI job does after a deploy. The box answers as soon as the runs are queued (a suite takes minutes; an HTTP request held open that long is a timeout, not a result), so the command returns without waiting for a verdict. Watch them in the viewer, or let the box report checks back to GitHub.
  • --env <name> — Which environment on the box to run in. Required with --run: which environments exist is the box operator’s decision, not something the suite can know.
  • --collection <name> — Collection to run; repeat the flag for several. Without it the box runs whatever that environment’s schedules name — “run what this environment normally runs”.
  • --pr <number> — Pull request this push belongs to. The box shows it in the queue, and a newer push of the same pull request withdraws the older runs that are still waiting — three pushes in five minutes cost one suite, not three.

Example

Terminal window
npx @unotest/web bundle push --run --env test --collection smoke

env push

Send an environment’s values to a box, from the same files a local run of it reads: unotest/.env, .env.<env>, .secrets, .secrets.<env>, later files winning. .env* become the box environment’s variables, .secrets* its secrets, APP_BASE_URL its target (the URL every run’s baseUrl auto-wires from). UNOTEST_* and empty values are not sent and listed as skipped — they configure the runner on your machine. The push REPLACES the box’s layers: a name your files no longer carry is removed there, and the command says which. Values never print, names do. The token is the project token bundle push uses, minted on the box with --values; a token without that scope is refused. Admins see and change the same values on the box’s admin page.

Terminal window
npx @unotest/web env push <env> [--box <url>] [--token <token>] [--box-env <name>] [--env-file <file>]... [--secrets-file <file>]... [--dry-run] [--json]
  • --box <url> — Box address. Defaults to $UNOTEST_BOX_URL.
  • --token <token> — Project token minted with --values on the box. Defaults to $UNOTEST_BOX_TOKEN.
  • --box-env <name> — The environment’s name on the box, when it differs from the local one.
  • --env-file <file> — Read variables from this file instead of unotest/.env + .env.<env>. Repeat for layers; later files win.
  • --secrets-file <file> — Same for secrets, instead of .secrets + .secrets.<env>.
  • --dry-run — Read and list what would be sent (names only), send nothing.
  • --json — Print the box’s answer — the names it now holds and the names it removed — as JSON.

Example

Terminal window
npx @unotest/web env push dev

env set

Set one value of a box environment. The value is read from stdin, never from the command line — an argument ends up in the shell history and in ps. --secret makes it a secret: injected into runs, never printed by the CLI — on the box an admin can reveal or copy it on the environment’s values page, and every look is audited; the name APP_BASE_URL is always the environment’s target. UNOTEST_* names are allowed here, unlike in env push — this is how an operator overrides UNOTEST_BROWSER_CHANNEL on purpose.

Terminal window
printf '%s' "$VALUE" | npx @unotest/web env set <env> <NAME> [--secret] [--box <url>] [--token <token>] [--box-env <name>] [--json]
  • --secret — Store it as a secret: injected into runs, never printed by the CLI; an admin can reveal it on the box’s values page, every look audited.
  • --box-env <name> — The environment’s name on the box, when it differs from the local one.

Example

Terminal window
printf '%s' "$GEMINI_API_KEY" | npx @unotest/web env set dev GEMINI_API_KEY --secret

env rm

Remove one value from a box environment. --secret names a secret; APP_BASE_URL puts the environment back on the target its box configuration names.

Terminal window
npx @unotest/web env rm <env> <NAME> [--secret] [--box <url>] [--token <token>] [--box-env <name>] [--json]

Example

Terminal window
npx @unotest/web env rm dev STALE_FLAG

box

Read a box’s results back — what ran, what failed, and the artifacts to look at — without a browser and without ssh. Reading uses a PERSONAL read token (UNOTEST_BOX_READ_TOKEN) that you mint for yourself on the box’s guard: it is read-only whatever your role there, and it is not the project token that pushes bundles. The address comes from UNOTEST_BOX_URL, and both are read from unotest/.env and unotest/.secrets as well as the shell, so an agent whose MCP server was started by an editor is configured by the project’s own files.

box envs lists the environments the token may look at, as <project>/<environment> — the name every other subcommand wants in --env. box runs lists recent runs, newest first. box run <id> explains one run: its status, the failure that ended it, the results of soft steps with their tags, and the judge’s verdicts; it exits 1 when the run did not pass. box queue shows who is running and who is waiting. box screenshot <id> saves one frame the run captured.

box run --download also fetches the run’s *.unotest.zip into .unotest/box/ and unpacks its failure bundle into .unotest/failures/, which is where list_failures, get_failure_* and agent_fix already look — so a run that failed on a box is debugged with the same commands as one that failed locally.

Terminal window
npx @unotest/web box envs
npx @unotest/web box runs --env <project>/<environment> [--latest] [--running] [--scenario <ref>] [--limit <n>]
npx @unotest/web box run <runId> --env <project>/<environment> [--download] [--dir <path>] [--no-screenshots]
npx @unotest/web box queue --env <project>/<environment>
npx @unotest/web box screenshot <runId> --env <project>/<environment> [--step <n>] [--out <file>]
  • --env <project>/<environment> — Which environment on the box. A read token says who you are, not where to look, so every subcommand but box envs needs it.
  • --latest — One line per scenario: its last run, and the length of its current failing streak — the answer a single status cannot give.
  • --running — Only runs in flight right now.
  • --scenario <ref> — Only this scenario’s runs, served from its own index.
  • --limit <n> — How many runs to list (default 20).
  • --download — Fetch the run as a zip (into .unotest/box/) and unpack its failure bundle where the failure tools read it.
  • --dir <path> — Where the zip goes. Default .unotest/box.
  • --no-screenshots — Ask the BOX to leave the run’s step frames out of the zip. They are usually most of the bytes, and this is what makes the download smaller — not just the disk.
  • --step <n> — Which frame to save, 1-based, in capture order. Default: the last one.
  • --out <file> — Where to write the PNG.
  • --box <url> — Box address. Defaults to $UNOTEST_BOX_URL, then unotest/.env.
  • --token <token> — Read token. Defaults to $UNOTEST_BOX_READ_TOKEN, then unotest/.secrets.
  • --json — Print the box’s answer as JSON instead of a table.

Example

Terminal window
npx @unotest/web box run checkout-mqf3pwr1 --env acme/test --download

schedules

Print what this project runs on its own: the schedules of unotest.config merged with unotest/schedules.yaml — the registry the viewer writes when you schedule a test or a collection from the tree (the file wins on a duplicate target). An entry names a collection or a single test, and travels with the tests it runs. Nothing in the CLI executes them: cron is run by the box or the CI that hosts your runs, and locally these are declarations. --json prints the machine shape a scheduler reads back.

Terminal window
npx @unotest/web schedules [--json]
  • --json — Print the entries as JSON instead of a table.
  • --check — Exit non-zero when any entry uses a cron expression outside the previewable subset (such entries are marked not previewable — an executor may drop it in the table). For a pre-push hook or a CI gate: a hand-written expression the executor’s parser refuses would otherwise be dropped silently on the box.

Example

Terminal window
npx @unotest/web schedules

lint

Static-check scenarios and helpers. Exits non-zero on errors.

Terminal window
npx @unotest/web lint [paths...]

Example

Terminal window
npx @unotest/web lint

types

Refresh editor typings: create unotest/jsconfig.json if missing and regenerate .unotest/types/env.d.ts from your unotest/.env / .secrets keys (names only, never values). Also runs automatically on lint and e2e.

Terminal window
npx @unotest/web types

install-chromium

Download Playwright’s bundled Chromium (~150 MB). Not needed if you chose system Chrome/Edge.

Terminal window
npx @unotest/web install-chromium

mcp

Run as an MCP stdio server. This is what your editor launches automatically; you rarely run it by hand.

Terminal window
npx @unotest/web mcp