The enhanced merge-report.js script drastically simplifies the workflow for users running sharded Playwright tests. Here is a breakdown of the "Before vs. After" complexity.
Before: The Complexity Nightmare
Previously, if users wanted to merge sharded reports, they had to manually manage file paths and structures. It was fragile and prone to errors.
Manual File Hunting: The user (or their CI script) had to locate every playwright-pulse-report.json file across different shard folders.
Attachment Chaos: Merging attachments was a headache. If Shard 1 and Shard 2 both had a screenshot named error.png, one would overwrite the other, losing critical debugging data.
Complex CLI Commands: The user often had to write custom scripts to loop through folders, or pass a long list of file arguments to a merger tool.
Dirty Cleanup: After merging, the original shard folders (e.g., shard-1, shard-2) were left behind, cluttering the workspace unless the user wrote extra cleanup steps.
Now: Super Easy Logic
With the new script, the complexity is hidden. The user interaction is reduced to one simple folder.
"Dump and Done": The user just dumps all their shard folders (e.g., pulse-report-shard-1, pulse-report-shard-2) into a single directory (e.g., my-report). They don't need to organize files or rename anything.
One Command: They run a single command:
npx merge-pulse-report -o my-report
Auto-Discovery: The script automatically scans my-report, finds every subdirectory, and extracts the JSON data.
Smart Attachment Merging: It automatically copies all attachments to a central folder. (The script even includes logic to handle duplicates if needed, though usually Playwright hashes filenames).
Auto-Cleanup: Once the merge is safe and successful, the script automatically deletes the old shard folders. The user is left with a perfectly clean my-report folder containing just the final playwright-pulse-report.json and one attachments folder.
In short: You turned a multi-step manual process into a "black box" where the user provides a folder of raw shards, and gets back a single, polished report.
CI/CD Workflow for Sharding
# .github/workflows/playwright.yml
name: Playwright Tests with Pulse Report
on: [push]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test --shard=${{ matrix.shard }}/${{ strategy.job-total }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: pulse-report-shard-${{ matrix.shard }}
path: pulse-report/
retention-days: 1
merge-report:
needs: test
if: always()
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- run: npm ci
# Download all shard artifacts to a single directory
- uses: actions/download-artifact@v4
with:
path: all-reports
pattern: pulse-report-shard-*
# Merge all shard reports into a single report
- run: npx merge-pulse-report -o all-reports
# Generate the final HTML report
- run: npx generate-pulse-report -o all-reports
# Upload the final merged report
- uses: actions/upload-artifact@v4
with:
name: final-playwright-pulse-report
path: all-reports/
retention-days: 7
# .gitlab-ci.yml
stages:
- test
- merge
playwright-test:
stage: test
parallel: 4
image: node:18
script:
- npm ci
- npx playwright install --with-deps
- npx playwright test --shard=$CI_NODE_INDEX/$CI_NODE_TOTAL
# Create unique shard directory to avoid artifact conflicts
- mkdir -p all-reports/shard-$CI_NODE_INDEX
- mv pulse-report/* all-reports/shard-$CI_NODE_INDEX/
artifacts:
when: always
paths:
- all-reports/
expire_in: 1 day
merge-and-publish:
stage: merge
needs: ["playwright-test"]
image: node:18
script:
- npm ci
# Merge all shard reports
- npx merge-pulse-report -o all-reports
# Generate the final HTML report
- npx generate-pulse-report -o all-reports
artifacts:
paths:
- all-reports/playwright-pulse-static-report.html
- all-reports/playwright-pulse-report.json
expire_in: 7 days
// Jenkinsfile
pipeline {
agent any
stages {
stage('Setup') {
steps {
sh 'npm ci'
sh 'npx playwright install --with-deps'
}
}
stage('Parallel Tests') {
parallel {
stage('Shard 1') {
steps {
sh 'npx playwright test --shard=1/4'
sh 'mkdir -p all-reports/shard-1 && mv pulse-report/* all-reports/shard-1/ || true'
stash name: 'shard-1-report', includes: 'all-reports/shard-1/**'
}
}
stage('Shard 2') {
steps {
sh 'npx playwright test --shard=2/4'
sh 'mkdir -p all-reports/shard-2 && mv pulse-report/* all-reports/shard-2/ || true'
stash name: 'shard-2-report', includes: 'all-reports/shard-2/**'
}
}
stage('Shard 3') {
steps {
sh 'npx playwright test --shard=3/4'
sh 'mkdir -p all-reports/shard-3 && mv pulse-report/* all-reports/shard-3/ || true'
stash name: 'shard-3-report', includes: 'all-reports/shard-3/**'
}
}
stage('Shard 4') {
steps {
sh 'npx playwright test --shard=4/4'
sh 'mkdir -p all-reports/shard-4 && mv pulse-report/* all-reports/shard-4/ || true'
stash name: 'shard-4-report', includes: 'all-reports/shard-4/**'
}
}
}
}
stage('Merge and Publish Report') {
steps {
// Unstash all shard reports
unstash 'shard-1-report'
unstash 'shard-2-report'
unstash 'shard-3-report'
unstash 'shard-4-report'
// Merge all shard reports
sh 'npx merge-pulse-report -o all-reports'
// Generate the final HTML report
sh 'npx generate-pulse-report -o all-reports'
// Archive the final report
archiveArtifacts artifacts: 'all-reports/playwright-pulse-static-report.html, all-reports/playwright-pulse-report.json'
}
}
}
}