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/*.pdf

GitLab 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 days

Bitbucket 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/*.pdf

Set 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-report

07Configuration reference

All options are the second element of the reporter tuple in playwright.config.ts.

OptionDefaultDescription
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.
projectNamefrom package.jsonProject or application name shown in the report.
licenseKeyRF_LICENSE_KEY envSubscription key (RFSU-…). Falls back to the RF_LICENSE_KEY environment variable.
logoAbsolute 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).
watermarkDiagonal watermark text on every page — e.g. 'CONFIDENTIAL' or 'DRAFT'.
pdfPasswordPassword-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).
maxInlineFailuresfrom compressionLevelMax failure entries rendered inline. Overflow is written to a sidecar {basename}-failures.json file.
maxFileSizeMb8Soft PDF size cap in MB. If exceeded, the report is re-rendered once with max compression.
openfalseOpen the PDF automatically after generation (local use only).
puppeteerExecutablePathauto-detectFull 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:

TokenReplaced withExample
{date}YYYY-MM-DD (UTC)2026-04-27
{branch}Git branch, sanitised (slashes → dashes)feature-auth
{status}Overall test verdictpassed / failed / timedout / interrupted
outputFile: 'reports/{date}-{branch}-{status}.pdf'
// → reports/2026-04-27-main-failed.pdf

09Templates

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.pdf

Duplicate 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.

  1. Set RF_LICENSE_KEY once (env var or config option).
  2. 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.
  3. 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.
  4. When the cached JWT has less than 24 hours of TTL remaining, the reporter refreshes it in the background on the next run.
  5. 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

Install Google Chrome Stable. On Linux, do not use the chromium-browser apt package — it is a snap on Ubuntu and Puppeteer cannot drive it. See Chrome setup above for per-OS instructions.

No PDF generated — no error shown

Verify RF_LICENSE_KEY is set and your subscription is active. Run with RF_DEBUG=1 — it will print whether the key is missing, the JWT is expired, the subscription is inactive, or the machine cap was hit.

Charts are blank in the PDF

The chart bundle must be compiled before the reporter runs. If you are building from source, run npm run bundle-chartjs first. The reporter waits up to 30 seconds for window.__chartsReady before giving up.

pdfPassword option has no effect

PDF password encryption requires qpdf on your PATH.
# Linux
sudo apt install qpdf

# macOS
brew install qpdf

# Windows (Chocolatey)
choco install qpdf

License key rejected — format error

Keys have the format RFSU-XXXX-XXXX-XXXX-XXXX. Check for accidental whitespace when copying from email. If the key still fails, email support@reportforge.org.

Machine cap reached

Your subscription allows up to 25 active machines in a rolling 30-day window. Machines that have not run in 30 days drop off automatically. If you hit the cap before then, email support to review your machine list.

Ready to ship a real report?

Start a 7-day trial — card required, no charge until day 8.