Which Cypress limitations are permanent by design, and which have workarounds?
answer
- The docs publish two separate lists
- Which limits follow from the architecture
- A workaround still has a price
- One browser, one superdomain, one language
- Hover is a workarounds page, not a command
basics
~20 sCypress documents two lists. Permanent: it is a specialized tool, specs run inside the browser, it drives one browser at a time, and each test is bound to one superdomain. Temporary: no hover command, no native or mobile events, limited iframe switching.
solid answer
~50 sCypress's own trade-offs page splits its limits into **permanent** and **temporary**, and that split is the useful thing to carry into an evaluation. Permanent: it is a specialized testing tool rather than general-purpose automation; test code is evaluated inside the browser, so specs are JavaScript or TypeScript and cannot import server-side modules; it will not drive two open browsers at once; and each test is bound to a single superdomain, with `cy.origin()` as the sanctioned way to cross. Temporary: there is no `cy.hover()` command, native and mobile events are unsupported, and iframe support stops short of a *switch into this iframe* command. Several permanent limits still have first-party escape hatches - `@cypress/puppeteer` for a second tab, `experimentalWebKitSupport` for a WebKit engine - so the real evaluation question is what each hatch costs, not whether one exists.
code
javascript · 22 linesconst { defineConfig } = require('cypress')
const { setup } = require('@cypress/puppeteer')
module.exports = defineConfig({
// Safari's engine via playwright-webkit - experimental, and cy.origin() is unsupported here
experimentalWebKitSupport: true,
e2e: {
baseUrl: 'https://staging.freight-portal.test',
setupNodeEvents(on) {
// Chromium-family browsers only; this handler runs in Node, not in the spec
setup({
on,
onMessage: {
async billOfLadingTabTitle(browser) {
const pages = await browser.pages()
return pages.at(-1).title()
},
},
})
},
},
})go deeper
Learn the four permanent ones by heart: specialized tool, code runs in the browser, one browser at a time, one superdomain per test. They explain most Cypress surprises you will hit.
Be ready to say why each permanent limit follows from running inside the browser, and to name the first-party hatch for the ones that have one.
Show that you price the workaround, not the limit. A hatch that changes where code executes costs every future reader of that spec, and that cost belongs in the decision.
Decide which hatches a team is allowed to reach for at all, and write that down. A suite where every constraint has been worked around has silently become a different tool.
## Two lists, and why the split matters Cypress publishes its constraints as two separate lists, and reading them as one list is how teams end up either rejecting the tool for a solvable problem or adopting it into a wall. A **permanent** trade-off follows from the architecture: test code runs inside the browser, in the same event loop as the application. No release note is going to undo that, so a permanent limit is something you design the suite around. A **temporary** trade-off is a gap the project intends to close; it is worth checking against the version you are on rather than against a blog post from three years ago. ## The permanent list - **Cypress is a specialized tool, not general-purpose automation.** The docs explicitly steer indexing the web, spidering links, performance testing and scripting third-party sites elsewhere. - **Commands run inside the browser.** There is no wire protocol and no object serialization between a command and an element, and the same fact means a spec cannot `import` a server-side module. `cy.task()` and `cy.request()` are the doors out to Node and your back end. - **One open browser at a time.** Cypress does not control more than one browser simultaneously, so a two-user collaboration test is not written as two browsers. - **One superdomain per test.** Cross-origin navigation inside a test is enabled by `cy.origin()`, which is a deliberate seam rather than a transparent redirect. ## The temporary list - **There is no `cy.hover()` command.** The documentation page for it is a workarounds page; calling it errors. Use `.trigger('mouseover')` or `.trigger('mouseenter')`, or force the action. - **No native or mobile event support.** - **Iframe support is partial.** Same-origin iframes can be queried natively; a *switch into this iframe* command remains an open proposal. ## The escape hatches, and what each one costs The interesting evaluation move is to price the workaround rather than the limit. | Limit | First-party hatch | What it costs | |---|---|---| | Journey crosses a superdomain | `cy.origin()` | The callback is an isolated block: outside variables do not travel into it, and some commands are unavailable inside | | Flow opens a second tab | `@cypress/puppeteer` plugin | Handler code runs in Node from `setupNodeEvents`, so no Cypress commands, no retry-ability and no Command Log inside it; Chromium-family browsers only; still public beta | | Safari engine coverage | `experimentalWebKitSupport` with `playwright-webkit` | Experimental, with published gaps: `cy.origin()` and Test Replay are unsupported there | | Wall-clock time of a long suite | Parallelisation through Cypress Cloud | A dependency on a hosted service that the suite's CI wiring then assumes | Read that table as a cost sheet. A limit with a cheap, readable hatch - `cy.origin()` for one sign-in hop - barely moves the adoption decision. A limit whose hatch drops you into a different execution model, as the Puppeteer plugin does by running your automation in Node, moves it a lot, because every engineer who later reads that spec has to hold two mental models at once. ## Using the classification during an evaluation 1. Sort the limits your flows actually hit into permanent and temporary. Ignore the ones nobody in your product will meet; a two-user chat limit is irrelevant to a single-operator console. 2. For each permanent limit, ask whether the design can absorb it. Simulating the second participant instead of opening a second browser is usually a better test anyway, because it is faster and deterministic. 3. For each temporary limit, check the current version's behaviour before you plan around it. Flags move: `experimentalMemoryManagement` became `manageBrowserMemory` and `experimentalFastVisibility` became `visibilityStrategy`, and `visibilityStrategy` is itself already deprecated. 4. For each hatch you plan to use, write down what it costs a future reader, not just whether it works today. ## The common mistake Teams treat *"there is a workaround"* as equivalent to *"there is no limit"*. It is not. A workaround you use once at a documented seam is fine; a workaround that appears in forty specs is the shape of the tool telling you the application is a misfit. The honest reading of the two lists is that the permanent items are the price of the in-browser architecture, and you either want that architecture enough to pay it or you do not.
- Why does the documentation argue that some of these limits are good to have?Because most of them push you away from slow, flaky tests. Not opening a second browser forces you to simulate the other participant, which is faster and deterministic. Binding a test to one superdomain keeps a journey from wandering into somebody else's deploy schedule. The constraint does part of your test design for you.
- How do you tell whether a Cypress limit you read about is still real?Check the version you are actually on, because this surface moves. Cypress 16 removed `Cypress.env()`, `cy.exec()` and `cy.end()`, and renamed several experiments. Read the configuration and trade-offs pages for your installed version rather than trusting a tutorial, then confirm with a five-line spec.
Treat the permanent list as a wall and the temporary list as a speed bump. You plan a route around a wall; you slow down for a bump and check whether it has been paved over since you last drove through.
saying these in an interview costs you the question
- Treats every documented limit as permanent
- Says a workaround exists, so there is no cost
- Claims cy.hover() exists in Cypress
- Thinks the Puppeteer plugin runs inside the spec
- Cites removed flags like experimentalMemoryManagement as current