Playwright's Built-in Reporters
Playwright ships several built-in reporters selectable via the reporter option: 'list' prints one line per test as it runs (the default for local interactive terminals), 'dot' prints a single character per test for compact CI logs, 'json' and 'junit' produce machine-readable output for integrating with dashboards or tools like Jenkins, and 'html' generates a self-contained, browsable report with screenshots, traces, and video attached to each test result. Choosing the right reporter is about audience: humans watching a terminal want 'list', CI log parsers want 'json' or 'junit', and anyone debugging a failure after the fact wants 'html'.
Cricket analogy: Choosing a reporter is like choosing how a match is covered: a radio commentator gives a rapid ball-by-ball 'dot' style call, while a full TV broadcast with replays is the detailed 'html' equivalent for later review.
The HTML Reporter in Depth
The HTML reporter produces a static site under playwright-report/ by default, showing a filterable dashboard of passed, failed, flaky, and skipped tests, and clicking into any test reveals step-by-step execution with timestamps, network requests, console logs, and—if trace: 'on' or 'on-first-retry' was configured—a full Playwright Trace Viewer embedded inline letting you scrub through a timeline of DOM snapshots and actions. Running npx playwright show-report serves this folder locally and auto-opens a browser tab, which is convenient locally but should be disabled on CI since CI runners have no browser to open and the command would otherwise hang or error.
Cricket analogy: The HTML report's embedded trace viewer is like a third-umpire review screen letting you scrub frame by frame through a run-out at the crease instead of just reading the final 'out' verdict.
// playwright.config.ts
export default defineConfig({
reporter: [
['html', { open: process.env.CI ? 'never' : 'on-failure', outputFolder: 'playwright-report' }],
],
use: {
trace: 'on-first-retry',
},
});Combining Multiple Reporters
You can configure multiple reporters simultaneously by passing an array of [name, options] tuples to the reporter property, e.g. reporter: [['dot'], ['html', {open: 'never'}], ['junit', {outputFile: 'results.xml'}]], so a single test run simultaneously prints compact progress to the CI log, writes a browsable HTML report as an artifact, and emits a JUnit XML file that your CI platform's test-results UI can parse to show pass/fail trends over time. This is standard practice in mature pipelines because no single reporter format satisfies both human debugging and automated dashboarding needs.
Cricket analogy: Running dot, html, and junit reporters together is like a match producing a live radio commentary, a full TV highlights package, and an official scorecard PDF all from the same innings simultaneously.
The Trace Viewer embedded in the HTML report lets you inspect every action's before/after DOM snapshot, network calls, and console output on a scrubbable timeline — it's often faster to diagnose a CI-only failure this way than by adding print statements and re-running the pipeline.
Custom Reporters
For bespoke needs—posting results to Slack, writing to a custom database, or annotating pull requests—you implement the Reporter interface, overriding lifecycle hooks like onBegin, onTestEnd(test, result), and onEnd(result) to receive structured data about every test as it completes, then reference your custom reporter file's path directly in the reporter array. Because onTestEnd fires once per attempt including retries, a naive custom reporter that just counts calls will over-report failures unless it checks result.retry to only count the final attempt per test.
Cricket analogy: A custom reporter posting results to Slack is like a franchise's dedicated stats analyst who feeds live win-probability numbers to the dugout during the match instead of waiting for the post-match report.
Always disable auto-opening the HTML report on CI with { open: 'never' }. Without it, npx playwright test (or a subsequent show-report call) can attempt to launch a browser window on a headless runner and hang the job or throw an unhelpful error.
- 'list', 'dot', 'json', 'junit', and 'html' are Playwright's built-in reporters, each suited to a different audience.
- The HTML report is a static, filterable dashboard with an embedded Trace Viewer for step-by-step debugging.
- Set { open: 'never' } on CI to prevent the HTML reporter from trying to auto-launch a browser.
- Multiple reporters can run in the same test execution via an array of [name, options] tuples.
- JUnit XML output integrates with CI platforms' native test-result dashboards and trend tracking.
- Custom reporters implement onBegin, onTestEnd, and onEnd to hook into structured per-test data.
- onTestEnd fires per attempt, so custom reporters must check result.retry to avoid double-counting.
Practice what you learned
1. Which built-in reporter produces a browsable dashboard with embedded traces and screenshots?
2. What must be set to prevent the HTML reporter from trying to open a browser tab on CI?
3. How do you run multiple reporters in a single Playwright test run?
4. Which lifecycle hook does a custom Reporter implement to receive per-test results as they complete?
5. Why must a custom reporter check result.retry inside onTestEnd?
Was this page helpful?
You May Also Like
Retries and Flaky Test Handling
Understanding why Playwright tests flake, how to configure retries safely, and how auto-waiting fixes root causes instead of masking them.
Playwright in CI/CD Pipelines
How to configure Playwright to run reliably on CI, including GitHub Actions setup, Docker images, and CI-aware config adjustments.
Parallel Workers and Sharding
How Playwright Test runs suites faster using isolated worker processes locally and distributes them across multiple CI machines with sharding.