From baab3373ee45eb208d9fdc17ddd90904f91110ec Mon Sep 17 00:00:00 2001 From: Marcel Wilson Date: Wed, 27 Aug 2025 16:13:24 -0500 Subject: [PATCH 1/2] adding feature to override ignored exceptions - #69 --- screenpy_selenium/actions/wait.py | 14 +++++++++++--- tests/test_actions.py | 17 +++++++++++++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/screenpy_selenium/actions/wait.py b/screenpy_selenium/actions/wait.py index a9c89e7..a91bf74 100644 --- a/screenpy_selenium/actions/wait.py +++ b/screenpy_selenium/actions/wait.py @@ -15,6 +15,7 @@ if TYPE_CHECKING: from screenpy import Actor + from selenium.types import WaitExcTypes from typing_extensions import Self from ..target import Target @@ -54,6 +55,7 @@ class Wait: args: Iterable[Any] timeout: float log_detail: str | None + ignored_exceptions: WaitExcTypes | None @classmethod def for_the(cls, target: Target) -> Self: @@ -128,6 +130,11 @@ def log_message(self) -> str: return self.log_detail.format(*self.args) + def ignoring(self, *ignored_exceptions: type[Exception]) -> Self: + """Set the expception classes to ignore.""" + self.ignored_exceptions = ignored_exceptions + return self + def describe(self) -> str: """Describe the Action in present tense.""" return f"Wait {self.timeout} seconds {self.log_message}." @@ -138,9 +145,9 @@ def perform_as(self, the_actor: Actor) -> None: browser = the_actor.ability_to(BrowseTheWeb).browser try: - WebDriverWait(browser, self.timeout, settings.POLLING).until( - self.condition(*self.args) - ) + WebDriverWait( + browser, self.timeout, settings.POLLING, self.ignored_exceptions + ).until(self.condition(*self.args)) except WebDriverException as e: msg = ( f"Encountered an exception using {self.condition.__name__} with " @@ -155,3 +162,4 @@ def __init__( self.timeout = seconds if seconds is not None else settings.TIMEOUT self.condition = EC.visibility_of_element_located self.log_detail = None + self.ignored_exceptions = None diff --git a/tests/test_actions.py b/tests/test_actions.py index 2061bd3..8e24c92 100644 --- a/tests/test_actions.py +++ b/tests/test_actions.py @@ -10,7 +10,11 @@ from screenpy import DeliveryError, Describable, Performable, UnableToAct, settings from screenpy.configuration import ScreenPySettings from screenpy_pyotp.abilities import AuthenticateWith2FA -from selenium.common.exceptions import WebDriverException +from selenium.common.exceptions import ( + NoSuchFrameException, + StaleElementReferenceException, + WebDriverException, +) from selenium.webdriver.common.keys import Keys from selenium.webdriver.remote.webelement import WebElement from selenium.webdriver.support import expected_conditions as EC @@ -1525,7 +1529,7 @@ def test_defaults( Wait.for_the(test_target).perform_as(Tester) mocked_webdriverwait.assert_called_once_with( - mocked_browser, settings.TIMEOUT, settings.POLLING + mocked_browser, settings.TIMEOUT, settings.POLLING, None ) mocked_ec.visibility_of_element_located.assert_called_once_with(test_target) mocked_webdriverwait( @@ -1544,10 +1548,15 @@ def test_override( mocked_browser = get_mocked_browser(Tester) timeout = 4 - Wait(timeout).seconds_for(test_target).perform_as(Tester) + Wait(timeout).seconds_for(test_target).ignoring( + StaleElementReferenceException, NoSuchFrameException + ).perform_as(Tester) mocked_webdriverwait.assert_called_once_with( - mocked_browser, timeout, settings.POLLING + mocked_browser, + timeout, + settings.POLLING, + (StaleElementReferenceException, NoSuchFrameException), ) mocked_ec.visibility_of_element_located.assert_called_once_with(test_target) mocked_webdriverwait(mocked_browser, timeout).until.assert_called_once_with( From c6c376af197b6d21eb55f74d79452c2aa4e723df Mon Sep 17 00:00:00 2001 From: Marcel Wilson Date: Thu, 28 Aug 2025 13:40:26 -0500 Subject: [PATCH 2/2] fixing annotations for target since technically webelements can be submitted too --- screenpy_selenium/actions/wait.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/screenpy_selenium/actions/wait.py b/screenpy_selenium/actions/wait.py index a91bf74..7cf74c8 100644 --- a/screenpy_selenium/actions/wait.py +++ b/screenpy_selenium/actions/wait.py @@ -16,6 +16,7 @@ if TYPE_CHECKING: from screenpy import Actor from selenium.types import WaitExcTypes + from selenium.webdriver.remote.webelement import WebElement from typing_extensions import Self from ..target import Target @@ -58,7 +59,7 @@ class Wait: ignored_exceptions: WaitExcTypes | None @classmethod - def for_the(cls, target: Target) -> Self: + def for_the(cls, target: Target | WebElement) -> Self: """Set the Target to wait for. Aliases: @@ -67,7 +68,7 @@ def for_the(cls, target: Target) -> Self: return cls(seconds=settings.TIMEOUT, args=[target]) @classmethod - def for_(cls, target: Target) -> Self: + def for_(cls, target: Target | WebElement) -> Self: """Alias for :meth:`~screenpy_selenium.actions.Wait.for_the`.""" return cls.for_the(target=target)