Skip to content

Commit 799d981

Browse files
committed
Display target text on wait_for texts errors.
1 parent e452045 commit 799d981

File tree

4 files changed

+100
-30
lines changed

4 files changed

+100
-30
lines changed

Diff for: dash/testing/browser.py

+36-27
Original file line numberDiff line numberDiff line change
@@ -262,19 +262,28 @@ def _get_element(self, elem_or_selector):
262262
return self.find_element(elem_or_selector)
263263
return elem_or_selector
264264

265-
def _wait_for(self, method, args, timeout, msg):
265+
def _wait_for(self, method, timeout, msg):
266266
"""Abstract generic pattern for explicit WebDriverWait."""
267-
_wait = (
268-
self._wd_wait if timeout is None else WebDriverWait(self.driver, timeout)
269-
)
270-
logger.debug(
271-
"method, timeout, poll => %s %s %s",
272-
method,
273-
_wait._timeout, # pylint: disable=protected-access
274-
_wait._poll, # pylint: disable=protected-access
275-
)
267+
try:
268+
_wait = (
269+
self._wd_wait
270+
if timeout is None
271+
else WebDriverWait(self.driver, timeout)
272+
)
273+
logger.debug(
274+
"method, timeout, poll => %s %s %s",
275+
method,
276+
_wait._timeout, # pylint: disable=protected-access
277+
_wait._poll, # pylint: disable=protected-access
278+
)
276279

277-
return _wait.until(method(*args), msg)
280+
return _wait.until(method)
281+
except Exception as err:
282+
if callable(msg):
283+
message = msg(self.driver)
284+
else:
285+
message = msg
286+
raise TimeoutException(message) from err
278287

279288
def wait_for_element(self, selector, timeout=None):
280289
"""wait_for_element is shortcut to `wait_for_element_by_css_selector`
@@ -286,8 +295,9 @@ def wait_for_element_by_css_selector(self, selector, timeout=None):
286295
equals to the fixture's `wait_timeout` shortcut to `WebDriverWait` with
287296
`EC.presence_of_element_located`."""
288297
return self._wait_for(
289-
EC.presence_of_element_located,
290-
((By.CSS_SELECTOR, selector),),
298+
EC.presence_of_element_located(
299+
(By.CSS_SELECTOR, selector),
300+
),
291301
timeout,
292302
f"timeout {timeout or self._wait_timeout}s => waiting for selector {selector}",
293303
)
@@ -310,8 +320,9 @@ def wait_for_element_by_id(self, element_id, timeout=None):
310320
equals to the fixture's `wait_timeout` shortcut to `WebDriverWait` with
311321
`EC.presence_of_element_located`."""
312322
return self._wait_for(
313-
EC.presence_of_element_located,
314-
((By.ID, element_id),),
323+
EC.presence_of_element_located(
324+
(By.ID, element_id),
325+
),
315326
timeout,
316327
f"timeout {timeout or self._wait_timeout}s => waiting for element id {element_id}",
317328
)
@@ -321,8 +332,7 @@ def wait_for_class_to_equal(self, selector, classname, timeout=None):
321332
if not set, equals to the fixture's `wait_timeout` shortcut to
322333
`WebDriverWait` with customized `class_to_equal` condition."""
323334
return self._wait_for(
324-
method=class_to_equal,
325-
args=(selector, classname),
335+
method=class_to_equal(selector, classname),
326336
timeout=timeout,
327337
msg=f"classname => {classname} not found within {timeout or self._wait_timeout}s",
328338
)
@@ -332,8 +342,7 @@ def wait_for_style_to_equal(self, selector, style, val, timeout=None):
332342
if not set, equals to the fixture's `wait_timeout` shortcut to
333343
`WebDriverWait` with customized `style_to_equal` condition."""
334344
return self._wait_for(
335-
method=style_to_equal,
336-
args=(selector, style, val),
345+
method=style_to_equal(selector, style, val),
337346
timeout=timeout,
338347
msg=f"style val => {style} {val} not found within {timeout or self._wait_timeout}s",
339348
)
@@ -345,11 +354,12 @@ def wait_for_text_to_equal(self, selector, text, timeout=None):
345354
shortcut to `WebDriverWait` with customized `text_to_equal`
346355
condition.
347356
"""
357+
method = text_to_equal(selector, text, timeout or self.wait_timeout)
358+
348359
return self._wait_for(
349-
method=text_to_equal,
350-
args=(selector, text),
360+
method=method,
351361
timeout=timeout,
352-
msg=f"text -> {text} not found within {timeout or self._wait_timeout}s",
362+
msg=method.message,
353363
)
354364

355365
def wait_for_contains_class(self, selector, classname, timeout=None):
@@ -360,8 +370,7 @@ def wait_for_contains_class(self, selector, classname, timeout=None):
360370
condition.
361371
"""
362372
return self._wait_for(
363-
method=contains_class,
364-
args=(selector, classname),
373+
method=contains_class(selector, classname),
365374
timeout=timeout,
366375
msg=f"classname -> {classname} not found inside element within {timeout or self._wait_timeout}s",
367376
)
@@ -373,11 +382,11 @@ def wait_for_contains_text(self, selector, text, timeout=None):
373382
shortcut to `WebDriverWait` with customized `contains_text`
374383
condition.
375384
"""
385+
method = contains_text(selector, text, timeout or self.wait_timeout)
376386
return self._wait_for(
377-
method=contains_text,
378-
args=(selector, text),
387+
method=method,
379388
timeout=timeout,
380-
msg=f"text -> {text} not found inside element within {timeout or self._wait_timeout}s",
389+
msg=method.message,
381390
)
382391

383392
def wait_for_page(self, url=None, timeout=10):

Diff for: dash/testing/plugin.py

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def __init__(self, **kwargs):
2222
)
2323
from dash.testing.browser import Browser
2424
from dash.testing.composite import DashComposite, DashRComposite, DashJuliaComposite
25+
2526
_installed = True
2627
except ImportError:
2728
# Running pytest without dash[testing] installed.

Diff for: dash/testing/wait.py

+27-3
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,10 @@ def until_not(
5353

5454

5555
class contains_text:
56-
def __init__(self, selector, text):
56+
def __init__(self, selector, text, timeout):
5757
self.selector = selector
5858
self.text = text
59+
self.timeout = timeout
5960

6061
def __call__(self, driver):
6162
try:
@@ -67,6 +68,17 @@ def __call__(self, driver):
6768
except WebDriverException:
6869
return False
6970

71+
def message(self, driver):
72+
try:
73+
element = self._get_element(driver)
74+
text = "found: " + str(element.text) or str(element.get_attribute("value"))
75+
except WebDriverException:
76+
text = f"{self.selector} not found"
77+
return f"text -> {self.text} not found inside element within {self.timeout}s, {text}"
78+
79+
def _get_element(self, driver):
80+
return driver.find_element(By.CSS_SELECTOR, self.selector)
81+
7082

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

8799

88100
class text_to_equal:
89-
def __init__(self, selector, text):
101+
def __init__(self, selector, text, timeout):
90102
self.selector = selector
91103
self.text = text
104+
self.timeout = timeout
92105

93106
def __call__(self, driver):
94107
try:
95-
elem = driver.find_element(By.CSS_SELECTOR, self.selector)
108+
elem = self._get_element(driver)
96109
logger.debug("text to equal {%s} => expected %s", elem.text, self.text)
97110
return (
98111
str(elem.text) == self.text
@@ -101,6 +114,17 @@ def __call__(self, driver):
101114
except WebDriverException:
102115
return False
103116

117+
def message(self, driver):
118+
try:
119+
element = self._get_element(driver)
120+
text = "found: " + str(element.text) or str(element.get_attribute("value"))
121+
except WebDriverException:
122+
text = f"{self.selector} not found"
123+
return f"text -> {self.text} not found within {self.timeout}s, {text}"
124+
125+
def _get_element(self, driver):
126+
return driver.find_element(By.CSS_SELECTOR, self.selector)
127+
104128

105129
class style_to_equal:
106130
def __init__(self, selector, style, val):

Diff for: tests/integration/test_duo.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import pytest
2+
from selenium.common.exceptions import TimeoutException
3+
4+
from dash import Dash, html
5+
6+
7+
def test_duo001_wait_for_text_error(dash_duo):
8+
app = Dash(__name__)
9+
app.layout = html.Div([html.Div("Content", id="content")])
10+
dash_duo.start_server(app)
11+
12+
with pytest.raises(TimeoutException) as err:
13+
dash_duo.wait_for_text_to_equal("#content", "Invalid", timeout=1.0)
14+
15+
assert err.value.args[0] == "text -> Invalid not found within 1.0s, found: Content"
16+
17+
with pytest.raises(TimeoutException) as err:
18+
dash_duo.wait_for_text_to_equal("#none", "None", timeout=1.0)
19+
20+
assert err.value.args[0] == "text -> None not found within 1.0s, #none not found"
21+
22+
with pytest.raises(TimeoutException) as err:
23+
dash_duo.wait_for_contains_text("#content", "invalid", timeout=1.0)
24+
25+
assert (
26+
err.value.args[0]
27+
== "text -> invalid not found inside element within 1.0s, found: Content"
28+
)
29+
30+
with pytest.raises(TimeoutException) as err:
31+
dash_duo.wait_for_contains_text("#none", "none", timeout=1.0)
32+
33+
assert (
34+
err.value.args[0]
35+
== "text -> none not found inside element within 1.0s, #none not found"
36+
)

0 commit comments

Comments
 (0)