SeleniumBase/seleniumbase/fixtures/page_actions.py

1425 lines
48 KiB
Python

"""This module contains useful methods for waiting on elements to load.
These methods improve and expand on existing WebDriver commands.
Improvements include making WebDriver commands more robust and more reliable
by giving page elements enough time to load before taking action on them.
The default option for searching for elements is by "css selector".
This can be changed by overriding the "By" parameter from this import:
> from selenium.webdriver.common.by import By
Options are:
By.CSS_SELECTOR # "css selector"
By.CLASS_NAME # "class name"
By.ID # "id"
By.NAME # "name"
By.LINK_TEXT # "link text"
By.XPATH # "xpath"
By.TAG_NAME # "tag name"
By.PARTIAL_LINK_TEXT # "partial link text"
"""
import codecs
import os
import time
from selenium.common.exceptions import ElementNotInteractableException
from selenium.common.exceptions import ElementNotVisibleException
from selenium.common.exceptions import NoAlertPresentException
from selenium.common.exceptions import NoSuchAttributeException
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoSuchWindowException
from selenium.common.exceptions import StaleElementReferenceException
from selenium.webdriver.common.action_chains import ActionChains
from seleniumbase.common.exceptions import LinkTextNotFoundException
from seleniumbase.common.exceptions import TextNotVisibleException
from seleniumbase.config import settings
from seleniumbase.fixtures import shared_utils
def is_element_present(driver, selector, by="css selector"):
"""
Returns whether the specified element selector is present on the page.
@Params
driver - the webdriver object (required)
selector - the locator for identifying the page element (required)
by - the type of selector being used (Default: "css selector")
@Returns
Boolean (is element present)
"""
try:
driver.find_element(by=by, value=selector)
return True
except Exception:
return False
def is_element_visible(driver, selector, by="css selector"):
"""
Returns whether the specified element selector is visible on the page.
@Params
driver - the webdriver object (required)
selector - the locator for identifying the page element (required)
by - the type of selector being used (Default: "css selector")
@Returns
Boolean (is element visible)
"""
try:
element = driver.find_element(by=by, value=selector)
return element.is_displayed()
except Exception:
return False
def is_element_clickable(driver, selector, by="css selector"):
"""
Returns whether the specified element selector is clickable.
@Params
driver - the webdriver object (required)
selector - the locator for identifying the page element (required)
by - the type of selector being used (Default: "css selector")
@Returns
Boolean (is element clickable)
"""
try:
element = driver.find_element(by=by, value=selector)
if element.is_displayed() and element.is_enabled():
return True
return False
except Exception:
return False
def is_element_enabled(driver, selector, by="css selector"):
"""
Returns whether the specified element selector is enabled on the page.
@Params
driver - the webdriver object (required)
selector - the locator for identifying the page element (required)
by - the type of selector being used (Default: "css selector")
@Returns
Boolean (is element enabled)
"""
try:
element = driver.find_element(by=by, value=selector)
return element.is_enabled()
except Exception:
return False
def is_text_visible(driver, text, selector, by="css selector", browser=None):
"""
Returns whether the text substring is visible in the given selector.
@Params
driver - the webdriver object (required)
text - the text string to search for (required)
selector - the locator for identifying the page element (required)
by - the type of selector being used (Default: "css selector")
@Returns
Boolean (is text visible)
"""
text = str(text)
try:
element = driver.find_element(by=by, value=selector)
element_text = element.text
if browser == "safari":
if element.tag_name.lower() in ["input", "textarea"]:
element_text = element.get_attribute("value")
else:
element_text = element.get_attribute("innerText")
elif element.tag_name.lower() in ["input", "textarea"]:
element_text = element.get_property("value")
return element.is_displayed() and text in element_text
except Exception:
return False
def is_exact_text_visible(
driver,
text,
selector,
by="css selector",
browser=None
):
"""
Returns whether the exact text is visible in the given selector.
(Ignores leading and trailing whitespace)
@Params
driver - the webdriver object (required)
text - the text string to search for (required)
selector - the locator for identifying the page element (required)
by - the type of selector being used (Default: "css selector")
@Returns
Boolean (is text visible)
"""
text = str(text)
try:
element = driver.find_element(by=by, value=selector)
element_text = element.text
if browser == "safari":
if element.tag_name.lower() in ["input", "textarea"]:
element_text = element.get_attribute("value")
else:
element_text = element.get_attribute("innerText")
elif element.tag_name.lower() in ["input", "textarea"]:
element_text = element.get_property("value")
return (
element.is_displayed()
and text.strip() == element_text.strip()
)
except Exception:
return False
def is_attribute_present(
driver, selector, attribute, value=None, by="css selector"
):
"""
Returns whether the specified attribute is present in the given selector.
@Params
driver - the webdriver object (required)
selector - the locator for identifying the page element (required)
attribute - the attribute that is expected for the element (required)
value - the attribute value that is expected (Default: None)
by - the type of selector being used (Default: "css selector")
@Returns
Boolean (is attribute present)
"""
try:
element = driver.find_element(by=by, value=selector)
found_value = element.get_attribute(attribute)
if found_value is None:
return False
if value is not None:
if found_value == value:
return True
else:
return False
else:
return True
except Exception:
return False
def hover_on_element(driver, selector, by="css selector"):
"""
Fires the hover event for the specified element by the given selector.
@Params
driver - the webdriver object (required)
selector - the locator for identifying the page element (required)
by - the type of selector being used (Default: "css selector")
"""
element = driver.find_element(by=by, value=selector)
hover = ActionChains(driver).move_to_element(element)
hover.perform()
return element
def hover_element(driver, element):
"""
Similar to hover_on_element(), but uses found element, not a selector.
"""
hover = ActionChains(driver).move_to_element(element)
hover.perform()
return element
def timeout_exception(exception, message):
exc, msg = shared_utils.format_exc(exception, message)
raise exc(msg)
def hover_and_click(
driver,
hover_selector,
click_selector,
hover_by="css selector",
click_by="css selector",
timeout=settings.SMALL_TIMEOUT,
):
"""
Fires the hover event for a specified element by a given selector, then
clicks on another element specified. Useful for dropdown hover based menus.
@Params
driver - the webdriver object (required)
hover_selector - the css selector to hover over (required)
click_selector - the css selector to click on (required)
hover_by - the hover selector type to search by (Default: "css selector")
click_by - the click selector type to search by (Default: "css selector")
timeout - number of seconds to wait for click element to appear after hover
"""
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
element = driver.find_element(by=hover_by, value=hover_selector)
hover = ActionChains(driver).move_to_element(element)
for x in range(int(timeout * 10)):
try:
hover.perform()
element = driver.find_element(by=click_by, value=click_selector)
element.click()
return element
except Exception:
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
plural = "s"
if timeout == 1:
plural = ""
message = "Element {%s} was not present after %s second%s!" % (
click_selector,
timeout,
plural,
)
timeout_exception(NoSuchElementException, message)
def hover_element_and_click(
driver,
element,
click_selector,
click_by="css selector",
timeout=settings.SMALL_TIMEOUT,
):
"""
Similar to hover_and_click(), but assumes top element is already found.
"""
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
hover = ActionChains(driver).move_to_element(element)
for x in range(int(timeout * 10)):
try:
hover.perform()
element = driver.find_element(by=click_by, value=click_selector)
element.click()
return element
except Exception:
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
plural = "s"
if timeout == 1:
plural = ""
message = "Element {%s} was not present after %s second%s!" % (
click_selector,
timeout,
plural,
)
timeout_exception(NoSuchElementException, message)
def hover_element_and_double_click(
driver,
element,
click_selector,
click_by="css selector",
timeout=settings.SMALL_TIMEOUT,
):
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
hover = ActionChains(driver).move_to_element(element)
for x in range(int(timeout * 10)):
try:
hover.perform()
element_2 = driver.find_element(by=click_by, value=click_selector)
actions = ActionChains(driver)
actions.move_to_element(element_2)
actions.double_click(element_2)
actions.perform()
return element_2
except Exception:
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
plural = "s"
if timeout == 1:
plural = ""
message = "Element {%s} was not present after %s second%s!" % (
click_selector,
timeout,
plural,
)
timeout_exception(NoSuchElementException, message)
def wait_for_element_present(
driver,
selector,
by="css selector",
timeout=settings.LARGE_TIMEOUT,
original_selector=None,
ignore_test_time_limit=False,
):
"""
Searches for the specified element by the given selector. Returns the
element object if it exists in the HTML. (The element can be invisible.)
Raises NoSuchElementException if the element does not exist in the HTML
within the specified timeout.
@Params
driver - the webdriver object
selector - the locator for identifying the page element (required)
by - the type of selector being used (Default: "css selector")
timeout - the time to wait for elements in seconds
original_selector - handle pre-converted ":contains(TEXT)" selector
ignore_test_time_limit - ignore test time limit (NOT related to timeout)
@Returns
A web element object
"""
element = None
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
for x in range(int(timeout * 10)):
if not ignore_test_time_limit:
shared_utils.check_if_time_limit_exceeded()
try:
element = driver.find_element(by=by, value=selector)
return element
except Exception:
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
plural = "s"
if timeout == 1:
plural = ""
if not element:
if (
original_selector
and ":contains(" in original_selector
and "contains(." in selector
):
selector = original_selector
message = "Element {%s} was not present after %s second%s!" % (
selector,
timeout,
plural,
)
timeout_exception(NoSuchElementException, message)
else:
return element
def wait_for_element_visible(
driver,
selector,
by="css selector",
timeout=settings.LARGE_TIMEOUT,
original_selector=None,
ignore_test_time_limit=False,
):
"""
Searches for the specified element by the given selector. Returns the
element object if the element is present and visible on the page.
Raises NoSuchElementException if the element does not exist in the HTML
within the specified timeout.
Raises ElementNotVisibleException if the element exists in the HTML,
but is not visible (eg. opacity is "0") within the specified timeout.
@Params
driver - the webdriver object (required)
selector - the locator for identifying the page element (required)
by - the type of selector being used (Default: "css selector")
timeout - the time to wait for elements in seconds
original_selector - handle pre-converted ":contains(TEXT)" selector
ignore_test_time_limit - ignore test time limit (NOT related to timeout)
@Returns
A web element object
"""
element = None
is_present = False
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
for x in range(int(timeout * 10)):
if not ignore_test_time_limit:
shared_utils.check_if_time_limit_exceeded()
try:
element = driver.find_element(by=by, value=selector)
is_present = True
if element.is_displayed():
return element
else:
element = None
raise Exception()
except Exception:
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
plural = "s"
if timeout == 1:
plural = ""
if not element and by != "link text":
if (
original_selector
and ":contains(" in original_selector
and "contains(." in selector
):
selector = original_selector
if not is_present:
# The element does not exist in the HTML
message = "Element {%s} was not present after %s second%s!" % (
selector,
timeout,
plural,
)
timeout_exception(NoSuchElementException, message)
# The element exists in the HTML, but is not visible
message = "Element {%s} was not visible after %s second%s!" % (
selector,
timeout,
plural,
)
timeout_exception(ElementNotVisibleException, message)
elif not element and by == "link text":
message = "Link text {%s} was not found after %s second%s!" % (
selector,
timeout,
plural,
)
timeout_exception(LinkTextNotFoundException, message)
else:
return element
def wait_for_text_visible(
driver,
text,
selector,
by="css selector",
timeout=settings.LARGE_TIMEOUT,
browser=None,
):
"""
Searches for the specified element by the given selector. Returns the
element object if the text is present in the element and visible
on the page.
Raises NoSuchElementException if the element does not exist in the HTML
within the specified timeout.
Raises ElementNotVisibleException if the element exists in the HTML,
but the text is not visible within the specified timeout.
@Params
driver - the webdriver object (required)
text - the text that is being searched for in the element (required)
selector - the locator for identifying the page element (required)
by - the type of selector being used (Default: "css selector")
timeout - the time to wait for elements in seconds
browser - used to handle a special edge case when using Safari
@Returns
A web element object that contains the text searched for
"""
element = None
is_present = False
full_text = None
text = str(text)
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
for x in range(int(timeout * 10)):
shared_utils.check_if_time_limit_exceeded()
full_text = None
try:
element = driver.find_element(by=by, value=selector)
is_present = True
if (
element.tag_name.lower() in ["input", "textarea"]
and browser != "safari"
):
if (
element.is_displayed()
and text in element.get_property("value")
):
return element
else:
if element.is_displayed():
full_text = element.get_property("value").strip()
element = None
raise Exception()
elif browser == "safari":
text_attr = "innerText"
if element.tag_name.lower() in ["input", "textarea"]:
text_attr = "value"
if (
element.is_displayed()
and text in element.get_attribute(text_attr)
):
return element
else:
if element.is_displayed():
full_text = element.get_attribute(text_attr)
full_text = full_text.strip()
element = None
raise Exception()
else:
if element.is_displayed() and text in element.text:
return element
else:
if element.is_displayed():
full_text = element.text.strip()
element = None
raise Exception()
except Exception:
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
plural = "s"
if timeout == 1:
plural = ""
if not element:
if not is_present:
# The element does not exist in the HTML
message = "Element {%s} was not present after %s second%s!" % (
selector,
timeout,
plural,
)
timeout_exception(NoSuchElementException, message)
# The element exists in the HTML, but the text is not visible
message = None
if not full_text or len(str(full_text.replace("\n", ""))) > 320:
message = (
"Expected text substring {%s} for {%s} was not visible "
"after %s second%s!" % (text, selector, timeout, plural)
)
else:
full_text = full_text.replace("\n", "\\n ")
message = (
"Expected text substring {%s} for {%s} was not visible "
"after %s second%s!\n (Actual string found was {%s})"
% (text, selector, timeout, plural, full_text)
)
timeout_exception(TextNotVisibleException, message)
else:
return element
def wait_for_exact_text_visible(
driver,
text,
selector,
by="css selector",
timeout=settings.LARGE_TIMEOUT,
browser=None,
):
"""
Searches for the specified element by the given selector. Returns the
element object if the text matches exactly with the text in the element,
and the text is visible.
Raises NoSuchElementException if the element does not exist in the HTML
within the specified timeout.
Raises ElementNotVisibleException if the element exists in the HTML,
but the exact text is not visible within the specified timeout.
@Params
driver - the webdriver object (required)
text - the exact text that is expected for the element (required)
selector - the locator for identifying the page element (required)
by - the type of selector being used (Default: "css selector")
timeout - the time to wait for elements in seconds
browser - used to handle a special edge case when using Safari
@Returns
A web element object that contains the text searched for
"""
element = None
is_present = False
actual_text = None
text = str(text)
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
for x in range(int(timeout * 10)):
shared_utils.check_if_time_limit_exceeded()
actual_text = None
try:
element = driver.find_element(by=by, value=selector)
is_present = True
if element.tag_name.lower() in ["input", "textarea"]:
if (
element.is_displayed()
and text.strip() == element.get_property("value").strip()
):
return element
else:
if element.is_displayed():
actual_text = element.get_property("value").strip()
element = None
raise Exception()
elif browser == "safari":
text_attr = "innerText"
if element.tag_name.lower() in ["input", "textarea"]:
text_attr = "value"
if element.is_displayed() and (
text.strip() == element.get_attribute(text_attr).strip()
):
return element
else:
if element.is_displayed():
actual_text = element.get_attribute(text_attr)
actual_text = actual_text.strip()
element = None
raise Exception()
else:
if (
element.is_displayed()
and text.strip() == element.text.strip()
):
return element
else:
if element.is_displayed():
actual_text = element.text.strip()
element = None
raise Exception()
except Exception:
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
plural = "s"
if timeout == 1:
plural = ""
if not element:
if not is_present:
# The element does not exist in the HTML
message = "Element {%s} was not present after %s second%s!" % (
selector,
timeout,
plural,
)
timeout_exception(NoSuchElementException, message)
# The element exists in the HTML, but the exact text is not visible
message = None
if not actual_text or len(str(actual_text)) > 120:
message = (
"Expected exact text {%s} for {%s} was not visible "
"after %s second%s!" % (text, selector, timeout, plural)
)
else:
actual_text = actual_text.replace("\n", "\\n")
message = (
"Expected exact text {%s} for {%s} was not visible "
"after %s second%s!\n (Actual text was {%s})"
% (text, selector, timeout, plural, actual_text)
)
timeout_exception(TextNotVisibleException, message)
else:
return element
def wait_for_attribute(
driver,
selector,
attribute,
value=None,
by="css selector",
timeout=settings.LARGE_TIMEOUT,
):
"""
Searches for the specified element attribute by the given selector.
Returns the element object if the expected attribute is present
and the expected attribute value is present (if specified).
Raises NoSuchElementException if the element does not exist in the HTML
within the specified timeout.
Raises NoSuchAttributeException if the element exists in the HTML,
but the expected attribute/value is not present within the timeout.
@Params
driver - the webdriver object (required)
selector - the locator for identifying the page element (required)
attribute - the attribute that is expected for the element (required)
value - the attribute value that is expected (Default: None)
by - the type of selector being used (Default: "css selector")
timeout - the time to wait for the element attribute in seconds
@Returns
A web element object that contains the expected attribute/value
"""
element = None
element_present = False
attribute_present = False
found_value = None
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
for x in range(int(timeout * 10)):
shared_utils.check_if_time_limit_exceeded()
try:
element = driver.find_element(by=by, value=selector)
element_present = True
attribute_present = False
found_value = element.get_attribute(attribute)
if found_value is not None:
attribute_present = True
else:
element = None
raise Exception()
if value is not None:
if found_value == value:
return element
else:
element = None
raise Exception()
else:
return element
except Exception:
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
plural = "s"
if timeout == 1:
plural = ""
if not element:
if not element_present:
# The element does not exist in the HTML
message = "Element {%s} was not present after %s second%s!" % (
selector,
timeout,
plural,
)
timeout_exception(NoSuchElementException, message)
if not attribute_present:
# The element does not have the attribute
message = (
"Expected attribute {%s} of element {%s} was not present "
"after %s second%s!" % (attribute, selector, timeout, plural)
)
timeout_exception(NoSuchAttributeException, message)
# The element attribute exists, but the expected value does not match
message = (
"Expected value {%s} for attribute {%s} of element {%s} was not "
"present after %s second%s! (The actual value was {%s})"
% (value, attribute, selector, timeout, plural, found_value)
)
timeout_exception(NoSuchAttributeException, message)
else:
return element
def wait_for_element_clickable(
driver,
selector,
by="css selector",
timeout=settings.LARGE_TIMEOUT,
original_selector=None,
):
"""
Searches for the specified element by the given selector. Returns the
element object if the element is present, visible, & clickable on the page.
Raises NoSuchElementException if the element does not exist in the HTML
within the specified timeout.
Raises ElementNotVisibleException if the element exists in the HTML,
but is not visible (eg. opacity is "0") within the specified timeout.
Raises ElementNotInteractableException if the element is not clickable.
@Params
driver - the webdriver object (required)
selector - the locator for identifying the page element (required)
by - the type of selector being used (Default: "css selector")
timeout - the time to wait for elements in seconds
original_selector - handle pre-converted ":contains(TEXT)" selector
@Returns
A web element object
"""
element = None
is_present = False
is_visible = False
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
for x in range(int(timeout * 10)):
shared_utils.check_if_time_limit_exceeded()
try:
element = driver.find_element(by=by, value=selector)
is_present = True
if element.is_displayed():
is_visible = True
if element.is_enabled():
return element
else:
element = None
raise Exception()
else:
element = None
raise Exception()
except Exception:
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
plural = "s"
if timeout == 1:
plural = ""
if not element and by != "link text":
if (
original_selector
and ":contains(" in original_selector
and "contains(." in selector
):
selector = original_selector
if not is_present:
# The element does not exist in the HTML
message = "Element {%s} was not present after %s second%s!" % (
selector,
timeout,
plural,
)
timeout_exception(NoSuchElementException, message)
if not is_visible:
# The element exists in the HTML, but is not visible
message = "Element {%s} was not visible after %s second%s!" % (
selector,
timeout,
plural,
)
timeout_exception(ElementNotVisibleException, message)
# The element is visible in the HTML, but is not clickable
message = "Element {%s} was not clickable after %s second%s!" % (
selector,
timeout,
plural,
)
timeout_exception(ElementNotInteractableException, message)
elif not element and by == "link text" and not is_visible:
message = "Link text {%s} was not found after %s second%s!" % (
selector,
timeout,
plural,
)
timeout_exception(LinkTextNotFoundException, message)
elif not element and by == "link text" and is_visible:
message = "Link text {%s} was not clickable after %s second%s!" % (
selector,
timeout,
plural,
)
timeout_exception(ElementNotInteractableException, message)
else:
return element
def wait_for_element_absent(
driver,
selector,
by="css selector",
timeout=settings.LARGE_TIMEOUT,
original_selector=None,
):
"""
Searches for the specified element by the given selector.
Raises an exception if the element is still present after the
specified timeout.
@Params
driver - the webdriver object
selector - the locator for identifying the page element (required)
by - the type of selector being used (Default: "css selector")
timeout - the time to wait for elements in seconds
original_selector - handle pre-converted ":contains(TEXT)" selector
"""
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
for x in range(int(timeout * 10)):
shared_utils.check_if_time_limit_exceeded()
try:
driver.find_element(by=by, value=selector)
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
except Exception:
return True
plural = "s"
if timeout == 1:
plural = ""
if (
original_selector
and ":contains(" in original_selector
and "contains(." in selector
):
selector = original_selector
message = "Element {%s} was still present after %s second%s!" % (
selector,
timeout,
plural,
)
timeout_exception(Exception, message)
def wait_for_element_not_visible(
driver,
selector,
by="css selector",
timeout=settings.LARGE_TIMEOUT,
original_selector=None,
):
"""
Searches for the specified element by the given selector.
Raises an exception if the element is still visible after the
specified timeout.
@Params
driver - the webdriver object (required)
selector - the locator for identifying the page element (required)
by - the type of selector being used (Default: "css selector")
timeout - the time to wait for the element in seconds
original_selector - handle pre-converted ":contains(TEXT)" selector
"""
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
for x in range(int(timeout * 10)):
shared_utils.check_if_time_limit_exceeded()
try:
element = driver.find_element(by=by, value=selector)
if element.is_displayed():
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
else:
return True
except Exception:
return True
plural = "s"
if timeout == 1:
plural = ""
if (
original_selector
and ":contains(" in original_selector
and "contains(." in selector
):
selector = original_selector
message = "Element {%s} was still visible after %s second%s!" % (
selector,
timeout,
plural,
)
timeout_exception(Exception, message)
def wait_for_text_not_visible(
driver,
text,
selector,
by="css selector",
timeout=settings.LARGE_TIMEOUT,
browser=None,
):
"""
Searches for the text in the element of the given selector on the page.
Returns True if the text is not visible on the page within the timeout.
Raises an exception if the text is still present after the timeout.
@Params
driver - the webdriver object (required)
text - the text that is being searched for in the element (required)
selector - the locator for identifying the page element (required)
by - the type of selector being used (Default: "css selector")
timeout - the time to wait for elements in seconds
@Returns
A web element object that contains the text searched for
"""
text = str(text)
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
for x in range(int(timeout * 10)):
shared_utils.check_if_time_limit_exceeded()
if not is_text_visible(driver, text, selector, by=by, browser=browser):
return True
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
plural = "s"
if timeout == 1:
plural = ""
message = "Text {%s} in {%s} was still visible after %s second%s!" % (
text,
selector,
timeout,
plural,
)
timeout_exception(Exception, message)
def wait_for_exact_text_not_visible(
driver,
text,
selector,
by="css selector",
timeout=settings.LARGE_TIMEOUT,
browser=None,
):
"""
Searches for the text in the element of the given selector on the page.
Returns True if the element is missing the exact text within the timeout.
Raises an exception if the exact text is still present after the timeout.
@Params
driver - the webdriver object (required)
text - the text that is being searched for in the element (required)
selector - the locator for identifying the page element (required)
by - the type of selector being used (Default: "css selector")
timeout - the time to wait for elements in seconds
@Returns
A web element object that contains the text searched for
"""
text = str(text)
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
for x in range(int(timeout * 10)):
shared_utils.check_if_time_limit_exceeded()
if not is_exact_text_visible(
driver, text, selector, by=by, browser=browser
):
return True
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
plural = "s"
if timeout == 1:
plural = ""
message = (
"Exact text {%s} for {%s} was still visible after %s second%s!" % (
text,
selector,
timeout,
plural,
)
)
timeout_exception(Exception, message)
def wait_for_attribute_not_present(
driver,
selector,
attribute,
value=None,
by="css selector",
timeout=settings.LARGE_TIMEOUT,
):
"""
Searches for the specified element attribute by the given selector.
Returns True if the attribute isn't present on the page within the timeout.
Also returns True if the element is not present within the timeout.
Raises an exception if the attribute is still present after the timeout.
@Params
driver - the webdriver object (required)
selector - the locator for identifying the page element (required)
attribute - the element attribute (required)
value - the attribute value (Default: None)
by - the type of selector being used (Default: "css selector")
timeout - the time to wait for the element attribute in seconds
"""
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
for x in range(int(timeout * 10)):
shared_utils.check_if_time_limit_exceeded()
if not is_attribute_present(
driver, selector, attribute, value=value, by=by
):
return True
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
plural = "s"
if timeout == 1:
plural = ""
message = (
"Attribute {%s} of element {%s} was still present after %s second%s!"
"" % (attribute, selector, timeout, plural)
)
if value:
message = (
"Value {%s} for attribute {%s} of element {%s} was still present "
"after %s second%s!"
"" % (value, attribute, selector, timeout, plural)
)
timeout_exception(Exception, message)
def find_visible_elements(driver, selector, by="css selector"):
"""
Finds all WebElements that match a selector and are visible.
Similar to webdriver.find_elements.
@Params
driver - the webdriver object (required)
selector - the locator for identifying the page element (required)
by - the type of selector being used (Default: "css selector")
"""
elements = driver.find_elements(by=by, value=selector)
try:
v_elems = [element for element in elements if element.is_displayed()]
return v_elems
except (StaleElementReferenceException, ElementNotInteractableException):
time.sleep(0.1)
elements = driver.find_elements(by=by, value=selector)
v_elems = []
for element in elements:
if element.is_displayed():
v_elems.append(element)
return v_elems
def save_screenshot(
driver, name, folder=None, selector=None, by="css selector"
):
"""
Saves a screenshot of the current page.
If no folder is specified, uses the folder where pytest was called.
The screenshot will include the entire page unless a selector is given.
If a provided selector is not found, then takes a full-page screenshot.
If the folder provided doesn't exist, it will get created.
The screenshot will be in PNG format: (*.png)
"""
if not name.endswith(".png"):
name = name + ".png"
if folder:
abs_path = os.path.abspath(".")
file_path = os.path.join(abs_path, folder)
if not os.path.exists(file_path):
os.makedirs(file_path)
screenshot_path = os.path.join(file_path, name)
else:
screenshot_path = name
if selector:
try:
element = driver.find_element(by=by, value=selector)
element_png = element.screenshot_as_png
with open(screenshot_path, "wb") as file:
file.write(element_png)
except Exception:
if driver:
driver.get_screenshot_as_file(screenshot_path)
else:
pass
else:
if driver:
driver.get_screenshot_as_file(screenshot_path)
else:
pass
def save_page_source(driver, name, folder=None):
"""
Saves the page HTML to the current directory (or given subfolder).
If the folder specified doesn't exist, it will get created.
@Params
name - The file name to save the current page's HTML to.
folder - The folder to save the file to. (Default = current folder)
"""
from seleniumbase.core import log_helper
if not name.endswith(".html"):
name = name + ".html"
if folder:
abs_path = os.path.abspath(".")
file_path = os.path.join(abs_path, folder)
if not os.path.exists(file_path):
os.makedirs(file_path)
html_file_path = os.path.join(file_path, name)
else:
html_file_path = name
page_source = driver.page_source
html_file = codecs.open(html_file_path, "w+", "utf-8")
rendered_source = log_helper.get_html_source_with_base_href(
driver, page_source
)
html_file.write(rendered_source)
html_file.close()
def wait_for_and_accept_alert(driver, timeout=settings.LARGE_TIMEOUT):
"""
Wait for and accept an alert. Returns the text from the alert.
@Params
driver - the webdriver object (required)
timeout - the time to wait for the alert in seconds
"""
alert = wait_for_and_switch_to_alert(driver, timeout)
alert_text = alert.text
alert.accept()
return alert_text
def wait_for_and_dismiss_alert(driver, timeout=settings.LARGE_TIMEOUT):
"""
Wait for and dismiss an alert. Returns the text from the alert.
@Params
driver - the webdriver object (required)
timeout - the time to wait for the alert in seconds
"""
alert = wait_for_and_switch_to_alert(driver, timeout)
alert_text = alert.text
alert.dismiss()
return alert_text
def wait_for_and_switch_to_alert(driver, timeout=settings.LARGE_TIMEOUT):
"""
Wait for a browser alert to appear, and switch to it. This should be usable
as a drop-in replacement for driver.switch_to.alert when the alert box
may not exist yet.
@Params
driver - the webdriver object (required)
timeout - the time to wait for the alert in seconds
"""
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
for x in range(int(timeout * 10)):
shared_utils.check_if_time_limit_exceeded()
try:
alert = driver.switch_to.alert
# Raises exception if no alert present
dummy_variable = alert.text # noqa
return alert
except NoAlertPresentException:
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
message = "Alert was not present after %s seconds!" % timeout
timeout_exception(Exception, message)
def switch_to_frame(driver, frame, timeout=settings.SMALL_TIMEOUT):
"""
Wait for an iframe to appear, and switch to it. This should be
usable as a drop-in replacement for driver.switch_to.frame().
@Params
driver - the webdriver object (required)
frame - the frame element, name, id, index, or selector
timeout - the time to wait for the alert in seconds
"""
from seleniumbase.fixtures import page_utils
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
for x in range(int(timeout * 10)):
shared_utils.check_if_time_limit_exceeded()
try:
driver.switch_to.frame(frame)
return True
except Exception:
if type(frame) is str:
by = None
if page_utils.is_xpath_selector(frame):
by = "xpath"
else:
by = "css selector"
if is_element_visible(driver, frame, by=by):
try:
element = driver.find_element(by=by, value=frame)
driver.switch_to.frame(element)
return True
except Exception:
pass
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
plural = "s"
if timeout == 1:
plural = ""
message = "Frame {%s} was not visible after %s second%s!" % (
frame,
timeout,
plural,
)
timeout_exception(Exception, message)
def switch_to_window(driver, window, timeout=settings.SMALL_TIMEOUT):
"""
Wait for a window to appear, and switch to it. This should be usable
as a drop-in replacement for driver.switch_to.window().
@Params
driver - the webdriver object (required)
window - the window index or window handle
timeout - the time to wait for the window in seconds
"""
if window == -1:
window = len(driver.window_handles) - 1
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
if isinstance(window, int):
caps = driver.capabilities
if (
caps["browserName"].lower() == "safari"
and "safari:platformVersion" in caps
):
# Reversed window_handles on Safari
window = len(driver.window_handles) - 1 - window
if window < 0:
window = 0
for x in range(int(timeout * 10)):
shared_utils.check_if_time_limit_exceeded()
try:
window_handle = driver.window_handles[window]
driver.switch_to.window(window_handle)
return True
except IndexError:
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
plural = "s"
if timeout == 1:
plural = ""
message = "Window {%s} was not present after %s second%s!" % (
window,
timeout,
plural,
)
timeout_exception(Exception, message)
else:
window_handle = window
for x in range(int(timeout * 10)):
shared_utils.check_if_time_limit_exceeded()
try:
driver.switch_to.window(window_handle)
return True
except NoSuchWindowException:
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
plural = "s"
if timeout == 1:
plural = ""
message = "Window {%s} was not present after %s second%s!" % (
window,
timeout,
plural,
)
timeout_exception(Exception, message)
############
# Duplicates for easier use without BaseCase
def wait_for_element(
driver,
selector,
by="css selector",
timeout=settings.LARGE_TIMEOUT,
):
return wait_for_element_visible(
driver=driver,
selector=selector,
by=by,
timeout=timeout,
)
def wait_for_text(
driver,
text,
selector,
by="css selector",
timeout=settings.LARGE_TIMEOUT,
):
browser = None # Only used for covering a Safari edge case
try:
if "safari:platformVersion" in driver.capabilities:
browser = "safari"
except Exception:
pass
return wait_for_text_visible(
driver=driver,
text=text,
selector=selector,
by=by,
timeout=timeout,
browser=browser,
)