Skip to content

Commit

Permalink
Browserstack little improvements and Edge fix about options (#260)
Browse files Browse the repository at this point in the history
* Fix settings of Edge capabilities

KEY attribute does not exist in Edge options before selenium 4.X.
see https://github.com/SeleniumHQ/selenium/blob/selenium-3.141.0/py/selenium/webdriver/edge/options.py

* Update browserstack URL about API endpoint

* Add ability to display public URL about browserstack report

For several organizations it is useful to access to the browserstack
report without any authentication.
  • Loading branch information
nfk committed Jan 30, 2021
1 parent 5c5be0e commit 68479ff
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 9 deletions.
18 changes: 18 additions & 0 deletions docs/user_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,24 @@ See the
for additional configuration that can be set using ``--capability`` command line
arguments.

Test result links
~~~~~~~~~~~~~~~~~

By default, links to BrowserStack jobs are only visible to users logged in to the account
that ran the job. You can choose to display a public URL in the pytest summary.

This can be configured by setting the ``BROWSERSTACK_JOB_ACCESS`` environment variable or by
adding ``report`` section in ``.browserstack`` configuration file.
Allowed values are ``browser_url`` for private access and ``public_url`` for everyone.

An example using a configuration file:

.. code-block:: ini
[report]
job_access = browser_url
TestingBot
----------

Expand Down
21 changes: 19 additions & 2 deletions pytest_selenium/drivers/browserstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
import pytest

from pytest_selenium.drivers.cloud import Provider
from pytest_selenium.exceptions import MissingCloudSettingError


class BrowserStack(Provider):

API = "https://www.browserstack.com/automate/sessions/{session}.json"
API = "https://api.browserstack.com/automate/sessions/{session}.json"

@property
def auth(self):
Expand All @@ -31,6 +32,21 @@ def key(self):
"key", ["BROWSERSTACK_ACCESS_KEY", "BROWSERSTACK_PSW"]
)

@property
def job_access(self):
"""Get job url field, private(required authentication) or public."""
try:
field = self.get_setting(
key="job_access",
envs=["BROWSERSTACK_JOB_ACCESS"],
section="report",
allowed_values=["browser_url", "public_url"],
)
except MissingCloudSettingError:
field = "browser_url"

return field


@pytest.mark.optionalhook
def pytest_selenium_runtest_makereport(item, report, summary, extra):
Expand All @@ -47,7 +63,7 @@ def pytest_selenium_runtest_makereport(item, report, summary, extra):

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"][provider.job_access]
# Add the job URL to the summary
summary.append("{0} Job: {1}".format(provider.name, job_url))
pytest_html = item.config.pluginmanager.getplugin("html")
Expand Down Expand Up @@ -79,6 +95,7 @@ def pytest_selenium_runtest_makereport(item, report, summary, extra):

def driver_kwargs(request, test, capabilities, **kwargs):
provider = BrowserStack()
assert provider.job_access
capabilities.setdefault("name", test)
capabilities.setdefault("browserstack.user", provider.username)
capabilities.setdefault("browserstack.key", provider.key)
Expand Down
24 changes: 20 additions & 4 deletions pytest_selenium/drivers/cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
import os
import configparser

from pytest_selenium.exceptions import MissingCloudCredentialError
from pytest_selenium.exceptions import (
MissingCloudCredentialError,
MissingCloudSettingError,
InvalidCloudSettingError,
)


class Provider(object):
Expand All @@ -22,13 +26,25 @@ def config(self):

def get_credential(self, key, envs):
try:
return self.config.get("credentials", key)
return self.get_setting(key, envs, "credentials")
except MissingCloudSettingError:
raise MissingCloudCredentialError(self.name, key, envs)

def get_setting(self, key, envs, section, allowed_values=None):
try:
value = self.config.get(section, key)
except (configparser.NoSectionError, configparser.NoOptionError, KeyError):
for env in envs:
value = os.getenv(env)
if value:
return value
raise MissingCloudCredentialError(self.name, key, envs)
break

if value is None:
raise MissingCloudSettingError(self.name, key, envs)
if allowed_values and value not in allowed_values:
raise InvalidCloudSettingError(self.name, key, value)

return value

def uses_driver(self, driver):
return driver.lower() == self.name.lower()
16 changes: 14 additions & 2 deletions pytest_selenium/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,22 @@
import pytest


class MissingCloudCredentialError(pytest.UsageError):
class MissingCloudSettingError(pytest.UsageError):
def __init__(self, driver, key, envs):
super(MissingCloudCredentialError, self).__init__(
super(MissingCloudSettingError, 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)
)


class MissingCloudCredentialError(MissingCloudSettingError):
pass


class InvalidCloudSettingError(pytest.UsageError):
def __init__(self, driver, key, value):
super(InvalidCloudSettingError, self).__init__(
"{0} {1} invalid value `{2}`, see the documentation for how "
"to use this parameter.".format(driver, key, value)
)
2 changes: 1 addition & 1 deletion pytest_selenium/pytest_selenium.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def capabilities(
key = firefox_options.KEY
options = firefox_options.to_capabilities()
elif browser == "EDGE":
key = edge_options.KEY
key = getattr(edge_options, "KEY", None)
options = edge_options.to_capabilities()
if all([key, options]):
capabilities[key] = _merge(capabilities.get(key, {}), options.get(key, {}))
Expand Down
6 changes: 6 additions & 0 deletions testing/test_browserstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,9 @@ def test_invalid_credentials_file(failure, monkeypatch, tmpdir):
out = failure()
messages = ["Invalid username or password", "basic auth failed"]
assert any(message in out for message in messages)


def test_invalid_job_access_value(failure, monkeypatch, tmpdir):
monkeypatch.setattr(os.path, "expanduser", lambda p: str(tmpdir))
tmpdir.join(".browserstack").write("[report]\njob_access=foo")
assert "BrowserStack job_access invalid value `foo`" in failure()

0 comments on commit 68479ff

Please sign in to comment.