Reports · Diagnostics · One language
Test APIs and browsers in one language.
Reports first, syntax second — a self-contained report.html, junit.xml, and teaching-quality diagnostics fall out of every run, no glue code between tools. Four pillars share one grammar — API, browser, load (validated against k6 on real contended workloads) and security scanning. Pre-1.0, not yet published.
checkout.tflw
test "user can check out" as shopper
let email = unique email
api POST /cart/checkout body { email: {email} }
expect status equals 201
capture body.orderId as orderId
log "order {orderId} placed, confirming delivery"
open "/orders/{orderId}"
click button "Confirm delivery"
expect text "Order confirmed" is visible
# same file, confirming the backend saw it too
api GET /orders/{orderId}
expect body.status equals "confirmed"
# and the same grammar covers load testing
test "checkout under load"
ramp to 50 users over 30s
api POST /cart/checkout body { id: "widget-1" }
expect status equals 201
threshold p95 duration is less than 800msReporting-first runtime
Every step is an event by construction — a self-contained report.html (full request/response detail), junit.xml, and results.json all fall out of the same event stream tflw run already emits, secrets redacted everywhere automatically.
Reporting & CI →Teaching-quality diagnostics
Source line + caret + "did you mean", stable TF0xx codes, a conservative unknown-variable checker pass — errors read like a compiler's, not a stack trace.
Running & debugging tests →One language for API, browser, load & security testing
UI steps share the same grammar as API steps, so a login → seed-via-API → drive-UI → assert-backend-state test stays one readable file instead of gluing two tools together. Load testing (ramp/hold/step/spike, thresholds) is validated within a few percent of k6 on real contended workloads — see the numbers. Security scanning runs inside the same test — hygiene, authorization, input handling and an active crawl — each behind a written authorized target declaration.
Start with functional testing →Real editor support
A real Language Server Protocol implementation (tflw lsp) powers the VS Code extension — diagnostics, hover, go-to-definition, autocomplete, rename, signature help, and semantic highlighting, live as you type.
Editor support →A local UI for the whole language
tflw ui reads a directory of .tflw files back as a page — what you have, what it is for, what happened when it last ran, and a form per statement that writes the language for you. It is local, needs no account, and keeps no state of its own — it reads files from disk and writes files back to disk, so closing it leaves exactly what git sees.
tflw ui →
Why tflw
Section titled “Why tflw”Compared to writing API tests by hand with fetch + a general-purpose test runner:
- Reporting is built in, not bolted on. Every step is an event by construction —
report.html,junit.xml, andresults.jsonall fall out of the same run, with secrets redacted everywhere automatically. No logging or capture code to write yourself. - Errors read like a compiler's, not a stack trace. Source line + caret + "did you mean",
stable
TF0xxcodes you can look up, a conservative unknown-variable checker pass. - One language, not several tools glued together. Sessions, retries, generated test data, and data-driven tables are grammar, not helper functions you maintain per project.
Compared to other dedicated tools: if you already have Karate working for your team, its
Java/Gherkin ecosystem and maturity are a real reason to stay. Hurl's single-file, no-runtime
.hurl scripts are a better fit for simple curl-replacement smoke checks than a full DSL.
On the browser side, Playwright and Cypress remain the right choice if browser automation is the whole job — tflw's browser steps run on Playwright under the hood, not instead of it. What tflw replaces is the glue: seeding state over the API, driving the UI, and asserting backend state afterward, without switching tools or writing a client by hand partway through a test.