diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..9c554d9e
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,6 @@
+repos:
+- repo: https://github.com/ambv/black
+ rev: stable
+ hooks:
+ - id: black
+ language_version: python3
diff --git a/README.rst b/README.rst
index 4bac8d50..c75ae9b4 100644
--- a/README.rst
+++ b/README.rst
@@ -16,6 +16,8 @@ support for running `Selenium `_ based tests.
.. image:: https://img.shields.io/badge/docs-latest-brightgreen.svg
:target: http://pytest-selenium.readthedocs.io/en/latest/
:alt: Read the Docs
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/ambv/black
.. image:: https://img.shields.io/github/issues-raw/pytest-dev/pytest-selenium.svg
:target: https://github.com/pytest-dev/pytest-selenium/issues
:alt: Issues
diff --git a/docs/conf.py b/docs/conf.py
index 8e21024c..0a47b7ae 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -19,48 +19,45 @@
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
+# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
-extensions = [
- 'sphinx.ext.autodoc',
- 'sphinx.ext.viewcode',
-]
+extensions = ["sphinx.ext.autodoc", "sphinx.ext.viewcode"]
# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md']
-source_suffix = '.rst'
+source_suffix = ".rst"
# The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
# The master toctree document.
-master_doc = 'index'
+master_doc = "index"
# General information about the project.
-project = u'pytest-selenium'
-copyright = u'2015, Dave Hunt'
-author = u'Dave Hunt'
+project = u"pytest-selenium"
+copyright = u"2015, Dave Hunt"
+author = u"Dave Hunt"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
-version = 'latest'
+version = "latest"
# The full version, including alpha/beta/rc tags.
-release = 'latest'
+release = "latest"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -71,37 +68,37 @@
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
-#today = ''
+# today = ''
# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
-exclude_patterns = ['_build']
+exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all
# documents.
-#default_role = None
+# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
-#show_authors = False
+# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
-#keep_warnings = False
+# keep_warnings = False
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
@@ -111,31 +108,31 @@
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-html_theme = 'default'
+html_theme = "default"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
-#html_theme_options = {}
+# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
-#html_title = None
+# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
-#html_logo = None
+# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
-#html_favicon = None
+# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
@@ -145,109 +142,111 @@
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
-#html_extra_path = []
+# html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
# If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
# If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
# If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
-#html_search_language = 'en'
+# html_search_language = 'en'
# A dictionary with options for the search language support, empty by default.
# Now only 'ja' uses this config value
-#html_search_options = {'type': 'default'}
+# html_search_options = {'type': 'default'}
# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
-#html_search_scorer = 'scorer.js'
+# html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder.
-htmlhelp_basename = 'pytest-seleniumdoc'
+htmlhelp_basename = "pytest-seleniumdoc"
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
-
-# Latex figure (float) alignment
-#'figure_align': 'htbp',
+ # The paper size ('letterpaper' or 'a4paper').
+ # 'papersize': 'letterpaper',
+ # The font size ('10pt', '11pt' or '12pt').
+ # 'pointsize': '10pt',
+ # Additional stuff for the LaTeX preamble.
+ # 'preamble': '',
+ # Latex figure (float) alignment
+ # 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
- (master_doc, 'pytest-selenium.tex', u'pytest-selenium Documentation',
- u'Dave Hunt', 'manual'),
+ (
+ master_doc,
+ "pytest-selenium.tex",
+ u"pytest-selenium Documentation",
+ u"Dave Hunt",
+ "manual",
+ )
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
-#latex_logo = None
+# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
# If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
# If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
# Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
# If false, no module index is generated.
-#latex_domain_indices = True
+# latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
@@ -255,12 +254,11 @@
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
- (master_doc, 'pytest-selenium', u'pytest-selenium Documentation',
- [author], 1)
+ (master_doc, "pytest-selenium", u"pytest-selenium Documentation", [author], 1)
]
# If true, show URL addresses after external links.
-#man_show_urls = False
+# man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
@@ -269,19 +267,25 @@
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
- (master_doc, 'pytest-selenium', u'pytest-selenium Documentation',
- author, 'pytest-selenium', 'One line description of project.',
- 'Miscellaneous'),
+ (
+ master_doc,
+ "pytest-selenium",
+ u"pytest-selenium Documentation",
+ author,
+ "pytest-selenium",
+ "One line description of project.",
+ "Miscellaneous",
+ )
]
# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
+# texinfo_appendices = []
# If false, no module index is generated.
-#texinfo_domain_indices = True
+# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
+# texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
-#texinfo_no_detailmenu = False
+# texinfo_no_detailmenu = False
diff --git a/docs/user_guide.rst b/docs/user_guide.rst
index 4ad31fac..daab7a79 100644
--- a/docs/user_guide.rst
+++ b/docs/user_guide.rst
@@ -136,6 +136,16 @@ preferences, and a command line argument:
See the `Firefox options API documentation`_ for full details of what can be
configured.
+You can also use the ``firefox_preferences`` and ``firefox_arguments`` markers:
+
+.. code-block:: python
+
+ import pytest
+ @pytest.mark.firefox_arguments('-foreground')
+ @pytest.mark.firefox_preferences({'browser.anchor_color': '#FF0000'})
+ def test_firefox(selenium):
+ selenium.get('http://www.example.com')
+
Chrome
------
diff --git a/pytest_selenium/drivers/browserstack.py b/pytest_selenium/drivers/browserstack.py
index 431c9eaa..7ba409cf 100644
--- a/pytest_selenium/drivers/browserstack.py
+++ b/pytest_selenium/drivers/browserstack.py
@@ -10,7 +10,7 @@
class BrowserStack(Provider):
- API = 'https://www.browserstack.com/automate/sessions/{session}.json'
+ API = "https://www.browserstack.com/automate/sessions/{session}.json"
@property
def auth(self):
@@ -18,65 +18,70 @@ def auth(self):
@property
def executor(self):
- return 'http://{0}:{1}@hub.browserstack.com:80/wd/hub'.format(
- self.username, self.key)
+ return "http://{0}:{1}@hub.browserstack.com:80/wd/hub".format(
+ self.username, self.key
+ )
@property
def username(self):
- return self.get_credential('username', ['BROWSERSTACK_USERNAME',
- 'BROWSERSTACK_USR'])
+ return self.get_credential(
+ "username", ["BROWSERSTACK_USERNAME", "BROWSERSTACK_USR"]
+ )
@property
def key(self):
- return self.get_credential('key', ['BROWSERSTACK_ACCESS_KEY',
- 'BROWSERSTACK_PSW'])
+ return self.get_credential(
+ "key", ["BROWSERSTACK_ACCESS_KEY", "BROWSERSTACK_PSW"]
+ )
@pytest.mark.optionalhook
def pytest_selenium_runtest_makereport(item, report, summary, extra):
provider = BrowserStack()
- if not provider.uses_driver(item.config.getoption('driver')):
+ if not provider.uses_driver(item.config.getoption("driver")):
return
- passed = report.passed or (report.failed and hasattr(report, 'wasxfail'))
+ passed = report.passed or (report.failed and hasattr(report, "wasxfail"))
session_id = item._driver.session_id
api_url = provider.API.format(session=session_id)
try:
job_info = requests.get(api_url, auth=provider.auth, timeout=10).json()
- job_url = job_info['automation_session']['browser_url']
+ job_url = job_info["automation_session"]["browser_url"]
# Add the job URL to the summary
- summary.append('{0} Job: {1}'.format(provider.name, job_url))
- pytest_html = item.config.pluginmanager.getplugin('html')
+ summary.append("{0} Job: {1}".format(provider.name, job_url))
+ pytest_html = item.config.pluginmanager.getplugin("html")
# Add the job URL to the HTML report
- extra.append(pytest_html.extras.url(job_url, '{0} Job'.format(
- provider.name)))
+ extra.append(pytest_html.extras.url(job_url, "{0} Job".format(provider.name)))
except Exception as e:
- summary.append('WARNING: Failed to determine {0} job URL: {1}'.format(
- provider.name, e))
+ summary.append(
+ "WARNING: Failed to determine {0} job URL: {1}".format(provider.name, e)
+ )
try:
# Update the job result
- job_status = job_info['automation_session']['status']
- status = 'running' if passed else 'error'
- if report.when == 'teardown' and passed:
- status = 'completed'
- if job_status not in ('error', status):
+ job_status = job_info["automation_session"]["status"]
+ status = "running" if passed else "error"
+ if report.when == "teardown" and passed:
+ status = "completed"
+ if job_status not in ("error", status):
# Only update the result if it's not already marked as failed
requests.put(
api_url,
- headers={'Content-Type': 'application/json'},
- params={'status': status},
+ headers={"Content-Type": "application/json"},
+ params={"status": status},
auth=provider.auth,
- timeout=10)
+ timeout=10,
+ )
except Exception as e:
- summary.append('WARNING: Failed to update job status: {0}'.format(e))
+ summary.append("WARNING: Failed to update job status: {0}".format(e))
def driver_kwargs(request, test, capabilities, **kwargs):
provider = BrowserStack()
- capabilities.setdefault('name', test)
+ capabilities.setdefault("name", test)
kwargs = {
- 'command_executor': provider.executor,
- 'desired_capabilities': capabilities}
+ "command_executor": provider.executor,
+ "desired_capabilities": capabilities,
+ }
return kwargs
diff --git a/pytest_selenium/drivers/chrome.py b/pytest_selenium/drivers/chrome.py
index 5287e269..1ffbe093 100644
--- a/pytest_selenium/drivers/chrome.py
+++ b/pytest_selenium/drivers/chrome.py
@@ -8,21 +8,21 @@
from selenium.webdriver.chrome.options import Options
-def driver_kwargs(capabilities, driver_args, driver_log, driver_path,
- chrome_options, **kwargs):
- kwargs = {'desired_capabilities': capabilities,
- 'service_log_path': driver_log}
+def driver_kwargs(
+ capabilities, driver_args, driver_log, driver_path, chrome_options, **kwargs
+):
+ kwargs = {"desired_capabilities": capabilities, "service_log_path": driver_log}
# Selenium 3.8.0 deprecated chrome_options in favour of options
- if LooseVersion(SELENIUM_VERSION) < LooseVersion('3.8.0'):
- kwargs['chrome_options'] = chrome_options
+ if LooseVersion(SELENIUM_VERSION) < LooseVersion("3.8.0"):
+ kwargs["chrome_options"] = chrome_options
else:
- kwargs['options'] = chrome_options
+ kwargs["options"] = chrome_options
if driver_args is not None:
- kwargs['service_args'] = driver_args
+ kwargs["service_args"] = driver_args
if driver_path is not None:
- kwargs['executable_path'] = driver_path
+ kwargs["executable_path"] = driver_path
return kwargs
diff --git a/pytest_selenium/drivers/cloud.py b/pytest_selenium/drivers/cloud.py
index 47f533e3..aca7fc6c 100644
--- a/pytest_selenium/drivers/cloud.py
+++ b/pytest_selenium/drivers/cloud.py
@@ -15,24 +15,21 @@
class Provider(object):
-
@property
def name(self):
return type(self).__name__
@property
def config(self):
- name = '.{0}'.format(self.name.lower())
+ name = ".{0}".format(self.name.lower())
config = configparser.ConfigParser()
- config.read([name, os.path.join(os.path.expanduser('~'), name)])
+ config.read([name, os.path.join(os.path.expanduser("~"), name)])
return config
def get_credential(self, key, envs):
try:
- return self.config.get('credentials', key)
- except (configparser.NoSectionError,
- configparser.NoOptionError,
- KeyError):
+ return self.config.get("credentials", key)
+ except (configparser.NoSectionError, configparser.NoOptionError, KeyError):
for env in envs:
value = os.getenv(env)
if value:
diff --git a/pytest_selenium/drivers/crossbrowsertesting.py b/pytest_selenium/drivers/crossbrowsertesting.py
index 4943bf79..36633774 100644
--- a/pytest_selenium/drivers/crossbrowsertesting.py
+++ b/pytest_selenium/drivers/crossbrowsertesting.py
@@ -11,7 +11,7 @@
class CrossBrowserTesting(Provider):
- API = 'https://crossbrowsertesting.com/api/v3/selenium/{session}'
+ API = "https://crossbrowsertesting.com/api/v3/selenium/{session}"
@property
def auth(self):
@@ -19,91 +19,102 @@ def auth(self):
@property
def executor(self):
- return 'http://{0}:{1}@hub.crossbrowsertesting.com:80/wd/hub'.format(
- self.username, self.key)
+ return "http://{0}:{1}@hub.crossbrowsertesting.com:80/wd/hub".format(
+ self.username, self.key
+ )
@property
def username(self):
- return self.get_credential('username', ['CROSSBROWSERTESTING_USERNAME',
- 'CROSSBROWSERTESTING_USR'])
+ return self.get_credential(
+ "username", ["CROSSBROWSERTESTING_USERNAME", "CROSSBROWSERTESTING_USR"]
+ )
@property
def key(self):
- return self.get_credential('key', ['CROSSBROWSERTESTING_AUTH_KEY',
- 'CROSSBROWSERTESTING_PSW'])
+ return self.get_credential(
+ "key", ["CROSSBROWSERTESTING_AUTH_KEY", "CROSSBROWSERTESTING_PSW"]
+ )
@pytest.mark.optionalhook
def pytest_selenium_capture_debug(item, report, extra):
provider = CrossBrowserTesting()
- if not provider.uses_driver(item.config.getoption('driver')):
+ if not provider.uses_driver(item.config.getoption("driver")):
return
- videos = requests.get(
- provider.API.format(session=item._driver.session_id),
- auth=provider.auth,
- timeout=10).json().get('videos')
+ videos = (
+ requests.get(
+ provider.API.format(session=item._driver.session_id),
+ auth=provider.auth,
+ timeout=10,
+ )
+ .json()
+ .get("videos")
+ )
if videos and len(videos) > 0:
- pytest_html = item.config.pluginmanager.getplugin('html')
+ pytest_html = item.config.pluginmanager.getplugin("html")
extra.append(pytest_html.extras.html(_video_html(videos[0])))
@pytest.mark.optionalhook
def pytest_selenium_runtest_makereport(item, report, summary, extra):
provider = CrossBrowserTesting()
- if not provider.uses_driver(item.config.getoption('driver')):
+ if not provider.uses_driver(item.config.getoption("driver")):
return
- passed = report.passed or (report.failed and hasattr(report, 'wasxfail'))
+ passed = report.passed or (report.failed and hasattr(report, "wasxfail"))
# Add the test URL to the summary
info = requests.get(
provider.API.format(session=item._driver.session_id),
auth=provider.auth,
- timeout=10).json()
+ timeout=10,
+ ).json()
- url = info.get('show_result_public_url')
- summary.append('{0}: {1}'.format(provider.name, url))
- pytest_html = item.config.pluginmanager.getplugin('html')
+ url = info.get("show_result_public_url")
+ summary.append("{0}: {1}".format(provider.name, url))
+ pytest_html = item.config.pluginmanager.getplugin("html")
# Add the job URL to the HTML report
extra.append(pytest_html.extras.url(url, provider.name))
try:
# Update the test result
- if report.when == 'setup' or info.get('test_score') is not 'fail':
+ if report.when == "setup" or info.get("test_score") is not "fail":
# Only update the result if it's not already marked as failed
- score = 'pass' if passed else 'fail'
- data = {'action': 'set_score', 'score': score}
+ score = "pass" if passed else "fail"
+ data = {"action": "set_score", "score": score}
r = requests.put(
- provider.API.format(session=info.get('selenium_test_id')),
+ provider.API.format(session=info.get("selenium_test_id")),
data=data,
auth=provider.auth,
- timeout=10)
+ timeout=10,
+ )
r.raise_for_status()
except Exception as e:
- summary.append('WARNING: Failed to update {0} job status: {1}'.format(
- provider.name, e))
+ summary.append(
+ "WARNING: Failed to update {0} job status: {1}".format(provider.name, e)
+ )
def driver_kwargs(request, test, capabilities, **kwargs):
provider = CrossBrowserTesting()
- capabilities.setdefault('name', test)
+ capabilities.setdefault("name", test)
kwargs = {
- 'command_executor': provider.executor,
- 'desired_capabilities': capabilities}
+ "command_executor": provider.executor,
+ "desired_capabilities": capabilities,
+ }
return kwargs
def _video_html(video):
- html.__tagspec__.update(dict([(x, 1) for x in ('video', 'source')]))
+ html.__tagspec__.update(dict([(x, 1) for x in ("video", "source")]))
video_attrs = {
- 'controls': '',
- 'poster': video.get('image'),
- 'play-pause-on-click': '',
- 'style': 'border:1px solid #e6e6e6; float:right; height:240px; '
- 'margin-left:5px; overflow:hidden; width:320px'}
- source_attrs = {'src': video.get('video'), 'type': 'video/mp4'}
- return str(html.video(
- html.source(**source_attrs),
- **video_attrs))
+ "controls": "",
+ "poster": video.get("image"),
+ "play-pause-on-click": "",
+ "style": "border:1px solid #e6e6e6; float:right; height:240px; "
+ "margin-left:5px; overflow:hidden; width:320px",
+ }
+ source_attrs = {"src": video.get("video"), "type": "video/mp4"}
+ return str(html.video(html.source(**source_attrs), **video_attrs))
diff --git a/pytest_selenium/drivers/edge.py b/pytest_selenium/drivers/edge.py
index 071121ad..14b3f27d 100644
--- a/pytest_selenium/drivers/edge.py
+++ b/pytest_selenium/drivers/edge.py
@@ -8,13 +8,13 @@
def driver_kwargs(capabilities, driver_log, driver_path, **kwargs):
# Selenium 3.14.0 deprecated log_path in favour of service_log_path
- if LooseVersion(SELENIUM_VERSION) < LooseVersion('3.14.0'):
- kwargs = {'log_path': driver_log}
+ if LooseVersion(SELENIUM_VERSION) < LooseVersion("3.14.0"):
+ kwargs = {"log_path": driver_log}
else:
- kwargs = {'service_log_path': driver_log}
+ kwargs = {"service_log_path": driver_log}
if capabilities:
- kwargs['capabilities'] = capabilities
+ kwargs["capabilities"] = capabilities
if driver_path is not None:
- kwargs['executable_path'] = driver_path
+ kwargs["executable_path"] = driver_path
return kwargs
diff --git a/pytest_selenium/drivers/firefox.py b/pytest_selenium/drivers/firefox.py
index 6b35ccdc..5475ed10 100644
--- a/pytest_selenium/drivers/firefox.py
+++ b/pytest_selenium/drivers/firefox.py
@@ -3,6 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from distutils.version import LooseVersion
import warnings
+import logging
import pytest
from selenium import __version__ as SELENIUM_VERSION
@@ -10,61 +11,70 @@
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
from selenium.webdriver.firefox.options import Options
+LOGGER = logging.getLogger(__name__)
+
def pytest_addoption(parser):
- group = parser.getgroup('selenium', 'selenium')
- group._addoption('--firefox-path',
- metavar='path',
- help='path to the firefox binary.')
- group._addoption('--firefox-preference',
- action='append',
- default=[],
- dest='firefox_preferences',
- metavar=('name', 'value'),
- nargs=2,
- help='additional firefox preferences.')
- group._addoption('--firefox-profile',
- metavar='path',
- help='path to the firefox profile.')
- group._addoption('--firefox-extension',
- action='append',
- default=[],
- dest='firefox_extensions',
- metavar='path',
- help='path to a firefox extension.')
+ group = parser.getgroup("selenium", "selenium")
+ group._addoption(
+ "--firefox-path", metavar="path", help="path to the firefox binary."
+ )
+ group._addoption(
+ "--firefox-preference",
+ action="append",
+ default=[],
+ dest="firefox_preferences",
+ metavar=("name", "value"),
+ nargs=2,
+ help="additional firefox preferences.",
+ )
+ group._addoption(
+ "--firefox-profile", metavar="path", help="path to the firefox profile."
+ )
+ group._addoption(
+ "--firefox-extension",
+ action="append",
+ default=[],
+ dest="firefox_extensions",
+ metavar="path",
+ help="path to a firefox extension.",
+ )
def pytest_configure(config):
config.addinivalue_line(
- 'markers', "firefox_arguments(args): arguments to be passed to "
+ "markers",
+ "firefox_arguments(args): arguments to be passed to "
"Firefox. This marker will be ignored for other browsers. For "
- "example: firefox_arguments('-foreground')")
+ "example: firefox_arguments('-foreground')",
+ )
config.addinivalue_line(
- 'markers', "firefox_preferences(dict): preferences to be passed to "
+ "markers",
+ "firefox_preferences(dict): preferences to be passed to "
"Firefox. This marker will be ignored for other browsers. For "
"example: firefox_preferences({'browser.startup.homepage': "
- "'https://pytest.org/'})")
+ "'https://pytest.org/'})",
+ )
-def driver_kwargs(capabilities, driver_log, driver_path, firefox_options,
- **kwargs):
+def driver_kwargs(capabilities, driver_log, driver_path, firefox_options, **kwargs):
# Selenium 3.14.0 deprecated log_path in favour of service_log_path
- if LooseVersion(SELENIUM_VERSION) < LooseVersion('3.14.0'):
- kwargs = {'log_path': driver_log}
+ if LooseVersion(SELENIUM_VERSION) < LooseVersion("3.14.0"):
+ kwargs = {"log_path": driver_log}
else:
- kwargs = {'service_log_path': driver_log}
+ kwargs = {"service_log_path": driver_log}
if capabilities:
- kwargs['capabilities'] = capabilities
+ kwargs["capabilities"] = capabilities
if driver_path is not None:
- kwargs['executable_path'] = driver_path
+ kwargs["executable_path"] = driver_path
# Selenium 3.8.0 deprecated firefox_options in favour of options
- if LooseVersion(SELENIUM_VERSION) < LooseVersion('3.8.0'):
- kwargs['firefox_options'] = firefox_options
+ if LooseVersion(SELENIUM_VERSION) < LooseVersion("3.8.0"):
+ kwargs["firefox_options"] = firefox_options
else:
- kwargs['options'] = firefox_options
+ kwargs["options"] = firefox_options
return kwargs
@@ -78,67 +88,100 @@ def firefox_options(request, firefox_path, firefox_profile):
if firefox_path is not None:
options.binary = FirefoxBinary(firefox_path)
- args = request.node.get_marker('firefox_arguments')
- if args is not None:
- for arg in args.args:
- options.add_argument(arg)
+ for arg in get_arguments_from_markers(request.node):
+ options.add_argument(arg)
- prefs = request.node.get_marker('firefox_preferences')
- if prefs is not None:
- for name, value in prefs.args[0].items():
- options.set_preference(name, value)
+ for name, value in get_preferences_from_markers(request.node).items():
+ options.set_preference(name, value)
return options
-@pytest.fixture(scope='session')
+def get_arguments_from_markers(node):
+ # get_marker is deprecated since pytest 3.6
+ # https://docs.pytest.org/en/latest/mark.html#marker-revamp-and-iteration
+ try:
+ arguments = []
+ [arguments.extend(m.args) for m in node.iter_markers("firefox_arguments")]
+ return arguments
+ except AttributeError:
+ arguments = node.get_marker("firefox_arguments")
+ # backwards-compat
+ # can be removed when minimum req pytest is 3.6
+ return arguments.args if arguments else []
+
+
+def get_preferences_from_markers(node):
+ # get_marker is deprecated since pytest 3.6
+ # https://docs.pytest.org/en/latest/mark.html#marker-revamp-and-iteration
+ try:
+ preferences = dict()
+ for mark in node.iter_markers("firefox_preferences"):
+ preferences.update(mark.args[0])
+ return preferences
+ except AttributeError:
+ # backwards-compat
+ # can be removed when minimum req pytest is 3.6
+ preferences = node.get_marker("firefox_preferences")
+ return preferences.args[0] if preferences else {}
+
+
+@pytest.fixture(scope="session")
def firefox_path(pytestconfig):
- if pytestconfig.getoption('firefox_path'):
+ if pytestconfig.getoption("firefox_path"):
warnings.warn(
- '--firefox-path has been deprecated and will be removed in a '
- 'future release. Please make sure the Firefox binary is in the '
- 'default location, or the system path. If you want to specify a '
- 'binary path then use the firefox_options fixture to and set this '
- 'using firefox_options.binary.', DeprecationWarning)
- return pytestconfig.getoption('firefox_path')
+ "--firefox-path has been deprecated and will be removed in a "
+ "future release. Please make sure the Firefox binary is in the "
+ "default location, or the system path. If you want to specify a "
+ "binary path then use the firefox_options fixture to and set this "
+ "using firefox_options.binary.",
+ DeprecationWarning,
+ )
+ return pytestconfig.getoption("firefox_path")
@pytest.fixture
def firefox_profile(pytestconfig):
profile = None
- if pytestconfig.getoption('firefox_profile'):
- profile = FirefoxProfile(pytestconfig.getoption('firefox_profile'))
+ if pytestconfig.getoption("firefox_profile"):
+ profile = FirefoxProfile(pytestconfig.getoption("firefox_profile"))
warnings.warn(
- '--firefox-profile has been deprecated and will be removed in '
- 'a future release. Please use the firefox_options fixture to '
- 'set a profile path or FirefoxProfile object using '
- 'firefox_options.profile.', DeprecationWarning)
- if pytestconfig.getoption('firefox_preferences'):
+ "--firefox-profile has been deprecated and will be removed in "
+ "a future release. Please use the firefox_options fixture to "
+ "set a profile path or FirefoxProfile object using "
+ "firefox_options.profile.",
+ DeprecationWarning,
+ )
+ if pytestconfig.getoption("firefox_preferences"):
profile = profile or FirefoxProfile()
warnings.warn(
- '--firefox-preference has been deprecated and will be removed in '
- 'a future release. Please use the firefox_options fixture to set '
- 'preferences using firefox_options.set_preference. If you are '
- 'using Firefox 47 or earlier then you will need to create a '
- 'FirefoxProfile object with preferences and set this using '
- 'firefox_options.profile.', DeprecationWarning)
- for preference in pytestconfig.getoption('firefox_preferences'):
+ "--firefox-preference has been deprecated and will be removed in "
+ "a future release. Please use the firefox_options fixture to set "
+ "preferences using firefox_options.set_preference. If you are "
+ "using Firefox 47 or earlier then you will need to create a "
+ "FirefoxProfile object with preferences and set this using "
+ "firefox_options.profile.",
+ DeprecationWarning,
+ )
+ for preference in pytestconfig.getoption("firefox_preferences"):
name, value = preference
if value.isdigit():
# handle integer preferences
value = int(value)
- elif value.lower() in ['true', 'false']:
+ elif value.lower() in ["true", "false"]:
# handle boolean preferences
- value = value.lower() == 'true'
+ value = value.lower() == "true"
profile.set_preference(name, value)
profile.update_preferences()
- if pytestconfig.getoption('firefox_extensions'):
+ if pytestconfig.getoption("firefox_extensions"):
profile = profile or FirefoxProfile()
warnings.warn(
- '--firefox-extensions has been deprecated and will be removed in '
- 'a future release. Please use the firefox_options fixture to '
- 'create a FirefoxProfile object with extensions and set this '
- 'using firefox_options.profile.', DeprecationWarning)
- for extension in pytestconfig.getoption('firefox_extensions'):
+ "--firefox-extensions has been deprecated and will be removed in "
+ "a future release. Please use the firefox_options fixture to "
+ "create a FirefoxProfile object with extensions and set this "
+ "using firefox_options.profile.",
+ DeprecationWarning,
+ )
+ for extension in pytestconfig.getoption("firefox_extensions"):
profile.add_extension(extension)
return profile
diff --git a/pytest_selenium/drivers/internet_explorer.py b/pytest_selenium/drivers/internet_explorer.py
index 4b627870..adc23790 100644
--- a/pytest_selenium/drivers/internet_explorer.py
+++ b/pytest_selenium/drivers/internet_explorer.py
@@ -8,13 +8,13 @@
def driver_kwargs(capabilities, driver_log, driver_path, **kwargs):
# Selenium 3.14.0 deprecated log_file in favour of service_log_path
- if LooseVersion(SELENIUM_VERSION) < LooseVersion('3.14.0'):
- kwargs = {'log_file': driver_log}
+ if LooseVersion(SELENIUM_VERSION) < LooseVersion("3.14.0"):
+ kwargs = {"log_file": driver_log}
else:
- kwargs = {'service_log_path': driver_log}
+ kwargs = {"service_log_path": driver_log}
if capabilities:
- kwargs['capabilities'] = capabilities
+ kwargs["capabilities"] = capabilities
if driver_path is not None:
- kwargs['executable_path'] = driver_path
+ kwargs["executable_path"] = driver_path
return kwargs
diff --git a/pytest_selenium/drivers/phantomjs.py b/pytest_selenium/drivers/phantomjs.py
index 2bf51ca3..08c752ea 100644
--- a/pytest_selenium/drivers/phantomjs.py
+++ b/pytest_selenium/drivers/phantomjs.py
@@ -3,12 +3,10 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-def driver_kwargs(capabilities, driver_args, driver_log, driver_path,
- **kwargs):
- kwargs = {'desired_capabilities': capabilities,
- 'service_log_path': driver_log}
+def driver_kwargs(capabilities, driver_args, driver_log, driver_path, **kwargs):
+ kwargs = {"desired_capabilities": capabilities, "service_log_path": driver_log}
if driver_args is not None:
- kwargs['service_args'] = driver_args
+ kwargs["service_args"] = driver_args
if driver_path is not None:
- kwargs['executable_path'] = driver_path
+ kwargs["executable_path"] = driver_path
return kwargs
diff --git a/pytest_selenium/drivers/remote.py b/pytest_selenium/drivers/remote.py
index dd40245a..78179eb9 100644
--- a/pytest_selenium/drivers/remote.py
+++ b/pytest_selenium/drivers/remote.py
@@ -4,15 +4,16 @@
import os
-HOST = os.environ.get('SELENIUM_HOST', 'localhost')
-PORT = os.environ.get('SELENIUM_PORT', 4444)
+HOST = os.environ.get("SELENIUM_HOST", "localhost")
+PORT = os.environ.get("SELENIUM_PORT", 4444)
def driver_kwargs(capabilities, firefox_profile, host, port, **kwargs):
- executor = 'http://{0}:{1}/wd/hub'.format(host, port)
+ executor = "http://{0}:{1}/wd/hub".format(host, port)
kwargs = {
- 'command_executor': executor,
- 'desired_capabilities': capabilities,
- 'browser_profile': firefox_profile}
+ "command_executor": executor,
+ "desired_capabilities": capabilities,
+ "browser_profile": firefox_profile,
+ }
return kwargs
diff --git a/pytest_selenium/drivers/safari.py b/pytest_selenium/drivers/safari.py
index 6ac3d764..2b96653e 100644
--- a/pytest_selenium/drivers/safari.py
+++ b/pytest_selenium/drivers/safari.py
@@ -4,7 +4,7 @@
def driver_kwargs(capabilities, driver_path, **kwargs):
- kwargs = {'desired_capabilities': capabilities}
+ kwargs = {"desired_capabilities": capabilities}
if driver_path is not None:
- kwargs['executable_path'] = driver_path
+ kwargs["executable_path"] = driver_path
return kwargs
diff --git a/pytest_selenium/drivers/saucelabs.py b/pytest_selenium/drivers/saucelabs.py
index 6fa16082..90d31ea2 100644
--- a/pytest_selenium/drivers/saucelabs.py
+++ b/pytest_selenium/drivers/saucelabs.py
@@ -15,8 +15,8 @@
class SauceLabs(Provider):
- API = 'https://saucelabs.com/rest/v1/{username}/jobs/{session}'
- JOB = 'https://saucelabs.com/jobs/{session}'
+ API = "https://saucelabs.com/rest/v1/{username}/jobs/{session}"
+ JOB = "https://saucelabs.com/jobs/{session}"
@property
def auth(self):
@@ -24,19 +24,19 @@ def auth(self):
@property
def executor(self):
- return 'https://ondemand.saucelabs.com/wd/hub'
+ return "https://ondemand.saucelabs.com/wd/hub"
@property
def username(self):
- return self.get_credential('username', ['SAUCELABS_USERNAME',
- 'SAUCELABS_USR',
- 'SAUCE_USERNAME'])
+ return self.get_credential(
+ "username", ["SAUCELABS_USERNAME", "SAUCELABS_USR", "SAUCE_USERNAME"]
+ )
@property
def key(self):
- return self.get_credential('key', ['SAUCELABS_API_KEY',
- 'SAUCELABS_PSW',
- 'SAUCE_ACCESS_KEY'])
+ return self.get_credential(
+ "key", ["SAUCELABS_API_KEY", "SAUCELABS_PSW", "SAUCE_ACCESS_KEY"]
+ )
def uses_driver(self, driver):
return driver.lower() == self.name.lower()
@@ -45,44 +45,42 @@ def uses_driver(self, driver):
@pytest.mark.optionalhook
def pytest_selenium_capture_debug(item, report, extra):
provider = SauceLabs()
- if not provider.uses_driver(item.config.getoption('driver')):
+ if not provider.uses_driver(item.config.getoption("driver")):
return
- pytest_html = item.config.pluginmanager.getplugin('html')
+ pytest_html = item.config.pluginmanager.getplugin("html")
extra.append(pytest_html.extras.html(_video_html(item._driver.session_id)))
@pytest.mark.optionalhook
def pytest_selenium_runtest_makereport(item, report, summary, extra):
provider = SauceLabs()
- if not provider.uses_driver(item.config.getoption('driver')):
+ if not provider.uses_driver(item.config.getoption("driver")):
return
- passed = report.passed or (report.failed and hasattr(report, 'wasxfail'))
+ passed = report.passed or (report.failed and hasattr(report, "wasxfail"))
session_id = item._driver.session_id
# Add the job URL to the summary
provider = SauceLabs()
job_url = get_job_url(item.config, provider, session_id)
- summary.append('{0} Job: {1}'.format(provider.name, job_url))
- pytest_html = item.config.pluginmanager.getplugin('html')
+ summary.append("{0} Job: {1}".format(provider.name, job_url))
+ pytest_html = item.config.pluginmanager.getplugin("html")
# Add the job URL to the HTML report
- extra.append(pytest_html.extras.url(job_url, '{0} Job'.format(
- provider.name)))
+ extra.append(pytest_html.extras.url(job_url, "{0} Job".format(provider.name)))
try:
# Update the job result
- api_url = provider.API.format(
- session=session_id,
- username=provider.username)
+ api_url = provider.API.format(session=session_id, username=provider.username)
job_info = requests.get(api_url, auth=provider.auth, timeout=10).json()
- if report.when == 'setup' or job_info.get('passed') is not False:
+ if report.when == "setup" or job_info.get("passed") is not False:
# Only update the result if it's not already marked as failed
- data = json.dumps({'passed': passed})
+ data = json.dumps({"passed": passed})
requests.put(api_url, data=data, auth=provider.auth, timeout=10)
except Exception as e:
- summary.append('WARNING: Failed to update {0} job status: {1}'.format(
- provider.name, e))
+ summary.append(
+ "WARNING: Failed to update {0} job status: {1}".format(provider.name, e)
+ )
def driver_kwargs(request, test, capabilities, **kwargs):
@@ -91,19 +89,20 @@ def driver_kwargs(request, test, capabilities, **kwargs):
markers = [m for m in keywords.keys() if isinstance(keywords[m], MarkInfo)]
_capabilities = capabilities
- if os.getenv('SAUCELABS_W3C') == 'true':
- _capabilities = capabilities.setdefault('sauce:options', {})
+ if os.getenv("SAUCELABS_W3C") == "true":
+ _capabilities = capabilities.setdefault("sauce:options", {})
- _capabilities.setdefault('username', provider.username)
- _capabilities.setdefault('accessKey', provider.key)
- _capabilities.setdefault('name', test)
- tags = _capabilities.get('tags', []) + markers
+ _capabilities.setdefault("username", provider.username)
+ _capabilities.setdefault("accessKey", provider.key)
+ _capabilities.setdefault("name", test)
+ tags = _capabilities.get("tags", []) + markers
if tags:
- _capabilities['tags'] = tags
+ _capabilities["tags"] = tags
kwargs = {
- 'command_executor': provider.executor,
- 'desired_capabilities': capabilities}
+ "command_executor": provider.executor,
+ "desired_capabilities": capabilities,
+ }
return kwargs
@@ -132,43 +131,48 @@ def _video_html(session):
"url":"https://assets.saucelabs.com/jobs/{session}/video.flv",\
"provider":"streamer",\
"autoPlay":false,\
- "autoBuffering":true}}]}}'.format(session=session)
-
- return str(html.div(html.object(
- html.param(value='true', name='allowfullscreen'),
- html.param(value='always', name='allowscriptaccess'),
- html.param(value='high', name='quality'),
- html.param(value='#000000', name='bgcolor'),
- html.param(
- value=flash_vars.replace(' ', ''),
- name='flashvars'),
- width='100%',
- height='100%',
- type='application/x-shockwave-flash',
- data='https://cdn1.saucelabs.com/sauce_skin_deprecated/lib/'
- 'flowplayer/flowplayer-3.2.17.swf',
- name='player_api',
- id='player_api'),
- id='player{session}'.format(session=session),
- style='border:1px solid #e6e6e6; float:right; height:240px;'
- 'margin-left:5px; overflow:hidden; width:320px'))
+ "autoBuffering":true}}]}}'.format(
+ session=session
+ )
+
+ return str(
+ html.div(
+ html.object(
+ html.param(value="true", name="allowfullscreen"),
+ html.param(value="always", name="allowscriptaccess"),
+ html.param(value="high", name="quality"),
+ html.param(value="#000000", name="bgcolor"),
+ html.param(value=flash_vars.replace(" ", ""), name="flashvars"),
+ width="100%",
+ height="100%",
+ type="application/x-shockwave-flash",
+ data="https://cdn1.saucelabs.com/sauce_skin_deprecated/lib/"
+ "flowplayer/flowplayer-3.2.17.swf",
+ name="player_api",
+ id="player_api",
+ ),
+ id="player{session}".format(session=session),
+ style="border:1px solid #e6e6e6; float:right; height:240px;"
+ "margin-left:5px; overflow:hidden; width:320px",
+ )
+ )
def get_job_url(config, provider, session_id):
from datetime import datetime
job_url = provider.JOB.format(session=session_id)
- job_auth = config.getini('saucelabs_job_auth').lower()
+ job_auth = config.getini("saucelabs_job_auth").lower()
- if job_auth == 'none':
+ if job_auth == "none":
return job_url
- if job_auth == 'token':
+ if job_auth == "token":
return get_auth_url(job_url, provider, session_id)
- elif job_auth == 'hour':
- time_format = '%Y-%m-%d-%H'
- elif job_auth == 'day':
- time_format = '%Y-%m-%d'
+ elif job_auth == "hour":
+ time_format = "%Y-%m-%d-%H"
+ elif job_auth == "day":
+ time_format = "%Y-%m-%d"
else:
raise ValueError("Invalid authorization type: {}".format(job_auth))
@@ -180,10 +184,8 @@ def get_auth_url(url, provider, session_id, ttl=None):
import hmac
from hashlib import md5
- key = '{0.username}:{0.key}'.format(provider)
+ key = "{0.username}:{0.key}".format(provider)
if ttl:
- key += ':{}'.format(ttl)
- token = hmac.new(key.encode('utf-8'),
- session_id.encode('utf-8'),
- md5).hexdigest()
- return '{}?auth={}'.format(url, token)
+ key += ":{}".format(ttl)
+ token = hmac.new(key.encode("utf-8"), session_id.encode("utf-8"), md5).hexdigest()
+ return "{}?auth={}".format(url, token)
diff --git a/pytest_selenium/drivers/testingbot.py b/pytest_selenium/drivers/testingbot.py
index d510d200..5195bf0c 100644
--- a/pytest_selenium/drivers/testingbot.py
+++ b/pytest_selenium/drivers/testingbot.py
@@ -9,14 +9,14 @@
from pytest_selenium.drivers.cloud import Provider
-HOST = 'hub.testingbot.com'
+HOST = "hub.testingbot.com"
PORT = 80
class TestingBot(Provider):
- API = 'https://api.testingbot.com/v1/tests/{session}'
- JOB = 'http://testingbot.com/members/tests/{session}'
+ API = "https://api.testingbot.com/v1/tests/{session}"
+ JOB = "http://testingbot.com/members/tests/{session}"
def __init__(self, host=None, port=None):
super(TestingBot, self).__init__()
@@ -29,71 +29,69 @@ def auth(self):
@property
def executor(self):
- return 'http://{0.key}:{0.secret}@{0.host}:{0.port}/wd/hub'.format(
- self)
+ return "http://{0.key}:{0.secret}@{0.host}:{0.port}/wd/hub".format(self)
@property
def key(self):
- return self.get_credential('key', ['TESTINGBOT_KEY',
- 'TESTINGBOT_USR'])
+ return self.get_credential("key", ["TESTINGBOT_KEY", "TESTINGBOT_USR"])
@property
def secret(self):
- return self.get_credential('secret', ['TESTINGBOT_SECRET',
- 'TESTINGBOT_PSW'])
+ return self.get_credential("secret", ["TESTINGBOT_SECRET", "TESTINGBOT_PSW"])
@pytest.mark.optionalhook
def pytest_selenium_capture_debug(item, report, extra):
provider = TestingBot()
- if not provider.uses_driver(item.config.getoption('driver')):
+ if not provider.uses_driver(item.config.getoption("driver")):
return
- pytest_html = item.config.pluginmanager.getplugin('html')
+ pytest_html = item.config.pluginmanager.getplugin("html")
extra.append(pytest_html.extras.html(_video_html(item._driver.session_id)))
@pytest.mark.optionalhook
def pytest_selenium_runtest_makereport(item, report, summary, extra):
provider = TestingBot()
- if not provider.uses_driver(item.config.getoption('driver')):
+ if not provider.uses_driver(item.config.getoption("driver")):
return
- passed = report.passed or (report.failed and hasattr(report, 'wasxfail'))
+ passed = report.passed or (report.failed and hasattr(report, "wasxfail"))
session_id = item._driver.session_id
# Add the job URL to the summary
job_url = provider.JOB.format(session=session_id)
- summary.append('{0} Job: {1}'.format(provider.name, job_url))
- pytest_html = item.config.pluginmanager.getplugin('html')
+ summary.append("{0} Job: {1}".format(provider.name, job_url))
+ pytest_html = item.config.pluginmanager.getplugin("html")
# Add the job URL to the HTML report
- extra.append(pytest_html.extras.url(job_url, '{0} Job'.format(
- provider.name)))
+ extra.append(pytest_html.extras.url(job_url, "{0} Job".format(provider.name)))
try:
# Update the job result
api_url = provider.API.format(session=session_id)
job_info = requests.get(api_url, auth=provider.auth, timeout=10).json()
- if report.when == 'setup' or job_info.get('success') is not False:
+ if report.when == "setup" or job_info.get("success") is not False:
# Only update the result if it's not already marked as failed
- data = {'test[success]': '1' if passed else '0'}
+ data = {"test[success]": "1" if passed else "0"}
requests.put(api_url, data=data, auth=provider.auth, timeout=10)
except Exception as e:
- summary.append('WARNING: Failed to update {0} job status: {1}'.format(
- provider.name, e))
+ summary.append(
+ "WARNING: Failed to update {0} job status: {1}".format(provider.name, e)
+ )
def driver_kwargs(request, test, capabilities, host, port, **kwargs):
provider = TestingBot(host, port)
keywords = request.node.keywords
- capabilities.setdefault('name', test)
+ capabilities.setdefault("name", test)
markers = [m for m in keywords.keys() if isinstance(keywords[m], MarkInfo)]
- groups = capabilities.get('groups', []) + markers
+ groups = capabilities.get("groups", []) + markers
if groups:
- capabilities['groups'] = groups
+ capabilities["groups"] = groups
kwargs = {
- 'command_executor': provider.executor,
- 'desired_capabilities': capabilities}
+ "command_executor": provider.executor,
+ "desired_capabilities": capabilities,
+ }
return kwargs
@@ -116,23 +114,28 @@ def _video_html(session):
"playerId":"mediaplayer{session}",\
"playlist":[{{\
"url":"{session}",\
- "provider":"rtmp"}}]}}'.format(session=session)
-
- return str(html.div(html.object(
- html.param(value='true', name='allowfullscreen'),
- html.param(value='always', name='allowscriptaccess'),
- html.param(value='high', name='quality'),
- html.param(value='#000000', name='bgcolor'),
- html.param(value='opaque', name='wmode'),
- html.param(
- value=flash_vars.replace(' ', ''),
- name='flashvars'),
- width='100%',
- height='100%',
- type='application/x-shockwave-flash',
- data='http://testingbot.com/assets/flowplayer-3.2.14.swf',
- name='mediaplayer_api',
- id='mediaplayer_api'),
- id='mediaplayer{session}'.format(session=session),
- style='border:1px solid #e6e6e6; float:right; height:240px;'
- 'margin-left:5px; overflow:hidden; width:320px'))
+ "provider":"rtmp"}}]}}'.format(
+ session=session
+ )
+
+ return str(
+ html.div(
+ html.object(
+ html.param(value="true", name="allowfullscreen"),
+ html.param(value="always", name="allowscriptaccess"),
+ html.param(value="high", name="quality"),
+ html.param(value="#000000", name="bgcolor"),
+ html.param(value="opaque", name="wmode"),
+ html.param(value=flash_vars.replace(" ", ""), name="flashvars"),
+ width="100%",
+ height="100%",
+ type="application/x-shockwave-flash",
+ data="http://testingbot.com/assets/flowplayer-3.2.14.swf",
+ name="mediaplayer_api",
+ id="mediaplayer_api",
+ ),
+ id="mediaplayer{session}".format(session=session),
+ style="border:1px solid #e6e6e6; float:right; height:240px;"
+ "margin-left:5px; overflow:hidden; width:320px",
+ )
+ )
diff --git a/pytest_selenium/exceptions.py b/pytest_selenium/exceptions.py
index 77836c32..db853770 100644
--- a/pytest_selenium/exceptions.py
+++ b/pytest_selenium/exceptions.py
@@ -6,9 +6,9 @@
class MissingCloudCredentialError(pytest.UsageError):
-
def __init__(self, driver, key, envs):
super(MissingCloudCredentialError, self).__init__(
- '{0} {1} must be set. Try setting one of the following '
- 'environment variables {2}, or see the documentation for '
- 'how to use a configuration file.'.format(driver, key, envs))
+ "{0} {1} must be set. Try setting one of the following "
+ "environment variables {2}, or see the documentation for "
+ "how to use a configuration file.".format(driver, key, envs)
+ )
diff --git a/pytest_selenium/pytest_selenium.py b/pytest_selenium/pytest_selenium.py
index 4074881b..fd8bae6d 100644
--- a/pytest_selenium/pytest_selenium.py
+++ b/pytest_selenium/pytest_selenium.py
@@ -13,26 +13,28 @@
from requests.structures import CaseInsensitiveDict
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
-from selenium.webdriver.support.event_firing_webdriver import \
- EventFiringWebDriver
+from selenium.webdriver.support.event_firing_webdriver import EventFiringWebDriver
from . import drivers
LOGGER = logging.getLogger(__name__)
-SUPPORTED_DRIVERS = CaseInsensitiveDict({
- 'BrowserStack': webdriver.Remote,
- 'CrossBrowserTesting': webdriver.Remote,
- 'Chrome': webdriver.Chrome,
- 'Edge': webdriver.Edge,
- 'Firefox': webdriver.Firefox,
- 'IE': webdriver.Ie,
- 'PhantomJS': webdriver.PhantomJS,
- 'Remote': webdriver.Remote,
- 'Safari': webdriver.Safari,
- 'SauceLabs': webdriver.Remote,
- 'TestingBot': webdriver.Remote})
+SUPPORTED_DRIVERS = CaseInsensitiveDict(
+ {
+ "BrowserStack": webdriver.Remote,
+ "CrossBrowserTesting": webdriver.Remote,
+ "Chrome": webdriver.Chrome,
+ "Edge": webdriver.Edge,
+ "Firefox": webdriver.Firefox,
+ "IE": webdriver.Ie,
+ "PhantomJS": webdriver.PhantomJS,
+ "Remote": webdriver.Remote,
+ "Safari": webdriver.Safari,
+ "SauceLabs": webdriver.Remote,
+ "TestingBot": webdriver.Remote,
+ }
+)
def _merge(a, b):
@@ -60,43 +62,44 @@ def _merge(a, b):
def pytest_addhooks(pluginmanager):
from . import hooks
- method = getattr(pluginmanager, 'add_hookspecs', None)
+
+ method = getattr(pluginmanager, "add_hookspecs", None)
if method is None:
method = pluginmanager.addhooks
method(hooks)
-@pytest.fixture(scope='session')
+@pytest.fixture(scope="session")
def session_capabilities(pytestconfig):
"""Returns combined capabilities from pytest-variables and command line"""
- driver = pytestconfig.getoption('driver').upper()
+ driver = pytestconfig.getoption("driver").upper()
capabilities = getattr(DesiredCapabilities, driver, {}).copy()
- if driver == 'REMOTE':
- browser = capabilities.get('browserName', '').upper()
+ if driver == "REMOTE":
+ browser = capabilities.get("browserName", "").upper()
capabilities.update(getattr(DesiredCapabilities, browser, {}))
capabilities.update(pytestconfig._capabilities)
return capabilities
@pytest.fixture
-def capabilities(request, driver_class, chrome_options, firefox_options,
- session_capabilities):
+def capabilities(
+ request, driver_class, chrome_options, firefox_options, session_capabilities
+):
"""Returns combined capabilities"""
capabilities = copy.deepcopy(session_capabilities) # make a copy
if driver_class == webdriver.Remote:
- browser = capabilities.get('browserName', '').upper()
+ browser = capabilities.get("browserName", "").upper()
key, options = (None, None)
- if browser == 'CHROME':
- key = getattr(chrome_options, 'KEY', 'goog:chromeOptions')
+ if browser == "CHROME":
+ key = getattr(chrome_options, "KEY", "goog:chromeOptions")
options = chrome_options.to_capabilities()
if key not in options:
- key = 'chromeOptions'
- elif browser == 'FIREFOX':
+ key = "chromeOptions"
+ elif browser == "FIREFOX":
key = firefox_options.KEY
options = firefox_options.to_capabilities()
if all([key, options]):
- capabilities[key] = _merge(
- capabilities.get(key, {}), options.get(key, {}))
+ capabilities[key] = _merge(capabilities.get(key, {}), options.get(key, {}))
capabilities.update(get_capabilities_from_markers(request.node))
return capabilities
@@ -106,17 +109,18 @@ def get_capabilities_from_markers(node):
# https://docs.pytest.org/en/latest/mark.html#marker-revamp-and-iteration
try:
capabilities = dict()
- for level, mark in node.iter_markers_with_node('capabilities'):
- LOGGER.debug('{0} marker <{1.name}> '
- 'contained kwargs <{1.kwargs}>'.
- format(level.__class__.__name__, mark))
+ for level, mark in node.iter_markers_with_node("capabilities"):
+ LOGGER.debug(
+ "{0} marker <{1.name}> "
+ "contained kwargs <{1.kwargs}>".format(level.__class__.__name__, mark)
+ )
capabilities.update(mark.kwargs)
- LOGGER.info('Capabilities from markers: {}'.format(capabilities))
+ LOGGER.info("Capabilities from markers: {}".format(capabilities))
return capabilities
except AttributeError:
# backwards-compat
# can be removed when minimum req pytest is 3.6
- capabilities = node.get_marker('capabilities')
+ capabilities = node.get_marker("capabilities")
return capabilities.kwargs if capabilities else {}
@@ -127,45 +131,57 @@ def driver_args():
@pytest.fixture
-def driver_kwargs(request, capabilities, chrome_options, driver_args,
- driver_class, driver_log, driver_path, firefox_options,
- firefox_profile, pytestconfig):
+def driver_kwargs(
+ request,
+ capabilities,
+ chrome_options,
+ driver_args,
+ driver_class,
+ driver_log,
+ driver_path,
+ firefox_options,
+ firefox_profile,
+ pytestconfig,
+):
kwargs = {}
- driver = getattr(drivers, pytestconfig.getoption('driver').lower())
- kwargs.update(driver.driver_kwargs(
- capabilities=capabilities,
- chrome_options=chrome_options,
- driver_args=driver_args,
- driver_log=driver_log,
- driver_path=driver_path,
- firefox_options=firefox_options,
- firefox_profile=firefox_profile,
- host=pytestconfig.getoption('host'),
- port=pytestconfig.getoption('port'),
- service_log_path=None,
- request=request,
- test='.'.join(split_class_and_test_names(request.node.nodeid))))
+ driver = getattr(drivers, pytestconfig.getoption("driver").lower())
+ kwargs.update(
+ driver.driver_kwargs(
+ capabilities=capabilities,
+ chrome_options=chrome_options,
+ driver_args=driver_args,
+ driver_log=driver_log,
+ driver_path=driver_path,
+ firefox_options=firefox_options,
+ firefox_profile=firefox_profile,
+ host=pytestconfig.getoption("host"),
+ port=pytestconfig.getoption("port"),
+ service_log_path=None,
+ request=request,
+ test=".".join(split_class_and_test_names(request.node.nodeid)),
+ )
+ )
pytestconfig._driver_log = driver_log
return kwargs
-@pytest.fixture(scope='session')
+@pytest.fixture(scope="session")
def driver_class(request):
- driver = request.config.getoption('driver')
+ driver = request.config.getoption("driver")
if driver is None:
- raise pytest.UsageError('--driver must be specified')
+ raise pytest.UsageError("--driver must be specified")
return SUPPORTED_DRIVERS[driver]
@pytest.fixture
def driver_log(tmpdir):
"""Return path to driver log"""
- return str(tmpdir.join('driver.log'))
+ return str(tmpdir.join("driver.log"))
@pytest.fixture
def driver_path(request):
- return request.config.getoption('driver_path')
+ return request.config.getoption("driver_path")
@pytest.yield_fixture
@@ -173,10 +189,10 @@ def driver(request, driver_class, driver_kwargs):
"""Returns a WebDriver instance based on options and capabilities"""
driver = driver_class(**driver_kwargs)
- event_listener = request.config.getoption('event_listener')
+ event_listener = request.config.getoption("event_listener")
if event_listener is not None:
# Import the specified event listener and wrap the driver instance
- mod_name, class_name = event_listener.rsplit('.', 1)
+ mod_name, class_name = event_listener.rsplit(".", 1)
mod = __import__(mod_name, fromlist=[class_name])
event_listener = getattr(mod, class_name)
if not isinstance(driver, EventFiringWebDriver):
@@ -194,26 +210,30 @@ def selenium(driver):
@pytest.hookimpl(trylast=True)
def pytest_configure(config):
- capabilities = config._variables.get('capabilities', {})
- capabilities.update({k: v for k, v in config.getoption('capabilities')})
+ capabilities = config._variables.get("capabilities", {})
+ capabilities.update({k: v for k, v in config.getoption("capabilities")})
config.addinivalue_line(
- 'markers', 'capabilities(kwargs): add or change existing '
- 'capabilities. specify capabilities as keyword arguments, for example '
- 'capabilities(foo=''bar'')')
- if hasattr(config, '_metadata'):
- config._metadata['Driver'] = config.getoption('driver')
- config._metadata['Capabilities'] = capabilities
- if all((config.getoption('host'), config.getoption('port'))):
- config._metadata['Server'] = '{0}:{1}'.format(
- config.getoption('host'),
- config.getoption('port'))
+ "markers",
+ "capabilities(kwargs): add or change existing "
+ "capabilities. specify capabilities as keyword arguments, for example "
+ "capabilities(foo="
+ "bar"
+ ")",
+ )
+ if hasattr(config, "_metadata"):
+ config._metadata["Driver"] = config.getoption("driver")
+ config._metadata["Capabilities"] = capabilities
+ if all((config.getoption("host"), config.getoption("port"))):
+ config._metadata["Server"] = "{0}:{1}".format(
+ config.getoption("host"), config.getoption("port")
+ )
config._capabilities = capabilities
def pytest_report_header(config, startdir):
- driver = config.getoption('driver')
+ driver = config.getoption("driver")
if driver is not None:
- return 'driver: {0}'.format(driver)
+ return "driver: {0}".format(driver)
@pytest.mark.hookwrapper
@@ -221,36 +241,38 @@ def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
summary = []
- extra = getattr(report, 'extra', [])
- driver = getattr(item, '_driver', None)
- xfail = hasattr(report, 'wasxfail')
+ extra = getattr(report, "extra", [])
+ driver = getattr(item, "_driver", None)
+ xfail = hasattr(report, "wasxfail")
failure = (report.skipped and xfail) or (report.failed and not xfail)
- when = item.config.getini('selenium_capture_debug').lower()
- capture_debug = when == 'always' or (when == 'failure' and failure)
+ when = item.config.getini("selenium_capture_debug").lower()
+ capture_debug = when == "always" or (when == "failure" and failure)
if capture_debug:
- exclude = item.config.getini('selenium_exclude_debug').lower()
- if 'logs' not in exclude:
+ exclude = item.config.getini("selenium_exclude_debug").lower()
+ if "logs" not in exclude:
# gather logs that do not depend on a driver instance
_gather_driver_log(item, summary, extra)
if driver is not None:
# gather debug that depends on a driver instance
- if 'url' not in exclude:
+ if "url" not in exclude:
_gather_url(item, report, driver, summary, extra)
- if 'screenshot' not in exclude:
+ if "screenshot" not in exclude:
_gather_screenshot(item, report, driver, summary, extra)
- if 'html' not in exclude:
+ if "html" not in exclude:
_gather_html(item, report, driver, summary, extra)
- if 'logs' not in exclude:
+ if "logs" not in exclude:
_gather_logs(item, report, driver, summary, extra)
# gather debug from hook implementations
item.config.hook.pytest_selenium_capture_debug(
- item=item, report=report, extra=extra)
+ item=item, report=report, extra=extra
+ )
if driver is not None:
# allow hook implementations to further modify the report
item.config.hook.pytest_selenium_runtest_makereport(
- item=item, report=report, summary=summary, extra=extra)
+ item=item, report=report, summary=summary, extra=extra
+ )
if summary:
- report.sections.append(('pytest-selenium', '\n'.join(summary)))
+ report.sections.append(("pytest-selenium", "\n".join(summary)))
report.extra = extra
@@ -258,142 +280,161 @@ def _gather_url(item, report, driver, summary, extra):
try:
url = driver.current_url
except Exception as e:
- summary.append('WARNING: Failed to gather URL: {0}'.format(e))
+ summary.append("WARNING: Failed to gather URL: {0}".format(e))
return
- pytest_html = item.config.pluginmanager.getplugin('html')
+ pytest_html = item.config.pluginmanager.getplugin("html")
if pytest_html is not None:
# add url to the html report
extra.append(pytest_html.extras.url(url))
- summary.append('URL: {0}'.format(url))
+ summary.append("URL: {0}".format(url))
def _gather_screenshot(item, report, driver, summary, extra):
try:
screenshot = driver.get_screenshot_as_base64()
except Exception as e:
- summary.append('WARNING: Failed to gather screenshot: {0}'.format(e))
+ summary.append("WARNING: Failed to gather screenshot: {0}".format(e))
return
- pytest_html = item.config.pluginmanager.getplugin('html')
+ pytest_html = item.config.pluginmanager.getplugin("html")
if pytest_html is not None:
# add screenshot to the html report
- extra.append(pytest_html.extras.image(screenshot, 'Screenshot'))
+ extra.append(pytest_html.extras.image(screenshot, "Screenshot"))
def _gather_html(item, report, driver, summary, extra):
try:
html = driver.page_source
except Exception as e:
- summary.append('WARNING: Failed to gather HTML: {0}'.format(e))
+ summary.append("WARNING: Failed to gather HTML: {0}".format(e))
return
- pytest_html = item.config.pluginmanager.getplugin('html')
+ pytest_html = item.config.pluginmanager.getplugin("html")
if pytest_html is not None:
# add page source to the html report
- extra.append(pytest_html.extras.text(html, 'HTML'))
+ extra.append(pytest_html.extras.text(html, "HTML"))
def _gather_logs(item, report, driver, summary, extra):
- pytest_html = item.config.pluginmanager.getplugin('html')
+ pytest_html = item.config.pluginmanager.getplugin("html")
try:
types = driver.log_types
except Exception as e:
# note that some drivers may not implement log types
- summary.append('WARNING: Failed to gather log types: {0}'.format(e))
+ summary.append("WARNING: Failed to gather log types: {0}".format(e))
return
for name in types:
try:
log = driver.get_log(name)
except Exception as e:
- summary.append('WARNING: Failed to gather {0} log: {1}'.format(
- name, e))
+ summary.append("WARNING: Failed to gather {0} log: {1}".format(name, e))
return
if pytest_html is not None:
- extra.append(pytest_html.extras.text(
- format_log(log), '%s Log' % name.title()))
+ extra.append(
+ pytest_html.extras.text(format_log(log), "%s Log" % name.title())
+ )
def _gather_driver_log(item, summary, extra):
- pytest_html = item.config.pluginmanager.getplugin('html')
- if hasattr(item.config, '_driver_log') and \
- os.path.exists(item.config._driver_log):
+ pytest_html = item.config.pluginmanager.getplugin("html")
+ if hasattr(item.config, "_driver_log") and os.path.exists(item.config._driver_log):
if pytest_html is not None:
- with io.open(item.config._driver_log, 'r', encoding='utf8') as f:
- extra.append(pytest_html.extras.text(f.read(), 'Driver Log'))
- summary.append('Driver log: {0}'.format(item.config._driver_log))
+ with io.open(item.config._driver_log, "r", encoding="utf8") as f:
+ extra.append(pytest_html.extras.text(f.read(), "Driver Log"))
+ summary.append("Driver log: {0}".format(item.config._driver_log))
def format_log(log):
- timestamp_format = '%Y-%m-%d %H:%M:%S.%f'
- entries = [u'{0} {1[level]} - {1[message]}'.format(
- datetime.utcfromtimestamp(entry['timestamp'] / 1000.0).strftime(
- timestamp_format), entry).rstrip() for entry in log]
- log = '\n'.join(entries)
+ timestamp_format = "%Y-%m-%d %H:%M:%S.%f"
+ entries = [
+ u"{0} {1[level]} - {1[message]}".format(
+ datetime.utcfromtimestamp(entry["timestamp"] / 1000.0).strftime(
+ timestamp_format
+ ),
+ entry,
+ ).rstrip()
+ for entry in log
+ ]
+ log = "\n".join(entries)
return log
def split_class_and_test_names(nodeid):
"""Returns the class and method name from the current test"""
- names = nodeid.split('::')
- names[0] = names[0].replace('/', '.')
- names = [x.replace('.py', '') for x in names if x != '()']
+ names = nodeid.split("::")
+ names[0] = names[0].replace("/", ".")
+ names = [x.replace(".py", "") for x in names if x != "()"]
classnames = names[:-1]
- classname = '.'.join(classnames)
+ classname = ".".join(classnames)
name = names[-1]
return classname, name
class DriverAction(argparse.Action):
-
def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, values)
driver = getattr(drivers, values.lower())
# set the default host and port if specified in the driver module
- namespace.host = namespace.host or getattr(driver, 'HOST', None)
- namespace.port = namespace.port or getattr(driver, 'PORT', None)
+ namespace.host = namespace.host or getattr(driver, "HOST", None)
+ namespace.port = namespace.port or getattr(driver, "PORT", None)
def pytest_addoption(parser):
- _capture_choices = ('never', 'failure', 'always')
- parser.addini('selenium_capture_debug',
- help='when debug is captured {0}'.format(_capture_choices),
- default=os.getenv('SELENIUM_CAPTURE_DEBUG', 'failure'))
- parser.addini('selenium_exclude_debug',
- help='debug to exclude from capture',
- default=os.getenv('SELENIUM_EXCLUDE_DEBUG'))
-
- _auth_choices = ('none', 'token', 'hour', 'day')
- parser.addini('saucelabs_job_auth',
- help='Authorization options for the Sauce Labs job: {0}'.
- format(_auth_choices),
- default=os.getenv('SAUCELABS_JOB_AUTH', 'none'))
-
- group = parser.getgroup('selenium', 'selenium')
- group._addoption('--driver',
- action=DriverAction,
- choices=SUPPORTED_DRIVERS,
- help='webdriver implementation.',
- metavar='str')
- group._addoption('--driver-path',
- metavar='path',
- help='path to the driver executable.')
- group._addoption('--capability',
- action='append',
- default=[],
- dest='capabilities',
- metavar=('key', 'value'),
- nargs=2,
- help='additional capabilities.')
- group._addoption('--event-listener',
- metavar='str',
- help='selenium eventlistener class, e.g. '
- 'package.module.EventListenerClassName.')
- group._addoption('--host',
- metavar='str',
- help='host that the selenium server is listening on, '
- 'which will default to the cloud provider default '
- 'or localhost.')
- group._addoption('--port',
- type=int,
- metavar='num',
- help='port that the selenium server is listening on, '
- 'which will default to the cloud provider default '
- 'or localhost.')
+ _capture_choices = ("never", "failure", "always")
+ parser.addini(
+ "selenium_capture_debug",
+ help="when debug is captured {0}".format(_capture_choices),
+ default=os.getenv("SELENIUM_CAPTURE_DEBUG", "failure"),
+ )
+ parser.addini(
+ "selenium_exclude_debug",
+ help="debug to exclude from capture",
+ default=os.getenv("SELENIUM_EXCLUDE_DEBUG"),
+ )
+
+ _auth_choices = ("none", "token", "hour", "day")
+ parser.addini(
+ "saucelabs_job_auth",
+ help="Authorization options for the Sauce Labs job: {0}".format(_auth_choices),
+ default=os.getenv("SAUCELABS_JOB_AUTH", "none"),
+ )
+
+ group = parser.getgroup("selenium", "selenium")
+ group._addoption(
+ "--driver",
+ action=DriverAction,
+ choices=SUPPORTED_DRIVERS,
+ help="webdriver implementation.",
+ metavar="str",
+ )
+ group._addoption(
+ "--driver-path", metavar="path", help="path to the driver executable."
+ )
+ group._addoption(
+ "--capability",
+ action="append",
+ default=[],
+ dest="capabilities",
+ metavar=("key", "value"),
+ nargs=2,
+ help="additional capabilities.",
+ )
+ group._addoption(
+ "--event-listener",
+ metavar="str",
+ help="selenium eventlistener class, e.g. "
+ "package.module.EventListenerClassName.",
+ )
+ group._addoption(
+ "--host",
+ metavar="str",
+ help="host that the selenium server is listening on, "
+ "which will default to the cloud provider default "
+ "or localhost.",
+ )
+ group._addoption(
+ "--port",
+ type=int,
+ metavar="num",
+ help="port that the selenium server is listening on, "
+ "which will default to the cloud provider default "
+ "or localhost.",
+ )
diff --git a/pytest_selenium/safety.py b/pytest_selenium/safety.py
index c67c5da8..d4931b61 100644
--- a/pytest_selenium/safety.py
+++ b/pytest_selenium/safety.py
@@ -11,37 +11,44 @@
def pytest_addoption(parser):
- parser.addini('sensitive_url',
- help='regular expression for identifying sensitive urls.')
+ parser.addini(
+ "sensitive_url", help="regular expression for identifying sensitive urls."
+ )
- group = parser.getgroup('safety', 'safety')
- group._addoption('--sensitive-url',
- help='regular expression for identifying sensitive urls.')
+ group = parser.getgroup("safety", "safety")
+ group._addoption(
+ "--sensitive-url", help="regular expression for identifying sensitive urls."
+ )
def pytest_configure(config):
- if hasattr(config, 'slaveinput'):
+ if hasattr(config, "slaveinput"):
return # xdist slave
- config.option.sensitive_url = config.getoption('sensitive_url') or \
- config.getini('sensitive_url') or \
- os.getenv('SENSITIVE_URL') or '.*'
+ config.option.sensitive_url = (
+ config.getoption("sensitive_url")
+ or config.getini("sensitive_url")
+ or os.getenv("SENSITIVE_URL")
+ or ".*"
+ )
config.addinivalue_line(
- 'markers', 'nondestructive: mark the test as nondestructive. '
- 'Tests are assumed to be destructive unless this marker is '
- 'present. This reduces the risk of running destructive tests '
- 'accidentally.')
+ "markers",
+ "nondestructive: mark the test as nondestructive. "
+ "Tests are assumed to be destructive unless this marker is "
+ "present. This reduces the risk of running destructive tests "
+ "accidentally.",
+ )
def pytest_report_header(config, startdir):
- base_url = config.getoption('base_url')
- sensitive_url = config.getoption('sensitive_url')
- msg = 'sensitiveurl: {0}'.format(sensitive_url)
+ base_url = config.getoption("base_url")
+ sensitive_url = config.getoption("sensitive_url")
+ msg = "sensitiveurl: {0}".format(sensitive_url)
if base_url and sensitive_url and re.search(sensitive_url, base_url):
- msg += ' *** WARNING: sensitive url matches {} ***'.format(base_url)
+ msg += " *** WARNING: sensitive url matches {} ***".format(base_url)
return msg
-@pytest.fixture(scope='session')
+@pytest.fixture(scope="session")
def sensitive_url(request, base_url):
"""Return the first sensitive URL from response history of the base URL"""
if not base_url:
@@ -55,7 +62,7 @@ def sensitive_url(request, base_url):
urls.extend([history.url for history in response.history])
except requests.exceptions.RequestException:
pass # ignore exceptions if this URL is unreachable
- search = partial(re.search, request.config.getoption('sensitive_url'))
+ search = partial(re.search, request.config.getoption("sensitive_url"))
matches = list(map(search, urls))
if any(matches):
# return the first match
@@ -63,13 +70,14 @@ def sensitive_url(request, base_url):
return first_match.string
-@pytest.fixture(scope='function', autouse=True)
+@pytest.fixture(scope="function", autouse=True)
def _skip_sensitive(request, sensitive_url):
"""Skip destructive tests if the environment is considered sensitive"""
- destructive = 'nondestructive' not in request.node.keywords
+ destructive = "nondestructive" not in request.node.keywords
if sensitive_url and destructive:
pytest.skip(
- 'This test is destructive and the target URL is '
- 'considered a sensitive environment. If this test is '
- 'not destructive, add the \'nondestructive\' marker to '
- 'it. Sensitive URL: {0}'.format(sensitive_url))
+ "This test is destructive and the target URL is "
+ "considered a sensitive environment. If this test is "
+ "not destructive, add the 'nondestructive' marker to "
+ "it. Sensitive URL: {0}".format(sensitive_url)
+ )
diff --git a/setup.py b/setup.py
index 7af45dac..f5ce580e 100644
--- a/setup.py
+++ b/setup.py
@@ -1,52 +1,57 @@
from setuptools import setup
-setup(name='pytest-selenium',
- use_scm_version=True,
- description='pytest plugin for Selenium',
- long_description=open('README.rst').read(),
- author='Dave Hunt',
- author_email='dhunt@mozilla.com',
- url='https://github.com/pytest-dev/pytest-selenium',
- packages=['pytest_selenium', 'pytest_selenium.drivers'],
- install_requires=[
- 'pytest>=3.0',
- 'pytest-base-url',
- 'pytest-html>=1.14.0',
- 'pytest-variables>=1.5.0',
- 'selenium>=3.0.0',
- 'requests'],
- entry_points={'pytest11': [
- 'selenium = pytest_selenium.pytest_selenium',
- 'selenium_safety = pytest_selenium.safety',
- 'browserstack_driver = pytest_selenium.drivers.browserstack',
- 'crossbrowsertesting_driver = '
- 'pytest_selenium.drivers.crossbrowsertesting',
- 'chrome_driver = pytest_selenium.drivers.chrome',
- 'edge_driver = pytest_selenium.drivers.edge',
- 'firefox_driver = pytest_selenium.drivers.firefox',
- 'ie_driver = pytest_selenium.drivers.internet_explorer',
- 'remote_driver = pytest_selenium.drivers.remote',
- 'phantomjs_driver = pytest_selenium.drivers.phantomjs',
- 'safari_driver = pytest_selenium.drivers.safari',
- 'saucelabs_driver = pytest_selenium.drivers.saucelabs',
- 'testingbot_driver = pytest_selenium.drivers.testingbot']},
- setup_requires=['setuptools_scm'],
- license='Mozilla Public License 2.0 (MPL 2.0)',
- keywords='py.test pytest selenium saucelabs browserstack webqa qa '
- 'mozilla',
- classifiers=[
- 'Development Status :: 5 - Production/Stable',
- 'Framework :: Pytest',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)',
- 'Operating System :: POSIX',
- 'Operating System :: Microsoft :: Windows',
- 'Operating System :: MacOS :: MacOS X',
- 'Topic :: Software Development :: Quality Assurance',
- 'Topic :: Software Development :: Testing',
- 'Topic :: Utilities',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3.6',
- 'Programming Language :: Python :: 3.7',
- ])
+setup(
+ name="pytest-selenium",
+ use_scm_version=True,
+ description="pytest plugin for Selenium",
+ long_description=open("README.rst").read(),
+ author="Dave Hunt",
+ author_email="dhunt@mozilla.com",
+ url="https://github.com/pytest-dev/pytest-selenium",
+ packages=["pytest_selenium", "pytest_selenium.drivers"],
+ install_requires=[
+ "pytest>=3.0",
+ "pytest-base-url",
+ "pytest-html>=1.14.0",
+ "pytest-variables>=1.5.0",
+ "selenium>=3.0.0",
+ "requests",
+ ],
+ entry_points={
+ "pytest11": [
+ "selenium = pytest_selenium.pytest_selenium",
+ "selenium_safety = pytest_selenium.safety",
+ "browserstack_driver = pytest_selenium.drivers.browserstack",
+ "crossbrowsertesting_driver = "
+ "pytest_selenium.drivers.crossbrowsertesting",
+ "chrome_driver = pytest_selenium.drivers.chrome",
+ "edge_driver = pytest_selenium.drivers.edge",
+ "firefox_driver = pytest_selenium.drivers.firefox",
+ "ie_driver = pytest_selenium.drivers.internet_explorer",
+ "remote_driver = pytest_selenium.drivers.remote",
+ "phantomjs_driver = pytest_selenium.drivers.phantomjs",
+ "safari_driver = pytest_selenium.drivers.safari",
+ "saucelabs_driver = pytest_selenium.drivers.saucelabs",
+ "testingbot_driver = pytest_selenium.drivers.testingbot",
+ ]
+ },
+ setup_requires=["setuptools_scm"],
+ license="Mozilla Public License 2.0 (MPL 2.0)",
+ keywords="py.test pytest selenium saucelabs browserstack webqa qa " "mozilla",
+ classifiers=[
+ "Development Status :: 5 - Production/Stable",
+ "Framework :: Pytest",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
+ "Operating System :: POSIX",
+ "Operating System :: Microsoft :: Windows",
+ "Operating System :: MacOS :: MacOS X",
+ "Topic :: Software Development :: Quality Assurance",
+ "Topic :: Software Development :: Testing",
+ "Topic :: Utilities",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3.6",
+ "Programming Language :: Python :: 3.7",
+ ],
+)
diff --git a/testing/conftest.py b/testing/conftest.py
index dd95ad02..2ea0c79c 100644
--- a/testing/conftest.py
+++ b/testing/conftest.py
@@ -6,7 +6,7 @@
import pytest
-pytest_plugins = 'pytester'
+pytest_plugins = "pytester"
def base_url(httpserver):
@@ -15,49 +15,55 @@ def base_url(httpserver):
@pytest.fixture
def httpserver_base_url(httpserver):
- return '--base-url={0}'.format(base_url(httpserver))
+ return "--base-url={0}".format(base_url(httpserver))
@pytest.fixture(autouse=True)
def testdir(request, httpserver_base_url):
item = request.node
- if 'testdir' not in item.funcargnames:
+ if "testdir" not in item.funcargnames:
return
- testdir = request.getfixturevalue('testdir')
+ testdir = request.getfixturevalue("testdir")
- testdir.makepyfile(conftest="""
+ testdir.makepyfile(
+ conftest="""
import pytest
@pytest.fixture
def webtext(base_url, selenium):
selenium.get(base_url)
return selenium.find_element_by_tag_name('h1').text
- """)
+ """
+ )
- testdir.makefile('.cfg', setup="""
+ testdir.makefile(
+ ".cfg",
+ setup="""
[tool:pytest]
filterwarnings =
error::DeprecationWarning
ignore:--firefox-\w+ has been deprecated:DeprecationWarning
- ignore:MarkInfo:DeprecationWarning:pytest_selenium.drivers.firefox:88
- """)
+ """,
+ )
def runpytestqa(*args, **kwargs):
- return testdir.runpytest(httpserver_base_url, '--driver', 'Firefox',
- *args, **kwargs)
+ return testdir.runpytest(
+ httpserver_base_url, "--driver", "Firefox", *args, **kwargs
+ )
testdir.runpytestqa = runpytestqa
def inline_runqa(*args, **kwargs):
- return testdir.inline_run(httpserver_base_url, '--driver', 'Firefox',
- *args, **kwargs)
+ return testdir.inline_run(
+ httpserver_base_url, "--driver", "Firefox", *args, **kwargs
+ )
testdir.inline_runqa = inline_runqa
def quick_qa(*args, **kwargs):
reprec = inline_runqa(*args)
outcomes = reprec.listoutcomes()
- names = ('passed', 'skipped', 'failed')
+ names = ("passed", "skipped", "failed")
for name, val in zip(names, outcomes):
wantlen = kwargs.get(name)
if wantlen is not None:
diff --git a/testing/test_browserstack.py b/testing/test_browserstack.py
index dd0a4d41..7d68de20 100644
--- a/testing/test_browserstack.py
+++ b/testing/test_browserstack.py
@@ -12,11 +12,13 @@
@pytest.fixture
def testfile(testdir):
- return testdir.makepyfile("""
+ return testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(selenium): pass
- """)
+ """
+ )
def failure_with_output(testdir, *args, **kwargs):
@@ -29,43 +31,52 @@ def failure_with_output(testdir, *args, **kwargs):
@pytest.fixture
def failure(testdir, testfile, httpserver_base_url):
- return partial(failure_with_output, testdir, testfile, httpserver_base_url,
- '--driver', 'BrowserStack')
+ return partial(
+ failure_with_output,
+ testdir,
+ testfile,
+ httpserver_base_url,
+ "--driver",
+ "BrowserStack",
+ )
def test_missing_username(failure, monkeypatch, tmpdir):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- assert 'BrowserStack username must be set' in failure()
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ assert "BrowserStack username must be set" in failure()
def test_missing_access_key_env(failure, monkeypatch, tmpdir):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- monkeypatch.setenv('BROWSERSTACK_USERNAME', 'foo')
- assert 'BrowserStack key must be set' in failure()
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ monkeypatch.setenv("BROWSERSTACK_USERNAME", "foo")
+ assert "BrowserStack key must be set" in failure()
def test_missing_access_key_file(failure, monkeypatch, tmpdir):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- tmpdir.join('.browserstack').write('[credentials]\nusername=foo')
- assert 'BrowserStack key must be set' in failure()
-
-
-@pytest.mark.parametrize(('username', 'key'), [('BROWSERSTACK_USERNAME',
- 'BROWSERSTACK_ACCESS_KEY'),
- ('BROWSERSTACK_USR',
- 'BROWSERSTACK_PSW')])
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ tmpdir.join(".browserstack").write("[credentials]\nusername=foo")
+ assert "BrowserStack key must be set" in failure()
+
+
+@pytest.mark.parametrize(
+ ("username", "key"),
+ [
+ ("BROWSERSTACK_USERNAME", "BROWSERSTACK_ACCESS_KEY"),
+ ("BROWSERSTACK_USR", "BROWSERSTACK_PSW"),
+ ],
+)
def test_invalid_credentials_env(failure, monkeypatch, tmpdir, username, key):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- monkeypatch.setenv(username, 'foo')
- monkeypatch.setenv(key, 'bar')
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ monkeypatch.setenv(username, "foo")
+ monkeypatch.setenv(key, "bar")
out = failure()
- messages = ['Invalid username or password', 'basic auth failed']
+ messages = ["Invalid username or password", "basic auth failed"]
assert any(message in out for message in messages)
def test_invalid_credentials_file(failure, monkeypatch, tmpdir):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- tmpdir.join('.browserstack').write('[credentials]\nusername=foo\nkey=bar')
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ tmpdir.join(".browserstack").write("[credentials]\nusername=foo\nkey=bar")
out = failure()
- messages = ['Invalid username or password', 'basic auth failed']
+ messages = ["Invalid username or password", "basic auth failed"]
assert any(message in out for message in messages)
diff --git a/testing/test_capabilities.py b/testing/test_capabilities.py
index e3a7a5e6..cfc737bc 100644
--- a/testing/test_capabilities.py
+++ b/testing/test_capabilities.py
@@ -11,56 +11,68 @@
@pytest.fixture
def testfile(testdir):
- return testdir.makepyfile("""
+ return testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_capabilities(capabilities):
assert capabilities['foo'] == 'bar'
- """)
+ """
+ )
def test_command_line(testfile, testdir):
- testdir.quick_qa('--capability', 'foo', 'bar', testfile, passed=1)
+ testdir.quick_qa("--capability", "foo", "bar", testfile, passed=1)
def test_file(testfile, testdir):
- variables = testdir.makefile('.json', '{"capabilities": {"foo": "bar"}}')
- testdir.quick_qa('--variables', variables, testfile, passed=1)
+ variables = testdir.makefile(".json", '{"capabilities": {"foo": "bar"}}')
+ testdir.quick_qa("--variables", variables, testfile, passed=1)
def test_file_remote(testdir):
- key = 'goog:chromeOptions'
- capabilities = {'browserName': 'chrome', key: {'args': ['foo']}}
- variables = testdir.makefile('.json', '{{"capabilities": {}}}'.format(
- json.dumps(capabilities)))
- file_test = testdir.makepyfile("""
+ key = "goog:chromeOptions"
+ capabilities = {"browserName": "chrome", key: {"args": ["foo"]}}
+ variables = testdir.makefile(
+ ".json", '{{"capabilities": {}}}'.format(json.dumps(capabilities))
+ )
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_capabilities(session_capabilities, capabilities):
assert session_capabilities['{0}']['args'] == ['foo']
assert capabilities['{0}']['args'] == ['foo']
- """.format(key))
+ """.format(
+ key
+ )
+ )
testdir.quick_qa(
- '--driver', 'Remote', '--variables', variables, file_test, passed=1)
+ "--driver", "Remote", "--variables", variables, file_test, passed=1
+ )
def test_fixture(testfile, testdir):
- testdir.makeconftest("""
+ testdir.makeconftest(
+ """
import pytest
@pytest.fixture(scope='session')
def capabilities():
return {'foo': 'bar'}
- """)
+ """
+ )
testdir.quick_qa(testfile, passed=1)
def test_mark(testdir):
- file_test = testdir.makepyfile("""
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
@pytest.mark.capabilities(foo='bar')
def test_capabilities(session_capabilities, capabilities):
assert 'foo' not in session_capabilities
assert capabilities['foo'] == 'bar'
- """)
+ """
+ )
testdir.quick_qa(file_test, passed=1)
diff --git a/testing/test_chrome.py b/testing/test_chrome.py
index fce9f7bd..f65afd1e 100644
--- a/testing/test_chrome.py
+++ b/testing/test_chrome.py
@@ -11,19 +11,22 @@
@pytest.mark.chrome
def test_launch(testdir, httpserver):
- httpserver.serve_content(content='Success! ')
- file_test = testdir.makepyfile("""
+ httpserver.serve_content(content="Success! ")
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(webtext):
assert webtext == u'Success!'
- """)
- testdir.quick_qa('--driver', 'Chrome', file_test, passed=1)
+ """
+ )
+ testdir.quick_qa("--driver", "Chrome", file_test, passed=1)
@pytest.mark.chrome
def test_options(testdir):
- testdir.makepyfile("""
+ testdir.makepyfile(
+ """
import pytest
@pytest.fixture
def chrome_options(chrome_options):
@@ -32,17 +35,19 @@ def chrome_options(chrome_options):
@pytest.mark.nondestructive
def test_pass(selenium): pass
- """)
- reprec = testdir.inline_run('--driver', 'Chrome')
+ """
+ )
+ reprec = testdir.inline_run("--driver", "Chrome")
passed, skipped, failed = reprec.listoutcomes()
assert len(failed) == 1
out = failed[0].longrepr.reprcrash.message
- assert 'no chrome binary at /foo/bar' in out
+ assert "no chrome binary at /foo/bar" in out
@pytest.mark.chrome
def test_args(testdir):
- file_test = testdir.makepyfile("""
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.fixture
def driver_log():
@@ -54,6 +59,7 @@ def driver_args():
@pytest.mark.nondestructive
def test_pass(selenium): pass
- """)
- testdir.quick_qa('--driver', 'Chrome', file_test, passed=1)
- assert os.path.exists(str(testdir.tmpdir.join('foo.log')))
+ """
+ )
+ testdir.quick_qa("--driver", "Chrome", file_test, passed=1)
+ assert os.path.exists(str(testdir.tmpdir.join("foo.log")))
diff --git a/testing/test_crossbrowsertesting.py b/testing/test_crossbrowsertesting.py
index e3fa679a..53835dec 100644
--- a/testing/test_crossbrowsertesting.py
+++ b/testing/test_crossbrowsertesting.py
@@ -12,11 +12,13 @@
@pytest.fixture
def testfile(testdir):
- return testdir.makepyfile("""
+ return testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(selenium): pass
- """)
+ """
+ )
def failure_with_output(testdir, *args, **kwargs):
@@ -29,45 +31,53 @@ def failure_with_output(testdir, *args, **kwargs):
@pytest.fixture
def failure(testdir, testfile, httpserver_base_url):
- return partial(failure_with_output, testdir, testfile, httpserver_base_url,
- '--driver', 'CrossBrowserTesting')
+ return partial(
+ failure_with_output,
+ testdir,
+ testfile,
+ httpserver_base_url,
+ "--driver",
+ "CrossBrowserTesting",
+ )
def test_missing_username(failure, monkeypatch, tmpdir):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- assert 'CrossBrowserTesting username must be set' in failure()
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ assert "CrossBrowserTesting username must be set" in failure()
def test_missing_access_key_env(failure, monkeypatch, tmpdir):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- monkeypatch.setenv('CROSSBROWSERTESTING_USERNAME', 'foo')
- assert 'CrossBrowserTesting key must be set' in failure()
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ monkeypatch.setenv("CROSSBROWSERTESTING_USERNAME", "foo")
+ assert "CrossBrowserTesting key must be set" in failure()
def test_missing_access_key_file(failure, monkeypatch, tmpdir):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- tmpdir.join('.crossbrowsertesting').write('[credentials]\nusername=foo')
- assert 'CrossBrowserTesting key must be set' in failure()
-
-
-@pytest.mark.parametrize(('username', 'key'),
- [('CROSSBROWSERTESTING_USERNAME',
- 'CROSSBROWSERTESTING_AUTH_KEY'),
- ('CROSSBROWSERTESTING_USR',
- 'CROSSBROWSERTESTING_PSW')])
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ tmpdir.join(".crossbrowsertesting").write("[credentials]\nusername=foo")
+ assert "CrossBrowserTesting key must be set" in failure()
+
+
+@pytest.mark.parametrize(
+ ("username", "key"),
+ [
+ ("CROSSBROWSERTESTING_USERNAME", "CROSSBROWSERTESTING_AUTH_KEY"),
+ ("CROSSBROWSERTESTING_USR", "CROSSBROWSERTESTING_PSW"),
+ ],
+)
def test_invalid_credentials_env(failure, monkeypatch, tmpdir, username, key):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- monkeypatch.setenv(username, 'foo')
- monkeypatch.setenv(key, 'bar')
- out = failure('--capability', 'browser_api_name', 'FF46')
- messages = ['missing auth', 'basic auth failed']
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ monkeypatch.setenv(username, "foo")
+ monkeypatch.setenv(key, "bar")
+ out = failure("--capability", "browser_api_name", "FF46")
+ messages = ["missing auth", "basic auth failed"]
assert any(message in out for message in messages)
def test_invalid_credentials_file(failure, monkeypatch, tmpdir):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- config = tmpdir.join('.crossbrowsertesting')
- config.write('[credentials]\nusername=foo\nkey=bar')
- out = failure('--capability', 'browser_api_name', 'FF46')
- messages = ['missing auth', 'basic auth failed']
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ config = tmpdir.join(".crossbrowsertesting")
+ config.write("[credentials]\nusername=foo\nkey=bar")
+ out = failure("--capability", "browser_api_name", "FF46")
+ messages = ["missing auth", "basic auth failed"]
assert any(message in out for message in messages)
diff --git a/testing/test_destructive.py b/testing/test_destructive.py
index 4d95d400..ae230340 100644
--- a/testing/test_destructive.py
+++ b/testing/test_destructive.py
@@ -8,70 +8,74 @@
def test_skip_destructive_by_default(testdir):
- file_test = testdir.makepyfile('def test_pass(): pass')
+ file_test = testdir.makepyfile("def test_pass(): pass")
testdir.quick_qa(file_test, passed=0, failed=0, skipped=1)
def test_warn_when_url_is_sensitive(testdir, httpserver, monkeypatch, capsys):
- monkeypatch.setenv('SENSITIVE_URL', r'127\.0\.0\.1')
- file_test = testdir.makepyfile('def test_pass(): pass')
- testdir.quick_qa(file_test, '--verbose', passed=0, failed=0, skipped=1)
+ monkeypatch.setenv("SENSITIVE_URL", r"127\.0\.0\.1")
+ file_test = testdir.makepyfile("def test_pass(): pass")
+ testdir.quick_qa(file_test, "--verbose", passed=0, failed=0, skipped=1)
out, err = capsys.readouterr()
- msg = '*** WARNING: sensitive url matches {} ***'.format(httpserver.url)
+ msg = "*** WARNING: sensitive url matches {} ***".format(httpserver.url)
assert msg in out
def test_skip_destructive_when_sensitive_command_line(testdir, httpserver):
- file_test = testdir.makepyfile('def test_pass(): pass')
+ file_test = testdir.makepyfile("def test_pass(): pass")
print(httpserver.url)
- testdir.quick_qa('--sensitive-url', r'127\.0\.0\.1', file_test, passed=0,
- failed=0, skipped=1)
+ testdir.quick_qa(
+ "--sensitive-url", r"127\.0\.0\.1", file_test, passed=0, failed=0, skipped=1
+ )
def test_skip_destructive_when_sensitive_config_file(testdir, httpserver):
- testdir.makefile('.ini', pytest='[pytest]\nsensitive_url=127\\.0\\.0\\.1')
- file_test = testdir.makepyfile('def test_pass(): pass')
+ testdir.makefile(".ini", pytest="[pytest]\nsensitive_url=127\\.0\\.0\\.1")
+ file_test = testdir.makepyfile("def test_pass(): pass")
testdir.quick_qa(file_test, passed=0, failed=0, skipped=1)
def test_skip_destructive_when_sensitive_env(testdir, httpserver, monkeypatch):
- monkeypatch.setenv('SENSITIVE_URL', r'127\.0\.0\.1')
- file_test = testdir.makepyfile('def test_pass(): pass')
+ monkeypatch.setenv("SENSITIVE_URL", r"127\.0\.0\.1")
+ file_test = testdir.makepyfile("def test_pass(): pass")
testdir.quick_qa(file_test, passed=0, failed=0, skipped=1)
def test_run_non_destructive_by_default(testdir):
- file_test = testdir.makepyfile("""
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(): pass
- """)
+ """
+ )
testdir.quick_qa(file_test, passed=1)
def test_run_destructive_when_not_sensitive_command_line(testdir, httpserver):
- file_test = testdir.makepyfile('def test_pass(): pass')
- testdir.quick_qa('--sensitive-url', 'foo', file_test, passed=1)
+ file_test = testdir.makepyfile("def test_pass(): pass")
+ testdir.quick_qa("--sensitive-url", "foo", file_test, passed=1)
def test_run_destructive_when_not_sensitive_config_file(testdir, httpserver):
- testdir.makefile('.ini', pytest='[pytest]\nsensitive_url=foo')
- file_test = testdir.makepyfile('def test_pass(): pass')
+ testdir.makefile(".ini", pytest="[pytest]\nsensitive_url=foo")
+ file_test = testdir.makepyfile("def test_pass(): pass")
testdir.quick_qa(file_test, passed=1, failed=0, skipped=0)
-def test_run_destructive_when_not_sensitive_env(testdir, httpserver,
- monkeypatch):
- monkeypatch.setenv('SENSITIVE_URL', 'foo')
- file_test = testdir.makepyfile('def test_pass(): pass')
+def test_run_destructive_when_not_sensitive_env(testdir, httpserver, monkeypatch):
+ monkeypatch.setenv("SENSITIVE_URL", "foo")
+ file_test = testdir.makepyfile("def test_pass(): pass")
testdir.quick_qa(file_test, passed=1, failed=0, skipped=0)
def test_run_destructive_and_non_destructive_when_not_sensitive(testdir):
- file_test = testdir.makepyfile("""
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass1(): pass
def test_pass2(): pass
- """)
- testdir.quick_qa('--sensitive-url', 'foo', file_test, passed=2)
+ """
+ )
+ testdir.quick_qa("--sensitive-url", "foo", file_test, passed=2)
diff --git a/testing/test_driver.py b/testing/test_driver.py
index 61bf662e..d985066b 100644
--- a/testing/test_driver.py
+++ b/testing/test_driver.py
@@ -11,11 +11,13 @@
@pytest.fixture
def testfile(testdir):
- return testdir.makepyfile("""
+ return testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(selenium): pass
- """)
+ """
+ )
def failure_with_output(testdir, *args, **kwargs):
@@ -32,121 +34,141 @@ def failure(testdir, testfile, httpserver_base_url):
def test_driver_case_insensitive(testdir):
- file_test = testdir.makepyfile("""
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(request):
assert request.config.getoption('driver') == 'SaUcELaBs'
- """)
- testdir.quick_qa('--driver', 'SaUcELaBs', file_test, passed=1)
+ """
+ )
+ testdir.quick_qa("--driver", "SaUcELaBs", file_test, passed=1)
def test_missing_driver(failure):
out = failure()
- assert 'UsageError: --driver must be specified' in out
+ assert "UsageError: --driver must be specified" in out
def test_invalid_driver(testdir):
- testdir.makepyfile("""
+ testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(): pass
- """)
- invalid_driver = 'noop'
- result = testdir.runpytest('--driver', invalid_driver)
+ """
+ )
+ invalid_driver = "noop"
+ result = testdir.runpytest("--driver", invalid_driver)
message = "--driver: invalid choice: '{}'".format(invalid_driver)
assert message in result.errlines[1]
def test_driver_quit(testdir):
- testdir.makepyfile("""
+ testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_driver_quit(selenium):
selenium.quit()
selenium.title
- """)
+ """
+ )
result = testdir.runpytestqa()
- result.stdout.fnmatch_lines_random([
- 'WARNING: Failed to gather URL: *',
- 'WARNING: Failed to gather screenshot: *',
- 'WARNING: Failed to gather HTML: *',
- 'WARNING: Failed to gather log types: *'])
+ result.stdout.fnmatch_lines_random(
+ [
+ "WARNING: Failed to gather URL: *",
+ "WARNING: Failed to gather screenshot: *",
+ "WARNING: Failed to gather HTML: *",
+ "WARNING: Failed to gather log types: *",
+ ]
+ )
outcomes = result.parseoutcomes()
- assert outcomes.get('failed') == 1
+ assert outcomes.get("failed") == 1
def test_default_host_port(testdir):
- host = 'localhost'
- port = 4444
- file_test = testdir.makepyfile("""
+ host = "localhost"
+ port = "4444"
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(driver_kwargs):
assert driver_kwargs['command_executor'] == 'http://{}:{}/wd/hub'
- """.format(host, port))
- testdir.quick_qa('--driver', 'Remote', file_test, passed=1)
+ """.format(
+ host, port
+ )
+ )
+ testdir.quick_qa("--driver", "Remote", file_test, passed=1)
def test_arguments_order(testdir):
- host = 'notlocalhost'
- port = 4441
- file_test = testdir.makepyfile("""
+ host = "notlocalhost"
+ port = "4441"
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(driver_kwargs):
assert driver_kwargs['command_executor'] == 'http://{}:{}/wd/hub'
- """.format(host, port))
- testdir.quick_qa('--driver', 'Remote',
- '--host', host,
- '--port', port,
- file_test, passed=1)
+ """.format(
+ host, port
+ )
+ )
+ testdir.quick_qa(
+ "--driver", "Remote", "--host", host, "--port", port, file_test, passed=1
+ )
def test_arguments_order_random(testdir):
- host = 'notlocalhost'
- port = 4441
- file_test = testdir.makepyfile("""
+ host = "notlocalhost"
+ port = "4441"
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(driver_kwargs):
assert driver_kwargs['command_executor'] == 'http://{}:{}/wd/hub'
- """.format(host, port))
- testdir.quick_qa('--host', host,
- '--driver', 'Remote',
- '--port', port,
- file_test, passed=1)
-
-
-@pytest.mark.parametrize('name',
- ['SauceLabs',
- 'TestingBot',
- 'CrossBrowserTesting',
- 'BrowserStack'])
+ """.format(
+ host, port
+ )
+ )
+ testdir.quick_qa(
+ "--host", host, "--driver", "Remote", "--port", port, file_test, passed=1
+ )
+
+
+@pytest.mark.parametrize(
+ "name", ["SauceLabs", "TestingBot", "CrossBrowserTesting", "BrowserStack"]
+)
def test_provider_naming(name):
import importlib
driver = name
module = importlib.import_module(
- 'pytest_selenium.drivers.{}'.format(driver.lower()))
+ "pytest_selenium.drivers.{}".format(driver.lower())
+ )
provider = getattr(module, driver)()
assert provider.uses_driver(driver)
assert provider.name == name
def test_service_log_path(testdir):
- file_test = testdir.makepyfile("""
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(driver_kwargs):
assert driver_kwargs['service_log_path'] is not None
- """)
- testdir.quick_qa('--driver', 'Firefox',
- file_test, passed=1)
+ """
+ )
+ testdir.quick_qa("--driver", "Firefox", file_test, passed=1)
def test_no_service_log_path(testdir):
- file_test = testdir.makepyfile("""
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.fixture
def driver_log():
@@ -155,6 +177,6 @@ def driver_log():
@pytest.mark.nondestructive
def test_pass(driver_kwargs):
assert driver_kwargs['service_log_path'] is None
- """)
- testdir.quick_qa('--driver', 'Firefox',
- file_test, passed=1)
+ """
+ )
+ testdir.quick_qa("--driver", "Firefox", file_test, passed=1)
diff --git a/testing/test_driver_log.py b/testing/test_driver_log.py
index aa42b60b..d2ac77df 100644
--- a/testing/test_driver_log.py
+++ b/testing/test_driver_log.py
@@ -13,26 +13,28 @@
def test_driver_log(testdir, httpserver):
- httpserver.serve_content(content='Success! ')
- testdir.makepyfile("""
+ httpserver.serve_content(content="Success! ")
+ testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_driver_log(webtext):
assert False
- """)
- path = testdir.tmpdir.join('report.html')
- testdir.runpytestqa('--html', path)
+ """
+ )
+ path = testdir.tmpdir.join("report.html")
+ testdir.runpytestqa("--html", path)
with open(str(path)) as f:
html = f.read()
assert re.search(LOG_REGEX, html) is not None
- log_path = testdir.tmpdir.dirpath(
- 'basetemp', 'test_driver_log0', 'driver.log')
+ log_path = testdir.tmpdir.dirpath("basetemp", "test_driver_log0", "driver.log")
assert os.path.exists(str(log_path))
def test_driver_log_fixture(testdir, httpserver):
- httpserver.serve_content(content='Success! ')
- file_test = testdir.makepyfile("""
+ httpserver.serve_content(content="Success! ")
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.fixture
def driver_log():
@@ -41,14 +43,16 @@ def driver_log():
@pytest.mark.nondestructive
def test_pass(webtext):
assert webtext == u'Success!'
- """)
+ """
+ )
testdir.quick_qa(file_test, passed=1)
- assert os.path.exists(str(testdir.tmpdir.join('foo.log')))
+ assert os.path.exists(str(testdir.tmpdir.join("foo.log")))
def test_no_driver_log(testdir, httpserver):
- httpserver.serve_content(content='Success! ')
- testdir.makepyfile("""
+ httpserver.serve_content(content="Success! ")
+ testdir.makepyfile(
+ """
import pytest
@pytest.fixture
def driver_log():
@@ -57,12 +61,12 @@ def driver_log():
@pytest.mark.nondestructive
def test_no_driver_log(webtext):
assert False
- """)
- path = testdir.tmpdir.join('report.html')
- testdir.runpytestqa('--html', path)
+ """
+ )
+ path = testdir.tmpdir.join("report.html")
+ testdir.runpytestqa("--html", path)
with open(str(path)) as f:
html = f.read()
assert re.search(LOG_REGEX, html) is None
- log_path = testdir.tmpdir.dirpath(
- 'basetemp', 'test_no_driver_log0', 'driver.log')
+ log_path = testdir.tmpdir.dirpath("basetemp", "test_no_driver_log0", "driver.log")
assert not os.path.exists(str(log_path))
diff --git a/testing/test_edge.py b/testing/test_edge.py
index ea6ebb4f..59068623 100644
--- a/testing/test_edge.py
+++ b/testing/test_edge.py
@@ -9,11 +9,13 @@
@pytest.mark.edge
def test_launch(testdir, httpserver):
- httpserver.serve_content(content='Success! ')
- file_test = testdir.makepyfile("""
+ httpserver.serve_content(content="Success! ")
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(webtext):
assert webtext == u'Success!'
- """)
- testdir.quick_qa('--driver', 'Edge', file_test, passed=1)
+ """
+ )
+ testdir.quick_qa("--driver", "Edge", file_test, passed=1)
diff --git a/testing/test_firefox.py b/testing/test_firefox.py
index 2153107e..2e3ab7aa 100644
--- a/testing/test_firefox.py
+++ b/testing/test_firefox.py
@@ -8,25 +8,29 @@
def test_launch(testdir, httpserver):
- httpserver.serve_content(content='Success! ')
- file_test = testdir.makepyfile("""
+ httpserver.serve_content(content="Success! ")
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(webtext):
assert webtext == u'Success!'
- """)
+ """
+ )
testdir.quick_qa(file_test, passed=1)
def test_launch_case_insensitive(testdir, httpserver):
- httpserver.serve_content(content='Success! ')
- file_test = testdir.makepyfile("""
+ httpserver.serve_content(content="Success! ")
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(webtext):
assert webtext == u'Success!'
- """)
- testdir.quick_qa('--driver', 'firefox', file_test, passed=1)
+ """
+ )
+ testdir.quick_qa("--driver", "firefox", file_test, passed=1)
def test_profile(testdir, httpserver):
@@ -36,12 +40,14 @@ def test_profile(testdir, httpserver):
when calling value_of_css_property.
"""
httpserver.serve_content(content='Success! Link ')
- profile = testdir.tmpdir.mkdir('profile')
- profile.join('prefs.js').write(
+ profile = testdir.tmpdir.mkdir("profile")
+ profile.join("prefs.js").write(
'user_pref("browser.anchor_color", "#FF69B4");'
'user_pref("browser.display.foreground_color", "#FF0000");'
- 'user_pref("browser.display.use_document_colors", false);')
- file_test = testdir.makepyfile("""
+ 'user_pref("browser.display.use_document_colors", false);'
+ )
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_profile(base_url, selenium):
@@ -52,8 +58,9 @@ def test_profile(base_url, selenium):
anchor_color = anchor.value_of_css_property('color')
assert header_color == 'rgb(255, 0, 0)'
assert anchor_color == 'rgb(255, 105, 180)'
- """)
- testdir.quick_qa('--firefox-profile', profile, file_test, passed=1)
+ """
+ )
+ testdir.quick_qa("--firefox-profile", profile, file_test, passed=1)
def test_profile_with_preferences(testdir, httpserver):
@@ -65,12 +72,14 @@ def test_profile_with_preferences(testdir, httpserver):
overridden by the preference.
"""
httpserver.serve_content(content='Success! Link ')
- profile = testdir.tmpdir.mkdir('profile')
- profile.join('prefs.js').write(
+ profile = testdir.tmpdir.mkdir("profile")
+ profile.join("prefs.js").write(
'user_pref("browser.anchor_color", "#FF69B4");'
'user_pref("browser.display.foreground_color", "#FF0000");'
- 'user_pref("browser.display.use_document_colors", false);')
- file_test = testdir.makepyfile("""
+ 'user_pref("browser.display.use_document_colors", false);'
+ )
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_preferences(base_url, selenium):
@@ -81,17 +90,27 @@ def test_preferences(base_url, selenium):
anchor_color = anchor.value_of_css_property('color')
assert header_color == 'rgb(255, 0, 0)'
assert anchor_color == 'rgb(255, 0, 0)'
- """)
- testdir.quick_qa('--firefox-preference', 'browser.anchor_color', '#FF0000',
- '--firefox-profile', profile, file_test, passed=1)
+ """
+ )
+ testdir.quick_qa(
+ "--firefox-preference",
+ "browser.anchor_color",
+ "#FF0000",
+ "--firefox-profile",
+ profile,
+ file_test,
+ passed=1,
+ )
def test_extension(testdir):
"""Test that a firefox extension can be added when starting Firefox."""
import os
- path = os.path.join(os.path.split(os.path.dirname(__file__))[0], 'testing')
- extension = os.path.join(path, 'empty.xpi')
- file_test = testdir.makepyfile("""
+
+ path = os.path.join(os.path.split(os.path.dirname(__file__))[0], "testing")
+ extension = os.path.join(path, "empty.xpi")
+ file_test = testdir.makepyfile(
+ """
import time
import pytest
from selenium.common.exceptions import StaleElementReferenceException
@@ -105,14 +124,16 @@ def test_extension(selenium):
lambda s: s.find_element_by_id(
'extensions-tbody').text)
assert 'Test Extension (empty)' in extensions
- """)
- testdir.quick_qa('--firefox-extension', extension, file_test, passed=1)
+ """
+ )
+ testdir.quick_qa("--firefox-extension", extension, file_test, passed=1)
def test_preferences_marker(testdir, httpserver):
"""Test that preferences can be specified using the marker."""
httpserver.serve_content(content='Success! Link ')
- file_test = testdir.makepyfile("""
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
@pytest.mark.firefox_preferences({
@@ -127,5 +148,22 @@ def test_preferences(base_url, selenium):
anchor_color = anchor.value_of_css_property('color')
assert header_color == 'rgb(255, 0, 0)'
assert anchor_color == 'rgb(255, 105, 180)'
- """)
+ """
+ )
+ testdir.quick_qa(file_test, passed=1)
+
+
+def test_arguments_marker(testdir):
+ file_test = testdir.makepyfile(
+ """
+ import pytest
+ pytestmark = pytest.mark.firefox_arguments('baz')
+ @pytest.mark.nondestructive
+ @pytest.mark.firefox_arguments('foo', 'bar')
+ def test_arguments(firefox_options):
+ actual = sorted(firefox_options.arguments)
+ expected = sorted(['baz', 'foo', 'bar'])
+ assert actual == expected
+ """
+ )
testdir.quick_qa(file_test, passed=1)
diff --git a/testing/test_metadata.py b/testing/test_metadata.py
index 789e0ea0..80796865 100644
--- a/testing/test_metadata.py
+++ b/testing/test_metadata.py
@@ -8,27 +8,34 @@
def test_metadata_default_host_port(testdir):
- host = 'localhost'
- port = 4444
- file_test = testdir.makepyfile("""
+ host = "localhost"
+ port = "4444"
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(metadata):
assert metadata['Server'] == '{}:{}'
- """.format(host, port))
- testdir.quick_qa('--driver', 'Remote', file_test, passed=1)
+ """.format(
+ host, port
+ )
+ )
+ testdir.quick_qa("--driver", "Remote", file_test, passed=1)
def test_metadata_host_port(testdir):
- host = 'notlocalhost'
- port = 4441
- file_test = testdir.makepyfile("""
+ host = "notlocalhost"
+ port = "4441"
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(metadata):
assert metadata['Server'] == '{}:{}'
- """.format(host, port))
- testdir.quick_qa('--driver', 'Remote',
- '--host', host,
- '--port', port,
- file_test, passed=1)
+ """.format(
+ host, port
+ )
+ )
+ testdir.quick_qa(
+ "--driver", "Remote", "--host", host, "--port", port, file_test, passed=1
+ )
diff --git a/testing/test_phantomjs.py b/testing/test_phantomjs.py
index 047f7448..3df2aaf5 100644
--- a/testing/test_phantomjs.py
+++ b/testing/test_phantomjs.py
@@ -11,19 +11,22 @@
@pytest.mark.phantomjs
def test_launch(testdir, httpserver):
- httpserver.serve_content(content='Success! ')
- file_test = testdir.makepyfile("""
+ httpserver.serve_content(content="Success! ")
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(webtext):
assert webtext == u'Success!'
- """)
- testdir.quick_qa('--driver', 'PhantomJS', file_test, passed=1)
+ """
+ )
+ testdir.quick_qa("--driver", "PhantomJS", file_test, passed=1)
@pytest.mark.phantomjs
def test_args(testdir):
- file_test = testdir.makepyfile("""
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.fixture
def driver_args():
@@ -31,6 +34,7 @@ def driver_args():
@pytest.mark.nondestructive
def test_pass(selenium): pass
- """)
- testdir.quick_qa('--driver', 'PhantomJS', file_test, passed=1)
- assert os.path.exists(str(testdir.tmpdir.join('foo.log')))
+ """
+ )
+ testdir.quick_qa("--driver", "PhantomJS", file_test, passed=1)
+ assert os.path.exists(str(testdir.tmpdir.join("foo.log")))
diff --git a/testing/test_report.py b/testing/test_report.py
index d7837a42..738255fd 100644
--- a/testing/test_report.py
+++ b/testing/test_report.py
@@ -11,38 +11,46 @@
pytestmark = pytest.mark.nondestructive
URL_LINK = 'URL '
-SCREENSHOT_LINK_REGEX = 'Screenshot ' # noqa
+SCREENSHOT_LINK_REGEX = (
+ 'Screenshot '
+) # noqa
SCREENSHOT_REGEX = ''
LOGS_REGEX = '.* Log '
HTML_REGEX = 'HTML '
def run(testdir, *args):
- testdir.makepyfile("""
+ testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_fail(webtext):
assert False
- """)
- path = testdir.tmpdir.join('report.html')
- result = testdir.runpytestqa('--html', path, *args)
+ """
+ )
+ path = testdir.tmpdir.join("report.html")
+ result = testdir.runpytestqa("--html", path, *args)
with open(str(path)) as f:
html = f.read()
return result, html
-@pytest.mark.parametrize('when', ['always', 'failure', 'never'])
+@pytest.mark.parametrize("when", ["always", "failure", "never"])
def test_capture_debug_env(testdir, httpserver, monkeypatch, when):
- httpserver.serve_content(content='Success! Ё
')
- monkeypatch.setenv('SELENIUM_CAPTURE_DEBUG', when)
- testdir.makepyfile("""
+ httpserver.serve_content(content="Success! Ё
")
+ monkeypatch.setenv("SELENIUM_CAPTURE_DEBUG", when)
+ testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_capture_debug(webtext):
assert {0}
- """.format('True' if 'always' else 'False'))
+ """.format(
+ "True" if "always" else "False"
+ )
+ )
result, html = run(testdir)
- if when in ['always', 'failure']:
+ if when in ["always", "failure"]:
assert URL_LINK.format(httpserver.url) in html
assert re.search(SCREENSHOT_LINK_REGEX, html) is not None
assert re.search(SCREENSHOT_REGEX, html) is not None
@@ -56,21 +64,30 @@ def test_capture_debug(webtext):
assert re.search(HTML_REGEX, html) is None
-@pytest.mark.parametrize('when', ['always', 'failure', 'never'])
+@pytest.mark.parametrize("when", ["always", "failure", "never"])
def test_capture_debug_config(testdir, httpserver, when):
- httpserver.serve_content(content='Success! Ё
')
- testdir.makefile('.ini', pytest="""
+ httpserver.serve_content(content="Success! Ё
")
+ testdir.makefile(
+ ".ini",
+ pytest="""
[pytest]
selenium_capture_debug={0}
- """.format(when))
- testdir.makepyfile("""
+ """.format(
+ when
+ ),
+ )
+ testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_capture_debug(webtext):
assert {0}
- """.format('True' if 'always' else 'False'))
+ """.format(
+ "True" if "always" else "False"
+ )
+ )
result, html = run(testdir)
- if when in ['always', 'failure']:
+ if when in ["always", "failure"]:
assert URL_LINK.format(httpserver.url) in html
assert re.search(SCREENSHOT_LINK_REGEX, html) is not None
assert re.search(SCREENSHOT_REGEX, html) is not None
@@ -84,64 +101,69 @@ def test_capture_debug(webtext):
assert re.search(HTML_REGEX, html) is None
-@pytest.mark.parametrize('exclude', ['url', 'screenshot', 'html', 'logs'])
+@pytest.mark.parametrize("exclude", ["url", "screenshot", "html", "logs"])
def test_exclude_debug_env(testdir, httpserver, monkeypatch, exclude):
- httpserver.serve_content(content='Success! Ё
')
- monkeypatch.setenv('SELENIUM_EXCLUDE_DEBUG', exclude)
+ httpserver.serve_content(content="Success! Ё
")
+ monkeypatch.setenv("SELENIUM_EXCLUDE_DEBUG", exclude)
result, html = run(testdir)
assert result.ret
- if exclude == 'url':
+ if exclude == "url":
assert URL_LINK.format(httpserver.url) not in html
else:
assert URL_LINK.format(httpserver.url) in html
- if exclude == 'screenshot':
+ if exclude == "screenshot":
assert re.search(SCREENSHOT_LINK_REGEX, html) is None
assert re.search(SCREENSHOT_REGEX, html) is None
else:
assert re.search(SCREENSHOT_LINK_REGEX, html) is not None
assert re.search(SCREENSHOT_REGEX, html) is not None
- if exclude == 'logs':
+ if exclude == "logs":
assert re.search(LOGS_REGEX, html) is None
else:
assert re.search(LOGS_REGEX, html) is not None
- if exclude == 'html':
+ if exclude == "html":
assert re.search(HTML_REGEX, html) is None
else:
assert re.search(HTML_REGEX, html) is not None
-@pytest.mark.parametrize('exclude', ['url', 'screenshot', 'html', 'logs'])
+@pytest.mark.parametrize("exclude", ["url", "screenshot", "html", "logs"])
def test_exclude_debug_config(testdir, httpserver, monkeypatch, exclude):
- httpserver.serve_content(content='Success! Ё
')
- testdir.makefile('.ini', pytest="""
+ httpserver.serve_content(content="Success! Ё
")
+ testdir.makefile(
+ ".ini",
+ pytest="""
[pytest]
selenium_exclude_debug={0}
- """.format(exclude))
+ """.format(
+ exclude
+ ),
+ )
result, html = run(testdir)
assert result.ret
- if exclude == 'url':
+ if exclude == "url":
assert URL_LINK.format(httpserver.url) not in html
else:
assert URL_LINK.format(httpserver.url) in html
- if exclude == 'screenshot':
+ if exclude == "screenshot":
assert re.search(SCREENSHOT_LINK_REGEX, html) is None
assert re.search(SCREENSHOT_REGEX, html) is None
else:
assert re.search(SCREENSHOT_LINK_REGEX, html) is not None
assert re.search(SCREENSHOT_REGEX, html) is not None
- if exclude == 'logs':
+ if exclude == "logs":
assert re.search(LOGS_REGEX, html) is None
else:
assert re.search(LOGS_REGEX, html) is not None
- if exclude == 'html':
+ if exclude == "html":
assert re.search(HTML_REGEX, html) is None
else:
assert re.search(HTML_REGEX, html) is not None
diff --git a/testing/test_safari.py b/testing/test_safari.py
index a44981f7..50705eaf 100644
--- a/testing/test_safari.py
+++ b/testing/test_safari.py
@@ -9,11 +9,13 @@
@pytest.mark.safari
def test_launch(testdir, httpserver):
- httpserver.serve_content(content='Success! ')
- file_test = testdir.makepyfile("""
+ httpserver.serve_content(content="Success! ")
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(webtext):
assert webtext == u'Success!'
- """)
- testdir.quick_qa('--driver', 'Safari', file_test, passed=1)
+ """
+ )
+ testdir.quick_qa("--driver", "Safari", file_test, passed=1)
diff --git a/testing/test_saucelabs.py b/testing/test_saucelabs.py
index 3b4d2f3f..fdd8400d 100644
--- a/testing/test_saucelabs.py
+++ b/testing/test_saucelabs.py
@@ -13,11 +13,13 @@
@pytest.fixture
def testfile(testdir):
- return testdir.makepyfile("""
+ return testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(selenium): pass
- """)
+ """
+ )
def failure_with_output(testdir, *args, **kwargs):
@@ -30,64 +32,75 @@ def failure_with_output(testdir, *args, **kwargs):
@pytest.fixture
def failure(testdir, testfile, httpserver_base_url):
- return partial(failure_with_output, testdir, testfile, httpserver_base_url,
- '--driver', 'SauceLabs')
+ return partial(
+ failure_with_output,
+ testdir,
+ testfile,
+ httpserver_base_url,
+ "--driver",
+ "SauceLabs",
+ )
def test_missing_username(failure, monkeypatch, tmpdir):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- assert 'SauceLabs username must be set' in failure()
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ assert "SauceLabs username must be set" in failure()
def test_missing_api_key_env(failure, monkeypatch, tmpdir):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- monkeypatch.setenv('SAUCELABS_USERNAME', 'foo')
- assert 'SauceLabs key must be set' in failure()
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ monkeypatch.setenv("SAUCELABS_USERNAME", "foo")
+ assert "SauceLabs key must be set" in failure()
def test_missing_api_key_file(failure, monkeypatch, tmpdir):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- tmpdir.join('.saucelabs').write('[credentials]\nusername=foo')
- assert 'SauceLabs key must be set' in failure()
-
-
-@pytest.mark.parametrize(('username', 'key'), [('SAUCELABS_USERNAME',
- 'SAUCELABS_API_KEY'),
- ('SAUCELABS_USR',
- 'SAUCELABS_PSW'),
- ('SAUCE_USERNAME',
- 'SAUCE_ACCESS_KEY')])
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ tmpdir.join(".saucelabs").write("[credentials]\nusername=foo")
+ assert "SauceLabs key must be set" in failure()
+
+
+@pytest.mark.parametrize(
+ ("username", "key"),
+ [
+ ("SAUCELABS_USERNAME", "SAUCELABS_API_KEY"),
+ ("SAUCELABS_USR", "SAUCELABS_PSW"),
+ ("SAUCE_USERNAME", "SAUCE_ACCESS_KEY"),
+ ],
+)
def test_invalid_credentials_env(failure, monkeypatch, tmpdir, username, key):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- monkeypatch.setenv(username, 'foo')
- monkeypatch.setenv(key, 'bar')
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ monkeypatch.setenv(username, "foo")
+ monkeypatch.setenv(key, "bar")
out = failure()
- messages = ['Sauce Labs Authentication Error', 'basic auth failed']
+ messages = ["Sauce Labs Authentication Error", "basic auth failed"]
assert any(message in out for message in messages)
def test_invalid_credentials_file(failure, monkeypatch, tmpdir):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- tmpdir.join('.saucelabs').write('[credentials]\nusername=foo\nkey=bar')
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ tmpdir.join(".saucelabs").write("[credentials]\nusername=foo\nkey=bar")
out = failure()
- messages = ['Sauce Labs Authentication Error', 'basic auth failed']
+ messages = ["Sauce Labs Authentication Error", "basic auth failed"]
assert any(message in out for message in messages)
def test_credentials_in_capabilities(monkeypatch, testdir):
- file_test = testdir.makepyfile("""
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_sauce_capabilities(driver_kwargs):
assert driver_kwargs['desired_capabilities']['username'] == 'foo'
assert driver_kwargs['desired_capabilities']['accessKey'] == 'bar'
- """)
+ """
+ )
run_sauce_test(monkeypatch, testdir, file_test)
def test_no_sauce_options(monkeypatch, testdir):
- file_test = testdir.makepyfile("""
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_sauce_capabilities(driver_kwargs):
@@ -96,107 +109,114 @@ def test_sauce_capabilities(driver_kwargs):
raise AssertionError(' should not be present!')
except KeyError:
pass
- """)
+ """
+ )
run_sauce_test(monkeypatch, testdir, file_test)
def run_sauce_test(monkeypatch, testdir, file_test):
- monkeypatch.setenv('SAUCELABS_USERNAME', 'foo')
- monkeypatch.setenv('SAUCELABS_API_KEY', 'bar')
+ monkeypatch.setenv("SAUCELABS_USERNAME", "foo")
+ monkeypatch.setenv("SAUCELABS_API_KEY", "bar")
- capabilities = {'browserName': 'chrome'}
- variables = testdir.makefile('.json', '{{"capabilities": {}}}'.format(
- json.dumps(capabilities)))
+ capabilities = {"browserName": "chrome"}
+ variables = testdir.makefile(
+ ".json", '{{"capabilities": {}}}'.format(json.dumps(capabilities))
+ )
testdir.quick_qa(
- '--driver', 'saucelabs', '--variables',
- variables, file_test, passed=1)
+ "--driver", "saucelabs", "--variables", variables, file_test, passed=1
+ )
def test_empty_sauce_options(monkeypatch, testdir):
- capabilities = {'browserName': 'chrome'}
- expected = {'name': 'test_empty_sauce_options.test_sauce_capabilities'}
+ capabilities = {"browserName": "chrome"}
+ expected = {"name": "test_empty_sauce_options.test_sauce_capabilities"}
run_w3c_sauce_test(capabilities, expected, monkeypatch, testdir)
def test_merge_sauce_options(monkeypatch, testdir):
- version = {'seleniumVersion': '3.8.1'}
- capabilities = {'browserName': 'chrome', 'sauce:options': version}
- expected = {'name': 'test_merge_sauce_options.test_sauce_capabilities'}
+ version = {"seleniumVersion": "3.8.1"}
+ capabilities = {"browserName": "chrome", "sauce:options": version}
+ expected = {"name": "test_merge_sauce_options.test_sauce_capabilities"}
expected.update(version)
run_w3c_sauce_test(capabilities, expected, monkeypatch, testdir)
def test_merge_sauce_options_with_conflict(monkeypatch, testdir):
- name = 'conflict'
- capabilities = {'browserName': 'chrome', 'sauce:options': {'name': name}}
- expected = {'name': name}
+ name = "conflict"
+ capabilities = {"browserName": "chrome", "sauce:options": {"name": name}}
+ expected = {"name": name}
run_w3c_sauce_test(capabilities, expected, monkeypatch, testdir)
def run_w3c_sauce_test(capabilities, expected_result, monkeypatch, testdir):
- username = 'foo'
- access_key = 'bar'
+ username = "foo"
+ access_key = "bar"
- monkeypatch.setenv('SAUCELABS_USERNAME', username)
- monkeypatch.setenv('SAUCELABS_API_KEY', access_key)
- monkeypatch.setenv('SAUCELABS_W3C', 'true')
+ monkeypatch.setenv("SAUCELABS_USERNAME", username)
+ monkeypatch.setenv("SAUCELABS_API_KEY", access_key)
+ monkeypatch.setenv("SAUCELABS_W3C", "true")
- expected_result.update({'username': username,
- 'accessKey': access_key,
- 'tags': ['nondestructive']})
+ expected_result.update(
+ {"username": username, "accessKey": access_key, "tags": ["nondestructive"]}
+ )
- variables = testdir.makefile('.json', '{{"capabilities": {}}}'.format(
- json.dumps(capabilities)))
+ variables = testdir.makefile(
+ ".json", '{{"capabilities": {}}}'.format(json.dumps(capabilities))
+ )
- file_test = testdir.makepyfile("""
+ file_test = testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_sauce_capabilities(driver_kwargs):
actual = driver_kwargs['desired_capabilities']['sauce:options']
assert actual == {}
- """.format(expected_result))
+ """.format(
+ expected_result
+ )
+ )
testdir.quick_qa(
- '--driver', 'saucelabs', '--variables',
- variables, file_test, passed=1)
+ "--driver", "saucelabs", "--variables", variables, file_test, passed=1
+ )
def test_auth_type_none(monkeypatch):
from pytest_selenium.drivers.saucelabs import SauceLabs, get_job_url
- monkeypatch.setenv('SAUCELABS_USERNAME', 'foo')
- monkeypatch.setenv('SAUCELABS_API_KEY', 'bar')
+ monkeypatch.setenv("SAUCELABS_USERNAME", "foo")
+ monkeypatch.setenv("SAUCELABS_API_KEY", "bar")
- session_id = '123456'
- expected = 'https://saucelabs.com/jobs/{}'.format(session_id)
- actual = get_job_url(Config('none'), SauceLabs(), session_id)
+ session_id = "123456"
+ expected = "https://saucelabs.com/jobs/{}".format(session_id)
+ actual = get_job_url(Config("none"), SauceLabs(), session_id)
assert actual == expected
-@pytest.mark.parametrize('auth_type', ['token', 'day', 'hour'])
+@pytest.mark.parametrize("auth_type", ["token", "day", "hour"])
def test_auth_type_expiration(monkeypatch, auth_type):
import re
from pytest_selenium.drivers.saucelabs import SauceLabs, get_job_url
- monkeypatch.setenv('SAUCELABS_USERNAME', 'foo')
- monkeypatch.setenv('SAUCELABS_API_KEY', 'bar')
+ monkeypatch.setenv("SAUCELABS_USERNAME", "foo")
+ monkeypatch.setenv("SAUCELABS_API_KEY", "bar")
- session_id = '123456'
- expected_pattern = r'https://saucelabs\.com/jobs/' \
- r'{}\?auth=[a-f0-9]{{32}}$'.format(session_id)
+ session_id = "123456"
+ expected_pattern = (
+ r"https://saucelabs\.com/jobs/" r"{}\?auth=[a-f0-9]{{32}}$".format(session_id)
+ )
actual = get_job_url(Config(auth_type), SauceLabs(), session_id)
assert re.match(expected_pattern, actual)
class Config(object):
-
def __init__(self, value):
self._value = value
def getini(self, key):
- if key == 'saucelabs_job_auth':
+ if key == "saucelabs_job_auth":
return self._value
else:
raise KeyError
diff --git a/testing/test_testingbot.py b/testing/test_testingbot.py
index 040b2811..f191a2fa 100644
--- a/testing/test_testingbot.py
+++ b/testing/test_testingbot.py
@@ -12,11 +12,13 @@
@pytest.fixture
def testfile(testdir):
- return testdir.makepyfile("""
+ return testdir.makepyfile(
+ """
import pytest
@pytest.mark.nondestructive
def test_pass(selenium): pass
- """)
+ """
+ )
def failure_with_output(testdir, *args, **kwargs):
@@ -29,52 +31,60 @@ def failure_with_output(testdir, *args, **kwargs):
@pytest.fixture
def failure(testdir, testfile, httpserver_base_url):
- return partial(failure_with_output, testdir, testfile, httpserver_base_url,
- '--driver', 'TestingBot')
+ return partial(
+ failure_with_output,
+ testdir,
+ testfile,
+ httpserver_base_url,
+ "--driver",
+ "TestingBot",
+ )
def test_missing_key(failure, monkeypatch, tmpdir):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- assert 'TestingBot key must be set' in failure()
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ assert "TestingBot key must be set" in failure()
def test_missing_secret_env(failure, monkeypatch, tmpdir):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- monkeypatch.setenv('TESTINGBOT_KEY', 'foo')
- assert 'TestingBot secret must be set' in failure()
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ monkeypatch.setenv("TESTINGBOT_KEY", "foo")
+ assert "TestingBot secret must be set" in failure()
def test_missing_secret_file(failure, monkeypatch, tmpdir):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- tmpdir.join('.testingbot').write('[credentials]\nkey=foo')
- assert 'TestingBot secret must be set' in failure()
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ tmpdir.join(".testingbot").write("[credentials]\nkey=foo")
+ assert "TestingBot secret must be set" in failure()
-@pytest.mark.parametrize(('key', 'secret'), [('TESTINGBOT_KEY',
- 'TESTINGBOT_SECRET'),
- ('TESTINGBOT_PSW',
- 'TESTINGBOT_USR')])
+@pytest.mark.parametrize(
+ ("key", "secret"),
+ [("TESTINGBOT_KEY", "TESTINGBOT_SECRET"), ("TESTINGBOT_PSW", "TESTINGBOT_USR")],
+)
def test_invalid_credentials_env(failure, monkeypatch, tmpdir, key, secret):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- monkeypatch.setenv(key, 'foo')
- monkeypatch.setenv(secret, 'bar')
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ monkeypatch.setenv(key, "foo")
+ monkeypatch.setenv(secret, "bar")
out = failure()
- messages = ['incorrect TestingBot credentials', 'basic auth failed']
+ messages = ["incorrect TestingBot credentials", "basic auth failed"]
assert any(message in out for message in messages)
def test_invalid_credentials_file(failure, monkeypatch, tmpdir):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- tmpdir.join('.testingbot').write('[credentials]\nkey=foo\nsecret=bar')
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ tmpdir.join(".testingbot").write("[credentials]\nkey=foo\nsecret=bar")
out = failure()
- messages = ['incorrect TestingBot credentials', 'basic auth failed']
+ messages = ["incorrect TestingBot credentials", "basic auth failed"]
assert any(message in out for message in messages)
def test_invalid_host(failure, monkeypatch, tmpdir):
- monkeypatch.setattr(os.path, 'expanduser', lambda p: str(tmpdir))
- tmpdir.join('.testingbot').write('[credentials]\nkey=foo\nsecret=bar')
- out = failure('--host', 'foo.bar.com')
- messages = ['nodename nor servname provided, or not known',
- 'Name or service not known']
+ monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
+ tmpdir.join(".testingbot").write("[credentials]\nkey=foo\nsecret=bar")
+ out = failure("--host", "foo.bar.com")
+ messages = [
+ "nodename nor servname provided, or not known",
+ "Name or service not known",
+ ]
assert any(message in out for message in messages)
diff --git a/testing/test_webdriver.py b/testing/test_webdriver.py
index 606a40aa..16dbed50 100644
--- a/testing/test_webdriver.py
+++ b/testing/test_webdriver.py
@@ -3,14 +3,14 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import pytest
-from selenium.webdriver.support.abstract_event_listener import \
- AbstractEventListener
+from selenium.webdriver.support.abstract_event_listener import AbstractEventListener
pytestmark = pytest.mark.nondestructive
def test_event_listening_webdriver(testdir, httpserver):
- file_test = testdir.makepyfile("""
+ file_test = testdir.makepyfile(
+ """
import pytest
from selenium.webdriver.support.event_firing_webdriver import \
EventFiringWebDriver
@@ -21,12 +21,16 @@ def test_selenium(base_url, selenium):
with pytest.raises(Exception) as e:
selenium.get(base_url)
assert 'before_navigate_to' in e.exconly()
- """)
- testdir.quick_qa('--event-listener', 'testing.'
- 'test_webdriver.ConcreteEventListener',
- file_test, passed=1)
+ """
+ )
+ testdir.quick_qa(
+ "--event-listener",
+ "testing." "test_webdriver.ConcreteEventListener",
+ file_test,
+ passed=1,
+ )
class ConcreteEventListener(AbstractEventListener):
def before_navigate_to(self, url, driver):
- raise Exception('before_navigate_to')
+ raise Exception("before_navigate_to")
diff --git a/tox.ini b/tox.ini
index 5dc01408..3b1f8d45 100644
--- a/tox.ini
+++ b/tox.ini
@@ -25,4 +25,5 @@ deps = flake8
commands = flake8 {posargs:.}
[flake8]
+max-line-length = 88
exclude = .eggs,.tox,docs