Skip to content

Commit e41e30e

Browse files
Add files via upload
1 parent 5941bb4 commit e41e30e

1 file changed

Lines changed: 283 additions & 0 deletions

File tree

‎selenium-bidi-study-guide.rst‎

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
=====================================
2+
WebDriver BiDi with Selenium (Python)
3+
=====================================
4+
5+
.. contents:: Table of Contents
6+
:depth: 2
7+
:local:
8+
9+
.. note::
10+
The BiDi API in Selenium is still evolving. Method names and modules can
11+
change between releases. Always verify examples against the docs for your
12+
installed version (``pip show selenium``).
13+
14+
15+
1. What is WebDriver BiDi?
16+
==========================
17+
18+
Classic WebDriver is **request/response**: your script sends a command over
19+
HTTP, the browser replies, and nothing happens in between. The browser cannot
20+
tell your script that something changed.
21+
22+
**WebDriver BiDi** (Bidirectional) keeps a **WebSocket** open between the test
23+
and the browser. Both sides can send messages at any time, so the browser can
24+
*push events* to your script (console logs, network traffic, navigation, ...).
25+
26+
It is a W3C standard being developed with the major browser vendors, and it is
27+
meant to replace the Chromium-only Chrome DevTools Protocol (CDP) for these use
28+
cases.
29+
30+
31+
2. Classic vs CDP vs BiDi
32+
=========================
33+
34+
.. list-table::
35+
:header-rows: 1
36+
:widths: 20 25 25 30
37+
38+
* - Feature
39+
- WebDriver Classic
40+
- CDP
41+
- WebDriver BiDi
42+
* - Transport
43+
- HTTP request/response
44+
- WebSocket
45+
- WebSocket
46+
* - Direction
47+
- One way at a time
48+
- Two way
49+
- Two way
50+
* - Browser support
51+
- All major browsers
52+
- Chromium only
53+
- Cross-browser (growing)
54+
* - Standardised
55+
- Yes (W3C)
56+
- No (Chrome protocol)
57+
- Yes (W3C, in progress)
58+
* - Stability
59+
- Stable
60+
- Can change between versions
61+
- Standardised, still evolving
62+
63+
64+
3. Architecture
65+
===============
66+
67+
.. code-block:: text
68+
69+
Test script (Python)
70+
|
71+
| Selenium client (driver.script, driver.network, ...)
72+
v
73+
WebSocket <-----------------------------> Browser driver
74+
(webSocketUrl) commands + events (chromedriver / geckodriver)
75+
|
76+
v
77+
Browser
78+
79+
Key ideas:
80+
81+
* **Commands** go from the script to the browser and get a response with a
82+
matching ``id``.
83+
* **Events** are pushed from the browser. You must **subscribe** first.
84+
* Functionality is grouped into **modules** (also called domains).
85+
86+
Raw protocol messages look like this:
87+
88+
.. code-block:: json
89+
90+
{"id": 1, "method": "session.subscribe",
91+
"params": {"events": ["log.entryAdded"]}}
92+
93+
{"type": "event", "method": "log.entryAdded",
94+
"params": {"level": "info", "text": "hello"}}
95+
96+
97+
4. Enabling BiDi in Python
98+
==========================
99+
100+
BiDi is enabled through the ``webSocketUrl`` capability. In Python, use the
101+
``enable_bidi`` option:
102+
103+
.. code-block:: python
104+
105+
from selenium import webdriver
106+
107+
options = webdriver.ChromeOptions()
108+
options.enable_bidi = True # sets the webSocketUrl capability
109+
110+
driver = webdriver.Chrome(options=options)
111+
112+
Firefox works the same way with ``webdriver.FirefoxOptions()``.
113+
114+
.. tip::
115+
If BiDi APIs fail with a connection error, check first that the capability
116+
is enabled and that your browser, driver, and Selenium versions are up to
117+
date and compatible.
118+
119+
120+
5. BiDi modules
121+
===============
122+
123+
The Python bindings expose these modules under
124+
``selenium.webdriver.common.bidi``:
125+
126+
.. list-table::
127+
:header-rows: 1
128+
:widths: 25 75
129+
130+
* - Module
131+
- Purpose
132+
* - ``session``
133+
- Session status, subscribe and unsubscribe to events
134+
* - ``browsing_context``
135+
- Tabs and windows: create, navigate, reload, close, get the context tree
136+
* - ``script``
137+
- Run JavaScript, listen to console messages and JavaScript errors
138+
* - ``log``
139+
- Log entries (console, JavaScript, other)
140+
* - ``network``
141+
- Observe, intercept, and modify requests and responses
142+
* - ``storage``
143+
- Read and set cookies
144+
* - ``browser``
145+
- Browser-level operations such as user contexts
146+
* - ``webextension``
147+
- Install and uninstall browser extensions
148+
* - ``cdp``, ``console``
149+
- Older CDP-based helpers, kept for backward compatibility
150+
151+
152+
6. Examples
153+
===========
154+
155+
6.1 Capture console messages
156+
----------------------------
157+
158+
.. code-block:: python
159+
160+
from selenium import webdriver
161+
from selenium.webdriver.common.by import By
162+
from selenium.webdriver.support.ui import WebDriverWait
163+
164+
options = webdriver.ChromeOptions()
165+
options.enable_bidi = True
166+
driver = webdriver.Chrome(options=options)
167+
168+
log_entries = []
169+
driver.script.add_console_message_handler(log_entries.append)
170+
171+
driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html")
172+
driver.find_element(By.ID, "consoleLog").click()
173+
174+
WebDriverWait(driver, 5).until(lambda _: len(log_entries) > 0)
175+
print(log_entries[0].text)
176+
177+
driver.quit()
178+
179+
6.2 Capture JavaScript errors
180+
-----------------------------
181+
182+
.. code-block:: python
183+
184+
js_errors = []
185+
driver.script.add_javascript_error_handler(js_errors.append)
186+
187+
driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html")
188+
driver.find_element(By.ID, "jsException").click()
189+
190+
WebDriverWait(driver, 5).until(lambda _: len(js_errors) > 0)
191+
assert "Error" in js_errors[0].text
192+
193+
6.3 Network monitoring and interception
194+
---------------------------------------
195+
196+
The ``network`` module lets you watch requests and responses, block or mock
197+
calls, and handle authentication prompts. Its API has changed across recent
198+
releases, so read the current Selenium Python API docs before writing code.
199+
200+
.. warning::
201+
Old tutorials use ``driver.bidi_connection()`` together with ``trio`` and
202+
CDP wrappers. Prefer the newer ``driver.script`` / ``driver.network`` style
203+
APIs for new work.
204+
205+
206+
7. Common use cases
207+
===================
208+
209+
* Fail a test when the page logs a JavaScript error
210+
* Assert on console output during a flow
211+
* Wait for network activity to finish instead of using ``time.sleep``
212+
* Mock or block API calls (offline mode, error responses, slow responses)
213+
* Capture request and response data for debugging or performance checks
214+
* Handle basic authentication without popups
215+
* Manage cookies and multiple tabs more precisely
216+
217+
218+
8. Best practices and pitfalls
219+
==============================
220+
221+
* **Subscribe before you trigger the action**, or you will miss the event.
222+
* Events arrive **asynchronously**. Use explicit waits, not ``sleep``.
223+
* Register handlers once, and remove them when done to avoid duplicate
224+
callbacks.
225+
* Keep event handlers fast. Collect data in a list and assert in the test.
226+
* Pin and test browser, driver, and Selenium versions together.
227+
* Do not assume every browser supports every BiDi module. Check support and
228+
add fallbacks or skip markers.
229+
* For new code, prefer BiDi over ``execute_cdp_cmd`` so tests are not tied to
230+
Chromium.
231+
232+
233+
9. Interview questions
234+
======================
235+
236+
**What is WebDriver BiDi and why was it introduced?**
237+
A W3C standard for two-way communication over a WebSocket. Classic
238+
WebDriver cannot receive browser events, and CDP works only on Chromium.
239+
BiDi gives a cross-browser way to stream events and control the browser.
240+
241+
**How is BiDi different from CDP?**
242+
CDP is a Chrome-specific protocol that can change between versions. BiDi is
243+
a standardised, cross-browser protocol supported by multiple vendors.
244+
245+
**How do you enable BiDi in Selenium Python?**
246+
Set ``options.enable_bidi = True`` (the ``webSocketUrl`` capability) before
247+
creating the driver.
248+
249+
**Give a real use case.**
250+
Listen for console errors and JavaScript exceptions during a UI test and
251+
fail the test if any appear, instead of only checking the visible page.
252+
253+
**Does BiDi replace WebDriver Classic?**
254+
Not immediately. Selenium is migrating its internals toward BiDi while
255+
keeping backward compatibility, so both coexist.
256+
257+
**What could make BiDi tests flaky?**
258+
Subscribing too late, using fixed sleeps, duplicated handlers, and version
259+
mismatches between browser, driver, and Selenium.
260+
261+
262+
10. Practice exercises
263+
======================
264+
265+
1. Enable BiDi and print every console message from a page of your choice.
266+
2. Write a pytest fixture that collects JavaScript errors and fails the test if
267+
the list is not empty at teardown.
268+
3. Open a second tab using the ``browsing_context`` module and switch between
269+
tabs.
270+
4. Add a network handler that logs the URL of every request during a login
271+
flow.
272+
5. Run the same BiDi test on Chrome and Firefox and note any differences.
273+
274+
275+
11. References
276+
==============
277+
278+
* Selenium docs, BiDirectional functionality:
279+
https://www.selenium.dev/documentation/webdriver/bidi/
280+
* Selenium Python API docs (``selenium.webdriver.common.bidi``):
281+
https://www.selenium.dev/selenium/docs/api/py/
282+
* W3C WebDriver BiDi specification:
283+
https://w3c.github.io/webdriver-bidi/

0 commit comments

Comments
 (0)