A Practical Guide to Cross-Browser Testing
One of the most practical features of GitHub Actions is the ability to manually trigger workflows and pass parameters at runtime. This is especially useful for end-to-end (E2E) testing, where you may want to select which browser to run the tests against rather than hard-coding that choice.
In this post, I'll walk you through a GitHub Actions workflow written in YAML that allows you to manually trigger Cypress tests and select the target browser directly from the GitHub UI.
The Full Workflow
Here's the workflow I'll be explaining:
name: End-to-end tests 🧪
on:
workflow_dispatch:
inputs:
browser:
description: "Browser to run tests"
type: choice
required: true
default: chrome
options:
- chrome
- edge
- electron
- firefox
- safari
jobs:
cypress-run:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Install WebKit system deps (Safari)
if: ${{ github.event.inputs.browser == 'safari' }}
run: npx playwright install-deps webkit
- name: Cypress run
uses: cypress-io/github-action@v6
with:
command: npm run test:${{ github.event.inputs.browser }}
- name: Upload screenshots (selected browser, on failure)
if: failure()
uses: actions/upload-artifact@v4
with:
name: screenshots-${{ github.event.inputs.browser }}
path: cypress/screenshots
if-no-files-found: ignore
Naming the Workflow
name: End-to-end tests 🧪
This is the friendly name shown in the GitHub Actions UI. Adding an emoji is optional, but it makes workflows easier to scan—especially when you have many of them.
Manual Trigger with workflow_dispatch
on:
workflow_dispatch:
The workflow_dispatch event enables manual execution of the workflow. This means:
- The workflow won't run automatically on
pushorpull_request - A "Run workflow" button will appear in the Actions tab
This is ideal for:
- Ad-hoc test runs
- Debugging browser-specific issues
- Running tests before a release
Defining Input Parameters
inputs:
browser:
description: "Browser to run tests"
type: choice
required: true
default: chrome
options:
- chrome
- edge
- electron
- firefox
- safari
This is the heart of the workflow.
What's happening here?
-
browseris a required input - The user must select one value from a predefined list
- The default option is
chrome
On GitHub, this renders as a dropdown selector in the UI.
This prevents invalid values and makes the workflow safer and more user-friendly.
Defining the Job
jobs:
cypress-run:
runs-on: ubuntu-24.04
- The workflow has a single job called
cypress-run - It runs on the
ubuntu-24.04GitHub-hosted runner
Ubuntu runners are commonly used for Cypress because they're fast, stable, and well supported by the Cypress GitHub Action.
Step 1: Checking Out the Code
- name: Checkout
uses: actions/checkout@v6
This step pulls your repository code into the runner so that:
- Cypress tests
package.json- Configuration files
are available during execution.
This step is required in almost every CI workflow.
Step 2: Installing Safari (WebKit) Dependencies Conditionally
- name: Install WebKit system deps (Safari)
if: ${{ github.event.inputs.browser == 'safari' }}
run: npx playwright install-deps webkit
This is an excellent example of conditional execution in GitHub Actions.
Why is this needed?
- Cypress runs Safari tests via WebKit
- WebKit requires additional system dependencies on Linux
- These dependencies are unnecessary for other browsers
What the condition does
The if expression ensures that this step:
- Runs only when
safariis selected - Is skipped for all other browsers
This keeps the workflow:
- Faster
- Cleaner
- Easier to maintain
Notes:
It's worth mentioning that for Cypress to work with WebKit:
- The
experimentalWebKitSupportproperty has to be set totruein thecypress.config.jsfile -
playwright-webkithas to be installed as a dev dependency (e.g.,npm i playwright-webkit -D)
Pro tip: Make sure to version not only the package.json file, but also the package-lock.json.
Step 3: Running Cypress with the Selected Browser
- name: Cypress run
uses: cypress-io/github-action@v6
with:
command: npm run test:${{ github.event.inputs.browser }}
This step uses the official Cypress GitHub Action image.
Key idea
The browser input is injected dynamically into the command:
npm run test:chrome
npm run test:firefox
npm run test:safari
This implies that your package.json contains scripts like:
{
"scripts": {
"test:chrome": "cypress run --browser chrome",
"test:firefox": "cypress run --browser firefox",
"test:safari": "cypress run --browser webkit"
}
}
This pattern keeps:
- The workflow generic
- Browser-specific logic inside your project configuration
Step 4: Uploading Screenshots on Failure
- name: Upload screenshots (selected browser, on failure)
if: failure()
uses: actions/upload-artifact@v4
with:
name: screenshots-${{ github.event.inputs.browser }}
path: cypress/screenshots
if-no-files-found: ignore
This step runs only if the job fails.
What it does
- Uploads Cypress screenshots as workflow artifacts
- Names the artifact based on the selected browser
- Avoids failing the workflow if no screenshots exist
Why this matters
When an E2E test fails:
- Screenshots provide visual context
- Browser-specific issues are easier to diagnose
- Artifacts are preserved for later analysis
Final Thoughts
This workflow demonstrates a clean and scalable way to:
- Manually trigger Cypress tests
- Select the target browser at runtime
- Handle browser-specific dependencies
- Collect meaningful artifacts on failure
It's a powerful pattern for teams that care about cross-browser confidence without overloading their CI pipelines with unnecessary runs.
More than just automation, this approach puts control and observability back in the team's hands—exactly where quality belongs.
For the complete implementation, take a look at the cross-browser-testing-gha GitHub repository.
Would you like to learn E2E Testing with Cypress from scratch until your tests are running on GitHub Actions and integrated with the Cypress Cloud?
Consider subscribing to my course: "Cypress, from Zero to the Cloud."
Top comments (0)