Skip to main content

Using the Cypress interactive mode in a Dev Container

Aug 02 2024 · 10 min

Listen to the article

As front-end developers we all love Cypress and its sweet interactive mode. It makes it easy to see and debug where things are going wrong: That api call that never happens, or that click of a button that leads to rerenders that shouldn’t occur. All to be seen and to debug step by step in the graphical interface of the Cypress app.

On the other hand, Dev Containers are increasing in popularity amongst developers, being a lightweight tool that provides a full-fledged, configurable and shareable development environment. This makes it easy for a team of developers to have a consistent environment across different machines and operating systems, which ensures that the provided code will run as expected on different host platforms, ultimately reducing the dreaded “well, it works on my machine” effect.

Now, there sadly is an issue when attempting to bring both of these wonderful tools together: A Dev Container, being a containerized environment, doesn’t have access to a display. This means that you won’t be able to open a graphical interface of an app from inside of a dev container. As a result, if you try to open cypress in interactive mode in the dev container, nothing will happen. The process will run, sure, but it just doesn’t have a way to display the window. Well, this is a let down, right?

Don’t worry, there is a way to reap the benefits of both dev containers and cypress and the solution lies in the good old x11 server.

Content:

Prerequisites

In order to run a project you need to have Docker installed and running on your machine, as well as your IDE set up so that it can run dev containers.

For guides see:

  1. VsCode
  2. JetBrains

Installing x11

First it’s important to understand what an x11 server actually is. An x11 server provides the graphical capabilities to unix-like operating systems (like Linux or macOs). Or in simple terms: They are the engine behind why you can see and interact with graphical windows on your display device.

The good news for Linux user is that on your system x11 should come preinstalled. For macOs however you need to install xQuartz which is an x11 implementation for macOs.

(macOs only):

  1. Install xQuartz ($ brew install --cask xquartz with homebrew).
  2. Restart your system.
  3. Run $ echo $DISPLAY in your terminal. This should print the address of you display. If it doesn’t, xQuartz did not install successfully.
  4. Open xQuartz by running open -a XQuartz.
  5. Go to the security tab of xQuartz’ preferences. Check the option to allow connection to network clients.

All Operating systems:

  1. Set an IP environment variable in you terminal by running $ IP=$(ifconfig en0 | grep inet | awk '$1=="inet" {print $2}') . This will set the variable to your local IP. You can confirm the variable has been set by running $ echo $IP.
  2. run $ xhost + $IP to add the IP to the allow list of the x11 server. This is necessary so that x11 accepts incoming connections from your IP address.

As this IP variable will be reset whenever you restart your device, it might be a good idea to add the small script to add the IP variable to your shell initialization script.

Setting up the dev container

You are now ready to set up your dev container and connect it to x11. We will utilise docker compose to provide an easily extensible and readable configuration, you could use a plain Dockerfile as well if it suits your needs.

  1. Create a .devcontainer in the root of your project.
  2. Add a .devcontainer/devcontainer.json configuration file with the following content:
{
	"name": "MyProject",
	"dockerComposeFile": "docker-compose.yml",
	"service": "devcontainer",
	"shutdownAction": "stopCompose",
	"forwardPorts": [<YOUR_APPLICATION_PORT>]
}

What we are doing here is configuring the dev container with a docker-compose.yml as an entry point for the build instructions. Note that with "shutdownAction": "stopCompose" we are stopping docker compose whenever the dev container is being shut down.

This is a very minimal configuration and you might want to configure the dev container with additional extensions or features installed suiting your project. For options have a look at the dev container metadata reference.

  1. Add a .devcontainer/Dockerfile with the following content:
FROM mcr.microsoft.com/devcontainers/typescript-node:0-20
RUN apt-get update

# Install Cypress dependencies: https://docs.cypress.io/guides/getting-started/installing-cypress#UbuntuDebian
RUN apt-get --no-install-recommends install -y \
libgtk2.0-0 \
libgtk-3-0 \
libgbm-dev \
libnotify-dev \
libgconf-2-4 \
libnss3 \
libxss1 \
libasound2 \
libxtst6 \
xauth \
xvfb \
# clean up
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean

Here we are instructing Docker to build our image from the node v20 image with typescript installed globally. We then run apt-get update to update all system dependencies to the latest versions to prevent conflicts when we are installing all the dependencies required by cypress in the next step. We do that by running apt-get --no-install-recommends install -y and listing all the dependencies, using the --no-install-recommends and -y flags to prevent downloading of recommended packages that aren’t strictly necessary as well as automatically accepting all installation dialogs, respectively.

  1. Add a .devcontainer/docker-compose.yml with the following content:
version: '3.8'
services:
	devcontainer:
		build:
			context: .
			dockerfile: Dockerfile
		volumes:
			- /tmp/.X11-unix:/tmp/.X11-unix
			- ~/.cache:/root/.cache
		command: sleep infinity
		environment:
			- DISPLAY=${IP}:0

In order for x11 to receive connections from the container we need to mount the hosts x socket /tmp/.X11-unix into a volume. Additionally we mount the ~/.cache folder into /root/.cache since this will allow us to cache the cypress binary between remounts of the container and install it simply via npm.

Lastly we provide the container with a DISPLAY environment variable setting it to our IP variable with the display number :0. This variable instructs X clients (graphical programs, e.g. Cypress) which X server to connect to.

Installing and Running Cypress

Run npm install cypress --save-dev within the terminal of your dev container, to install cypress in the dev-dependencies of your project.

  1. Add following script to your package.json for ease of use:
"scripts": {
	"cypress:open": "cypress open"
},
  1. Run npm run cypress:open

Congratulations on successfully setting up Cypress in a Dev Container! Happy coding and testing! 🎉