Skip to main content
Cypress App

Migration Guide

Migrating to Cypress 15.0​

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.

Before
cy.exec('rake db:seed').its('code').should('eq', 0)
After
cy.exec('rake db:seed').its('exitCode').should('eq', 0)

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.

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-server for component testing. This use case is most common and will require an update to Webpack 5.
  • @cypress/webpack-preprocessor for 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-preprocessor as 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 install --save-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 install --save-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(),
})
)
}

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 install --save-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 @.

Before
import { mount } from `cypress/angular`
After
import { mount } from `@cypress/angular`

Selector Playground API changes​

The Cypress.SelectorPlayground API has been renamed to Cypress.ElementSelector. Additionally, 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.

Before
Cypress.SelectorPlayground.defaults({
selectorPriority: ['class', 'id'],
})
After
Cypress.ElementSelector.defaults({
selectorPriority: ['class', 'id'],
})

Migrating to Cypress 14.0​

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')
})
info

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.

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.

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 justInTimeCompile component configuration option.
  • JIT compilation no longer applies with vite, since there is no benefit to enabling this with vite.

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.

const { defineConfig } = require('cypress')

module.exports = defineConfig({
component: {
justInTimeCompile: false,
},
})

For users with the existing experimentalJustInTimeCompile flag set, you can remove this flag from your configuration.

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.

Before
import { mount } from 'cypress/react18'
After
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 install --save-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 @.

Before
import { mount } from 'cypress/react'
After
import { mount } from '@cypress/react'

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.

Before
import { mount } from 'cypress/angular-signals'
After
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 install --save-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 @.

Before
import { mount } from 'cypress/angular'
After
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 install --save-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 @.

Before
import { mount } from 'cypress/vue2'
After
import { mount } from '@cypress/vue2'

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-vue to 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.

Before
const { defineConfig } = require('cypress')

module.exports = defineConfig({
component: {
devServer: {
framework: 'vue-cli',
bundler: 'webpack',
},
},
})
After
const { defineConfig } = require('cypress')

module.exports = 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 install --save-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​

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.

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. 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.

const { defineConfig } = require('cypress')

module.exports = 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.

const { defineConfig } = require('cypress')

module.exports = defineConfig({
// value can be true/false -or- an integer between 0 and 51
videoCompression: true,
})

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.

Migrating to Cypress 12.0​

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.

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 reached its end of life on April 30, 2022. See Node's release schedule. This Node.js version will no longer be supported when installing Cypress. The minimum Node.js version supported to install Cypress is Node.js 14+.

Test Isolation​

The testIsolation config option is enabled by default. This means Cypress resets the browser context before each test by:

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('email').type('[email protected]')
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.

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')
+ })
+ })

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():

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​

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.

Changes to Mounting Options​

Each major library we support has a mount function with two arguments:

  1. The component
  2. 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: [
'https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css',
],
})
})

After (Cypress 11)​

/** style.css */
@import "https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.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.

Migrating to Cypress 10.0​

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 run or cypress open. Instead, an error will display.
  • In 9.x and earlier versions, cypress open would bring you directly to the project specs list. In 10.0.0, you must pass --browser and --e2e or --component as 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-file command line flag or the configFile module API option to specify a .js or .ts file. JSON config files are no longer supported.
  • Cypress now requires a config file, so specifying --config-file false on the command line or a configFile value of false in the module API is no longer valid.
  • You can't have both cypress.config.js and cypress.config.ts files. 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, the defineConfig function 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.js and cypress.config.ts vs the older cypress.json. For example:
const { defineConfig } = require('cypress')

module.exports = 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.js plugins 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 same on and config arguments, and should return the same value. See the Config option changes section of this migration guide for more details.
  • The devServer config 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 setupNodeEvents as well as the legacy plugins file. For example:
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
})
},
},
})

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.

caution

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.

caution

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 the devServer config option instead.
info

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)

Before
cypress/plugins/index.js
const { 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 })
)
}
}
After
const { defineConfig } = require('cypress')
const webpackConfig = require('./webpack.config.js')

module.exports = defineConfig({
component: {
devServer: {
framework: 'react', // or vue
bundler: 'webpack',
webpackConfig,
},
},
})

Variant 2 (react plugin dev servers)

Before
const devServer = require('@cypress/react/plugins/react-scripts')

module.exports = (on, config) => {
if (config.testingType === 'component') {
injectDevServer(on, config, {})
}
}
After
const { defineConfig } = require('cypress')

module.exports = defineConfig({
component: {
devServer: {
framework: 'react', // or vue
bundler: 'webpack',
},
},
})

experimentalStudio​

This option is no longer used. The experimental "Cypress Studio" has been removed and will be rethought/revisited in a later release.

caution

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.excludeSpecPattern default value is *.hot-update.js (same as pervious ignore value)
  • component.excludeSpecPattern default value is ['/snapshots/*', '/image_snapshots/*'] updated from *.hot-update.js
  • The **/node_modules/** pattern is automatically added to both e2e.specExcludePattern and component.specExcludePattern, and does not need to be specified (and can't be overridden).
Before
{
"ignoreTestFiles": "path/to/**/*.js"
}
After
{
component: {
excludeSpecPattern: "path/to/**/*.js"
},
e2e: {
excludeSpecPattern: "other/path/to/**/*.js"
}
}
caution

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.

caution

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.

caution

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
}
}
After
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
},
},
})

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.

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,
},
})

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).

caution

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.

Before
{
"supportFile": "cypress/support/index.js"
}
After
{
component: {
supportFile: 'cypress/support/component.js'
},
e2e: {
supportFile: 'cypress/support/e2e.js'
}
}
caution

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 .coffee or .cjsx.
  • e2e.specPattern default value is cypress/e2e/**/*.cy.{js,jsx,ts,tsx}.
  • component.specPattern default value is **/*.cy.{js,jsx,ts,tsx}.

Important note about matching:

  • E2E tests will be found using the e2e.specPattern value.
  • Component tests will be found using the component.specPattern value but any tests found matching the e2e.specPattern value will be automatically excluded.
caution

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:

Before
{
"componentFolder": "src",
"integrationFolder": "cypress/integration",
"testFiles": "**/*.cy.js"
}
After
{
component: {
specPattern: 'src/**/*.cy.js'
},
e2e: {
specPattern: 'cypress/integration/**/*.cy.js'
}
}
caution

Attempting to set componentFolder, integrationFolder, or testFiles in the config will result in an error when Cypress loads.

danger

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.js
    • cypress/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.png
    • cypress/screenshots/two.cy.js/my-screenshot.png
  • Generated video files
    • cypress/videos/file/one.cy.js.mp4
    • cypress/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.