From 147913aef0c5686ad14df6fc8d53f44f5260a276 Mon Sep 17 00:00:00 2001 From: Delta456 Date: Thu, 8 May 2025 18:28:36 +0530 Subject: [PATCH 1/4] [py] support for custom error messages through functions for `WebDriverWait.until/until_not` --- py/selenium/webdriver/support/wait.py | 20 +++++++++++++------ .../webdriver/common/webdriverwait_tests.py | 15 ++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/py/selenium/webdriver/support/wait.py b/py/selenium/webdriver/support/wait.py index 0f452ca752b97..34824cdf844b6 100644 --- a/py/selenium/webdriver/support/wait.py +++ b/py/selenium/webdriver/support/wait.py @@ -16,6 +16,7 @@ # under the License. import time +from typing import Any from typing import Callable from typing import Generic from typing import Literal @@ -92,7 +93,9 @@ def __init__( def __repr__(self) -> str: return f'<{type(self).__module__}.{type(self).__name__} (session="{self._driver.session_id}")>' - def until(self, method: Callable[[D], Union[Literal[False], T]], message: str = "") -> T: + def until( + self, method: Callable[[D], Union[Literal[False], T]], message: Union[str, Callable[[Any], str]] = "" + ) -> T: """Wait until the method returns a value that is not False. Calls the method provided with the driver as an argument until the @@ -103,7 +106,7 @@ def until(self, method: Callable[[D], Union[Literal[False], T]], message: str = method: callable(WebDriver) - A callable object that takes a WebDriver instance as an argument. - message: str + message: Union[str, Callable[[Any], str]] - Optional message for :exc:`TimeoutException` Return: @@ -143,9 +146,13 @@ def until(self, method: Callable[[D], Union[Literal[False], T]], message: str = if time.monotonic() > end_time: break time.sleep(self._poll) - raise TimeoutException(message, screen, stacktrace) - def until_not(self, method: Callable[[D], T], message: str = "") -> Union[T, Literal[True]]: + final_msg = message() if callable(message) else message + raise TimeoutException(final_msg, screen, stacktrace) + + def until_not( + self, method: Callable[[D], T], message: Union[str, Callable[[Any], str]] = "" + ) -> Union[T, Literal[True]]: """Wait until the method returns a value that is not False. Calls the method provided with the driver as an argument until the @@ -156,7 +163,7 @@ def until_not(self, method: Callable[[D], T], message: str = "") -> Union[T, Lit method: callable(WebDriver) - A callable object that takes a WebDriver instance as an argument. - message: str + message: Union[str, Callable[[Any], str]] - Optional message for :exc:`TimeoutException` Return: @@ -192,4 +199,5 @@ def until_not(self, method: Callable[[D], T], message: str = "") -> Union[T, Lit if time.monotonic() > end_time: break time.sleep(self._poll) - raise TimeoutException(message) + final_msg = message() if callable(message) else message + raise TimeoutException(final_msg) diff --git a/py/test/selenium/webdriver/common/webdriverwait_tests.py b/py/test/selenium/webdriver/common/webdriverwait_tests.py index 51e96ffca3907..d3acb53b5b596 100644 --- a/py/test/selenium/webdriver/common/webdriverwait_tests.py +++ b/py/test/selenium/webdriver/common/webdriverwait_tests.py @@ -58,6 +58,21 @@ def test_should_still_fail_to_find_an_element_with_explicit_wait(driver, pages): WebDriverWait(driver, 0.01).until(EC.presence_of_element_located((By.ID, "box0"))) +def test_should_still_fail_to_find_an_element_with_explicit_wait_with_custom_timeout_messages(driver, pages): + pages.load("dynamic.html") + with pytest.raises(TimeoutException) as exception: + WebDriverWait(driver, 0.01).until( + EC.presence_of_element_located((By.ID, "box0")), message=lambda: "custom timeout message" + ) + assert "custom timeout message" in str(exception.value) + + with pytest.raises(TimeoutException) as exception: + WebDriverWait(driver, 0.01).until( + EC.presence_of_element_located((By.ID, "box0")), message="custom timeout message" + ) + assert "custom timeout message" in str(exception.value) + + def test_should_explicitly_wait_until_at_least_one_element_is_found_when_searching_for_many(driver, pages): pages.load("dynamic.html") add = driver.find_element(By.ID, "adder") From c320e83bbf4d96bc174649f55c93b2ebb2a89049 Mon Sep 17 00:00:00 2001 From: Delta456 Date: Wed, 14 May 2025 14:41:48 +0530 Subject: [PATCH 2/4] remove lambda param --- py/selenium/webdriver/support/wait.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py/selenium/webdriver/support/wait.py b/py/selenium/webdriver/support/wait.py index 34824cdf844b6..407626697fe32 100644 --- a/py/selenium/webdriver/support/wait.py +++ b/py/selenium/webdriver/support/wait.py @@ -94,7 +94,7 @@ def __repr__(self) -> str: return f'<{type(self).__module__}.{type(self).__name__} (session="{self._driver.session_id}")>' def until( - self, method: Callable[[D], Union[Literal[False], T]], message: Union[str, Callable[[Any], str]] = "" + self, method: Callable[[D], Union[Literal[False], T]], message: Union[str, Callable[[], str]] = "" ) -> T: """Wait until the method returns a value that is not False. @@ -151,7 +151,7 @@ def until( raise TimeoutException(final_msg, screen, stacktrace) def until_not( - self, method: Callable[[D], T], message: Union[str, Callable[[Any], str]] = "" + self, method: Callable[[D], T], message: Union[str, Callable[[], str]] = "" ) -> Union[T, Literal[True]]: """Wait until the method returns a value that is not False. From 95720d19f72c0427d20d4098604a43ea453833bc Mon Sep 17 00:00:00 2001 From: Delta456 Date: Wed, 14 May 2025 15:20:41 +0530 Subject: [PATCH 3/4] fmt --- py/selenium/webdriver/support/wait.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/py/selenium/webdriver/support/wait.py b/py/selenium/webdriver/support/wait.py index 407626697fe32..4bfafaea21096 100644 --- a/py/selenium/webdriver/support/wait.py +++ b/py/selenium/webdriver/support/wait.py @@ -93,9 +93,7 @@ def __init__( def __repr__(self) -> str: return f'<{type(self).__module__}.{type(self).__name__} (session="{self._driver.session_id}")>' - def until( - self, method: Callable[[D], Union[Literal[False], T]], message: Union[str, Callable[[], str]] = "" - ) -> T: + def until(self, method: Callable[[D], Union[Literal[False], T]], message: Union[str, Callable[[], str]] = "") -> T: """Wait until the method returns a value that is not False. Calls the method provided with the driver as an argument until the From af49b067b181937a4ad9edcdadedee2a5c603551 Mon Sep 17 00:00:00 2001 From: Delta456 Date: Wed, 14 May 2025 15:31:24 +0530 Subject: [PATCH 4/4] remove import --- py/selenium/webdriver/support/wait.py | 1 - 1 file changed, 1 deletion(-) diff --git a/py/selenium/webdriver/support/wait.py b/py/selenium/webdriver/support/wait.py index 4bfafaea21096..b83e40dfa1a0f 100644 --- a/py/selenium/webdriver/support/wait.py +++ b/py/selenium/webdriver/support/wait.py @@ -16,7 +16,6 @@ # under the License. import time -from typing import Any from typing import Callable from typing import Generic from typing import Literal