feat(selenium-devtools-py): Python Selenium adapter, and a runnable backend entry - #246
feat(selenium-devtools-py): Python Selenium adapter, and a runnable backend entry#246vishnuv688 wants to merge 24 commits into
Conversation
…ntain permissions' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Greptile SummaryThe PR introduces a Python Selenium adapter and a standalone backend CLI entry. The session-lifecycle fix handles sequential driver replacement, but still uses one rotating capture slot for simultaneously active drivers.
Confidence Score: 4/5The PR is not yet safe to merge because simultaneously active Selenium drivers still overwrite and finalize one another's capture resources when their commands interleave. The attempted session-state fix supports sequential driver replacement, but each non-skipped command compares against one global armed session and tears down that session's snapshot and screencast before arming another. The reply reports the issue as fixed, but alternating commands between two still-live drivers are a concrete residual case that repeatedly truncates capture and reattaches event handlers. Files Needing Attention: packages/selenium-devtools-py/src/selenium_devtools/instrumentation.py, packages/selenium-devtools-py/src/selenium_devtools/bidi.py
|
| Filename | Overview |
|---|---|
| packages/selenium-devtools-py/src/selenium_devtools/instrumentation.py | Implements Selenium command instrumentation and session setup, but rotating one global resource set breaks interleaved live sessions. |
| packages/selenium-devtools-py/src/selenium_devtools/bidi.py | Adds console and network capture whose additive registrations are repeated whenever instrumentation re-arms a previously seen live driver. |
| packages/selenium-devtools-py/src/selenium_devtools/capturer.py | Tracks metadata per session and explicitly attributes finalized screencasts, though upstream resource ownership remains global. |
| packages/backend/src/server.ts | Adds a dedicated backend CLI entry that parses options, starts the server, reports its bound address, and exits on startup failure. |
| packages/backend/src/server-args.ts | Adds isolated parsing for backend port, hostname, and help options. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
A[Driver A command] --> S[Global armed_session A]
B[Driver B command] --> R1[Finalize A resources]
R1 --> S2[Global armed_session B]
C[Next Driver A command] --> R2[Finalize B resources]
R2 --> S3[Reattach and restart A resources]
Reviews (2): Last reviewed commit: "fix(selenium-devtools-py): capture every..." | Re-trigger Greptile
| if _state.get("setup_done"): | ||
| return |
There was a problem hiding this comment.
Global state mixes Selenium sessions
When a pytest run or plain Python process creates a second WebDriver, the process-wide patch sends its commands through the same capturer while setup_done skips its metadata, BiDi, snapshot, and screencast initialization, causing later sessions to be attributed to the first session and captured incompletely; quitting either driver also clears the shared capture state.
There was a problem hiding this comment.
Confirmed, and the cause was wider than the one guard. Three separate pieces of state made a second driver in the same process capture incompletely:
_state["setup_done"](a boolean) skipped metadata, BiDi, snapshot and screencast bringup for every session after the first.SessionCapturer._metadata_sent(also a boolean) meant the second session's id never reached the wire, so its commands were attributed to session one._on_quittore down the shared recorder and snapshot regardless of which driver quit, andsend_screencastkeyed on the latest session id, so a rotated video would be filed under the wrong session.
That matters for the most ordinary pytest setup there is, a function-scoped driver fixture, where every test builds and quits a driver.
Fixed by mirroring how the JS adapters already handle this (one capturer per run, per-session bringup, scoped teardown):
setup_donebecomesarmed_session, holding the session id. Bringup re-runs whenever the id changes, and rotating closes out the previous session first: final mutation drain, snapshot dropped, screencast finalized and attributed to the session that produced it._metadata_sentbecomes a set of session ids, so each session announces itself once. The dashboard already keys metadata bysessionId, so both sessions are kept rather than the first being overwritten._on_quittakes the quitting driver and returns early when another driver is the armed one, then clearsarmed_sessionso a driver created later re-arms instead of inheriting the dead session's state.send_screencastaccepts an explicitsession_idfor the already-replaced case.
Five tests added under TestMultipleSessions. Four of them fail against the old guards and pass with the fix, verified by reverting the change locally: per-session metadata, per-session BiDi attach, quitting a stale driver leaving the live one capturing, and quitting the armed driver clearing arming so the next one re-arms. Suite is 145 tests, green on 3.9 and 3.12.
One residual, deliberately not changed here: for a plain script (no test framework), the synthetic suite is still marked complete on the first quit(), so a script that drives two browsers sequentially marks its single suite done early. That only affects the no-framework path and is cosmetic in the tree; worth its own issue rather than widening this PR.
| previous = _state.get("armed_session") | ||
| if previous: | ||
| # A second driver in the same process. Close out the previous session | ||
| # before arming this one, or its video and DOM would be attributed here. | ||
| _flush_mutations(capturer) | ||
| _state["snapshot"] = None | ||
| _finalize_screencast(capturer, previous) |
There was a problem hiding this comment.
Live sessions replace capture state
If two live WebDrivers issue interleaved commands, every change in session_id finalizes the shared screencast, discards the shared snapshot, and reattaches BiDi handlers for a session that remains active. Alternating commands therefore truncate or discard each session's video and DOM stream and can duplicate its console and network events.
What & why
Currently, DevTools supports only TypeScript and JavaScript tests, as it was originally designed for WebdriverIO. With the addition of Selenium support, we are also introducing Python support as a first step, since Selenium supports multiple languages such as Python, Java, JavaScript, and Ruby.
Type of change
Packages touched
shared(types and contracts)core(framework-agnostic capture/reporting)service(WebdriverIO adapter)nightwatch-devtools(Nightwatch adapter)selenium-devtools(Selenium adapter)backend(server)app(UI)script(page-injected runtime)selenium-py-devtoolsNotes for reviewers
Screenshots / recordings
Fixes #279
Tracked under #277 (Python Selenium adapter). The remaining work is broken out as sub-issues there, one pull request each, so this PR stays scoped to the rename and the backend CLI entry.