A login test failed in CI at the same line three times: the submit button was found, the click was sent, and the next heading never appeared. The first instinct was to blame the selector. The useful clue came from the timeline instead. In Firefox, the page had navigated to a new browsing context; in Chromium, a console error had interrupted the form handler; in the local run, neither event was visible in the test log. The protocol question was not an abstract standards debate. It was a question about which evidence the failure needed.

Start with the user contract

WebDriver Classic remains the right starting point for a journey that should run across Chrome, Firefox, Safari, and Edge. Its value is the portable action model: open a page, locate an element, enter a value, click, and assert the result. A test that says “a customer can sign in” should not require a Chromium-only debugging domain just to perform the sign-in.

Before adding a richer protocol, write the assertion in plain language and name the evidence that is missing. If the test only needs to know whether the account page opened, keep the action in WebDriver. If the team must know whether the page threw an exception, whether a response returned 500, or whether a browsing context changed, the observation layer can be richer than the action layer.

This separation makes failures easier to explain. The product contract is portable; the diagnostic attachment is allowed to be specific. Our editorial rule is simple: a protocol feature belongs in the test because it answers a recurring failure question, not because its API looks newer.

What each surface is good at

  • WebDriver Classic is the broad contract for user journeys and regression coverage.
  • WebDriver BiDi is useful for browser events such as console output, network responses, and browsing-context changes.
  • CDP is the practical choice for Chromium-only traces, DevTools domains, and experiments with browser internals.

The boundaries matter. CDP can expose a powerful network trace, but a test that uses a CDP command to decide whether a Safari checkout works has already narrowed its claim. BiDi is designed to keep the automation model closer to the WebDriver standard, but event support still needs a browser and driver compatibility check. Classic WebDriver gives the least evidence and the widest portability. There is no protocol that gives every team all three qualities for free.

A small failure record beats a full transcript

The following is an illustrative record for the sign-in failure above. It is deliberately small enough to compare across runs.

{
  "step": "submit-login",
  "browser": "Firefox 128",
  "driver": "geckodriver 0.35",
  "protocol": "WebDriver + BiDi console",
  "last_action": "click submit",
  "last_event": "browsingContext.navigationStarted",
  "expected": "heading Account",
  "observed": "new context, no heading"
}

The record says what happened without storing every request, cookie, or page body. Add the runner version, operating system, and feature flag when those values can change the outcome. If a CDP trace is attached, label it as Chromium evidence rather than presenting it as a portable test result.

Choose by failure question

For a timeout after a navigation, first observe context and navigation events. For a missing response, record the request URL pattern and status rather than the entire payload. For a layout failure, keep the screenshot and viewport dimensions; a protocol switch will not repair a wrong CSS assertion. For a browser crash, collect the driver and browser versions before changing the selector.

This order prevents a common maintenance trap: adding BiDi or CDP to every fixture until the suite can no longer start without the richest available channel. Keep the action path small, attach listeners only around the risky step, and remove listeners after the assertion. The test then remains readable to someone who does not need to know the entire protocol history.

A migration that can be reviewed

Start with one diagnostic fixture and two browsers. Compare the investigation time for the old screenshot-only record with the new event record. Document which browsers emit the selected events, what happens when an event is unavailable, and whether the listener itself changes timing. Then promote the pattern to a shared helper.

Read the first divergent event

When two runs disagree, align their timelines at the last successful assertion. Compare the next navigation, console, and network event rather than comparing the final screenshots. An illustrative review may find that a Firefox run creates a new context at 12:04:10, while Chromium remains in the checkout context until a 30-second timeout. That difference tells the engineer which owner to contact: popup policy or application navigation, not selector maintenance.

Keep the event record short enough for a ticket. Store the test name, browser and driver versions, the failed step, three relevant events before the failure, and the next proposed check. A long trace can remain attached in CI, but the summary should stand alone. This makes protocol choice an editorial decision about evidence, not a permanent expansion of every test's data surface.

The success measure is not the number of BiDi subscriptions or CDP calls in the repository. It is whether a failed test now tells the next engineer which layer to inspect. A portable user action plus a bounded witness is usually more durable than a test that quietly depends on one browser’s internal vocabulary.