Skip to content
This repository has been archived by the owner on Jan 19, 2024. It is now read-only.

Commit

Permalink
Merge pull request #224 from edx/jmbowman/retry_page_load_errors
Browse files Browse the repository at this point in the history
Retry page load errors
  • Loading branch information
jmbowman authored Feb 19, 2019
2 parents eed593a + eed686f commit 216effb
Show file tree
Hide file tree
Showing 29 changed files with 222 additions and 216 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ sudo: false
branches:
only:
- master
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
before_install:
- wget https://github.com/mozilla/geckodriver/releases/download/v0.21.0/geckodriver-v0.21.0-linux64.tar.gz
- mkdir geckodriver
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
v0.9.3 (02/14/19)
* PageLoadError and WrongPageError now inherit WebDriverException so
they can trigger retries instead of immediately failing tests

v0.9.2 (11/20/18) (0.9.1 skipped due to PyPI deployment issue)
* Support passing additional desired capabilities to remote WebDriver instances.

Expand Down
2 changes: 1 addition & 1 deletion bok_choy/a11y/a11y_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def _get_rules_js(self):
Raises: `RuntimeError` if the file isn't found.
"""
if not os.path.isfile(self.config.rules_file):
msg = 'Could not find the accessibility tools JS file: {}'.format(
msg = u'Could not find the accessibility tools JS file: {}'.format(
self.config.rules_file)
raise RuntimeError(msg)

Expand Down
20 changes: 10 additions & 10 deletions bok_choy/a11y/axe_core_ruleset.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class AxeCoreAuditConfig(A11yAuditConfig):
def __init__(self, *args, **kwargs):
super(AxeCoreAuditConfig, self).__init__(*args, **kwargs)
self.rules, self.context = None, None
self.custom_rules = "customRules={}"
self.custom_rules = u"customRules={}"
self.rules_file = os.path.join(
os.path.split(CUR_DIR)[0],
'vendor/axe-core/axe.min.js'
Expand Down Expand Up @@ -243,7 +243,7 @@ def _check_rules(browser, rules_js, config):
__Caution__: You probably don't really want to call this method
directly! It will be used by `AxeCoreAudit.do_audit`.
"""
audit_run_script = dedent("""
audit_run_script = dedent(u"""
{rules_js}
{custom_rules}
axe.configure(customRules);
Expand All @@ -259,7 +259,7 @@ def _check_rules(browser, rules_js, config):
options=config.rules
)

audit_results_script = dedent("""
audit_results_script = dedent(u"""
window.console.log(window.a11yAuditResults);
return window.a11yAuditResults;
""")
Expand Down Expand Up @@ -350,14 +350,14 @@ def _get_message(node):

lines = []
for error_type in errors:
lines.append("Severity: {}".format(error_type.get("impact")))
lines.append("Rule ID: {}".format(error_type.get("id")))
lines.append("Help URL: {}\n".format(error_type.get('helpUrl')))
lines.append(u"Severity: {}".format(error_type.get("impact")))
lines.append(u"Rule ID: {}".format(error_type.get("id")))
lines.append(u"Help URL: {}\n".format(error_type.get('helpUrl')))

for node in error_type['nodes']:
msg = "Message: {}".format(_get_message(node))
html = "Html: {}".format(node.get('html').encode('utf-8'))
target = "Target: {}".format(node.get('target'))
msg = u"Message: {}".format(_get_message(node))
html = u"Html: {}".format(node.get('html').encode('utf-8'))
target = u"Target: {}".format(node.get('target'))
fill_opts = {
'width': 100,
'initial_indent': '\t',
Expand All @@ -382,7 +382,7 @@ def report_errors(audit, url):
"""
errors = AxeCoreAudit.get_errors(audit)
if errors["total"] > 0:
msg = "URL '{}' has {} errors:\n\n{}".format(
msg = u"URL '{}' has {} errors:\n\n{}".format(
url,
errors["total"],
AxeCoreAudit.format_errors(errors["errors"])
Expand Down
14 changes: 7 additions & 7 deletions bok_choy/a11y/axs_ruleset.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ def set_scope(self, include=None, exclude=None):
page.a11y_audit.config.set_scope()
"""
if include:
self.scope = "document.querySelector(\"{}\")".format(
', '.join(include)
self.scope = u"document.querySelector(\"{}\")".format(
u', '.join(include)
)
else:
self.scope = "null"
Expand Down Expand Up @@ -160,20 +160,20 @@ def _check_rules(browser, rules_js, config):
# run all rules.
rules = config.rules_to_run
if rules:
rules_config = "auditConfig.auditRulesToRun = {rules};".format(
rules_config = u"auditConfig.auditRulesToRun = {rules};".format(
rules=rules)
else:
rules_config = ""

ignored_rules = config.rules_to_ignore
if ignored_rules:
rules_config += (
"\nauditConfig.auditRulesToIgnore = {rules};".format(
u"\nauditConfig.auditRulesToIgnore = {rules};".format(
rules=ignored_rules
)
)

script = dedent("""
script = dedent(u"""
{rules_js}
var auditConfig = new axs.AuditConfiguration();
{rules_config}
Expand Down Expand Up @@ -219,9 +219,9 @@ def report_errors(audit, url):
"""
errors = AxsAudit.get_errors(audit)
if errors:
msg = "URL '{}' has {} errors:\n{}".format(
msg = u"URL '{}' has {} errors:\n{}".format(
url,
len(errors),
(', ').join(errors)
', '.join(errors)
)
raise AccessibilityError(msg)
38 changes: 19 additions & 19 deletions bok_choy/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def save_source(driver, name):
with open(file_name, 'wb') as output_file:
output_file.write(source.encode('utf-8'))
except Exception: # pylint: disable=broad-except
msg = "Could not save the browser page source to {}.".format(file_name)
msg = u"Could not save the browser page source to {}.".format(file_name)
LOGGER.warning(msg)


Expand Down Expand Up @@ -131,8 +131,8 @@ def save_screenshot(driver, name):

else:
msg = (
"Browser does not support screenshots. "
"Could not save screenshot '{name}'"
u"Browser does not support screenshots. "
u"Could not save screenshot '{name}'"
).format(name=name)

LOGGER.warning(msg)
Expand Down Expand Up @@ -182,8 +182,8 @@ def save_driver_logs(driver, prefix):
output_file.write("{}{}".format(dumps(line), '\n'))
except: # pylint: disable=bare-except
msg = (
"Could not save browser log of type '{log_type}'. "
"It may be that the browser does not support it."
u"Could not save browser log of type '{log_type}'. "
u"It may be that the browser does not support it."
).format(log_type=log_type)

LOGGER.warning(msg, exc_info=True)
Expand Down Expand Up @@ -311,22 +311,22 @@ def _firefox_profile():
profile_dir = os.environ.get(FIREFOX_PROFILE_ENV_VAR)

if profile_dir:
LOGGER.info("Using firefox profile: %s", profile_dir)
LOGGER.info(u"Using firefox profile: %s", profile_dir)
try:
firefox_profile = webdriver.FirefoxProfile(profile_dir)
except OSError as err:
if err.errno == errno.ENOENT:
raise BrowserConfigError(
"Firefox profile directory {env_var}={profile_dir} does not exist".format(
u"Firefox profile directory {env_var}={profile_dir} does not exist".format(
env_var=FIREFOX_PROFILE_ENV_VAR, profile_dir=profile_dir))
elif err.errno == errno.EACCES:
raise BrowserConfigError(
"Firefox profile directory {env_var}={profile_dir} has incorrect permissions. It must be \
u"Firefox profile directory {env_var}={profile_dir} has incorrect permissions. It must be \
readable and executable.".format(env_var=FIREFOX_PROFILE_ENV_VAR, profile_dir=profile_dir))
else:
# Some other OSError:
raise BrowserConfigError(
"Problem with firefox profile directory {env_var}={profile_dir}: {msg}"
u"Problem with firefox profile directory {env_var}={profile_dir}: {msg}"
.format(env_var=FIREFOX_PROFILE_ENV_VAR, profile_dir=profile_dir, msg=str(err)))
else:
LOGGER.info("Using default firefox profile")
Expand Down Expand Up @@ -373,14 +373,14 @@ def _local_browser_class(browser_name):
"""

# Log name of local browser
LOGGER.info("Using local browser: %s [Default is firefox]", browser_name)
LOGGER.info(u"Using local browser: %s [Default is firefox]", browser_name)

# Get class of local browser based on name
browser_class = BROWSERS.get(browser_name)
headless = os.environ.get('BOKCHOY_HEADLESS', 'false').lower() == 'true'
if browser_class is None:
raise BrowserConfigError(
"Invalid browser name {name}. Options are: {options}".format(
u"Invalid browser name {name}. Options are: {options}".format(
name=browser_name, options=", ".join(list(BROWSERS.keys()))))
else:
if browser_name == 'firefox':
Expand All @@ -392,7 +392,7 @@ def _local_browser_class(browser_name):
firefox_options = FirefoxOptions()
firefox_options.log.level = 'trace'
if headless:
firefox_options.set_headless(True)
firefox_options.headless = True
browser_args = []
browser_kwargs = {
'firefox_profile': _firefox_profile(),
Expand All @@ -418,7 +418,7 @@ def _local_browser_class(browser_name):
elif browser_name == 'chrome':
chrome_options = ChromeOptions()
if headless:
chrome_options.set_headless(True)
chrome_options.headless = True

# Emulate webcam and microphone for testing purposes
chrome_options.add_argument('--use-fake-device-for-media-stream')
Expand All @@ -429,7 +429,7 @@ def _local_browser_class(browser_name):

browser_args = []
browser_kwargs = {
'chrome_options': chrome_options,
'options': chrome_options,
}
else:
browser_args, browser_kwargs = [], {}
Expand All @@ -453,14 +453,14 @@ def _remote_browser_class(env_vars, tags=None):
caps = _capabilities_dict(envs, tags)

if 'accessKey' in caps:
LOGGER.info("Using SauceLabs: %s %s %s", caps['platform'], caps['browserName'], caps['version'])
LOGGER.info(u"Using SauceLabs: %s %s %s", caps['platform'], caps['browserName'], caps['version'])
else:
LOGGER.info("Using Remote Browser: %s", caps['browserName'])
LOGGER.info(u"Using Remote Browser: %s", caps['browserName'])

# Create and return a new Browser
# We assume that the WebDriver end-point is running locally (e.g. using
# SauceConnect)
url = "http://{0}:{1}/wd/hub".format(
url = u"http://{0}:{1}/wd/hub".format(
envs['SELENIUM_HOST'], envs['SELENIUM_PORT'])

browser_args = []
Expand Down Expand Up @@ -532,13 +532,13 @@ def _required_envs(env_vars):
missing = [key for key, val in list(envs.items()) if val is None]
if missing:
msg = (
"These environment variables must be set: " + ", ".join(missing)
u"These environment variables must be set: " + u", ".join(missing)
)
raise BrowserConfigError(msg)

# Check that we support this browser
if envs['SELENIUM_BROWSER'] not in BROWSERS:
msg = "Unsuppported browser: {0}".format(envs['SELENIUM_BROWSER'])
msg = u"Unsuppported browser: {0}".format(envs['SELENIUM_BROWSER'])
raise BrowserConfigError(msg)

return envs
Expand Down
12 changes: 6 additions & 6 deletions bok_choy/javascript.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,14 @@ def _wait_for_js(self):
if hasattr(self, '_js_vars') and self._js_vars:
EmptyPromise(
lambda: _are_js_vars_defined(self.browser, self._js_vars),
"JavaScript variables defined: {0}".format(", ".join(self._js_vars))
u"JavaScript variables defined: {0}".format(", ".join(self._js_vars))
).fulfill()

# Wait for RequireJS dependencies to load
if hasattr(self, '_requirejs_deps') and self._requirejs_deps:
EmptyPromise(
lambda: _are_requirejs_deps_loaded(self.browser, self._requirejs_deps),
"RequireJS dependencies loaded: {0}".format(", ".join(self._requirejs_deps)),
u"RequireJS dependencies loaded: {0}".format(", ".join(self._requirejs_deps)),
try_limit=5
).fulfill()

Expand All @@ -144,13 +144,13 @@ def _are_js_vars_defined(browser, js_vars):
"""
# This script will evaluate to True iff all of
# the required vars are defined.
script = " && ".join([
"!(typeof {0} === 'undefined')".format(var)
script = u" && ".join([
u"!(typeof {0} === 'undefined')".format(var)
for var in js_vars
])

try:
return browser.execute_script("return {}".format(script))
return browser.execute_script(u"return {}".format(script))
except WebDriverException as exc:
if "is not defined" in exc.msg or "is undefined" in exc.msg:
return False
Expand All @@ -176,7 +176,7 @@ def _are_requirejs_deps_loaded(browser, deps):
# We install a RequireJS module with the dependencies we want
# to ensure are loaded. When our module loads, we return
# control to the test suite.
script = dedent("""
script = dedent(u"""
// Retrieve the callback function used to return control to the test suite
var callback = arguments[arguments.length - 1];
Expand Down
Loading

0 comments on commit 216effb

Please sign in to comment.