# -*- coding: utf-8 -*-
"""TODO"""
import os
import sys
from qacode.core.bots.modules.nav_base import NavBase
from qacode.core.exceptions.core_exception import CoreException
from qacode.core.loggers.logger_manager import LoggerManager
from qautils.files import settings
from selenium import webdriver as WebDriver
from selenium.webdriver import DesiredCapabilities
from selenium.webdriver.chrome.options import Options as ChromeOptions
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.touch_actions import TouchActions
from selenium.webdriver.firefox.options import Options as FirefoxOptions
from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
from selenium.webdriver.support.ui import WebDriverWait
[docs]class BotBase(object):
"""Class Base for handle selenium functionality throught this wrapper
curr_caps -- Capabilities class
curr_driver -- WebDriver class
curr_driver_path -- WebDriver browser executable path
curr_driver_wait -- Wait for expected conditions
curr_driver_actions -- Performs actions on elements
curr_driver_touch -- Perform touch actions on elements
navigation -- Bot methods to brigde selenium functions
bot_config -- Bot configuration object
logger_manager -- logger manager class loaded from
LoggerManager object
log -- log class to write messages
"""
settings = None
curr_caps = None
curr_driver = None
curr_driver_path = None
# Browser options
curr_options = None
# Wait for expected conditions
curr_driver_wait = None
# Performs actions on elements
curr_driver_actions = None
# Perform touch actions on elements
curr_driver_touch = None
navigation = None
logger_manager = None
log = None
IS_64BITS = sys.maxsize > 2**32
IS_WIN = os.name == 'nt'
BROWSERS_WITHOUT_OPTIONS = ('iexplorer', 'edge')
def __init__(self, **kwargs):
"""Create new Bot browser based on options object what can be
(help for each option can be found on settings.json)
Arguments:
settings {dict} -- configuracion obtained from JSON
file to dict instance
Raises:
CoreException -- Fail at instance LoggerManager class
CoreException -- settings is None
CoreException -- settings.get('mode') is not in [local, remote]
"""
self.settings = kwargs.get("bot")
# default read from qacode.configs, file named 'settings.json'
if not self.settings:
self.settings = settings().get('bot')
self._load()
def _load(self):
self.logger_manager = LoggerManager(
log_path=self.settings.get('log_output_file'),
log_name=self.settings.get('log_name'),
log_level=self.settings.get('log_level'))
self.log = self.logger_manager.logger
required_keys = [
'mode',
'browser',
'options',
'url_hub',
'drivers_path',
'drivers_names',
'log_name',
'log_output_file',
'log_level',
]
for setting in self.settings.keys():
if setting not in required_keys:
msg = ("Key for config isn't valid for"
" key='{}'").format(setting)
raise CoreException(msg=msg, log=self.log)
# Configure browser settings
browser_name = self.settings.get('browser')
headless_enabled = self.settings.get(
'options').get('headless')
# Instance selenium settings classes
self.curr_caps = self.get_capabilities(
browser_name=browser_name)
self.curr_options = self.get_options(
browser_name=browser_name,
headless_enabled=headless_enabled)
# Open browser based on mode from settings.json
if self.settings.get('mode') == 'local':
self.mode_local(browser_name=browser_name)
elif self.settings.get('mode') == 'remote':
self.mode_remote(browser_name=browser_name)
else:
msg = "Bad mode selected, mode={}".format(
self.settings.get('mode'))
raise CoreException(msg=msg, log=self.log)
# Instance all needed for BotBase instance
self.curr_driver_wait = WebDriverWait(self.curr_driver, 10)
self.curr_driver_actions = ActionChains(self.curr_driver)
self.curr_driver_touch = TouchActions(self.curr_driver)
self.navigation = NavBase(
self.curr_driver,
self.log,
driver_wait=self.curr_driver_wait,
driver_actions=self.curr_driver_actions,
driver_touch=self.curr_driver_touch)
[docs] def get_capabilities(self, browser_name='chrome'):
"""Instance DesiredCapabilities class from selenium and return it
Keyword Arguments:
browser_name {str} -- name of a valid browser name for selenium
(default: {'chrome'})
Raises:
CoreException -- if name of browser isn't supported
Returns:
[DesiredCapabilities] -- DesiredCapabilities inherit
class instanced for one browser
"""
capabilities = None
try:
capabilities = {
"chrome": DesiredCapabilities.CHROME.copy(),
"firefox": DesiredCapabilities.FIREFOX.copy(),
"iexplorer": DesiredCapabilities.INTERNETEXPLORER.copy(),
"edge": DesiredCapabilities.EDGE.copy(),
}[browser_name]
except KeyError:
msg = 'Bad browser selected at load options'
raise CoreException(msg=msg, log=self.log)
return capabilities
def __check_driver_ready__(self, driver_path, capabilities, options):
"""Some checks to ensure driver path, caps and options
are ready to be used
"""
if driver_path is None:
driver_path = self.curr_driver_path
if capabilities is None:
capabilities = self.curr_caps
if options is None:
options = self.curr_options
[docs] def get_options(self, browser_name='chrome', headless_enabled=False):
"""Instance Options class from selenium and return it
Keyword Arguments:
browser_name {str} -- name of a valid browser name for selenium
(default: {'chrome'})
headless_enabled {bool} -- allow to configure --headless param
(default: {False})
Raises:
CoreException -- if name of browser isn't supported
Returns:
[Options] -- Options inherit
class instanced for one browser
"""
options = None
msg_not_conf = ("get_options | : doesn't have configurations"
" for browser='{}'".format(browser_name))
try:
options = {
"chrome": ChromeOptions(),
"firefox": FirefoxOptions(),
}[browser_name]
if headless_enabled:
options.add_argument("--headless")
except KeyError:
if browser_name in self.BROWSERS_WITHOUT_OPTIONS:
self.log.debug(msg_not_conf)
else:
raise CoreException(msg="Bad browser selected", log=self.log)
return options
[docs] def driver_name_filter(self, driver_name=None):
"""Filter names of driver to search selected on config list
Keyword Arguments:
driver_name {str} -- driver_name_format is
{driver_name}{arch}{os} (default: {None})
Raises:
CoreException -- driver_name param is None
CoreException -- driver_name not in
Returns:
str -- name of driver
(example: chromedriver_32.exe)
"""
driver_name_format = '{}{}{}'
if driver_name is None:
raise CoreException(msg='driver_name received it\'s None')
driver_name_format = driver_name_format.format(
driver_name, '{}', '{}')
if self.IS_WIN:
driver_name_format = driver_name_format.format('{}', '.exe')
else:
driver_name_format = driver_name_format.format('{}', '')
if self.IS_64BITS:
driver_name_format = driver_name_format.format('driver_64')
else:
driver_name_format = driver_name_format.format('driver_32')
for name in self.settings.get('drivers_names'):
if name.endswith(driver_name_format):
return driver_name_format
msg = 'Driver name not found {}'.format(driver_name_format)
raise CoreException(msg=msg, log=self.log)
[docs] def get_driver_chrome(self, driver_path=None, capabilities=None,
options=None):
"""Open WebDriver selenium based on Chrome browser
Keyword Arguments:
driver_path {str} -- Path for driver binary path
(default: {None})
capabilities {DesiredCapabilities} -- Capabilities for browser
(default: {None})
options {Options} -- Options for browser (default: {None})
Returns:
[WebDriver.Chrome] -- WebDriver opened and ready to be used
"""
self.__check_driver_ready__(driver_path, capabilities, options)
return WebDriver.Chrome(
executable_path=driver_path,
desired_capabilities=capabilities,
chrome_options=options)
[docs] def get_driver_firefox(self, driver_path=None, capabilities=None,
options=None):
"""Open WebDriver selenium based on Firefox browser
Keyword Arguments:
driver_path {str} -- Path for driver binary path
(default: {None})
capabilities {DesiredCapabilities} -- Capabilities for browser
(default: {None})
options {Options} -- Options for browser (default: {None})
Returns:
[WebDriver.Firefox] -- WebDriver opened and ready to be used
"""
self.__check_driver_ready__(driver_path, capabilities, options)
return WebDriver.Firefox(
executable_path=driver_path,
capabilities=capabilities,
firefox_options=options)
[docs] def get_driver_iexplorer(self, driver_path=None, capabilities=None):
"""Open WebDriver selenium based on InternetExplorer browser
Keyword Arguments:
driver_path {str} -- Path for driver binary path
(default: {None})
capabilities {DesiredCapabilities} -- Capabilities for browser
(default: {None})
Returns:
[WebDriver.Ie] -- WebDriver opened and ready to be used
"""
self.__check_driver_ready__(driver_path, capabilities)
return WebDriver.Ie(
executable_path=driver_path,
capabilities=capabilities)
[docs] def get_driver_edge(self, driver_path=None, capabilities=None):
"""Open WebDriver selenium based on Edge browser
Keyword Arguments:
driver_path {str} -- Path for driver binary path
(default: {None})
capabilities {DesiredCapabilities} -- Capabilities for browser
(default: {None})
options {Options} -- Options for browser (default: {None})
Returns:
[WebDriver.Edge] -- WebDriver opened and ready to be used
"""
self.__check_driver_ready__(driver_path, capabilities)
return WebDriver.Edge(
executable_path=driver_path,
capabilities=capabilities)
[docs] def mode_local(self, browser_name='chrome'):
"""Open new brower on local mode
Raises:
CoreException -- driver_name on config JSON
file is not valid value
"""
driver_name = self.driver_name_filter(driver_name=browser_name)
# TODO: Need it ? maybe a test for this ?
self.curr_driver_path = os.path.abspath("{}/{}".format(
self.settings.get('drivers_path'),
driver_name))
sys.path.append(self.curr_driver_path)
self.log.debug('Starting browser with mode : LOCAL ...')
try:
self.curr_driver = {
"chrome": self.get_driver_chrome(),
"firefox": self.get_driver_firefox(),
"iexplorer": self.get_driver_iexplorer(),
"edge": self.get_driver_edge(),
}[browser_name]
except KeyError:
raise CoreException(
msg=("config file error, SECTION=bot, KEY=browser isn't "
"valid value: {}".format(browser_name)),
log=self.log)
self.log.info('Started browser with mode : REMOTE OK')
[docs] def mode_remote(self, browser_name='chrome'):
"""Open new brower on remote mode
Raises:
CoreException -- browser name is not in valid values list
"""
url_hub = self.settings.get('url_hub')
self.log.debug('Starting browser with mode : REMOTE ...')
self.curr_driver = RemoteWebDriver(
command_executor=url_hub,
desired_capabilities=self.curr_caps,
options=self.curr_options
)
self.log.info('Started browser with mode : REMOTE OK')
[docs] def close(self):
"""Close curr_driver browser"""
self.log.debug('Closing browser...')
self.curr_driver.quit()
self.log.info('Closed browser')
def __repr__(self):
"""Show basic properties for this object"""
_settings = None
if self.settings.get("mode") == "remote":
hidden = {
"drivers_names": [
"Hidden at '__repr__' for remote drivers..."]}
_settings = self.settings.copy()
_settings.update(hidden)
return ("BotBase: IS_WIN={}, IS_64BITS={}\n"
" navigation={} \n"
" settings={}").format(
self.IS_WIN,
self.IS_64BITS,
repr(self.navigation),
self.settings)