Using Cypress in GitLab CI
Cypress is a nice way to do automated tests for applications that don't come with a testing framework. Since I stumbled upon some issues getting it to work with our CI (Gitlab CI) I though to contribute with some documentation on how to get it working.
What you will learn from this how-to
- Creating Gitlab CI configuration
- Define variables in Gitlab CI
- Run cypress tests in Gitlab CI
- Fix cypress 'sad face' crash
- In the second part of this post: Automatic deploys with GitLab CI
A bit of history
I have written something similar a while back on my personal blog - Testing websites with cypress and GitLab CI - Part2
The new version described here is updated and works even better with Gitlab CI and it should be easy for you to adapt it to some other CI.
This post also does not cover the installation of cypress, I have written about this in my Testing websites with cypress and GitLab CI - Part1 post and there are a lot of posts about that out there.
Move cypress to a separate docker image
I needed cypress with ruby 2.3 to get wagon
from LocomotiveCMS working, to keep my project's Dockerfile
and .gitlab-ci.yml
clean I decided to create a separate docker image for cypress and ruby based on the official cypress Dockerfile you can find the resulting Dockerfile in our Gitlab repo.
I will spare you the content of the Dockerfile and you can find the resulting image on Dockerhub under lcxat/cypress-ruby
The main takeaway is that it bases on ruby:2.3.8 and node:12.1.0 after which it installs cypress on top of it. Update it to the ruby version you need if 2.3.8 does not suit you.
Creating Gitlab CI configuration
The Building stage
Let's start with the building stage in gitlab CI and work from there to a complete .gitlab-ci.yml
image: lcxat/cypress-ruby
stages:
- build
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- vendor/ruby
build:
stage: build
script:
- gem install bundler
- bundle install -j $(nproc) --path vendor
- sed -e s/APIKEY/$LOCO_API/g config/deploy-testing.yml > config/deploy.yml
- bundle exec wagon deploy testing -v -d
Let's analyze what's going on here, I'm starting off the previously mentioned Docker image lcxat/cypress-ruby
. To speed things up I have added a cache setting in the .gitlab-ci.yml
and also told bundler to install the gems in the vendor
path which is cached.
Then there is this sed
part, a little bit of wagon
background: The wagon
tool needs an API key to be able to deploy to our testing environment, you could start wagon
locally on the gitlab CI and test it like that, but the problem is that wagon
does not behave 100% the same as after deploying the page, that's why I went with a real deploy to a testing page on a live server. Now since you don't want any API keys in the git repo, I use sed
and Gitlab's variable settings to fix this and create a valid deploy.yml
file.
Go in your gitlab under settings -> CI/CD and add a new variable called LOCO_API
with your API Key as the value.
My deploy.yml
looks something like this
testing:
host: cms.example.at
handle: site-autotest
email: livadaru+cypress@example.com
api_key: APIKEY
When sed
runs it replaces APIKEY
with the value provided in Gitlab's variables setting mentioned above.
If you now commit and push to gitlab you should have a successful pipeline run for the building stage. Great, now let's go to the testing part.
Testing with cypress
Add a test stage to your .gitlab-ci.yml
file like this:
cypress:
stage: test
script:
- sed -i -e s#http://localhost:3333/#$CMS_URL#g cypress.json
- npm test
artifacts:
paths:
- /builds/foo/bar/cypress/videos/
and extend your stages
definition
stages:
- build
- test
ok, let's dig into this. There is again a sed command the edits the cypress.json
in place -i
I'm doing this since I also want to be able to test locally, that's why the cypress.json
looks like this
{
"baseUrl": "http://localhost:3333/",
"viewportWidth": 1000,
"viewportHeight": 1000
}
This allows me to test locally but of course, will fail when testing on CI since wagon did not start the server locally. Once again you must define a gitlab variable pointing to where the test page is deployed and name the variable CMS_URL
For the npm test
to work, you need to have a proper setup package.json
something like this, please note "private": true,
to prevent npm from publishing your repo.
Ok, now commit and push to gitlab and wait for the test to start ... and most likely fail.
We detected that the Chromium Renderer process just crashed.
This is the equivalent to seeing the 'sad face' when Chrome dies.
This can happen for a number of different reasons:
- You wrote an endless loop and you must fix your own code
- There is a memory leak in Cypress (unlikely but possible)
- You are running Docker (there is an easy fix for this: see link below)
- You are running lots of tests on a memory intense application
- You are running in a memory starved VM environment
- There are problems with your GPU / GPU drivers
- There are browser bugs in Chromium
You can learn more including how to fix Docker here:
https://on.cypress.io/renderer-process-crashed
or as screenshot
Well, this is a bummer :(
What's happening here is that the Electron app runs out of shared memory and crashes, the cypress link tells you if you are running in docker, that you should start it with --ipc=host
but that won't work in my case. Gitlab runner is running as a docker container but I would not like to start all containers with this setting. Assuming you are running gitlab-runner in docker, edit your config.toml
and add this to your volume setting
which will then look something like this:"/dev/shm:/dev/shm"
volumes = ["/opt/docker/gitlab-runner/cache:/cache","/dev/shm:/dev/shm"]
you don't really need the cache
part, but I wanted to see what gitlab-ci is doing with the cache so I added that part as well.
Stop & Restart your gitlab-runner to pick up the changes and rerun the failed job, it should now pass with flying colors and no more Chromium crashes.
Some notes about the cache
I have been playing with several cache settings and here are some things that might help when looking at what gitlab-ci does. If you look at the end of the build, you must see that gitlab-ci has created the cache
Creating cache new-docker...
vendor/ruby: found 9813 matching files
No URL provided, cache will be not uploaded to shared cache server. Cache will be stored only locally.
Created cache
Don't freak when looking at the next stage where it says Removing vendor/
Fetching changes...
Reinitialized existing Git repository in /builds/foo/bar/.git/
Checking out ee8d93ed as new_docker...
Removing config/deploy.yml
Removing vendor/
This is not gitlab removing the cache but git clean! Don't worry, the cache will be back when needed, gitlab-ci takes care of that. Just make sure you run bundle
on each stage where you need the gems and don't forget to add the --path vendor otherwise it will install all gems from scratch an not use the cache
In the second part - Automatic deploys with GitLab CI, I will take this a step further and configure gitlab to automatically deploy to staging and add a manual option to allow deploying to production with the click of a button.