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 GitHub

Playwright says

Error: locator('[data-testid="place-order"]')
timed out after 5000ms

razo narrates what the test did

  1. Type "ada@example.com" into field "Email"
  2. Assert table "Cart items" has 2 rows
  3. 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

Read the bot's comment on the pull request →

How it works

Three steps to a verdict on every PR

1

Install razo

Name your controls, wire the reporter in your Playwright config.

npm i -D @razohq/razo
2

Upload from CI

One project token, two lines in your workflow.

npx razo-upload
3

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

  1. Type "ada@example.com" into field "Email"
  2. Assert table "Cart items" has 2 rows
  3. 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 free