cypress
Frameworkcypress-io/cypress
Fast, easy, and reliable testing for anything that runs in a browser.
Overview
Cypress is a front-end testing tool built for the modern web. It enables developers to write end-to-end, component, and integration tests with real-time reloading, time-travel debugging, and automatic waiting. It simplifies testing and provides reliable results.
README Preview
\n \n \n \n \n \n \n \n\n\n Documentation |\n Changelog |\n Roadmap\n\n\n\n The web has evolved. Finally, testing has too.\n\n\n\n Fast, easy and reliable testing for anything that runs in a browser.\n\n\n Join us, we're hiring.\n\n\n\n \n \n \n \n \n \n \n \n \n\n\n## What is Cypress?\n\n\n \n \n \n\n\n## Installing\n\n[](https://badge.fury.io/js/cypress)\n\nInstall Cypress for Mac, Linux, or Windows, then [get started](https://on.cypress.io/install).\n\n```bash\nnpm install cypress --save-dev\n```\nor\n```bash\nyarn add cypress --dev\n```\nor\n```bash\npnpm add cypress --save-dev\n```\n\n\n\n## Contributing\n\n[](https://cloud.cypress.io/projects/ypt4pf/runs)\n[](https://circleci.com/gh/cypress-io/cypress/tree/develop) - `develop` branch\n\nPlease see our [Contributing Guideline](./CONTRIBUTING.md) which explains repo organization, linting, testing, and other steps.\n\n## License\n\n[](https://github.com/cypress-io/cypress/blob/develop/LICENSE)\n\nThis project is licensed under the terms of the [MIT license](/LICENSE).\n\n## Badges\n\nConfigure a badge for your project's README to show your test status or test count in the [Cypress Cloud](https://www.cypress.io/cloud).\n\n[](https://cloud.cypress.io/projects/ypt4pf/runs)\n\n[](https://cloud.cypress.io/projects/ypt4pf/runs)\n\nOr let the world know your project is using Cypress with the badge below.\n\n[](https://www.cypress.io/)\n\n```\n[](https://www.cypress.io/)\n```\n
FAQ (5)
TroubleshootingHow to fix Cypress 16 clientCertificates causing a Chrome certificate picker in open mode or hanging in run mode?
In Cypress 16, Chrome uses the native HTTP/2 network path by default, which does not present clientCertificates for mTLS. Force the legacy HTTP/1 network stack by setting forceHttp1: true in cypress.config.ts:
import { defineConfig } from 'cypress'
export default defineConfig({
forceHttp1: true,
clientCertificates: [
{\n url: 'https://test.com',
ca: [],
certs: [
{\n cert: 'certs/client.crt',
key: 'certs/client.key',
},
],
},
],
})
Alternatively, run Cypress with the CLI flag:
cypress run --browser chrome --force-http1
Firefox and other non-Chromium browsers continue to use the legacy network stack. If the workaround is not viable, downgrade to Cypress 15.21.1. A permanent fix is being tracked in the Cypress PR that presents client certificates on the browser network path.
TroubleshootingHow to fix Cypress 16 `cy.wait()` returning undefined response for a `fetch` POST with a 302 redirect in Chrome?
This is a Cypress 16.1.0 native network/CDP Fetch bug affecting same-origin fetch() POST requests made with redirect: 'follow' when the server returns 302. The interception waits for Fetch.requestPaused at the response stage, never receives it, and after 30 seconds fails with Timed out waiting for CDP Fetch response pause; interception.response is undefined even though the browser successfully follows the redirect.
Workaround: enable forceHttp1 in cypress.config.js to use the legacy proxy path used by Cypress 15:
const { defineConfig } = require('cypress')
module.exports = defineConfig({
forceHttp1: true,
})
For ESM config:
import { defineConfig } from 'cypress'
export default defineConfig({
forceHttp1: true,
})
Restart Cypress after changing the config. With this option enabled, cy.wait('@alias') resolves without the 30-second delay and yields the POST hop's 302 status and Location header as expected. Plain GET requests are not affected; the issue is specific to redirecting fetch POST requests on Cypress 16's Chrome native network path.
TroubleshootingHow to fix Cypress sending a stale cookie after `cy.request()` rotates the session cookie?
This is a Cypress 15.20.0+ regression: cy.request() updates the browser cookie but not Cypress's server-side cookie jar, so the next AUT-frame document navigation sends the previous stale cookie. XHR/fetch requests are unaffected.
Workaround: wrap the login request in cy.session(), which clears/resets the cookie jar:
beforeEach(() => {
cy.session('login', () => {
cy.request('POST', '/login')
})
cy.visit('/account')
})
Alternatively, downgrade to Cypress 15.19.x. The permanent fix is to upgrade to a release containing the PR "fix: keep the cookie jar in sync when cookies are set outside the proxy."
TroubleshootingHow to fix CypressError: sinon.js was not found in the remote iframe's window when using cy.server()?
This happens when Cypress did not load the current page directly with cy.visit(), usually after clicking a regular link. Load the target URL with Cypress before stubbing XHRs:
cy.visit('/your-page')
cy.server().route('POST', /users\.json/, {})
If the POST happens during page load, define the route before visiting:
cy.server()
cy.route('POST', /users\.json/, {})
cy.visit('/your-page')
Do not rely on navigation performed by clicking a plain <a> link in a previous test; either call cy.visit(targetUrl) directly or reload the page through Cypress before using cy.server(). Cross-origin pages cannot be stubbed this way.
In modern Cypress, replace cy.server()/cy.route() with:
cy.intercept('POST', '**/users.json', {})
cy.visit('/your-page')TroubleshootingHow to fix "TypeError: Cannot read properties of undefined (reading 'selector')" when using cy.contains() inside .within() in Cypress?
This is a Cypress bug (reproduced in 15.18.1): inside .within(), the subject is re-queried on every retry, and if it resolves to an empty set on the FIRST attempt of cy.contains(), getOptions.withinSubject stays undefined. The error-message builder getPhrase() then crashes on el.selector of undefined, and that error aborts retrying — so the test fails in milliseconds instead of waiting for defaultCommandTimeout (typical trigger: a list that briefly unmounts during a refetch/loading state). Workaround: replace cy.contains() with cy.get() plus a text assertion, since cy.get() inside .within() retries correctly:
// Flaky — throws TypeError instead of retrying
cy.get('#group').within(() => {
cy.contains('new');
});
// Reliable — retries until the subject re-mounts and contains the text
cy.get('#group').within(() => {
cy.get('p').should('contain.text', 'new');
});Alternatively, guard the
contains so the subject exists on its first attempt:cy.get('#group').within(() => {
cy.get('p').should('exist'); // waits out the loading state
cy.contains('new');
});Permanent fix: upgrade Cypress once the patch lands — the proposed fix makes
getPhrase fall back to the selector (or a generic phrase) when withinSubject is undefined, so cy.contains() keeps retrying like cy.get().