From 741aac57f85d8e4dd78b4c32065a9e361985a7cf Mon Sep 17 00:00:00 2001 From: Delta456 Date: Mon, 9 Jun 2025 18:24:17 +0530 Subject: [PATCH 1/3] [py] do not use global var for devtools, allows multiple devtools to run --- py/selenium/webdriver/remote/webdriver.py | 57 +++++++++---------- .../webdriver/common/devtools_tests.py | 14 ++++- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/py/selenium/webdriver/remote/webdriver.py b/py/selenium/webdriver/remote/webdriver.py index 07627a488a10e..b90ae35d703a0 100644 --- a/py/selenium/webdriver/remote/webdriver.py +++ b/py/selenium/webdriver/remote/webdriver.py @@ -14,7 +14,6 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. - """The WebDriver implementation.""" import base64 @@ -75,7 +74,6 @@ from .websocket_connection import WebSocketConnection cdp = None -devtools = None def import_cdp(): @@ -267,6 +265,7 @@ def __init__( self._storage = None self._webextension = None self._permissions = None + self._devtools = None def __repr__(self): return f'<{type(self).__module__}.{type(self).__name__} (session="{self.session_id}")>' @@ -1182,32 +1181,28 @@ def orientation(self, value) -> None: raise WebDriverException("You can only set the orientation to 'LANDSCAPE' and 'PORTRAIT'") def start_devtools(self): - global devtools - if self._websocket_connection: - return devtools, self._websocket_connection + global cdp + import_cdp() + if self.caps.get("se:cdp"): + ws_url = self.caps.get("se:cdp") + version = self.caps.get("se:cdpVersion").split(".")[0] else: - global cdp - import_cdp() - - if not devtools: - if self.caps.get("se:cdp"): - ws_url = self.caps.get("se:cdp") - version = self.caps.get("se:cdpVersion").split(".")[0] - else: - version, ws_url = self._get_cdp_details() - - if not ws_url: - raise WebDriverException("Unable to find url to connect to from capabilities") - - devtools = cdp.import_devtools(version) - if self.caps["browserName"].lower() == "firefox": - raise RuntimeError("CDP support for Firefox has been removed. Please switch to WebDriver BiDi.") - self._websocket_connection = WebSocketConnection(ws_url) - targets = self._websocket_connection.execute(devtools.target.get_targets()) - target_id = targets[0].target_id - session = self._websocket_connection.execute(devtools.target.attach_to_target(target_id, True)) - self._websocket_connection.session_id = session - return devtools, self._websocket_connection + version, ws_url = self._get_cdp_details() + + if not ws_url: + raise WebDriverException("Unable to find url to connect to from capabilities") + + self._devtools = cdp.import_devtools(version) + if self._websocket_connection: + return self._devtools, self._websocket_connection + if self.caps["browserName"].lower() == "firefox": + raise RuntimeError("CDP support for Firefox has been removed. Please switch to WebDriver BiDi.") + self._websocket_connection = WebSocketConnection(ws_url) + targets = self._websocket_connection.execute(self._devtools.target.get_targets()) + target_id = targets[0].target_id + session = self._websocket_connection.execute(self._devtools.target.attach_to_target(target_id, True)) + self._websocket_connection.session_id = session + return self._devtools, self._websocket_connection @asynccontextmanager async def bidi_connection(self): @@ -1282,9 +1277,8 @@ def browser(self): @property def _session(self): - """ - Returns the BiDi session object for the current WebDriver session. - """ + """Returns the BiDi session object for the current WebDriver + session.""" if not self._websocket_connection: self._start_bidi() @@ -1295,7 +1289,8 @@ def _session(self): @property def browsing_context(self): - """Returns a browsing context module object for BiDi browsing context commands. + """Returns a browsing context module object for BiDi browsing context + commands. Returns: -------- diff --git a/py/test/selenium/webdriver/common/devtools_tests.py b/py/test/selenium/webdriver/common/devtools_tests.py index 8d46f3b2742af..0af099ce115d0 100644 --- a/py/test/selenium/webdriver/common/devtools_tests.py +++ b/py/test/selenium/webdriver/common/devtools_tests.py @@ -15,7 +15,6 @@ # specific language governing permissions and limitations # under the License. import pytest - from selenium.webdriver.support.ui import WebDriverWait @@ -38,3 +37,16 @@ def test_check_console_messages(driver, pages, recwarn): assert console_api_calls[0].args[0].value == "I love cheese" assert console_api_calls[1].type_ == "error" assert console_api_calls[1].args[0].value == "I love bread" + + +@pytest.mark.xfail_safari +@pytest.mark.xfail_firefox +@pytest.mark.xfail_remote +def test_check_start_twice(clean_driver, clean_options, clean_service): + driver1 = clean_driver(options=clean_options, service=clean_service) + devtools1, connection1 = driver1.start_devtools() + driver1.quit() + + driver2 = clean_driver(options=clean_options, service=clean_service) + devtools2, connection2 = driver2.start_devtools() + driver2.quit() From f53a442ba508ae274852ecf925ff0b3c1a5df10b Mon Sep 17 00:00:00 2001 From: Delta456 Date: Mon, 9 Jun 2025 18:48:43 +0530 Subject: [PATCH 2/3] format --- py/test/selenium/webdriver/common/devtools_tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/py/test/selenium/webdriver/common/devtools_tests.py b/py/test/selenium/webdriver/common/devtools_tests.py index 0af099ce115d0..b3fad4d04337b 100644 --- a/py/test/selenium/webdriver/common/devtools_tests.py +++ b/py/test/selenium/webdriver/common/devtools_tests.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. import pytest + from selenium.webdriver.support.ui import WebDriverWait From 975a3ef0c4e20997956e63c00c2f6c6bacbfe79f Mon Sep 17 00:00:00 2001 From: Delta456 Date: Tue, 10 Jun 2025 10:02:24 +0530 Subject: [PATCH 3/3] implement xfail for clean_driver --- py/conftest.py | 13 +++++++++++++ py/test/selenium/webdriver/common/devtools_tests.py | 6 +++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/py/conftest.py b/py/conftest.py index fd1b28d6bebc9..c790b27a489c0 100644 --- a/py/conftest.py +++ b/py/conftest.py @@ -443,6 +443,19 @@ def clean_driver(request): except (AttributeError, TypeError): raise Exception("This test requires a --driver to be specified.") driver_reference = getattr(webdriver, driver_class) + + # conditionally mark tests as expected to fail based on driver + marker = request.node.get_closest_marker(f"xfail_{driver_class.lower()}") + if marker is not None: + if "run" in marker.kwargs: + if marker.kwargs["run"] is False: + pytest.skip() + yield + return + if "raises" in marker.kwargs: + marker.kwargs.pop("raises") + pytest.xfail(**marker.kwargs) + yield driver_reference if request.node.get_closest_marker("no_driver_after_test"): driver_reference = None diff --git a/py/test/selenium/webdriver/common/devtools_tests.py b/py/test/selenium/webdriver/common/devtools_tests.py index b3fad4d04337b..46f35e7672d61 100644 --- a/py/test/selenium/webdriver/common/devtools_tests.py +++ b/py/test/selenium/webdriver/common/devtools_tests.py @@ -43,11 +43,11 @@ def test_check_console_messages(driver, pages, recwarn): @pytest.mark.xfail_safari @pytest.mark.xfail_firefox @pytest.mark.xfail_remote -def test_check_start_twice(clean_driver, clean_options, clean_service): - driver1 = clean_driver(options=clean_options, service=clean_service) +def test_check_start_twice(clean_driver, clean_options): + driver1 = clean_driver(options=clean_options) devtools1, connection1 = driver1.start_devtools() driver1.quit() - driver2 = clean_driver(options=clean_options, service=clean_service) + driver2 = clean_driver(options=clean_options) devtools2, connection2 = driver2.start_devtools() driver2.quit()