Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions config/config.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,29 @@
# may be changed in different environments
BASE_URL = "https://go.mail.ru/"
import os

username = None
access_key = None

try:
username = os.environ["SAUCE_USERNAME"]
access_key = os.environ["SAUCE_ACCESS_KEY"]
except KeyError:
pass


class Config:
"""Configuration class."""

base_url = None
screen_on_fail = None
browser = None
all_browser = None
platform = None
browser_version = None
has_saucelabs_connect = username and access_key
saucelabs_connection_string = f"https://{username}:{access_key}@ondemand.saucelabs.com/wd/hub"

@classmethod
def setup(cls, cfg):
"""Get values from config file."""
for key, value in cfg.items():
setattr(cls, key, value)
89 changes: 45 additions & 44 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,26 @@
import os
import pytest
import datetime
from selenium import webdriver

AVAILABLE_BROWSERS = {
"chrome": "chrome",
"firefox": "firefox"
}
from config.config import Config
from src.selenium.browser_factory import BrowserFactory
from src.selenium.browsers.available_browsers import AvailableBrowsers
from src.selenium.capabilities import Capabilities


@pytest.fixture(scope="function")
@pytest.fixture(scope="class")
def driver(request, param):
browser_type = request.config.getoption("--browser") or param
save_screen = request.config.getoption("--screenonfail") or param
# use param if its parametrized run
browser_type = Config.browser or param
save_screen = Config.browser

username = None
access_key = None
capabilities = Capabilities(browser_type, version="", page_load_strategy="none")

try:
username = os.environ["SAUCE_USERNAME"]
access_key = os.environ["SAUCE_ACCESS_KEY"]
except KeyError:
pass

has_sauce_lab = username and access_key
caps = {}
command_executor = f"https://{username}:{access_key}@ondemand.saucelabs.com/wd/hub"

if AVAILABLE_BROWSERS["chrome"] in browser_type:
# use "none" only for Chrome, because there some trouble in geckrodriver with this strategy
caps.update({"pageLoadStrategy": "none"})
if has_sauce_lab:
caps.update({"browserName": "chrome", "platform": "Windows 10", "version": "71.0"})
browser = webdriver.Remote(desired_capabilities=caps, command_executor=command_executor)
else:
browser = webdriver.Chrome(desired_capabilities=caps)

elif AVAILABLE_BROWSERS["firefox"] in browser_type:
if has_sauce_lab:
caps.update({"browserName": "firefox", "platform": "Windows 10", "version": "64.0"})
browser = webdriver.Remote(desired_capabilities=caps, command_executor=command_executor)
else:
browser = webdriver.Firefox(desired_capabilities=caps)

else:
raise RuntimeError(f"Unknown browser ${browser_type}")
if Config.has_saucelabs_connect:
command_executor = Config.saucelabs_connection_string
capabilities.set_capability(Capabilities.command_executor, command_executor)

browser = BrowserFactory.get_driver(capabilities)
yield browser

if request.node.rep_call.failed and save_screen:
Expand All @@ -59,7 +34,7 @@ def driver(request, param):
browser.quit()


@pytest.fixture(scope="function")
@pytest.fixture(scope="class")
def param(request):
# we don"t have request.param when not parametrize tests with pytest_generate_tests
try:
Expand All @@ -71,22 +46,48 @@ def param(request):
def pytest_addoption(parser):
parser.addoption("--browser",
action="append",
help=f"Browser. Valid options are {AVAILABLE_BROWSERS.keys()}")
help=f"Browser. Valid options are {AvailableBrowsers.get_available_browsers()}")

parser.addoption("--allbrowsers",
parser.addoption("--all_browsers",
default=False,
action="store_true",
help="Run tests in all available browsers")

parser.addoption("--screenonfail",
parser.addoption("--browser_version",
default="Windows",
action="store_true",
help="Browsers version for run tests")

parser.addoption("--platform",
default="Windows",
action="store_true",
help="OS where run tests")

parser.addoption("--screen_on_fail",
default=False,
action="store_true",
help="Save screenshot on test fail")

parser.addoption("--base_url",
default="https://go.mail.ru/",
action="store_true",
help="base url for tests")


def pytest_configure(config):
Config.setup({
"base_url": config.getoption("--base_url"),
"screen_on_fail": config.getoption("--screen_on_fail"),
"browser": config.getoption("--browser"),
"all_browsers": config.getoption("--all_browsers"),
"browser_version": config.getoption("--browser_version"),
"platform": config.getoption("--platform"),
})


def pytest_generate_tests(metafunc):
if metafunc.config.getoption("allbrowsers") and not metafunc.config.getoption("browser"):
metafunc.parametrize("param", AVAILABLE_BROWSERS.keys())
if Config.all_browser and not Config.browser:
metafunc.parametrize("param", AvailableBrowsers.get_available_browsers())


# https://automated-testing.info/t/pytest-krivo-otobrazhaet-kejsy-parametrizaczii-na-russkom/17908
Expand Down
Empty file added src/selenium/__init__.py
Empty file.
28 changes: 28 additions & 0 deletions src/selenium/browser_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from typing import Union
from src.selenium.browsers.available_browsers import AvailableBrowsers
from src.selenium.capabilities import Capabilities


class BrowserFactory(object):
"""Factory class for WebDriver"""

def __init__(self):
pass

@staticmethod
def get_driver(capabilities: Capabilities) -> Union[AvailableBrowsers.get_available_browser_constructors()]:
browser_name = capabilities.get_capability(Capabilities.browser_name)

if browser_name not in AvailableBrowsers.get_available_browsers():
raise RuntimeError(f"Unknown browser name: {browser_name}")

webdriver_constructor = None
browser_constructors = AvailableBrowsers.get_available_browser_constructors()
for constructor in browser_constructors:
if browser_name == constructor.get_browser_name():
webdriver_constructor = constructor
break

webdriver = webdriver_constructor()
webdriver.set_capability(capabilities)
return webdriver.create_driver_instance()
Empty file.
38 changes: 38 additions & 0 deletions src/selenium/browsers/abstract_browser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from src.selenium.capabilities import Capabilities
from abc import ABC, abstractmethod

import typing


class AbstractBrowser(ABC):

browser_name = None

def __init__(self):
self.capabilities: Capabilities = None
self.browser_name: str = None

def _capabilities_to_dict(self) -> dict:
return self.capabilities.to_dictionary()

@classmethod
def _set_browser_name(cls, name: str) -> None:
cls.browser_name = name

@classmethod
def get_browser_name(cls) -> str:
return cls.browser_name

def set_capability(self, caps: Capabilities = None) -> None:
if caps:
self.capabilities = caps

def is_local_browser(self) -> bool:
return bool(self.capabilities.command_executor)

@abstractmethod
def create_driver_instance(self) -> typing.NoReturn:
"""
Each subclass of AbstractBrowser should implement this method. By design.
"""
raise NotImplemented("init_driver not implemented")
22 changes: 22 additions & 0 deletions src/selenium/browsers/available_browsers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from typing import List, Type, Union
from src.selenium.browsers.chrome_browser import ChromeBrowser
from src.selenium.browsers.firefox_browser import FirefoxBrowser


class AvailableBrowsers(object):
CHROME = 'chrome'
FIREFOX = 'firefox'

@classmethod
def get_available_browsers(cls) -> List[str]:
available_browsers = []

for attribute in dir(AvailableBrowsers):
if not attribute.startswith("__") and not callable(getattr(AvailableBrowsers, attribute)):
available_browsers.append(attribute)

return available_browsers

@staticmethod
def get_available_browser_constructors() -> List[Type[Union[ChromeBrowser, FirefoxBrowser]]]:
return [ChromeBrowser, FirefoxBrowser]
21 changes: 21 additions & 0 deletions src/selenium/browsers/chrome_browser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from src.selenium.browsers.abstract_browser import AbstractBrowser
from src.selenium.browsers.available_browsers import AvailableBrowsers
from src.selenium.capabilities import Capabilities
from selenium.webdriver import Chrome, Remote


_DEFAULT_CHROME_CAPS = {"page_load_strategy": "none", "browser_name": AvailableBrowsers.CHROME, "version": "71.0"}


class ChromeBrowser(AbstractBrowser):
def __init__(self):
self.browser_name = AvailableBrowsers.CHROME
self.capabilities = Capabilities(**_DEFAULT_CHROME_CAPS)
super().__init__()

def create_driver_instance(self) -> Chrome:
if self.is_local_browser():
return Chrome(desired_capabilities=self._capabilities_to_dict())

return Remote(desired_capabilities=self._capabilities_to_dict(),
command_executor=self.capabilities.command_executor)
18 changes: 18 additions & 0 deletions src/selenium/browsers/firefox_browser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from src.selenium.browsers.abstract_browser import AbstractBrowser
from src.selenium.browsers.available_browsers import AvailableBrowsers
from selenium.webdriver import Firefox, Remote

_DEFAULT_FIREFOX_CAPS = {"pageLoadStrategy": "eager", "browserName": AvailableBrowsers.FIREFOX, "version": "64.0"}


class FirefoxBrowser(AbstractBrowser):
def __init__(self):
self._set_browser_name(AvailableBrowsers.FIREFOX)
super().__init__()

def create_driver_instance(self) -> Firefox:
if self.is_local_browser():
return Firefox(capabilities=self._capabilities_to_dict())

return Remote(desired_capabilities=self._capabilities_to_dict(),
command_executor=self.capabilities.command_executor)
23 changes: 23 additions & 0 deletions src/selenium/capabilities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
class Capabilities(object):
# link between fields of Capability and capability names
browser_name = "browserName"
version = "version"
page_load_strategy = "pageLoadStrategy"
command_executor = "command_executor"

def __init__(self, browser_name, version, page_load_strategy, command_executor=None):
self._capability = {
Capabilities.browser_name: browser_name,
Capabilities.version: version,
Capabilities.page_load_strategy: page_load_strategy,
Capabilities.command_executor: command_executor,
}

def set_capability(self, key, value) -> None:
self._capability[key] = value

def get_capability(self, key):
return self._capability[key]

def to_dictionary(self) -> dict:
return self._capability