Limits, Flake, and Choosing
Every tool is wonderful in the demo. The question that actually matters is where it hurts at 2am when the suite is red and you can't tell if the app broke or the test did. This phase is the honest map: where Cypress hits walls it can't climb, where Selenium leaks flake, how each one scales, and how the whole picture shifts now that Playwright exists. By the end you'll be able to choose on purpose.
Where Cypress hits a wall
Remember phase 1: Cypress lives inside the browser. That gift is also the cage. The big limits are direct consequences of sharing the browser process, and no amount of config makes them fully disappear.
- Same-origin restriction. Historically a single Cypress test could only operate within one origin (scheme + domain + port). Newer Cypress relaxes this: the
cy.origin()command lets a test visit a second origin by running a block of commands in that origin's context. It works, but you have to wrap that code deliberately — cross-origin flows like third-party OAuth logins are extra effort, not free. - One browser tab, one window. Cypress cannot drive multiple tabs or a second browser window. A flow that opens a new tab (a "share" popup, a payment window) can't be followed there. The usual workaround is to test the intent — assert the link has the right
targetandhref— rather than the new tab itself. - JavaScript/TypeScript only. Tests are JS or TS, full stop. A Python or Java shop can't write Cypress in their main language. That's fine if your team lives in the JS world and a real constraint if it doesn't.
- Browser coverage is narrower. Cypress runs in Chromium-family browsers, Firefox, and (with caveats and version dependence) WebKit-based browsers. It is not the "every browser, every version" story that WebDriver is.
Cypress can't follow a new tab:
[Test in tab A] --click "Open report"--> [tab B opens]
| ✗ Cypress stays in tab A
└── workaround: assert href/target, then visit that URL directly in tab A
What just happened: the test never crosses into tab B. Instead of chasing the new tab, you verify the link is correct and, if you need to test the destination, cy.visit() that URL in the original tab.
Where Selenium leaks flake
Selenium's pain is the mirror image: the cross-process protocol that gives it superpowers also opens a gap where timing problems breed. The test and the browser are not in sync by default, so flake is the tax you pay for reach.
The usual culprits:
- Timing. The test fires a command before the page is ready, or reads state a moment too early. This is the explicit-wait discipline from phase 2 — get it right and most flake evaporates.
- Stale element references. You grabbed an element handle, the page re-rendered, and now the handle points at a DOM node that no longer exists. Selenium raises
StaleElementReferenceException. The fix is to re-find the element right before you use it rather than holding handles across actions. - Environment drift. Driver version vs. browser version mismatches once caused endless grief; Selenium Manager has largely tamed this, but headless-vs-headed differences and OS quirks still surprise people.
# Re-find instead of reusing a handle across a re-render
=
What just happened: by looking the element up immediately before clicking, you sidestep the stale-reference trap that comes from caching a handle while the DOM churns underneath it. Flake of this kind is a deep topic — the flaky-tests guide is worth your time if a suite is fighting you.
Scaling up: Grid and parallelism
When one machine isn't enough, the two tools scale differently.
Selenium Grid is the classic answer and a direct payoff of the protocol design. A Grid has a hub that distributes tests and many nodes, each offering browsers. Because your test only speaks WebDriver to a remote endpoint, you point it at the Grid and it runs your suite across many browsers and machines in parallel — different OSes, different browser versions, all at once.
=
What just happened: the only change from a local run is the endpoint. Because Selenium was always talking over the protocol, aiming it at a remote Grid is a one-line difference — that portability is the architecture paying dividends.
Cypress scales mainly by splitting specs across CI machines and running them in parallel, with its dashboard service balancing the load. It's effective, but it's parallelism-by-sharding rather than a true distributed browser farm, and broad cross-browser, cross-OS matrices are more Selenium's home turf.
How both compare to Playwright
You can't choose between Cypress and Selenium honestly without naming the third player, because Playwright reshaped the decision. Playwright is a newer tool that, in a sense, takes the best of both architectures: like Selenium it's an out-of-process driver, so it isn't boxed into one tab or one origin; like Cypress it has auto-waiting and excellent developer experience built in.
A fair, qualitative comparison:
| Dimension | Cypress | Selenium / WebDriver | Playwright |
|---|---|---|---|
| Architecture | In-browser | Out-of-process (W3C standard) | Out-of-process |
| Auto-waiting | Yes, built in | No — explicit waits | Yes, built in |
| Languages | JS / TS only | Java, Python, C#, Ruby, JS, more | JS/TS, Python, Java, .NET |
| Multiple tabs / origins | Constrained | Native | Native |
| Browser coverage | Narrower | Widest (vendor drivers) | Chromium, Firefox, WebKit |
| Standard | Proprietary runner | W3C WebDriver | Own protocol (uses CDP-style control) |
| Sweet spot | JS apps, great DX, simple flows | Polyglot teams, broad matrices, legacy | Modern apps wanting DX + reach |
What just happened: Playwright collapses the old tradeoff — auto-waiting and multi-tab and multiple languages. That's why many new projects pick it. Cypress still wins for some teams on debugging feel and ecosystem; Selenium still wins where the W3C standard, sheer browser breadth, or an existing investment matters.
So which do you choose?
Decide from your constraints, not from hype:
- Reach for Cypress when your team is JavaScript/TypeScript, your flows stay within one origin and one tab, and you value the time-travel debugger and gentle learning curve over breadth.
- Reach for Selenium when you need a non-JS language, the widest possible browser and OS matrix, a real distributed Grid, or you're maintaining a suite that already exists. The W3C-standard footing is a long-term bet that ages well.
- Seriously consider Playwright for a greenfield project, since it sidesteps Cypress's tab/origin limits while keeping the auto-waiting ergonomics — it's the default many teams now start from.
The deepest mistake isn't picking the "wrong" tool — it's picking a tool whose architecture fights your app's reality. A multi-tab, multi-origin checkout flow will quietly punish a Cypress team for a year. A small JS app driven by a hand-rolled Selenium Grid is a maintenance burden nobody needed. Match the architecture to the shape of what you're testing, and the tool mostly disappears into the background — which is exactly what a good test tool should do.
[
{
"q": "Why can't a single Cypress test naturally follow a flow that opens a second browser tab?",
"choices": [
"Cypress lacks a click command",
"Cypress runs inside one browser context and cannot drive multiple tabs or windows",
"New tabs are blocked by the WebDriver standard",
"It requires a paid plan to enable"
],
"answer": 1,
"explain": "Living inside the browser limits Cypress to a single tab/window. The common workaround is to assert the link's href/target and visit the URL directly instead."
},
{
"q": "What does Selenium Grid let you do that follows directly from WebDriver being a protocol?",
"choices": [
"Write tests without any selectors",
"Run tests against remote browsers across many machines and OS/browser combos by pointing at a remote endpoint",
"Eliminate all flake automatically",
"Convert Selenium tests into Cypress tests"
],
"answer": 1,
"explain": "Because the test only speaks WebDriver to an endpoint, swapping localhost for a Grid hub distributes the suite across many remote browsers — a near one-line change."
},
{
"q": "How does Playwright relate to the Cypress-vs-Selenium tradeoff?",
"choices": [
"It is just a rebrand of Selenium",
"It runs inside the browser exactly like Cypress",
"It is out-of-process like Selenium (multi-tab/origin, multiple languages) but adds auto-waiting and DX like Cypress",
"It only works for mobile apps"
],
"answer": 2,
"explain": "Playwright is an out-of-process driver — so no single-tab/origin cage and multiple language bindings — while keeping built-in auto-waiting and strong developer experience."
}
]
← Phase 2: Writing and Running Tests | Overview
Check your understanding 3 questions
1. Why can't a single Cypress test naturally follow a flow that opens a second browser tab?
2. What does Selenium Grid let you do that follows directly from WebDriver being a protocol?
3. How does Playwright relate to the Cypress-vs-Selenium tradeoff?