Getting Started
Add visual regression testing to your test suite in under 5 minutes — fully local, no account needed.
Prerequisites
- Node.js 18+ (Node 20+ recommended)
- A test suite that drives a real browser (Playwright, Selenium, WebdriverIO, Cypress, Puppeteer, etc.)
Path A — Dedicated adapters (recommended)
Most suites have a first-class adapter today: Playwright, WebdriverIO, Selenium (JavaScript, Python, Java), and Ruby (Capybara / RSpec). They use the framework's native screenshot APIs — no CLI wrapper, no Chrome remote debugging port, no race conditions.
Playwright
The Playwright adapter integrates as a reporter. Just install and add a capture call.
1. Install
npm install -D @testivai/witness-playwright @playwright/test
npx playwright install chromium
2. (Optional) Customize settings
The reporter compares locally and writes an HTML report with zero configuration — no account, no API key, nothing uploaded.
If you want to tune tolerances or change the report output directory, create .testivai/config.json at your project root:
{
"threshold": 0.1,
"reportDir": "visual-report",
"autoOpen": false
}
Optional tolerance and capture settings (all have safe defaults):
| Field | Default | What it does |
|---|---|---|
maxDiffPercent | 0 | Diffs at or below this percentage report as passed — your team's tolerance dial |
maxDiffPixels | unset | Absolute variant: pass when changed-pixel count is at or below this |
noiseAutoPass | false | Auto-pass likely render noise, up to noiseMaxDiffPercent: only diffs whose DOM is structurally identical (dom.noiseHint) and whose computed styles were compared and match (dom.styleCheck: "match"). It never passes a diff whose styles were not compared ("unavailable") or changed ("mismatch"), nor one concentrated in one area (every region within a box covering at most a third of the page). Changed in this release: @testivai/witness 2.1.1 and earlier also passed diffs whose styles were not compared, and diffs concentrated in one area |
noiseMaxDiffPercent | 1 | Upper bound (diff %) for noiseAutoPass |
stabilize | true | Freeze animations/transitions, hide the caret, and wait for web fonts before every capture |
ignoreSelectors | [] | Elements hidden (visibility: hidden) during capture — timestamps, ads, live widgets |
mask | [] | Areas excluded from the pixel diff and hatched in the report — selectors or geometric regions (details) |
diffRegions | {minSize: 10, mergeDistance: 12} | Diff clustering tunables: smallest listed region + merge gap (details) |
shiftTolerance | unset | Pass diffs where every changed region is a pure element shift of at most N px per axis — content moved, nothing changed (needs element maps on both sides) |
volatileAttributes | [] | Attributes whose value is ignored by the DOM diff (presence still counts) |
mcpApprovalPrompt | true | @testivai/mcp: the approve tools ask a person on every call. false lets them follow your client's own permission settings |
baselinesDir | .testivai/baselines | Where baselines live; supports a {platform} token for per-OS baselines |
failOnDiff | false | Exit non-zero on changes without passing --fail-on-diff |
failOnMissing | true | Exit 3 when a committed baseline receives no capture (silent coverage loss) |
shareUploadCommand | unset | Command template run by report --share to push share.html to your own storage |
Crawler-only fields, used by testivai witness <url>: pages, maxPages (default 10), viewport (default 1280×800).
The full list with types lives in LocalConfig — see
packages/witness/src/config/local-config.ts.
Nothing auto-passes until you set one of these criteria. Auto-passed snapshots keep their diff image and are labeled in the report and in results.json (autoPassed: "threshold" | "noise" | "shift"), so tolerance never hides information — it just stops demanding review for changes you've declared acceptable.
3. Add the reporter
In playwright.config.ts:
import { defineConfig } from '@playwright/test';
export default defineConfig({
reporter: [
['list'],
['@testivai/witness-playwright/reporter'],
],
});
4. Add a capture call
import { test } from '@playwright/test';
import { witness } from '@testivai/witness-playwright';
test('homepage looks correct', async ({ page }, testInfo) => {
await page.goto('http://localhost:3000');
await witness(page, testInfo, 'homepage');
});
5. Run
npx playwright test
First run: baselines are created under .testivai/baselines/<name>/screenshot.png and the report shows New: N. Commit them to git.
Later runs: screenshots are compared, the HTML report opens at visual-report/index.html, and results.json is produced.
WebdriverIO
npm install -D @testivai/witness @testivai/witness-webdriverio
Create .testivai/config.json at your project root — the WebdriverIO service
only writes a report when it finds this file:
{}
Add the service to wdio.conf.ts:
import { TestivaiService } from '@testivai/witness-webdriverio/service';
export const config = {
services: [[TestivaiService, {}]],
};
Capture inside a test:
import { testivai } from '@testivai/witness-webdriverio';
it('homepage looks correct', async () => {
await browser.url('http://localhost:3000');
await testivai.witness(browser, 'homepage');
});
Same .testivai/baselines/ layout, same HTML report, same approval workflow as the Playwright lane.
→ See the full WebdriverIO quickstart for service options.
Ruby (Capybara / RSpec)
# Gemfile
gem "testivai", group: :test
require "testivai"
RSpec.describe "Homepage", type: :feature, js: true do
it "looks right" do
visit "/"
Testivai.witness(page, "homepage")
end
end
Run your suite exactly as you do today, then compare:
bundle exec rspec
npx testivai report
→ See the full Ruby quickstart for options and driver notes.
Selenium, Python, Java
Native adapters share the same baselines and report — see Selenium, Python, and Java.
Path B — Other Frameworks (experimental)
For Cypress, Puppeteer, Robot Framework and similar, use the framework-agnostic CLI from @testivai/witness. It wraps your test command and captures via Chrome's DevTools Protocol.
This sidecar mode is labeled experimental — launch coordination across frameworks is brittle. For Playwright and WebdriverIO, prefer the dedicated adapters above. See the sidecar caveats for the full picture, and community adapter contract if you'd like to write a proper adapter for your framework.
1. Install the CLI
npm install -D @testivai/witness
2. Run the setup wizard
npx testivai init
The wizard detects your framework and generates helper files plus a testivai.config.ts.
3. Add a capture call
The wizard generates an example file. The key call is witness('name'):
// Cypress
it('homepage looks correct', () => {
cy.visit('/');
cy.witness('homepage');
});
# pytest
from testivai_witness import witness
def test_homepage(driver):
driver.get('http://localhost:3000')
witness(driver, 'homepage')
4. Run
# Cypress
npx testivai run "cypress run --browser chrome"
# pytest
npx testivai run "pytest tests/ -v"
The wrapper boots Chrome with --remote-debugging-port=9222, runs your tests, captures screenshots, and writes baselines + report.
What gets produced (local mode)
| Path | Purpose |
|---|---|
.testivai/baselines/<name>/screenshot.png | Committed baseline (track in git) |
.testivai/temp/ | Transient per-run captures (gitignore this) |
visual-report/index.html | Self-contained HTML diff report |
visual-report/results.json | Machine-readable summary |
Recommended .gitignore:
.testivai/temp/
visual-report/
What's Next
- How It Works — local pipeline, capture layers, diff algorithm
- vs. Playwright's built-in — honest comparison with
toHaveScreenshot() - vs. rolling your own (Selenium) — for Selenium suites, where there is no built-in
- Philosophy — local-first detection, bring-your-own-model AI
- MCP server — hand your agent the evidence; your model does the reasoning
- Claude Code plugin — the whole report, screenshots included, inside Claude Code
- Maintenance & roadmap — who builds this, cadence, and what happens if it stops
- Playwright adapter / WebdriverIO adapter
- GitHub Action — post results to PRs, approve from a comment, publish each run's report to GitHub Pages, and have Claude triage each diff with
review@v1(AI triage) - Extension API — write a community adapter for your framework
- Guides: CI/CD — running OSS lane in GitHub Actions