Skip to content

feat: waitForNetworkIdle / waitForResponse (CDP + BiDi) - #85

Merged
shossain786 merged 3 commits into
masterfrom
spike/wait-for-network-idle
Sep 27, 2026
Merged

shossain786 merged 3 commits into
masterfrom
spike/wait-for-network-idle

Conversation

@shossain786

@shossain786 shossain786 commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

What

WaitEngine.waitForNetworkIdle() / waitForNetworkIdle(Duration quietFor) — waits until no request has been in flight for a quiet period (default 500ms), bounded by the configured explicit timeout.

WaitEngine.waitForResponse(String urlPattern) / waitForResponse(String urlPattern, Duration timeout) — waits for a response whose URL matches (same glob syntax as NetworkMock.stub), returns its HTTP status code.

Both: CDP on Chrome/Edge, BiDi on any HasBiDi driver (Firefox), UnsupportedOperationException on neither.

Why

Design note from the 09-26 roadmap entry. Locator's stale-proof re-resolution (#67/#68) and zero-config (#66) closed the framework's other cold-start gaps; this is the next held-back item — waiting for network activity has been a hand-rolled Thread.sleep/JS-polling problem for every consumer until now.

How

Split for testability: NetworkActivityTracker.IdleState (in-flight counter + last-activity timestamp) and ResponseLatch (a CompletableFuture-backed latch) are pure, driver-independent logic — unit tested directly. CDP/BiDi wiring itself needs a real browser and is verified manually, same convention NetworkMockTest documents for CDP request interception.

Real gap found and fixed while verifying end-to-end: without this PR's fix, Firefox raised a raw BiDiException instead of working out of the box — LocalFirefoxDriverProvider never requested a BiDi session. Fixed by defaulting the webSocketUrl capability (an explicit value in the user's own capabilities still wins).

Tests

  • 8 new unit tests for IdleState/ResponseLatch — 547/547 total green.
  • Verified twice end-to-end via selenium-boot-test (real BaseTest lifecycle, zero manual config) against real Chrome and real Firefox — a delayed 1.5s request correctly extends the wait; waitForResponse returns the right status. Throwaway test removed after, not part of that repo's permanent example.

🤖 Generated with Claude Code

shossain786 and others added 2 commits September 27, 2026 12:16
Prototype per the 09-26 design note: CDP path on Chromium, BiDi path
on browsers implementing HasBiDi (Firefox), throws on neither. Both
paths track an in-flight request counter + last-activity timestamp;
idle means the counter is 0 and quietFor has elapsed, bounded by an
overall timeout.

Verified manually against real headless Chrome (not part of the unit
suite, no browser required there):
- Static page: idle correctly at ~quietFor after load (545ms observed
  for quietFor=500ms).
- Page with a request fired 200ms after load, taking 1.5s to respond:
  wait correctly extends past it (2361ms observed vs. a naive
  page-load-only wait), proving it isn't just measuring page load.

Not verified against Firefox/BiDi locally — the firefox binary here is
an unconfigured snap stub, no real browser available. Code path
mirrors the CDP one exactly (in-flight counter + BeforeRequestSent/
ResponseCompleted/FetchError) but is unexercised.

Spike only — not wired into WaitEngine's public API, no unit tests
added (needs a real browser, which the existing suite deliberately
avoids). Next: decide scope (wire into WaitEngine, add
waitForResponse(urlPattern), pick BiDi-only vs BiDi+CDP everywhere)
and verify the BiDi path against real Firefox before that.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Installed a real Firefox (156.0.1, official Mozilla tarball — the
system 'firefox' was an unconfigured snap stub) + geckodriver v0.37.1
locally and ran the same delayed-slow-request scenario used to verify
the CDP path.

Result: firefox idle-reached ms=2300, matching the Chrome CDP result
(2361ms) for the same scenario (200ms delay + 1.5s response +
500ms quietFor). BiDi path (onBeforeRequestSent/onResponseCompleted/
onFetchError via org.openqa.selenium.bidi.module.Network) confirmed
working on a real browser, not just code-reviewed.

Also verified end-to-end in selenium-boot-test (the consumer demo
suite) via a throwaway test — real BaseTest lifecycle, real Chrome,
real consumer classpath — same result, removed after (not part of
that repo's public example).

Both CDP and BiDi paths are now verified. Remaining before this can
be considered for merge (unchanged from the PR description): wire
into WaitEngine's public API, decide waitForResponse(urlPattern)
scope, add test coverage once the API shape is settled.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@shossain786

Copy link
Copy Markdown
Contributor Author

Update: installed a real Firefox (156.0.1, official tarball — the system one was an unconfigured snap stub) + geckodriver v0.37.1 and verified the BiDi path.

Same scenario as the CDP verification (a page firing a delayed 1.5s-slow request): firefox idle-reached ms=2300, matching Chrome's 2361ms for the equivalent run. Both paths now proven on real browsers, not just reviewed.

Also ran it end-to-end through selenium-boot-test (real BaseTest lifecycle, real consumer classpath) — same result, via a throwaway test removed afterward.

Still needs before merge: wire into WaitEngine's public API, decide waitForResponse(urlPattern) scope, add real test coverage.

Builds out the spike into WaitEngine's real API:

- WaitEngine.waitForNetworkIdle() / waitForNetworkIdle(Duration quietFor)
  — waits until no request has been in flight for the quiet period
  (default 500ms), bounded by the configured explicit timeout.
- WaitEngine.waitForResponse(String urlPattern) /
  waitForResponse(String urlPattern, Duration timeout) — waits for a
  response whose URL matches (same glob syntax as NetworkMock.stub),
  returns its HTTP status code.
- Both: CDP on Chrome/Edge, BiDi (org.openqa.selenium.bidi.module.Network)
  on any HasBiDi driver (Firefox), UnsupportedOperationException on
  neither.

Implementation split for testability: NetworkActivityTracker.IdleState
and ResponseLatch are pure, driver-independent logic (in-flight
counter + last-activity timestamp; a CompletableFuture-backed latch)
and are unit tested directly (8 new tests, 547/547 total green). CDP/
BiDi wiring itself needs a real browser and is verified manually —
same convention NetworkMockTest documents for CDP request
interception.

Real gap found and fixed while verifying end-to-end: without this,
Firefox raised a raw BiDiException ("Unable to create a BiDi
connection") instead of working out of the box, because
LocalFirefoxDriverProvider never requested a BiDi session. Fixed by
setting the "webSocketUrl" capability by default (an explicit value
in the user's own capabilities still wins) — otherwise every Firefox
user would have needed to know to add that capability manually,
breaking the framework's zero-config promise for this feature.

Verified twice end-to-end via selenium-boot-test (real BaseTest
lifecycle, real Chrome and real Firefox, zero manual config) with a
throwaway test removed after — not part of that repo's permanent
example. Chrome: waitForNetworkIdle correctly waited past a delayed
1.5s request; waitForResponse returned 200. Firefox: same, and
reproduced+fixed the BiDi gap in the same pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@shossain786
shossain786 marked this pull request as ready for review September 27, 2026 07:55
@shossain786 shossain786 changed the title spike: waitForNetworkIdle prototype (CDP + BiDi) feat: waitForNetworkIdle / waitForResponse (CDP + BiDi) Sep 27, 2026
@shossain786
shossain786 merged commit 3878c55 into master Sep 27, 2026
2 checks passed
@shossain786
shossain786 deleted the spike/wait-for-network-idle branch September 27, 2026 08:42
@shossain786 shossain786 mentioned this pull request Sep 27, 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