From c910e59efa7363d593f5493c7769430b70ccfbe4 Mon Sep 17 00:00:00 2001 From: AutomatedTester Date: Thu, 11 Jun 2026 13:09:45 +0100 Subject: [PATCH 1/2] [py] translate continue_request/continue_response kwargs to BiDi wire format The high-level Request.continue_request and Response.continue_response accepted **kwargs and merged them straight onto the wire params. This bypassed the snake_case to camelCase translation (status -> statusCode, reason_phrase -> reasonPhrase) and the value encoders used everywhere else in this layer, so pythonic calls like continue_response(status=503) or continue_request(headers={...}) silently sent wrong or untranslated values. Replace the kwargs with explicit keyword-only params matching the set_* mutators and route them through _continue_params so they are translated uniformly while still overriding recorded mutations. --- py/private/_network_handlers.py | 68 ++++++++++++++----- .../webdriver/common/bidi_network_tests.py | 50 ++++++++++++++ 2 files changed, 100 insertions(+), 18 deletions(-) diff --git a/py/private/_network_handlers.py b/py/private/_network_handlers.py index 7eaafdb0a4c5d..3c3ae69ecf91c 100644 --- a/py/private/_network_handlers.py +++ b/py/private/_network_handlers.py @@ -355,24 +355,42 @@ def provide_response(self, status=None, headers=None, body=None, reason_phrase=N self._stub = stub self._execute_provide_response() - def continue_request(self, **kwargs) -> None: + def continue_request( + self, + *, + url: str | None = None, + method: str | None = None, + headers: dict[str, Any] | None = None, + cookies: list | None = None, + body: str | None = None, + ) -> None: """Continue the intercepted request, applying any recorded mutations. - Keyword arguments are passed through to ``network.continueRequest`` and - override recorded mutations. Data URLs (``data:``) are skipped silently - because browsers do not create an interceptable request entry for them, - so calling ``network.continueRequest`` would raise "no such request". + Each keyword argument overrides the corresponding mutation recorded via + ``set_url``/``set_method``/``set_headers``/``set_cookies``/``set_body``. + Arguments use the same Python types as those setters and are translated + to the BiDi wire format automatically. Data URLs (``data:``) are + skipped silently because browsers do not create an interceptable request + entry for them, so calling ``network.continueRequest`` would raise + "no such request". + + Args: + url: Replacement request URL. + method: Replacement HTTP method. + headers: Replacement request headers as a name → value dict. + cookies: Replacement request cookies as a list of dicts. + body: Replacement request body string. """ self._handled = True if self.url.startswith("data:"): return - params = self._continue_params() - params.update(kwargs) + overrides = {"url": url, "method": method, "headers": headers, "cookies": cookies, "body": body} + params = self._continue_params({k: v for k, v in overrides.items() if v is not None}) self._conn.execute(command_builder("network.continueRequest", params)) - def _continue_params(self) -> dict: + def _continue_params(self, overrides: dict | None = None) -> dict: params: dict[str, Any] = {"request": self._request_id} - mutations = self._mutations + mutations = {**self._mutations, **(overrides or {})} if "url" in mutations: params["url"] = mutations["url"] if "method" in mutations: @@ -483,24 +501,38 @@ def set_body(self, body: str) -> None: self.body = body self._mutations["body"] = body - def continue_response(self, **kwargs) -> None: + def continue_response( + self, + *, + status: int | None = None, + reason_phrase: str | None = None, + headers: dict[str, Any] | None = None, + cookies: list | None = None, + ) -> None: """Continue the intercepted response, applying any recorded mutations. - Keyword arguments are passed through to ``network.continueResponse`` - and override recorded mutations. Data URLs (``data:``) are skipped - silently because browsers do not create an interceptable entry for - them. + Each keyword argument overrides the corresponding mutation recorded via + ``set_status``/``set_headers``/``set_cookies``. Arguments use the same + Python types as those setters and are translated to the BiDi wire format + automatically. Data URLs (``data:``) are skipped silently because + browsers do not create an interceptable entry for them. + + Args: + status: Replacement HTTP status code. + reason_phrase: Replacement HTTP reason phrase. + headers: Replacement response headers as a name → value dict. + cookies: Replacement set-cookie entries as a list of dicts. """ self._handled = True if self.url.startswith("data:"): return - params = self._continue_params() - params.update(kwargs) + overrides = {"status": status, "reason_phrase": reason_phrase, "headers": headers, "cookies": cookies} + params = self._continue_params({k: v for k, v in overrides.items() if v is not None}) self._conn.execute(command_builder("network.continueResponse", params)) - def _continue_params(self) -> dict: + def _continue_params(self, overrides: dict | None = None) -> dict: params: dict[str, Any] = {"request": self._request_id} - mutations = self._mutations + mutations = {**self._mutations, **(overrides or {})} if "status" in mutations: params["statusCode"] = mutations["status"] if "reason_phrase" in mutations: diff --git a/py/test/unit/selenium/webdriver/common/bidi_network_tests.py b/py/test/unit/selenium/webdriver/common/bidi_network_tests.py index 96648f2d859e7..7c650fc1d8896 100644 --- a/py/test/unit/selenium/webdriver/common/bidi_network_tests.py +++ b/py/test/unit/selenium/webdriver/common/bidi_network_tests.py @@ -209,6 +209,56 @@ def test_continue_request_sends_command_for_regular_urls(): assert conn.commands[0]["params"]["request"] == "request-id-2" +def test_continue_request_translates_explicit_args_to_wire_format(): + conn = FakeConnection() + params = {"request": {"url": "https://example.com/api", "request": "request-id-3"}} + request = Request(conn, params) + + request.continue_request( + url="https://example.com/redirected", + method="POST", + headers={"x-test": "1"}, + cookies=[{"name": "sid", "value": "abc"}], + body="payload", + ) + + sent = conn.commands_named("network.continueRequest")[0]["params"] + assert sent["url"] == "https://example.com/redirected" + assert sent["method"] == "POST" + assert sent["headers"] == [{"name": "x-test", "value": {"type": "string", "value": "1"}}] + assert sent["cookies"] == [{"name": "sid", "value": {"type": "string", "value": "abc"}}] + assert sent["body"] == {"type": "string", "value": "payload"} + + +def test_continue_request_explicit_args_override_recorded_mutations(): + conn = FakeConnection() + params = {"request": {"url": "https://example.com/api", "request": "request-id-4"}} + request = Request(conn, params) + + request.set_method("GET") + request.continue_request(method="DELETE") + + sent = conn.commands_named("network.continueRequest")[0]["params"] + assert sent["method"] == "DELETE" + + +def test_continue_response_translates_explicit_args_to_wire_format(): + conn = FakeConnection() + params = {"request": {"request": "request-id-5"}, "response": {"url": "https://example.com/api"}} + response = Response(conn, params) + + response.continue_response( + status=503, + reason_phrase="Service Unavailable", + headers={"x-test": "1"}, + ) + + sent = conn.commands_named("network.continueResponse")[0]["params"] + assert sent["statusCode"] == 503 + assert sent["reasonPhrase"] == "Service Unavailable" + assert sent["headers"] == [{"name": "x-test", "value": {"type": "string", "value": "1"}}] + + def test_request_parses_event_properties(): request = Request(FakeConnection(), make_before_request_event()) From abd9e30f424d4c8dfdefe54c52fb87612865b805 Mon Sep 17 00:00:00 2001 From: AutomatedTester Date: Thu, 11 Jun 2026 13:40:00 +0100 Subject: [PATCH 2/2] [py] add tests for continue_* override merge and falsy values --- .../webdriver/common/bidi_network_tests.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/py/test/unit/selenium/webdriver/common/bidi_network_tests.py b/py/test/unit/selenium/webdriver/common/bidi_network_tests.py index 7c650fc1d8896..a7e9f5be63bd2 100644 --- a/py/test/unit/selenium/webdriver/common/bidi_network_tests.py +++ b/py/test/unit/selenium/webdriver/common/bidi_network_tests.py @@ -235,11 +235,28 @@ def test_continue_request_explicit_args_override_recorded_mutations(): params = {"request": {"url": "https://example.com/api", "request": "request-id-4"}} request = Request(conn, params) + # A recorded mutation on a different field must survive when only one + # field is overridden via the keyword argument. + request.set_url("https://example.com/recorded") request.set_method("GET") request.continue_request(method="DELETE") sent = conn.commands_named("network.continueRequest")[0]["params"] assert sent["method"] == "DELETE" + assert sent["url"] == "https://example.com/recorded" + + +def test_continue_request_keeps_falsy_body_override(): + conn = FakeConnection() + params = {"request": {"url": "https://example.com/api", "request": "request-id-6"}} + request = Request(conn, params) + + # An empty-string body is a valid value, not "unset": it must not be + # dropped by the None filter. + request.continue_request(body="") + + sent = conn.commands_named("network.continueRequest")[0]["params"] + assert sent["body"] == {"type": "string", "value": ""} def test_continue_response_translates_explicit_args_to_wire_format():