Diagnostics
In normal operation the CLI prints a single actionable line — no stack traces. For full diagnostics (JSONL log + per-call artifacts), set:
cd unotest && UNOTEST_DEBUG=1 npx @unotest/web e2e <name>The JSONL lands in unotest/.debug/<pid>-<start-time>/log.jsonl (rotated
as log-001.jsonl, …, with index.jsonl and one folder per tool call
beside it); stdout stays as it was. The log is layered (tool, step,
resolver, driver, refs, snapshot, network); the step layer
carries enter / exit events per
scenario statement (file, line, column, the enclosing step("…") label),
so every driver action below it says which statement it happened under.
Common issues
astro: not found/ command missing — install dependencies first (npm install).- No MCP tools in the editor — re-run
init, then reload the editor so it re-reads.mcp.json. - Selector keeps drifting — switch to a stable selector
(
getByTestId/getByRole). - A JS global in a scenario — for time and waiting a scenario has
steps of its own:
Date.now()→clockFreeze/clockTick/clockRun, ornowMs();setTimeout(…)→pause(ms)orwaitUntil(() => …). Another framework’s spelling (clock.freeze(…),page.waitForTimeout(…)) is answered with the name to use here. run_testanswersspawn_failed— the runner child died before it wrote its firstruntime.json; the reason carries its exit code and the last lines ofspawn.stderr.login the run’s directory. A runner that is alive is waited for (up to 60 s; a run waiting in the queue is reported as queued instead), so this is a real start failure — read the stderr tail.run_testanswersconfig_error— the runner refused the scenario before its first step because the judge it calls withassertJudgecannot answer. Nothing was driven; the reason carries the runner’s stderr with what to fix (a missingUNOTEST_JUDGE_*variable, a judge service that is not running). Fix the setup, not the launch. The judge guide lists the variables.- A run hangs after a failed step — the page’s main thread is busy (a
script in an endless loop), and reading it for the failure bundle waits
for an answer that never comes. Since 0.49.1 each read gets 15 s; the
test stays red with its own error, and the output adds
the page gave no answer within 15s after the failed step. Find what keeps the page busy. A DSLevaluate(js)on such a page fails its step after the action timeout (defaultTimeoutMs) withthe page gave no answer; a script that has started runs as long as it needs, within the test’s time budget. - Flaky tab/iframe timing — wait for the tab or the content, not for
a time:
waitForPage()takes the tab a click opened and makes it active; in a frame,enterFrame(loc)and then awaitFor…on what it shows.pause(ms)with a// reason:comment is the last resort. - iOS: “command not found” on
xcrun— you’re not on macOS or missing Xcode CLI tools. iOS is macOS-only.
Known limitations
- Network HAR & video aren’t captured yet (failure bundle tier 3). Use screenshot + console + semantic DOM + trace.
dragAndDrop/ clipboard paste use synthetic events, not native HTML5DragEvent/ClipboardEvent. For apps that require native events, dispatch viaevaluate().- The viewer currently drives web only. Its runner architecture is multi-runner-ready, but mobile isn’t wired into it yet.
For agent-facing rules, see the Authoring guide.