Skip to content

[docs] ADR: Selenium waits for interaction readiness, by default on BiDi sessions - #17886

Open
AutomatedTester wants to merge 1 commit into
trunkfrom
adr-interaction-readiness-waits
Open

[docs] ADR: Selenium waits for interaction readiness, by default on BiDi sessions#17886
AutomatedTester wants to merge 1 commit into
trunkfrom
adr-interaction-readiness-waits

Conversation

@AutomatedTester

@AutomatedTester AutomatedTester commented Aug 6, 2026

Copy link
Copy Markdown
Member

📄 The decision, its rationale, considered options, and consequences are in the record file
this PR adds; read it there. The sections below are proposal notes and review logistics.

Updated: the record now proposes that readiness waiting be the default behavior on
BiDi-enabled sessions
, not an opt-in wait users have to remember to call. Classic HTTP-only
sessions are unchanged. Earlier revisions of this PR deferred that question; it is now decision 2.

🔗 Related

  • Builds on 17670 — BiDi implementation boundaries: the readiness waits are high-level, protocol-neutral API, with BiDi as an implementation mechanism that must not leak into their signatures. The default is capability-gated, not API-gated — no signature names BiDi, and nothing new is reachable off the driver.
  • Reference implementation (Python): py-quiescence-bidi-preload — the oracle as a shared JavaScript atom registered as a BiDi preload script, exposed as driver.wait_for_dom_settled and driver.wait_until_actionable, with ~1,500 lines of behavioral tests. Evidence that the semantics are implementable and testable; not proposed as the final API shape.
  • Intended to become a proposed WebDriver BiDi quiescence module; no working-group issue filed yet.

📝 Proposal notes

Why the default is gated on BiDi. Two reasons, and both have to hold:

  1. It is already an opt-in. The user asks for a BiDi session, and that surface is documented as
    evolving (17670). Behavior can change inside that opt-in without touching the timing of existing
    classic-only tests.
  2. The mechanism only exists there. A preload script runs before page script, so the observer is
    watching mutation and motion from before the command arrives. Just-in-time injection starts at
    the moment of the command, which makes "not moving" a guess rather than an observation.

What the default does and does not wait for. Actionability only — visible, enabled, editable
where the interaction writes, in the viewport, unobstructed, not moving. It deliberately does not
require DOM settledness or a quiet pending-work ledger: an application that long-polls or animates
continuously would otherwise pay a settle timeout on every click. Those two layers stay explicit.
The low-level Actions API does not auto-wait either.

Compatibility. A test that fails today fails the same way after this, later and with a better
message: an expired implicit wait raises ElementClickInterceptedException /
ElementNotInteractableException, not a timeout error, so existing catch/except blocks keep
matching. Escape hatches are the new readiness session timeout (0 disables) and a session-level
toggle. The honest cost is that failing tests get slower, and existing BiDi users see changed
timing on upgrade.

Why one record. The three layers (pending work, DOM settledness, actionability) share a single
context and rationale, and the default is a composition of them — deciding them separately would
mean a reader has to open three records to understand any one of them.

Deliberately out of scope.

  • A default for classic sessions. Not just deferred — Considered option 5 argues it cannot be done
    at full fidelity without a preload. Listed as a follow-up only as a possible reduced-fidelity
    variant.
  • Deprecating ExpectedConditions.elementToBeClickable and equivalents. Listed as a follow-up.
  • The BiDi module proposal itself. Pursued in parallel; the record commits to taking it to the
    working group, not to its shape.

Two places the record departs from the reference implementation. The prototype returns a result
record on timeout, where the record decides a timeout raises (Considered options 9–10, decision 5);
and the prototype implements only the explicit waits, so the default of decision 2 is proposed here
rather than demonstrated. Both are called out in the Appendix so neither is mistaken for an
oversight.

Cross-binding impact. Every binding gains two driver methods, a new session timeout, a session
toggle, the interaction-path change, and the packaging wiring to ship a JavaScript resource;
bindings that already ship atoms have most of the last part. Only the Python explicit waits have
been built. The project's own BiDi-enabled tests that expect immediate interaction errors will need
the toggle.

🗣 Discussion

Questions I would most like input on:

  1. Is default-on for BiDi sessions acceptable, or should it ship off for a release and flip after
    feedback? Considered option 6 rejects the staged rollout on the grounds that enabling BiDi is
    already the opt-in and a second gate delays the feedback the heuristics need — but this is the
    thing I would most like to be argued out of.
  2. Failure type on an expired implicit wait. Preserving ElementClickInterceptedException keeps
    existing error handling working, at the cost of an error whose name no longer describes the whole
    story ("intercepted" now means "still intercepted after 10s"). Right trade?
  3. The default readiness timeout value. 10s is proposed. Playwright uses 30s. A high value
    makes failing suites slow; a low one makes the feature look unreliable on slow CI.
  4. Scope of the guarantee — the heuristics will be wrong sometimes, and now they are wrong in the
    default path, where a false "not ready" turns a passing test into a timeout. Is the project
    willing to own that, and is "best-effort, documented limits, one-line toggle" enough?
  5. Entry-point names and placementwaitForDomSettled / waitUntilActionable on the driver.
    Better names, or do these belong somewhere other than the driver?
  6. Divergence between transports. After this, BiDi and classic differ in timing, not only in
    transport, until BiDi becomes the default. Is that acceptable as a transitional state?

Not yet discussed at a TLC meeting — requesting an agenda slot.

📌 Tracking

Tracking issue: (linked on acceptance)

@AutomatedTester
AutomatedTester force-pushed the adr-interaction-readiness-waits branch 2 times, most recently from 181658f to d03f066 Compare August 6, 2026 10:37
Proposes that Selenium wait for interaction readiness before it acts:
click, send_keys, clear and submit wait for the element to be actionable
by default on a BiDi-enabled session, bounded by a new readiness timeout
and raising today's interaction errors with the diagnosis appended.
Classic sessions are unchanged, and a session toggle disables it.

Readiness is modelled in three composable layers - a pending-work
ledger, DOM settledness, and element actionability - also exposed as
explicit protocol-neutral waits, with the semantics defined once in a
shared JavaScript atom injected as a BiDi preload script, and framed as
a prototype of a proposed BiDi quiescence module.

Status: Proposed.
@AutomatedTester
AutomatedTester force-pushed the adr-interaction-readiness-waits branch from d03f066 to e71ef1c Compare August 10, 2026 08:29
@AutomatedTester AutomatedTester changed the title [docs] ADR: Selenium waits for interaction readiness, not just element presence [docs] ADR: Selenium waits for interaction readiness, by default on BiDi sessions Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant