Capture fixtures
The reporter uploads complete test results — statuses, errors, traces, HTML reports — without any change to your test code. The capture fixtures are an optional, one-file addition that observes your tests from the inside and unlocks the dashboard's richest features: slow-endpoint analysis, Web Vitals, console capture, failure-time ARIA snapshots, and locator healing.
If you do one thing beyond installing the reporter, do this.
(What a captured locator snapshot actually stores — and doesn't — is in Core concepts.)
Setup
Option A — extend your existing fixtures:
// tests/fixtures.ts
import { test as base, expect } from '@playwright/test'
import { piwiFixtures } from '@piwitests/reporter'
export const test = base.extend(piwiFixtures)
export { expect }Option B — one-line extend with extendPiwiFixtures:
// tests/fixtures.ts
import { test as base } from '@playwright/test'
import { extendPiwiFixtures } from '@piwitests/reporter'
export const test = extendPiwiFixtures(base)
export { expect } from '@playwright/test'Then import test from your fixtures file in every spec. A spec that imports test from @playwright/test directly still runs and reports fine — it just isn't captured:
import { test, expect } from './fixtures'
test('homepage loads', async ({ page }) => {
await page.goto('/')
// network, console, Web Vitals, and locator data are captured automatically
})That's the entire setup — there is nothing to start, wrap, or await inside your tests.
What gets captured
| Data | Captured | Powers |
|---|---|---|
| Network requests — method, URL, status, duration, content type. Only API/document traffic (fetch, XHR, document); static assets are skipped | per request | Slow API endpoints table on the run page with /api/users/:id-style route normalization; backend log correlation via the X-Piwi-Logs response header |
Console entries — warning, error, and assert messages with source location (console.log noise is not collected) | as they happen | Console card on the test case page; AI diagnosis evidence |
| Web Vitals — TTFB, DOM Interactive, DOMContentLoaded, Load Complete, First Paint, First Contentful Paint, plus LCP, CLS and INP (Chromium-only) | at test teardown | Web vitals card with color-coded thresholds; performance trends |
| ARIA snapshot of the final page state | on failure | Failure evidence on the test-case and cluster pages; AI diagnosis context |
| Locator snapshots — element attributes, stable-ancestor anchors, and same-role position, plus ranked alternative locators (including rename-proof ancestor-scoped and name-free ones) for each element a test proves resolvable, stamped with the call site | after each successful action and each passing web-first assertion (toBeVisible(), toHaveText(), …) | Locator healing; when a failing name-based locator (getByRole, getByText, getByLabel, …) matches nothing, a fresh suggestion is attached to the test as a Playwright annotation |
Test source is captured without any fixture
On a failure the reporter also reads the call stack's in-project source — the line that actually threw plus the callers above it (helpers, page objects), each as a small line-numbered snippet with the failing line marked. It needs no fixture (it comes from the stack trace plus the local source files) and renders as the Test source call stack on the test-case and cluster pages. node_modules/Playwright frames are skipped.
With and without the fixtures
The reporter degrades gracefully — nothing breaks without the fixtures. This is exactly what you give up:
| Dashboard feature | Reporter only | Reporter + fixtures |
|---|---|---|
| Run history, statuses, errors, traces, HTML reports | ✅ | ✅ |
| Live streaming, sharding, CI/SCM metadata | ✅ | ✅ |
| Failure clustering & flaky-test analytics | ✅ | ✅ |
| AI failure diagnosis | ✅ error + code-diff grounding | ✅ richer evidence: ARIA snapshot, console, network |
| Slow API endpoints table | — | ✅ |
| Web Vitals & performance trends | — | ✅ |
| Console warnings/errors card | — | ✅ |
| Failure-time ARIA snapshot + locator suggestion | — | ✅ |
| Locator healing (ranked alternatives panel) | — | ✅ |
| Backend log correlation | — | ✅ with a backend integration |
Where capture works
The fixtures wire capture at the browser level, so it works however your tests create pages:
- the standard
pagefixture, browser.newPage()andbrowser.newContext().newPage()— including inside your own custom fixtures,- popups (
window.open) and pages a context opens on its own.
Semantics worth knowing:
beforeAll/afterAllactivity is intentionally not captured — only what happens inside a test is attributed to that test.- Multi-page tests attribute Web Vitals and the failure ARIA snapshot to the most recently active page.
- Repeated call sites — actions in a loop, or a page-object method called several times — probe the element once per call site per test, for assertions and actions alike. The dashboard stores one locator snapshot per location, so further probes at the same line would be discarded anyway. If a probe fails, the next run of that line tries again.
- Assertion capture is positive-presence only — negated assertions (
.not.…), absence checks (toBeHidden,toBeDetached), multi-element checks (toHaveCount, array forms) and page-level ones (toHaveTitle,toHaveURL) never probe.
Composing with your own fixtures
piwiFixtures is a plain Playwright fixtures object, so it composes with your own fixtures in any order:
import { test as base, mergeTests } from '@playwright/test'
import { piwiFixtures, extendPiwiFixtures } from '@piwitests/reporter'
// Your fixtures first, capture second
export const test = base.extend<MyFixtures>({ /* ... */ }).extend(piwiFixtures)
// Capture first, your fixtures second
export const test = extendPiwiFixtures(base).extend<MyFixtures>({ /* ... */ })
// Or merge two independent test objects
export const test = mergeTests(myTest, extendPiwiFixtures(base))Two rules:
- The fixture name
piwiCaptureis reserved — defining your own fixture with that name replaces the capture teardown and silently disables the attachments.piwiFixturestypes it, so a redefinition with an incompatible type is a compile error; a same-typed (void) one still compiles, so avoid the name entirely. - If you override
browserorpageyourself, extend withpiwiFixturesafter your override so the capture wrapping still applies.
Cost & opt-outs
Capture is designed to never fail or noticeably slow down a test:
- Per call site: one DOM read, and — only when the element's attributes don't already settle its accessible name — a bounded ARIA snapshot (500 ms deadline). Actions and passing assertions alike pay this at most once per call site per test.
- Per page: the element probe is installed once as an init script (a
__piwiProbeElementglobal) so each DOM read sends a small call rather than the probe's source. Pages it doesn't reach fall back to sending the source, and nothing is installed when locator capture is off. - At teardown: draining in-flight captures is capped at 2 seconds.
- A capture that can't complete (mid-navigation, detached element) is dropped silently — it never throws into your test.
| Option | Effect |
|---|---|
collectPerformanceMetrics: false | Master switch — disables all fixture capture |
captureLocators: false (or PIWI_CAPTURE_LOCATORS=false) | Disables only the locator snapshots (action and assertion capture alike); network, console, and Web Vitals stay on |
capturePageState: false (or PIWI_CAPTURE_PAGE_STATE=false) | Disables only the test-end app-state capture (URL, storage key names, cookie flags — values are never captured) |
inspectOnFailure: true (or PIWI_INSPECT_ON_FAIL=true) | Opt-in local debugging aid — a failing test opens the Playwright Inspector on its still-open page (headed browsers only, never in CI). See Inspect the failing page live |
pickLocatorOnFailure: true (or PIWI_PICK_LOCATOR_ON_FAIL=true) | Opt-in local debugging aid — after a locator failure, click the intended element on the still-open page and confirm a ranked replacement locator; the choice is recorded for the healing panel (headed browsers only, never in CI). See Pick a replacement locator |
Troubleshooting
- Data missing for some specs only — those specs import
testfrom@playwright/testinstead of your fixtures file. Capture is per-test-object; the import is the switch. - No fixture data at all — check that
collectPerformanceMetricsis notfalse, and that tests navigate to a real page (about:blank-only tests produce no Web Vitals). - ARIA snapshot or locator healing missing — same root causes as above; also check
captureLocators/PIWI_CAPTURE_LOCATORS.
Try it
A runnable example project exercising every capture path — including an intentionally failing test that lights up the ARIA snapshot, the locator suggestion, and the healing panel — lives in examples/playwright-fixtures.