Simplify retry logic for ui helper functions
This commit is contained in:
@@ -75,7 +75,8 @@ def make_desired_capabilities():
|
||||
desired_capabilities['build'] = settings.SAUCE.get('BUILD')
|
||||
desired_capabilities['video-upload-on-pass'] = False
|
||||
desired_capabilities['sauce-advisor'] = False
|
||||
desired_capabilities['record-screenshots'] = False
|
||||
desired_capabilities['capture-html'] = True
|
||||
desired_capabilities['record-screenshots'] = True
|
||||
desired_capabilities['selenium-version'] = "2.34.0"
|
||||
desired_capabilities['max-duration'] = 3600
|
||||
desired_capabilities['public'] = 'public restricted'
|
||||
@@ -87,6 +88,7 @@ def initial_setup(server):
|
||||
"""
|
||||
Launch the browser once before executing the tests.
|
||||
"""
|
||||
# from nose.tools import set_trace; set_trace()
|
||||
world.absorb(settings.SAUCE.get('SAUCE_ENABLED'), 'SAUCE_ENABLED')
|
||||
|
||||
if not world.SAUCE_ENABLED:
|
||||
|
||||
@@ -5,7 +5,7 @@ from lettuce import world
|
||||
import time
|
||||
import platform
|
||||
from urllib import quote_plus
|
||||
from selenium.common.exceptions import WebDriverException, StaleElementReferenceException
|
||||
from selenium.common.exceptions import WebDriverException
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
@@ -18,11 +18,6 @@ def wait(seconds):
|
||||
time.sleep(float(seconds))
|
||||
|
||||
|
||||
@world.absorb
|
||||
def wait_for(func):
|
||||
WebDriverWait(world.browser.driver, 5).until(func)
|
||||
|
||||
|
||||
@world.absorb
|
||||
def visit(url):
|
||||
world.browser.visit(django_url(url))
|
||||
@@ -34,7 +29,7 @@ def url_equals(url):
|
||||
|
||||
|
||||
@world.absorb
|
||||
def is_css_present(css_selector, wait_time=5):
|
||||
def is_css_present(css_selector, wait_time=10):
|
||||
return world.browser.is_element_present_by_css(css_selector, wait_time=wait_time)
|
||||
|
||||
|
||||
@@ -44,92 +39,116 @@ def is_css_not_present(css_selector, wait_time=5):
|
||||
|
||||
|
||||
@world.absorb
|
||||
def css_has_text(css_selector, text, index=0, max_attempts=5):
|
||||
return world.css_text(css_selector, index=index, max_attempts=max_attempts) == text
|
||||
def css_has_text(css_selector, text, index=0):
|
||||
return world.css_text(css_selector, index=index) == text
|
||||
|
||||
|
||||
@world.absorb
|
||||
def css_find(css, wait_time=5):
|
||||
def is_visible(_driver):
|
||||
return EC.visibility_of_element_located((By.CSS_SELECTOR, css,))
|
||||
def wait_for(func, timeout=5):
|
||||
WebDriverWait(world.browser.driver, timeout).until(func)
|
||||
|
||||
world.browser.is_element_present_by_css(css, wait_time=wait_time)
|
||||
wait_for(is_visible)
|
||||
|
||||
def wait_for_present(css_selector, timeout=30):
|
||||
"""
|
||||
Waiting for the element to be present in the DOM.
|
||||
Throws an error if the wait_for time expires.
|
||||
Otherwise this method will return None
|
||||
"""
|
||||
WebDriverWait(driver=world.browser.driver,
|
||||
timeout=60).until(EC.presence_of_element_located((By.CSS_SELECTOR, css_selector,)))
|
||||
|
||||
|
||||
@world.absorb
|
||||
def wait_for_visible(css_selector, timeout=30):
|
||||
"""
|
||||
Waiting for the element to be visible in the DOM.
|
||||
Throws an error if the wait_for time expires.
|
||||
Otherwise this method will return None
|
||||
"""
|
||||
WebDriverWait(driver=world.browser.driver,
|
||||
timeout=timeout).until(EC.visibility_of_element_located((By.CSS_SELECTOR, css_selector,)))
|
||||
|
||||
|
||||
@world.absorb
|
||||
def wait_for_invisible(css_selector, timeout=30):
|
||||
"""
|
||||
Waiting for the element to be either invisible or not present on the DOM.
|
||||
Throws an error if the wait_for time expires.
|
||||
Otherwise this method will return None
|
||||
"""
|
||||
WebDriverWait(driver=world.browser.driver,
|
||||
timeout=timeout).until(EC.invisibility_of_element_located((By.CSS_SELECTOR, css_selector,)))
|
||||
|
||||
|
||||
@world.absorb
|
||||
def wait_for_clickable(css_selector, timeout=30):
|
||||
"""
|
||||
Waiting for the element to be present and clickable.
|
||||
Throws an error if the wait_for time expires.
|
||||
Otherwise this method will return None.
|
||||
"""
|
||||
# Sometimes the element is clickable then gets obscured.
|
||||
# In this case, pause so that it is not reported clickable too early
|
||||
WebDriverWait(world.browser.driver,
|
||||
timeout=timeout).until(EC.element_to_be_clickable((By.CSS_SELECTOR, css_selector,)))
|
||||
|
||||
|
||||
@world.absorb
|
||||
def css_find(css, wait_time=30):
|
||||
"""
|
||||
Wait for the element(s) as defined by css locator
|
||||
to be present.
|
||||
|
||||
This method will return a WebDriverElement.
|
||||
"""
|
||||
wait_for_present(css_selector=css, timeout=wait_time)
|
||||
return world.browser.find_by_css(css)
|
||||
|
||||
|
||||
@world.absorb
|
||||
def css_click(css_selector, index=0, max_attempts=5, success_condition=lambda: True):
|
||||
def css_click(css_selector, index=0, wait_time=30):
|
||||
"""
|
||||
Perform a click on a CSS selector, retrying if it initially fails.
|
||||
Perform a click on a CSS selector, first waiting for the element
|
||||
to be present and clickable.
|
||||
|
||||
This function handles errors that may be thrown if the component cannot be clicked on.
|
||||
However, there are cases where an error may not be thrown, and yet the operation did not
|
||||
actually succeed. For those cases, a success_condition lambda can be supplied to verify that the click worked.
|
||||
|
||||
This function will return True if the click worked (taking into account both errors and the optional
|
||||
success_condition).
|
||||
This method will return True if the click worked.
|
||||
"""
|
||||
assert is_css_present(css_selector), "{} is not present".format(css_selector)
|
||||
for _ in range(max_attempts):
|
||||
try:
|
||||
world.css_find(css_selector)[index].click()
|
||||
if success_condition():
|
||||
return
|
||||
except WebDriverException:
|
||||
# Occasionally, MathJax or other JavaScript can cover up
|
||||
# an element temporarily.
|
||||
# If this happens, wait a second, then try again
|
||||
world.wait(1)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
# try once more, letting exceptions raise
|
||||
world.css_find(css_selector)[index].click()
|
||||
if not success_condition():
|
||||
raise Exception("unsuccessful click")
|
||||
wait_for_clickable(css_selector, timeout=wait_time)
|
||||
assert world.css_find(css_selector)[index].visible
|
||||
|
||||
# Sometimes you can't click in the center of the element, as
|
||||
# another element might be on top of it. In this case, try
|
||||
# clicking in the upper left corner.
|
||||
try:
|
||||
return world.css_find(css_selector)[index].click()
|
||||
|
||||
except WebDriverException:
|
||||
return css_click_at(css_selector, index=index)
|
||||
|
||||
|
||||
@world.absorb
|
||||
def css_check(css_selector, index=0, max_attempts=5, success_condition=lambda: True):
|
||||
def css_check(css_selector, index=0, wait_time=30):
|
||||
"""
|
||||
Checks a check box based on a CSS selector, retrying if it initially fails.
|
||||
Checks a check box based on a CSS selector, first waiting for the element
|
||||
to be present and clickable. This is just a wrapper for calling "click"
|
||||
because that's how selenium interacts with check boxes and radio buttons.
|
||||
|
||||
This function handles errors that may be thrown if the component cannot be clicked on.
|
||||
However, there are cases where an error may not be thrown, and yet the operation did not
|
||||
actually succeed. For those cases, a success_condition lambda can be supplied to verify that the check worked.
|
||||
|
||||
This function will return True if the check worked (taking into account both errors and the optional
|
||||
success_condition).
|
||||
This method will return True if the check worked.
|
||||
"""
|
||||
assert is_css_present(css_selector), "{} is not present".format(css_selector)
|
||||
for _ in range(max_attempts):
|
||||
try:
|
||||
world.css_find(css_selector)[index].check()
|
||||
if success_condition():
|
||||
return
|
||||
except WebDriverException:
|
||||
# Occasionally, MathJax or other JavaScript can cover up
|
||||
# an element temporarily.
|
||||
# If this happens, wait a second, then try again
|
||||
world.wait(1)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
# try once more, letting exceptions raise
|
||||
world.css_find(css_selector)[index].check()
|
||||
if not success_condition():
|
||||
raise Exception("unsuccessful check")
|
||||
return css_click(css_selector=css_selector, index=index, wait_time=wait_time)
|
||||
|
||||
|
||||
@world.absorb
|
||||
def css_click_at(css, x_cord=10, y_cord=10):
|
||||
def css_click_at(css_selector, index=0, x_coord=10, y_coord=10, timeout=5):
|
||||
'''
|
||||
A method to click at x,y coordinates of the element
|
||||
rather than in the center of the element
|
||||
'''
|
||||
element = css_find(css).first
|
||||
element.action_chains.move_to_element_with_offset(element._element, x_cord, y_cord)
|
||||
wait_for_clickable(css_selector, timeout=timeout)
|
||||
element = css_find(css_selector)[index]
|
||||
assert element.visible
|
||||
|
||||
element.action_chains.move_to_element_with_offset(element._element, x_coord, y_coord)
|
||||
element.action_chains.click()
|
||||
element.action_chains.perform()
|
||||
|
||||
@@ -139,58 +158,55 @@ def id_click(elem_id):
|
||||
"""
|
||||
Perform a click on an element as specified by its id
|
||||
"""
|
||||
world.css_click('#%s' % elem_id)
|
||||
css_click('#%s' % elem_id)
|
||||
|
||||
|
||||
@world.absorb
|
||||
def css_fill(css_selector, text, index=0, max_attempts=5):
|
||||
assert is_css_present(css_selector)
|
||||
return world.retry_on_exception(lambda: world.browser.find_by_css(css_selector)[index].fill(text), max_attempts=max_attempts)
|
||||
def css_fill(css_selector, text, index=0):
|
||||
css_find(css_selector)[index].fill(text)
|
||||
|
||||
|
||||
@world.absorb
|
||||
def click_link(partial_text, index=0, max_attempts=5):
|
||||
return world.retry_on_exception(lambda: world.browser.find_link_by_partial_text(partial_text)[index].click(), max_attempts=max_attempts)
|
||||
def click_link(partial_text, index=0):
|
||||
world.browser.find_link_by_partial_text(partial_text)[index].click()
|
||||
|
||||
|
||||
@world.absorb
|
||||
def css_text(css_selector, index=0, max_attempts=5):
|
||||
|
||||
def css_text(css_selector, index=0, timeout=30):
|
||||
# Wait for the css selector to appear
|
||||
if world.is_css_present(css_selector):
|
||||
return world.retry_on_exception(lambda: world.browser.find_by_css(css_selector)[index].text, max_attempts=max_attempts)
|
||||
if is_css_present(css_selector):
|
||||
return css_find(css_selector, wait_time=timeout)[index].text
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
@world.absorb
|
||||
def css_value(css_selector, index=0, max_attempts=5):
|
||||
|
||||
def css_value(css_selector, index=0):
|
||||
# Wait for the css selector to appear
|
||||
if world.is_css_present(css_selector):
|
||||
return world.retry_on_exception(lambda: world.browser.find_by_css(css_selector)[index].value, max_attempts=max_attempts)
|
||||
if is_css_present(css_selector):
|
||||
return css_find(css_selector)[index].value
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
@world.absorb
|
||||
def css_html(css_selector, index=0, max_attempts=5):
|
||||
def css_html(css_selector, index=0):
|
||||
"""
|
||||
Returns the HTML of a css_selector and will retry if there is a StaleElementReferenceException
|
||||
Returns the HTML of a css_selector
|
||||
"""
|
||||
assert is_css_present(css_selector)
|
||||
return world.retry_on_exception(lambda: world.browser.find_by_css(css_selector)[index].html, max_attempts=max_attempts)
|
||||
return css_find(css_selector)[index].html
|
||||
|
||||
|
||||
@world.absorb
|
||||
def css_has_class(css_selector, class_name, index=0, max_attempts=5):
|
||||
return world.retry_on_exception(lambda: world.css_find(css_selector)[index].has_class(class_name), max_attempts=max_attempts)
|
||||
def css_has_class(css_selector, class_name, index=0):
|
||||
return css_find(css_selector)[index].has_class(class_name)
|
||||
|
||||
|
||||
@world.absorb
|
||||
def css_visible(css_selector, index=0, max_attempts=5):
|
||||
def css_visible(css_selector, index=0):
|
||||
assert is_css_present(css_selector)
|
||||
return world.retry_on_exception(lambda: world.browser.find_by_css(css_selector)[index].visible, max_attempts=max_attempts)
|
||||
return css_find(css_selector)[index].visible
|
||||
|
||||
|
||||
@world.absorb
|
||||
@@ -235,14 +251,17 @@ def click_tools():
|
||||
def is_mac():
|
||||
return platform.mac_ver()[0] is not ''
|
||||
|
||||
|
||||
@world.absorb
|
||||
def is_firefox():
|
||||
return world.browser.driver_name is 'Firefox'
|
||||
|
||||
|
||||
@world.absorb
|
||||
def trigger_event(css_selector, event='change', index=0):
|
||||
world.browser.execute_script("$('{}:eq({})').trigger('{}')".format(css_selector, index, event))
|
||||
|
||||
|
||||
@world.absorb
|
||||
def retry_on_exception(func, max_attempts=5):
|
||||
attempt = 0
|
||||
|
||||
Reference in New Issue
Block a user