
Cypress vs Playwright vs Selenium: choosing the right automation testing tool by how it drives the browser
Most feature tables disagree because they list results, not causes. Where the test code runs, and which wire carries each command, tells you in advance which limit your suite will hit first.
Cypress vs Playwright vs Selenium: which one should you pick?#
The answer comes from how each project says it reaches the browser. First, Selenium's components page says "WebDriver talks to a browser through a driver". Then the Why Cypress page says "Cypress is executed in the same run loop as your application". And the Microsoft project's browsers page says "Each version of Playwright needs specific versions of browser binaries to operate".
Together, those three lines predict most rows of any Cypress vs Playwright vs Selenium table. If a tool lives inside the page, it can touch the app directly, but it cannot easily leave the page. In contrast, a tool that drives the browser from outside can open tabs and use any language that has a client. And a tool that ships its own browser builds can promise the same behaviour on every machine. But it can only promise that for the builds it ships.
What does picking by mechanism save a test team?#
A framework picked by feature list costs a rewrite the day the suite needs a second tab, Safari or Java, and the drive mechanism predicts that day before the first test. So the real saving in a Playwright vs Cypress vs Selenium choice is a rewrite you never start.
For example, take one team and state its needs up front. Say the team writes tests in Java and must cover Safari. Also, its checkout opens a payment page in a second tab. Then Cypress is out twice, by its own trade-offs page. First, it says "The only language we'll ever support is the language of the web". Second, it says it cannot control "more than 1 open browser at a time". So that leaves Selenium vs Playwright, since both have Java clients.
Then Safari decides it. Selenium's browser docs carry a section on "Safari specific functionality", so Selenium drives the Safari app itself. But the Microsoft project runs its own build of WebKit, the engine inside Safari, and its config calls that profile "Desktop Safari". If the team must sign off on the real Safari app, Selenium fits. If that engine is enough, the pinned builds fit and add retries on top.
In practice, a team can list these needs before the first test exists. Once each is a yes or no, the pick stops being a matter of taste.
Selenium passes every requirement
Selenium: passes
W3C WebDriver commands through a browser driver, from outside the browser
Cypress: Ruled out by language, browsers, tab needs
"The only language we'll ever support is the language of the web" (trade-offs page)
Playwright: Ruled out by browsers
"Each version of Playwright needs specific versions of browser binaries to operate." (browsers page)
| Tool | Limit | Documented line | Rules it out now |
|---|---|---|---|
| Cypress | language | "The only language we'll ever support is the language of the web" (trade-offs page) | Yes |
| Cypress | browsers | Its browser list covers Chrome-family browsers, Edge and Firefox, with WebKit marked experimental (launching browsers page) | Yes |
| Cypress | tab needs | "Cypress does not support controlling more than 1 open browser at a time. You can test multiple tabs, however, using our @cypress/puppeteer plugin." (trade-offs page) | Yes |
| Playwright | language | Its languages page lists JavaScript and TypeScript, Python, Java and .NET | No |
| Playwright | browsers | "Each version of Playwright needs specific versions of browser binaries to operate." (browsers page) | Yes |
How does each tool actually drive the browser?#
Selenium sends W3C WebDriver commands through a browser driver, Cypress runs test code inside the browser beside the app, and Playwright speaks its own protocol to browser builds pinned per release. So those are three different homes for the test code.
Selenium: commands from outside the browser#
The W3C WebDriver draft, dated 2 July 2026, opens with "WebDriver is a remote control interface". There, your test runs in its own process, in any language with a Selenium client. Then each step becomes a request to a driver, a small program each browser vendor ships for its own browser. And the driver tells the browser what to do. As a result, the test sees the page the way a user does, one command at a time. Also, because every command is a remote request, the same script can run on another machine. Selenium's Grid page says Grid runs "WebDriver scripts on remote machines by routing commands sent by the client to remote browser instances".
Cypress: code inside the page#
But the in-page runner turns that around. Its own docs say most testing tools "operate by running outside of the browser". Instead, its test code runs in the same loop as the app. Also, a Node server process, the JavaScript runtime outside the browser, works beside it for anything the page cannot do. Because the test shares the page's run loop, it can read the app's state, stub its network calls and see each change as it lands.
Playwright: its own protocol and pinned builds#
This tool sits outside the browser like Selenium, but it does not use the W3C wire. Instead, it talks to the browser over its own protocol, and each release pins exact browser builds. For Chromium, the open-source project behind Chrome, it can also attach over the Chrome DevTools Protocol (CDP). But its BrowserType reference says that route is "only supported for Chromium-based browsers". It also calls the route "significantly lower fidelity" than its own protocol.
What does each mechanism make easy, and what does it rule out?#
Where the test code runs sets the limits: Cypress, living inside the page, drives one browser at a time in JavaScript, while Selenium and Playwright drive tabs and windows from outside it. So the table below puts each limit next to the mechanism that causes it.
The in-page framework states its limits openly on its trade-offs page. First, each test is bound to one superdomain, meaning one site and its subdomains. Then the cy.origin command lets a test cross to another origin. On tabs, it says "You can test multiple tabs, however, using our @cypress/puppeteer plugin". Also, its browser list covers Chrome-family browsers, Edge and Firefox, with WebKit marked experimental.
The pinned-build model, by contrast, is built for many pages. Its pages guide says "Each browser context can host multiple pages (tabs)". And a browser context is what its docs call an "incognito-like" profile, so each test gets a fresh one. Then its languages page lists JavaScript and TypeScript, Python, Java and .NET, Microsoft's runtime for C#.
Selenium keeps the widest language list, because the W3C wire does not care which language sends the command. So its first-script docs carry the same test in Java, Python, C#, Ruby, JavaScript and Kotlin.
Playwright vs Selenium vs Cypress, side by side#
| Requirement | Selenium | Cypress | Playwright |
|---|---|---|---|
| Where test code runs | Its own process, outside the browser | Inside the browser, beside the app | Its own process, outside the browser |
| Wire to the browser | W3C WebDriver through a driver; BiDi adds a WebSocket | Same run loop as the app, plus a Node server process | Its own protocol; CDP attach for Chromium only |
| Test languages | Java, Python, C#, Ruby, JavaScript, Kotlin | JavaScript and TypeScript only | JavaScript and TypeScript, Python, Java, .NET |
| Browsers | Chrome, Edge, Firefox, Safari, through each vendor's driver | Chrome family, Edge, Firefox; WebKit experimental | Chromium, Firefox, WebKit builds pinned per release; Chrome and Edge channels |
| Second tab or window | Yes, from outside | One browser at a time; tabs through its Puppeteer plugin | Yes, many pages per browser context |
| Second origin in one test | Yes | Through cy.origin | Yes |
Source: each project's documentation pages linked in this guide, read in October 2026.
How long does each tool wait for an element by default?#
Selenium's docs set the implicit wait to 0 ms, while Cypress's docs retry a command for 4000 ms and Playwright's docs retry an assertion for 5000 ms, as printed in October 2026. And that gap explains much of what teams call flakiness.
Selenium's waits page is blunt about it. It says "The default value is 0, which means that if the element is not found, it will immediately return an error". So an outside driver that waits for nothing leaves every wait to the person writing the test. Then explicit waits fix it, but someone has to write each one. The same page calls them "loops added to the code that poll the application for a specific condition", and it warns "Do not mix implicit and explicit waits".
But the other two tools retry by default. The Cypress configuration reference, read in October 2026, sets defaultCommandTimeout to 4000 milliseconds. Also, the Microsoft project's timeouts page, read the same month, gives auto-retrying assertions "5 seconds by default". Each test gets "30 seconds by default". Since both keep trying while the page settles, they absorb most of the small timing gaps that make a test fail now and then.
Show data table
| Item | Value |
|---|---|
| Cypress command timeout | 4,000 |
| Playwright assertion timeout | 5,000 |
| Playwright test timeout | 30,000 |
| Cypress page load timeout | 60,000 |
Selenium starts at zero, and the other two start with a budget.
Flakiness, then, is partly a default you can change. So a Selenium suite with a sound explicit-wait helper can be as steady as the other two. Still, Selenium starts at zero, and the other two start with a budget.
What does WebDriver BiDi change for Selenium?#
WebDriver BiDi adds a WebSocket to a W3C WebDriver session, so Selenium can stream console, network and error events that once needed the Chrome DevTools Protocol. BiDi is short for bidirectional, and so it closes the oldest gap between Selenium and the protocol-based tools.
Selenium's BiDi page says "It adds a WebSocket connection to WebDriver", a channel that stays open both ways. The same page says the Selenium project wrote the standard "together with the browser vendors", so the WebDriver session a Selenium suite already opens is where the events arrive. Once it is on, scripts can react to network requests, console messages and JavaScript errors as they happen. Then a test turns it on with the webSocketUrl capability, which the Java client sets with options.setCapability and the C# client exposes as UseWebSocketUrl. Before BiDi, each command was a request and a reply, and live events needed CDP. It also calls BiDi "the cross-browser replacement for the Chrome DevTools Protocol".
Still, the standard keeps moving. The W3C WebDriver BiDi draft is a Working Draft dated 8 October 2026. Meanwhile, WebdriverIO is a test framework built on WebDriver. Its protocols page says it starts a local session "using the WebDriver Bidi protocol" by default. In short, the event gap is closing on a shared standard rather than on one vendor's protocol.
How many teams install each tool?#
npm counts 418,027,999 playwright downloads, 26,211,545 cypress and 8,480,303 selenium-webdriver from 5 September to 4 October 2026, and a download is an install, not a team. So those are the counts npm, the JavaScript package registry, returns from its downloads API (application programming interface) for that 30-day window.
On npm's counts for 5 September to 4 October 2026, the playwright package was downloaded about 16 times as often as cypress. It was also downloaded about 49 times as often as selenium-webdriver, on our arithmetic from npm's figures. But that is not market share. Because every CI (continuous integration) run that installs a package adds a download, a package pulled in by another tool counts too.
Show data table
| Item | Value |
|---|---|
| playwright | 418,027,999 |
| puppeteer | 48,129,992 |
| cypress | 26,211,545 |
| webdriverio | 11,338,452 |
| selenium-webdriver | 8,480,303 |
A download is an install, not a team.
Also, the count misses most Selenium users. Selenium's Java, Python and C# clients live in other registries, so npm sees only its JavaScript client. For that reason, the number shows where JavaScript projects are moving, but it cannot rank the tools for a Java team.
What are Playwright's pros and cons, and what are the alternatives?#
Playwright gives one API across Chromium, Firefox and WebKit in JavaScript, Python, Java and .NET, and its cost is browser builds pinned to each release plus a weaker Chromium-only attach. So both the Playwright pros and cons come from owning its protocol and its browsers.
First, the pros follow from that ownership. Its intro page says it "supports Chromium, WebKit and Firefox on Windows, Linux and macOS". Also, it bundles its own test harness, assertions, isolation and parallel runs in one install. And the pinned builds mean a test behaves the same on a laptop and in CI.
But the cons come from the same place. First, you test its browser builds, not the Safari app your users run. Second, the pins move fast. Its release notes, read on 30 October 2026, list Chromium 139 for version 1.54 and Chromium 156 for version 1.64. Third, attaching to a running Chromium over CDP is "significantly lower fidelity", in its own words. Finally, its Selenium Grid page marks that link experimental. It says the link "only works for Google Chrome and Microsoft Edge".
Show data table
| Item | Value |
|---|---|
| Playwright 1.54 | 139 |
| Playwright 1.55 | 140 |
| Playwright 1.56 | 141 |
| Playwright 1.57 | 143 |
| Playwright 1.58 | 145 |
| Playwright 1.59 | 147 |
| Playwright 1.60 | 148 |
| Playwright 1.61 | 149 |
| Playwright 1.62 | 151 |
| Playwright 1.63 | 153 |
| Playwright 1.64 | 156 |
Chromium moved seventeen majors across eleven releases, so a pinned suite meets a new browser at nearly every upgrade.
Which tools are the alternatives?#
The alternatives to Playwright are the other two mechanisms, plus two more tools. First, Selenium is the pick for real Safari, a Grid you already run, or a language it lacks. Then the in-page runner fits a JavaScript app tested one tab at a time, where reading the app's own state helps. Beyond those, WebdriverIO runs on WebDriver and BiDi. And Puppeteer, a Node library that drives Chrome, gives the multi-tab plugin above its name. In npm's counts for 5 September to 4 October 2026, puppeteer had 48,129,992 downloads and webdriverio 11,338,452.
| What the docs show | |
|---|---|
| Pros | One API across Chromium, Firefox and WebKit in JavaScript, Python, Java and .NET; its own test harness, assertions, isolation and parallel runs in one install |
| Cons | Pinned builds rather than the Safari app; a lower-fidelity CDP attach for Chromium only; an experimental Selenium Grid link for Chrome and Edge |
| Alternatives | Selenium for real Safari, a Grid you already run, or a language it lacks; Cypress for a JavaScript app tested one tab at a time; WebdriverIO on WebDriver and BiDi; Puppeteer, a Node library that drives Chrome |
What does a first test look like in each tool?#
The same check, open a page and confirm what it shows, fits in one short file per tool, and the shape of each file shows its mechanism. And each file below is cut down from that project's own first-test docs.
// 1. Selenium WebDriver (npm install selenium-webdriver): an explicit driver session
const { Builder, Browser } = require('selenium-webdriver');
const assert = require('assert');
(async function firstTest() {
const driver = await new Builder().forBrowser(Browser.CHROME).build();
try {
await driver.get('https://www.selenium.dev/selenium/web/web-form.html');
assert.equal('Web form', await driver.getTitle());
} finally {
await driver.quit();
}
})();
// 2. Cypress (a spec file in the Cypress app): commands queued inside the browser
describe('My First Test', () => {
it('finds the content "type"', () => {
cy.visit('https://example.cypress.io');
cy.contains('type');
});
});
// 3. Playwright Test (tests/example.spec.ts, after npm init playwright@latest)
import { test, expect } from '@playwright/test';
test('has title', async ({ page }) => {
await page.goto('https://playwright.dev/');
await expect(page).toHaveTitle(/Playwright/);
});When you read them side by side, the three shapes show the three mechanisms. First, the Selenium file opens and closes a driver session by hand. Then the second file queues commands, and cy.contains fails on its own if the text never shows. And the third file is handed a page and waits inside the assertion.
Which official pages should the first suite be built from?#
Build from the project's own docs in order: install, first test, browsers, then waits or timeouts, because those four pages decide whether the first suite is stable. So each list below is in build order.
For Playwright:
For Cypress:
For Selenium:
When is each one the wrong tool?#
Cypress is the wrong tool for a flow that drives two browsers at once, and Playwright is the wrong tool for a suite that must run on an existing Selenium Grid beyond Chrome and Edge. And Selenium has its own wrong case too.
First, take a chat app where two users talk in two browsers. The in-page framework's trade-offs page, read in October 2026, rules it out, since it cannot control "more than 1 open browser at a time". So use an outside driver instead: two browser contexts in one test of the pinned-build tool, or Selenium with two drivers.
Next, take a team with a large Selenium Grid that runs Firefox and Safari nodes. However, the Playwright automation tool links to a Grid only for Chrome and Edge, and it marks that link experimental. So keep that suite on Selenium, and add BiDi where you need live events.
Finally, take a small JavaScript team that wants waits handled for them and has no time to write a wait helper. Because Selenium's implicit wait starts at 0, every timing gap is theirs to close. So either retrying tool will serve them better, because both retry by default.
| Case | Ruled out | Better fit |
|---|---|---|
| A chat app where two users talk in two browsers | Cypress: it cannot control more than 1 open browser at a time | Two browser contexts in one Playwright test, or Selenium with two drivers |
| A large Selenium Grid with Firefox and Safari nodes | Playwright: its Grid link covers only Chrome and Edge, marked experimental | Keep the suite on Selenium, and add BiDi for live events |
| A small JavaScript team with no time to write a wait helper | Selenium: its implicit wait starts at 0 | Either retrying tool, because both retry by default |
Where to go from here#
For tool selection beyond the browser, read the guide to choosing testing tools; for the suite itself, QA software engineers and a CI pipeline are the next two steps. And each route below stands on its own.
The wider question of which tools a project needs at all is in choosing the right testing tools for your project. Since good automation starts from clear cases, a beginner's guide to clear and effective test cases covers how to write them. For accessibility checks inside the chosen framework, see web accessibility testing tools. Where AI (artificial intelligence) and ML (machine learning) sit beside a scripted suite is in AI and ML in software testing.
If you want people to build the suite, QA (quality assurance) software engineers can write and run it. Then, to run it on every pull request, a CI and CD (continuous delivery) pipeline is the next step. Either way, the docs listed above are enough to settle Cypress vs Playwright vs Selenium and ship a first suite on your own.