Introduction
In the previous tutorials, we learned why CI/CD matters for Playwright automation and how Docker creates a consistent test execution environment. Now it is time to connect our Playwright framework to a real Continuous Integration pipeline.
In this tutorial, we will use GitHub Actions to automatically execute Playwright tests whenever code is pushed to a GitHub repository or a pull request is created.
Instead of manually opening a terminal and running:
npx playwright testGitHub Actions will perform the entire process for us.
The pipeline will:
- Download the automation project
- Install Node.js
- Install project dependencies
- Install Playwright browsers
- Execute the test suite
- Generate an HTML report
- Upload the report as an artifact
- Preserve debugging files when tests fail
By the end of this tutorial, you will have a working Playwright CI pipeline similar to the pipelines used by professional software development teams.
What Is GitHub Actions?
GitHub Actions is GitHub's built-in automation platform. It allows teams to create workflows that automatically build, test, package, and deploy applications.
A GitHub Actions workflow is defined using a YAML file stored inside the repository.
According to GitHub's official documentation, a workflow is a configurable automated process made up of one or more jobs. Each workflow is defined in a YAML configuration file.
A workflow can start when a specific event occurs, such as:
- Code is pushed
- A pull request is created
- A pull request is updated
- A release is published
- A user starts the workflow manually
- A scheduled time is reached
For Playwright automation, the most common triggers are:
Push to main branch
Pull request to main branch
Manual execution
Nightly scheduled executionGitHub Actions is especially convenient because it is directly integrated with GitHub repositories. You do not need to configure a separate Jenkins server or maintain additional infrastructure to create your first pipeline.
Why Use GitHub Actions with Playwright?
Running Playwright tests locally is useful during development, but local execution does not protect the entire development team.
Consider this situation:
- A developer creates a new feature.
- The developer runs only a few tests locally.
- The code is pushed to GitHub.
- Another feature unexpectedly breaks.
- The problem is discovered after deployment.
A CI pipeline helps prevent this situation.
Whenever code changes, GitHub Actions can automatically run the complete Playwright test suite. If a critical test fails, the workflow fails and the team can investigate the problem before merging or deploying the code.
Playwright officially supports execution in CI environments and provides sample GitHub Actions configurations. A standard setup installs dependencies, installs the required browsers, runs the tests, and generates an HTML report.
Understanding the GitHub Actions Structure
Before writing our workflow, let's understand the main GitHub Actions concepts.
A typical structure looks like this:
Workflow
└── Job
└── Step
└── Action or CommandWorkflow
A workflow is the complete automation process. For example: Playwright Test Pipeline. It may contain one or more jobs.
Job
A job is a group of steps that runs on a machine called a runner. For example: Run Playwright Tests. Another job could be: Deploy Application.
Step
A step is an individual task inside a job. Examples: Checkout repository, Install Node.js, Install dependencies, Run Playwright tests, Upload report.
Action
An action is a reusable automation component.
uses: actions/checkout@v4This action downloads the repository code into the GitHub Actions runner.
Runner
A runner is the machine where the workflow executes. GitHub provides hosted runners for Ubuntu, Windows, and macOS. For most Playwright CI pipelines, Ubuntu is the preferred starting point because it is generally fast, widely supported, and suitable for headless browser execution.
Project Requirements
Before creating the pipeline, make sure your Playwright project contains the following files:
playwright-framework/
│
├── tests/
│ └── example.spec.ts
│
├── playwright.config.ts
├── package.json
├── package-lock.json
└── .gitignoreYour package.json should include Playwright as a development dependency. Example:
{
"name": "playwright-framework",
"version": "1.0.0",
"scripts": {
"test": "playwright test",
"test:headed": "playwright test --headed",
"report": "playwright show-report"
},
"devDependencies": {
"@playwright/test": "^1.0.0",
"typescript": "^5.0.0"
}
}Do not copy the sample version numbers blindly into an existing project. Your package.json and package-lock.json should use the versions currently installed and tested by your team.
Step 1: Create the Workflow Directory
GitHub Actions workflow files must be stored inside this directory:
.github/workflows/From the project root, create the folders:
mkdir -p .github/workflowsThen create a new file: .github/workflows/playwright.yml
Your project structure should now look like this:
playwright-framework/
│
├── .github/
│ └── workflows/
│ └── playwright.yml
│
├── tests/
├── playwright.config.ts
├── package.json
└── package-lock.jsonThe workflow filename can be different, but it must use the .yml or .yaml extension.
Step 2: Create the Basic Playwright Workflow
Add the following configuration to playwright.yml:
name: Playwright Tests
on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:
jobs:
playwright-tests:
name: Run Playwright Tests
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test
- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-html-report
path: playwright-report/
retention-days: 14This is a complete working CI pipeline. Let's understand every section.
Understanding the Workflow Name
The first line defines the workflow name:
name: Playwright TestsThis name appears in the Actions tab of the GitHub repository. Choose a clear and meaningful name, especially when the repository contains multiple workflows. For example: Playwright Tests, API Regression Tests, UI Smoke Tests, Production Deployment.
Understanding Workflow Triggers
The on section defines when the pipeline should start.
on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:Push Trigger
This starts the pipeline when code is pushed to the main branch.
Pull Request Trigger
This starts the pipeline when a pull request targets the main branch. This is extremely important in professional projects. Before code is merged, the Playwright tests can validate that the proposed changes have not broken critical application functionality.
Manual Trigger
workflow_dispatch allows an authorized user to start the workflow manually from the GitHub Actions page. Manual execution is useful when QA wants to rerun tests, a test environment has been refreshed, a deployment needs additional validation, or a previous failure was caused by temporary infrastructure problems.
Understanding the Job
The jobs section contains one job named playwright-tests. The identifier is used internally by GitHub Actions. The readable job name is set with the name field. The job runs on an Ubuntu runner using runs-on: ubuntu-latest.
GitHub creates a temporary virtual machine for the workflow, executes the job, and removes the machine after completion. This means every pipeline run starts with a clean environment.
Why Set a Timeout?
We added timeout-minutes: 30. This prevents a broken or hanging test suite from running indefinitely. A workflow might become stuck because of an unavailable test environment, a server that never responds, a test waiting for an incorrect condition, a browser process that does not close, or a network-related problem.
A timeout protects your CI resources and makes failures easier to identify. The correct timeout depends on the size of your test suite. A smoke suite may need only 10 minutes, while a large regression suite may need significantly more time.
Step 3: Checkout the Repository
The first workflow step is:
- name: Checkout repository
uses: actions/checkout@v4The GitHub runner begins as a clean machine. It does not automatically contain your repository files. The checkout action downloads the repository code so the remaining commands can access tests, configuration files, page objects, API clients, test data, and package files.
Without this step, commands such as npm ci and npx playwright test would not find the project.
Step 4: Install Node.js
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: npmPlaywright for TypeScript and JavaScript requires Node.js. The Node.js version used in CI should match the version supported by your project. A professional team may define the version in .nvmrc, package.json, Dockerfile, and the GitHub Actions workflow. Keeping the Node.js version consistent reduces differences between local and CI execution.
The line cache: npm enables npm dependency caching. GitHub's Node.js workflow documentation supports caching npm dependencies through the setup-node action. Caching can make later workflow runs faster because required package data may be restored instead of downloaded again. Caching does not replace dependency installation. The workflow must still run npm ci.
Step 5: Install Project Dependencies
- name: Install dependencies
run: npm ciYou may be familiar with npm install. However, npm ci is generally preferred inside CI pipelines. It installs dependencies based on the exact versions recorded in package-lock.json. This provides more predictable execution.
The workflow should fail if the lock file and package.json are not synchronized. That is useful because inconsistent dependency files can produce different results on different machines. For reliable CI execution, commit both package.json and package-lock.json. Do not add package-lock.json to .gitignore.
Step 6: Install Playwright Browsers
- name: Install Playwright browsers
run: npx playwright install --with-depsPlaywright tests require browser binaries such as Chromium, Firefox, and WebKit. The --with-deps option also installs required Linux system dependencies.
If your project only uses Chromium, you can reduce installation time by installing only that browser:
- name: Install Chromium
run: npx playwright install --with-deps chromiumHowever, install all browsers when the framework is expected to perform cross-browser validation.
Step 7: Run the Playwright Tests
- name: Run Playwright tests
run: npx playwright testThis uses the configuration from playwright.config.ts. A basic configuration might look like this:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: Boolean(process.env.CI),
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 2 : undefined,
reporter: [
['list'],
['html', { outputFolder: 'playwright-report', open: 'never' }]
],
use: {
baseURL: process.env.BASE_URL || 'https://example.com',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure'
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] }
}
]
});The setting forbidOnly: Boolean(process.env.CI) prevents accidentally committed tests containing test.only() from passing through the pipeline while skipping the rest of the suite.
The line retries: process.env.CI ? 2 : 0 enables retries in CI. Retries can help identify intermittent failures, but they should not be used to hide unstable tests. A test that regularly passes only after retrying should be investigated.
Step 8: Upload the HTML Report
- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-html-report
path: playwright-report/
retention-days: 14The important condition is if: always(). Without this condition, the report upload step may be skipped when the test command fails. But the report is often most valuable when tests fail. The always() condition tells GitHub Actions to attempt the upload whether the test step passes or fails.
The report is uploaded as a workflow artifact named playwright-html-report. Artifacts are files created during a workflow that can be stored and reviewed after the job completes.
How to Download the Playwright Report
After the workflow finishes:
- Open the GitHub repository.
- Select the Actions tab.
- Open the Playwright workflow.
- Select the latest workflow run.
- Scroll to the Artifacts section.
- Download playwright-html-report.
- Extract the downloaded ZIP file.
- Open index.html.
You can also open the report locally using:
npx playwright show-report playwright-reportUpload Debugging Artifacts
The HTML report is useful, but enterprise teams often preserve additional debugging files. Playwright may generate screenshots, videos, trace files, test attachments, error context, and console output. These files are commonly stored inside test-results/.
Add another upload step:
- name: Upload test results
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-test-results
path: test-results/
retention-days: 14
if-no-files-found: ignoreThis step runs only when the workflow fails. The option if-no-files-found: ignore prevents the artifact step from failing when the folder does not exist or contains no files.
Passing Environment Variables
Most real Playwright frameworks need environment-specific values such as Base URL, API URL, Username, Password, and Authentication token.
A non-sensitive value can be defined directly in the workflow:
- name: Run Playwright tests
run: npx playwright test
env:
BASE_URL: https://qa.example.comSensitive values should not be hardcoded. Store secrets in GitHub via Repository → Settings → Secrets and variables → Actions → New repository secret.
Suppose you create TEST_USERNAME and TEST_PASSWORD. Use them like this:
- name: Run Playwright tests
run: npx playwright test
env:
BASE_URL: https://qa.example.com
TEST_USERNAME: ${{ secrets.TEST_USERNAME }}
TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}Inside the Playwright test, access them through process.env:
const username = process.env.TEST_USERNAME;
const password = process.env.TEST_PASSWORD;Running Only Smoke Tests in Pull Requests
A full regression suite may take too long to run for every pull request. A common enterprise strategy is: Pull request → Smoke tests, Main branch push → Full regression tests, Nightly schedule → Complete cross-browser regression.
You can use a test tag:
test('user can log in @smoke', async ({ page }) => {
// Test implementation
});Then run only smoke tests:
- name: Run smoke tests
run: npx playwright test --grep "@smoke"Later in this series, we will create more advanced workflows with separate jobs, environments, browser matrices, and sharding.
Common GitHub Actions Problems
Browser Dependencies Are Missing
Error: Host system is missing dependencies to run browsers. Solution:
npx playwright install --with-depsPackage Lock File Is Missing
Error: npm ci can only install with an existing package-lock.json. Solution: run npm install locally, then commit package-lock.json.
Tests Pass Locally but Fail in CI
Possible causes include hardcoded local URLs, missing secrets, different Node.js versions, tests depending on local files, incorrect time-zone assumptions, tests sharing state, insufficient waiting or synchronization, and the application being unavailable from the runner. Use traces, screenshots, videos, and reports to investigate.
Report Is Not Uploaded After Failure
Make sure the artifact step includes if: always().
Pipeline Is Too Slow
Possible improvements include installing only required browsers, caching npm dependencies, running smoke tests for pull requests, using parallel execution, using test sharding, avoiding unnecessary setup, and reusing authenticated state safely. We will cover matrices, parallel execution, and sharding in later tutorials.
Enterprise Best Practices
For a professional Playwright GitHub Actions pipeline:
- Use npm ci instead of npm install.
- Keep Node.js versions consistent.
- Never hardcode credentials.
- Store sensitive values in GitHub Secrets.
- Upload reports even when tests fail.
- Preserve traces, screenshots, and videos.
- Use meaningful workflow and job names.
- Add execution timeouts.
- Prevent committed test.only() with forbidOnly.
- Separate smoke and regression execution.
- Investigate flaky tests instead of relying on retries.
- Protect the main branch with required status checks.
- Keep workflow files inside version control.
- Review action versions periodically.
- Use the minimum repository permissions required by the workflow.
The pipeline should be treated as production code. It needs proper naming, maintainability, security, documentation, and code review.
Complete Pipeline Flow
Our first GitHub Actions pipeline now follows this process:
Developer Pushes Code
│
▼
GitHub Detects the Change
│
▼
GitHub Actions Starts
│
▼
Create Ubuntu Runner
│
▼
Checkout Repository
│
▼
Install Node.js
│
▼
Restore npm Cache
│
▼
Install Dependencies
│
▼
Install Playwright Browsers
│
▼
Execute Playwright Tests
│
▼
Generate HTML Report
│
▼
Upload Report and Debugging Files
│
▼
Display Pass or Fail StatusThis is the foundation of an enterprise Playwright CI/CD pipeline.
Key Takeaways
GitHub Actions transforms your Playwright framework from a locally executed test project into an automated quality gate. Every push and pull request can now be validated through a consistent process. The runner installs the required dependencies, installs Playwright browsers, executes the tests, generates reports, and preserves debugging artifacts.
This gives the development team fast feedback and reduces the chance that broken functionality will be merged into the main branch.
You also learned how to create a GitHub Actions workflow, configure push, pull-request, and manual triggers, run the workflow on an Ubuntu runner, install Node.js, cache npm dependency data, install packages with npm ci, install Playwright browsers and Linux dependencies, execute Playwright tests, upload HTML reports, preserve screenshots, videos, and traces, pass environment variables, read credentials from GitHub Secrets, and troubleshoot common CI failures.
However, this pipeline currently executes a single standard test job. Real enterprise projects often need different configurations for development, QA, staging, and production environments.
In the next tutorial, we will build a multiple-environment Playwright pipeline that can switch base URLs, API endpoints, credentials, and configuration values without changing the test code. That will take our framework one step closer to a scalable, production-ready CI/CD solution.
Get Playwright tutorials in your inbox
Weekly tips, real-world examples, and framework patterns – no spam, unsubscribe anytime.
Playwright Framework Series
View all →- 1Getting Started with Playwright: Installation, Setup, and Your First Test
- 2Playwright Locators: The Complete Guide with Real-World Examples
- 3Playwright Actions: Complete Guide to Click, Fill, Hover, Keyboard, Mouse & File Upload
- 4Playwright Assertions: The Complete Guide with Real-World Examples
- 5Playwright Auto Waiting: The Complete Guide with Real-World Examples
- 6Playwright Fixtures: The Complete Guide with Real-World Examples
- 7Playwright Browser Context & Multiple Tabs: The Complete Guide with Real-World Examples
- 8Playwright Authentication & Session Management: Complete Guide with Enterprise Examples
- 9Playwright Network Interception & API Mocking: Complete Guide with Real-World Examples
- 10Playwright Page Object Model (POM): The Complete Guide with Enterprise Examples
- 11Page Object Model with Playwright: A Practical Guide
- 12How to Build a Robust Professional Playwright Framework from Scratch (Step-by-Step)
- 13Building an Enterprise Playwright Framework from Scratch
API Testing Series
View all →- 1Playwright API Testing: The Complete Guide with Real-World Examples
- 2Part 1 : Playwright API Testing Tutorial: Build Enterprise-Level API Automation Framework
- 3Part 2: Playwright API Testing Tutorial: Setting Up Project & Sending Your First API Request
- 4Part 3: Mastering CRUD Operations in Playwright API Testing
- 5Part 4: Authentication in Playwright API Testing (Bearer Token, JWT, API Key & Reusable Fixtures)
- 6Part 5: Building an Enterprise-Level Playwright API Automation Framework
- 7Part 6: API Models, Schema Validation & Test Data Management in Playwright
- 8Part 7: Custom Fixtures, Hooks, Logging & Parallel Execution in Playwright API Testing
- 9Part 8: Building a Production-Ready Playwright API Framework (Environment Management, CI/CD, Reporting & Best Practices)
- 10Part 9: Advanced Playwright API Testing Techniques for Enterprise Automation
- 11Part 10: Building a Complete Enterprise Playwright API Automation Framework (Final Part)
- 12API Testing with Playwright: Request Context, Auth, and Assertions