Skip to content

Commit

Permalink
Display target text on wait_for texts errors.
Browse files Browse the repository at this point in the history
  • Loading branch information
T4rk1n committed Feb 8, 2023
1 parent e452045 commit 7a6ac6a
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 30 deletions.
63 changes: 36 additions & 27 deletions dash/testing/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,19 +262,28 @@ def _get_element(self, elem_or_selector):
return self.find_element(elem_or_selector)
return elem_or_selector

def _wait_for(self, method, args, timeout, msg):
def _wait_for(self, method, timeout, msg):
"""Abstract generic pattern for explicit WebDriverWait."""
_wait = (
self._wd_wait if timeout is None else WebDriverWait(self.driver, timeout)
)
logger.debug(
"method, timeout, poll => %s %s %s",
method,
_wait._timeout, # pylint: disable=protected-access
_wait._poll, # pylint: disable=protected-access
)
try:
_wait = (
self._wd_wait
if timeout is None
else WebDriverWait(self.driver, timeout)
)
logger.debug(
"method, timeout, poll => %s %s %s",
method,
_wait._timeout, # pylint: disable=protected-access
_wait._poll, # pylint: disable=protected-access
)

return _wait.until(method(*args), msg)
return _wait.until(method)
except Exception as err:
if callable(msg):
message = msg(self.driver)
else:
message = msg
raise TimeoutException(message) from err

def wait_for_element(self, selector, timeout=None):
"""wait_for_element is shortcut to `wait_for_element_by_css_selector`
Expand All @@ -286,8 +295,9 @@ def wait_for_element_by_css_selector(self, selector, timeout=None):
equals to the fixture's `wait_timeout` shortcut to `WebDriverWait` with
`EC.presence_of_element_located`."""
return self._wait_for(
EC.presence_of_element_located,
((By.CSS_SELECTOR, selector),),
EC.presence_of_element_located(
(By.CSS_SELECTOR, selector),
),
timeout,
f"timeout {timeout or self._wait_timeout}s => waiting for selector {selector}",
)
Expand All @@ -310,8 +320,9 @@ def wait_for_element_by_id(self, element_id, timeout=None):
equals to the fixture's `wait_timeout` shortcut to `WebDriverWait` with
`EC.presence_of_element_located`."""
return self._wait_for(
EC.presence_of_element_located,
((By.ID, element_id),),
EC.presence_of_element_located(
(By.ID, element_id),
),
timeout,
f"timeout {timeout or self._wait_timeout}s => waiting for element id {element_id}",
)
Expand All @@ -321,8 +332,7 @@ def wait_for_class_to_equal(self, selector, classname, timeout=None):
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),
method=class_to_equal(selector, classname),
timeout=timeout,
msg=f"classname => {classname} not found within {timeout or self._wait_timeout}s",
)
Expand All @@ -332,8 +342,7 @@ def wait_for_style_to_equal(self, selector, style, val, timeout=None):
if not set, equals to the fixture's `wait_timeout` shortcut to
`WebDriverWait` with customized `style_to_equal` condition."""
return self._wait_for(
method=style_to_equal,
args=(selector, style, val),
method=style_to_equal(selector, style, val),
timeout=timeout,
msg=f"style val => {style} {val} not found within {timeout or self._wait_timeout}s",
)
Expand All @@ -345,11 +354,12 @@ def wait_for_text_to_equal(self, selector, text, timeout=None):
shortcut to `WebDriverWait` with customized `text_to_equal`
condition.
"""
method = text_to_equal(selector, text, timeout or self.wait_timeout)

return self._wait_for(
method=text_to_equal,
args=(selector, text),
method=method,
timeout=timeout,
msg=f"text -> {text} not found within {timeout or self._wait_timeout}s",
msg=method.message,
)

def wait_for_contains_class(self, selector, classname, timeout=None):
Expand All @@ -360,8 +370,7 @@ def wait_for_contains_class(self, selector, classname, timeout=None):
condition.
"""
return self._wait_for(
method=contains_class,
args=(selector, classname),
method=contains_class(selector, classname),
timeout=timeout,
msg=f"classname -> {classname} not found inside element within {timeout or self._wait_timeout}s",
)
Expand All @@ -373,11 +382,11 @@ def wait_for_contains_text(self, selector, text, timeout=None):
shortcut to `WebDriverWait` with customized `contains_text`
condition.
"""
method = contains_text(selector, text, timeout or self.wait_timeout)
return self._wait_for(
method=contains_text,
args=(selector, text),
method=method,
timeout=timeout,
msg=f"text -> {text} not found inside element within {timeout or self._wait_timeout}s",
msg=method.message,
)

def wait_for_page(self, url=None, timeout=10):
Expand Down
1 change: 1 addition & 0 deletions dash/testing/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def __init__(self, **kwargs):
)
from dash.testing.browser import Browser
from dash.testing.composite import DashComposite, DashRComposite, DashJuliaComposite

_installed = True
except ImportError:
# Running pytest without dash[testing] installed.
Expand Down
30 changes: 27 additions & 3 deletions dash/testing/wait.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ def until_not(


class contains_text:
def __init__(self, selector, text):
def __init__(self, selector, text, timeout):
self.selector = selector
self.text = text
self.timeout = timeout

def __call__(self, driver):
try:
Expand All @@ -67,6 +68,17 @@ def __call__(self, driver):
except WebDriverException:
return False

def message(self, driver):
try:
element = self._get_element(driver)
text = "found: " + str(element.text) or str(element.get_attribute("value"))
except WebDriverException:
text = f"{self.selector} not found"
return f"text -> {self.text} not found inside element within {self.timeout}s, {text}"

def _get_element(self, driver):
return driver.find_element(By.CSS_SELECTOR, self.selector)


class contains_class:
def __init__(self, selector, classname):
Expand All @@ -86,13 +98,14 @@ def __call__(self, driver):


class text_to_equal:
def __init__(self, selector, text):
def __init__(self, selector, text, timeout):
self.selector = selector
self.text = text
self.timeout = timeout

def __call__(self, driver):
try:
elem = driver.find_element(By.CSS_SELECTOR, self.selector)
elem = self._get_element(driver)
logger.debug("text to equal {%s} => expected %s", elem.text, self.text)
return (
str(elem.text) == self.text
Expand All @@ -101,6 +114,17 @@ def __call__(self, driver):
except WebDriverException:
return False

def message(self, driver):
try:
element = self._get_element(driver)
text = "found: " + str(element.text) or str(element.get_attribute("value"))
except WebDriverException:
text = f"{self.selector} not found"
return f"text -> {self.text} not found within {self.timeout}s, {text}"

def _get_element(self, driver):
return driver.find_element(By.CSS_SELECTOR, self.selector)


class style_to_equal:
def __init__(self, selector, style, val):
Expand Down
36 changes: 36 additions & 0 deletions tests/integration/test_duo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import pytest
from selenium.common import TimeoutException

from dash import Dash, html


def test_duo001_wait_for_text_error(dash_duo):
app = Dash(__name__)
app.layout = html.Div([html.Div("Content", id="content")])
dash_duo.start_server(app)

with pytest.raises(TimeoutException) as err:
dash_duo.wait_for_text_to_equal("#content", "Invalid", timeout=1.0)

assert err.value.args[0] == "text -> Invalid not found within 1.0s, found: Content"

with pytest.raises(TimeoutException) as err:
dash_duo.wait_for_text_to_equal("#none", "None", timeout=1.0)

assert err.value.args[0] == "text -> None not found within 1.0s, #none not found"

with pytest.raises(TimeoutException) as err:
dash_duo.wait_for_contains_text("#content", "invalid", timeout=1.0)

assert (
err.value.args[0]
== "text -> invalid not found inside element within 1.0s, found: Content"
)

with pytest.raises(TimeoutException) as err:
dash_duo.wait_for_contains_text("#none", "none", timeout=1.0)

assert (
err.value.args[0]
== "text -> none not found inside element within 1.0s, #none not found"
)

0 comments on commit 7a6ac6a

Please sign in to comment.