diff --git a/stere/areas/repeating.py b/stere/areas/repeating.py index fe8c27ea..e66f1d5b 100644 --- a/stere/areas/repeating.py +++ b/stere/areas/repeating.py @@ -1,4 +1,7 @@ import copy +import typing + +from ..utils import _retry class Repeating: @@ -114,3 +117,18 @@ def children(self): container.append(copy_items) return container + + def has_children(self, retry_time: typing.Optional[int] = None) -> bool: + """Check if any children can be found. + + Arguments: + retry_time: Number of seconds to check for. + + Returns: + bool + + """ + return _retry( + lambda: len(self.children()) > 0, + retry_time=retry_time, + ) diff --git a/stere/fields/field.py b/stere/fields/field.py index aa69d5c7..7b7e45ec 100644 --- a/stere/fields/field.py +++ b/stere/fields/field.py @@ -1,23 +1,6 @@ -import time - from .decorators import stere_performer from .element_builder import build_element - - -def _try_until_timeout(func=None, wait_time=0): - """Try to get a True value from a function and return it. Once the - timeout is hit, then return False instead. - - Arguments: - func (function): The function to try to execute - wait_time (int): Number of seconds to wait - """ - end_time = time.time() + wait_time - while time.time() < end_time: - if func(): - return True - - return False +from ..utils import _retry @stere_performer('null_action', consumes_arg=False) @@ -179,9 +162,9 @@ def value_contains(self, expected, wait_time=2): >>> assert pet_store.price.value_contains("19.19", wait_time=6) """ - return _try_until_timeout( - func=lambda: expected in self.value, - wait_time=wait_time, + return _retry( + lambda: expected in self.value, + retry_time=wait_time, ) def value_equals(self, expected, wait_time=2): @@ -204,9 +187,9 @@ def value_equals(self, expected, wait_time=2): >>> assert pet_store.price.value_equals("$19.19", wait_time=6) """ - return _try_until_timeout( - func=lambda: expected == self.value, - wait_time=wait_time, + return _retry( + lambda: expected == self.value, + retry_time=wait_time, ) def find(self): diff --git a/stere/strategy/splinter.py b/stere/strategy/splinter.py index a5cbf6c0..66faff66 100644 --- a/stere/strategy/splinter.py +++ b/stere/strategy/splinter.py @@ -1,31 +1,8 @@ import copy -import time import typing -from stere import Stere - from .strategy import strategy - - -def _retry(fn, retry_time: typing.Optional[int] = None) -> bool: - """Retry a function for a specific amount of time. - - Returns: - True if the function returns a truthy value, else False - - Arguments: - fn: Function to retry - retry_time: Number of seconds to retry. If not specified, - Stere.retry_time will be used. - - """ - retry_time = retry_time or Stere.retry_time - end_time = time.time() + retry_time - - while time.time() < end_time: - if fn(): - return True - return False +from ..utils import _retry class SplinterBase: diff --git a/stere/utils.py b/stere/utils.py index 33827e97..52a6e1ba 100644 --- a/stere/utils.py +++ b/stere/utils.py @@ -1,8 +1,33 @@ +import time +import typing from functools import reduce +from stere import Stere + def rgetattr(obj, attr, *args): """A nested getattr""" def _getattr(obj, attr): return getattr(obj, attr, *args) return reduce(_getattr, [obj] + attr.split('.')) + + +def _retry(fn, retry_time: typing.Optional[int] = None) -> bool: + """Retry a function for a specific amount of time. + + Returns: + True if the function returns a truthy value, else False + + Arguments: + fn (function): Function to retry + retry_time: Number of seconds to retry. If not specified, + Stere.retry_time will be used. + + """ + retry_time = retry_time or Stere.retry_time + end_time = time.time() + retry_time + + while time.time() < end_time: + if fn(): + return True + return False diff --git a/tests/splinter/test_field.py b/tests/splinter/test_field.py index ab8d15e3..4e2ace38 100644 --- a/tests/splinter/test_field.py +++ b/tests/splinter/test_field.py @@ -6,35 +6,35 @@ from selenium.webdriver.remote.remote_connection import LOGGER from stere.fields import Field -from stere.fields.field import _try_until_timeout +from stere.utils import _retry LOGGER.setLevel(logging.WARNING) -def test_try_until_timeout(): - """When I call _try_until_timeout +def test_retry(): + """When I call _retry Then a function is called until it returns a True value """ now = time.time() - result = _try_until_timeout( - func=lambda: True if time.time() >= (now + 6) else False, - wait_time=8, + result = _retry( + lambda: True if time.time() >= (now + 6) else False, + retry_time=8, ) assert result -def test_try_until_timeout_fails(): - """When I call _try_until_timeout +def test_retry_fails(): + """When I call _retry And the timeout is hit Then it returns False """ now = time.time() - result = _try_until_timeout( - func=lambda: True if time.time() == (now + 6) else False, - wait_time=4, + result = _retry( + lambda: True if time.time() == (now + 6) else False, + retry_time=4, ) assert not result diff --git a/tests/splinter/test_repeating.py b/tests/splinter/test_repeating.py index f040f266..b3ca4756 100644 --- a/tests/splinter/test_repeating.py +++ b/tests/splinter/test_repeating.py @@ -60,3 +60,10 @@ def test_all_roots_not_found(test_page): 'Could not find any RepeatingArea using the root: ' '.repeatingRepeating', ) + + +def test_has_children(test_page): + test_page.navigate() + + r = test_page.repeating + assert r.has_children()