Jump to content

Connect SuperML | Leeroopedia MCP: Equip your AI agents with best practices, code verification, and debugging knowledge. Powered by Leeroo — building Organizational Superintelligence. Contact us at founders@leeroo.com.

Principle:Webdriverio Webdriverio WebDriver Bidi Protocol

From Leeroopedia
Knowledge Sources
Domains Bidi_Protocol, WebDriver
Last Updated 2026-02-12 00:00 GMT

Overview

Enabling bidirectional, real-time communication between a test framework and a browser through a persistent WebSocket connection for event subscription, script evaluation, and network interception.

Description

Traditional WebDriver communication follows a strict request-response model over HTTP, where the test framework sends a command and waits for the browser to return a result. The WebDriver Bidi Protocol introduces a bidirectional channel over WebSocket that allows the browser to push events to the test framework in real time, without polling. This enables capabilities such as subscribing to console log events, intercepting network requests before they are sent, evaluating JavaScript in specific browsing contexts, and receiving DOM mutation notifications. The protocol defines a typed message format with command, result, and event message types, each carrying structured payloads. The framework must manage connection lifecycle, message routing, subscription state, and type-safe deserialization of both local and remote object references.

Usage

This principle applies when tests need to observe or intercept browser behavior that is not accessible through traditional WebDriver commands. It is the right choice for scenarios such as: capturing browser console output in real time, intercepting and modifying network requests (for mocking or authentication injection), listening for page lifecycle events (navigation, load, unload), evaluating scripts in specific frames or workers, and receiving notifications about dialog appearances or download starts. It is essential for modern testing scenarios that require low-latency, event-driven interaction with the browser.

Theoretical Basis

The Bidi protocol is built on a publish-subscribe messaging pattern over a persistent WebSocket connection:

  • Connection establishment: During session creation, the browser advertises a WebSocket URL. The framework opens a persistent connection to this endpoint, which remains active for the duration of the session.
  • Message framing: All messages are JSON-encoded and follow a discriminated union structure:
    • Command messages (client to browser): Carry a unique numeric id, a method string (e.g., script.evaluate, network.addIntercept), and a params object.
    • Result messages (browser to client): Echo the command id and carry either a result object or an error object.
    • Event messages (browser to client): Carry a method string and params object, but no id, representing asynchronous notifications.
  • Command correlation: Each outgoing command is assigned a monotonically increasing ID. The framework maintains a map of pending command IDs to promise resolvers, enabling async/await style usage. When a result message arrives, its ID is used to look up and resolve the corresponding promise.
  • Event subscription: The framework sends session.subscribe commands specifying which event categories to receive (e.g., log.entryAdded, network.beforeRequestSent). The browser then pushes matching events as they occur. Event handlers are registered locally and dispatched based on the event method name.
  • Type system: The protocol defines local types (values sent from client to browser, such as script source and arguments) and remote types (values received from browser, such as remote object references with handles for later interaction). This distinction ensures type safety and prevents accidental misuse of browser-internal references.

Pseudocode for Bidi message handling:

function sendCommand(method, params):
    id = nextCommandId++
    promise = createDeferredPromise()
    pendingCommands[id] = promise
    websocket.send(serialize({id, method, params}))
    return promise
function onMessage(raw):
    message = deserialize(raw)
    if message has id:
        pendingCommands[message.id].resolve(message.result)
        delete pendingCommands[message.id]
    else:
        eventEmitter.emit(message.method, message.params)

Related Pages

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment