mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-13 17:52:38 +02:00
13 KiB
13 KiB
CI: CircleCI, Azure DevOps, and Jenkins
When to use: Running Playwright tests in CI platforms other than GitHub Actions or GitLab.
Table of Contents
- Common Commands
- Jenkins
- CircleCI
- Azure DevOps
- JUnit Reporter Config
- Platform Comparison
- Troubleshooting
- Anti-Patterns
Common Commands
npx playwright install --with-deps # browsers + OS dependencies
npx playwright test --shard=1/4 # parallel sharding
npx playwright merge-reports ./blob-report # combine shard results
npx playwright test --reporter=dot,html # multiple reporters
Jenkins
Declarative Pipeline
// Jenkinsfile
pipeline {
agent {
docker {
image 'mcr.microsoft.com/playwright:v1.48.0-noble'
args '-u root'
}
}
environment {
CI = 'true'
HOME = '/root'
npm_config_cache = "${WORKSPACE}/.npm"
}
options {
timeout(time: 30, unit: 'MINUTES')
disableConcurrentBuilds()
}
stages {
stage('Install') {
steps {
sh 'npm ci'
}
}
stage('Test') {
steps {
sh 'npx playwright test'
}
post {
always {
junit allowEmptyResults: true,
testResults: 'results/junit.xml'
archiveArtifacts artifacts: 'pw-report/**',
allowEmptyArchive: true
archiveArtifacts artifacts: 'results/**',
allowEmptyArchive: true
}
}
}
}
post {
failure {
echo 'Tests failed!'
}
cleanup {
cleanWs()
}
}
}
Parallel Shards
// Jenkinsfile (sharded)
pipeline {
agent none
environment {
CI = 'true'
HOME = '/root'
}
options {
timeout(time: 30, unit: 'MINUTES')
}
stages {
stage('Test') {
parallel {
stage('Shard 1') {
agent {
docker {
image 'mcr.microsoft.com/playwright:v1.48.0-noble'
args '-u root'
}
}
steps {
sh 'npm ci'
sh 'npx playwright test --shard=1/4'
}
post {
always {
archiveArtifacts artifacts: 'blob-report/**',
allowEmptyArchive: true
}
}
}
stage('Shard 2') {
agent {
docker {
image 'mcr.microsoft.com/playwright:v1.48.0-noble'
args '-u root'
}
}
steps {
sh 'npm ci'
sh 'npx playwright test --shard=2/4'
}
post {
always {
archiveArtifacts artifacts: 'blob-report/**',
allowEmptyArchive: true
}
}
}
stage('Shard 3') {
agent {
docker {
image 'mcr.microsoft.com/playwright:v1.48.0-noble'
args '-u root'
}
}
steps {
sh 'npm ci'
sh 'npx playwright test --shard=3/4'
}
post {
always {
archiveArtifacts artifacts: 'blob-report/**',
allowEmptyArchive: true
}
}
}
stage('Shard 4') {
agent {
docker {
image 'mcr.microsoft.com/playwright:v1.48.0-noble'
args '-u root'
}
}
steps {
sh 'npm ci'
sh 'npx playwright test --shard=4/4'
}
post {
always {
archiveArtifacts artifacts: 'blob-report/**',
allowEmptyArchive: true
}
}
}
}
}
}
}
CircleCI
Basic Pipeline
# .circleci/config.yml
version: 2.1
executors:
pw:
docker:
- image: mcr.microsoft.com/playwright:v1.48.0-noble
working_directory: ~/app
jobs:
install:
executor: pw
steps:
- checkout
- restore_cache:
keys:
- deps-{{ checksum "package-lock.json" }}
- run: npm ci
- save_cache:
key: deps-{{ checksum "package-lock.json" }}
paths:
- node_modules
- persist_to_workspace:
root: .
paths:
- node_modules
test:
executor: pw
parallelism: 4
steps:
- checkout
- attach_workspace:
at: .
- run:
name: Run tests
command: |
npx playwright test --shard=$((CIRCLE_NODE_INDEX + 1))/$CIRCLE_NODE_TOTAL
- store_artifacts:
path: pw-report
destination: pw-report
- store_artifacts:
path: results
destination: results
- store_test_results:
path: results/junit.xml
workflows:
test:
jobs:
- install
- test:
requires:
- install
Using Orbs
# .circleci/config.yml
version: 2.1
orbs:
node: circleci/node@latest
executors:
pw:
docker:
- image: mcr.microsoft.com/playwright:v1.48.0-noble
jobs:
e2e:
executor: pw
parallelism: 4
steps:
- checkout
- node/install-packages
- run:
name: Run tests
command: npx playwright test --shard=$((CIRCLE_NODE_INDEX + 1))/$CIRCLE_NODE_TOTAL
- store_artifacts:
path: pw-report
- store_test_results:
path: results/junit.xml
workflows:
main:
jobs:
- e2e
Azure DevOps
Basic Pipeline
# azure-pipelines.yml
trigger:
branches:
include:
- main
pr:
branches:
include:
- main
pool:
vmImage: "ubuntu-latest"
variables:
CI: "true"
npm_config_cache: $(Pipeline.Workspace)/.npm
steps:
- task: NodeTool@0
inputs:
versionSpec: "20.x"
displayName: "Install Node.js"
- task: Cache@2
inputs:
key: 'npm | "$(Agent.OS)" | package-lock.json'
restoreKeys: |
npm | "$(Agent.OS)"
path: $(npm_config_cache)
displayName: "Cache npm"
- script: npm ci
displayName: "Install dependencies"
- script: npx playwright install --with-deps
displayName: "Install browsers"
- script: npx playwright test
displayName: "Run tests"
- task: PublishTestResults@2
condition: always()
inputs:
testResultsFormat: "JUnit"
testResultsFiles: "results/junit.xml"
mergeTestResults: true
testRunTitle: "E2E Tests"
displayName: "Publish results"
- task: PublishPipelineArtifact@1
condition: always()
inputs:
targetPath: pw-report
artifact: pw-report
publishLocation: "pipeline"
displayName: "Upload report"
With Sharding
# azure-pipelines.yml
trigger:
branches:
include:
- main
pr:
branches:
include:
- main
variables:
CI: "true"
stages:
- stage: Test
jobs:
- job: E2E
pool:
vmImage: "ubuntu-latest"
strategy:
matrix:
shard1:
SHARD: "1/4"
shard2:
SHARD: "2/4"
shard3:
SHARD: "3/4"
shard4:
SHARD: "4/4"
steps:
- task: NodeTool@0
inputs:
versionSpec: "20.x"
- script: npm ci
displayName: "Install dependencies"
- script: npx playwright install --with-deps
displayName: "Install browsers"
- script: npx playwright test --shard=$(SHARD)
displayName: "Run tests (shard $(SHARD))"
- task: PublishPipelineArtifact@1
condition: always()
inputs:
targetPath: blob-report
artifact: blob-report-$(System.JobPositionInPhase)
displayName: "Upload blob report"
- stage: Report
dependsOn: Test
condition: always()
jobs:
- job: MergeReports
pool:
vmImage: "ubuntu-latest"
steps:
- task: NodeTool@0
inputs:
versionSpec: "20.x"
- script: npm ci
displayName: "Install dependencies"
- task: DownloadPipelineArtifact@2
inputs:
patterns: "blob-report-*/**"
path: all-blob-reports
displayName: "Download blob reports"
- script: npx playwright merge-reports --reporter=html ./all-blob-reports
displayName: "Merge reports"
- task: PublishPipelineArtifact@1
inputs:
targetPath: pw-report
artifact: pw-report
displayName: "Upload merged report"
JUnit Reporter Config
All platforms benefit from JUnit output for native test result display:
// playwright.config.ts
import { defineConfig } from "@playwright/test";
export default defineConfig({
reporter: process.env.CI
? [
["dot"],
["html", { open: "never" }],
["junit", { outputFile: "results/junit.xml" }],
]
: [["html", { open: "on-failure" }]],
});
Platform Comparison
| Feature | CircleCI | Azure DevOps | Jenkins |
|---|---|---|---|
| Docker support | docker: executor |
vmImage or container jobs |
Docker Pipeline plugin |
| Parallelism | parallelism: N + CIRCLE_NODE_INDEX |
strategy.matrix |
parallel stages |
| Artifact upload | store_artifacts |
PublishPipelineArtifact@1 |
archiveArtifacts |
| JUnit integration | store_test_results |
PublishTestResults@2 |
junit step |
| Shard variable | $((CIRCLE_NODE_INDEX + 1))/$CIRCLE_NODE_TOTAL |
Define in matrix: SHARD: '1/4' |
Hardcode per stage |
| Cache key | checksum "package-lock.json" |
Cache@2 with key template |
stash/unstash |
| Secrets | Context + env variables | Variable groups | Credentials plugin |
Troubleshooting
Jenkins: "Browser closed unexpectedly"
Running as non-root in container causes sandbox issues.
agent {
docker {
image 'mcr.microsoft.com/playwright:v1.48.0-noble'
args '-u root'
}
}
environment {
HOME = '/root'
}
CircleCI: "Executable doesn't exist"
Image version mismatch with @playwright/test version. Use latest tag or match versions:
docker:
- image: mcr.microsoft.com/playwright:v1.48.0-noble
Azure DevOps: Test results not showing
Missing JUnit reporter or PublishTestResults@2 task:
reporter: [['junit', { outputFile: 'results/junit.xml' }]],
- task: PublishTestResults@2
condition: always()
inputs:
testResultsFormat: "JUnit"
testResultsFiles: "results/junit.xml"
Shard index off by one
CircleCI's CIRCLE_NODE_INDEX is 0-based, Playwright's --shard is 1-based:
# CircleCI - add 1
command: npx playwright test --shard=$((CIRCLE_NODE_INDEX + 1))/$CIRCLE_NODE_TOTAL
Anti-Patterns
| Anti-Pattern | Problem | Solution |
|---|---|---|
Missing --with-deps on bare metal |
OS libs missing, browser launch fails | Use Playwright Docker image or --with-deps |
| No JUnit reporter | CI can't display test results | Add ['junit', { outputFile: 'results/junit.xml' }] |
| No job timeout | Hung tests consume resources indefinitely | Set explicit timeout (20-30 min) |
| No artifact upload on success | Can't verify passing results | Always upload reports (condition: always()) |
| Non-root in container without setup | Permission errors on browser binaries | Run as root or configure permissions |
| Hardcoded shard count | Must update multiple places | Use CI-native variables |