Running Cypress in Gitlab CI

Running Cypress in Gitlab CI

I ran into some difficulties a few weeks ago when attempting to run Cypress tests as part of my CI script in Gitlab. The application I was working with has a Golang backend and the front end is generated with Nuxt. As this project had private dependencies, my CI script built the application in separate stages. One for the server and one to generate static files. This pattern worked well for creating a final Docker image.

However; when trying to add E2E tests as part of the flow, I rapidly ran into issues. The articles I came across in my search were either outdated or provided incomplete solutions. In case you run into similar issues, here's what I did to get this running.

1 - Create a New Dockerfile for Cypress

Cypress does have an official Docker image that comes with all of the dependencies to run Cypress, except Cypress itself. As I didn't want to create a package.json, nor did I want to install a bunch of dependencies that I wouldn't need for this step, I opted to take care of it inside of the Dockerfile.

FROM cypress/base:10
WORKDIR /app
# This allows for creating a package.json within the Dockerfile itself
RUN npm init --yes
RUN npm i cypress
# Copying both the test files and the config for cypress
COPY cypress cypress
COPY cypress.json .
# I had a local copy of this script, but it is easily findable online
COPY wait-for-it.sh
RUN chmod +x wait-for-it.sh

2 - Create a Docker Compose File

Now that I had a Dockerfile to run the Cypress tests, the next step was to get it to run against my application. I already had an existing Dockerfile to assemble my application, so all I needed to get it running was Docker Compose.

version: "3"

services:
    web:
        build:
            context: .
            dockerfile: "docker/Dockerfile.web"
        ports:
            - "8080:8080"
    cypress:
        build:
            context: .
            dockerfile: "docker/Dockerfile.cypress"
        depends_on:
            - web
        environment:
            - CYPRESS_BASEURL=http://web:8080
        command: [
            "./wait-for-it.sh", "web:8080", "-s", "-t", "0", "--",
            "npx", "cypress", "run"
        ]

Since I had added the wait-for-it.sh script to my Dockerfile, I was able to ensure that my application was running and accessible before attempting to run any tests against it.

3 - Update .gitlab-ci.yml to Run E2E Tests

The final step was to get Gitlab to run the tests. I've added a simplified version of my .gitlab-ci.yml file below. The key things to get this working were:

  • Setting the entrypoint as an empty string.
    • Most of the examples I came across for running Cypress involved setting a custom entrypoint. However; Gitlab will continue to run sh as the entrypoint.
    • After a lot of digging, I found the solution here: Gitlab Runner Issue #2692
  • Having docker-compose exit when the Cypress container shut down.
    • This is needed in order to prevent the build from hanging as well as fail the build if the tests fail.
image: docker:stable-git

services:
    - docker:dind

variables:
    ARTIFACTS_DIR: $CI_PROJECT_DIR/artifacts
    GO_LINK_OPTIONS: -s

cache:
    paths:
        - node_modules/

stages:
    - build-test
    - e2e-test

before_script:
    - set -e
    - if [ "$CI_JOB_NAME" = "build-test:go" ]; then apk update && apk add --no-cache build-base git curl bash docker; fi
    - mkdir -p $ARTIFACTS_DIR

build-test:node:
    image: node:12
    stage: build-test
    artifacts:
        expires_in: 1 day
        paths:
            - $ARTIFACTS_DIR/
    script:
        - npm ci
        - npm test
        - npm run generate
        - cp -r .nuxt $ARTIFACTS_DIR/.nuxt
        - cp -r dist $ARTIFACTS_DIR/dist

build-test:go
    image: golang:1.12-alpine
    stage: build-test
    artifacts:
        expires_in: 1 day
        paths:
            - $ARTIFACTS_DIR/
    script:
        - cd cmd
        - go test ./...
        - go build -ldflags=all=$GO_LINK_OPTIONS -o $ARTIFACTS_DIR/$CI_PROJECT_NAME .

test:integration:
    image:
        name: docker/compose:1.24.0
        # needed in order to be able to run custom scripts
        entrypoint: [""]
    stage: e2e-test
    dependencies:
        - build-test:node
        - build-test:go
    artifacts:
        expires_in: 1 day
        paths:
            - $ARTIFACTS_DIR/
    script:
        # Copy buld artifacts
        - cp -r $ARTIFACTS_DIR/dist ./dist
        - cp -r $ARTIFACTS_DIR/.nuxt ./.nuxt
        - cp -r $ARTIFACTS_DIR/$CI_PROJECT_NAME ./cmd/$CI_PROJECT_NAME
        - docker-compose build
        # Without this the build will hang and will not fail on failing tests
        - docker-compose up --abort-on-container-exit --exit-code-from cypress

I hope this helps you resolve similar issues you may be facing getting Cypress to work in your CI scripts.

Originally published on jessie.codes