diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d7df52520..fcb21c01f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ This project adheres to [Semantic Versioning](https://semver.org/). - [#2050](https://github.com/plotly/dash/pull/2050) Changed `find_element` and `find_elements` to accept an `attribute` argument that aligns with Selenium's `By` class, allowing you to search elements by other attributes. Default value is `CSS_SELECTOR` to maintain backwards compatibility with previous `find_elements`. +### Added + +- [#2049](https://github.com/plotly/dash/pull/2043) Added `wait_for_class_to_equal` and `wait_for_contains_class` methods to `dash.testing` + ## [2.4.1] - 2022-05-11 ### Fixed diff --git a/dash/testing/browser.py b/dash/testing/browser.py index 29203dedd8..6b3dc941fc 100644 --- a/dash/testing/browser.py +++ b/dash/testing/browser.py @@ -20,7 +20,14 @@ MoveTargetOutOfBoundsException, ) -from dash.testing.wait import text_to_equal, style_to_equal, contains_text, until +from dash.testing.wait import ( + text_to_equal, + style_to_equal, + class_to_equal, + contains_text, + contains_class, + until, +) from dash.testing.dash_page import DashPageMixin from dash.testing.errors import DashAppLoadingError, BrowserError, TestingTimeoutError from dash.testing.consts import SELENIUM_GRID_DEFAULT @@ -309,6 +316,17 @@ def wait_for_element_by_id(self, element_id, timeout=None): f"timeout {timeout or self._wait_timeout}s => waiting for element id {element_id}", ) + def wait_for_class_to_equal(self, selector, classname, timeout=None): + """Explicit wait until the element's class has expected `value` timeout + if not set, equals to the fixture's `wait_timeout` shortcut to + `WebDriverWait` with customized `class_to_equal` condition.""" + return self._wait_for( + method=class_to_equal, + args=(selector, classname), + timeout=timeout, + msg=f"classname => {classname} not found within {timeout or self._wait_timeout}s", + ) + def wait_for_style_to_equal(self, selector, style, val, timeout=None): """Explicit wait until the element's style has expected `value` timeout if not set, equals to the fixture's `wait_timeout` shortcut to @@ -334,6 +352,20 @@ def wait_for_text_to_equal(self, selector, text, timeout=None): msg=f"text -> {text} not found within {timeout or self._wait_timeout}s", ) + def wait_for_contains_class(self, selector, classname, timeout=None): + """Explicit wait until the element's classes contains the expected `classname`. + + timeout if not set, equals to the fixture's `wait_timeout` + shortcut to `WebDriverWait` with customized `contains_class` + condition. + """ + return self._wait_for( + method=contains_class, + args=(selector, classname), + timeout=timeout, + msg=f"classname -> {classname} not found inside element within {timeout or self._wait_timeout}s", + ) + def wait_for_contains_text(self, selector, text, timeout=None): """Explicit wait until the element's text contains the expected `text`. diff --git a/dash/testing/wait.py b/dash/testing/wait.py index 7c877c0e0c..c0d41baa70 100644 --- a/dash/testing/wait.py +++ b/dash/testing/wait.py @@ -68,6 +68,23 @@ def __call__(self, driver): return False +class contains_class: + def __init__(self, selector, classname): + self.selector = selector + self.classname = classname + + def __call__(self, driver): + try: + elem = driver.find_element(By.CSS_SELECTOR, self.selector) + classname = elem.get_attribute("class") + logger.debug( + "contains class {%s} => expected %s", classname, self.classname + ) + return self.classname in str(classname).split(" ") + except WebDriverException: + return False + + class text_to_equal: def __init__(self, selector, text): self.selector = selector @@ -99,3 +116,20 @@ def __call__(self, driver): return val == self.val except WebDriverException: return False + + +class class_to_equal: + def __init__(self, selector, classname): + self.selector = selector + self.classname = classname + + def __call__(self, driver): + try: + elem = driver.find_element(By.CSS_SELECTOR, self.selector) + classname = elem.get_attribute("class") + logger.debug( + "class to equal {%s} => expected %s", classname, self.classname + ) + return str(classname) == self.classname + except WebDriverException: + return False