OPEN SOURCE · MIT

Visual diffs that say what changed.

Each screenshot is stored with the page's DOM and computed styles. A diff is reported as a kind of change, with the element it belongs to: "style-only change on button.cta", "likely render noise", "layout shift". MIT-licensed and free to use, including commercially. Runs on your machine and in CI; no account.

GitHub repository Example report
npm i -D @testivai/witness-playwright
Playwright · WebdriverIO · Selenium · Python · Java · Ruby MIT licensed
Example

An example report.

Three changes to the example site: a layout shift from an inserted banner, a style-only change to the buttons, a missing product card.

TestivAI report from the example repo: a layout-shift verdict on home, a style-only-change verdict on buttons, and a structural-change verdict on products, each with baseline, heatmap diff and current screenshots

Rebuilt by the example repo's CI on every push.

Playwright's toHaveScreenshot()

When a pixel count
is not the answer.

Fine when a pixel count answers the question. Where it does not:

"0.4% of pixels differ"
A count says that something changed. TestivAI says what: "style-only change, 34 elements restyled", with each region's selector.
One line added, forty snapshots red
A pixel differ reports forty failures. TestivAI reports one page shift, passed within your tolerance.
Approving with a local flag
--update-snapshots rewrites every snapshot the run touched. TestivAI approves from a reviewed PR comment, write access verified.
A deleted test stops guarding its page
TestivAI fails the run when a baseline received no capture.

Full comparison, including what the built-in does better →

Open Source

What the packages
include.

Stabilized captures
Animations frozen, caret hidden, fonts loaded, then a wait until the page stops changing (150 ms quiet, 5 s cap). Default in every language.
Tolerances
maxDiffPercent or a pixel budget. Optional auto-pass for likely render noise, labeled in the report.
DOM- and style-aware noise detection
Pixels changed, DOM and styles identical, diff small and scattered: "likely render noise", still shown. A diff in one area is not called noise. Only styles changed: "style-only change on button.cta". Styles not recorded: "styles not compared". Browser or OS changed, page unchanged: "rendering drift". A font or stylesheet failed to load (Playwright, testivai witness): "unhealthy capture", never noise.
Approvals in the PR
The Action posts the diff on the pull request; a second action publishes each run's report to GitHub Pages, linked from the comment. Comment /testivai approve: baselines are committed back, and the reply names the run you approved.
A neutral check
A visual change is visible in the PR and does not block the merge. fail-on-diff makes it a hard gate.
Sharded CI
Shards capture, one job compares. Same two environment variables in Playwright, Selenium, Python, Java and Ruby. Recipe →
All packages →
How It Works

What a comparison
checks.

Pixels, DOM, computed styles, and which element owns each changed region. One verdict per snapshot.

DETERMINISTIC
Local comparison
Pixel, DOM and style checks run on your machine. Mask or ignore dynamic regions by selector; tolerances per snapshot.
OPTIONAL
Your own AI model
Connect the coding agent you already use (Claude Code, Cursor, Copilot) through MCP. explain_snapshot hands it the evidence. No hosted AI, no API key. Example payload →
Get Started

Add it to
an existing suite.

One capture call in the tests you already have.

Install
npm i -D @testivai/witness-playwright
Wire & capture
Register the reporter, then call witness() in any test.
reporter: [['list'], ['@testivai/witness-playwright/reporter']]

import { witness } from '@testivai/witness-playwright';
await witness(page, testInfo, 'home');
Run
The first run writes baselines; commit them. Later runs diff and write the report.
npx playwright test
open visual-report/index.html

No test suite: npx testivai witness http://localhost:3000 captures each page it finds. For AI-built apps →

Adapters

Supported
frameworks.

Playwright — TypeScript / JavaScript
Reporter and capture adapter. Mobile web through device emulation, with baselines per device. Mobile web guide →
Python — Playwright / Selenium
playwright-python and Selenium, with a pytest plugin. Computed styles on the Selenium adapter only.
pip install "testivai[playwright]"
Java — Playwright / Selenium
JUnit 5 extension for playwright-java, plus a Selenium adapter. Not on Maven Central yet; build from the repo.
Experimental
Selenium — JS / Python / Java
Native WebDriver adapters, any test runner. Full-page captures on Chrome and Edge, and on Firefox in Python and Java. vs. a hand-rolled setup →
Java experimental
Ruby — Capybara / RSpec
Gem for Capybara, RSpec and Cucumber, through the browser the suite already drives. Ruby guide →
gem "testivai"
WebdriverIO
WDIO service, including the style check.
CLI sidecar
Browser remote debugging for Cypress, Puppeteer, Robot Framework and custom setups.
Experimental
AI coding agents
MCP server for Claude Code, Cursor and any MCP client. Approvals from Claude Code ask you to confirm. The testivai-report plugin shows the whole report in Claude Code.
@testivai/mcp
OPEN SOURCE

Try it on one test.

MIT-licensed, free to use. Start with one page.

GitHub repository → Docs