Sharding from the parallelism chapter creates a non-obvious reporting problem. When the suite is split across several machines and each passes its part, each ends up with its own report about its third of the tests. As a result, instead of one whole picture of the run there are three fragments from which the overall result cannot be seen: how many passed in total, what failed, where the trace is. Separate shard reports are useless as an overall report - they must be stitched into one.
The solution is the intermediate blob format. On CI the reporter is switched to blob: instead of ready HTML each shard writes a machine-readable blob report - a self-contained chunk of data about its part of the run, including traces and attachments. A blob is not meant to be read by a human directly; it is the raw material from which a single human-readable report is later assembled. Each shard produces its blob and stores it as a CI artifact.
Next the blob reports of all shards are gathered in one place. After the parallel shard jobs finish, a separate job downloads all their blob artifacts - and gets the full set of run chunks. This is the assembly point: here for the first time all information about the whole run appears, laid out across blob files, ready to be merged into one report. The assembly job is made dependent on the shards so it starts after them.
The merge-reports command stitches it all. npx playwright merge-reports with the html reporter specified and the directory where all blobs are stored assembles from them a single HTML report of the whole run - as if the suite had run on one machine. The report shows all tests, all failures and all traces together, not in pieces. Other formats can be assembled from blobs too, but html is the one given to the team for analysis.
It helps to see both parts once - configuring blob on the shards and the merge command. Below is switching the reporter to blob for CI and calling merge-reports over the directory of gathered blobs. You return to this pair when setting up a sharded pipeline: first each shard writes a blob, then a separate job merges them into one report, and only it goes to the team.
The pipeline structure follows from this directly. A shard matrix - several parallel jobs, each with the blob reporter and uploading its blob artifact. Then a final job with needs on the whole matrix, which downloads all blob artifacts and calls merge-reports. So the parallelism from the scaling chapter gets whole reporting: tests run apart for speed, while the result is gathered together for analysis.
Without this assembly a sharded run loses the main thing - a unified picture. Three separate reports force you to manually match what failed where, and give no total of passed at all; the trace of a specific failure has to be sought across shards. Merge-reports returns what a report is for: one look at the whole run. So in any sharded pipeline merging is not an option but a mandatory final step.
The typical failures here are predictable. Leaving the ordinary html reporter on the shards instead of blob - and getting unmergeable pieces from which a single report can no longer be assembled. Forgetting to upload or download the blob artifacts - and the assembly job has nothing to merge. And not making the merge job depend on the shards - it starts before the blobs are ready. Write a blob on each shard, gather the artifacts in a dependent job and merge them with merge-reports into a single report.
// On CI with sharding each shard writes a blob instead of html
export default defineConfig({
reporter: process.env.CI ? 'blob' : [['html'], ['list']],
})# A separate job: download all shard blob artifacts and stitch into one report
npx playwright merge-reports --reporter html ./all-blob-reports
# the gathered blobs produce a single HTML report of the whole run