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

Retry page load errors #224

Merged
merged 1 commit into from
Feb 19, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -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
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.

2 changes: 1 addition & 1 deletion bok_choy/a11y/a11y_audit.py
Original file line number Diff line number Diff line change
@@ -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)

20 changes: 10 additions & 10 deletions bok_choy/a11y/axe_core_ruleset.py
Original file line number Diff line number Diff line change
@@ -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'
@@ -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);
@@ -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;
""")
@@ -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',
@@ -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"])
14 changes: 7 additions & 7 deletions bok_choy/a11y/axs_ruleset.py
Original file line number Diff line number Diff line change
@@ -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"
@@ -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}
@@ -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
@@ -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)


@@ -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)
@@ -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)
@@ -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")
@@ -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':
@@ -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(),
@@ -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')
@@ -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 = [], {}
@@ -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 = []
@@ -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
12 changes: 6 additions & 6 deletions bok_choy/javascript.py
Original file line number Diff line number Diff line change
@@ -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()

@@ -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
@@ -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];

Loading