If you upgraded to WebdriverIO v9 and your existing $()/$$()-based tests kept passing without a single line change, that's real — but it's easy to miss that the protocol actually driving the browser underneath those calls changed. WebdriverIO now uses the WebDriver BiDi protocol by default for every new session, not the classic WebDriver protocol it used before.
Why this isn't just a version bump
Classic WebDriver is a synchronous, request-response HTTP protocol: your test sends a command, waits for a response, sends the next command. It's been the standard for a decade and it works, but it was never designed for the browser to proactively tell your test about things happening on its own — a new tab opening, a console message being logged, a network request firing.
BiDi ("bidirectional") is a WebSocket-based protocol that lets the browser push events to your test session as they happen, not just respond when asked. That structural difference is what unlocked several v9 features that would have been awkward or impossible to build cleanly on classic WebDriver:
- Cross-browser request mocking that actually works outside Chromium, because interception can now hook into the browser's real network layer instead of a CDP-specific one
addInitScript, which injects a script into every new browsing context and can stream data back to your test as that script runs, rather than a one-shot inject-and-forget- Fake timers (
browser.emulate('clock', ...)) for controlling time-dependent code, which needs the browser to cooperate with the test session in a way request-response polling doesn't support well - Automatic dialog suppression — browser dialogs no longer block execution by default; you opt into handling them via an event listener instead of the test hanging until you manually accept/dismiss
None of these are exotic edge-case features. They're the kind of thing you'd reach for in an actual test suite the moment you need to mock a third-party API in Firefox, or verify a UI updates correctly after some time-dependent debounce logic.
The opt-out, if you need it
If your setup depends on classic WebDriver behavior — some driver-server combinations, corporate proxy setups, or CI infrastructure not yet BiDi-compatible — there's an explicit escape hatch:
export const config: Options.Testrunner = {
capabilities: [
{
browserName: 'chrome',
'wdio:enforceWebDriverClassic': true,
},
],
// ...
};
Set that capability and WebdriverIO falls back to the old protocol for that session, no other config changes required.
What I'd actually check before assuming this is invisible
"Your existing tests still pass" and "nothing about your test environment changed" are different claims. If your CI runs behind a proxy that only expects HTTP request-response traffic, or your Selenium Grid / cloud provider setup hasn't caught up to BiDi support, this is worth verifying explicitly rather than assuming it's fine because the test suite went green. A quick way to confirm which protocol a session actually negotiated: check the session capabilities WebdriverIO logs at startup with logLevel: 'debug' — it'll show whether BiDi was actually established or silently fell back.
