Drastically Simplify Testing on CI with Cypress GitHub Action

November 20, 2019

•

By Gleb Bahmutov

Setting up continuous integration server is ... no one's favorite activity. Copying and pasting config YAML or JSON files, pushing code, waiting for the server to reject your commit due to wrong indentation or a missing comma - it is a very frustrating experience, we get it.

This is why we are so excited to introduce cypress-io/github-action - an action we specifically coded to make running Cypress end-to-end on GitHub as easy as 🥧. Here is a typical configuration file you should add to your repo's .github/workflows/main.yml

name: End-to-end tests
on: [push]
jobs:
  cypress-run:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v1

      - name: Cypress run
        uses: cypress-io/github-action@v1

Note: check the up-to-date documentation in the repository for the current action version and usage, as this blog post is a time capsule.

The above fragment looks deceptively simple: check out code, run Cypress tests. But behind the scenes, cypress-io/github-action@v1 installs NPM dependencies (using npm ci or yarn depending on the files found), caches Node modules and Cypress binary, and finally runs the tests. Do you want to build the web application and start the server? No problem, Cypress GH Action accepts these commands as parameters, for example:

name: End-to-end tests
on: [push]
jobs:
  cypress-run:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v1

      - name: Cypress run
        uses: cypress-io/github-action@v1
        with:
          build: npm run build
          start: npm start
          wait-on: http://localhost:3030

There is even a parameter wait-on that checks that a local server url responds before running tests - because those WebPack dev servers do not start instantly!

Let's kick the tests up a notch. When running lots of end-to-end tests, CI jobs take a long time, which is bad. Cypress Dashboard has parallelization feature where multiple CI workers can be orchestrated to load balance all spec files, finishing in 1/N time. Here is an example GH Action workflow that splits entire test load across 3 machines using strategy > matrix > machines: [1, 2, 3] syntax.

name: Parallel Cypress Tests

on: [push]

jobs:
  test:
    name: Cypress run
    runs-on: ubuntu-latest
    strategy:
      matrix:
        # the actual items in the array do not matter,
        # just the number - to force CI to sping 3 copies
        # of the current job in parallel
        machines: [1, 2, 3]
    steps:
      - name: Checkout
        uses: actions/checkout@v1

      # because of "record" and "parallel" parameters
      # these machines will load balance all found tests among themselves
      - name: Cypress run
        uses: cypress-io/github-action@v1
        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 }}

Note that you need to pass record: true and parallel: true parameters and the CYPRESS_RECORD_KEY secret token to authenticate with the Cypress Dashboard. The result is visible in the Dashboard web application: 3 machines split all specs

A parallel test run using Cypress GitHub Action

The power of GitHub Actions comes from Azure CI I believe; the Actions can be executed on all 3 major platforms: Windows, Linux and MacOS, even if you use the free shared runners. With Cypress GH Action, you can set up a parallel build on all 3 platforms where each group of workers splits all tests on that platform - you just need to pass the group parameter with the OS name to tie them together.

name: Parallel on every OS
on: [push, pull_request]
jobs:
  parallel-runs-across-platforms:
    name: every OS
    strategy:
      matrix:
        # run 2 copies of the current job in parallel
        # and they will load balance all specs
        os: ['ubuntu-latest', 'windows-latest', 'macos-latest']
        machines: [1, 2]
    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout
        uses: actions/checkout@v1

      # because of "record" and "parallel" parameters
      # these containers will load balance all found tests among themselves
      - name: run tests
        uses: cypress-io/github-action@v1
        timeout-minutes: 10
        with:
          record: true
          parallel: true
          group: Parallel 2x on ${{ matrix.os }}
          # on Mac and Linux we can use "npm start"
          start: npm start
          # but for this particular project on Windows we need a different start command
          start-windows: npm run start:ci:windows
        env:
          # pass the Dashboard record key as an environment variable
          CYPRESS_RECORD_KEY: ${{ secrets.dashboardRecordKey }}

All test runners contact the Cypress Dashboard API, and because the checks runs with the same commit SHA, the Dashboard can place all the results into a single run, grouped by the given name Parallel 2x on ${{ matrix.os }}.

Linux jobs have finished, while tests on Mac and Windows are still running

Give Cypress GitHub Action a try, and file any bugs and feature requests in its repository, cypress-io/github-action.

Happy Testing!