Skip to content

Running & debugging tests

Terminal window
✓ health check (16 ms)
PASS 1/1 passed · env local · seed 1486355565 · now 2026-07-20T19:09:07.104Z · 17 ms
report: report/report.html

One line per test (✓/✗, name, duration), then a summary line (the badge, pass tally, the env that ran, the seed/now that generated any random/date values, total duration), then the report path.

The badge has four words, not two. PASS and FAIL are the run's verdict; ABORTED (Ctrl-C before a workload's planned duration elapsed) and INCONCLUSIVE (tflw's own generator saturated, so the numbers describe tflw rather than your system) mean the run reached no verdict — it neither passed nor failed, and the tally beside them is only what completed. Both print in the same red as FAIL, because the one thing such a run is not is green. report/results.json's ok says the same thing to a script (ok: false with failed: 0), and the exit code distinguishes them: 130 aborted, 3 inconclusive. A failing test also prints its failing step(s) indented underneath, live, the moment it fails — you don't have to wait for the whole suite or open the report to see what broke:

Terminal window
✗ health check
request failed: GET http://localhost:3001/health — fetch failed — connection refused; is the
service actually listening at that host:port?
✗ health check (12 ms)
✗ api GET /health
request failed: GET http://localhost:3001/health — fetch failed — connection refused; is the
service actually listening at that host:port?
FAIL 0/1 passed, 1 failed · env local · seed 868036364 · now 2026-07-20T19:09:56.501Z · 13 ms

Network failures name the likely cause instead of a bare fetch failed (see Config & environments for the cert/proxy variants of this).

By default only failing steps print. --verbose prints every step of every test as it runs, passing or not — useful when a test times out rather than fails outright, since you can see which step it's stuck on:

Terminal window
npx tflw run --verbose
Terminal window
health check
✓ GET http://localhost:3001/health → 200 (13ms)
✓ status to equal 200 (0ms)
✓ health check (14ms)

Re-running the whole suite to chase one failure is slow. Narrow to exactly what you're debugging:

Terminal window
npx tflw run --only "health check" # one test, by its exact declared name
npx tflw run --tag smoke # every test carrying @smoke (comma-separated OR)
npx tflw run --only "health check" --tag smoke # composes as AND
npx tflw run --failed # only the tests whose last run failed
npx tflw run --bail # stop at the first failure — don't wait for the rest

--failed is the fast loop for fixing a batch of failures one at a time: run once, fix the first thing, tflw run --failed again to check just what's still red — the failure set narrows every time until an all-green --failed run falls back to the full suite (nothing left to replay). --bail is the opposite direction: stop as soon as anything breaks, so a broken early assumption doesn't waste time running everything downstream of it.

Any random/date-family generator value and the run's notion of "now" are derived from a seed that's different every run by default — printed in the summary line (seed 868036364 · now 2026-07-20T...). Pin both to get byte-for-byte the same generated values back:

Terminal window
npx tflw run --seed 868036364 --now 2026-07-20T19:09:56.501Z

Handy for turning "it failed once in CI and I can't repro it" into a reliable local repro — copy the seed/now straight out of the CI log's summary line.

tflw watch — live headed re-run while you write a browser test

Section titled “tflw watch — live headed re-run while you write a browser test”
Terminal window
npx tflw watch checkout.tflw

Opens one real, visible browser window and re-runs checkout.tflw headed every time you save it — the same browser window stays open across re-runs (only its page navigates fresh), and the same seed is reused for the whole session so flakiness from generated-data drift doesn't creep in while you iterate. Saving tflw.config instead re-runs the whole suite. Ctrl+C closes the browser and stops. --env/--seed/--browser/--no-color all work the same as run's.

tflw pick — a verified locator for a real element

Section titled “tflw pick — a verified locator for a real element”
Terminal window
npx tflw pick http://localhost:3000/checkout

Opens a real, visible browser at the given URL (absolute — no tflw.config involved) and prints one verified locator every time you click an element — not a best guess, the actual locator tflw's own resolver confirmed resolves to exactly that element, in the same button "…"/ field "…"/text "…"/css "…" form a .tflw file uses. Clicking never navigates the page (picking is inert), so you can click around a real, already-logged-in app and paste locators straight into a test as you go. Runs until the window is closed or Ctrl+C — including while the browser is still opening, which on a cold machine is the wait you are most likely to want out of.

tflw check runs the same parse + full checker pipeline run does, with no execution and no secrets required — catches typos and unknown-matcher/unknown-variable mistakes in editor-quality diagnostics, in milliseconds, before any request goes out:

Terminal window
npx tflw check example.tflw
Terminal window
error[TF014]: unknown matcher `eq`
--> example.tflw:3:17
|
3 | expect status eq 200
| ^^
|
= help: expected one of: equals, contains, matches, is …, has …

Source line + caret + "did you mean" + a stable TF0xx code you can look up in the diagnostic codes reference — the same diagnostics VS Code shows live as you type (see Editor support), available from the CLI for non-editor workflows (a pre-commit hook, CI lint step) too.

The terminal only ever shows a summary. report/report.html (written on every run, pass or fail) has the complete picture for every step: full request/response — method, URL, headers, body, status, timing — with secrets redacted the same way everywhere. Open it in a browser first when a failure isn't self-explanatory from the terminal output alone. --evidence <level> trims how much of that detail gets captured (full/headers only/none; the CLI flag keeps the hyphen, --evidence headers-only, since a shell argument is not lexed by tflw) — see CI, reporting & safety.

tflw ui shows a run beside the source that produced it, so a red step and the line that wrote it are on one screen. A failed browser attempt's trace opens in Playwright's own viewer without leaving the page, from the playwright-core the project already resolves. Everything this chapter describes still applies — the page renders the run, it does not re-word it. See The page.

log "created order {orderId}" emits one line into the run log and into the report, with variables interpolated. An optional level comes first — log warn "retrying against the stale replica" — and what actually reaches the terminal depends on the log level key in tflw.config.

It is the debugging tool that survives into CI: a line written this way is in report.html beside the step that wrote it, which a console.log in a use module is not. Secrets are redacted here the same way they are everywhere else, so interpolating a captured token prints the redaction rather than the token.