Skip to main content
Cypress App

Run cypress tap in an AI agent sandbox

An agent can start Cypress, run a spec, and read the result without anyone at the keyboard.

Cloud coding agents such as Claude Code on the web and Cursor Cloud run in a sandbox: an isolated container or virtual machine with no screen and no person to click through the Cypress app. cypress tap reads from a running Cypress app in open mode, so the sandbox has to run that app too. This page covers what the sandbox needs and how an agent starts a session on its own.

info

The behavior described on this page relies on changes made in Cypress v16.3.0. Earlier versions of Cypress can be run inside sandboxes but may require additional setup and configuration.

How it fits together​

Three things run in the same sandbox:

  1. The agent, which runs shell commands.
  2. A Cypress app in open mode (cypress open), with a browser it launches for the selected testing type.
  3. cypress tap commands, which the agent runs to start specs and read results.

cypress tap finds the Cypress session through files on the same machine and talks to it over local connections. It cannot reach a Cypress app running on another machine, so the agent and Cypress have to share one sandbox.

Requirements​

  • Cypress version 16.3.0 or greater
  • A version of Node.js supported by Cypress. See System requirements.
  • On Linux, the xvfb package. A sandbox has no screen; Cypress starts a virtual one for the app and the browser using Xvfb. If you already have a display you want Cypress to use, set the DISPLAY environment variable and Cypress uses it. If DISPLAY points to a display that is not running, Cypress starts its own.
  • Network access to the hosts in the table below.
  • A supported browser. See Choose a browser.

Network access​

Many agent sandboxes block network traffic by default. Allow these hosts so the Cypress binary downloads and the app can reach its services:

HostUsed for
registry.npmjs.orgInstalling the cypress npm package
download.cypress.ioFinding and downloading the Cypress binary
cdn.cypress.ioDownloading the Cypress binary
api.cypress.ioRequests the Cypress App makes to Cypress Cloud

In addition to the hosts above, you will need to ensure the sandbox is allowed to access any hosts used by your tests; this includes your UI, API, auth layers, and any other network resources your tests rely on. If your test suite relies on dependencies downloaded from repositories other than NPMJS you will need to configure those as well.

note

If your application is network-restricted (only accessible from your corporate VPN for example) then you will need to coordinate with your network security team to establish a method for a remote sandbox to access it. This may require use of a proxy or other advanced configuration.

Detecting an AI agent​

Cypress recognizes common coding agents from the environment variables they set. When an agent is detected, certain UI behaviors are bypassed or automated to facilitate running headlessly. If your agent is not recognized, set CYPRESS_AGENT_MODE=1 on the same command line that starts Cypress. Note that some agents start a new shell for each command, so it may be necessary to change how the environment variable is set depending on how your agent works. See Environment variables.

Changes made in a sandbox​

When an agent runs inside a sandbox there is the possibility it could create, delete, or edit files depending on the task it has been given. These changes are performed inside the sandbox, so you will need to take action to ensure they are not lost. This could involve copying the changes back to your local machine or committing them to a remote git branch. The process for identifying and handling file changes depends on your choice of agent and how you have configured your sandbox - refer to your agent's documentation for more information.

Running Cypress Tests Non-interactively​

Tests can be run for basic pass/fail measurements inside a sandbox using cypress run.

Running Cypress Tests Interactively​

An agent can run tests one at a time, monitor the execution, and query the application under test throughout using cypress tap. Doing this in a sandbox requires running a backgrounded instance of Cypress.

Prepare the project​

When launched in a sandbox, Cypress will use the established cypress.config.js file from your project.

If a Cypress configuration has not been created yet Cypress will automatically create one when running in --e2e mode when launched by an agent. Component testing mode (--component) requires that configuration be created before launching inside a sandbox to avoid potential mistakes with library auto-detection.

Start Cypress in the background​

Run cypress open with a browser and a testing type, and leave it running. Use nohup so Cypress keeps running after the command that starts it returns:

Command
nohup npx cypress open --e2e --browser chromium --project . > cypress-open.log 2>&1 &

With the above command, logging output will be written to cypress-open.log. This includes error conditions, configuration issues, and the location of files that are created automatically. Instruct your agent to refer to this file in the event of unexpected behavior.

When an AI agent has been detected, Cypress will automatically bypass the "welcome" portions of the UI when --project, --browser (or set defaultBrowser in the config), and a testing type parameter are supplied.

cypress open --detached is an alternative that waits until Cypress reports ready before returning, instead of backgrounding right away like the nohup form above. It doesn't keep writing to a log the way the nohup form's redirect does: once Cypress is ready, --detached stops relaying output to the command that started it, so a redirect on it only captures what printed before that point, not anything that happens afterward. For the ongoing log this page's troubleshooting steps assume, start Cypress with the nohup form above; reach for --detached only when you don't need that log and just want the command to block until Cypress is ready.

Command
npx cypress open --e2e --browser chromium --project . --detached

Wait for the browser​

Once the "open" mode instance of Cypress has been launched it can be controlled and queried using cypress tap. On initial launch it may take some time for the browser to launch; poll status until it reports a successful connection to the browser. If the loop completes unsuccessfully read cypress-open.log for the reason.

Command
deadline=$((SECONDS + 90))

until out=$(npx cypress tap status --json) && echo "$out" | grep -Eq '"browserAttached": *true'; do
echo "$out" | grep -Eq '"status": *"error"' && { echo "$out"; exit 1; }
[ "$SECONDS" -ge "$deadline" ] && { echo "no browser after 90 s; see cypress-open.log"; exit 1; }
sleep 1
done

Run specs and read the results​

From here the agent uses the same commands as on a local machine:

Command
npx cypress tap run cypress/e2e/checkout.cy.ts
npx cypress tap status --json
npx cypress tap reporter --json

See Getting Started for the full loop and Polling for a verdict for how to tell that a run finished. The cypress-tap skill teaches an agent this loop.

If status stays at spec not selected after you run a spec, run the spec again.

Choose a browser​

cypress tap works with Chromium-based browsers: Chrome, Chromium, Edge, and Electron.

  • Chromium or Chrome. Pass the browser by name if it's on the PATH, or point straight to its executable:

    Command
    npx cypress open --e2e --browser /usr/bin/chromium --project .

    Either way, Cypress runs <path> --version and checks the answer against what it expects for that browser, such as Chromium 123.0.6312.4. Passed by name, it only checks the pattern for that one name; passed by path, it checks every pattern it knows and uses whichever one matches. So if a build isn't recognized under the name you gave it, its full path is worth trying before you give up on it — but if it isn't recognized either way, its --version output doesn't match anything Cypress knows, and renaming or moving it won't change that. When Cypress can't use the browser you asked for, it writes the reason, along with the raw <path> --version output, to cypress-open.log, and cypress tap status stays at browser not selected.

  • Electron. Electron ships with Cypress, so it works without installing a browser. Cypress prints a deprecation warning because Electron as a test browser is deprecated.

Sandboxes often come with a Chromium already installed by a browser-automation tool. Find it with ls /opt/pw-browsers/chromium-*/chrome-linux/chrome (the location varies by sandbox), then run <path> --version yourself to check that it introduces itself as Chromium before pointing Cypress at it. From there, pass the path straight to --browser, or put it on your PATH under a name Cypress knows, like chromium, and pass that name instead — a Chromium build like this one is recognized either way. If you try a name and Cypress doesn't recognize it, fall back to the full path, since it's checked against more patterns.

Once a cypress.config.* file exists, setting defaultBrowser in it works the same way as passing --browser, so you can set the browser once in the project instead of on every command that starts Cypress. On the very first launch of an unconfigured project, there is no config file yet to read defaultBrowser from, so pass --browser instead.

Configure your sandbox​

  1. In the environment's network settings, allow the hosts in Network access.

  2. In the environment's setup script, install your dependencies, which also downloads the Cypress binary:

    Setup script
    npm ci

    As an alternative that lives in your repository, add a "SessionStart" hook that installs when the session runs on the web:

    .claude/settings.json
    {
    "hooks": {
    "SessionStart": [
    {
    "hooks": [
    {
    "type": "command",
    "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-start.sh"
    }
    ]
    }
    ]
    }
    }
    .claude/hooks/session-start.sh
    #!/bin/bash
    set -euo pipefail
    [ "${CLAUDE_CODE_REMOTE:-}" = "true" ] || exit 0
    cd "$CLAUDE_PROJECT_DIR"
    npm install --no-audit --no-fund >&2
    npx cypress verify >&2 || { npx cypress install >&2; npx cypress verify >&2; }

    By default most agents will not persist the sandbox filesystem - this means Cypress and your dependencies will be re-downloaded each time which can have substantial time and bandwidth costs. Review your agent's documentation to determine if it is appropriate to cache the Cypress binary (see Environment variables) and other dependencies.

  3. Claude's sandbox environments already have xvfb and a Chromium build installed, but DISPLAY is left unset and that Chromium isn't on PATH. If you have a display of your own, set DISPLAY yourself; otherwise Cypress starts an Xvfb display for you. Symlink the bundled Chromium onto PATH as chromium, so you can refer to it by name instead of typing out the full path, then start Cypress with it. Tell the agent how to do this, or add the recipe to your agent's instructions.

    Command
    mkdir -p ~/bin
    ln -sf "$(ls /opt/pw-browsers/chromium-*/chrome-linux/chrome | head -1)" ~/bin/chromium
    PATH="$HOME/bin:$PATH" nohup npx cypress open --e2e --browser chromium --project . > cypress-open.log 2>&1 &

Add the recipe to your agent's instructions​

Add the steps to the file your agent reads, or install the cypress-tap skill, which includes them:

AGENTS.md
## Run Cypress tests

Start Cypress once per session, in the background. Replace `<browser>` with
`chromium` if it is on the PATH, or with the path to a Chromium or Chrome
executable:

nohup npx cypress open --e2e --browser <browser> --project . > cypress-open.log 2>&1 &

Wait until `npx cypress tap status --json` reports a status other than
`not connected` or `browser not selected`. Stop waiting if it reports `error`
or if nothing changes after about 90 seconds, and read `cypress-open.log`.

Then run a spec with `npx cypress tap run <spec>` and poll
`npx cypress tap status --json` until `status` is `passed` or `failed` and
`startedAt` has changed. Read failures with `npx cypress tap reporter --json`.

For component testing, write the configuration, support files, and bundler
config first, and check them with `npx cypress run --component --spec <spec>`.

Environment variables​

NameDescription
CYPRESS_AGENT_MODESet to 1 or true to make Cypress recognize this run as an AI agent, or to 0 or false to turn that off. It only controls recognition: skipping the welcome screen still needs --project, a browser (or defaultBrowser), and a testing type.
CYPRESS_CACHE_FOLDERChanges the location of the Cypress binary cache
CYPRESS_INSTALL_BINARYSets which Cypress binary to install: a version, URL, or local file

Troubleshooting​

  • The binary download fails. The sandbox blocks download.cypress.io or cdn.cypress.io. Allow them, or use a mirror. See Network access.

  • Cypress reports a missing system library. The error names the library and an install command for Debian and Ubuntu. For a library it does not recognize, it gives an apt-file search command instead; apt-file is not installed by default, so install it first (apt-get install apt-file && apt-file update). Install the library and run the command again.

  • Cypress reports that Xvfb is missing. Install the xvfb package, or set DISPLAY to a display that is running.

  • status stays at not connected. Cypress is not running yet, or it has not started loading the project. Read cypress-open.log. A project with a configuration file but no support file may also stay at not connected instead of reporting an error; check that cypress/support/e2e.js or cypress/support/e2e.ts exists.

  • status reports error. The project failed to load: an invalid configuration file, or (for component testing) a dev server that failed to start. Cypress prints the same error to cypress-open.log, and npx cypress tap status --json includes it under error. After a configuration file fails to load, or setupNodeEvents throws, Cypress stops watching the configuration and will need to be restarted to pick up any fixes made to the configuration.

  • status stays at browser not selected. This can happen for two reasons. First, check that you passed --project, --browser (or set defaultBrowser), and a testing type to cypress open; if you already did, set CYPRESS_AGENT_MODE=1, since Cypress didn't recognize the agent. Second, if you pointed --browser at a path, Cypress may not have recognized the binary at that path; read the reason in cypress-open.log and see Choose a browser.

  • Cypress ignores ELECTRON_RUN_AS_NODE. Some agent environments set this variable, which makes Electron run as plain Node.js with no window. cypress open ignores it and prints a warning.

  • Component testing shows no session and no error. Check that the configuration, support file, component-index.html, and bundler config (such as vite.config.*) exist, and that setupNodeEvents does not throw. Cypress does not scaffold component testing for an agent because it needs a framework and bundler to be chosen and their dependencies installed, so create these files first. To check the setup, run npx cypress run --component --spec <spec>.

  • status reports not connected for a session that was running. cypress tap running in a different PID namespace than Cypress, such as a separate container sharing the filesystem, can decide the session is gone. Run tap in the same container as Cypress.

  • cypress open exits right away. Run npx cypress verify to see why the Cypress binary cannot start.

Limitations​

  • The agent and Cypress have to run in the same sandbox.
  • An open Cypress session with a browser uses about 2 GB of memory in a small project, and more in a larger one. Size the sandbox accordingly.
  • Each new session in a sandbox that does not persist the filesystem starts with an empty Cypress binary cache. Downloading, extracting, and verifying the binary adds a delay to startup in each new sandbox instance. It is recommended to configure your sandbox to persist the binary cache directory for optimal performance.
  • --detached leaves the Xvfb that Cypress started running after Cypress exits. You can find its process ID with pgrep Xvfb and stop it when you're done — but only if it's the only one on the host. If another session is also running Xvfb, pgrep Xvfb turns up both with no way to tell which is yours, so skip pkill Xvfb too: it would stop every Xvfb on the host, including the other session's. Giving each session its own sandbox, torn down once the session ends, avoids the problem entirely.
  • cypress tap has several documented limitations to be aware of.

See also​