QAAll levels

Selenium

Selenium is an open-source set of browser automation tools built on W3C WebDriver, a standard that browser vendors implement in drivers of their own. The page opens with the twelve journeys from End-to-end testing written in Selenium — who supplies each — and the one run only Selenium can add to the shop’s suite: the browsers customers actually install, Safari on an iPhone included. Then the protocol and its drivers, waiting, sessions and the lane one Mac sets, WebDriver BiDi, the grid and its open door, five changes underneath working suites, and where it does not help.

Current version
4.51 · October 2026
The browser is
The one customers run
Isolation unit
A session: a browser

The shop’s twelve journeys in Selenium on one screen — and who supplies each

Selenium does less than the other two tools on this site, and it does so on purpose. It drives a browser and stops there. So the useful question is who does the rest. Below are the twelve journeys from the End-to-end testing page — the same online shop — as the shop would write them in Selenium. The fourth column says how. The fifth says who supplies it: WebDriver, the standard every browser vendor implements; WebDriver BiDi, its newer two-way channel; the language the tests are written in; or the test environment.

#JourneyWhat it needs from the toolIn SeleniumSupplied by
1Search “blue jumper”, open SKU 30417, pay with the test card, get an order numberTyping into the card fields, which live in the payment provider’s iframe on another originswitchTo().frame() enters the provider’s frame and defaultContent() leaves it — the driver works at the browser’s level, so the frame’s origin does not matter; the order number is an explicit waitWebDriver
2Sign up, confirm the email, sign in, reload — still signed inThe same test in the Safari customers have — on a Mac and on an iPhoneApple’s own safaridriver drives the shipped Safari on macOS, and Safari on an iPhone plugged into that Mac with platformName: 'ios' — line 2.3Apple’s driver, through the grid
3Reset a forgotten password from the emailed linkReading a test inbox the browser never seesNothing from Selenium: the language’s HTTP client asks the inbox’s API, a FluentWait polls it until the email is there, and get() follows the linkYour language
4Apply SPRING10, change the quantity, payComparing the screen with what the provider was asked to chargeAn HTTP client reads the charge from the sandbox; the total on the page is read again after the basket re-renders, or it is a stale element (section 3)Your language
5Pay with a card that needs 3-D Secure: out to the bank’s check and backLeaving the shop’s origin and coming back into the same sessionNothing special: the session follows the browser across origins as a person does, and its cookies come back with itWebDriver
6A card is declined; pay again with a second cardExactly one order afterwardsAn HTTP client counts the customer’s orders after the second paymentYour language
7Ask the order assistant “where is my order” about the order just placedThe assistant’s model, replayed from a recording — but the model is called by the serverThe replay is set in the environment, as in every tool; Selenium waits for the status on the screenThe environment
8Start a return on day 30; the returns label arrives by emailDay 30 of the return window, in the browser and on the serverWebDriver has no clock. A BiDi preload script fixes Date before the shop’s code runs — in Chrome, Edge and Firefox, not in Safari; the server’s test clock is set through its own APIBiDi and the environment
9The warehouse confirms the return arrived; the refund landsSending the warehouse’s webhook, then waiting for an emailAn HTTP client posts the webhook; a wait polls the inbox with a timeout written for it, never a sleepYour language
10Change the delivery address after ordering, before dispatchReading what the warehouse receivedOrdinary commands in the browser, then an HTTP client reads the warehouse’s test endpointYour language
11Check out as a guest, then create an account from the confirmation pageStarting genuinely anonymousA new session is a new browser with a clean profile, so nothing journey 10 left behind can reach it — at the price of a browser launchWebDriver
12A support agent refunds a damaged item in the back officeA second identity, already signed inA second session. The agent signs in through the API; the test opens a small page on the back office’s domain and adds the session cookie — WebDriver refuses a cookie for any other domainWebDriver and your language

The table answers whether Selenium can run the shop’s journeys. It can, all twelve. The second half of this screen answers a question only Selenium can: do the journeys work in the browsers customers actually installed? Playwright runs builds of its own — its is not Safari — which is why the Playwright page leaves journey 2 in the shipped Safari to a person with an iPhone before each release. The run below closes that gap. It is four journeys in five browsers on one grid, every night, written down as the shop’s decisions and numbered so later sections can cite “line 2.3”.

  1. 1.What runs

    1. Journeys 1, 2, 5 and 11 — the four whose outcome depends on how the browser itself treats , frames and other origins. The other eight run in Playwright only.
    2. Every night and before each release, after the Playwright merge suite is green. A red night never blocks a merge; it opens a ticket.
    3. In the branded Chrome, Edge and Firefox customers install, in Safari on macOS — and journey 2 also on an iPhone.
  2. 2.Where it runs

    1. One grid inside the shop’s network. The router has a username and password, and no port of it is reachable from the internet.
    2. Two Linux nodes from the project’s Docker images, with Chrome, Edge and Firefox, four sessions each.
    3. A Mac mini with one Safari slot, because Safari allows one session at a time, and an iPhone plugged into it — trusted, unlocked, with Remote Automation on. The iPhone shares that one slot.
  3. 3.How a session is made and waited on

    1. A new session for every journey. No session is ever shared between two.
    2. No implicit wait anywhere. Every wait is explicit: 10 seconds, polled every 250 ms, with a message that names the condition.
    3. Elements are found by data-testid first and a short CSS selector second. No copied from the browser’s tools.
  4. 4.Versions, and when one fails

    1. Selenium is pinned to an exact version and upgraded in a change of its own. Selenium Manager resolves each driver; the Linux nodes pin browserVersion and move it on purpose.
    2. A failure keeps a screenshot and the page source, and in Chrome, Edge and Firefox the console log through BiDi. Safari gives the first two and the driver’s own log.
    3. A red journey is read against Playwright’s result for the same journey. Green there and red here means the browser, not the shop, and the ticket names the browser’s version.

A protocol with three owners, and one round trip per command

Understanding Selenium starts with the fact that it is not one program. Your test calls a binding, the client library in your language. The binding sends each command as an ordinary HTTP request carrying — to find the pay button, a POST to /session/{id}/element with {"using": "css selector", "value": "[data-testid=pay]"}. A driver, a program written by the browser’s vendor, receives it, makes the browser do it and answers. Every command is a round trip, and nothing comes back that the test did not ask for.

The middle of that chain is a W3C standard: WebDriver became a Recommendation on 5 June 2018, and its next level is a Working Draft, last updated on 2 July 2026. So the chain has three owners. You own the test. The Selenium project owns the bindings, the grid and Selenium Manager, the tool that fetches drivers. The browser vendors own the drivers and the browsers. Because the wire format is public, either end can be swapped, and Apple’s own page says so: tests can be written “in Python, Java, PHP, JavaScript, and any other language whose library is compatible with the W3C WebDriver protocol”.

Selenium is a protocol with three owners. Your test calls a binding from the Selenium project; the binding sends each command as an HTTP request carrying JSON to a driver written by the browser’s vendor, which makes the browser act and answers. One request, one answer: nothing comes back unasked, which is why waiting is the test’s job. The middle is a W3C standard, so either end can be swapped. And because the driver is the vendor’s — chromedriver, msedgedriver, geckodriver, and Apple’s safaridriver, which also drives Safari on an iPhone plugged into the Mac — the browser under test is the one customers run, not a build patched for testing. WebDriver BiDi adds a channel back for console messages, network requests and errors; as of October 2026 Chrome, Edge and Firefox support it, and Safari supports none of it.

The driver being the vendor’s is the fact that matters most. Playwright drives Firefox and WebKit through builds it patched itself; a WebDriver driver drives the browser as the vendor ships it. The table is what that means browser by browser.

BrowserDriver, and who ships itHow you get itWorth knowing
Chromechromedriver, GoogleSelenium Manager downloads the one that matches; since Chrome 115 drivers are published with Chrome for TestingBranded Chrome updates itself — every two weeks since Chrome 153 in September 2026. Set browserVersion and Selenium Manager fetches that exact Chrome for Testing build instead
Edgemsedgedriver, MicrosoftSelenium ManagerThe same Chromium engine with Microsoft’s browser around it — sign-in, policies, the features a corporate customer has switched on
Firefoxgeckodriver, MozillaSelenium ManagerThe branded Firefox — the one Playwright cannot drive. It is also the browser that moved first from the DevTools protocol to WebDriver BiDi
Safari on macOSsafaridriver, AppleInstalled with the operating system; switched on once with safaridriver --enableApple: “Only one Safari browser instance can be active at any given time, and only one WebDriver session at a time can be attached to the browser instance.” One Mac, one Safari session
Safari on iPhone and iPadThe same safaridriver, on a Mac the device is plugged intoOn the device: Settings → Safari → Advanced → Web Inspector and Remote Automation; in the test: platformName: 'ios'Since iOS 13 (2019). The device must be trusted and unlocked, and the software keyboard is suppressed during a session — typing is simulated, not tapped

Waiting is yours to write: what WebDriver checks once, and what it never checks

People who come from Playwright expect a click to wait until clicking makes sense. A WebDriver click does not. The standard’s Element Click scrolls the element into view and checks two things, once. If the element is still out of view, the answer is element not interactable. If something else covers it, the answer is element click intercepted. Either way the error comes back at once; nothing is retried. Navigation waits only until the document’s readyState is “complete”, and an application that renders after that — almost any modern shop — is not waited for at all.

So the shop’s journeys meet the standard’s errors in predictable places, and each error has one fix that works and one that hides it.

ErrorWhat the standard saysWhere the shop meets itThe fix
no such elementThe element “could not be located on the page using the given search parameters”Journey 1: reading the order number before the confirmation page has renderedAn explicit wait for that element. Not a sleep, not an implicit wait
stale element referenceThe element “is no longer attached to the DOM”Journey 4: the total found before SPRING10 re-rendered the basket, read after itFind it again inside the wait. Keep locators, not found elements, in page objects
element click interceptedThe element receiving the click “is obscuring the element that was requested clicked”Journey 11: the cookie banner still over “Create account”Wait for the banner to go, or accept it in the journey’s setup. Never a click through JavaScript, which clicks what a person could not
element not interactableThe element is not “pointer- or keyboard interactable”Journey 5: the bank’s “Confirm” while its 3-D Secure panel is still sliding inWait for the element to be clickable, which checks it is displayed and enabled
invalid cookie domainAn attempt to “set a cookie under a different domain than the current page”Journey 12: adding the agent’s session cookie before opening the back officeOpen a small page on that domain first — the documentation suggests the 404 page
session not created“A new session could not be created.”Any journey, the morning after Chrome updated itself and the driver on the machine did notLet Selenium Manager resolve the driver, and pin browserVersion where the run must be reproducible (line 4.1)

Selenium offers two mechanisms for waiting, and only one of them is safe. An implicit wait is a setting for the whole session: every element lookup keeps retrying for up to that long before it fails, and the default is zero. An explicit wait is a loop in the test: it polls a named condition — the element is visible, it is clickable, the text has changed — until the condition holds or the timeout fires, and the timeout message names what was being waited for. The third approach, a fixed sleep, is not a mechanism at all. It is a guess that is too short on a loaded machine and wasted on a fast one.

The example from Selenium’s documentation: an implicit wait of 10 seconds and an explicit wait of 15, for an element that never appears. The explicit wait is a loop that calls find and sleeps half a second; with an implicit wait set, each find itself searches for up to 10 seconds. So the finds block from 0 to 10 and from 10.5 to 20.5 seconds, the deadline passes in the middle of the second, and the loop notices only when it returns: the wait fails at 20.5 seconds, not 15. With the implicit wait at zero the same wait fails at exactly 15.0 seconds, or finds an element that appears at 6.2 seconds on the poll at 6.5. That is why the documentation says never to mix the two.

The second habit that decides whether a suite ages well is how elements are found. A By in Selenium is a in the full sense: a recipe, resolved each time it is used. What findElement returns is not. It is a reference to one node, and when the page re-renders that node is gone — journey 4’s stale total. So hold By values and find on demand. As for which recipe, the documentation prefers an id where ids are unique and stable, then “a well-written CSS selector”, and it warns that XPath selectors “are typically not performance tested by browser vendors and tend to be quite slow”. The shop adds an attribute for tests (line 3.3): find an element by what it is for, not by where it sits.

A session is a browser — isolation, parallel runs, and the lane one Mac sets

Selenium has no unit of isolation smaller than a session, and a session is a browser process with a fresh profile. The shop opens a new one for every journey (line 3.1). That is why journey 11 is free: a new session cannot see what journey 10 left behind. Every journey pays a browser launch — about two seconds in Chrome on the shop’s nodes, four in Safari, twenty on the iPhone.

A second identity is therefore a second browser. Journey 12 opens one for the agent and has nothing like a saved storage state to start it with. What it has is the cookie. The agent signs in through the , the test opens a small page on the back office’s domain and adds the session cookie, and from then on the agent is signed in. The order matters: WebDriver refuses a cookie for a domain the browser is not on. With BiDi, storage.setCookie sets one without the detour — in Chrome, Edge and Firefox.

Running tests in parallel is not Selenium’s job either. The test runner does it — JUnit, TestNG, pytest with its parallel plugin, NUnit — and each test opens its own session on the grid. The grid decides how many run at once, through slots: one per processor core for Chrome-family browsers and Firefox on a node, and, the documentation says, “For Safari, only one slot is created.” That one sentence is the shape of the shop’s night.

The shop’s nightly run of journeys 1, 2, 5 and 11 in the browsers customers install. A session first starts its browser: about 2 seconds on the Linux nodes, 4 in Safari, 20 on the iPhone. The Linux nodes have four slots each, so Chrome’s and Edge’s eight sessions start at once and Firefox’s four wait in the grid’s queue until slots free at 62 and 72 seconds; Linux is done at 154. The Mac mini has one slot, because Safari allows one session at a time, and the iPhone shares it: four journeys in Safari, then journey 2 on the iPhone, end at 396 seconds. The night lasts as long as the Mac; a second Mac would end it at 238 seconds.

WebDriver BiDi: the channel the protocol never had — and the browser that does not speak it

Classic WebDriver is request and response, so the browser can never speak first. It cannot say that a request failed, that a script threw an error or that the console printed something. For years the workaround was the , which only Chrome-family browsers speak well. WebDriver BiDi is the standard answer. It adds a to the session, over which the browser sends events as they happen, and it adds commands classic never had. The Selenium documentation describes it as “created by the Selenium project together with the browser vendors” and as “the cross-browser replacement for the Chrome DevTools Protocol”. It is a W3C Working Draft, last updated on 8 October 2026, and a test switches it on with the webSocketUrl capability.

ModuleWhat it addsWhere the shop would use it
logConsole messages and JavaScript errors, as they happenKept with every failure (line 4.2)
networkEvery request seen, intercepted, answered or failedBlocking analytics, maps and the marketing chat, as Playwright’s line 1.3 does
scriptA script preloaded before the page’s own, in every frameJourney 8: Date fixed at day 30 before the shop’s code reads it
storageCookies read and set in any partition, without visiting the siteJourney 12 without the detour through the 404 page
emulationLocale, time zone, geolocation and screen overridden per sessionA fixed time zone and locale, as Playwright’s line 1.2

All five work in Chrome and Edge from version 126 — emulation from 137 — and in Firefox. That is the part a plan has to absorb, because the list stops there. As of MDN’s compatibility data of 15 October 2026, Safari supports no part of WebDriver BiDi — the requests to add preload scripts and cookie commands are open in WebKit’s tracker. The browser that is the main reason to run this grid is the one browser on it where the new features do not exist. Everything on the Safari lane uses classic WebDriver: screenshots, page source and the driver’s own log.

Elsewhere the move is already done. Mozilla deprecated its support for the DevTools protocol from Firefox 129, telling users in May 2024 to migrate to BiDi, and Selenium removed its DevTools support for Firefox in 4.29, in February 2025.

Grid: the same protocol with a router in front — and a door that is open by default

Selenium Grid puts a router in front of the same protocol. The test changes one thing, the address it connects to, and the grid decides which machine runs the session. It has six parts, and since Grid 4 each one can run as its own process.

PartWhat it doesIn the shop’s night
RouterThe one entry point. A new-session request goes to the queue; a command for an existing session goes straight to the node that holds itBehind a username and password, unreachable from the internet (line 2.1)
New Session QueueHolds new-session requests first in, first out, and rejects one that waits past its timeoutFirefox’s four sessions wait here until Chrome and Edge free a slot
DistributorKnows every node and its free slots, takes the first request a free slot can serve, and creates the session thereSends every Safari request to the Mac, one at a time
Session MapRecords which node runs which session, so the router can forward each commandLooked up on every command of the night
NodeA machine with browsers and drivers, offering slots; it only runs commands. It can also relay to another WebDriver endpoint — Appium or a cloud providerTwo Linux nodes with four slots each; the Mac with one (lines 2.2, 2.3)
Event BusCarries the messages between the other parts — a node registering, a session endingInvisible to the tests; the first process started in a fully distributed grid
Selenium Grid puts one address in front of many machines. The first request of a session goes from the router to the New Session Queue, first in, first out; the Distributor takes the first request a free slot can serve, creates the session on that node and records it in the Session Map. Every later command goes to the router, which looks the session up and forwards the command straight to the node. The shop’s grid has two Linux nodes with four slots each and a Mac mini with one Safari slot and the iPhone. The router has no password until one is set, because the project assumes a private network; in July 2024 Wiz described SeleniumGreed, which opened sessions on grids left open on port 4444 and had their nodes start Python instead of a browser.

All six can run as one process (standalone), as a hub with nodes, or fully distributed, and the project publishes Docker images for each. What none of those modes changes is the first sentence of the security story.

That sentence is Selenium’s own: “Selenium Grid by default doesn’t have any authentication as the assumption has always been that we want you to put this behind a secure network”. On 25 July 2024 Wiz Research published what happens when it is not. A campaign it named SeleniumGreed was opening sessions on grids exposed to the internet and, in the session’s Chrome options, setting the browser’s binary to Python and its arguments to a script. The node started Python instead of Chrome, the script opened a reverse shell, and the machine was mining cryptocurrency. Wiz counted over 30,000 exposed grids, half of them on the old 3.141.59 and half newer, most on the default port 4444, and confirmed the technique on version 4 as well. Nothing was exploited in the usual sense. A grid runs the program its client asks for — that is its job.

Five changes underneath working suites

A Selenium suite depends on three parties at once: the project, the drivers and the browsers. Each of these changes was announced in advance by the people who made it, and each sorted the suites that let the project manage drivers and pinned versions on purpose from the ones that downloaded binaries by hand.

ChangeWhat it broke or exposedWhat protected a suite
Selenium 4.0, 13 October 2021: the W3C protocol only; the older JSON Wire Protocol is goneCapabilities not shaped to the standard could stop a session from starting; anything browser-specific needs a vendor prefix such as goog:chromeOptionsThe browser’s Options classes instead of hand-built capability maps
Chrome 115, 18 July 2023: ChromeDriver moves to Chrome for Testing’s endpointsScripts and driver managers that fetched from the old address found no driver for the new Chrome; the download page now opens with “If you’re using Chrome version 115 or newer, consult the Chrome for Testing availability dashboard”Selenium Manager, bundled since 4.6 in November 2022, which from 4.11 on 31 July 2023 also downloads Chrome for Testing itself
Firefox 129, 2024, and Selenium 4.29, February 2025: the DevTools protocol for Firefox deprecated, then removed from SeleniumCode that reached Firefox through Selenium’s DevTools classes stopped working; Selenium’s own post says that implementation “was always partial”The high-level BiDi APIs, which work in Firefox and in Chrome alike
Chrome 132, 14 January 2025: --headless=old removed from ChromeSuites on the old headless mode got an error, or the new mode and new screenshots; the old mode survives only as a separate binary, chrome-headless-shellChoosing the headless mode by name in the browser options — the old one now as chrome-headless-shell — and screenshot baselines treated as belonging to a browser build
Chrome 153, 8 September 2026: a new stable Chrome every two weeks instead of fourTwice as many browser updates under every node, and twice as many mornings when an unpinned machine runs a browser nobody choseSelenium Manager, which resolves the matching driver at every session start — and browserVersion pinned where a run must be reproducible

Where it does not help, and how it compares

Selenium answers a narrower question than it used to. For a new suite with no constraint pushing elsewhere, Playwright does more for less work, and its page says so. Saying it here is what makes the cases where Selenium wins mean something.

  • It gives you a browser and nothing else. The runner, the report, the retries, the record of a failed run — a team chooses and wires each one itself.
  • Waiting is code you write, and an inherited suite has usually written it wrong.
  • Safari under automation starts from a clean slate, like a private window, behind a glass pane. That is close to a first visit, not to a customer with a year of history, and on an iPhone the on-screen keyboard is not shown at all.
  • The new features stop where this page’s best argument begins: Safari speaks no BiDi.
  • It does not test native apps — Appium does, over the same protocol — and it is not a load testing tool.

Selenium is the right call when

  • The question is the browsers customers ship — branded Chrome, Edge and Firefox, Safari on a Mac and on an iPhone.
  • The tests are written in Ruby, PHP or another language no other tool ships, or the team already lives in a Selenium stack.
  • The system under test sits inside a network only your own grid can reach.

Prefer Playwright or Cypress when

  • The suite is new, in JavaScript, Java, Python or .NET, and nothing forces a browser constraint.
  • Unreliable waiting is the current pain, and checks that wait by themselves remove its largest cause.
  • You want a failed run’s full record from the pipeline without assembling it yourself.

Anti-patterns worth naming

  • A global implicit wait combined with explicit waits, which the documentation itself warns produces unpredictable wait times.
  • Absolute XPath copied out of the browser’s tools, which breaks on the next redesign and explains nothing when it does.
  • Assertions inside page objects. The documentation: “Page objects themselves should never make verifications or assertions.”
  • One session shared across tests to save launch time, which brings back exactly the order dependency a session was meant to remove.
  • A grid on a public port. See SeleniumGreed.
  • Code a coding agent wrote from a decade of old posts. Selenium’s own blog, on 29 September 2026, lists what it produces — a DesiredCapabilities object “that hasn’t existed for years, an absolute XPath copied out of DevTools, and a sleep(5)” — and adds: “It will also be describing Selenium 3.”

When to use it

Use it when

  • Checking the journeys that depend on the browser itself in the branded browsers customers install, Safari on an iPhone included.
  • Teams whose tests are written in Ruby, PHP or another language only WebDriver serves.
  • Systems inside a private network, where a grid of your own is the way to reach them.
  • Long-lived suites where an open standard is worth more than a newer API’s convenience.

Avoid it when

  • A new suite with no browser or language constraint, where a modern runner does more for less effort.
  • Teams without the appetite to write waiting, reporting and isolation themselves, since the protocol supplies none of it.
  • As a reason to put a grid where the internet can reach it, or to hide a slow suite behind more slots.
  • Anywhere an implicit wait is still in place, until it is removed — no other change makes the suite predictable.

Sources and method

What this page stands on

  • 23 sources
  • 1 documented failure
  • Checked October 19, 2026

Researched with Claude. Finding and cross-checking the primary sources was done with Claude (Anthropic). Selection, writing and fact-checking: the Anatomy of IT Product editors.

Found this useful?

Share it with someone who is working on the same problem.