diff --git a/selen_kaa/component/__init__.py b/selen_kaa/component/__init__.py deleted file mode 100644 index 931c7cd..0000000 --- a/selen_kaa/component/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__all__ = ["component", "components_array"] diff --git a/selen_kaa/component/component.py b/selen_kaa/component/component.py deleted file mode 100644 index 1197739..0000000 --- a/selen_kaa/component/component.py +++ /dev/null @@ -1,23 +0,0 @@ - -class Component: - - def __init__(self, source): - self.source = source - - def __getattr__(self, attr): - try: - orig_attr = self.source.web_element.__getattribute__(attr) - except AttributeError: - try: - orig_attr = self.source.__getattribute__(attr) - except AttributeError as exc: - raise AttributeError(f"Component\'s source has no attribute {attr}.\n{exc}") - if callable(orig_attr): - def hooked(*args, **kwargs): - result = orig_attr(*args, **kwargs) - # prevent recursion - if result == self.source: - return self - return result - return hooked - return orig_attr diff --git a/selen_kaa/component/components_array.py b/selen_kaa/component/components_array.py deleted file mode 100644 index 8cdd6a7..0000000 --- a/selen_kaa/component/components_array.py +++ /dev/null @@ -1,24 +0,0 @@ - -class ComponentsArray: - - def __init__(self, source_array): - self.source_array = source_array - - def __getattr__(self, attr): - try: - orig_attr = self.source_array.__getattribute__(attr) - except AttributeError: - try: - orig_attr = self.source_array._lazy_array.__getattribute__(attr) - except AttributeError as exc: - raise AttributeError(f"Component\'s array has no attribute {attr}.\n{exc}") - orig_attr = self.source_array.__getattribute__(attr) - if callable(orig_attr): - def hooked(*args, **kwargs): - result = orig_attr(*args, **kwargs) - # prevent recursion - if result == self.source_array: - return self - return result - return hooked - return orig_attr diff --git a/selen_kaa/element/element_waits.py b/selen_kaa/element/element_waits.py index ec4acf0..3827f36 100644 --- a/selen_kaa/element/element_waits.py +++ b/selen_kaa/element/element_waits.py @@ -12,56 +12,54 @@ def __init__(self, se_web_element, webdriver: WebDriver, timeout: TimeoutType): self._timeout = timeout self._wait = Wait(webdriver) - def be_visible(self, timeout: TimeoutType): + def be_visible(self, timeout: TimeoutType = None): """True when an element is visible on the html page. :param timeout: time to wait element visibility. """ - timeout_ = timeout if timeout else self._timeout + timeout_ = timeout if timeout is not None else self._timeout return self._wait.element_to_be_visible(self.__web_element, timeout=timeout_) - def be_invisible(self, timeout: TimeoutType): + def be_invisible(self, timeout: TimeoutType = None): """True if an element is not visible on the html page. :param timeout: time to wait element visibility. """ - timeout_ = timeout if timeout else self._timeout + timeout_ = timeout if timeout is not None else self._timeout return self._wait.element_to_be_invisible(self.__web_element, timeout_) - def have_class(self, expected_class: str, timeout: TimeoutType): + def have_class(self, expected_class: str, timeout: TimeoutType = None): """True when an element has a specific class. :param expected_class: class_name for expected class (not css_selector). :param timeout: time to wait for an element to have a class name. """ - timeout_ = timeout if timeout else self._timeout + timeout_ = timeout if timeout is not None else self._timeout return self._wait.element_to_get_class(self.__web_element, expected_class, timeout_) - def include_element(self, child_css_selector: str, timeout: TimeoutType): + def include_element(self, child_css_selector: str, timeout: TimeoutType = None): """True when an element gets a desired child element. :param child_css_selector: a css selector for a child element. :param timeout: time to wait for the condition. """ - timeout_ = timeout if timeout else self._timeout + timeout_ = timeout if timeout is not None else self._timeout return self._wait.element_to_include_child_element( self.__web_element, child_css_selector, timeout_ ) - def contain_text(self, text: str, timeout: TimeoutType): + def contain_text(self, text: str, timeout: TimeoutType = None): """True if an element contains a provided text in its text attribute. :param text: expected text. :param timeout: time to wait for the condition. """ - timeout_ = self._timeout - if timeout: - timeout_ = timeout + timeout_ = timeout if timeout is not None else self._timeout return self._wait.element_to_contain_text(self.__web_element, text, timeout_) - def have_similar_text(self, text: str, timeout: TimeoutType): + def have_similar_text(self, text: str, timeout: TimeoutType = None): """True if an element has a similar text in texts attribute. Not precise comparision, e.g. returns True for: "some" in "this is some text", " test\n" and "test", "TEST" and "test" @@ -69,34 +67,28 @@ def have_similar_text(self, text: str, timeout: TimeoutType): :param timeout: time to wait for the condition. """ - timeout_ = self._timeout - if timeout: - timeout_ = timeout + timeout_ = timeout if timeout is not None else self._timeout return self._wait.element_have_similar_text(self.__web_element, text, timeout_) - def have_exact_text(self, text: str, timeout: TimeoutType): + def have_exact_text(self, text: str, timeout: TimeoutType = None): """True if an element has exactly provided text, and no other text. Precise comparision, e.g. returns False if "some" == "this is some text" :param text: exact text to search inside an element :param timeout: time to wait for the condition. """ - timeout_ = self._timeout - if timeout: - timeout_ = timeout + timeout_ = timeout if timeout is not None else self._timeout return self._wait.element_to_have_exact_text(self.__web_element, text, timeout_) - def not_present_in_dom(self, timeout: TimeoutType): + def not_present_in_dom(self, timeout: TimeoutType = None): """True for an element to be stale or absent in DOM. :param timeout: equal to the self.timeout if other not passed. """ - timeout_ = self._timeout - if timeout: - timeout_ = timeout + timeout_ = timeout if timeout is not None else self._timeout return self._wait.element_not_present(self.__web_element, timeout_) - def be_on_the_screen(self, timeout: TimeoutType): + def be_on_the_screen(self, timeout: TimeoutType = None): """True for an element is present on the screen (inside the viewport). Checks if element's coordinates match viewport height and width. Different from `to_be_visible` as `to_be_visible` checks element has size > 1px @@ -104,7 +96,5 @@ def be_on_the_screen(self, timeout: TimeoutType): :param timeout: equal to the self.timeout if other not passed. """ - timeout_ = self._timeout - if timeout: - timeout_ = timeout + timeout_ = timeout if timeout is not None else self._timeout return self._wait.element_to_be_in_viewport(self.__web_element, timeout_) diff --git a/selen_kaa/element/expectations.py b/selen_kaa/element/expectations.py index 1e0a8b6..865e18b 100644 --- a/selen_kaa/element/expectations.py +++ b/selen_kaa/element/expectations.py @@ -11,67 +11,60 @@ class Expectations(ElementWaits): Errors are handled by returning False. """ - def be_visible(self, timeout: TimeoutType): + def be_visible(self, timeout: TimeoutType = None): """True when an element is visible on the html page. :param timeout: time to wait element visibility. """ - timeout_ = timeout if timeout else self._timeout try: - return super().be_visible(timeout=timeout_) + return super().be_visible(timeout) except TimeoutException: return False - def be_invisible(self, timeout: TimeoutType): + def be_invisible(self, timeout: TimeoutType = None): """True if an element is not visible on the page. :param timeout: time to wait element visibility. """ - timeout_ = timeout if timeout else self._timeout try: - return super().be_invisible(timeout=timeout_) + return super().be_invisible(timeout) except TimeoutException: return False - def have_class(self, expected_class: str, timeout: TimeoutType): + def have_class(self, expected_class: str, timeout: TimeoutType = None): """True when an element has a specific class. :param expected_class: class_name for expected class (not css_selector). :param timeout: time to wait for an element ho have a class name. """ - timeout_ = timeout if timeout else self._timeout try: - return super().have_class(expected_class, timeout=timeout_) + return super().have_class(expected_class, timeout) except TimeoutException: return False - def include_element(self, child_css_selector: str, timeout: TimeoutType): + def include_element(self, child_css_selector: str, timeout: TimeoutType = None): """True when an element gets a desired child element. :param child_css_selector: a css selector for a child element. :param timeout: time to wait for the condition. """ - timeout_ = timeout if timeout else self._timeout try: - return super().include_element(child_css_selector, timeout_) + return super().include_element(child_css_selector, timeout) except TimeoutException: return False - def contain_text(self, text: str, timeout: TimeoutType): + def contain_text(self, text: str, timeout: TimeoutType = None): """True if an element contains a provided text in its text attribute. :param text: expected text. :param timeout: time to wait for the condition. """ - timeout_ = self._timeout - if timeout: - timeout_ = timeout try: - return super().contain_text(text, timeout_) + return super().contain_text(text, timeout) except TimeoutException: return False - def have_similar_text(self, text: str, timeout: TimeoutType): + def have_similar_text(self, text: str, timeout: TimeoutType = None): """True if an element has a similar text in texts attribute. Not precise comparision, e.g. returns True for: "some" in "this is some text", " test\n" and "test", "TEST" and "test" @@ -79,52 +72,40 @@ def have_similar_text(self, text: str, timeout: TimeoutType): :param timeout: time to wait for the condition. """ - timeout_ = self._timeout - if timeout: - timeout_ = timeout try: - return super().have_similar_text(text, timeout_) + return super().have_similar_text(text, timeout) except TimeoutException: return False - def have_exact_text(self, text: str, timeout: TimeoutType): + def have_exact_text(self, text: str, timeout: TimeoutType = None): """True if an element has exactly provided text, and no other text. Precise comparision, e.g. returns False if "some" == "this is some text" :param text: exact text to search inside an element :param timeout: time to wait for the condition. """ - timeout_ = self._timeout - if timeout: - timeout_ = timeout try: - return super().have_exact_text(text, timeout_) + return super().have_exact_text(text, timeout) except TimeoutException: return False - def not_present_in_dom(self, timeout: TimeoutType): + def not_present_in_dom(self, timeout: TimeoutType = None): """True for an element to be stale or absent in DOM. :param timeout: equal to the self.timeout if other not passed. :type: int """ - timeout_ = self._timeout - if timeout: - timeout_ = timeout try: - return super().not_present_in_dom(timeout_) + return super().not_present_in_dom(timeout) except TimeoutException: return False - def be_on_the_screen(self, timeout: TimeoutType): + def be_on_the_screen(self, timeout: TimeoutType = None): """True for an element is present on the screen (inside the viewport). False if element's coordinates don't match viewport height and width. :param timeout: equal to the self.timeout if other not passed. """ - timeout_ = self._timeout - if timeout: - timeout_ = timeout try: - return super().be_on_the_screen(timeout_) + return super().be_on_the_screen(timeout) except TimeoutException: return False diff --git a/selen_kaa/element/se_web_element.py b/selen_kaa/element/se_web_element.py index 5912301..8cfb8f7 100644 --- a/selen_kaa/element/se_web_element.py +++ b/selen_kaa/element/se_web_element.py @@ -99,4 +99,4 @@ def get_class(self): return self.web_element.get_attribute("class") def __repr__(self): - return f"{self.web_element} with selector {self.selector}." + return f"Selen-kaa WebElement with selector `{self.selector}`." diff --git a/selen_kaa/waits.py b/selen_kaa/waits.py index d6a1ce1..cb5f641 100644 --- a/selen_kaa/waits.py +++ b/selen_kaa/waits.py @@ -21,6 +21,7 @@ class Wait: DEFAULT_TIMEOUT = 4 + PULL_FREQUENCY = 0.2 def __init__(self, webdriver: WebDriver): self._webdriver: WebDriver = webdriver @@ -60,7 +61,7 @@ def element_to_be_invisible(self, target: ElementType, timeout: TimeoutType = DE def wrapped_webelement_disappears(): try: # init web_element within wait's timeout, not web_element's - target.get_web_element_by_timeout(timeout) + target.get_web_element_by_timeout(self.PULL_FREQUENCY) if target.web_element.is_displayed(): return False # return True if element is not stale and is not displayed @@ -88,11 +89,11 @@ def element_not_present(self, target: ElementType, timeout: TimeoutType = DEFAUL def no_wrapped_webelement_in_dom(): try: - target.get_web_element_by_timeout(timeout) + target.get_web_element_by_timeout(self.PULL_FREQUENCY) if target.web_element.is_enabled(): return False - # it might be unreached condition, but keep it for code consistency - return target + # return False even element isn't enabled, but still present + return False except (NoSuchElementException, StaleElementReferenceException): return target @@ -396,15 +397,15 @@ def wait_fluently(condition: Callable, timeout: TimeoutType, err_msg: str): :return: element if condition is True, else raises TimeoutException """ - if not timeout: + if timeout is None: timeout = 0 start_time = time.time() while True: - if time.time() - start_time >= timeout: - raise TimeoutException(err_msg) res = condition() if res: return res + if time.time() - start_time >= timeout: + raise TimeoutException(err_msg) time.sleep(0.3) @staticmethod diff --git a/setup.py b/setup.py index 457810b..5156537 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def get_requirements(): setup( name="selen-kaa", - version="0.0.3.0", + version="0.0.3.1.0", author="Viktor Grygorchuk", author_email="vvgrygorchuk@gmail.com", keywords=['Selenium', 'Test Automation'], @@ -21,7 +21,7 @@ def get_requirements(): long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/VicGrygorchyk/selen_kaa.git", - download_url="https://github.com/VicGrygorchyk/selen_kaa/archive/0.0.3.0.tar.gz", + download_url="https://github.com/VicGrygorchyk/selen_kaa/archive/0.0.3.1.0.tar.gz", packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "test*"]), install_requires=get_requirements(), classifiers=[ diff --git a/tests/se_test/test_wait_presense.py b/tests/se_test/test_wait_presense.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/se_test/test_wait_visibility.py b/tests/se_test/test_wait_visibility.py index ecd26ed..ce0482d 100644 --- a/tests/se_test/test_wait_visibility.py +++ b/tests/se_test/test_wait_visibility.py @@ -77,7 +77,13 @@ def test_expect_invisibility_has_no_exception(app): assert not index_page.test_div.expect.be_invisible(timeout=1) -def test_timeout_duration_on_visibility(app): +def test_expect_no_exc_if_no_such_element(app): + # no exception even such element doesn't exist + index_page = app.goto_index_page() + assert index_page.no_such_element.expect.be_invisible(1) + + +def test_timeout_duration_on_expect_visibility(app): index_page = app.goto_index_page() start_t = time.time() assert not index_page.test_div.expect.be_visible(timeout=1) @@ -88,3 +94,40 @@ def test_timeout_duration_on_visibility(app): assert not index_page.test_div.expect.be_visible(timeout=5) after_6_sec_time = time.time() assert all((after_6_sec_time - start_t2 >= 5, after_6_sec_time - start_t2 <= 6)) + + +def test_timeout_duration_on_invisibility(app): + index_page = app.goto_index_page() + start_t = time.time() + assert index_page.test_div.expect.be_invisible(timeout=1) + after_1_sec_time = time.time() + # it should be True faster than 1 seconds, as the condition is true from beginning + assert after_1_sec_time - start_t <= 1 + + start_t2 = time.time() + assert index_page.test_div.expect.be_invisible(timeout=5) + after_6_sec_time = time.time() + # it should be True faster than 5 seconds, as the condition is true from beginning + assert after_6_sec_time - start_t2 <= 1 + + +def test_invisible_timeout_none_and_zero(app): + index_page = app.goto_index_page() + start_t = time.time() + assert index_page.test_div.expect.be_invisible() + assert index_page.test_div.expect.be_invisible(None) + assert index_page.test_div.expect.be_invisible(0) + after_time = time.time() + # it should be faster than 1 seconds + assert after_time - start_t <= 1 + + +def test_visible_timeout_none_and_zero(app): + index_page = app.goto_index_page() + start_t = time.time() + assert index_page.btn_show_div.expect.be_visible() + assert index_page.btn_show_div.expect.be_visible(None) + assert index_page.btn_show_div.expect.be_visible(0) + after_time = time.time() + # it should be faster than 1 seconds + assert after_time - start_t <= 1 diff --git a/tests/se_test/test_wrapped_arr.py b/tests/se_test/test_wrapped_arr.py index 3203809..4723337 100644 --- a/tests/se_test/test_wrapped_arr.py +++ b/tests/se_test/test_wrapped_arr.py @@ -1,6 +1,5 @@ from selen_kaa.element.se_elements_array import SeElementsArray from selen_kaa.element.se_web_element import SeWebElement -from tests.webapp.driver_wrapper import WrappedElementsArray, WebElementWrapper from tests.webapp.pages.index_page import THE_SAME_CLASS diff --git a/tests/webapp/driver_wrapper.py b/tests/webapp/driver_wrapper.py index 7715ea6..ea58221 100644 --- a/tests/webapp/driver_wrapper.py +++ b/tests/webapp/driver_wrapper.py @@ -6,8 +6,7 @@ from selenium.common.exceptions import WebDriverException, UnexpectedAlertPresentException from selen_kaa.webdriver import SeWebDriver -from selen_kaa.component.component import Component -from selen_kaa.component.components_array import ComponentsArray +from selen_kaa.element.se_web_element import SeWebElement from selen_kaa.utils import se_utils @@ -15,25 +14,16 @@ TimeoutType = se_utils.TimeoutType -class WebElementWrapper(Component): - - def __init__(self, source): - super().__init__(source) +class WebElementWrapper(SeWebElement): def should_be_unclickable(self, timeout: TimeoutType = 0.1): try: - self.source.click(timeout) + self.click(timeout) except ElementNotClickableError: return True return False -class WrappedElementsArray(ComponentsArray): - - def __init__(self, source_array): - super().__init__(source_array) - - class ElementNotClickableError(WebDriverException): """ Special exception for cases where element can't receive a click. """ @@ -61,8 +51,5 @@ def get_screenshot_as_png(self): except (WebDriverException, UnexpectedAlertPresentException): logging.error("Unable to get a screenshot from WebDriver.") - def init_web_element(self, selector: str, timeout: TimeoutType = None): - return WebElementWrapper(super().init_web_element(selector, timeout)) - - def init_all_web_elements(self, selector: str, timeout: TimeoutType = None): - return super().init_all_web_elements(selector, timeout) + def init_web_element(self, selector: str, timeout: TimeoutType = 1): + return WebElementWrapper(self.webdriver, selector, timeout) diff --git a/tests/webapp/pages/index_page.py b/tests/webapp/pages/index_page.py index bd5bf34..f33fe78 100644 --- a/tests/webapp/pages/index_page.py +++ b/tests/webapp/pages/index_page.py @@ -14,3 +14,4 @@ def __init__(self, webdriver): self.btn_show_div = self._webdriver.init_web_element(SHOW_DIV_BTN) self.btn_hide_div = self._webdriver.init_web_element(HIDE_DIV_BTN) self.test_div = self._webdriver.init_web_element(TEST_DIV_VISIBILITY) + self.no_such_element = self._webdriver.init_web_element(".no-such-class-for-no-element")