ScreenshotNeo

BlogHow-to

How to Set Up End-to-End Testing with Cypress

Install Cypress in an existing app, configure its base URL, write a first browser test, and run it reliably in CI.

By the ScreenshotNeo team4 October 202610 min read

To set up end-to-end (E2E) testing with Cypress, install Cypress in your application project, open it from the project root, choose E2E Testing, and let the Launchpad create the initial configuration. Start your app separately, set e2e.baseUrl to its local address, add a spec that exercises a real user journey, and run it in an installed browser. In CI, wait for the app server to become ready before running Cypress.

This guide uses npm and JavaScript for its runnable example. The same setup works with TypeScript and other supported package managers. Check the current Cypress installation requirements for supported Node.js and operating system versions before installing; these requirements change over time.

1. Install Cypress in your existing project

Run the install command from the directory containing your application’s package.json. Cypress should be a project development dependency so its version is tracked in the lockfile and shared with your team.

npm install cypress --save-dev

Other package managers use these commands:

yarn add cypress --dev
pnpm add --save-dev cypress
bun add --dev cypress

Commit the updated manifest and lockfile. In CI, install from the lockfile—for npm, use npm ci—so the runner gets the Cypress version selected by the project.

2. Initialize Cypress E2E testing

From the project root, launch the Cypress app:

npx cypress open

Choose E2E Testing in the Launchpad, accept its proposed configuration and folder structure, and choose an installed browser. The Launchpad can generate the initial files for you. The first setup typically creates cypress.config.js and a cypress/e2e directory.

Use npx cypress open while authoring: it opens the interactive runner, shows the browser and command log, and reruns specs as you edit them. Use npx cypress run for a headless command-line run, which is generally the starting point for CI.

Optional package scripts make the commands easier to remember. Add names such as cy:open and cy:run; avoid naming a script simply cypress, which can conflict with package-manager command resolution.

{
  "scripts": {
    "cy:open": "cypress open",
    "cy:run": "cypress run --browser chrome"
  }
}

3. Start the application and configure baseUrl

Cypress drives a browser against a running web application. Start the app in a separate terminal using your project’s normal development command. The example below assumes it is available at http://localhost:8080; replace that address with the URL and port your app actually uses.

npm run dev

Configure the E2E base URL in cypress.config.js:

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  e2e: {
    baseUrl: 'http://localhost:8080',
  },
})

For an ES module or TypeScript configuration, use the corresponding export form:

import { defineConfig } from 'cypress'

export default defineConfig({
  e2e: {
    baseUrl: 'http://localhost:8080',
  },
})

With this setting, cy.visit('/') and cy.request('/api/...') resolve relative to the configured host. You can override the URL in CI with CYPRESS_BASE_URL, which is useful when testing a preview deployment. Cypress restarts when its configuration changes.

4. Write a first end-to-end spec

Create cypress/e2e/home.cy.js. This example checks a user-visible heading and a navigation link. Adjust the route, accessible name, and expected content to match your own app.

describe('home page', () => {
  it('shows the main heading and lets a visitor open the pricing page', () => {
    cy.visit('/')

    cy.get('h1').should('be.visible')
    cy.get('a').contains('Pricing').click()
    cy.location('pathname').should('eq', '/pricing')
  })
})

Run the spec interactively with npx cypress open, or run it from the terminal in Chrome:

npx cypress run --browser chrome --spec 'cypress/e2e/home.cy.js'

A durable E2E test checks an outcome that matters to the user, such as reaching a page, seeing a confirmation, or completing a purchase flow. Prefer stable selectors such as accessible roles, labels, and deliberate test attributes over styling classes that change during redesigns.

5. Add reliable CI execution

CI needs the same essential pieces as local development: install the locked dependencies, provide a supported browser and its system requirements, start the app, wait for it to answer, and run Cypress. Starting a server in the background and immediately launching the tests creates a readiness race.

One simple npm-based workflow uses start-server-and-test. Install it as a development dependency:

npm install --save-dev start-server-and-test

Add scripts, replacing npm run start and the health URL with your application’s actual server command and readiness endpoint:

{
  "scripts": {
    "start": "your-app-start-command",
    "cy:run": "cypress run --browser chrome",
    "test:e2e:ci": "start-server-and-test start http://localhost:8080 cy:run"
  }
}

Then the CI job can run:

npm ci
npm run test:e2e:ci

The readiness check should point to a URL that responds only after the app is ready to serve tests. A health endpoint is often a better readiness target than a route that depends on external services. Cypress also documents using wait-on or the official GitHub Action’s start and wait-on options. See the Cypress CI guide for provider-specific configuration.

Example GitHub Actions job

This example uses the official Cypress GitHub Action to start the app and wait for its URL. Adjust the Node version, start command, URL, browser, and action version to fit your repository and current workflow policy.

name: E2E tests

on: [push, pull_request]

jobs:
  cypress:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - uses: cypress-io/github-action@v6
        with:
          start: npm run start
          wait-on: 'http://localhost:8080'
          browser: chrome

Use the action version supported by your organization and the current Cypress documentation. If your job starts the server manually, wait for it with npx wait-on http://localhost:8080 before invoking npx cypress run. A fixed sleep can waste time on fast runs and still fail on slow ones.

6. Choose browsers and configuration deliberately

Cypress detects compatible browsers installed on the machine. The browser must also be present in CI. Select one in interactive mode or pass its name to the CLI:

npx cypress run --browser chrome
npx cypress run --browser firefox
npx cypress run --browser edge

Cypress’s current browser guide lists Chrome-family browsers and Firefox, with WebKit support marked experimental. Electron is deprecated as a test browser; use an installed browser and specify it explicitly in new CI configurations. Browser availability and minimum versions can change, so consult the browser launching guide before setting a matrix.

Choice When it helps Tradeoff
One stable browser on each commit Fast feedback on routine changes Does not cover every browser engine
Multiple browsers on selected branches or nightly Broader compatibility coverage More runtime and CI capacity
Chrome for Testing Version-controlled Chrome runs where available Requires managing the browser version in the environment
WebKit Experimental checks against Safari’s engine Experimental support may have limitations

Choose browsers based on the users and risks relevant to your product rather than running every available browser on every commit. Cypress’s CI documentation gives a baseline of at least 2 CPUs and 4 GB RAM, with 8 GB or more recommended for longer runs or video recording. Linux runners may need system dependencies; Cypress Docker images can package browser prerequisites.

7. Improve test stability and maintainability

  • Test user outcomes. Assert visible results and navigation rather than internal implementation details.
  • Wait for meaningful conditions. Cypress retries many assertions automatically. For network-dependent flows, define the relevant request with cy.intercept() before visiting or clicking, then wait on that alias instead of inserting arbitrary delays.
  • Keep test data controlled. Seed required state through a test API or isolated fixtures when possible. Avoid relying on records that may be edited by another run.
  • Keep specs independent. A spec should establish the state it needs and should not depend on a previous test having passed.
  • Separate environments from code. Use a CI base URL or protected environment variables for environment-specific values; keep secrets out of committed files and logs.
  • Make browser choice explicit in CI. This makes the browser engine part of the job’s visible configuration and avoids implicit fallback behavior.

Cypress can capture screenshots and video for test runs when configured to do so. Keep useful failure artifacts in CI, but account for storage and upload time. If browser runs are slow, first inspect the slow specs and the server’s behavior; parallelization can reduce wall-clock time, but it also consumes more CI capacity and should be weighed against its cost.

8. Optional: record runs with Cypress Cloud

Cypress’s local testing app works without Cypress Cloud. Cloud is an optional hosted service for recording CI runs and related debugging, collaboration, analytics, and orchestration capabilities. If you choose to record, connect the project using the project ID and supply the record key through a protected environment variable such as CYPRESS_RECORD_KEY, then run with --record.

npx cypress run --browser chrome --record

Store the key in your CI provider’s secret settings, not in source code. Cloud plans and allowances can change; check the current Cypress pricing page before choosing a plan.

Or skip the browser setup

If your goal is to capture a page for a visual review, report, or downstream workflow—not to exercise browser interactions—ScreenshotNeo can return a screenshot or PDF with one GET request. Cypress remains the tool for driving and asserting application behavior; ScreenshotNeo is a separate option for page capture.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo API documentation for request options. Cookie banners are accepted and removed before the shot, along with known newsletter popups and chat widgets. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for ScreenshotNeo’s free plan.

Troubleshooting Cypress setup

Symptom Likely cause Fix
cy.visit() cannot connect or gets a connection refused error The app is stopped, listening on a different port, or the configured base URL is wrong. Start the app separately, open the exact URL in a normal browser, and make e2e.baseUrl match it. In CI, check the server logs and readiness URL.
CI fails intermittently at the beginning of a run Cypress started before the application finished booting. Wait on an HTTP readiness check with wait-on or start-server-and-test, or configure the Cypress GitHub Action’s start and wait-on inputs.
No browser appears in Cypress or --browser chrome fails The browser is missing, cannot be detected, or is not installed in the CI image. Install a supported browser in that environment, inspect detected browsers with npx cypress info, and consult the troubleshooting guide. Ensure your Docker image includes the browser you selected.
Cypress installation or launch fails on Linux Required operating system libraries are absent, or the system does not meet current requirements. Check the current installation requirements and Linux prerequisites; use an appropriate Cypress Docker image if maintaining system packages is difficult.
A test passes locally but fails in CI Different app readiness, browser versions, environment variables, or test data can change behavior. Use the same explicit browser and locked dependencies, wait for readiness, make test data deterministic, and inspect CI screenshots, video, and command logs.
A test times out waiting for network activity The test waits for the wrong request, registers its intercept after the request already occurred, or assumes Cypress waits for all asynchronous calls. Register cy.intercept() before the action that triggers the request, match the correct method and URL, and wait for the alias. Assert a specific request rather than expecting Cypress to infer that every request has completed.
Chrome launch fails on a managed Windows machine Organization browser policies can interfere with Cypress’s controlled launch. Review the Cypress error guidance with your administrator; try an allowed Chromium-based browser or adjust organizational policy where appropriate.
The Electron deprecation warning appears The run uses Electron explicitly or relies on it as a default. Install a supported browser in local and CI environments and pass --browser chrome (or configure an appropriate default browser).

Performance, reliability, and cost

  • Install time: Cypress downloads a browser automation binary in addition to package dependencies. Persist the Cypress binary cache between CI jobs to avoid unnecessary downloads. Cypress advises caching its system cache and the package manager’s cache, rather than carrying node_modules between builds.
  • Run time: Keep the default commit workflow focused on high-value journeys. Add broader browser coverage where it brings confidence, balancing runtime and runner expense.
  • Reliability: Deterministic test data, explicit browser selection, and server readiness checks reduce common sources of flaky runs. Avoid fixed sleeps as a substitute for checking a real condition.
  • Infrastructure: Browser, application server, and test runner all consume resources. Cypress’s CI guidance recommends at least 2 CPUs and 4 GB RAM, and 8 GB or more for longer runs or video recording.
  • Hosted service cost: Cypress Cloud is optional. Review its live pricing and current feature allowances if you need recorded runs or collaboration features.

Frequently asked questions

How do I install Cypress in an existing project?

Run npm install cypress --save-dev in the project root, then open it with npx cypress open and choose E2E Testing.

How do I configure Cypress baseUrl?

Set e2e.baseUrl in cypress.config.js or cypress.config.ts to the address where the test server is reachable. Relative visits such as cy.visit('/') use that host.

Which browsers does Cypress support?

The browser list includes Chrome-family browsers and Firefox; WebKit is experimental and Electron is deprecated. Check the current browser documentation for support details.

Can I use Cypress without Cypress Cloud?

Yes. Cypress’s local app and CLI can run tests without Cloud. Cloud is optional for recording runs and related hosted features.

Should every pull request run every browser?

No fixed matrix fits every project. Start with the browser most important to your users, then add coverage on selected branches or scheduled runs when the added confidence justifies the CI time and capacity.

Primary references