From b91113a0562635152615691ef6add7cc666c87a1 Mon Sep 17 00:00:00 2001 From: Corey Goldberg <1113081+cgoldberg@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:23:23 -0500 Subject: [PATCH 01/10] [py][bidi] Allow resetting viewport --- .../webdriver/common/bidi/browsing_context.py | 26 ++++++--- .../common/bidi_browsing_context_tests.py | 55 +++++++++++++++---- 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/py/selenium/webdriver/common/bidi/browsing_context.py b/py/selenium/webdriver/common/bidi/browsing_context.py index 403e10b87d539..64139aa2603c1 100644 --- a/py/selenium/webdriver/common/bidi/browsing_context.py +++ b/py/selenium/webdriver/common/bidi/browsing_context.py @@ -20,9 +20,13 @@ from dataclasses import dataclass from typing import Any +from typing_extensions import Sentinel + from selenium.webdriver.common.bidi.common import command_builder from selenium.webdriver.common.bidi.session import Session +UNDEFINED = Sentinel("UNDEFINED") + class ReadinessState: """Represents the stage of document loading at which a navigation command will return.""" @@ -980,16 +984,16 @@ def reload( def set_viewport( self, context: str | None = None, - viewport: dict | None = None, - device_pixel_ratio: float | None = None, + viewport: dict | None | UNDEFINED = UNDEFINED, + device_pixel_ratio: float | UNDEFINED = UNDEFINED, user_contexts: list[str] | None = None, ) -> None: """Modifies specific viewport characteristics on the given top-level traversable. Args: context: The browsing context ID. - viewport: The viewport parameters. - device_pixel_ratio: The device pixel ratio. + viewport: The viewport parameters (`None` resets to default). + device_pixel_ratio: The device pixel ratio (`None` resets default). user_contexts: The user context IDs. Raises: @@ -998,10 +1002,18 @@ def set_viewport( params: dict[str, Any] = {} if context is not None: params["context"] = context - if viewport is not None: + if viewport is UNDEFINED: + pass + elif viewport is None: + params["viewport"] = None + else: params["viewport"] = viewport - if device_pixel_ratio is not None: - params["devicePixelRatio"] = device_pixel_ratio + if device_pixel_ratio is UNDEFINED: + pass + elif device_pixel_ratio is None: + params["devicePixelRatio"] = None + else: + params["devicePixelRatio"] = viewport if user_contexts is not None: params["userContexts"] = user_contexts diff --git a/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py b/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py index fb9941582a395..e2f1064f2ef3b 100644 --- a/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py +++ b/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py @@ -348,12 +348,15 @@ def test_set_viewport(driver, pages): context_id = driver.current_window_handle driver.get(pages.url("formPage.html")) - driver.browsing_context.set_viewport(context=context_id, viewport={"width": 250, "height": 300}) + try: + driver.browsing_context.set_viewport(context=context_id, viewport={"width": 251, "height": 301}) - viewport_size = driver.execute_script("return [window.innerWidth, window.innerHeight];") + viewport_size = driver.execute_script("return [window.innerWidth, window.innerHeight];") - assert viewport_size[0] == 250 - assert viewport_size[1] == 300 + assert viewport_size[0] == 251 + assert viewport_size[1] == 301 + finally: + driver.browsing_context.set_viewport(context=context_id, viewport=None, device_pixel_ratio=None) def test_set_viewport_with_device_pixel_ratio(driver, pages): @@ -361,18 +364,46 @@ def test_set_viewport_with_device_pixel_ratio(driver, pages): context_id = driver.current_window_handle driver.get(pages.url("formPage.html")) - driver.browsing_context.set_viewport( - context=context_id, viewport={"width": 250, "height": 300}, device_pixel_ratio=5 - ) + try: + driver.browsing_context.set_viewport( + context=context_id, viewport={"width": 252, "height": 302}, device_pixel_ratio=5 + ) + + viewport_size = driver.execute_script("return [window.innerWidth, window.innerHeight];") + + assert viewport_size[0] == 252 + assert viewport_size[1] == 302 + + device_pixel_ratio = driver.execute_script("return window.devicePixelRatio") + + assert device_pixel_ratio == 5 + finally: + driver.browsing_context.set_viewport(context=context_id, viewport=None, device_pixel_ratio=None) - viewport_size = driver.execute_script("return [window.innerWidth, window.innerHeight];") - assert viewport_size[0] == 250 - assert viewport_size[1] == 300 +def test_set_viewport_back_to_default(driver, pages): + """Test resetting the viewport and device pixel ratio to defaults.""" + context_id = driver.current_window_handle + driver.get(pages.url("formPage.html")) + + default_viewport_size = driver.execute_script("return [window.innerWidth, window.innerHeight];") + default_device_pixel_ratio = driver.execute_script("return window.devicePixelRatio") + + try: + driver.browsing_context.set_viewport( + context=context_id, viewport={"width": 253, "height": 303}, device_pixel_ratio=10 + ) + + driver.browsing_context.set_viewport(context=context_id, viewport=None, device_pixel_ratio=None) - device_pixel_ratio = driver.execute_script("return window.devicePixelRatio") + viewport_size = driver.execute_script("return [window.innerWidth, window.innerHeight];") + device_pixel_ratio = driver.execute_script("return window.devicePixelRatio") - assert device_pixel_ratio == 5 + assert viewport_size[0] == default_viewport_size[0] + assert viewport_size[1] == default_viewport_size[0] + assert device_pixel_ratio == default_device_pixel_ratio + finally: + driver.browsing_context.set_viewport(context=context_id, viewport=None, device_pixel_ratio=None) def test_print_page(driver, pages): From ce185c64f8864e88ea190abf63504bbee0511844 Mon Sep 17 00:00:00 2001 From: Corey Goldberg <1113081+cgoldberg@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:35:52 -0500 Subject: [PATCH 02/10] Fix typo in assertion --- .../selenium/webdriver/common/bidi_browsing_context_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py b/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py index e2f1064f2ef3b..fec599a4aded0 100644 --- a/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py +++ b/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py @@ -400,7 +400,7 @@ def test_set_viewport_back_to_default(driver, pages): device_pixel_ratio = driver.execute_script("return window.devicePixelRatio") assert viewport_size[0] == default_viewport_size[0] - assert viewport_size[1] == default_viewport_size[0] + assert viewport_size[1] == default_viewport_size[1] assert device_pixel_ratio == default_device_pixel_ratio finally: driver.browsing_context.set_viewport(context=context_id, viewport=None, device_pixel_ratio=None) From ac447463f5e35a3b5733574510f5ab56a5410ef4 Mon Sep 17 00:00:00 2001 From: Corey Goldberg <1113081+cgoldberg@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:36:47 -0500 Subject: [PATCH 03/10] Fix incorrect assignment --- py/selenium/webdriver/common/bidi/browsing_context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/selenium/webdriver/common/bidi/browsing_context.py b/py/selenium/webdriver/common/bidi/browsing_context.py index 64139aa2603c1..0ed9ad2cb9480 100644 --- a/py/selenium/webdriver/common/bidi/browsing_context.py +++ b/py/selenium/webdriver/common/bidi/browsing_context.py @@ -1013,7 +1013,7 @@ def set_viewport( elif device_pixel_ratio is None: params["devicePixelRatio"] = None else: - params["devicePixelRatio"] = viewport + params["devicePixelRatio"] = device_pixel_ratio if user_contexts is not None: params["userContexts"] = user_contexts From 1ea46d119d25b320e46d4bf72e990f41e08345eb Mon Sep 17 00:00:00 2001 From: Corey Goldberg <1113081+cgoldberg@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:46:12 -0500 Subject: [PATCH 04/10] Fix type hint --- py/selenium/webdriver/common/bidi/browsing_context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/selenium/webdriver/common/bidi/browsing_context.py b/py/selenium/webdriver/common/bidi/browsing_context.py index 0ed9ad2cb9480..7d6b8e1043ca0 100644 --- a/py/selenium/webdriver/common/bidi/browsing_context.py +++ b/py/selenium/webdriver/common/bidi/browsing_context.py @@ -985,7 +985,7 @@ def set_viewport( self, context: str | None = None, viewport: dict | None | UNDEFINED = UNDEFINED, - device_pixel_ratio: float | UNDEFINED = UNDEFINED, + device_pixel_ratio: float | None | UNDEFINED = UNDEFINED, user_contexts: list[str] | None = None, ) -> None: """Modifies specific viewport characteristics on the given top-level traversable. From 5632a35bbd08a69a4acdc5fc93b1d197b468e1e8 Mon Sep 17 00:00:00 2001 From: Corey Goldberg <1113081+cgoldberg@users.noreply.github.com> Date: Fri, 21 Nov 2025 08:52:24 -0500 Subject: [PATCH 05/10] Add test and exceptions --- .../webdriver/common/bidi/browsing_context.py | 19 +++++++++---- .../common/bidi_browsing_context_tests.py | 27 ++++++++++++++++++- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/py/selenium/webdriver/common/bidi/browsing_context.py b/py/selenium/webdriver/common/bidi/browsing_context.py index 7d6b8e1043ca0..baf8919b91ac3 100644 --- a/py/selenium/webdriver/common/bidi/browsing_context.py +++ b/py/selenium/webdriver/common/bidi/browsing_context.py @@ -997,11 +997,22 @@ def set_viewport( user_contexts: The user context IDs. Raises: - Exception: If the browsing context is not a top-level traversable. + Exception: If the browsing context is not a top-level traversable + ValueError: If neither `contexts` or `user_contexts` are used + ValueError: If both `contexts` and `user_contexts` are used """ + + if contexts is not None and user_contexts is not None: + raise ValueError("Cannot specify both contexts and user_contexts") + + if contexts is None and user_contexts is None: + raise ValueError("Must specify either contexts or user_contexts") + params: dict[str, Any] = {} - if context is not None: - params["context"] = context + if contexts is not None: + params["contexts"] = contexts + elif user_contexts is not None: + params["userContexts"] = user_contexts if viewport is UNDEFINED: pass elif viewport is None: @@ -1014,8 +1025,6 @@ def set_viewport( params["devicePixelRatio"] = None else: params["devicePixelRatio"] = device_pixel_ratio - if user_contexts is not None: - params["userContexts"] = user_contexts self.conn.execute(command_builder("browsingContext.setViewport", params)) diff --git a/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py b/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py index fec599a4aded0..0b07d97953dda 100644 --- a/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py +++ b/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py @@ -381,6 +381,31 @@ def test_set_viewport_with_device_pixel_ratio(driver, pages): driver.browsing_context.set_viewport(context=context_id, viewport=None, device_pixel_ratio=None) +def test_set_viewport_with_no_args_doesnt_change_values(driver, pages): + """Test setting the viewport with no args doesn't change viewport or device pixel ratio.""" + context_id = driver.current_window_handle + driver.get(pages.url("formPage.html")) + + try: + driver.browsing_context.set_viewport( + context=context_id, viewport={"width": 253, "height": 303}, device_pixel_ratio=6 + ) + + driver.browsing_context.set_viewport(context=context_id) + + viewport_size = driver.execute_script("return [window.innerWidth, window.innerHeight];") + + assert viewport_size[0] == 253 + assert viewport_size[1] == 303 + + device_pixel_ratio = driver.execute_script("return window.devicePixelRatio") + + assert device_pixel_ratio == 6 + finally: + driver.browsing_context.set_viewport(context=context_id, viewport=None, device_pixel_ratio=None) + + + def test_set_viewport_back_to_default(driver, pages): """Test resetting the viewport and device pixel ratio to defaults.""" context_id = driver.current_window_handle @@ -391,7 +416,7 @@ def test_set_viewport_back_to_default(driver, pages): try: driver.browsing_context.set_viewport( - context=context_id, viewport={"width": 253, "height": 303}, device_pixel_ratio=10 + context=context_id, viewport={"width": 254, "height": 304}, device_pixel_ratio=10 ) driver.browsing_context.set_viewport(context=context_id, viewport=None, device_pixel_ratio=None) From 44c72f4bb49f9ac847b64e1e8da20637ecd7e63a Mon Sep 17 00:00:00 2001 From: Navin Chandra Date: Fri, 21 Nov 2025 20:20:47 +0530 Subject: [PATCH 06/10] contexts -> context --- .../webdriver/common/bidi/browsing_context.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/py/selenium/webdriver/common/bidi/browsing_context.py b/py/selenium/webdriver/common/bidi/browsing_context.py index baf8919b91ac3..d92dccbf17815 100644 --- a/py/selenium/webdriver/common/bidi/browsing_context.py +++ b/py/selenium/webdriver/common/bidi/browsing_context.py @@ -1002,15 +1002,15 @@ def set_viewport( ValueError: If both `contexts` and `user_contexts` are used """ - if contexts is not None and user_contexts is not None: - raise ValueError("Cannot specify both contexts and user_contexts") + if context is not None and user_contexts is not None: + raise ValueError("Cannot specify both context and user_contexts") - if contexts is None and user_contexts is None: - raise ValueError("Must specify either contexts or user_contexts") + if context is None and user_contexts is None: + raise ValueError("Must specify either context or user_contexts") params: dict[str, Any] = {} - if contexts is not None: - params["contexts"] = contexts + if context is not None: + params["context"] = context elif user_contexts is not None: params["userContexts"] = user_contexts if viewport is UNDEFINED: From fa2d6e83f6837d6fa268fdcd951f5c249b5c5b07 Mon Sep 17 00:00:00 2001 From: Navin Chandra Date: Fri, 21 Nov 2025 20:22:57 +0530 Subject: [PATCH 07/10] run formatter --- py/test/selenium/webdriver/common/bidi_browsing_context_tests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py b/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py index 0b07d97953dda..c5c9284418ba2 100644 --- a/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py +++ b/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py @@ -405,7 +405,6 @@ def test_set_viewport_with_no_args_doesnt_change_values(driver, pages): driver.browsing_context.set_viewport(context=context_id, viewport=None, device_pixel_ratio=None) - def test_set_viewport_back_to_default(driver, pages): """Test resetting the viewport and device pixel ratio to defaults.""" context_id = driver.current_window_handle From 6944c36c775e0de5d53b7895770905bbac2a2e20 Mon Sep 17 00:00:00 2001 From: Navin Chandra Date: Fri, 21 Nov 2025 20:39:51 +0530 Subject: [PATCH 08/10] lint again --- py/selenium/webdriver/common/bidi/browsing_context.py | 1 - 1 file changed, 1 deletion(-) diff --git a/py/selenium/webdriver/common/bidi/browsing_context.py b/py/selenium/webdriver/common/bidi/browsing_context.py index d92dccbf17815..d68cb9f8c5243 100644 --- a/py/selenium/webdriver/common/bidi/browsing_context.py +++ b/py/selenium/webdriver/common/bidi/browsing_context.py @@ -1001,7 +1001,6 @@ def set_viewport( ValueError: If neither `contexts` or `user_contexts` are used ValueError: If both `contexts` and `user_contexts` are used """ - if context is not None and user_contexts is not None: raise ValueError("Cannot specify both context and user_contexts") From a3763e6078e5805b972b96239969c5ac968f561d Mon Sep 17 00:00:00 2001 From: Navin Chandra Date: Fri, 21 Nov 2025 23:18:41 +0530 Subject: [PATCH 09/10] improve docstring --- py/selenium/webdriver/common/bidi/browsing_context.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/py/selenium/webdriver/common/bidi/browsing_context.py b/py/selenium/webdriver/common/bidi/browsing_context.py index d68cb9f8c5243..228e31707f336 100644 --- a/py/selenium/webdriver/common/bidi/browsing_context.py +++ b/py/selenium/webdriver/common/bidi/browsing_context.py @@ -992,14 +992,14 @@ def set_viewport( Args: context: The browsing context ID. - viewport: The viewport parameters (`None` resets to default). - device_pixel_ratio: The device pixel ratio (`None` resets default). + viewport: The viewport parameters - {"width": , "height": } (`None` resets to default). + device_pixel_ratio: The device pixel ratio (`None` resets to default). user_contexts: The user context IDs. Raises: Exception: If the browsing context is not a top-level traversable - ValueError: If neither `contexts` or `user_contexts` are used - ValueError: If both `contexts` and `user_contexts` are used + ValueError: If neither `context` nor `user_contexts` is provided + ValueError: If both `context` and `user_contexts` are provided """ if context is not None and user_contexts is not None: raise ValueError("Cannot specify both context and user_contexts") From 9c27b4f029868040784b2b7e729ae09b0ec10153 Mon Sep 17 00:00:00 2001 From: Corey Goldberg <1113081+cgoldberg@users.noreply.github.com> Date: Mon, 24 Nov 2025 09:26:59 -0500 Subject: [PATCH 10/10] Simplify checking for sentinel --- .../webdriver/common/bidi/browsing_context.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/py/selenium/webdriver/common/bidi/browsing_context.py b/py/selenium/webdriver/common/bidi/browsing_context.py index 228e31707f336..696a4f73bbb5f 100644 --- a/py/selenium/webdriver/common/bidi/browsing_context.py +++ b/py/selenium/webdriver/common/bidi/browsing_context.py @@ -1012,17 +1012,9 @@ def set_viewport( params["context"] = context elif user_contexts is not None: params["userContexts"] = user_contexts - if viewport is UNDEFINED: - pass - elif viewport is None: - params["viewport"] = None - else: + if viewport is not UNDEFINED: params["viewport"] = viewport - if device_pixel_ratio is UNDEFINED: - pass - elif device_pixel_ratio is None: - params["devicePixelRatio"] = None - else: + if device_pixel_ratio is not UNDEFINED: params["devicePixelRatio"] = device_pixel_ratio self.conn.execute(command_builder("browsingContext.setViewport", params))