diff --git a/screenpy_selenium/actions/wait.py b/screenpy_selenium/actions/wait.py index 6a24cb1..25b982c 100644 --- a/screenpy_selenium/actions/wait.py +++ b/screenpy_selenium/actions/wait.py @@ -17,6 +17,8 @@ from collections.abc import Iterable from screenpy import Actor + from selenium.types import WaitExcTypes + from selenium.webdriver.remote.webelement import WebElement from typing_extensions import Self from screenpy_selenium.target import Target @@ -56,9 +58,10 @@ class Wait: args: Iterable[Any] timeout: float log_detail: str | None + 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 +70,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) @@ -140,6 +143,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}." @@ -150,9 +158,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 " @@ -169,3 +177,4 @@ def __init__( self.timeout = seconds if seconds is not None else settings.TIMEOUT self.condition = selenium_conditions.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 578b09f..4c53f96 100644 --- a/tests/test_actions.py +++ b/tests/test_actions.py @@ -11,7 +11,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 selenium_conditions @@ -1565,9 +1569,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( @@ -1590,12 +1592,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, + (StaleElementReferenceException, NoSuchFrameException), ) mocked_ec.visibility_of_element_located.assert_called_once_with(test_target) mocked_webdriverwait(mocked_browser, timeout).until.assert_called_once_with(