Documentation
From install to first PDF
in under five minutes.
ReportForge is a Playwright reporter that turns a test run into a branded PDF — three templates for developers, QA teams, and stakeholders. This page covers everything from npm install to CI/CD to the full configuration reference.
01Install the package
Add the reporter and Puppeteer to your test project. Puppeteer-core is a peer dependency — install it explicitly so version drift is visible in your lockfile.
npm install --save-dev pdf-report-forge puppeteer-core
Node 18 or later is required. The package ships with three pre-compiled templates and a self-contained Chart.js bundle — no postinstall downloads, no Chromium pulled into node_modules.
02Wire it into playwright.config.ts
Add the reporter to your reporter array. The minimum config is two lines.
// playwright.config.ts
import { defineConfig } from '@playwright/test';
import { defineReporterConfig } from 'pdf-report-forge';
export default defineConfig({
reporter: [
['list'],
['pdf-report-forge', defineReporterConfig({
template: 'detailed',
outputFile: 'reports/{date}-{branch}-{status}.pdf',
projectName: 'My Project',
})],
],
use: { screenshot: 'only-on-failure' },
});defineReporterConfig is an optional typed identity helper — it gives you IntelliSense and type-checking on the options object without a separate ReporterOptions import. A plain object literal works too.
Tokens like {date}, {branch}, and {status} are expanded at write time. See Filename tokens for the full list.
03Set your license key
ReportForge requires an active subscription to generate PDFs. Start a 7-day free trial at reportforge.org/pricing — card required, no charge until day 8.
After signup you receive a license key (RFSU-…) by email. Set it as an environment variable in your shell or CI secrets.
# Linux / macOS export RF_LICENSE_KEY=RFSU-XXXX-XXXX-XXXX-XXXX # Windows (PowerShell) $env:RF_LICENSE_KEY = "RFSU-XXXX-XXXX-XXXX-XXXX" # Windows (Command Prompt) set RF_LICENSE_KEY=RFSU-XXXX-XXXX-XXXX-XXXX
Or pass it directly in playwright.config.ts via the licenseKey option. Without a valid key the reporter logs a one-line warning and skips PDF generation — your tests still run normally.
04Run your tests
The reporter generates the PDF after Playwright finishes — no separate command needed.
npx playwright test # → reports/2026-04-27-main-passed.pdf
05Chrome setup by operating system
The reporter uses Puppeteer to render the PDF and needs a Chrome binary on the machine. The path is auto-detected via PUPPETEER_EXECUTABLE_PATH, then system Chrome, then chrome-finder.
Windows
# Install via winget (Windows 10 1709 and later) winget install Google.Chrome # Set the path (PowerShell) $env:PUPPETEER_EXECUTABLE_PATH = "C:\Program Files\Google\Chrome\Application\chrome.exe" # Set the path (Command Prompt) set PUPPETEER_EXECUTABLE_PATH=C:\Program Files\Google\Chrome\Application\chrome.exe
If Chrome is already installed on your machine, the reporter usually finds it automatically. Download the installer from google.com/chrome if winget is unavailable.
Linux (Debian / Ubuntu)
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | \ sudo gpg --dearmor -o /usr/share/keyrings/google-chrome.gpg echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] \ http://dl.google.com/linux/chrome/deb/ stable main" | \ sudo tee /etc/apt/sources.list.d/google-chrome.list sudo apt-get update -qq && sudo apt-get install -y google-chrome-stable export PUPPETEER_EXECUTABLE_PATH=/usr/bin/google-chrome-stable
Do not use the chromium-browser apt package — on Ubuntu it installs as a snap and Puppeteer cannot drive it. Always install google-chrome-stable.
macOS
# Install via Homebrew brew install --cask google-chrome export PUPPETEER_EXECUTABLE_PATH="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
If Chrome is already installed in /Applications, the reporter finds it automatically — no env variable needed.
Set the path in config (all platforms)
Alternatively, hardcode the path in playwright.config.ts:
reporter: [['pdf-report-forge', {
// Linux CI
puppeteerExecutablePath: '/usr/bin/google-chrome-stable',
// Windows
// puppeteerExecutablePath: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
// macOS
// puppeteerExecutablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
}]]06CI/CD integration
Add RF_LICENSE_KEY as a secret in your CI provider, install Chrome, and upload the PDF as an artifact.
GitHub Actions
# .github/workflows/test.yml
- name: Install Google Chrome
run: |
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | \
sudo gpg --dearmor -o /usr/share/keyrings/google-chrome.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] \
http://dl.google.com/linux/chrome/deb/ stable main" | \
sudo tee /etc/apt/sources.list.d/google-chrome.list
sudo apt-get update -qq && sudo apt-get install -y google-chrome-stable
- name: Run Playwright tests
env:
PUPPETEER_EXECUTABLE_PATH: /usr/bin/google-chrome-stable
RF_LICENSE_KEY: ${{ secrets.RF_LICENSE_KEY }}
run: npx playwright test
- name: Upload PDF report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-pdf-report
path: reports/*.pdfGitLab CI
# .gitlab-ci.yml
playwright-tests:
image: mcr.microsoft.com/playwright:v1.49.0-jammy
variables:
PUPPETEER_EXECUTABLE_PATH: /usr/bin/google-chrome-stable
RF_LICENSE_KEY: $RF_LICENSE_KEY # set in CI/CD → Variables
before_script:
- apt-get update -qq && apt-get install -y google-chrome-stable
script:
- npm ci && npx playwright test
artifacts:
paths: [reports/*.pdf]
when: always
expire_in: 30 daysBitbucket Pipelines
# bitbucket-pipelines.yml
image: mcr.microsoft.com/playwright:v1.49.0-jammy
pipelines:
default:
- step:
name: Playwright Tests + PDF Report
caches: [node]
script:
- npm ci
- npx playwright test
artifacts:
- playwright-report/*.pdfSet RF_LICENSE_KEY in Repository Settings → Repository variables. The Playwright Docker image includes Chromium — no separate Chrome install step needed.
Jenkins
// Jenkinsfile
stage('Playwright Tests') {
environment {
PUPPETEER_EXECUTABLE_PATH = '/usr/bin/google-chrome-stable'
}
steps {
sh 'apt-get update -qq && apt-get install -y google-chrome-stable'
withCredentials([string(credentialsId: 'rf-license-key', variable: 'RF_LICENSE_KEY')]) {
sh 'npm ci && npx playwright test'
}
archiveArtifacts artifacts: 'reports/*.pdf', allowEmptyArchive: true
}
}Azure DevOps
# azure-pipelines.yml
- script: |
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | \
sudo gpg --dearmor -o /usr/share/keyrings/google-chrome.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] \
http://dl.google.com/linux/chrome/deb/ stable main" | \
sudo tee /etc/apt/sources.list.d/google-chrome.list
sudo apt-get update -qq && sudo apt-get install -y google-chrome-stable
displayName: Install Google Chrome
- script: npx playwright test
displayName: Run tests
env:
PUPPETEER_EXECUTABLE_PATH: /usr/bin/google-chrome-stable
RF_LICENSE_KEY: $(RF_LICENSE_KEY)
- task: PublishPipelineArtifact@1
condition: always()
inputs:
targetPath: reports
artifact: playwright-pdf-report07Configuration reference
All options are the second element of the reporter tuple in playwright.config.ts.
| Option | Default | Description |
|---|---|---|
| template | 'minimal' | Template(s) to generate: 'minimal', 'detailed', or 'executive'. Pass an array to produce one PDF per template — the template name is appended to the filename automatically (e.g. report-detailed.pdf). |
| outputFile | 'playwright-report/{date}-report.pdf' | Output path. Supports {date}, {branch}, {status} tokens. |
| reportTitle | 'Playwright Test Report' | Title shown in the report header and cover page. |
| projectName | from package.json | Project or application name shown in the report. |
| licenseKey | RF_LICENSE_KEY env | Subscription key (RFSU-…). Falls back to the RF_LICENSE_KEY environment variable. |
| logo | — | Absolute or relative path to a logo image (PNG, JPG, or SVG) embedded in the header. |
| primaryColor | '#CC785C' | Brand colour used for headers and accent bars (hex). |
| accentColor | '#3F7D58' | Accent colour used for highlights and badges (hex). |
| watermark | — | Diagonal watermark text on every page — e.g. 'CONFIDENTIAL' or 'DRAFT'. |
| pdfPassword | — | Password-protect the PDF. Requires qpdf on PATH. |
| compressionLevel | 'auto' | Screenshot quality preset: 'auto' (default), 'none' (original PNG), 'balanced' (JPEG q85), or 'max' (JPEG q70, smallest). |
| maxInlineFailures | from compressionLevel | Max failure entries rendered inline. Overflow is written to a sidecar {basename}-failures.json file. |
| maxFileSizeMb | 8 | Soft PDF size cap in MB. If exceeded, the report is re-rendered once with max compression. |
| open | false | Open the PDF automatically after generation (local use only). |
| puppeteerExecutablePath | auto-detect | Full path to a Chrome or Chromium binary. |
| serverUrl | 'https://reportforge.org' | Licensing server base URL. Override only for self-hosting. |
08Filename tokens
The outputFile path supports these tokens, expanded at write time:
| Token | Replaced with | Example |
|---|---|---|
| {date} | YYYY-MM-DD (UTC) | 2026-04-27 |
| {branch} | Git branch, sanitised (slashes → dashes) | feature-auth |
| {status} | Overall test verdict | passed / failed / timedout / interrupted |
outputFile: 'reports/{date}-{branch}-{status}.pdf'
// → reports/2026-04-27-main-failed.pdf09Templates
Set template to one of three values — or an array of values to generate multiple PDFs in one run. Each template produces a different PDF structure for a different audience.
minimal — Developer / PR checks
Clean white layout. KPI strip (total / passed / failed / duration), hierarchical test table with per-suite breakdown, failure details with stack traces and embedded screenshots. Fast to generate. Good default for local development and PR checks where file size matters.
detailed — QA team
Everything in minimal, plus:
- Chart.js pass-rate doughnut + per-suite bar chart (bundled inline, no CDN)
- Numbered defect log with severity, test name, and first stack-trace line
- Requirements traceability matrix — tag tests with @REQ-xxx or @FEAT-xxx and the matrix rolls up pass/fail/coverage per requirement
- Full CI/CD environment table (runner OS, Node version, branch, commit, timestamps)
executive — Stakeholder presentations
Full-page cover with overall verdict and KPI strip, followed by charts and a compact failure summary (test titles only — no raw stack traces). Good for sprint reports, release sign-off documents, and management dashboards.
Generating multiple templates in one run
Pass an array to template to generate one PDF per template from a single test run. One license check, one data-collection pass — faster than multiple reporter instances.
reporter: [
['pdf-report-forge', {
template: ['detailed', 'executive'],
outputFile: 'reports/{date}-report.pdf',
licenseKey: process.env.RF_LICENSE_KEY,
}],
]
// Output:
// → reports/2026-04-28-report-detailed.pdf
// → reports/2026-04-28-report-executive.pdfDuplicate entries are silently deduplicated. A single-element array produces the same output as a plain string — no suffix is appended.
Requirements traceability (detailed template)
Tag tests to link them to requirements:
test('@REQ-101 user can login', async ({ page }) => { /* ... */ });
test.describe('@FEAT-payment', () => { /* ... */ });The matrix rolls tests up per tag with pass count, fail count, and coverage percentage.
10License & offline behaviour
ReportForge uses a hybrid offline-first model. After the first activation, every subsequent run is fully offline.
- Set RF_LICENSE_KEY once (env var or config option).
- On the first run the reporter calls the license server, which checks your Razorpay subscription status, enforces the 25-machine cap, and returns an Ed25519-signed JWT.
- The JWT is cached at ~/.reportforge/license.json. Every subsequent run verifies it locally against a public key bundled into the npm package — no network call.
- When the cached JWT has less than 24 hours of TTL remaining, the reporter refreshes it in the background on the next run.
- Each subscription allows up to 25 active machines in any rolling 30-day window. A "machine" is a stable hash of the environment: provider:repo in CI, hostname:mac on a developer workstation. Machines that have not reported in 30 days are pruned automatically.
Offline behaviour
- Cache hit, JWT valid → run proceeds, no network call.
- Cache hit, JWT within 24h of expiry → run proceeds, background refresh.
- Cache hit, JWT expired, network down → reporter skips PDF generation with a clear notice. Tests still run.
- Server returns 4xx (subscription canceled / cap exceeded) → cache is cleared, PDF generation skipped. Tests still run.
- Subscription canceled → cache keeps working until currentPeriodEnd, then PDF generation stops on renewal.
A license issue never aborts your test run. The reporter logs a single-line warning and skips the PDF artifact. Your Playwright tests still pass or fail on their own merit.
11Troubleshooting
Set RF_DEBUG=1 to log the full activation flow and any error stack traces:
# Linux / macOS RF_DEBUG=1 npx playwright test 2>&1 | grep '\[reportforge\]' # Windows (PowerShell) $env:RF_DEBUG = "1"; npx playwright test
Chrome / Chromium not found
No PDF generated — no error shown
Charts are blank in the PDF
pdfPassword option has no effect
# Linux sudo apt install qpdf # macOS brew install qpdf # Windows (Chocolatey) choco install qpdf
License key rejected — format error
Machine cap reached
Ready to ship a real report?
Start a 7-day trial — card required, no charge until day 8.