Skip to content

Commit 041b165

Browse files
committed
Add method: "wait_for_element_clickable()"
1 parent ae72061 commit 041b165

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-0
lines changed

help_docs/method_summary.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,10 @@ self.assert_element_absent(selector, by="css selector", timeout=None)
701701

702702
############
703703

704+
self.wait_for_element_clickable(selector, by="css selector", timeout=None)
705+
706+
############
707+
704708
self.wait_for_element_not_visible(selector, by="css selector", timeout=None)
705709

706710
self.assert_element_not_visible(selector, by="css selector", timeout=None)

seleniumbase/fixtures/base_case.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7217,6 +7217,28 @@ def wait_for_element_visible(
72177217
original_selector=original_selector,
72187218
)
72197219

7220+
def wait_for_element_clickable(
7221+
self, selector, by="css selector", timeout=None
7222+
):
7223+
"""Waits for the element to be clickable, but does NOT click it."""
7224+
self.__check_scope()
7225+
if not timeout:
7226+
timeout = settings.LARGE_TIMEOUT
7227+
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
7228+
timeout = self.__get_new_timeout(timeout)
7229+
original_selector = selector
7230+
selector, by = self.__recalculate_selector(selector, by)
7231+
if self.__is_shadow_selector(selector):
7232+
# If a shadow selector, use visible instead of clickable
7233+
return self.__wait_for_shadow_element_visible(selector, timeout)
7234+
return page_actions.wait_for_element_clickable(
7235+
self.driver,
7236+
selector,
7237+
by,
7238+
timeout=timeout,
7239+
original_selector=original_selector,
7240+
)
7241+
72207242
def wait_for_element_not_present(
72217243
self, selector, by="css selector", timeout=None
72227244
):

seleniumbase/fixtures/page_actions.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,111 @@ def wait_for_attribute(
707707
return element
708708

709709

710+
def wait_for_element_clickable(
711+
driver,
712+
selector,
713+
by="css selector",
714+
timeout=settings.LARGE_TIMEOUT,
715+
original_selector=None,
716+
):
717+
"""
718+
Searches for the specified element by the given selector. Returns the
719+
element object if the element is present, visible, & clickable on the page.
720+
Raises NoSuchElementException if the element does not exist in the HTML
721+
within the specified timeout.
722+
Raises ElementNotVisibleException if the element exists in the HTML,
723+
but is not visible (eg. opacity is "0") within the specified timeout.
724+
Raises ElementNotInteractableException if the element is not clickable.
725+
@Params
726+
driver - the webdriver object (required)
727+
selector - the locator for identifying the page element (required)
728+
by - the type of selector being used (Default: By.CSS_SELECTOR)
729+
timeout - the time to wait for elements in seconds
730+
original_selector - handle pre-converted ":contains(TEXT)" selector
731+
@Returns
732+
A web element object
733+
"""
734+
from selenium.webdriver.support import expected_conditions as EC
735+
from selenium.webdriver.support.ui import WebDriverWait
736+
737+
element = None
738+
is_present = False
739+
is_visible = False
740+
start_ms = time.time() * 1000.0
741+
stop_ms = start_ms + (timeout * 1000.0)
742+
for x in range(int(timeout * 10)):
743+
shared_utils.check_if_time_limit_exceeded()
744+
try:
745+
element = driver.find_element(by=by, value=selector)
746+
is_present = True
747+
if element.is_displayed():
748+
is_visible = True
749+
if WebDriverWait(driver, 0.001).until(
750+
EC.element_to_be_clickable((by, selector))
751+
):
752+
return element
753+
else:
754+
element = None
755+
raise Exception()
756+
else:
757+
element = None
758+
raise Exception()
759+
except Exception:
760+
now_ms = time.time() * 1000.0
761+
if now_ms >= stop_ms:
762+
break
763+
time.sleep(0.1)
764+
plural = "s"
765+
if timeout == 1:
766+
plural = ""
767+
if not element and by != By.LINK_TEXT:
768+
if (
769+
original_selector
770+
and ":contains(" in original_selector
771+
and "contains(." in selector
772+
):
773+
selector = original_selector
774+
if not is_present:
775+
# The element does not exist in the HTML
776+
message = "Element {%s} was not present after %s second%s!" % (
777+
selector,
778+
timeout,
779+
plural,
780+
)
781+
timeout_exception(NoSuchElementException, message)
782+
if not is_visible:
783+
# The element exists in the HTML, but is not visible
784+
message = "Element {%s} was not visible after %s second%s!" % (
785+
selector,
786+
timeout,
787+
plural,
788+
)
789+
timeout_exception(ElementNotVisibleException, message)
790+
# The element is visible in the HTML, but is not clickable
791+
message = "Element {%s} was not clickable after %s second%s!" % (
792+
selector,
793+
timeout,
794+
plural,
795+
)
796+
timeout_exception(ElementNotInteractableException, message)
797+
elif not element and by == By.LINK_TEXT and not is_visible:
798+
message = "Link text {%s} was not visible after %s second%s!" % (
799+
selector,
800+
timeout,
801+
plural,
802+
)
803+
timeout_exception(ElementNotVisibleException, message)
804+
elif not element and by == By.LINK_TEXT and is_visible:
805+
message = "Link text {%s} was not clickable after %s second%s!" % (
806+
selector,
807+
timeout,
808+
plural,
809+
)
810+
timeout_exception(ElementNotInteractableException, message)
811+
else:
812+
return element
813+
814+
710815
def wait_for_element_absent(
711816
driver,
712817
selector,

0 commit comments

Comments
 (0)