Source code for holmium.core.config

"""
configuration objects for holmium
"""
import inspect
import os
import sys

import jinja2
from selenium import webdriver
from selenium.webdriver import FirefoxProfile


[docs]class Config(dict): """Dictionary like helper class for maintaining test data configurations per environment. :class:`holmium.core.TestCase` looks for either a config.json or config.py file in the same directory as the test file, and will make a ``config`` object available to the test case instance. The :class:`holmium.core.Config` object is aware of the environment and will return the config variable from that environment or from the `default` key. Values in the config file can use :class:`jinja2.Template` templates to access either values from itself, environment variables or a select magic holmium variables: ``holmium.environment``, ``holmium.browser``, ``holmium.user_agent`` and ``holmium.remote``. Example config structure (which uses a magic variable ``holmium.environment`` and an environment variable ``$PATH``). JSON .. code-block:: json { "default": { "path": "{{PATH}}", "login_url": "{{url}}/{{holmium.environment}}/login", "username": "{{holmium.environment}}user" }, "production": { "url": "http://prod.com", "password": "sekret" }, "development": { "url": "http://dev.com", "password": "password" } } Python .. code-block:: python config = { { 'default': { 'path':"{{PATH}}", 'login_url': '{{url}}/{{holmium.environment}}/login'}, 'username' : '{{holmium.environment}}user' }, 'production': { 'url':'http://prod.com', 'password': 'sekret' }, 'development': { 'url':'http://dev.com', 'password': 'password' } } } When accessing ``self.config`` within a test, due to the default: * ``self.config['path']`` will always return the value of the environment variable `PATH`, * ``self.config['password']`` will always return 'sekret' if ``HO_ENV`` or ``--holmium-env`` are ``production``: * ``self.config['username']`` will return ``productionuser`` * ``self.config['password']`` will return ``sekret`` * ``self.config['login_url']`` will return ``http://prod.com/production/login`` if ``HO_ENV`` or ``--holmium-env`` are ``development``: * ``self.config['username']`` will return ``developmentuser`` * ``self.config['password']`` will return ``password`` * ``self.config['login_url']`` will return ``http://dev.com/development/login`` """ # pylint: disable=dangerous-default-value def __init__(self, dct, environment={"holmium": {"environment": "development"}}): self.env = environment dict.__init__(self, dct) def __getitem__(self, key): """ override to evaluate the values through the template """ def __render(item, context): """ renders the string given the context using the jinja template """ def _check_string_type(_item): """ meh python2/3 stuff. """ if isinstance(_item, str): return True elif sys.version_info < (3, 0, 0): if isinstance(_item, eval("unicode")): return True return False if _check_string_type(item): template = jinja2.Template(item) rendered = template.render(context) if rendered != item: return __render(rendered, context) else: return rendered else: return item env_ctx = dict.setdefault(self, self.env["holmium"]["environment"], {}) default_ctx = dict.setdefault(self, "default", {}) try: item = env_ctx[key] except KeyError: item = default_ctx[key] context = dict(self) context.update(os.environ) context.update(self.env) context.update(default_ctx) context.update(env_ctx) return __render(item, context) def __setitem__(self, key, value): """ override to put the value in the right environment bucket """ sub_dict = dict.setdefault(self, self.env["holmium"]["environment"], {}) sub_dict[key] = value
BROWSER_MAPPING = { "chrome": webdriver.Chrome, "edge": webdriver.Edge, "firefox": webdriver.Firefox, "ie": webdriver.Ie, "ipad": webdriver.Remote, "iphone": webdriver.Remote, "remote": webdriver.Remote, "safari": webdriver.Safari, "webkitgtk": webdriver.WebKitGTK, } #: CAPABILITIES = { "chrome": webdriver.DesiredCapabilities.CHROME, "edge": webdriver.DesiredCapabilities.EDGE, "firefox": webdriver.DesiredCapabilities.FIREFOX, "ie": webdriver.DesiredCapabilities.INTERNETEXPLORER, "ipad": webdriver.DesiredCapabilities.IPAD, "iphone": webdriver.DesiredCapabilities.IPHONE, "safari": webdriver.DesiredCapabilities.SAFARI, "webkitgtk": webdriver.DesiredCapabilities.WEBKITGTK, }
[docs]class HolmiumConfig(dict): """ utility class for storing holmium configuration options strictly. The class behaves like a dictionary after construction with the additional behavior that any attributes set on it are available as keys in the dictionary and vice versa. """ # pylint: disable=unused-argument,too-many-arguments,star-args def __init__( self, browser, remote, capabilities, user_agent, environment, ignore_ssl, fresh_instance, ): data = {} for arg in inspect.getfullargspec(HolmiumConfig.__init__).args[1:]: setattr(self, arg, locals()[arg]) data[arg] = locals()[arg] super().__init__(**data) def __setattr__(self, key, value): super().__setattr__(key, value) super().__setitem__(key, value) def __setitem__(self, key, value): super().__setattr__(key, value) super().__setitem__(key, value)
# pylint: disable=too-few-public-methods class DriverConfig: """ base class for configuring a webdriver """ def __call__(self, config, args): return args class FirefoxConfig(DriverConfig): """ configuration for firefox """ def __call__(self, config, args): profile = FirefoxProfile() if config.user_agent: profile.set_preference("general.useragent.override", config.user_agent) if config.ignore_ssl: profile.accept_untrusted_certs = True args["firefox_profile"] = profile args["capabilities"] = args["desired_capabilities"] args.pop("desired_capabilities") return args class ChromeConfig(DriverConfig): """ configuration for chrome """ def __call__(self, config, args): args["desired_capabilities"].setdefault("chrome.switches", []) if config.user_agent: args["desired_capabilities"]["chrome.switches"].append( "--user-agent=%s" % config.user_agent ) if config.ignore_ssl: args["desired_capabilities"]["chrome.switches"].append( "--ignore-certificate-errors" ) return super().__call__(config, args) class IeConfig(DriverConfig): """ configuration for internet explorer """ def __call__(self, config, args): args["desired_capabilities"] = args.pop("desired_capabilities", {}) return super().__call__(config, args) class RemoteConfig(DriverConfig): """ configuration for remote driver (and anything that doesnt have a specific configuration) """ def __call__(self, config, args): if config.browser == "firefox": if "firefox_profile" in args: args["browser_profile"] = args["firefox_profile"] args.pop("firefox_profile") args["desired_capabilities"] = args["capabilities"] args.pop("capabilities") args["command_executor"] = config.remote return super().__call__(config, args) CONFIGURATOR_MAPPER = { "firefox": FirefoxConfig(), "chrome": ChromeConfig(), "ie": IeConfig(), "remote": RemoteConfig(), } def configure(config): """ sets up the arguments required by the specific :class:`selenium.webdriver.Webdriver` instance based on the :class:`holmium.core.config.HolmiumConfig` object that is passed in. """ if config.browser not in BROWSER_MAPPING.keys(): raise RuntimeError("unknown browser %s" % config.browser) merged_capabilities = CAPABILITIES[config.browser] merged_capabilities.update(config.capabilities) args = {"desired_capabilities": merged_capabilities} if config.browser in CONFIGURATOR_MAPPER: args = CONFIGURATOR_MAPPER[config.browser](config, args) if config.remote: args = CONFIGURATOR_MAPPER["remote"](config, args) return args