Did the app break,
or did the tests go stale?
razo gives every Playwright control a human name, so it knows which controls each test touched. Cross that with what the pull request changed, and a red build stops being a mystery: a regression to fix, or a deliberate change your tests haven't caught up with.
Free · no card required · the framework is MIT on GitHubPlaywright says
Error: locator('[data-testid="place-order"]')
timed out after 5000msrazo narrates what the test did
- Type "ada@example.com" into field "Email"
- Assert table "Cart items" has 2 rows
- Click button "Place order"
then reads the pull request
Intentional change
PR intentionally hides Place order and removes Mouse row; both tests assert the old express-checkout UI.↳ touched button "Place order", which this PR changed in tests/checkout-page.ts
How it works
Three steps to a verdict on every PR
Install razo
Name your controls, wire the reporter in your Playwright config.
npm i -D @razohq/razo
Upload from CI
One project token, two lines in your workflow.
npx razo-upload
Get the verdict
Every failing PR run gets a classified verdict as a comment. With no PR to read — a nightly, a local run — you still get the narrated story of what the test did, and a browsable history behind it.
The controls
You name the control once
Twenty controls, each taking the name you'd use out loud. That name is what makes the report readable by someone who never opens the code — and it's the same name razo later matches against the diff. Write your tests with plain Playwright calls and you get a stack trace like any other run.
await new Input(page, 'email', 'Email').fill('ada@example.com'); await new Table(page, 'items', 'Cart items').expectRowCount(2); await new Button(page, 'place-order', 'Place order').click();
razo writes the sentence
- Type "ada@example.com" into field "Email"
- Assert table "Cart items" has 2 rows
- Click button "Place order"
and records it
{
"action": "click",
"controlType": "button",
"name": "Place order",
"selector": "[data-testid=\"place-order\"]",
"status": "failed"
}The payoff
Matched against the diff
Because the artifact records the control by name, razo can line up what the test touched with what the pull request changed. That intersection is the whole verdict — and it needs a named control to exist at all.
The test touched
button "Place order"
This PR changed
- <button data-testid="place-order">+ <button data-testid="place-order" style="display:none">
Pricing
Start free, upgrade when it earns it
Free
Unlimited projects and runs · 30-day narrated history · 3 trial verdicts, read against your PR like the paid ones · unlimited BYO-key analyses via the OSS CLI. Even with no PR to read, every run leaves a report your whole team can follow.
Start freePro — $12/month
100 AI analyses per month · full history · a classified verdict on every failing PR, read against its diff · cancel anytime.
Upgrade in-app