GitHub Action for running Cypress end-to-end tests. Includes NPM installation, custom caching and lots of configuration options.
- Basic
- Explicit version
- Run tests in a given browser
- using Firefox
- using Edge
- using headless mode
- Using Docker image
- Specify environment variables
- Run only some spec files
- Test project in subfolder
- Record results on Cypress Dashboard
- Tag recordings
- Quiet output
- Store test artifacts on GitHub
- Set Cypress config values
- Use specific config file
- Run tests in parallel
- Build app before running the tests
- Start server before running the tests
- Start multiple servers before running the tests
- Wait for server to respond before running the tests
- use custom install command
- use command prefix
- use own custom test command
- pass custom build id when recording to Dashboard
- use different working-directory
- use custom cache key
- run tests on multiple Node versions
- split install and tests into separate jobs
- use custom install commands
- install only Cypress to avoid installing all dependencies
- more examples
name: End-to-end tests
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-16.04
steps:
- name: Checkout
uses: actions/checkout@v1
# Install NPM dependencies, cache them correctly
# and run all Cypress tests
- name: Cypress run
uses: cypress-io/github-action@v2
The workflow file .github/workflows/example-basic.yml shows how Cypress runs on GH Actions using Ubuntu (16, 18, or 20), on Windows, and on Mac without additional OS dependencies necessary.
Note: this package assumes that cypress
is declared as a development dependency in the package.json
file. The cypress
NPM module is required to run Cypress via its NPM module API.
Best practice:
Our examples specify the tag of the action to use listing only the major version @v2
- name: Cypress run
uses: cypress-io/github-action@v2
When using cypress-io/github-action@v2
from your workflow file, you automatically will be using the latest tagged version from this repository. If you want to precisely control the version of this module, use the full tag version, for example:
- name: Cypress run
uses: cypress-io/github-action@v2.2.7
By using the full version tag, you will avoid accidentally using a newer version of the action.
Specify the browser name or path with browser
parameter
name: E2E on Chrome
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-16.04
# let's make sure our tests pass on Chrome browser
name: E2E on Chrome
steps:
- uses: actions/checkout@v1
- uses: cypress-io/github-action@v2
with:
browser: chrome
In order to run Firefox, you need to use non-root user (Firefox security restriction).
name: Firefox
on: push
jobs:
firefox:
runs-on: ubuntu-latest
container:
image: cypress/browsers:node12.16.1-chrome80-ff73
options: --user 1001
steps:
- uses: actions/checkout@v1
- uses: cypress-io/github-action@v2
with:
browser: firefox
Note: the magical user id 1001
works because it matches permissions settings on the home folder, see issue #104
name: Edge
on: push
jobs:
tests:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- uses: cypress-io/github-action@v2
with:
browser: edge
Note: Microsoft has not released Edge for Linux yet, thus you need to run these tests on Windows or Mac runners with Edge preinstalled. You can use cypress info
command to see the browsers installed on the machine.
Run the browser in headless mode
name: Chrome headless
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-16.04
steps:
- uses: actions/checkout@v1
- uses: cypress-io/github-action@v2
with:
browser: chrome
headless: true
You can run tests in a GH Action in your Docker container.
name: E2E in custom container
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-16.04
# Cypress Docker image with Chrome v78
# and Firefox v70 pre-installed
container: cypress/browsers:node12.13.0-chrome78-ff70
steps:
- uses: actions/checkout@v1
- uses: cypress-io/github-action@v2
with:
browser: chrome
Specify the env argument with env
parameter
name: Cypress tests
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-16.04
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Cypress run with env
uses: cypress-io/github-action@v2
with:
env: host=api.dev.local,port=4222
When passing the environment variables this way, unfortunately due to GitHub Actions syntax, the variables should be listed in a single line, which can be hard to read. As an alternative, you can use the step's env
block where every variable can be set on its own line. In this case, you should prefix every variable with CYPRESS_
because such variables are loaded by Cypress automatically. The above code example is equivalent to:
name: Cypress tests
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-16.04
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Cypress run with env
uses: cypress-io/github-action@v2
env:
CYPRESS_host: api.dev.local
CYPRESS_port: 4222
For more examples, see the workflow example below.
Specify the spec files to run with spec
parameter
name: Cypress tests
on: [push]
jobs:
cypress-run:
name: Cypress run
runs-on: ubuntu-16.04
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Cypress run
uses: cypress-io/github-action@v2
with:
spec: cypress/integration/spec1.js
For more information, visit the Cypress command-line docs.
Specify the project to run with project
parameter
name: Cypress tests
on: [push]
jobs:
cypress-run:
name: Cypress run
runs-on: ubuntu-16.04
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Cypress run
uses: cypress-io/github-action@v2
with:
project: ./some/nested/folder
For more information, visit the Cypress command-line docs.
name: Cypress tests
on: [push]
jobs:
cypress-run:
name: Cypress run
runs-on: ubuntu-16.04
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Cypress run
uses: cypress-io/github-action@v2
with:
record: true
env:
# pass the Dashboard record key as an environment variable
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
# pass GitHub token to allow accurately detecting a build vs a re-run build
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Tip 1: We recommend using the action with on: [push]
instead of on: [pull_request]
to get the most accurate information related to the commit on the dashboard. With pull requests, the merge commit is created automatically and might not correspond to a meaningful commit in the repository.
Tip 2: we recommend passing the GITHUB_TOKEN
secret (created by the GH Action automatically) as an environment variable. This will allow correctly identifying every build and avoid confusion when re-running a build.
Tip 3: if running on pull_request
event, the commit message is "merge SHA into SHA", which is not what you want probably. You can overwrite the commit message sent to the Dashboard by setting an environment variable. See issue 124 for details.
You can provide quiet
flag for cypress run to silence any Cypress specific output from stdout
name: example-quiet
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
# Install NPM dependencies, cache them correctly
# and run all Cypress tests with `quiet` parameter
- name: Cypress run
uses: ./
with:
working-directory: examples/quiet
quiet: true
You can pass a single or multiple tags when recording a run. For example
name: tags
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-latest
# let's make sure our "app" works on several versions of Node
strategy:
matrix:
node: [10, 12]
name: E2E on Node v${{ matrix.node }}
steps:
- name: Setup Node
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
- run: node -v
- name: Checkout
uses: actions/checkout@v1
- name: Cypress run
uses: cypress-io/github-action@v2
with:
record: true
tag: node-${{ matrix.node }}
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
The recording will have tags as labels on the run.
You can pass multiple tags using commas like tag: node-10,nightly,staging
.
If you don't record the test run on Cypress Dashboard, you can still store generated videos and screenshots as CI artifacts. See cypress-gh-action-example and the workflow example below
name: Artifacts
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-16.04
name: Artifacts
steps:
- uses: actions/checkout@v1
- uses: cypress-io/github-action@v2
# after the test run completes
# store videos and any screenshots
# NOTE: screenshots will be generated only if E2E test failed
# thus we store screenshots only on failures
# Alternative: create and commit an empty cypress/screenshots folder
# to always have something to upload
- uses: actions/upload-artifact@v1
if: failure()
with:
name: cypress-screenshots
path: cypress/screenshots
# Test run video was always captured, so this action uses "always()" condition
- uses: actions/upload-artifact@v1
if: always()
with:
name: cypress-videos
path: cypress/videos
Specify configuration values with config
parameter
name: Cypress tests
on: [push]
jobs:
cypress-run:
name: Cypress run
runs-on: ubuntu-16.04
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Cypress run
uses: cypress-io/github-action@v2
with:
config: pageLoadTimeout=100000,baseUrl=http://localhost:3000
Specify the path to your config file with config-file
parameter
name: Cypress tests
on: [push]
jobs:
cypress-run:
name: Cypress run
runs-on: ubuntu-16.04
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Cypress run
uses: cypress-io/github-action@v2
with:
config-file: tests/cypress-config.json
You can spin multiple containers running in parallel using strategy: matrix
argument. Just add more dummy items to the containers: [1, 2, ...]
array to spin more free or paid containers. Then use record
and parallel
parameters to load balance tests
name: Parallel Cypress Tests
on: [push]
jobs:
test:
name: Cypress run
runs-on: ubuntu-16.04
strategy:
# when one test fails, DO NOT cancel the other
# containers, because this will kill Cypress processes
# leaving the Dashboard hanging ...
# https://github.com/cypress-io/github-action/issues/48
fail-fast: false
matrix:
# run 3 copies of the current job in parallel
containers: [1, 2, 3]
steps:
- name: Checkout
uses: actions/checkout@v1
# because of "record" and "parallel" parameters
# these containers will load balance all found tests among themselves
- name: Cypress run
uses: cypress-io/github-action@v2
with:
record: true
parallel: true
group: 'Actions example'
env:
# pass the Dashboard record key as an environment variable
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
# Recommended: pass the GitHub token lets this action correctly
# determine the unique run id necessary to re-run the checks
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Warning GITHUB_TOKEN
to get the correct branch and the number of jobs run, making it possible to re-run without the need of pushing an empty commit. If you don't want to use the GITHUB_TOKEN
you can still run your tests without problem with the only note that Cypress Dashboard API connects parallel jobs into a single logical run using GitHub commit SHA plus workflow name. If you attempt to re-run GitHub checks, the Dashboard thinks the run has already ended. In order to truly rerun parallel jobs, push an empty commit with git commit --allow-empty -m "re-run checks" && git push
. As another work around you can generate and cache a custom build id, read Adding a unique build number to GitHub Actions
You can run a build step before starting tests
name: Build
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-16.04
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Cypress run
uses: cypress-io/github-action@v2
with:
build: npm run build
If your tests run against a local server, use start
parameter, the server will run in the background and will shut down after tests complete
name: With server
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-16.04
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Cypress run
uses: cypress-io/github-action@v2
with:
start: npm start
Note: sometimes on Windows you need to run a different start command. You can use start-windows
parameter for this
name: With server
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-16.04
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Cypress run
uses: cypress-io/github-action@v2
with:
# Linux and MacOS
start: npm start
# Takes precedences on Windows
start-windows: npm run start:windows:server
Note: GitHub cleans up the running server processes automatically. This action does not stop them.
You can start multiple server processes. For example, if you have an API to start using npm run api
and the web server to start using npm run web
you can put those commands in start
using comma separation.
name: With servers
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-18.04
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Cypress run
uses: cypress-io/github-action@v2
with:
start: npm run api, npm run web
If you are starting a local server and it takes a while to start, you can add a parameter wait-on
and pass url to wait for the server to respond.
name: After server responds
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-16.04
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Cypress run
uses: cypress-io/github-action@v2
with:
start: npm start
# quote the url to be safe against YML parsing surprises
wait-on: 'http://localhost:8080'
By default, wait-on
will retry for 60 seconds. You can pass a custom timeout in seconds using wait-on-timeout
.
- uses: cypress-io/github-action@v2
with:
start: npm start
wait-on: 'http://localhost:8080/status'
# wait for 2 minutes for the server to respond
wait-on-timeout: 120
You can wait for multiple URLs to respond by separating urls with a comma
- uses: cypress-io/github-action@v2
with:
# API runs on port 3050
# Web server runs on port 8080
start: npm run api, npm run web
# wait for all services to respond
wait-on: 'http://localhost:3050, http://localhost:8080'
The action will wait for the first url to respond, then will check the second url, and so on.
You can even use your own command (usually by using npm
, yarn
, npx
) to wait for the server to respond. For example, if you want to use wait-on utility to ping the server and run the Cypress tests after the server responds:
- uses: cypress-io/github-action@v2
with:
start: npm start
wait-on: 'npx wait-on --timeout 5000 http://localhost:3000'
See example-wait-on.yml workflow file.
If this action times out waiting for the server to respond, please see Debugging section in this README file.
If you want to overwrite the install command
- uses: cypress-io/github-action@v2
with:
install-command: yarn --frozen-lockfile --silent
See example-install-command.yml workflow file.
You can prefix the default test command using the command-prefix
option. This is useful for example when running Percy, which requires the test command to be wrapped with percy exec --
.
name: Visual
on: [push]
jobs:
e2e:
runs-on: ubuntu-16.04
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Cypress run
uses: cypress-io/github-action@v2
with:
start: npm start
# quote the url to be safe against YML parsing surprises
wait-on: 'http://localhost:8080'
# the entire command will automatically be prefixed with "npm"
# and we need the second "npm" to execute "cypress run ..." command line
command-prefix: 'percy exec -- npx'
See live example angular-pizza-creator.
You can overwrite the Cypress run command with your own
steps:
- name: Checkout 🛎
uses: actions/checkout@v1
- name: Custom tests 🧪
uses: cypress-io/github-action@v1
with:
command: npm run e2e:ci
See .github/workflows/example-custom-command.yml file.
You can overwrite ci-build-id
used to link separate machines running tests into a single parallel run.
name: Parallel
on: [push]
jobs:
test:
runs-on: ubuntu-16.04
strategy:
matrix:
# run 3 copies of the current job in parallel
containers: [1, 2, 3]
steps:
- uses: actions/checkout@v1
- uses: cypress-io/github-action@v2
with:
record: true
parallel: true
group: 'Actions example'
ci-build-id: '${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}'
env:
# pass the Dashboard record key as an environment variable
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Tip: see GitHub Actions environment variables and expression syntax.
In a monorepo, the end-to-end test might be placed in a different sub-folder from the application itself, like this
repo/
app/
e2e/
cypress
cypress.json
package.json
You can specify the e2e
working directory when running Cypress tests using working-directory
parameter
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-16.04
steps:
- uses: actions/checkout@v1
- uses: cypress-io/github-action@v2
with:
start: npm start
working-directory: e2e
See cypress-gh-action-monorepo for a running example
Sometimes Cypress and end-to-end tests have their own package.json
file in a subfolder, like
root/
e2e/
(code for installing and running Cypress tests)
package.json
package-lock.json
cypress.json
cypress
(code for running the "app" with "npm start")
package.json
yarn.lock
In that case you can combine this action with bahmutov/npm-install action to install dependencies separately.
name: E2E
on: push
jobs:
test:
runs-on: ubuntu-16.04
steps:
- uses: actions/checkout@master
- name: Install root dependencies
uses: bahmutov/npm-install@v1
- name: Start server in the background
run: npm start &
# Cypress has its own package.json in folder "e2e"
- name: Install Cypress and run tests
uses: cypress-io/github-action@v2
with:
working-directory: e2e
See cypress-gh-action-subfolders for example.
This action should discover Yarn workspace correctly. For example, see folder examples/start-and-yarn-workspaces and workflow file example-start-and-yarn-workspaces.yml
name: example-start-and-yarn-workspaces
on: [push]
jobs:
single:
# the example has Yarn workspace in its "root" folder
# examples/start-and-yarn-workspaces
# and tests in a subfolder like "workspace-1"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: cypress-io/github-action@v2
with:
working-directory: examples/start-and-yarn-workspaces/workspace-1
build: yarn run build
start: yarn start
wait-on: 'http://localhost:5000'
Sometimes the default cache key does not work. For example, if you cannot share the Node modules across Node versions due to native extensions. In that case pass your own cache-key
parameter.
name: End-to-end tests
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-16.04
# let's make sure our "app" works on several versions of Node
strategy:
matrix:
node: [10, 12]
name: E2E on Node v${{ matrix.node }}
steps:
- name: Setup Node
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
- name: Checkout
uses: actions/checkout@v1
# run Cypress tests and record them under the same run
# associated with commit SHA and just give a different group name
- name: Cypress run
uses: cypress-io/github-action@v2
with:
record: true
group: Tests on Node v${{ matrix.node }}
cache-key: node-v${{ matrix.node }}-on-${{ runner.os }}-hash-${{ hashFiles('yarn.lock') }}
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
You can run your tests across multiple Node versions.
name: Node versions
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-latest
strategy:
matrix:
node: [10, 12]
name: E2E on Node v${{ matrix.node }}
steps:
- uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
- uses: actions/checkout@v1
- uses: cypress-io/github-action@v2
Note: because this action uses npm ci
and npx
commands, it requires at least Node 8.12 that includes the version of NPM with those commands.
Sometimes you may want to run additional commands between installation and tests. To enable this use the install
and runTests
parameters.
name: E2E
on: push
jobs:
test:
runs-on: ubuntu-16.04
steps:
- uses: actions/checkout@master
- name: Install dependencies
uses: cypress-io/github-action@v2
with:
# just perform install
runTests: false
- run: yarn lint
- name: Run e2e tests
uses: cypress-io/github-action@v2
with:
# we have already installed all dependencies above
install: false
# Cypress tests and config file are in "e2e" folder
working-directory: e2e
See cypress-gh-action-monorepo for working example.
Finally, you might not need this GH Action at all. For example, if you want to split the NPM dependencies installation from the Cypress binary installation, then it makes no sense to use this action. Instead you can install and cache Cypress yourself. See cypress-gh-action-split-install for working example.
If the project has many dependencies, but you want to install just Cypress you can combine this action with actions/cache
and npm i cypress
commands yourself.
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: |
~/.cache/Cypress
node_modules
key: my-cache-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
- run: npm i cypress
- uses: cypress-io/github-action@v2
with:
install: false
See .github/workflows/example-install-only.yml file.
Name | Description |
---|---|
cypress-gh-action-example | uses Yarn, and runs in parallel on several versions of Node, also different browsers |
cypress-gh-action-monorepo | splits install and running tests commands, runs Cypress from sub-folder |
cypress-gh-action-subfolders | separate folder for Cypress dependencies |
cypress-gh-action-split-install | only install NPM dependencies, then install and cache Cypress binary yourself |
cypress-gh-action-vue-example | project was scaffolded using Vue CLI |
gh-action-and-gh-integration | records to the dashboard and has Cypress GH Integration app installed |
This action installs local dependencies using lock files. If yarn.lock
file is found, the install uses yarn --frozen-lockfile
command. Otherwise it expects to find package-lock.json
and install using npm ci
command.
This action uses several production dependencies. The minimum Node version required to run this action depends on the minimum Node required by the dependencies.
You can see verbose messages from GitHub Actions by setting the following secrets (from Debugging Actions Guide)
ACTIONS_RUNNER_DEBUG: true
ACTIONS_STEP_DEBUG: true
The ACTIONS_RUNNER_DEBUG
will show generic Actions messages, while ACTIONS_STEP_DEBUG
will enable the core.debug(...)
messages from this actions.
The above ACTIONS_STEP_DEBUG
setting enables the debug logs from the action itself. To see the Cypress debug logs add an environment variable to the action:
- name: Cypress tests with debug logs
uses: cypress-io/github-action@v2
env:
DEBUG: 'cypress:*'
If you have a problem with wait-on
not working, you can check the src/ping.js logic from the local machine.
- clone this repository to the local machine
- install dependencies with
npm install
- start your server
- from another terminal call the
ping
yourself to validate the server is responding:
$ node src/ping-cli.js <url>
For example
$ node src/ping.js https://example.cypress.io
pinging url https://example.cypress.io for 30 seconds
::debug::pinging https://example.cypress.io has finished ok
Read DEVELOPMENT.md
- Read our blog post Drastically Simplify Testing on CI with Cypress GitHub Action
- Read Test the Preview Vercel Deploys blog post
- Building actions docs
This GH Action sets an output dashboardUrl
if the run was recorded on Cypress Dashboard, see action.yml. To use this output:
- name: Cypress tests
uses: cypress-io/github-action@v2
# let's give this action an ID so we can refer
# to its output values later
id: cypress
# Continue the build in case of an error, as we need to set the
# commit status in the next step, both in case of success and failure
continue-on-error: true
with:
record: true
env:
CYPRESS_RECORD_KEY: ${{ secrets.RECORDING_KEY }}
- name: Print Dashboard URL
run: |
echo Cypress finished with: ${{ steps.cypress.outcome }}
echo See results at ${{ steps.cypress.outputs.dashboardUrl }}
If your repository does not have package.json
or yarn.json
(maybe it contains a static site and does not need any dependencies), you can run Cypress tests using cypress/included:...
Cypress Docker images. In that case you don't even need this GH Action, instead use the Docker container and write cypress run
command like this example from cypress-gh-action-included
name: included
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-latest
# Docker image with Cypress pre-installed
# https://github.com/cypress-io/cypress-docker-images/tree/master/included
container: cypress/included:3.8.3
steps:
- uses: actions/checkout@v1
- run: cypress run
This is noted as a breaking change ... but you should not see any changes. We have changed how we run Cypress (from using the command line to using the NPM module API), which is a big change. But hopefully our examples are complete and we did not break anyone's code.