Running & debugging tests
What a run actually prints
Section titled “What a run actually prints” ✓ health check (16 ms)
PASS 1/1 passed · env local · seed 1486355565 · now 2026-07-20T19:09:07.104Z · 17 ms
report: report/report.htmlOne 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:
✗ 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 msNetwork failures name the likely cause instead of a bare fetch failed (see
Config & environments for the cert/proxy variants of this).
--verbose — one line per step
Section titled “--verbose — one line per step”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:
npx tflw run --verbosehealth check ✓ GET http://localhost:3001/health → 200 (13ms) ✓ status to equal 200 (0ms)✓ health check (14ms)Isolate a test
Section titled “Isolate a test”Re-running the whole suite to chase one failure is slow. Narrow to exactly what you're debugging:
npx tflw run --only "health check" # one test, by its exact declared namenpx tflw run --tag smoke # every test carrying @smoke (comma-separated OR)npx tflw run --only "health check" --tag smoke # composes as ANDnpx tflw run --failed # only the tests whose last run failednpx 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.
Reproduce a failure exactly
Section titled “Reproduce a failure exactly”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:
npx tflw run --seed 868036364 --now 2026-07-20T19:09:56.501ZHandy 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”npx tflw watch checkout.tflwOpens 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”npx tflw pick http://localhost:3000/checkoutOpens 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.
Lint before you run — tflw check
Section titled “Lint before you run — tflw check”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:
npx tflw check example.tflwerror[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 full picture — report.html
Section titled “The full picture — report.html”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.
Reading a failure in the page
Section titled “Reading a failure in the page”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.
Printing from inside a test — log
Section titled “Printing from inside a test — log”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.