Playwright vs Selenium vs Cypress
How Playwright, Selenium, Cypress, WebdriverIO and Puppeteer handle iframes, shadow DOM, drag and drop, downloads and auth — with a page for each.
Fifteen situations, and what Playwright, Selenium, Cypress, WebdriverIO and Puppeteer each ask of you. Every row links to a page on this site that reproduces it. Reviewed 2026-09; treat the vendor's current documentation as authoritative.
The frameworks
| Framework | Languages | How it drives the browser |
|---|---|---|
| Playwright | JS/TS, Python, Java, .NET | Runs out of process, drives the browser over CDP/WebDriver BiDi |
| Selenium | Java, Python, C#, JS, Ruby, more | W3C WebDriver, the interoperable standard across browsers and grids |
| Cypress | JS/TS | Runs inside the browser alongside the application under test |
| WebdriverIO | JS/TS | WebDriver and BiDi with its own runner and service ecosystem |
| Puppeteer | JS/TS | Chrome DevTools Protocol, Chromium-first with Firefox support |
Situation by situation
| Situation | Playwright | Selenium | Cypress |
|---|---|---|---|
| Waiting for an element | Actions auto-wait for visible and actionable | Explicit WebDriverWait with named expected conditions | Commands retry for presence; visibility is a chained assertion |
| Native <select> | selectOption | Select helper class | cy.select |
| Custom listbox widget | Ordinary clicks, role-based locators | Ordinary clicks — the Select helper does not apply | Ordinary clicks and assertions on aria-selected |
| HTML5 drag and drop | Built-in drag often works; synthetic events are the fallback | Actions API frequently needs synthetic dragstart/drop | Needs a plugin or hand-dispatched events |
| Native dialogs (alert/confirm/prompt) | page.on("dialog"), registered before the click | switch_to.alert | Auto-accepts by default; stub the window event to assert |
| iframes | frameLocator | switch_to.frame and back to default_content | No native switching — reach through the frame document |
| Open shadow DOM | CSS locators pierce open roots by default | getShadowRoot() on the host (Selenium 4) | includeShadowDom option |
| New windows and tabs | Wait for the popup event on the context | Switch by window handle | Not supported — assert the link target instead |
| File upload | setInputFiles | send_keys(path) on the input | selectFile |
| File download | Download event with a suggested filename | Browser preferences plus filesystem assertions | Downloads folder plus filesystem assertions |
| Request interception | page.route | CDP or WebDriver BiDi network interception | cy.intercept |
| HTTP Basic Auth | httpCredentials on the browser context | CDP/BiDi auth handler; URL credentials are being restricted | auth option on cy.visit or a request header |
| HTTP Digest Auth | Not covered by httpCredentials — compute the response | Not covered — compute the response or use an HTTP client | Not covered — compute the response or use cy.request |
| Asserting a status code | Read the Response from navigation or APIRequestContext | Use an HTTP client alongside the driver, or BiDi | cy.request returns the status directly |
| Cookies and web storage | Context cookies plus storageState | Driver cookie API; storage via script execution | cy.getCookie, cy.clearLocalStorage |
What this site is for
Fun UI Playground is a target, not a framework: a stable set of pages that reproduce the situations above on demand, so you can practise a technique or reproduce a failure without standing up an application first. Every interactive element carries a documented id and data-testid, and every page states what is deterministic and what is not.
What it is not
It is not a benchmark, and nothing here measures one framework against another. The table is a map of where the same task needs a different approach, so that a test failing on this site tells you something about the technique rather than about the page.
How to use it with any framework
Point your runner at a challenge URL, locate by the documented id, data-testid or role, and assert the outcome the page publishes — the site-wide flash element, a URL change, or a named result element. The Getting Started guide has the same login test written five times, one per framework.
Updated
Fun UI Playground — all challenges, bug hunts, getting started, glossary, about.