Upgrade Cypress
Migrating to Cypress 16.0
Upgrade to Cypress 16 with your AI assistant
Copies a ready-made prompt that walks your AI coding assistant through this migration. Works with any AI tool that can read and edit your project.
Read https://docs.cypress.io/app/references/migration-guide/migrating-to-cypress-16-0.md and https://docs.cypress.io/app/references/migration-guide/migrating-away-from-cypress-env.md, which covers in full the Cypress.env() removal that the 16.0 section only summarizes. Then upgrade this project from Cypress 15 to 16 by working through these steps: 1. Confirm the starting point. If the project isn't on Cypress 15.x yet, stop and tell me; majors should be upgraded one at a time. 2. Check requirements. Verify my environment meets the requirements in the 16.0 section (Node.js 22, 24, or 26+, and Angular 21+ / Vite 8+ / Next.js 15.0.4+ if I use component testing) and flag anything unsupported. 3. Update Cypress. Detect my package manager and update the cypress dependency. If I depend on @cypress/angular-zoneless, replace it with @cypress/angular and update the imports. 4. Migrate off Cypress.env(), following the Cypress.env() page rather than guessing. Find every Cypress.env() usage and classify each value as sensitive or public, since that choice decides the replacement. Move sensitive values (keys, tokens, passwords, credentials) to cy.env(), which is asynchronous and yields only the keys you request, and public values (feature flags, API versions, public URLs) to Cypress.expose() with a top-level expose block in my config. Replace any env key in a per-test or per-suite config override with expose. Check for --env CLI flags, for reads inside cy.origin() callbacks, and for plugins that call Cypress.env(), which that page lists with the versions that support the new APIs. Show me the proposed changes before applying them. 5. Fix the remaining breaking changes in the 16.0 section that affect my code and config: delete cy.end() calls, replace cy.exec() calls with cy.task() registered in setupNodeEvents and remove execTimeout, replace experimentalMemoryManagement: false with manageBrowserMemory: false, remove allowCypressEnv, experimentalSourceRewriting, and experimentalFastVisibility, move Cypress.config() calls that set viewportWidth, viewportHeight, or blockHosts during a test into cy.viewport() or a test configuration object, and convert any .coffee specs, support files, or fixtures to JavaScript or TypeScript. 6. Review what changed behavior without changing my code. Flag, but don't rewrite unless a test actually fails: assertions on req.httpVersion, compression headers, or a 304 status inside cy.intercept(); visibility assertions that depended on the legacy algorithm; tests that relied on the implicit 10ms keystrokeDelay; and cookie or storage assertions written inside .then(), which doesn't retry. Don't set forceHttp1 or visibilityStrategy: 'legacy' to make a test pass; both are deprecated escape hatches. 7. Verify. Run npx cypress verify and my Cypress tests, and confirm the run starts with no deprecation or removed-option warnings. Finish with a summary of what changed and anything you couldn't safely automate.
This guide details the code changes needed to migrate to Cypress version 16.0. See the full changelog for version v16.0.
What you may need to change
| Change | Action needed |
|---|---|
| Native browser network in Chrome, Chromium, and Edge | Usually none. Update tests that assert on req.httpVersion, compression headers, or a 304 status. |
| Modern visibility algorithm on by default | Usually none. Update tests that relied on legacy-only visibility semantics. |
| Browser memory management on by default | Only if you set experimentalMemoryManagement: false. Replace it with manageBrowserMemory: false. |
keystrokeDelay default changed from 10 to 0 | Only if a test depended on the implicit delay. |
| Cookie and storage reads are now queries | Usually none. Move assertions out of .then() and onto .should() to get retries. |
| Node.js 20 and 25 dropped | Yes, if you run Cypress on either version. |
Cypress.env() removed | Yes. Migrate to cy.env() or Cypress.expose(). |
cy.end() removed | Yes. Delete the .end() calls. |
cy.exec() removed | Yes. Replace the calls with cy.task(). |
viewportWidth, viewportHeight, and blockHosts via Cypress.config() | Yes, if you set them while a test is executing. |
experimentalSourceRewriting removed | Yes. Remove the option, and enable removeSRIAttributes if you used it for SRI. |
| CoffeeScript support removed | Yes, if you have .coffee specs, support files, or fixtures. |
| Angular, Vite, and Next.js minimums raised for component testing | Yes, if you are below Angular 21, Vite 8, or Next.js 15.0.4. |
| Electron deprecated as a test browser | Not yet, but plan to switch to an installed browser. |
Node.js 20 and 25 no longer supported
Cypress 16 requires Node.js 22.x, 24.x, or 26.x and above to install the Cypress binary. Node.js 20 and Node.js 25 are no longer supported. See system requirements and Node's release schedule.
Chrome, Chromium, and Edge use the native browser network
In Cypress 16, Chrome, Chromium, and Edge intercept test traffic through the native browser network. Your application connects to your server directly and negotiates whatever protocol the server supports (HTTP/1.1, HTTP/2, or HTTP/3), exactly as it does in production.
Firefox, WebKit, and Electron continue to use the legacy network path and are unaffected.
cy.intercept() works as before, and most suites need no edits. A few behaviors differ in Chrome, Chromium, and Edge on the native browser network. See Native network interception for the full list with examples.
Aside from the cy.intercept() behaviors listed in that guide, your suite should not need any changes. Commands, stubbing, waiting, aliases, and assertions all work as they did before, and adopting the native browser network requires no configuration change.
If you need a temporary escape hatch, either while you update those tests or if you hit behavior on the native browser network that the guide does not list, route every browser back through the legacy network path:
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
forceHttp1: true,
})
import { defineConfig } from 'cypress'
export default defineConfig({
forceHttp1: true,
})
If setting forceHttp1 to true allows your suite to pass, and the difference is not one of the documented behaviors, please open an issue with what you are experiencing so it can be investigated.
forceHttp1 is both introduced and deprecated in Cypress 16. It exists only to give suites time to migrate, or as a temporary escape hatch while an issue you have filed is fixed, and it will be removed once the native browser network is supported long term. Do not set it to true only to avoid updating tests for the native browser network, such as skipping Cypress.isBrowser() gates. Treat it as a temporary aid, not a permanent setting.
Default keystrokeDelay changed from 10 to 0
The default delay between keystrokes in cy.type() has been changed from 10 to 0 milliseconds to improve performance of typing-heavy test runs.
If your tests relied on the implicit 10ms delay between keystrokes, you can restore the previous behavior using any of these approaches. The resolved keystrokeDelay is read from your Cypress configuration first, then Cypress.Keyboard.defaults(), and falls back to the built-in default of 0 if neither is set. A per-command delay passed to .type() is independent of this and overrides the resolved value for that single call.
Set keystrokeDelay in your Cypress configuration
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
keystrokeDelay: 10,
})
import { defineConfig } from 'cypress'
export default defineConfig({
keystrokeDelay: 10,
})
Set keystrokeDelay via Cypress.Keyboard.defaults()
Cypress.Keyboard.defaults({
keystrokeDelay: 10,
})
Set delay per command
cy.get('input').type('slow typing', { delay: 10 })
experimentalFastVisibility replaced by visibilityStrategy
The experimental experimentalFastVisibility boolean flag has been removed. Its successor is the new visibilityStrategy configuration option, which accepts 'legacy' or 'modern' and defaults to 'modern'. The modern algorithm is now the default for all users, not just those who opted into the experiment.
If you were setting experimentalFastVisibility: true: remove it. The modern algorithm is now the default.
If you were setting experimentalFastVisibility: false (or relying on the 15.x default) and a test now fails: the modern algorithm intentionally drops some behaviors the legacy algorithm had (ancestor overflow clipping, transform-based hiding, and coverage detection for fixed/sticky elements). Prefer rewriting the affected assertions to verify the same user-visible behavior in an algorithm-agnostic way. See Visibility Strategy for what each algorithm checks and recommended rewrite patterns.
If you need a temporary escape hatch while you update those tests, opt back into the legacy algorithm:
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
visibilityStrategy: 'legacy',
})
import { defineConfig } from 'cypress'
export default defineConfig({
visibilityStrategy: 'legacy',
})
visibilityStrategy is deprecated. Both the 'legacy' value and the option itself will be removed in a future major version of Cypress. Treat it as a temporary migration aid only.
experimentalMemoryManagement replaced by manageBrowserMemory
The experimental experimentalMemoryManagement configuration option has been replaced by the new manageBrowserMemory configuration option, which defaults to true. Browser memory management is now on by default for all users running Chromium-based browsers.
If your Cypress configuration still sets experimentalMemoryManagement, Cypress now ignores the option and prints a warning.
If you were setting experimentalMemoryManagement: true: remove it. Memory management is now the default.
{
experimentalMemoryManagement: true,
}
{
// Remove the experimentalMemoryManagement option
}
If you were setting experimentalMemoryManagement: false: replace it with manageBrowserMemory: false. Deleting it without adding the replacement silently opts you into memory management.
{
experimentalMemoryManagement: false,
}
{
manageBrowserMemory: false,
}
See manageBrowserMemory for more information.
cy.getCookie(), cy.getCookies(), and cy.getAllCookies() are now queries
cy.getCookie(),
cy.getCookies(), and
cy.getAllCookies() are now retry-able
query commands, just like
cy.get() and cy.contains().
Previously they were one-shot commands that resolved a single value and ran any chained assertions only once. As of Cypress 16 they retry their built-in and chained assertions until they pass or time out.
Reads are now retried
A read such as cy.getCookie('session_id').should('exist') no longer fails on the first
missing read. The command re-reads the cookie and retries the assertion until it
passes or the command times out, so you no longer need to manually wait for a
cookie to be written before asserting on it.
Timeouts now follow defaultCommandTimeout
This changes which timeout governs them:
- Query commands (
cy.getCookie(),cy.getCookies(), andcy.getAllCookies()) are now governed bydefaultCommandTimeout(default4000ms), the same timeout used by every other query. - Cookie action commands (
cy.setCookie(),cy.clearCookie(),cy.clearCookies(), andcy.clearAllCookies()) are unchanged. They make a single automation round-trip, do not retry assertions, and remain governed byresponseTimeout(default30000ms).
The new retry behavior applies only to assertions chained directly onto the
command. A .then() callback still runs only once, after
the command settles, and is not retried. The cookie (or array of cookies) it
receives is a single snapshot read at that moment, so an assertion made inside
.then() will not wait for the cookie to be set. To benefit from retries, chain
.should() assertions directly instead:
// Runs once and does not retry, so it fails immediately if the cookie isn't set yet
cy.getCookie('session_id').then((cookie) => {
expect(cookie).to.have.property('value', '189jd09su')
})
// Retries, re-reading the cookie, until the assertion passes or times out
cy.getCookie('session_id').should('have.property', 'value', '189jd09su')
If you specifically want a one-shot check that does not retry, for example
to assert that a cookie does not yet exist, perform the assertion inside a
.then() callback, which runs once and is not retried:
cy.getCookie('session_id').then((cookie) => {
expect(cookie).to.be.null
})
cy.getCookie(), cy.getCookies(), and cy.getAllCookies() can no longer be overwritten with Cypress.Commands.overwrite()
Queries must be overwritten using Cypress.Commands.overwriteQuery(). If you
were previously overwriting cy.getCookie(), cy.getCookies(), or
cy.getAllCookies(), update your code to use
Cypress.Commands.overwriteQuery('getCookie', function () { ... }) rather than
Cypress.Commands.overwrite('getCookie', () => { ... }). For more details on
overwriting queries, see
Overwriting Existing Queries.
Cypress.Commands.overwrite('getCookie', (name, options) => {
return cy.getCookie(name, options)
})
Cypress.Commands.overwriteQuery('getCookie', (name, options) => {
return cy.getCookie(name, options)
})
See Overwriting Existing Queries for more details.
cy.getAllLocalStorage() and cy.getAllSessionStorage() are now queries
cy.getAllLocalStorage() and
cy.getAllSessionStorage() are now
retry-able query commands, just like
cy.getCookie() and
cy.getCookies().
Previously they read storage once and ran any chained assertions a single time,
so they could fail on the first read when storage was populated asynchronously.
As of Cypress 16 they re-read localStorage and sessionStorage from all
origins and retry their chained assertions until they pass or time out.
Reads are now retried
A read such as
cy.getAllLocalStorage().should('have.property', 'https://example.cypress.io')
no longer fails on the first empty read. The command re-reads storage and retries
the assertion until it passes or the command times out, so you no longer need to
manually wait for storage to be written before asserting on it.
Timeouts now follow defaultCommandTimeout
Both commands are now Timeoutable
and accept a timeout option. They are governed by
defaultCommandTimeout (default
4000 ms), the same timeout used by every other query. When a read times out,
the error is reported with the standard Timed out retrying after ... prefix.
The new retry behavior applies only to assertions chained directly onto the
command. A .then() callback still runs only once, after
the command settles, and is not retried. The object it receives is a single
snapshot read at that moment, so an assertion made inside .then() will not wait
for storage to be populated. To benefit from retries, chain
.should() assertions directly instead:
// Runs once and does not retry, so it fails immediately if the origin isn't present
cy.getAllLocalStorage().then((result) => {
expect(result).to.have.property('https://example.cypress.io')
})
// Retries, re-reading storage, until the assertion passes or times out
cy.getAllLocalStorage().should('have.property', 'https://example.cypress.io')
If you specifically want a one-shot check that does not retry, for example
to assert that no storage has been written yet, perform the assertion inside a
.then() callback, which runs once and is not retried:
cy.getAllLocalStorage().then((result) => {
expect(result).to.deep.equal({})
})
cy.getAllLocalStorage() and cy.getAllSessionStorage() can no longer be overwritten with Cypress.Commands.overwrite()
Queries must be overwritten using Cypress.Commands.overwriteQuery(). If you
were previously overwriting cy.getAllLocalStorage() or
cy.getAllSessionStorage(), update your code to use
Cypress.Commands.overwriteQuery('getAllLocalStorage', function () { ... })
rather than
Cypress.Commands.overwrite('getAllLocalStorage', () => { ... }). For more
details on overwriting queries, see
Overwriting Existing Queries.
Cypress.Commands.overwrite('getAllLocalStorage', (options) => {
return cy.getAllLocalStorage(options)
})
Cypress.Commands.overwriteQuery('getAllLocalStorage', (options) => {
return cy.getAllLocalStorage(options)
})
See Overwriting Existing Queries for more details.
viewportWidth and viewportHeight can no longer be set with Cypress.config() during test execution
viewportWidth and viewportHeight can no longer be set with Cypress.config() while a test is executing. Doing so applied to the next test rather than the current one, and the change was not reflected in Test Replay, so Cypress now throws instead.
Use cy.viewport() to change the viewport during a test, or set the values in the test configuration of a describe, context, or it block.
it('does not display the sidebar on small screens', () => {
Cypress.config('viewportWidth', 400)
Cypress.config('viewportHeight', 1000)
cy.get('#sidebar').should('not.be.visible')
})
it('does not display the sidebar on small screens', () => {
cy.viewport(400, 1000)
cy.get('#sidebar').should('not.be.visible')
})
Or, to apply the viewport to a whole suite or test:
describe(
'page display on medium size screen',
{ viewportWidth: 400, viewportHeight: 1000 },
() => {
it('does not display the sidebar', () => {
cy.get('#sidebar').should('not.be.visible')
})
}
)
Setting viewportWidth and viewportHeight in your Cypress configuration, or with Cypress.config() outside of test execution (for example at the top level of a support file or spec file), is unchanged.
blockHosts can no longer be set with Cypress.config() during test execution
blockHosts can no longer be set with Cypress.config() while a test is executing. Doing so applied to the next test rather than the current one, and the value leaked into every test that followed, so Cypress now throws instead.
Set blockHosts in the test configuration of a describe, context, or it block instead, which takes effect for the suite or test where it is set and is restored afterwards.
it('does not load analytics', () => {
Cypress.config('blockHosts', 'www.google-analytics.com')
cy.visit('/')
})
it(
'does not load analytics',
{ blockHosts: 'www.google-analytics.com' },
() => {
cy.visit('/')
}
)
Setting blockHosts in your Cypress configuration, or with Cypress.config() outside of test execution (for example at the top level of a support file or spec file), is unchanged.
experimentalSourceRewriting config option removed
The experimentalSourceRewriting configuration option has been removed. The default regex-based rewriter handles every case the AST-based rewriter did and more, without the performance issues the AST-based approach could introduce.
If your cypress.config still sets experimentalSourceRewriting, Cypress will print a warning telling you to remove it. You can safely delete the option from your config.
You only need a replacement option if your application pins resources with Subresource Integrity (SRI) integrity attributes on <script> or <link> elements. Because Cypress rewrites those resources, the pinned hash no longer matches and the browser blocks them, reporting an error such as Failed to find a valid digest in the 'integrity' attribute for resource '…'. If you were relying on experimentalSourceRewriting to avoid this, enable the removeSRIAttributes configuration option instead, which strips the integrity attribute so the rewritten resources load. If your application does not use SRI, no replacement is required.
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
// Remove this line
experimentalSourceRewriting: true,
})
import { defineConfig } from 'cypress'
export default defineConfig({
// Remove this line
experimentalSourceRewriting: true,
})
cy.end() removed
cy.end() has been removed. It ended a chain of commands by yielding null,
which was never necessary, because a Cypress chain is already terminated when
the next cy.<command>() starts a new chain.
There is no replacement. Delete the .end() calls.
cy.get('[data-cy="submit"]').click().end()
cy.get('[data-cy="name"]').type('Jane')
cy.get('[data-cy="submit"]').click()
cy.get('[data-cy="name"]').type('Jane')
cy.end() was deprecated in Cypress
15.15.0 and removed in 16.0.
cy.exec() removed
cy.exec() has been removed. It ran a command through the shell on the machine
running Cypress, so its behavior depended on the operating system, the shell,
the login environment, and whether a terminal was attached.
Use cy.task() instead. A task runs in the Node.js
process that loads your Cypress configuration, so there is no shell resolution,
no dependence on an attached terminal, and no per-platform quoting rules. It
also returns real values instead of stdout you have to parse.
Register the task in setupNodeEvents:
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
const fs = require('fs')
const path = require('path')
module.exports = defineConfig({
// setupNodeEvents can be defined in either
// the e2e or component configuration
e2e: {
setupNodeEvents(on, config) {
const downloads = config.downloadsFolder
on('task', {
latestDownload() {
const files = fs
.readdirSync(downloads)
.map((name) => ({
name,
time: fs.statSync(path.join(downloads, name)).mtimeMs,
}))
.sort((a, b) => b.time - a.time)
return files.length ? path.join(downloads, files[0].name) : null
},
})
},
},
})
import { defineConfig } from 'cypress'
import fs from 'fs'
import path from 'path'
export default defineConfig({
// setupNodeEvents can be defined in either
// the e2e or component configuration
e2e: {
setupNodeEvents(on, config) {
const downloads = config.downloadsFolder
on('task', {
latestDownload() {
const files = fs
.readdirSync(downloads)
.map((name) => ({
name,
time: fs.statSync(path.join(downloads, name)).mtimeMs,
}))
.sort((a, b) => b.time - a.time)
return files.length ? path.join(downloads, files[0].name) : null
},
})
},
},
})
Then call it from your test:
Beforecy.exec('ls -t cypress/downloads | head -1')
.its('stdout')
.then((filename) => {
cy.readFile(`cypress/downloads/${filename.trim()}`)
})
cy.task('latestDownload').then((filepath) => {
cy.readFile(filepath)
})
If you need to run something that is not Node.js, spawn it from inside the task
with execFileSync and an argument array, which avoids the shell entirely:
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
const { execFileSync } = require('child_process')
module.exports = defineConfig({
// setupNodeEvents can be defined in either
// the e2e or component configuration
e2e: {
setupNodeEvents(on, config) {
on('task', {
resetDatabase() {
execFileSync('docker', ['exec', 'db', 'reset-script'], {
stdio: 'pipe',
})
return null
},
})
},
},
})
import { defineConfig } from 'cypress'
import { execFileSync } from 'child_process'
export default defineConfig({
// setupNodeEvents can be defined in either
// the e2e or component configuration
e2e: {
setupNodeEvents(on, config) {
on('task', {
resetDatabase() {
execFileSync('docker', ['exec', 'db', 'reset-script'], {
stdio: 'pipe',
})
return null
},
})
},
},
})
A task must return a value or null, and tasks time out after 60 seconds by
default. Use taskTimeout to change that.
The cy.task() examples cover the patterns
cy.exec() was commonly used for, including
cleaning up and verifying downloaded files,
running an external command or CLI,
and seeding a database.
The execTimeout configuration option has been removed alongside the command.
Setting it prints a warning and is otherwise ignored, so you can delete it from
your configuration.
cy.exec() was deprecated in Cypress
15.21.0 and removed in 16.0.
Cypress.env() removed
Cypress.env() has been removed in Cypress 16.0. Use cy.env() for sensitive values (API keys, tokens, secrets) or Cypress.expose() for non-sensitive configuration that needs synchronous browser access.
See Migrating away from Cypress.env() below for detailed before/after examples.
env removed from test config overrides
The env key is no longer valid in per-test or suite config overrides. Test config overrides run in the browser, so env values set there could never be applied server-side. If absolutely necessary, expose can be used instead.
it(
'runs with feature flag',
{
env: {
FEATURE_FLAG: true,
API_VERSION: 'v2',
},
},
() => {
expect(Cypress.env('FEATURE_FLAG')).to.be.true
expect(Cypress.env('API_VERSION')).eq('v2')
}
)
it(
'runs with feature flag',
{
expose: {
FEATURE_FLAG: true,
API_VERSION: 'v2',
},
},
() => {
expect(Cypress.expose('FEATURE_FLAG')).to.be.true
expect(Cypress.expose('API_VERSION')).eq('v2')
}
)
allowCypressEnv config option removed
The allowCypressEnv configuration option has been removed. If your cypress.config still includes it, Cypress will print a warning telling you to remove it. There is no behavior change, so just remove the key.
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
// Remove this line
allowCypressEnv: true,
})
import { defineConfig } from 'cypress'
export default defineConfig({
// Remove this line
allowCypressEnv: true,
})
CoffeeScript support removed
Built-in CoffeeScript support has been removed. The default
@cypress/webpack-batteries-included-preprocessor
no longer bundles coffee-loader or coffeescript, and .coffee is no longer
resolved for spec files, support files, or
cy.fixture().
Most projects are unaffected. If your project has no .coffee files, there is
nothing to do, and dropping the CoffeeScript toolchain from the default
preprocessor means one less thing to bundle on every spec startup.
If you do have .coffee files, the recommended path is to convert them to
JavaScript or TypeScript, both of which Cypress compiles out of the box. If you
need to keep CoffeeScript, add the loader to your own webpack configuration and
register it with
@cypress/webpack-preprocessor:
const { defineConfig } = require('cypress')
const webpackPreprocessor = require('@cypress/webpack-preprocessor')
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
on(
'file:preprocessor',
webpackPreprocessor({
webpackOptions: {
resolve: { extensions: ['.js', '.coffee'] },
module: {
rules: [{ test: /\.coffee$/, use: ['coffee-loader'] }],
},
},
})
)
return config
},
},
})
You are responsible for installing coffee-loader and coffeescript in your
own project.
cypress info no longer prints environment variables
cypress info no longer prints proxy environment variables (HTTP_PROXY,
HTTPS_PROXY, NO_PROXY) or any CYPRESS_* environment variables. Those
values routinely hold credentials, such as a record key, a proxy password, or an
API token, and cypress info output is most often pasted into a bug report or a
CI log, so its output is now safe to share.
The command still reports the details it is usually run for: detected browsers, application data, browser profile and binary cache paths, the Cypress version, and system platform and memory.
Angular 18, 19, and 20 are no longer supported for Component Testing
Angular 18 and 19 have reached end-of-life, and Angular 20 reaches LTS end in November 2026. As a result, the minimum required Angular version for component testing is now 21.0.0.
This allows us to ship the zoneless testing harness as the new default cypress/angular test harness in Cypress 16.0.
Additionally, the @cypress/angular-zoneless npm package is deprecated and no longer supported. It has been merged into @cypress/angular.
To continue using Angular below 21.0.0
If you haven't been able to migrate away from an older Angular version and still need that test harness, it can be installed independently via the @cypress/angular 4.x.x package from npm.
Note that this test harness version is deprecated and no longer supported by Cypress. This version is intended to serve as a temporary workaround to migrate your project to Angular v21.0.0+.
npm install --save-dev @cypress/angular@4
Inside your support file (ex: ./cypress/support/component.(js|ts)), or wherever your mount function is imported, make the following update to add @.
import { mount } from 'cypress/angular'
import { mount } from '@cypress/angular'
If already using cypress/angular-zoneless and Angular 21.0.0
If you are already using cypress/angular-zoneless, update your imports to use cypress/angular instead. The zoneless harness has been merged into the cypress/angular test harness in Cypress 16.0.
import { mount } from 'cypress/angular-zoneless'
import { mount } from 'cypress/angular'
@angular/platform-browser-dynamic has been replaced with @angular/platform-browser
As of Cypress 16.0.0, the @angular/platform-browser-dynamic package has been replaced with the @angular/platform-browser package.
This is because the @angular/platform-browser-dynamic package is deprecated and no longer the default.
To migrate, nothing additional is required as Angular 21 uses @angular/platform-browser by default. You can likely uninstall the @angular/platform-browser-dynamic package from your project dependencies.
Vite 5, 6, and 7 are no longer supported for Component Testing
As of Cypress 16.0.0, @cypress/vite-dev-server requires Vite 8.x or above for component testing. Vite 5, 6, and 7 are no longer supported when Cypress resolves the Vite version installed in your project.
If an unsupported Vite version is detected, Cypress throws ViteVersionNotSupportedError:
ViteVersionNotSupportedError: Vite 8 is the required version to use cypress/vite-dev-server. Found Vite version v<your-version>
The recommended path is to upgrade your project to Vite 8.
To continue using Vite 5 through 7
If you are upgrading to Cypress 16 but still need Vite 5, 6, or 7 for component testing, install 7.x of @cypress/vite-dev-server from npm and configure it explicitly as the dev server:
npm install --save-dev @cypress/vite-dev-server@7
Then configure the devServer in your cypress.config.js or cypress.config.ts file:
import { defineConfig } from 'cypress'
import { devServer } from '@cypress/vite-dev-server'
export default defineConfig({
component: {
devServer(devServerConfig) {
return devServer({
...devServerConfig,
framework: '<YOUR_FRAMEWORK>',
bundler: 'vite',
})
},
},
})
Change <YOUR_FRAMEWORK> to 'react', 'vue', 'svelte', or another supported framework to match your component testing setup.
Note that @cypress/vite-dev-server@7 is deprecated and no longer supported by Cypress. It is intended as a temporary workaround until you can migrate to Vite 8. More information on configuring a custom dev server can be found in the Cypress Vite Dev Server documentation.
Next.js 14 is no longer supported for Component Testing
As of Cypress 16.0.0, component testing requires Next.js 15.0.4 or newer,
or Next.js 16. Next.js 14 is no longer supported.
Upgrade Next.js in your project before upgrading Cypress. See the Next.js upgrade guide for the steps that apply to your application, and Next.js component testing for the Cypress-side configuration.
Electron browser deprecated
The bundled Electron browser is deprecated as a test browser in Cypress 16.0.0 and will be removed in a future major version. If your runs rely on the implicit Electron default, set a defaultBrowser (for example, 'chrome') and/or pass --browser to switch to an installed browser. See Migrating away from the Electron browser for the full guide.
Migrating away from Cypress.env()
Migrate off Cypress.env() with your AI assistant
Copies a ready-made prompt that walks your AI coding assistant through this migration. Works with any AI tool that can read and edit your project.
Read https://docs.cypress.io/app/references/migration-guide/migrating-away-from-cypress-env.md, then migrate this project off Cypress.env() by working through these steps: 1. Check my Cypress version. cy.env() and Cypress.expose() require Cypress 15.10.0 or newer, so check my installed version first: if it's on an older 15.x release, detect my package manager and update the cypress dependency to the latest 15.x; if it's below Cypress 15, stop and tell me, since majors should be upgraded one at a time first. 2. Classify each value. Find every Cypress.env() usage and classify each value as sensitive or public. 3. Migrate the values. Migrate sensitive reads to cy.env() and public values to Cypress.expose() with a top-level expose block in my Cypress config. Check for --env CLI flags and plugins that rely on Cypress.env(), and show me the proposed changes before applying them. 4. Lock it down and verify. Once every Cypress.env() call is gone, add allowCypressEnv: false to my config and run my Cypress tests to confirm nothing broke. Finish with a summary of what changed and anything you couldn't safely automate.
Cypress.env() was deprecated in Cypress 15.10.0 and removed in Cypress 16.0. This guide helps you migrate to cy.env() (for sensitive values) or Cypress.expose() (for public/non-sensitive values), depending on your use case.
Why Cypress.env() was removed
Cypress.env() made it easy to accidentally expose more data than intended because Cypress hydrated all configured environment variables into the browser context, including values your test never reads.
Removing it reduces the risk of unintentionally exposing secrets in the browser-accessible state. This enforces safer defaults and prevents accidental exposure of sensitive data.
What was the risk
With Cypress.env(), environment values available in the browser could be inspected through normal browser tooling and accessed by code running in that context (for example, app code, third-party scripts, or extensions). The core issues were:
- All-or-nothing exposure: All environment variables were serialized and sent to the browser where they could be accessed via
Cypress.env()or inspected in the browser's developer tools. - Cross-origin risks: When using
cy.origin()for cross-origin testing, all environment variables were automatically passed to the cross-origin context, potentially exposing sensitive data to untrusted origins. - Application code access: Since environment variables were available in the browser context when tests ran, application code or third-party scripts could potentially access them by checking for
window.Cypressor similar patterns.
Choose the right migration path
When migrating from Cypress.env(), you have two options: cy.env() and Cypress.expose(). The choice depends on the sensitivity of your configuration values and your access requirements.
Use cy.env() when:
- The values are sensitive (API keys, passwords, tokens, credentials, secrets)
- You're using the value inside tests/hooks where async Cypress commands are fine
- You want the most conservative exposure model (only request what you need)
Use Cypress.expose() when:
- The value is public/non-sensitive (feature flags, API versions, public URLs, plugin configurations)
- You need synchronous access
- It's acceptable for the value to be accessible in the browser context (e.g. application code, third-party scripts, or extensions)
Migrate sensitive values to cy.env()
cy.env() is read-only and asynchronous.
cy.env() logs the key names you ask for and never the values. That protection
ends at the command boundary. The object cy.env() yields is an ordinary
JavaScript object, and Cypress does not mask, redact, or track the values inside
it. Assertions, .its(),
.invoke(), and any chained command that fails can all
print a value to the Command Log and
the console output. What happens
to a value after cy.env() yields it is up to you. See
Handling the yielded value safely.
Every assertion writes to the Command Log, and the entry contains the values being compared. Assertions accept no logging options, so you cannot suppress them. Assert on a boolean you derive from the value instead.
Incorrect Usage
cy.env(['apiKey']).should('deep.include', { apiKey: 'secret-key-12345' })
// ❌ Command Log: assert expected { apiKey: 'secret-key-12345' } to deep
// include { apiKey: 'secret-key-12345' }
Correct Usage
cy.env(['apiKey']).then(({ apiKey }) => {
expect(Boolean(apiKey)).to.be.true
})
// ✅ Command Log: assert expected true to be true
Read one value
Before: Cypress.env()describe('API tests', () => {
it('makes a request to the API', () => {
const apiKey = Cypress.env('apiKey')
cy.request({
url: 'https://api.example.com/users',
headers: { Authorization: `Bearer ${apiKey}` },
})
.its('status')
.should('eq', 200)
})
})
describe('API tests', () => {
it('makes a request to the API', () => {
cy.env(['apiKey']).then(({ apiKey }) => {
cy.request({
url: 'https://api.example.com/users',
headers: { Authorization: `Bearer ${apiKey}` },
})
.its('status')
.should('eq', 200)
})
})
})
Read multiple values
Before: Cypress.env()describe('API tests', () => {
it('makes authenticated requests', () => {
const apiUrl = Cypress.env('apiUrl')
const apiKey = Cypress.env('apiKey')
cy.request({
url: `${apiUrl}/users`,
headers: { Authorization: `Bearer ${apiKey}` },
})
.its('status')
.should('eq', 200)
})
})
describe('API tests', () => {
it('makes authenticated requests', () => {
cy.env(['apiUrl', 'apiKey']).then(({ apiUrl, apiKey }) => {
cy.request({
url: `${apiUrl}/users`,
headers: { Authorization: `Bearer ${apiKey}` },
})
.its('status')
.should('eq', 200)
})
})
})
See cy.env() for more details.
Migrate public config to Cypress.expose()
Use this path only for non-sensitive values. Cypress.expose() is synchronous and is designed for configuration that is safe to be available in the browser.
Via configuration file
Before: Cypress.env()const pluginConfig = Cypress.env('PLUGIN_CONFIG')
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
env: {
apiKey: 'secret-key-12345',
featureFlag: true,
apiVersion: 'v2',
publicApiUrl: 'https://api.example.com',
pluginConfig: 'development',
},
})
import { defineConfig } from 'cypress'
export default defineConfig({
env: {
apiKey: 'secret-key-12345',
featureFlag: true,
apiVersion: 'v2',
publicApiUrl: 'https://api.example.com',
pluginConfig: 'development',
},
})
const pluginConfig = Cypress.expose('PLUGIN_CONFIG')
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
expose: {
featureFlag: true,
apiVersion: 'v2',
publicApiUrl: 'https://api.example.com',
pluginConfig: 'development',
},
env: {
apiKey: 'secret-key-12345', // Sensitive - use env, not expose
},
})
import { defineConfig } from 'cypress'
export default defineConfig({
expose: {
featureFlag: true,
apiVersion: 'v2',
publicApiUrl: 'https://api.example.com',
pluginConfig: 'development',
},
env: {
apiKey: 'secret-key-12345', // Sensitive - use env, not expose
},
})
See Cypress.expose() for more details.
Via CLI flags
Use the --expose or -x CLI flags when running Cypress.
cypress run --env FEATURE_FLAG=true,API_VERSION=v2,PUBLIC_API_URL=https://api.example.com
cypress run --expose FEATURE_FLAG=true,API_VERSION=v2,PUBLIC_API_URL=https://api.example.com
See Cypress.expose() for more details.
Via runtime
You can set exposed values at runtime using Cypress.expose(key, value) or Cypress.expose(object). Note that these changes only persist for the remainder of the current spec file:
// Set a single value
Cypress.expose('featureFlag', true)
// Set multiple values
Cypress.expose({
featureFlag: true,
apiVersion: 'v2',
})
See Cypress.expose() for more details.
If you were setting values with Cypress.env()
cy.env() cannot set values. If you used Cypress.env() to write sensitive values at runtime, you can use cy.task() to store state in the Cypress config process. These are not environment variables and are not accessible through cy.env().
Setup in cypress.config.js:
// cypress.config.js
export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
const configProcessScopedVariables = {}
on('task', {
set: (keySet) => {
Object.entries(keySet).forEach(([key, value]) => {
configProcessScopedVariables[key] = value
})
return null
},
get: (keys) => {
const variablesToReturn = {}
keys.forEach((key) => {
variablesToReturn[key] = configProcessScopedVariables[key]
})
return variablesToReturn
},
})
return config
},
},
})
Usage in your tests:
// spec.cy.js
describe('API tests', () => {
it('stores and retrieves sensitive runtime values', () => {
// Get an access token from an API
cy.getAccessTokenFromApi().then(({ token }) => {
// Store it securely in the config process
cy.task('set', { accessToken: token })
})
// Later in the test, retrieve it
cy.task('get', ['accessToken']).then(({ accessToken }) => {
// Use the token securely
cy.request({
url: 'https://api.example.com/data',
headers: { Authorization: `Bearer ${accessToken}` },
})
})
})
})
Migrate plugins that use Cypress.env()
If you're using Cypress plugins that reference Cypress.env(), you should check for updated versions that support the new API. Starting in Cypress 15.10.0, plugins using Cypress.env() would throw errors when allowCypressEnv: false was set. In Cypress 16.0, allowCypressEnv has been removed and Cypress.env() is no longer available, so plugins that still call it will always throw errors.
Check for plugin updates
Review your installed plugins and update them to their latest versions. Many popular plugins need to be updated to use Cypress.expose() or cy.env() instead of Cypress.env().
Plugins requiring migration
The following plugins require updates to their latest major versions and migration to the new API:
@cypress/code-coverage
Update to version 4.0.0+ of @cypress/code-coverage and follow its migration guide to move from environment variables to --expose CLI flags and expose config.
@cypress/grep
Update to version 6.0.0+ of @cypress/grep and follow its migration guide to move from environment variables to --expose CLI flags and expose config.
Community plugins
Many community plugins have already adopted the new API. Update to at least the version listed below to stop the Cypress.env() deprecation warning, and make the config change noted, if any.
| Plugin | Version | Config changes |
|---|---|---|
| @badeball/cypress-cucumber-preprocessor | 27.0.0+ | Use -x or --expose command line options for configuration overrides, not -e or --env as previously |
| @bahmutov/cy-api | 2.3.0+ | Move options from env to expose |
| @chromatic-com/cypress | 0.12.5+ | Move options from env to expose; requires Cypress 15.10+ |
| @frsource/cypress-plugin-visual-regression-diff | 4.1.0+ | Move options from env to expose |
| @percy/cypress | 3.1.8+ | Move PERCY_* values from env to expose (unexposed still warns) |
| @simonsmith/cypress-image-snapshot | 10.1.1+ | Move options from env to expose (unexposed still warns) |
| cypress-data-session | 3.0.0+ | Move options from env to expose; requires Cypress 15.10+ |
| cypress-fail-fast | 8.0.0+ | Move options from env to expose; renamed (FAIL_FAST_STRATEGY → failFastStrategy) |
| cypress-firebase | 4.4.0+ | Set allowCypressEnv: false to disable its Cypress.env() fallback |
| cypress-image-diff-js | 2.7.0+ | None |
| cypress-localstorage-commands | 2.3.0+ | None |
| cypress-maildev | 2.0.0+ | Move MAILDEV_* values from env to expose |
| cypress-mailosaur | 4.0.0+ | None |
| cypress-mailpit | 2.0.0+ | Move MAILPIT_URL from env to expose; keep credentials in env |
| cypress-mongodb | 7.0.0+ | None |
| cypress-network-idle | 2.0.1+ | None; requires Cypress 15.10+ |
| cypress-ntlm-auth | 4.2.4+ | None |
| cypress-plugin-api | 2.12.0+ | Move options from env to expose (unexposed options still warn) |
| cypress-plugin-grep-boxes | 3.0.0+ | Move options from env to expose |
| cypress-plugin-last-failed | 3.0.0+ | Requires Cypress 15.10+; also update @cypress/grep to 6.0.0+ |
| cypress-plugin-xhr-toggle | 1.3.0+ | Move hideXhr from env to expose (unexposed still warns) |
| cypress-runner-themes | 1.1.0+ | Move theme from env to expose (unexposed still warns) |
| cypress-visual-regression | 6.0.0+ | Move options from env to expose |
| cypress-voice-plugin | 2.0.0+ | Move options from env to expose |
| cypress-watch-and-reload | 2.1.0+ | Requires Cypress 15.11+ |
If a plugin you depend on isn't listed here, search its shipped code for Cypress.env() to check whether it has migrated.
Lock it down with allowCypressEnv: false (Cypress ^15.10.0)
allowCypressEnv was introduced in Cypress 15.10.0 as a migration gate and removed in Cypress 16.0. If you are already on Cypress 16.0, skip this step. Cypress.env() is gone and allowCypressEnv is no longer a valid config option.
On Cypress ^15.10.0, once you've migrated all Cypress.env() invocations to either cy.env() or Cypress.expose(), you can set allowCypressEnv: false to enforce the new APIs and catch any remaining usages.
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
allowCypressEnv: false,
env: {
apiUrl: 'https://api.example.com',
apiKey: 'secret-key-12345',
},
e2e: {
baseUrl: 'http://localhost:3000',
},
})
import { defineConfig } from 'cypress'
export default defineConfig({
allowCypressEnv: false,
env: {
apiUrl: 'https://api.example.com',
apiKey: 'secret-key-12345',
},
e2e: {
baseUrl: 'http://localhost:3000',
},
})
When allowCypressEnv is set to false on Cypress ^15.10.0:
Cypress.env()calls will throw an errorenvtest configuration overrides are disabled
Migration Checklist
- ✅ Search your codebase for all
Cypress.env()calls - ✅ Replace each
Cypress.env()call with eithercy.env()orCypress.expose() - ✅ Update code to handle the asynchronous nature of
cy.env() - ✅ Migrate plugins that use
Cypress.env() - ✅ Cypress ^15.10.0 only: Set
allowCypressEnv: falsein your Cypress configuration and verify no errors are thrown - ✅ Cypress 16.0+: Remove
allowCypressEnvfrom your configuration if present
Migrating away from the Electron browser
Starting with Cypress 16.0.0, the bundled Electron browser is deprecated as a test browser and will be removed in a future major version. This guide helps you move your local and CI runs to an installed browser, such as Chrome, before the removal.
Why Electron is being removed
The Electron browser ships inside Cypress as a version of Chromium bundled with Electron. It was convenient because it required no separate install, but it no longer gives you a representative testing environment:
- It lags Chromium. Electron's Chromium version is tied to the Electron release Cypress bundles, so it trails the current stable Chrome by weeks or months and misses recent web-platform features and fixes.
- Its behavior diverges from Chrome. Because it is an embedded Chromium rather than the browser your users run, test behavior in Electron can differ from real Chrome, which undermines confidence in the results.
Standardizing on installed browsers means your tests run in the same engines your users do, on a predictable, evergreen update cadence.
What happens after removal
Until removal, targeting Electron continues to work but emits a deprecation
warning in cypress run output and in the Cypress app. After Electron is
removed, runs that target it, whether through --browser electron, a
defaultBrowser: 'electron' setting, or an implicit fallback to Electron when no
browser is specified, will fail with an error.
Migrate to an installed browser
-
Install a supported browser in your local and CI environments (for example, Google Chrome). If you don't want to install one yourself, our Continuous Integration guides cover browser options for CI, including the Cypress Docker images, the GitHub Action, and the CircleCI Orb.
-
Set a
defaultBrowserso Cypress no longer falls back to Electron when no browser is specified:import { defineConfig } from 'cypress'export default defineConfig({defaultBrowser: 'chrome',}) -
Or pass
--browserexplicitly wherever you launch Cypress:cypress run --browser chrome -
Update your CI configuration. If your pipeline relied on the implicit Electron default, update any jobs that previously ran without a browser argument to target an installed browser. See Continuous Integration for provider-specific setup details.
-
Update your Docker image, if you use one. If you use the
cypress/baseDocker image and don't explicitly install a browser, you'll need to switch tocypress/browsersor install a browser yourself. -
Remove any explicit Electron usage:
--browser electron,defaultBrowser: 'electron', or{ browser: 'electron' }test/suite overrides. Replace it with an installed browser.
Migration checklist
- ✅ Install a supported browser locally and in CI
- ✅ Set
defaultBrowser(or pass--browser) so runs don't fall back to Electron - ✅ Replace any
--browser electron/defaultBrowser: 'electron'usage - ✅ Update your CI config (GitHub Action, Docker) to target an installed browser
- ✅ Verify your runs no longer emit the Electron deprecation warning
Migrating to Cypress 15.0
Upgrade to Cypress 15 with your AI assistant
Copies a ready-made prompt that walks your AI coding assistant through this migration. Works with any AI tool that can read and edit your project.
Read https://docs.cypress.io/app/references/migration-guide/migrating-to-cypress-15-0.md, then upgrade this project from Cypress 14 to 15 by working through these steps: 1. Confirm the starting point. If the project isn't on Cypress 14.x yet, stop and tell me; majors should be upgraded one at a time. 2. Check requirements. Verify my environment meets the requirements in that section (Node.js 20, 22, or 24+, a supported Linux distribution, Firefox 140+ if I run tests in Firefox, and webpack 5 / Vite 5 / Angular 18+ if I use component testing) and flag anything unsupported. 3. Update Cypress. Detect my package manager and update the cypress dependency. 4. Fix breaking changes. Fix every breaking change in that section that affects my code and config. 5. Verify. Run npx cypress verify and my Cypress tests. Finish with a summary of what changed and anything you couldn't safely automate.
This guide details the code changes needed to migrate to Cypress version 15. See the full changelog for version v15.0.
Node.js 20, 22 and 24+ support
Cypress requires Node.js in order to install the Cypress binary and the supported versions are now Node.js 20, 22, 24 and above. Node.js versions 18 and 23 are no longer supported. See Node's release schedule.
cy.exec code property renamed
The code property on cy.exec() has been renamed to exitCode.
cy.exec('rake db:seed').its('code').should('eq', 0)
cy.exec('rake db:seed').its('exitCode').should('eq', 0)
cy.stub 3-argument signature removed
The deprecated 3 argument signature of cy.stub(), cy.stub(object, name, fn), is no longer supported. Use cy.stub(object, name).callsFake(fn) instead.
cy.stub(user, 'getName', () => 'Jane')
cy.stub(user, 'getName').callsFake(() => 'Jane')
Unsupported Linux Distributions
Prebuilt binaries for Linux are no longer compatible with Linux distributions based on glibc <2.31.
This support is in line with Node.js's support for Linux in 20+.
If you're using a Linux distribution based on glibc <2.31, you'll need to
update your system to a newer version to install Cypress 15+.
To display which version of glibc your Linux system is running, execute ldd --version.
Firefox is automated with WebDriver BiDi only
Support for the Chrome DevTools Protocol (CDP) with Firefox has been removed. WebDriver BiDi is Firefox's supported automation interface. Cypress 14.1.0 began automating Firefox 135 and above with it and fell back to CDP for older versions. Cypress 15 drops that fallback, so WebDriver BiDi is the only way Cypress automates Firefox.
Because WebDriver BiDi is only available in newer Firefox releases, this sets a minimum Firefox version. Cypress cannot launch a version below the minimum and will fail with an error naming the version to install.
- Cypress
15.0.0through15.18.1require Firefox135or newer. - Cypress 15.19.0 and newer require Firefox
140or newer, the oldest release line Mozilla still ships.
If you pin a Firefox version, such as an ESR build, a Docker image tag, or a browser installed by your CI provider, update it before upgrading. Chrome, Edge, Electron, and experimental WebKit are unaffected, and no test code changes are needed: Cypress commands behave the same in Firefox under WebDriver BiDi.
Webpack 4 is no longer supported
Cypress is no longer supporting Webpack 4 as it is no longer maintained by the core Webpack team and Webpack 5 has been available since Q4 2020. This includes dropping Webpack 4 support for:
@cypress/webpack-dev-serverfor component testing. This use case is most common and will require an update to Webpack5.@cypress/webpack-dev-serveralso no longer supports Webpack Dev Server v4. We shipped Webpack Dev Server v5 as the default in Cypress 14 withwebpack-dev-server@4being an option.
@cypress/webpack-preprocessorfor end-to-end testing. Cypress, by default, uses the Webpack Batteries Included Preprocessor to process your files for end-to-end testing, which has used Webpack 5 since Cypress 13. Unless you are already using@cypress/webpack-preprocessoras a standalone package, this change likely does not apply.
To continue using Webpack 4
Component Testing
If you haven't been able to migrate away from Webpack 4 or Webpack Dev Server 4 and still need to be able to run your component tests with Webpack 4 or Webpack Dev Server 4, you can install the following packages independently:
- npm
- Yarn
- pnpm
- Bun
npm install @cypress/webpack-dev-server@4 --save-dev
yarn add @cypress/webpack-dev-server@4 --dev
pnpm add --save-dev @cypress/webpack-dev-server@4
bun add --dev @cypress/webpack-dev-server@4
and configure the dev server within your cypress.config.js or cypress.config.ts file:
import { devServer } from '@cypress/webpack-dev-server'
import { defineConfig } from 'cypress'
export default defineConfig({
component: {
devServer(devServerConfig) {
return devServer({
...devServerConfig,
framework: 'react',
webpackConfig: require('./webpack.config.js'),
})
},
},
})
Note that this package version is deprecated and no longer supported by Cypress and is intended as a workaround until you can migrate to Webpack 5. More information on how to configure the dev server v4 can be found in the Cypress Webpack Dev Server documentation and Custom Dev Server documentation.
End-to-End Testing
If you haven't been able to migrate away from Webpack 4, need custom end-to-end spec file preprocessing, are already using @cypress/webpack-preprocessor as a standalone package, and still need to be able to run your end-to-end tests with Webpack 4, you can install the following package independently:
- npm
- Yarn
- pnpm
- Bun
npm install @cypress/webpack-preprocessor@6 --save-dev
yarn add @cypress/webpack-preprocessor@6 --dev
pnpm add --save-dev @cypress/webpack-preprocessor@6
bun add --dev @cypress/webpack-preprocessor@6
and configure the preprocessor within your cypress.config.js or cypress.config.ts file:
import { defineConfig } from 'cypress'
import webpackPreprocessor from '@cypress/webpack-preprocessor'
export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
on('file:preprocessor', webpackPreprocessor())
},
},
})
As stated earlier, this is likely unnecessary unless you are already using @cypress/webpack-preprocessor as a standalone package. Cypress by default uses the Webpack Batteries Included Preprocessor to process your spec files for end-to-end testing.
Note that this package version is deprecated and no longer supported by Cypress and is intended as a workaround until you can migrate to Webpack 5. More information on how to configure the preprocessor can be found in the Preprocessors API documentation and Webpack Preprocessor documentation.
@cypress/webpack-batteries-included-preprocessor no longer shims all built-ins provided by webpack v4
The default file preprocessor, @cypress/webpack-batteries-included-preprocessor, no longer shims all built-ins that were previously provided by webpack v4. This is mainly to reduce security vulnerabilities and bundle size within the end-to-end file preprocessor.
However, @cypress/webpack-batteries-included-preprocessor still ships with some built-ins, such as buffer, path, process, os, and stream. If other built-ins are required, install @cypress/webpack-batteries-included-preprocessor independently and follow the webpack documentation described in webpack's resolve.fallback to configure the built-ins you need.
For example, the following code shows how to provide the querystring built-in to the preprocessor:
const webpackPreprocessor = require('@cypress/webpack-batteries-included-preprocessor')
function getWebpackOptions() {
const options = webpackPreprocessor.getFullWebpackOptions()
// add built-ins as needed
// NOTE: for this example, querystring-es3 needs to be installed as a dependency
options.resolve.fallback.querystring = require.resolve('querystring-es3')
return options
}
module.exports = (on) => {
on(
'file:preprocessor',
webpackPreprocessor({
// if using typescript, you will need to set the typescript option to true
typescript: true,
webpackOptions: getWebpackOptions(),
})
)
}
Vite 4 is no longer supported
@cypress/vite-dev-server no longer supports Vite 4 for component testing. The minimum supported Vite version is now 5. Review the Vite migration guide for any changes needed in your own Vite configuration when upgrading.
@cypress/vite-dev-server is now ESM-only
@cypress/vite-dev-server, used for Vite-based component testing, is now an ESM-only package and can no longer be used from a CommonJS context.
If your Cypress configuration is CommonJS (for example a cypress.config.cjs, or a cypress.config.js using require() and module.exports without "type": "module" in your package.json), move it to an ESM context: convert the config to use import/export default (a cypress.config.mjs, or set "type": "module" in your package.json), or use a TypeScript config (cypress.config.ts).
Angular 17 CT no longer supported
With LTS end for Angular 17, the minimum required Angular version for component testing is now 18.0.0.
To continue using Angular below 18.0.0
If you haven't been able to migrate away from an older Angular version and still need that test harness, it can be installed independently via the @cypress/angular 3.x.x package from npm.
Note that this test harness version is deprecated and no longer supported by Cypress. This version is intended to serve as a temporary workaround to migrate your project to Angular v18.0.0+.
- npm
- Yarn
- pnpm
- Bun
npm install @cypress/angular@3 --save-dev
yarn add @cypress/angular@3 --dev
pnpm add --save-dev @cypress/angular@3
bun add --dev @cypress/angular@3
Inside your support file (ex: ./cypress/support/component.(js|ts)), or wherever your mount function is imported, make the following update to add @.
import { mount } from `cypress/angular`
import { mount } from `@cypress/angular`
@cypress/angular requires zone.js 0.14.0+
@cypress/angular now requires a minimum zone.js version of 0.14.0. If your project uses Angular component testing and resolves a zone.js version below 0.14.0, update it:
npm install --save-dev zone.js@^0.14.0
Selector Playground API changes
The Cypress.SelectorPlayground API has been renamed to
Cypress.ElementSelector. Additionally, the getSelector method has been removed, and the onElement function has been removed as an option to the defaults method.
This change was made in order to reflect its use in features beyond just the Selector Playground - like Cypress Studio.
The following code shows how to migrate from the Cypress.SelectorPlayground API to the
Cypress.ElementSelector API.
Cypress.SelectorPlayground.defaults({
selectorPriority: ['class', 'id'],
})
Cypress.ElementSelector.defaults({
selectorPriority: ['class', 'id'],
})
Migrating to Cypress 14.0
Upgrade to Cypress 14 with your AI assistant
Copies a ready-made prompt that walks your AI coding assistant through this migration. Works with any AI tool that can read and edit your project.
Read https://docs.cypress.io/app/references/migration-guide/migrating-to-cypress-14-0.md, then upgrade this project from Cypress 13 to 14 by working through these steps: 1. Confirm the starting point. If the project isn't on Cypress 13.x yet, stop and tell me; majors should be upgraded one at a time. 2. Check requirements. Verify my environment meets the requirements in that section (Node.js 18+, macOS 11+, a supported Linux distribution, and, if I use component testing, that my framework and bundler versions are still supported) and flag anything unsupported. 3. Update Cypress. Detect my package manager and update the cypress dependency. 4. Fix breaking changes. Fix every breaking change in that section that affects my code and config. 5. Verify. Run npx cypress verify and my Cypress tests. Finish with a summary of what changed and anything you couldn't safely automate.
This guide details the code changes needed to migrate to Cypress version 14. See the full changelog for version v14.0.
Node.js 18+ support
Cypress comes bundled with its own
Node.js version.
However, installing the cypress npm package uses the Node.js version installed
on your system.
See Node's release schedule. Node.js version 16 and 21 will no longer be supported when installing Cypress. The minimum Node.js version supported to install Cypress is Node.js 18+.
Unsupported Linux Distributions
Prebuilt binaries for Linux are no longer compatible with Linux distributions based on glibc <2.28.
This support is in line with Node.js's support for Linux in 18+.
If you're using a Linux distribution based on glibc <2.28, for example, Ubuntu 14-18, RHEL 7, CentOS 7, Amazon Linux 2, you'll need to
update your system to a newer version to install Cypress 14+.
Minimum macOS 11 (Big Sur)
Cypress 14.0 upgrades Electron to 33.2.1.
On macOS this requires a minimum version of macOS 11 (Big Sur).
If you're using a lower version of macOS make sure that you update.
Updated Browser Support
Starting in Cypress 14, Cypress will officially support the latest 3 major versions of Chrome, Firefox, and Edge.
Older browser versions may still work with Cypress, but we recommend keeping your browsers up to date to ensure compatibility with Cypress.
Changes to cy.origin()
To account for Chrome's impending deprecation of setting document.domain, and to support sites that use origin-keyed agent clusters,
Cypress no longer injects document.domain into text/html content by default.
Because of this, tests that visit more than one origin (defined as a composite of the URL scheme, hostname, and port) must now use cy.origin().
Without cy.origin(), interacting with a second origin in the same test will cause the test to fail, even if the two origins
are in the same superdomain. This means you must now use cy.origin() in more situations than before.
Failing Test
cy.visit('https://www.cypress.io')
cy.visit('https://docs.cypress.io')
// Cypress will not be able to interact with the page, causing the test to fail
cy.get('[role="banner"]').should('be.visible')
Fixed Test
cy.visit('https://www.cypress.io')
cy.visit('https://docs.cypress.io')
cy.origin('https://docs.cypress.io', () => {
cy.get('[role="banner"]').should('be.visible')
})
To ease this transition, Cypress v14.0 introduced the "injectDocumentDomain" configuration option. When this option
is set to true, cy.origin() will not be required to navigate between origins, as long as the superdomain matches.
If injectDocumentDomain is set to true,
Cypress will warn that this option is deprecated.
injectDocumentDomain will be removed in a
future version of Cypress.
Setting injectDocumentDomain to true
may cause certain sites to stop working in Cypress. Please read the
configuration notes before
use.
If your test suites require
experimentalWebKitSupport, injectDocumentDomain must be set to true.
Chrome may remove support for
document.domain at any time; if this configuration option is enabled, Cypress
may cease to work in Chrome at any time. If this occurs, Chrome will raise an
issue in its developer tools indicating that the deprecated document.domain is
in use. To resolve this issue, set the injectDocumentDomain option to false
and issue any newly necessary cy.origin() commands.
Additionally, the experimentalSkipDomainInjection configuration option has been removed. Its behavior (not injecting document.domain) is now the default. Remove it from your configuration. If you still need the legacy document.domain injection behavior, set injectDocumentDomain to true instead, keeping in mind the deprecation warnings above.
Deprecation of resourceType on cy.intercept
The resourceType option on cy.intercept has been deprecated in Cypress 14.0.0. We anticipate the types of the resourceType to change in the future or be completely removed
from the API.
Our intention is to replace essential functionality dependent on the resourceType within Cypress in a future version (like hiding network logs that are not fetch/xhr). If you're using resourceType in your tests, please leave feedback on which resourceType values are important to you in this GitHub issue.
experimentalFetchPolyfill removed
The experimentalFetchPolyfill configuration option, deprecated since Cypress 6.0.0, has been removed. Remove it from your configuration and use cy.intercept() to handle fetch requests.
before:browser:launch browser arguments
The second argument of the before:browser:launch node event is no longer yielded as an array of browser arguments. This behavior had been deprecated since Cypress 4.0.0. Update any handler that treats the second argument as an array to use the launchOptions object (for example, launchOptions.args) instead.
open-ct and run-ct commands removed
The cypress open-ct and cypress run-ct CLI commands, deprecated since Cypress 10.0, have been removed. Update any npm scripts or CI jobs to use cypress open --component or cypress run --component respectively.
Undocumented Cypress.backend methods removed
The undocumented Cypress.backend('firefox:force:gc') and Cypress.backend('log:memory:pressure') methods have been removed. There is no replacement; remove any calls to them.
Fetch from about:blank in Electron no longer supported
It is no longer possible to make a fetch or XMLHttpRequest request from the about:blank page in Electron (for example, cy.window().then((win) => win.fetch('<some-url>')) before any navigation). Use cy.request() instead, or perform an initial navigation with cy.visit() first.
CT Just in Time Compile changes
In Cypress 13.14.0, we released an experimental flag, experimentalJustInTimeCompile,
to enable Just in Time (JIT) compilation for Component Testing with vite and webpack. The response from this change was positive and we've made a few changes in response:
- JIT compilation is the default behavior for component tests as a
justInTimeCompilecomponent configuration option. - JIT compilation no longer applies with
vite, since there is no benefit to enabling this withvite.
This option will only compile resources directly related to your spec, compiling them 'just-in-time' before spec execution. This should result in improved memory management and performance for component tests in cypress open and cypress run modes, especially for large test suites.
Disable JIT Compilation
If you would like to disable JIT compilation, you can do so by setting justInTimeCompile to false in your component configuration.
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
component: {
justInTimeCompile: false,
},
})
import { defineConfig } from 'cypress'
export default defineConfig({
component: {
justInTimeCompile: false,
},
})
For users with the existing experimentalJustInTimeCompile flag set, you can remove this flag from your configuration.
webpack-dev-server v3 no longer supported
@cypress/webpack-dev-server no longer supports webpack-dev-server version 3, and now ships with webpack-dev-server version 5 by default. If you are still using webpack version 4, install webpack-dev-server version 4 alongside Cypress.
React <18 CT no longer supported
With LTS ending for React 16 and 17 several years ago, the minimum required React version for component testing is now 18.0.0.
Now that the minimum version of React supported for Component Testing is 18.0.0, Cypress is able to merge the cypress/react18 test harness into the main cypress/react test harness. Because of this, the @cypress/react18 harness is deprecated and no longer shipped with the binary. Support has been moved to cypress/react.
To migrate, change the test harness from cypress/react18 to cypress/react.
import { mount } from 'cypress/react18'
import { mount } from 'cypress/react'
To continue using React below v18
If you haven't been able to migrate away from an older React version and still need that test harness, it can be installed independently via the @cypress/react 8.x.x package from npm.
Note that this test harness version is deprecated and no longer supported by Cypress. This version is intended to serve as a temporary workaround to migrate your project to React v18+.
- npm
- Yarn
- pnpm
- Bun
npm install @cypress/react@8 --save-dev
yarn add @cypress/react@8 --dev
pnpm add --save-dev @cypress/react@8
bun add --dev @cypress/react@8
Inside your support file (ex: ./cypress/support/component.(js|ts)), or wherever your mount function is imported, make the following update to add @.
import { mount } from 'cypress/react'
import { mount } from '@cypress/react'
Next.js <15 CT no longer supported
Component testing no longer supports Next.js versions 10, 11, 12, and 13. The minimum supported Next.js version is now 15.0.4. Versions 15.0.0 - 15.0.3 depend on the React 19 Release Candidate and are not officially supported by Cypress, but should still work.
Angular <17.2.0 CT no longer supported
With LTS ending for Angular 16, the minimum required Angular version for component testing is now 17.2.0 in order to support signals as a first class citizen.
Now that the minimum version of Angular supported for Component Testing is 17.2.0, Cypress is able to merge the cypress/angular-signals test harness into the main cypress/angular test harness. Because of this, the @cypress/angular-signals harness is deprecated and no longer shipped with the binary. Support has been moved to cypress/angular.
To migrate, just change the test harness from cypress/angular-signals to cypress/angular.
import { mount } from 'cypress/angular-signals'
import { mount } from 'cypress/angular'
To continue using Angular below v17.2.0
If you haven't been able to migrate away from an older Angular version and still need that test harness, it can be installed independently via the @cypress/angular 2.x.x package from npm.
Note that this test harness version is deprecated and no longer supported by Cypress. This version is intended to serve as a temporary workaround to migrate your project to Angular v17.2.0+.
- npm
- Yarn
- pnpm
- Bun
npm install @cypress/angular@2 --save-dev
yarn add @cypress/angular@2 --dev
pnpm add --save-dev @cypress/angular@2
bun add --dev @cypress/angular@2
Inside your support file (ex: ./cypress/support/component.(js|ts)), or wherever your mount function is imported, make the following update to add @.
import { mount } from 'cypress/angular'
import { mount } from '@cypress/angular'
Vue 2 CT no longer supported
Vue 2 reached end-of-life on December 31st, 2023. With Cypress 14, Cypress no longer ships the Vue 2 component testing harness with the Cypress binary.
To continue using Vue 2
If you haven't been able to migrate away from Vue 2 and still need that test harness, it can be installed independently via the @cypress/vue2 package.
Note that this test harness is deprecated and no longer supported by Cypress. This package is intended to serve as a temporary workaround to migrate your project to Vue 3. The Cypress launchpad will warn against Component testing mismatched dependencies, but this will not stop you from running your component tests.
- npm
- Yarn
- pnpm
- Bun
npm install @cypress/vue2 --save-dev
yarn add @cypress/vue2 --dev
pnpm add --save-dev @cypress/vue2
bun add --dev @cypress/vue2
Inside your support file (ex: ./cypress/support/component.(js|ts)), or wherever your mount function is imported, make the following update to add @.
import { mount } from 'cypress/vue2'
import { mount } from '@cypress/vue2'
Nuxt 2 CT no longer supported
Nuxt 2 reached end-of-life on June 30th, 2024 and is built on Vue 2, which is also end-of-life. Component testing a Nuxt 2 project relied on the Vue 2 harness. With Cypress 14 no longer shipping that harness with the binary, Nuxt 2 apps can no longer be component tested out of the box.
For component testing a Nuxt 3+ app (which is Vue 3 + Vite), see Using Cypress with Nuxt.
To continue using Nuxt 2
If you haven't been able to migrate away from Nuxt 2, the underlying Vue 2 harness can still be installed independently via the @cypress/vue2 package, as described in To continue using Vue 2. Note that this package is deprecated and no longer supported by Cypress, and is intended only as a temporary workaround while you migrate to Nuxt 3+.
Create React App CT no longer supported
create-react-app is no longer actively maintained or supported (see CRA issue #13393). Your component tests will now need a bundler to run. If still using create-react-app, you'll either need to:
- Eject the configuration to bundle with webpack.
- Leverage vite to bundle your component tests (quick setup with create-vite).
After selecting a bundler, change the framework option in your Cypress config from create-react-app to react. If ejecting the create-react-app, change your cypress config to look something like this:
process.env.NODE_ENV = 'development'
const { defineConfig } = require('cypress')
const webpackConfig = require('./config/webpack.config.js')
module.exports = defineConfig({
component: {
devServer: {
framework: 'react',
bundler: 'webpack',
webpackConfig: webpackConfig('development'),
},
},
})
@vue/cli-service CT no longer supported
@vue/cli-service is in maintenance mode and is no longer maintained by the Vue core team. Your component tests will now need a bundler to run. If still using Vue CLI, you will either need to:
- Migrate to webpack (see example).
- Leverage vite. The Vue team recommends migrating to using
create-vueto scaffold a Vite-based project.
After selecting a bundler, change the framework option in your Cypress config from "vue-cli" to "vue". Your Cypress configuration should change as outlined below.
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
component: {
devServer: {
framework: 'vue-cli',
bundler: 'webpack',
},
},
})
import { defineConfig } from 'cypress'
export default defineConfig({
component: {
devServer: {
framework: 'vue-cli',
bundler: 'webpack',
},
},
})
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
component: {
devServer: {
framework: 'vue',
bundler: 'vite', // or 'webpack'
},
},
})
import { defineConfig } from 'cypress'
export default defineConfig({
component: {
devServer: {
framework: 'vue',
bundler: 'vite', // or 'webpack'
},
},
})
Svelte 3 & 4 CT no longer supported
With Cypress 14, Cypress no longer ships the Svelte 3 and 4 component testing harness with the Cypress binary.
However, if you have not been able to upgrade Svelte and still need the Cypress Svelte 3 and 4 test harness, it can be installed independently via version 2.x.x of the @cypress/svelte package.
- npm
- Yarn
- pnpm
- Bun
npm install @cypress/svelte@2 --save-dev
yarn add @cypress/svelte@2 --dev
pnpm add --save-dev @cypress/svelte@2
bun add --dev @cypress/svelte@2
Note that this version of the test harness is deprecated and no longer actively supported by Cypress and is intended to serve as a temporary work around until you are able to migrate your project to Svelte 5+. The Cypress launchpad will also warn against Component testing mismatched dependencies, but this will not stop you from running your component tests.
To update, inside your support file (ex: ./cypress/support/component.(js|ts)) or wherever your mount function is imported, change
import { mount } from 'cypress/svelte'
to
import { mount } from '@cypress/svelte'
Your code should now look like this:
import MySvelteComponent from './MySvelteComponent'
import { mount } from '@cypress/svelte'
it('renders', () => {
cy.mount(MySvelteComponent)
})
Migrating to Cypress 13.0
Upgrade to Cypress 13 with your AI assistant
Copies a ready-made prompt that walks your AI coding assistant through this migration. Works with any AI tool that can read and edit your project.
Read https://docs.cypress.io/app/references/migration-guide/migrating-to-cypress-13-0.md, then upgrade this project from Cypress 12 to 13 by working through these steps: 1. Confirm the starting point. If the project isn't on Cypress 12.x yet, stop and tell me; majors should be upgraded one at a time. 2. Check requirements. Verify my Node.js version is 16+ (18+ recommended) and, if I use TypeScript, that it's 4.x or newer, and flag anything unsupported. 3. Update Cypress. Detect my package manager and update the cypress dependency. 4. Fix breaking changes. Fix every breaking change in that section that affects my code and config, calling out behavior changes like video recording being off by default. 5. Verify. Run npx cypress verify and my Cypress tests. Finish with a summary of what changed and anything you couldn't safely automate.
This guide details the changes and how to change your code to migrate to Cypress version 13. See the full changelog for version v13.0.
Node.js 16+ support
Node.js 14 support has been removed and Node.js 16 support has been deprecated. Node.js 16 may continue to work with Cypress v13, but is not supported moving forward, to closer coincide with Node.js 16's end-of-life schedule. It is recommended that users update to at least Node.js 18.
Additionally, the minimum supported TypeScript version is 4.x.
Cypress Cloud Test Replay
Test Replay is enabled by default in v13 of the Cypress App.
You may need to allowlist capture.cypress.io if you work with a strict VPN. See our FAQ section about VPN subdomain allowlisting.
With Test Replay enabled, the Cypress Runner UI is hidden by default when recording a run to the Cloud. This has two side effects: recorded video will not show the Runner, and a screenshot taken with capture: 'runner' is captured as if capture: 'viewport' were passed. If the Runner UI is needed during the run, you can enable it by passing --runner-ui to the cypress run command.
You can opt-out of this feature in Cloud project-level settings.
Video updates
video is set to false by default
You can continue recording video by setting video to true either in your Cypress configuration or via overriding options. This can be useful if you want video locally or want video for some other reason, like in non-Chromium browsers where Test Replay is not available.
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
video: true,
})
import { defineConfig } from 'cypress'
export default defineConfig({
video: true,
})
videoUploadOnPasses configuration option has been removed
Most users used videoUploadOnPasses as a way to skip the time to compress and upload videos to the Cloud. Since we're turning off videoCompression by default, this configuration option does not offer the time saving value that it once would.
If you want to prevent a passing test from uploading to the Cloud, we recommend deleting the video using our guide with code examples to discard captured video of passing tests.
videoCompression is set to false by default
Cypress has the capability to compress recorded videos after a run to reduce the video file size. By default, compression is now turned off. This results in a reduced run time by removing the time to compress the video, a larger video file size and better video quality.
You can enable this with the videoCompression configuration option if you'd like to reduce the video file size for any reason. This will also reduce the video quality and take slightly longer to process and complete the run.
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
// value can be true/false -or- an integer between 0 and 51
videoCompression: true,
})
import { defineConfig } from 'cypress'
export default defineConfig({
// value can be true/false -or- an integer between 0 and 51
videoCompression: true,
})
nodeVersion configuration option removed
The deprecated nodeVersion configuration option has been removed. Remove it from your configuration; Cypress now always uses the Node.js version it was launched with to execute your setupNodeEvents.
cy.readFile() is now a query command
In Cypress v13, the .readFile() command is now a query.
Tests written using it should continue to operate exactly as before; no changes
are necessary.
readFile() will re-read the file from disk if any upcoming command in the same
chain fails. Assertions no longer have to be directly attached.
cy.readFile(`users.json`).its('users.123.fullName').should('eq', 'John Doe')
Beginning with Cypress v13, the above test will re-read the file until the file
exists, it has the requested property, and it passes the assertion.
In previous versions of Cypress, the above command would retry until the file existed, but would not re-read it from disk if the file didn't have the requested property or the contents didn't match.
.readFile() can no longer be overwritten with Cypress.Commands.overwrite()
Queries must be overwritten using Cypress.Commands.overwriteQuery(). If you
were previously overwriting cy.readFile(), you will need to update your code
to use Cypress.Commands.overwriteQuery('readFile', function() { ... }) rather
than Cypress.Commands.overwrite('readFile', () => { ... }). For more details
on overwriting queries, see the
Overwriting Existing Queries.
Module API, after:run, and after:spec changes
The properties and values returned by the Module API and included in the arguments of handlers for the after:run and after:spec events have been changed to be more consistent. If you use the Module API (the return value of cypress.run()) or read the result arguments in after:run or after:spec handlers within setupNodeEvents, review that code against the linked API documentation and update any property access to the new result shape.
Migrating to Cypress 12.0
Upgrade to Cypress 12 with your AI assistant
Copies a ready-made prompt that walks your AI coding assistant through this migration. Works with any AI tool that can read and edit your project.
Read https://docs.cypress.io/app/references/migration-guide/migrating-to-cypress-12-0.md, then upgrade this project from Cypress 11 to 12 by working through these steps: 1. Confirm the starting point. If the project isn't on Cypress 11.x yet, stop and tell me; majors should be upgraded one at a time. 2. Check requirements. Verify my Node.js version is 14, 16, or 18+ and flag anything unsupported. 3. Update Cypress. Detect my package manager and update the cypress dependency. 4. Fix breaking changes. Fix every breaking change in that section that affects my code and config, paying special attention to test isolation being enabled by default: audit my tests for state shared between tests and fix them or configure testIsolation as that section describes. 5. Verify. Run npx cypress verify and my Cypress tests. Finish with a summary of what changed and anything you couldn't safely automate.
This guide details the changes and how to change your code to migrate to Cypress version 12.0. See the full changelog for version 12.0.
The Session and Origin experiment has been released as General Availability
(GA), meaning that we have deemed this experiment to be feature complete and
free of issues in the majority of use cases. With releasing this as GA, the
experimentalSessionAndOrigin flag has been removed, the
cy.origin() and
cy.session() commands are generally available and
Test Isolation
is enabled by default.
One exception: using require and import with a callback supplied to
cy.origin() is not enabled by default. To leverage
external dependencies in cy.origin() callbacks, set the
e2e.experimentalOriginDependencies
configuration option to true.
Node.js 14+ support
Cypress comes bundled with its own
Node.js version.
However, installing the cypress npm package uses the Node.js version installed
on your system.
Node.js 12, 15, and 17 have reached their end of life and are no longer supported when installing Cypress. See Node's release schedule. Installing Cypress now requires Node.js 14, 16, or 18+.
Test Isolation
The
testIsolation
config option is enabled by default. Additionally, the testIsolation
configuration values have changed from on or off to true or false. If
you configured this option while it was experimental, update its value. Test
isolation means Cypress resets the browser context before each test by:
- clearing the dom state by visiting
about:blank - clearing cookies in all domains
- clearing
localStoragein all domains - clearing
sessionStoragein all domains
Test suites that relied on the application to persist between tests may have to be updated to revisit their application and rebuild the browser state for each test that needs it.
Before this change, it was possible to write tests such that you could rely on the application (i.e. DOM state) to persist between tests. For example you could log in to a CMS in the first test, change some content in the second test, verify the new version is displayed on a different URL in the third, and log out in the fourth.
Here's a simplified example of such a test strategy.
Before Multiple small tests against different origins
it('logs in', () => {
cy.visit('https://example.cypress.io')
cy.get('input#password').type('Password123!')
cy.get('button#submit').click()
})
it('updates the content', () => {
// already on page redirect from clicking button#submit
cy.get('#current-user').contains('logged in')
cy.get('button#edit-1').click()
cy.get('input#title').type('Updated title')
cy.get('button#submit').click()
cy.get('.toast').contains('Changes saved!')
})
it('validates the change', () => {
cy.visit('/items/1')
cy.get('h1').contains('Updated title')
})
After migrating, when testIsolation=true by default, this flow would need to
be contained within a single test. While the above practice has always been
discouraged
we know some users have historically written tests this way, often to get around
the same-origin restrictions. But with cy.origin()
you no longer need these kind of brittle hacks, as your multi-origin logic can
all reside in a single test, like the following.
After One big test using cy.origin()
it('securely edits content', () => {
cy.origin('cypress.io', () => {
cy.visit('https://example.cypress.io')
cy.get('input#password').type('Password123!')
cy.get('button#submit').click()
})
cy.origin('cypress-dx.com', () => {
cy.url().should('contain', 'cms')
cy.get('#current-user').contains('logged in')
cy.get('button#edit-1').click()
cy.get('input#title').type('Updated title')
cy.get('button#submit').click()
cy.get('.toast').contains('Changes saved!')
})
cy.visit('/items/1')
cy.get('h1').contains('Updated title')
})
The just-released cy.session() command can be used to setup and cache cookies,
local storage and session storage between tests to easily re-establish the
previous (or common) browser contexts needed in a suite. This command will run
setup on its initial execution and will restore the saved browser state on each
sequential command execution. This command reduces the need for repeated
application logins, while users also benefit from the test isolation guardrails
to write independent, reliable and deterministic tests from the start.
If for whatever reason you still need to persist the dom and browser context
between tests, you can disable test isolation by setting testIsolation=false
on the root configuration or at the suite-level. For example:
describe('workflow', { testIsolation: false }, () => {
...
})
It is important to note that while disabling test isolation may improve the overall performance of end-to-end tests, it can cause state to "leak" between tests. This can make later tests dependent on the results of earlier tests, and potentially cause misleading test failures. It is important to be extremely mindful of how tests are written when using this mode, and ensure that tests continue to run independently of one another.
For example the following tests are not independent nor deterministic:
describe('workflow', { testIsolation: false }, () => {
it('logs in', () => {
cy.visit('https://example.cypress.io/log-in')
cy.get('username').type('User1')
cy.task('getSecret', 'USER1_PASSWORD').then((password) => {
cy.get('password').type(password)
})
cy.get('button#login').click()
cy.contains('User1')
})
it('clicks user profile', () => {
cy.get('User1').find('#profile_avatar').click()
cy.contains('Email Preferences')
})
it('updates profile', () => {
cy.get('button#edit')
cy.get('button#save').click()
})
})
In the above example, each test is relying on the previous test to be successful to correctly execute. If at any point, the first or second test fails, the sequential test(s) will automatically fail and provide unreliable debugging errors since the errors are representative of the previous test.
The best way to ensure your tests are independent is to add a .only() to your
test and verify it can run successfully without the test before it.
Simulating Pre-Test Isolation Behavior
Test isolation did not truly exist pre-12. Pre-Cypress 12, the behavior was a
hybrid of both testIsolation enabled and disabled. All local storage and
cookies on the current domain were cleared, but Cypress did not clear session
storage and the page always persisted.
In Cypress 12+ when testIsolation is enabled, local storage, session storage
and cookies in all domains are cleared and the page is cleared. When
testIsolation is disabled, nothing is cleared before the next test so all
local storage, session storage and cookies & the page persists.
If you wanted to match pre-Cypress 12 behavior, you need to disable
testIsolation, then run cy.clearLocalStorage() and cy.clearCookies() in a
beforeEach hook to clear the local storage and cookies in the current domain.
describe('match pre-12 behavior', { testIsolation: false }, () => {
beforeEach(() => {
cy.clearLocalStorage()
cy.clearCookies()
// other beforeEach logic to restore the expected local storage or cookies needed on the client.
})
})
Many of the issues test isolation solved were around cookie management with tests trying to save and persist cookies because the page was still available, but the cookies on the domain were unexpectedly cleared which broke interactions with the application. It wasn’t obvious Cypress was doing a partial browser clean up. Explicitly setting test isolation to enabled or disabled allows you to choose what is right for your tests.
Behavior Changes in Alias Resolution
Cypress always re-queries aliases when they are referenced. This can result in certain tests that used to pass could start to fail. For example,
cy.findByTestId('popover')
.findByRole('button', { expanded: true })
.as('button')
.click()
cy.get('@button').should('have.attr', 'aria-expanded', 'false')
previously passed, because the initial button was collapsed when first queried, and then later expanded. However, in Cypress 12, this test fails because the alias is always re-queried from the DOM, effectively resulting in the following execution:
cy.findByTestId('popover').findByRole('button', { expanded: true }).click()
cy.findByTestId('popover')
.findByRole('button', { expanded: true }) // A button which matches here (is expanded)...
.should('have.attr', 'aria-expanded', 'false') // ...will never pass this assertion.
You can rewrite tests like this to be more specific; in our case, we changed the alias to be the first button rather than the unexpanded button.
cy.findByTestId('popover').findAllByRole('button').first().as('button')
If you want to alias a static value, such that it is never re-queried, you will
need Cypress 12.3.0 or later, which
introduced the type option for .as() to opt into the old
behavior.
cy.get('.username').invoke('val').as('username', { type: 'static' })
See .as() for more details.
Command / Cypress API Changes
Cypress.Cookies.defaults and Cypress.Cookies.preserveOnce
The Cypress.Cookies.defaults and Cypress.Cookies.preserveOnce APIs been
removed. Use the cy.session() command to preserve
cookies (and local and session storage) between tests.
Cookie commands now use hostname instead of superdomain
The cookie commands now use the hostname as the domain by default instead of
the superdomain. This change aligns Cypress' cookie rules with the browser
cookie rules. This may affect what cookies are returned by
cy.getCookie(), what cookies are set with
cy.setCookie(), and the cookies cleared with
cy.clearCookies(). If your tests depend on
cookies being shared across subdomains, pass an explicit domain option to
these commands.
If you were using Cypress.Cookies.preserveOnce to preserve a specific cookie
within a single spec, this might look like the following:
describe('Dashboard', () => {
beforeEach(() => {
- cy.login()
- Cypress.Cookies.preserveOnce('session_id', 'remember_token')
+ cy.session('unique_identifier', cy.login, {
+ validate () {
+ cy.getCookies().should('have.length', 2)
+ },
+ })
})
If you were using Cypress.Cookies.defaults to preserve a cookie or set of
cookies across test, this might look like the following:
describe('Dashboard', () => {
beforeEach(() => {
- cy.login()
- Cypress.Cookies.defaults({
- preserve: ['session_id', 'remember_token']
- })
+ cy.session('unique_identifier', cy.login, {
+ validate () {
+ cy.getCookies().should('have.length', 2)
+ },
+ cacheAcrossSpecs: true
+ })
})
cy.server(), cy.route() and Cypress.Server.defaults
The cy.server() and cy.route() commands and the Cypress.server.defaults
API has been removed. Use the cy.intercept()
command instead.
it('can encode + decode headers', () => {
- Cypress.Server.defaults({
- delay: 500,
- method: 'GET',
- })
- cy.server()
- cy.route(/api/, () => {
- return {
- 'test': 'We’ll',
- }
- }).as('getApi')
+ cy.intercept('GET', /api/, (req) => {
+ req.on('response', (res) => {
+ res.setDelay(500)
+ })
+ req.body.'test': 'We’ll'
+ }).as('getApi')
cy.visit('/index.html')
cy.window().then((win) => {
const xhr = new win.XMLHttpRequest
xhr.open('GET', '/api/v1/foo/bar?a=42')
xhr.send()
})
cy.wait('@getApi')
- .its('url').should('include', 'api/v1')
+ .its('request.url').should('include', 'api/v1')
})
.invoke()
The .invoke() command now throws an error if the
function returns a promise. If you wish to call a method that returns a promise
and wait for it to resolve, use .then() instead of
.invoke().
cy.wrap(myAPI)
- .invoke('makeARequest', 'http://example.com')
+ .then(api => api.makeARequest('http://example.com'))
.then(res => { ...handle response... })
If .invoke() is followed by additional commands or assertions, it will call
the named function multiple times. This has the benefit that the chained
assertions can more reliably use the function's return value.
If this behavior is undesirable because you expect the function to be invoked only once, break the command chain and move the chained commands and/or assertions to their own chain. For example, rewrite
- cy.get('input').invoke('val', 'text').type('newText')
+ cy.get('input').invoke('val', 'text')
+ cy.get('input').type('newText')
.should()
The .should() assertion now throws an error if Cypress
commands are invoked from inside a .should() callback. This previously
resulted in unusual and undefined behavior. If you wish to execute a series of
commands on the yielded value, use.then() instead.
cy.get('button')
- .should(($button) => {
})
+ .then(api => api.makeARequest('http://example.com'))
.then(res => { ...handle response... })
.within()
The .within() command now throws an error if it is
passed multiple elements as the subject. This previously resulted in
inconsistent behavior, where some commands would use all passed in elements,
some would use only the first and ignore the rest, and
.screenshot() would throw an error if used inside
a .within() block with multiple elements.
If you were relying on the old behavior, you have several options depending on the desired result.
The simplest option is to reduce the subject to a single element.
cy.get('tr')
+ .first() // Limit the subject to a single element before calling .within()
.within(() => {
cy.contains('Edit').click()
})
If you have multiple subjects and wish to run commands over the collection as a
whole, you can alias the subject rather than use .within().
cy.get('tr')
- .within(() => {
- cy.get('td').should('have.class', 'foo')
- cy.get('td').should('have.class', 'bar')
- })
+ .as('rows') // Store multiple elements as an alias
+cy.get('@rows').find('td').should('have.class', 'foo')
+cy.get('@rows').find('td').should('have.class', 'bar')
Or if you have a collection and want to run commands over every element, use
.each() in conjunction with .within().
cy.get('tr')
- .within(() => {
- cy.contains('Edit').should('have.attr', 'disabled')
- })
+ .each($tr => {
+ cy.wrap($tr).within(() => {
+ cy.contains('Edit').should('have.attr', 'disabled')
+ })
+ })
cy.request() query string handling
The cy.request() command now uses
querystringify to stringify
and parse the qs option. This change aligns with how the
cy.visit() command generates urls with query
parameters. If you pass complex values (like arrays or nested objects) in the
qs option, verify the generated query strings still match what your server
expects.
Cypress.Commands.overwrite()
In Cypress 12.0.0, we introduced a new command type, called queries. A query is a small and fast command for getting data from the window or DOM. This distinction is important because Cypress can retry chains of queries, keeping the yielded subject up-to-date as a page rerenders.
With the introduction of query commands, the following commands have been
re-categorized and can no longer be overwritten with
Cypress.Commands.overwrite():
.as().children().closest().contains()cy.debug()cy.document().eq().filter().find().first().focused().get().hash().its().last()cy.location().next().nextAll().not().parent().parents().parentsUntil().prev().prevUntil()cy.root().shadow().siblings()cy.title()cy.url()cy.window()
If you were previously overwriting one of the above commands, try adding your
version as a new command using
Cypress.Commands.add() under a different
name.
Migrating to Cypress 11.0
Upgrade to Cypress 11 with your AI assistant
Copies a ready-made prompt that walks your AI coding assistant through this migration. Works with any AI tool that can read and edit your project.
Read https://docs.cypress.io/app/references/migration-guide/migrating-to-cypress-11-0.md, then upgrade this project from Cypress 10 to 11 by working through these steps: 1. Confirm the starting point. If the project isn't on Cypress 10.x yet, stop and tell me; majors should be upgraded one at a time. 2. Update Cypress. Detect my package manager and update the cypress dependency. 3. Fix breaking changes. Fix every breaking change in that section that affects my code and config. If I use component testing, update my devServer configuration and any removed mounting APIs (mountHook, unmount, mountCallback) as that section describes. 4. Verify. Run npx cypress verify and my Cypress tests. Finish with a summary of what changed and anything you couldn't safely automate.
This guide details the changes and how to change your code to migrate to Cypress version 11.0. See the full changelog for version 11.0.
Component Testing Updates
As of Cypress 11, Component Testing is now generally available. There are some minor breaking changes. Most projects should be able to migrate without any code modifications.
Repeated mount calls
Subsequent cy.mount() calls within the same test now remove the previously
mounted component from the DOM. If a test relies on multiple mounted components
existing at the same time, combine them into a single mounted component.
Vue mount return value
mount from cypress/vue now returns an object with both the VueWrapper
(wrapper) and the component instance (component), instead of the
VueWrapper itself. Update any code that consumes the yielded value
accordingly.
Changes to Mounting Options
Each major library we support has a mount function with two arguments:
- The component
- Mounting Options
Mounting options previously had several properties that are now removed:
- cssFile, cssFiles
- style, styles
- stylesheet, stylesheets
Read more about the rationale
here.
We recommend writing test-specific styles in a separate css file you import in
your test, or in your supportFile.
Before (Cypress 10)
import { mount } from 'cypress/react'
import { Card } from './Card'
it('renders some content', () => {
cy.mount(<Card title="title" />, {
styles: `
.card { width: 100px; }
`,
stylesheets: [
],
})
})
After (Cypress 11)
/** style.css */
.card { width: 100px }
/** Card.cy.jsx */
import { mount } from 'cypress/react'
import { Card } from './Card'
import './styles.css' // contains CDN link and custom styling.
it('renders some content', () => {
cy.mount(<Card title="title" />)
})
React - mountHook Removed
mountHook from cypress/react has been removed. Read more about the rationale
here.
We recommend simply replacing it with mount and a component.
Consider the following useCounter hook:
import { useState, useCallback } from 'react'
function useCounter() {
const [count, setCount] = useState(0)
const increment = useCallback(() => setCount((x) => x + 1), [])
return { count, increment }
}
Before - Cypress 10 and mountHook
import { mountHook } from 'cypress/react'
import { useCounter } from './useCounter'
it('increments the count', () => {
mountHook(() => useCounter()).then((result) => {
expect(result.current.count).to.equal(0)
result.current.increment()
expect(result.current.count).to.equal(1)
result.current.increment()
expect(result.current.count).to.equal(2)
})
})
After - Cypress 11 and mount
import { useCounter } from './useCounter'
it('increments the count', () => {
function Counter() {
const { count, increment } = useCounter()
return (
<>
<h1 name="count">Count is {{ count }}</h1>
<button onClick={increment}>Increment</button>
</>
)
}
cy.mount(<Counter />).then(() => {
cy.get('[name="count"]')
.should('contain', 0)
.get('button')
.click()
.get('[name="count"]')
.should('contain', 1)
})
})
React - unmount Removed
unmount from cypress/react has been removed. Read more about the rationale
here.
We recommend using the API React provides for unmounting components,
unmountComponentAtNode.
Before - Cypress 10 and unmount
import { unmount } from 'cypress/react'
it('calls the prop', () => {
cy.mount(<Comp onUnmount={cy.stub().as('onUnmount')} />)
cy.contains('My component')
unmount()
// the component is gone from the DOM
cy.contains('My component').should('not.exist')
cy.get('@onUnmount').should('have.been.calledOnce')
})
After - Cypress 11 and unmountComponentAtNode
import { getContainerEl } from 'cypress/react'
import ReactDom from 'react-dom'
it('calls the prop', () => {
cy.mount(<Comp onUnmount={cy.stub().as('onUnmount')} />)
cy.contains('My component')
cy.then(() => ReactDom.unmountComponentAtNode(getContainerEl()))
// the component is gone from the DOM
cy.contains('My component').should('not.exist')
cy.get('@onUnmount').should('have.been.calledOnce')
})
Vue - mountCallback Removed
mountCallback from cypress/vue has been removed. Read more about the
rationale
here.
We recommend using mount.
Before - Cypress 10 and mountCallback
import { mountCallback } from 'cypress/vue'
beforeEach(mountCallback(MessageList))
it('shows no messages', () => {
getItems().should('not.exist')
})
After - Cypress 11 and mount
beforeEach(() => cy.mount(MessageList))
it('shows no messages', () => {
getItems().should('not.exist')
})
Angular - Providers Mounting Options Change
There is one breaking change for Angular users in regards to providers. In
Cypress 10, we took any providers passed as part of the Mounting Options and
overrode the component providers via the TestBed.overrideComponent API.
In Cypress 11, providers passed as part of the Mounting Options will be assigned
at the module level using the TestBed.configureTestingModule API.
This means that module-level providers (resolved from imports or
@Injectable({ providedIn: 'root' }) can be overridden, but providers specified
in @Component({ providers: [...] }) will not be overridden when using
cy.mount(MyComponent, { providers: [...] }).
To override component-level providers, use the TestBed.overrideComponent API.
See a concrete example here.
Vite Dev Server (cypress/vite-dev-server)
When providing an inline viteConfig inside of cypress.config, any
vite.config.js file is not automatically merged.
Before - Cypress 10 and viteConfig
import { defineConfig } from 'cypress'
export default defineConfig({
component: {
devServer: {
framework: 'react',
bundler: 'vite',
viteConfig: {
// ... custom vite config ...
// result merged with `vite.config` file if present
},
},
},
})
After - Cypress 11 and viteConfig
import { defineConfig } from 'cypress'
import viteConfig from './vite.config'
export default defineConfig({
component: {
devServer: {
framework: 'react',
bundler: 'vite',
viteConfig: {
...viteConfig,
// ... other overrides ...
},
},
},
})
Vite 3+ users could make use of the
mergeConfig API.
cy.session() requires a setup function
cy.session() now requires a setup function to be
provided when a session is created. If you were calling cy.session() without
a setup argument, add one that recreates the session's browser state (for
example, logging in).
Cypress Cloud communication verifies certificate authorities
Communication with Cypress Cloud now verifies certificate authorities (CAs) and
rejects any unauthorized calls. If your network intercepts this traffic with a
self-signed CA, set the npm_config_ca, npm_config_cafile, or
NODE_EXTRA_CA_CERTS environment variable so recorded runs can reach Cypress
Cloud.
Migrating to Cypress 10.0
Upgrade to Cypress 10 with your AI assistant
Copies a ready-made prompt that walks your AI coding assistant through this migration. Works with any AI tool that can read and edit your project.
Read https://docs.cypress.io/app/references/migration-guide/migrating-to-cypress-10-0.md, then upgrade this project from Cypress 9 to 10 by working through these steps: 1. Confirm the starting point. If the project isn't on Cypress 9.x yet, stop and tell me; majors should be upgraded one at a time. 2. Update Cypress. Detect my package manager and update the cypress dependency. 3. Apply the configuration migration. Apply the configuration migration that section describes: convert cypress.json to a cypress.config file, move the plugins file into setupNodeEvents, rename support files and spec file locations to the new defaults, and update any removed config options or APIs my code uses. 4. Verify. Run npx cypress verify and my Cypress tests. Finish with a summary of what changed and anything you couldn't safely automate.
This guide details the changes and how to change your code to migrate to Cypress version 10.0. See the full changelog for version 10.0.
Cypress Changes
- The "Run all specs" and "Run filtered specs" functionality have been removed.
- The experimental "Cypress Studio" has been removed and will be rethought/revisited in a later release.
- Unsupported browser versions can no longer be run via
cypress runorcypress open. Instead, an error will display. - In 9.x and earlier versions,
cypress openwould bring you directly to the project specs list. In 10.0.0, you must pass--browserand--e2eor--componentas well to launch Cypress directly to the specs list.
Configuration File Changes
Cypress now supports JavaScript and TypeScript configuration files. By default,
Cypress will automatically load a cypress.config.js or cypress.config.ts
file in the project root if one exists. The
Configuration guide has been updated to
reflect these changes, and explains them in greater detail.
Because of this, support for cypress.json has been removed since Cypress v10.
Related notes:
- If no config file exists when you open Cypress, the automatic set up process will begin and either a JavaScript or TypeScript config file will be created depending on what your project uses.
- You may use the
--config-filecommand line flag or theconfigFilemodule API option to specify a.jsor.tsfile. JSON config files are no longer supported. - Cypress now requires a config file, so specifying
--config-file falseon the command line or aconfigFilevalue offalsein the module API is no longer valid. - You can't have both
cypress.config.jsandcypress.config.tsfiles. This will result in an error when Cypress loads. - A
defineConfig()helper function is now exported by Cypress, which provides automatic code completion for configuration in many popular code editors. For TypeScript users, thedefineConfigfunction will ensure the configuration object passed into it satisfies the type definition of the configuration file. - Many pages and examples throughout the documentation have been updated to show
configuration using
cypress.config.jsandcypress.config.tsvs the oldercypress.json. For example:
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:1234',
},
})
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
baseUrl: 'http://localhost:1234',
},
})
Plugins File Removed
Because Cypress now supports JavaScript and TypeScript configuration files, a
separate "plugins file" (which used to default to cypress/plugins/index.js) is
no longer needed.
Support for the plugins file has been removed, and it has been replaced with the
new setupNodeEvents() and
devServer config options.
Related notes:
- The
cypress/plugins/index.jsplugins file is no longer automatically loaded by Cypress. - The
setupNodeEvents()config option is functionally equivalent to the function exported from the plugins file; it takes the sameonandconfigarguments, and should return the same value. See the Config option changes section of this migration guide for more details. - The
devServerconfig option is specific to component testing, and offers a much more streamlined and consistent way to configure a component testing dev server than using the plugins file. See the Config option changes section of this migration guide for more details. - Many pages and examples throughout the documentation have been updated to show
configuration in
setupNodeEventsas well as the legacy plugins file. For example:
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
// setupNodeEvents can be defined in either
// the e2e or component configuration
e2e: {
setupNodeEvents(on, config) {
// bind to the event we care about
on('<event>', (arg1, arg2) => {
// plugin stuff here
})
},
},
})
import { defineConfig } from 'cypress'
export default defineConfig({
// setupNodeEvents can be defined in either
// the e2e or component configuration
e2e: {
setupNodeEvents(on, config) {
// bind to the event we care about
on('<event>', (arg1, arg2) => {
// plugin stuff here
})
},
},
})
Config Option Changes
baseUrl
The baseUrl config option is no longer valid at the top level of the
configuration, and may only be defined inside the
e2e configuration object.
Attempting to set the baseUrl config option at the top level of the
configuration will result in an error when Cypress loads.
componentFolder
The componentFolder config option is no longer used, as it has been replaced
by the specPattern
testing-type specific option.
Attempting to set the componentFolder config option will result in an error
when Cypress loads.
devServer
All functionality related to starting a component testing dev server previously
in the pluginsFile has moved here. These options are not valid at the
top-level, and may only be defined in the
component configuration object.
Related notes:
- Do not configure your dev server inside
setupNodeEvents(), use thedevServerconfig option instead.
See the dev server documentation for the UI framework you're using for more
specific instructions on what the devServer should be for that framework. Some
examples can be found in our
framework documentation.
Variant 1 (webpack & vite dev servers)
Beforeconst { startDevServer } = require('@cypress/webpack-dev-server')
const webpackConfig = require('../../webpack.config.js')
module.exports = (on, config) => {
if (config.testingType === 'component') {
on('dev-server:start', async (options) =>
startDevServer({ options, webpackConfig })
)
}
}
- cypress.config.js
- cypress.config.js (verbose)
- cypress.config.ts
- cypress.config.ts (verbose)
const { defineConfig } = require('cypress')
const webpackConfig = require('./webpack.config.js')
module.exports = defineConfig({
component: {
devServer: {
framework: 'react', // or vue
bundler: 'webpack',
webpackConfig,
},
},
})
const { defineConfig } = require('cypress')
const webpackConfig = require('./webpack.config.js')
module.exports = defineConfig({
component: {
devServer(cypressConfig) {
return devServer({
framework: 'react', // or vue
cypressConfig,
webpackConfig,
})
},
},
})
import { defineConfig } from 'cypress'
import webpackConfig from './webpack.config'
export default defineConfig({
component: {
devServer: {
framework: 'react', // or vue
bundler: 'webpack',
webpackConfig,
},
},
})
import { defineConfig } from 'cypress'
import { devServer } from '@cypress/webpack-dev-server'
import webpackConfig from './webpack.config'
export default defineConfig({
component: {
devServer(cypressConfig) {
return devServer({
framework: 'react', // or vue
cypressConfig,
webpackConfig,
})
},
},
})
Variant 2 (react plugin dev servers)
Beforeconst devServer = require('@cypress/react/plugins/react-scripts')
module.exports = (on, config) => {
if (config.testingType === 'component') {
injectDevServer(on, config, {})
}
}
- cypress.config.js
- more verbose
const { defineConfig } = require('cypress')
module.exports = defineConfig({
component: {
devServer: {
framework: 'react', // or vue
bundler: 'webpack',
},
},
})
const { defineConfig } = require('cypress')
const webpackConfig = require('./webpack.config.js')
module.exports = defineConfig({
component: {
devServer(cypressConfig) {
return devServer({
framework: 'react', // or vue
cypressConfig,
webpackConfig,
})
},
},
})
experimentalStudio
This option is no longer used. The experimental "Cypress Studio" has been removed and will be rethought/revisited in a later release.
Attempting to set the experimentalStudio config option will result in an error
when Cypress loads.
ignoreTestFiles → excludeSpecPattern
The ignoreTestFiles option is no longer used, and has been replaced with the
excludeSpecPattern
testing-type specific option.
Default values
e2e.excludeSpecPatterndefault value is*.hot-update.js(same as pervious ignore value)component.excludeSpecPatterndefault value is['/snapshots/*', '/image_snapshots/*']updated from*.hot-update.js- The
**/node_modules/**pattern is automatically added to bothe2e.specExcludePatternandcomponent.specExcludePattern, and does not need to be specified (and can't be overridden).
{
"ignoreTestFiles": "path/to/**/*.js"
}
{
component: {
excludeSpecPattern: "path/to/**/*.js"
},
e2e: {
excludeSpecPattern: "other/path/to/**/*.js"
}
}
Attempting to set the ignoreTestFiles config option will result in an error
when Cypress loads.
Also, attempting to set the excludeSpecPattern config option at the top level
of the configuration will result in an error when Cypress loads.
integrationFolder
This option is no longer used, as it has been replaced by the specPattern
testing-type specific option.
Attempting to set the integrationFolder config option will result in an error
when Cypress loads.
pluginsFile
This option is no longer used, and all plugin file functionality has moved into
the setupNodeEvents() and
devServer options. See the
Plugins file removed section of this migration guide
for more details.
Attempting to set the pluginsFile config option will result in an error when
Cypress loads.
setupNodeEvents()
All functionality related to setting up events or modifying the config,
previously done in the plugins file, has moved into the setupNodeEvents()
config options. This option is not valid at the top level of the config, and may
only be defined inside the component or e2e
configuration objects.
More information can be found in the Node Events Overview and the Configuration API documentation.
Before cypress/plugins/index.js
module.exports = (on, config) => {
if (config.testingType === 'component') {
// component testing dev server setup code
// component testing node events setup code
} else {
// e2e testing node events setup code
}
}
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
component: {
devServer(cypressConfig) {
// component testing dev server setup code
},
setupNodeEvents(on, config) {
// component testing node events setup code
},
},
e2e: {
setupNodeEvents(on, config) {
// e2e testing node events setup code
},
},
})
import { defineConfig } from 'cypress'
export default defineConfig({
component: {
devServer(cypressConfig) {
// component testing dev server setup code
},
setupNodeEvents(on, config) {
// component testing node events setup code
},
},
e2e: {
setupNodeEvents(on, config) {
// e2e testing node events setup code
},
},
})
Alternately, you can continue to use an external plugins file, but you will need
to load that file explicitly, and also update it to move any component testing
dev server code into the devServer config option.
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
const setupNodeEvents = require('./cypress/plugins/index.js')
module.exports = defineConfig({
component: {
devServer(cypressConfig) {
// component testing dev server setup code
},
setupNodeEvents,
},
e2e: {
setupNodeEvents,
},
})
import { defineConfig } from 'cypress'
import setupNodeEvents from './cypress/plugins/index.js'
export default defineConfig({
component: {
devServer(cypressConfig) {
// component testing dev server setup code
},
setupNodeEvents,
},
e2e: {
setupNodeEvents,
},
})
slowTestThreshold
The slowTestThreshold configuration option is no longer valid at the top level
of the configuration, and is now a
testing-type specific option.
Note that the default values are unchanged (10000 for e2e and 250 for
component).
Attempting to set the slowTestThreshold config option at the top level of the
configuration will result in an error when Cypress loads.
supportFile
The supportFile configuration option is no longer valid at the top level of
the configuration, and is now a
testing-type specific option.
More information can be found in the
support file docs.
{
"supportFile": "cypress/support/index.js"
}
{
component: {
supportFile: 'cypress/support/component.js'
},
e2e: {
supportFile: 'cypress/support/e2e.js'
}
}
Attempting to set the supportFile config option at the top level of the
configuration will result in an error when Cypress loads.
Also, for a given testing type, multiple matching supportFile files will
result in an error when Cypress loads.
testFiles → specPattern
The testFiles option is no longer used, and has been replaced with the
specPattern option, which must be defined inside the
component and
e2e configuration objects.
Default values:
- No longer matches with
.coffeeor.cjsx. e2e.specPatterndefault value iscypress/e2e/**/*.cy.{js,jsx,ts,tsx}.component.specPatterndefault value is**/*.cy.{js,jsx,ts,tsx}.
Important note about matching:
- E2E tests will be found using the
e2e.specPatternvalue. - Component tests will be found using the
component.specPatternvalue but any tests found matching thee2e.specPatternvalue will be automatically excluded.
Attempting to set the testFiles config option will result in an error when
Cypress loads.
Also, attempting to set the specPattern config option at the top level of the
configuration will result in an error when Cypress loads.
Updated Test File Locations
Previously, you could specify the locations of test files and folders using the
configuration options: componentFolder, or integrationFolder, and
testFiles. These options have been replaced with specPattern, which is not
valid at the top-level, but within the
component or
e2e configuration objects. For
example:
{
"componentFolder": "src",
"integrationFolder": "cypress/integration",
"testFiles": "**/*.cy.js"
}
{
component: {
specPattern: 'src/**/*.cy.js'
},
e2e: {
specPattern: 'cypress/integration/**/*.cy.js'
}
}
Attempting to set componentFolder, integrationFolder, or testFiles in the
config will result in an error when Cypress loads.
For Cypress Cloud users, changing your specPattern and files names or
extensions of your spec files will result in a loss of data in Cypress Cloud.
Because of this, if we detect your project is using Cypress Cloud during
automatic migration, we won't suggest changing your spec files. We also don't
recommend doing it manually if you are a Cypress Cloud user.
Generated Files
Generated screenshots and videos will still be created inside their respective
folders (screenshotsFolder,
videosFolder). However, the paths of generated files inside those folders will
be stripped of any common ancestor paths shared between all spec files found by
the specPattern option (or via the --spec command line option or spec
module API option, if specified).
Here are a few examples, assuming the value of videosFolder is
cypress/videos, screenshotsFolder is cypress/screenshots and
cy.screenshot('my-screenshot') is called once per spec file:
Example 1
- Spec file found
cypress/e2e/path/to/file/one.cy.js
- Common ancestor paths (calculated at runtime)
cypress/e2e/path/to/file
- Generated screenshot file
cypress/screenshots/one.cy.js/my-screenshot.png
- Generated video file
cypress/videos/one.cy.js.mp4
Example 2
- Spec files found
cypress/e2e/path/to/file/one.cy.jscypress/e2e/path/to/two.cy.js
- Common ancestor paths (calculated at runtime)
cypress/e2e/path/to
- Generated screenshot files
cypress/screenshots/file/one.cy.js/my-screenshot.pngcypress/screenshots/two.cy.js/my-screenshot.png
- Generated video files
cypress/videos/file/one.cy.js.mp4cypress/videos/two.cy.js.mp4
Command / Cypress API Changes
cy.mount()
If you set up your app using the automatic configuration wizard, a basic
cy.mount() command will be imported for you in your
support file from one our supported frameworks.
Cypress.Commands.add()
Cypress.Commands.add() has been updated to
allow the built-in "placeholder" custom mount and hover commands to be
overwritten without needing to use Cypress.Commands.overwrite().
Component Testing Changes
Component Testing has moved from experimental to beta status in 10.0.0.
Component Testing can now be ran from the main app, and launching into component
testing via the command cypress open-ct is now deprecated. To launch directly
into component testing, use the cypress open --component command instead.
All the Component Testing dev servers are now included in the main cypress npm
package. Configuring them is done via specifying a framework and bundler in the
devServer config option, and the packages are no longer directly importable.
See
Framework Configuration
for more info.
The mount libraries for React and Vue have also been included in the main
cypress package and can be imported from cypress/react and cypress/vue
respectively.
Any previous dev servers or mounting libraries from the @cypress namespace
should be uninstalled in Cypress 10.
Clashing Types with Jest
You may want to consider configuring your app with a separate tsconfig.json to solve
clashing types with jest.
You will need to exclude cypress.config.ts, cypress, node_modules in your
root tsconfig.json file.
{
"exclude": ["cypress.config.ts", "cypress", "node_modules"]
}
Code Coverage Plugin
The Cypress Code Coverage plugin will need to be updated to version >= 3.10 to work with Cypress 10. Using a previous version will result in an error when tests are ran with code coverage enabled.
--
For earlier migration guides, see the documentation's repo history for this file.