Jenkins blocks JavaScript in HTML reports by default: the Content Security Policy header prevents the Playwright report from rendering unless you explicitly relax it in Jenkins Script Console or the HTML Publisher plugin settings. The other common failure is missing system libraries on the agent, which the official Playwright Docker image fixes by packaging all browser dependencies. This guide covers the complete Jenkinsfile from minimal working example to multi-stage pipeline with parallel browser execution, sharding across agents, secrets management, and scheduled nightly runs.
Why Jenkins Is Still Relevant
- Many enterprises have decade-long investments in Jenkins infrastructure
- On-premise hosting for security/compliance requirements
- Highly customizable — plugins for virtually everything
- Some CI/CD setups are Jenkins + Kubernetes at scale
If your company uses Jenkins, you can't just switch to GitHub Actions. You work with what you have.
Prerequisites
- Jenkins installed (on-premise or via Jenkins in Docker)
- Playwright project in Git (GitHub, GitLab, Bitbucket, etc.)
- Jenkins plugins: Pipeline, Git, HTML Publisher, JUnit
Basic Jenkinsfile
Jenkins pipelines are defined in a Jenkinsfile at the root of your repository. There are two syntaxes — Declarative (structured) and Scripted (Groovy). Use Declarative unless you need advanced logic.
// Jenkinsfile
pipeline {
agent {
docker {
image 'mcr.microsoft.com/playwright:v1.44.0-jammy'
args '-u root' // Run as root to avoid permission issues
}
}
environment {
BASE_URL = credentials('staging-base-url') // From Jenkins credentials
CI = 'true'
}
stages {
stage('Install') {
steps {
sh 'npm ci'
}
}
stage('Run Tests') {
steps {
sh 'npx playwright test --reporter=list,junit'
}
post {
always {
// Publish JUnit results
junit 'test-results/results.xml'
// Publish HTML report
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'playwright-report',
reportFiles: 'index.html',
reportName: 'Playwright Report'
])
}
}
}
}
post {
failure {
// Notify on failure
emailext(
subject: "FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: "Tests failed. See: ${env.BUILD_URL}",
to: 'qa-team@mycompany.com'
)
}
}
}Multi-Stage Pipeline
A realistic pipeline has more stages — linting, unit tests, then E2E:
pipeline {
agent {
docker {
image 'mcr.microsoft.com/playwright:v1.44.0-jammy'
args '-u root'
}
}
environment {
BASE_URL = credentials('staging-url')
NODE_ENV = 'test'
}
stages {
stage('Install Dependencies') {
steps {
sh 'npm ci'
}
}
stage('Lint') {
steps {
sh 'npm run lint'
}
}
stage('Unit Tests') {
steps {
sh 'npm run test:unit -- --reporter=junit'
}
post {
always {
junit 'unit-test-results/*.xml'
}
}
}
stage('Smoke Tests') {
steps {
sh '''
npx playwright test \
--grep @smoke \
--project=chromium \
--reporter=list,junit
'''
}
post {
always {
junit 'test-results/results.xml'
}
}
}
stage('Full Regression') {
when {
anyOf {
branch 'main'
branch 'release/*'
}
}
steps {
sh '''
npx playwright test \
--workers=4 \
--reporter=list,junit,html
'''
}
post {
always {
junit 'test-results/results.xml'
publishHTML([
reportDir: 'playwright-report',
reportFiles: 'index.html',
reportName: 'Full Regression Report',
keepAll: true
])
}
}
}
}
}Parallel Test Execution
Run tests across multiple browsers in parallel using Jenkins' parallel step:
stage('Cross-Browser Tests') {
parallel {
stage('Chrome') {
steps {
sh 'npx playwright test --project=chromium --reporter=junit'
}
post {
always {
junit 'test-results/chromium-results.xml'
}
}
}
stage('Firefox') {
steps {
sh 'npx playwright test --project=firefox --reporter=junit'
}
post {
always {
junit 'test-results/firefox-results.xml'
}
}
}
stage('Safari') {
steps {
sh 'npx playwright test --project=webkit --reporter=junit'
}
post {
always {
junit 'test-results/webkit-results.xml'
}
}
}
}
}For this to work, your playwright.config.ts must output to separate files per project:
reporter: [
['junit', {
outputFile: `test-results/${process.env.BROWSER || 'all'}-results.xml`
}],
],And run with:
BROWSER=chromium npx playwright test --project=chromiumSharding Across Multiple Agents
For large test suites, split across multiple Jenkins agents:
pipeline {
agent none // Each stage picks its own agent
stages {
stage('Test') {
parallel {
stage('Shard 1/3') {
agent { docker { image 'mcr.microsoft.com/playwright:v1.44.0-jammy' } }
steps {
sh 'npm ci'
sh 'npx playwright test --shard=1/3 --reporter=blob'
}
post {
always {
archiveArtifacts 'blob-report/**'
}
}
}
stage('Shard 2/3') {
agent { docker { image 'mcr.microsoft.com/playwright:v1.44.0-jammy' } }
steps {
sh 'npm ci'
sh 'npx playwright test --shard=2/3 --reporter=blob'
}
post {
always {
archiveArtifacts 'blob-report/**'
}
}
}
stage('Shard 3/3') {
agent { docker { image 'mcr.microsoft.com/playwright:v1.44.0-jammy' } }
steps {
sh 'npm ci'
sh 'npx playwright test --shard=3/3 --reporter=blob'
}
post {
always {
archiveArtifacts 'blob-report/**'
}
}
}
}
}
stage('Merge Reports') {
agent { docker { image 'mcr.microsoft.com/playwright:v1.44.0-jammy' } }
steps {
// Download artifacts from all shards
copyArtifacts(projectName: env.JOB_NAME, selector: specific(env.BUILD_NUMBER))
sh 'npx playwright merge-reports --reporter html,junit ./blob-report'
}
post {
always {
junit 'test-results/results.xml'
publishHTML([
reportDir: 'playwright-report',
reportFiles: 'index.html',
reportName: 'Merged Report'
])
}
}
}
}
}Credentials and Secrets
Never hardcode credentials in Jenkinsfile. Use Jenkins Credentials Manager:
environment {
// String credential
BASE_URL = credentials('staging-base-url')
// Username + password credential (creates two env vars)
ADMIN = credentials('admin-credentials')
// Creates: ADMIN_USR (username) and ADMIN_PSW (password)
}
steps {
withCredentials([
string(credentialsId: 'stripe-test-key', variable: 'STRIPE_KEY'),
usernamePassword(
credentialsId: 'db-credentials',
usernameVariable: 'DB_USER',
passwordVariable: 'DB_PASS'
)
]) {
sh 'npx playwright test'
}
}Triggering Tests
On every push
triggers {
// Poll SCM every 5 minutes (less ideal than webhooks)
pollSCM('H/5 * * * *')
}Better: configure webhook in GitHub/GitLab to trigger Jenkins builds automatically.
Scheduled runs (nightly)
triggers {
// Run at 2 AM every night
cron('0 2 * * *')
}On PR creation
Use the GitHub Branch Source or Multibranch Pipeline plugin. Jenkins automatically runs tests on every PR and reports status back to GitHub.
Common Jenkins + Playwright Issues
"No such file or directory: /home/pwuser/..."Playwright runs as a non-root user inside its Docker image. Add args '-u root' to the docker agent or adjust file paths.
Missing system dependencies. Use the official Playwright Docker image — it has everything pre-installed.
HTML report not showing in JenkinsJenkins' Content Security Policy blocks JavaScript in HTML reports. Run this in Jenkins Script Console:
System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "")Or use the HTML Publisher plugin configuration to relax CSP.
Tests pass locally, fail in JenkinsCheck: environment variables, base URL, browser sandbox settings. Add --no-sandbox flag if needed (Docker containers often need this for Chromium).
Minimal Working Jenkinsfile
If you just want something that works:
pipeline {
agent {
docker {
image 'mcr.microsoft.com/playwright:v1.44.0-jammy'
args '-u root'
}
}
stages {
stage('Test') {
steps {
sh 'npm ci'
sh 'npx playwright test --reporter=junit'
}
post {
always {
junit 'test-results/results.xml'
}
}
}
}
}This is the minimum. Start here, then add stages, parallel execution, and notifications as needed.
Summary
| Concept | Implementation |
|---------|---------------|
| Agent | Official Playwright Docker image |
| Secrets | Jenkins Credentials Manager |
| Results | JUnit plugin + publishHTML |
| Parallel | parallel {} block or multiple agents |
| Sharding | --shard=X/Y + merge-reports |
| Scheduling | cron() trigger |
| On PR | GitHub Branch Source plugin + webhook |
Jenkins is more work than GitHub Actions, but once configured it's stable and powerful. The Jenkinsfile approach (pipeline-as-code) means your CI configuration lives in Git alongside your tests — version-controlled, reviewable, and reproducible.
→ See also: CI/CD for QA: GitHub Actions, Jenkins, and GitLab Compared | GitHub Actions for Playwright Tests: The Complete Setup (2026) | Parallel Execution in Playwright: Workers, Shards, and Sharding for Speed