QA How-To
Playwright 1.5 Merge Reports from Shards (2026)
Learn playwright 1.5 merge reports from shards with blob reports, GitHub Actions artifacts, one HTML report, failure-safe jobs, and local checks in CI.
18 min read | 2,644 words
TL;DR
Configure CI runs with Playwright's blob reporter, upload each shard's blob-report directory, download all artifacts into one directory, and run npx playwright merge-reports --reporter=html ./all-blob-reports. Publish the resulting playwright-report directory as the single report artifact.
Key Takeaways
- Generate a blob report in every shard because HTML directories cannot be safely combined by copying files.
- Give each shard artifact a unique name, then download all blob ZIP files into one flat directory.
- Run playwright merge-reports once in a separate job to produce the final HTML report.
- Use if: ${{ !cancelled() }} on upload and merge steps so failed shards still contribute diagnostics.
- Install dependencies from the same lockfile in shard and merge jobs to prevent version mismatches.
- Verify the merged report contains every project, shard, failure attachment, and expected test count.
Playwright 1.5 merge reports from shards is best handled with Playwright's blob reporter and the merge-reports CLI. Each parallel job writes a mergeable ZIP, a final CI job downloads those ZIP files into one directory, and Playwright converts the complete set into one HTML report. Do not try to combine multiple playwright-report folders because their HTML assets and indexes overwrite one another.
This tutorial builds a four-shard GitHub Actions workflow using Playwright Test 1.61.1, Node.js 24 LTS, and current artifact actions. The same report pipeline works locally, in GitLab CI, Jenkins, or any system that can retain files between jobs. If you need broader framework context first, review the Playwright advanced automation guide and the parallel test sharding in CI guide.
TL;DR
| Stage | Input | Command or action | Output |
|---|---|---|---|
| Test shard | Assigned test subset | npx playwright test --shard=1/4 |
One blob ZIP |
| Artifact upload | blob-report/ |
actions/upload-artifact@v4 |
One named artifact per shard |
| Merge job | All blob ZIP files | npx playwright merge-reports --reporter=html ./all-blob-reports |
playwright-report/ |
| Publication | Merged HTML directory | actions/upload-artifact@v4 |
One downloadable report |
The critical detail is format. Blob reports preserve test identities, projects, retries, steps, stdout, errors, traces, screenshots, and other attachments in data that Playwright can merge. HTML output is a presentation format, not a merge input.
What You Will Build
By the end, you will have:
- A small TypeScript suite that is easy to split across four CI machines.
- A Playwright configuration that emits HTML locally and blob data in CI.
- A local two-shard rehearsal that proves merging before CI is involved.
- A GitHub Actions matrix that uploads one uniquely named artifact per shard.
- A final job that runs even after test failures and creates one browsable HTML report.
- Verification checks for blob count, report generation, attachments, and missing shards.
The workflow separates execution from presentation. That separation makes the result deterministic: shard jobs only execute tests and record facts, while the merge job renders those facts after every available shard finishes.
Prerequisites
Use Node.js 24 LTS, npm 11 or the npm version bundled with your Node installation, and @playwright/test 1.61.1. Pinning Playwright matters because blob report compatibility follows the Playwright version that created it. Every shard and the merge job must install from the same committed package-lock.json.
Start in a new or existing Node project:
npm install --save-dev @playwright/test@1.61.1 typescript@5.9.2
npx playwright install --with-deps chromium
You also need GitHub Actions access for the CI part and a repository containing the lockfile. Confirm the local tools:
node --version
npm --version
npx playwright --version
Verification: the final line must print Version 1.61.1. Commit both package.json and package-lock.json. If npx offers to install another Playwright version, stop and run npm ci; that prompt indicates the declared dependency is missing. For general runner setup, the GitHub Actions for Playwright tutorial covers repository secrets, triggers, and browser installation in more depth.
Step 1: Create a Shard-Friendly Test Suite
Create tests/catalog.spec.ts. The example uses Playwright's public documentation site so it runs without a private application or credentials:
import { test, expect } from '@playwright/test';
const pages = [
{ path: '/', heading: 'Playwright enables reliable end-to-end testing' },
{ path: '/docs/intro', heading: 'Installation' },
{ path: '/docs/test-assertions', heading: 'Assertions' },
{ path: '/docs/test-reporters', heading: 'Reporters' },
{ path: '/docs/test-sharding', heading: 'Sharding' },
{ path: '/docs/trace-viewer-intro', heading: 'Trace viewer' },
{ path: '/docs/test-parallel', heading: 'Parallelism' },
{ path: '/docs/test-retries', heading: 'Retries' },
];
for (const item of pages) {
test(`opens ${item.path}`, async ({ page }) => {
await page.goto(item.path);
await expect(page.getByRole('heading', { name: item.heading, exact: false }).first()).toBeVisible();
});
}
These eight independent tests give four shards useful work. Playwright assigns tests to shards; it does not launch four shards merely because workers is set. The --shard=current/total CLI flag defines which slice a process owns. With fullyParallel: true, Playwright can balance individual tests rather than treating each spec file as an indivisible unit.
Run the unsharded baseline:
npx playwright test tests/catalog.spec.ts --project=chromium
Verification: the terminal should report eight passed tests. A failure here is unrelated to merging, so fix the locator, network access, or browser installation before adding artifact mechanics.
Step 2: Configure Blob Reports for CI
Create playwright.config.ts with an environment-sensitive reporter:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: process.env.CI
? [['blob', { outputDir: 'blob-report' }]]
: [['html', { open: 'never' }], ['list']],
use: {
baseURL: 'https://playwright.dev',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
],
});
One worker per CI machine keeps resource use predictable; the matrix supplies machine-level parallelism. fullyParallel improves distribution when one spec contains many tests. The blob reporter chooses a shard-aware filename by default, such as report-<hash>-1.zip, so files from multiple artifacts can coexist after download.
Test the CI branch locally:
CI=1 npx playwright test --shard=1/2
ls -la blob-report
Verification: blob-report must contain one .zip file whose name ends with a shard indicator. It should not contain index.html. The directory is cleared when a new blob run starts, which is why each shard needs its own isolated workspace or artifact upload before another local shard runs.
Step 3: Rehearse Playwright 1.5 Merge Reports from Shards Locally
A local rehearsal catches wrong directories and incompatible blobs without waiting for CI. Run each shard with a different blob output directory. Environment variables are convenient because PLAYWRIGHT_BLOB_OUTPUT_DIR overrides the configured blob directory:
CI=1 PLAYWRIGHT_BLOB_OUTPUT_DIR=blob-report-1 npx playwright test --shard=1/2
CI=1 PLAYWRIGHT_BLOB_OUTPUT_DIR=blob-report-2 npx playwright test --shard=2/2
mkdir -p all-blob-reports
cp blob-report-1/*.zip all-blob-reports/
cp blob-report-2/*.zip all-blob-reports/
npx playwright merge-reports --reporter=html ./all-blob-reports
The command reads blob ZIP files directly. Do not unzip them. It creates playwright-report/index.html using the HTML reporter's normal output location. If you require multiple outputs, pass a comma-separated list such as --reporter=html,json, but define explicit output paths before using that in CI to avoid artifact ambiguity.
Open the result:
npx playwright show-report playwright-report
Verification: the overview should show all eight tests, not four. Filter by status and open a test to confirm its duration and steps are present. If only half the suite appears, count the ZIP files in all-blob-reports and confirm the two filenames differ. This local method is also a good diagnostic companion to the test framework reporting guide.
Step 4: Add the GitHub Actions Shard Matrix
Create .github/workflows/playwright.yml:
name: Playwright tests
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
playwright-tests:
timeout-minutes: 30
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 24
cache: npm
- run: npm ci
- run: npx playwright install --with-deps chromium
- name: Run shard ${{ matrix.shardIndex }} of ${{ matrix.shardTotal }}
run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
- name: Upload blob report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: blob-report-${{ matrix.shardIndex }}
path: blob-report
if-no-files-found: error
retention-days: 1
fail-fast: false lets the remaining shards finish when one matrix leg fails. The upload step uses !cancelled() rather than success() or always(): it preserves failure evidence but does not insist on running after an explicit cancellation. Unique artifact names prevent matrix jobs from writing to the same artifact, a pattern GitHub Actions does not support safely.
Verification: push a branch and inspect the Actions run. Four matrix jobs should appear. Each completed job should expose an artifact named blob-report-1 through blob-report-4, even if its test command failed. The GitHub Actions matrix testing guide explains how to extend the matrix with browsers or operating systems without confusing those dimensions with shards.
Step 5: Download and Flatten Every Blob Artifact
Append a second job at the same indentation as playwright-tests:
merge-reports:
if: ${{ !cancelled() }}
needs: [playwright-tests]
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 24
cache: npm
- run: npm ci
- name: Download all blob reports
uses: actions/download-artifact@v5
with:
path: all-blob-reports
pattern: blob-report-*
merge-multiple: true
- name: List merge inputs
run: find all-blob-reports -maxdepth 1 -type f -name '*.zip' -print
needs makes the merge job wait for every matrix leg. The job-level condition is essential because a default dependent job is skipped when any required job fails. pattern selects only the intended artifacts, and merge-multiple: true places their contents in one directory. Without flattening, ZIP files remain in nested artifact directories and the merge command may not find the intended input set.
The merge job runs npm ci even though it does not launch a browser. That installs the precise Playwright CLI and reporters recorded in the lockfile. Browser installation is unnecessary for report rendering, so omit it to save time.
Verification: the list step should print four distinct ZIP paths for a normal four-shard run. Treat a smaller count as an incomplete report. Artifact download success alone proves only that at least one matching artifact existed.
Step 6: Merge the Shards into One HTML Report
Add these steps after the download check:
- name: Verify all shard reports arrived
shell: bash
run: |
count=$(find all-blob-reports -maxdepth 1 -type f -name '*.zip' | wc -l | tr -d ' ')
test "$count" -eq 4 || { echo "Expected 4 blob reports, found $count"; exit 1; }
- name: Merge into HTML report
run: npx playwright merge-reports --reporter=html ./all-blob-reports
- name: Verify HTML entry point
run: test -f playwright-report/index.html
The count guard prevents a polished but incomplete report from being mistaken for the whole suite. It assumes one Playwright invocation and therefore one blob ZIP per shard. If a shard intentionally runs separate commands, calculate the expected total accordingly or verify shard identifiers rather than raw ZIP count.
merge-reports can exit successfully even when merged test results contain failures. That is desirable: the execution jobs already carry the test status, while the merge job's responsibility is producing diagnostics. The resulting HTML retains the outcome of every imported test.
Verification: the entry-point check must pass, and the job log should show report generation without an incompatible-version error. Downloading index.html alone is not enough because the report references adjacent data and attachment assets. Always publish the entire directory.
Step 7: Upload the Combined Report Reliably
Finish the merge job with a final upload step:
- name: Upload combined Playwright report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: playwright-html-report-attempt-${{ github.run_attempt }}
path: playwright-report
if-no-files-found: error
retention-days: 14
Including github.run_attempt prevents confusion after rerunning failed jobs. A rerun can otherwise collide with an artifact name from the original attempt because v4 artifacts are immutable within the same workflow run. Fourteen days is an example retention period, not a universal policy. Match it to debugging needs and storage rules.
To inspect the artifact, download and extract it, then serve the directory through Playwright:
npx playwright show-report ./playwright-report
Verification: the Actions summary should show one final artifact in addition to the short-lived blob artifacts. Open several tests from different shards. For a deliberate failure, confirm the error, retry, trace, and failure screenshot are accessible. If you want a durable public portfolio rather than a private CI artifact, follow the deploy test reports to GitHub Pages guide, but review whether screenshots or traces contain sensitive data first.
Step 8: Customize Merge Output with a Dedicated Config
The CLI flag is enough for HTML, but a merge config is cleaner when you need reporter options. Create merge.config.ts:
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests',
reporter: [
['html', { outputFolder: 'playwright-report', open: 'never' }],
['json', { outputFile: 'playwright-report/results.json' }],
],
});
Then replace the merge command:
npx playwright merge-reports --config=merge.config.ts ./all-blob-reports
A dedicated config also helps when blob reports were created on different operating systems. Set testDir to the shared logical test root so source paths can be resolved consistently. Keep execution-only settings out of this file unless they affect report interpretation; the merge command does not rerun tests.
The JSON output is useful for controlled downstream processing, while HTML remains the human interface. Avoid parsing Playwright's internal blob ZIP format yourself because it is an interchange format for Playwright versions, not a stable public analytics schema.
Verification: confirm both playwright-report/index.html and playwright-report/results.json exist. Parse the JSON with a real parser rather than checking only its size: node -e "JSON.parse(require('fs').readFileSync('playwright-report/results.json','utf8')); console.log('valid JSON')".
Playwright 1.5 Merge Reports from Shards: Design Choices
| Choice | Recommended use | Main trade-off |
|---|---|---|
| Blob per shard, HTML after merge | Standard parallel CI | Requires artifact transfer |
| HTML per shard | Each shard is investigated independently | No unified totals or navigation |
| Blob plus JSON after merge | Dashboards and machine processing | Larger artifact and schema handling |
| One unsharded HTML run | Tiny suites | Gives up machine-level speed |
| Allure results per shard | Teams standardized on Allure | Adds adapter and report-generation maintenance |
Use native blob merging unless an external reporting platform provides capabilities you actually need. Blob files are specifically designed to preserve Playwright attachments and reconstruct supported output reporters. If your organization already operates Allure, compare the ownership and publication steps in Allure reporting in CI.
The phrase Playwright 1.5 in the query is often interpreted as a version reference, but the workflow in this guide deliberately targets current Playwright 1.61.1. The merge-reports workflow belongs to modern Playwright Test. Do not pin an obsolete 1.5-era package to satisfy wording in a search query. Confirm your installed version with npx playwright --version, and keep all report producers and the consumer aligned.
Best Practices
- Pin
@playwright/testand install withnpm cieverywhere. A floating CLI can fail to decode blobs made by another version. - Upload blobs after failed tests. Failure artifacts are the most valuable inputs to the merged report.
- Keep
fail-fast: falseon the matrix. Otherwise one early failure can cancel shards whose coverage never reaches the report. - Use a unique artifact name per shard and per final run attempt. Artifact identity should explain its scope.
- Flatten downloaded blob contents into one known directory and list that directory before merging. This makes path errors obvious in logs.
- Validate the expected shard input count. A report with missing shards can look valid while understating failures and coverage.
- Retain raw blobs briefly and the merged report long enough for investigation. Raw blobs are intermediate files, not the primary reader experience.
- Publish the whole HTML folder. Its index depends on bundled resources and attachments.
- Avoid secrets in screenshots, traces, request headers, and page content. Artifact access controls reduce exposure but do not sanitize captured data.
- Keep shard count stable within one matrix run. Mixing
1/4and2/5creates overlapping or missing test allocation.
Troubleshooting
Problem: merge-reports says no report files were found. -> Point the command at the directory containing blob ZIP files, not at playwright-report and not at a parent full of nested directories. Add find all-blob-reports -type f -print immediately before the command. In GitHub Actions, use merge-multiple: true.
Problem: only one shard appears in the combined report. -> Check for filename overwrites and missing artifacts. Use the blob reporter's default shard-aware names, unique artifact names, and a ZIP count assertion. Never force every shard to write the same PLAYWRIGHT_BLOB_OUTPUT_FILE.
Problem: the merge job is skipped after a test failure. -> Add if: ${{ !cancelled() }} to the merge job itself, not only to an internal step, and keep needs: [playwright-tests]. A dependent job otherwise inherits the failed prerequisite and does not start.
Problem: blobs are incompatible or parsing fails. -> Ensure shard jobs and the merge job use the identical package-lock.json and run npm ci. Do not merge blobs left over from a previous workflow attempt or created by another Playwright version. Include the run ID or use isolated artifact downloads when diagnosing cross-run contamination.
Problem: the HTML report exists but traces or screenshots do not open. -> Upload the complete playwright-report directory and preserve blob attachments during artifact transfer. Do not extract, recompress selectively, or copy only index.html. Confirm failure capture settings were enabled before execution.
Problem: the merged test count is lower than expected even though four ZIP files exist. -> First confirm all shard commands used the same total, for example 1/4 through 4/4. Then inspect project and grep filters, because different filters create different test selections. Compare the merged count with npx playwright test --list for the same projects and filters.
Interview Questions and Answers
The most useful interview discussion focuses on why the pipeline is shaped this way, not on memorizing one YAML file. Prepare to explain blob data, dependency ordering, failure conditions, and verification. The interviewQnA section below contains model answers covering those decisions.
A strong practical demonstration is to draw the data flow: four test jobs produce four immutable inputs, the merge job waits and downloads them, and one reporter renders the combined result. Mention that test failures and infrastructure failures need different handling. A valid failed test should still yield a blob, while a cancelled or crashed job may produce no artifact and should make the completeness guard fail.
Where To Go Next
You now have the essential report pipeline: shard, upload, flatten, validate, merge, and publish. Extend one dimension at a time so failures remain diagnosable.
- Use parallel test sharding in CI to choose a sensible shard count and understand balancing.
- Expand browsers and operating systems with GitHub Actions matrix testing, then tag environments so the merged report distinguishes them.
- Improve workflow caching with the GitHub Actions caching guide after measuring install time.
- Compare native and external outputs in adding reporting to a test framework.
- Publish a sanitized artifact through deploying test reports to GitHub Pages when stakeholders need a stable URL.
- Practice explaining your implementation on the QA interview practice surface or upload a resume for role-specific feedback in the QAJobFit dashboard.
Conclusion
To merge Playwright reports from shards, emit blob reports during execution and render HTML only after every shard artifact is collected. The reliable implementation depends on four safeguards: identical Playwright versions, unique shard artifacts, failure-tolerant job conditions, and an explicit completeness check.
Run the two-shard local rehearsal first. Once it shows the complete suite, commit the GitHub Actions workflow and deliberately fail one test. If the final artifact still contains all shards plus the failure trace, the reporting pipeline is ready for real CI use.
Interview Questions and Answers
Why does Playwright use blob reports for sharded result merging?
Blob reports preserve structured run data, test identities, projects, retries, steps, errors, and attachments. Playwright can consume that data later and render a supported reporter such as HTML or JSON. An HTML directory is already rendered output, so copying several HTML directories cannot reconstruct one accurate suite result.
How would you ensure the report is generated when one shard has test failures?
I disable matrix fail-fast, upload each blob with `if: ${{ !cancelled() }}`, and apply the same condition to the dependent merge job. This lets expected test failures flow into the report while respecting manual cancellation. I also check that all expected shard blobs arrived before publishing.
What is the purpose of merge-multiple in actions/download-artifact?
Each shard is uploaded as a separate artifact. `merge-multiple: true` downloads the matching artifact contents into one destination rather than retaining a directory layer for each artifact. The Playwright merge CLI can then read the blob ZIP inputs from a single known directory.
How do workers differ from shards in Playwright?
Workers are parallel processes inside one Playwright invocation and usually one machine. Shards divide the selected suite among separate invocations, often across multiple CI machines. A shard may use multiple workers, but in CI I tune both levels to match available CPU and avoid oversubscription.
How do you prevent version-related blob merge failures?
I pin `@playwright/test`, commit the lockfile, and use `npm ci` in execution and merge jobs. I never download blob artifacts from unrelated workflow runs into the current merge directory. Logging `npx playwright --version` in both job types makes mismatches easy to diagnose.
How would you prove a merged report is complete?
I first validate the expected number and identity of shard blob files. Then I compare the merged test total with the expected selection from the same projects and filters, and sample tests known to belong to different shards. For a controlled failing run, I also verify that its retry and attachments open.
Frequently Asked Questions
How do I merge Playwright reports from multiple shards?
Configure every shard to use the blob reporter, place all generated blob ZIP files in one directory, and run `npx playwright merge-reports --reporter=html ./all-blob-reports`. Upload the entire resulting `playwright-report` directory as the combined artifact.
Can I merge separate Playwright HTML report folders directly?
No. HTML reports are final presentation output and their files can overwrite each other. Generate blob reports in shard jobs and convert the collected blobs into one HTML report with `merge-reports`.
Why is my Playwright merge job skipped when a shard fails?
A job that depends on a failed job is skipped by default. Put `if: ${{ !cancelled() }}` on the merge job and on shard artifact uploads so ordinary test failures still produce the combined diagnostic report.
Must every shard use the same Playwright version?
Yes, treat identical versions as a requirement. Install from one committed lockfile with `npm ci` in every shard and in the merge job because blob compatibility and reporter behavior can change between Playwright releases.
Does a merged HTML report preserve Playwright traces and screenshots?
Blob reports include attachments such as traces and screenshot diffs, so the merged report can retain them. The capture setting must have produced those attachments, and CI must transfer the complete blobs and publish the complete HTML directory.
How can I detect a missing shard before publishing a report?
Count the downloaded blob ZIP files or validate their shard identifiers before calling `merge-reports`. For one Playwright invocation on four shards, require four ZIP files and fail the merge job if fewer arrive.
Related Guides
- Generating Playwright tests from a user story (2026)
- How to Build a Playwright TypeScript framework from scratch (2026)
- Playwright 1.5 API Testing TypeScript Tutorial (2026)
- Playwright 1.5 Test Agents Setup Tutorial (2026)
- Accessibility testing with Playwright (2026)
- AI code review for Playwright tests (2026)