diff --git a/.gitignore b/.gitignore index 281457c5..3fc850a4 100755 --- a/.gitignore +++ b/.gitignore @@ -103,9 +103,11 @@ ENV/ # QACODE ignores qacode/configs/settings.json -tests/reports/* -tests/reports/coverage/* -tests/reports/benchmarks/benchmark.*.svg +qacode_old/configs/settings.json + +tests_old/reports/* +tests_old/reports/coverage/* +tests_old/reports/benchmarks/benchmark.*.svg # Visual Studio .vs/ # Visual code diff --git a/MANIFEST.in b/MANIFEST.in index e1972a20..074b406f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,20 +1,20 @@ -include *.in -include *.md -include *.pyproj -include *.rst -include *.yml -include LICENSE -include pytest.ini -include tox.ini -include conftest.py -include requirements*.txt -recursive-include logs *.gitkeep -recursive-include qacode *.json -recursive-include tests *.py -recursive-include source-docs *.rst -recursive-include source-docs *.py -recursive-include source-docs *.yml -recursive-include source-docs *.bat -recursive-include source-docs Makefile -recursive-include .github *.md -recursive-include .circleci *.yml \ No newline at end of file +include *.in +include *.md +include *.py +include *.rst +include *.txt +include *.yml +include .circleci/config.yml +include source-docs/Makefile +include source-docs/make.bat +include pytest.ini +include tox.ini +recursive-include docs *.gitkeep +recursive-include logs *.gitkeep +recursive-include qacode *.json +recursive-include qacode *.py +recursive-include tests *.py +recursive-include tests *.xml +recursive-include source-docs *.rst +recursive-include source-docs *.py +exclude tests/reports/coverage.xml \ No newline at end of file diff --git a/README.rst b/README.rst index f7a28aaf..ccde972b 100755 --- a/README.rst +++ b/README.rst @@ -122,8 +122,8 @@ Getting Started SETTINGS = settings( - file_path="/home/user/config/dir/", - file_name="settings.json" + path="/home/user/config/dir/", + name="settings.json" ) diff --git a/conftest.py b/conftest.py index 35ed78b4..75b7409b 100644 --- a/conftest.py +++ b/conftest.py @@ -3,7 +3,7 @@ import pytest import sys -from qacode.core.bots.bot_base import BotBase +from qacode.core.bots.bot import Bot from qacode.utils import settings @@ -11,14 +11,24 @@ @pytest.fixture(scope="session") -def browser(): - CFG = settings(file_path="qacode/configs/", file_name="settings.json") - bot = None +def bot(): + CFG = settings(path="qacode/configs/", name="settings.json") + bot = Bot(**CFG) try: - bot = BotBase(**CFG) yield bot except Exception as err: raise err + + +@pytest.fixture(scope="session") +def browser(): + CFG = settings(path="qacode/configs/", name="settings.json") + bot = Bot(**CFG) + try: + yield bot.browser_create(bot.config.browsers[0]) + except Exception as err: + raise err finally: - if bot: - bot.close() + if bot.browsers[0].driver is not None: + bot.browsers[0].close() + # else: useless browser, never opened diff --git a/qacode/configs/settings.json b/qacode/configs/settings.json index 511b2dc2..8aaaef88 100644 --- a/qacode/configs/settings.json +++ b/qacode/configs/settings.json @@ -1,29 +1,47 @@ -{ "bot": { - "log_output_file": "logs/", - "log_name": "qacode", - "log_level": "INFO", - "mode": "remote", - "browser": "chrome", - "options": { "headless": false }, - "url_hub": "http://netzulo.tk:11000/wd/hub", +{ + "log":{ + "path": "logs/", "name": "qacode", "level": "INFO" + }, + "bot":{ + "hub_url": "http://146.255.103.8:11000/wd/hub", "drivers_path": "../qadrivers", - "drivers_names": [ - "chromedriver_32.exe", - "chromedriver_64.exe", - "chromedriver_32", - "chromedriver_64", - "firefoxdriver_32.exe", - "firefoxdriver_64.exe", - "firefoxdriver_64", - "firefoxdriver_32", - "iexplorerdriver_32.exe", - "iexplorerdriver_64.exe", - "edgedriver_32.exe", - "edgedriver_64.exe", - "operadriver_32.exe", - "operadriver_64.exe", - "operadriver_32", - "operadriver_64" + "drivers_names": { + "chrome": "chromedriver_64.exe", + "firefox": "firefoxdriver_64.exe", + "opera": "operadriver_64.exe", + "iexplorer": "iexplorerdriver_64.exe", + "edge": "edgedriver_64.exe" + }, + "browsers": [ + {"browser": "chrome", "mode": "remote", "options": {"headless": false}}, + {"browser": "chrome", "mode": "local", "options": {"headless": false}} + ], + "pages": [ + {"name": "login", "url": "http://146.255.103.8:83/qacode/login"}, + {"name": "logout", "url": "http://146.255.103.8:83/qacode/logout"}, + {"name": "inputs", "url": "http://146.255.103.8:83/qacode/forms/inputs"}, + {"name": "lists", "url": "http://146.255.103.8:83/qacode/data/lists"} + ], + "controls": [ + { "name": "form", "selector": "#frmLogin", "pages": ["login"]}, + { "name": "username", "selector": "#txtUsername-field", "pages": ["login"]}, + { "name": "password", "selector": "#txtPassword-field", "pages": ["login"]}, + { "name": "submit", "selector": "#btnLogin", "pages": ["login"]}, + { "name": "btn_logout", "selector": "a[href='/qacode/logout']", "pages": ["logout"]}, + { "name": "btn_login", "selector": "a[href='/qacode/login']", "pages": ["logout"]}, + { "name": "title_buttons", "selector": "#titleButtons", "pages": ["inputs"]}, + { "name": "btn_invisible", "selector": "#btnInvisibleAtTimeout", "pages": ["inputs"]}, + { "name": "btn_visible", "selector": "#btnVisibleAtTimeout", "pages": ["inputs"]}, + { "name": "input_text", "selector": "#txtTest001", "pages": ["inputs"]}, + { "name": "dd_base", "selector": "#txtTest002", "pages": ["inputs"]}, + { "name": "dd_multiple", "selector": "#txtTest003", "pages": ["inputs"]}, + { "name": "lst_ordered", "selector": "ol:nth-child(1)", "pages": ["lists"]}, + { "name": "lst_ordered_child", "selector": "li", "pages": ["lists"]}, + { "name": "dd_menu_data", "selector": "#ddData", "pages": ["lists"]}, + { "name": "dd_menu_data_lists", "selector": "a[href='/qacode/data/lists']", "pages": ["lists"]}, + { "name": "tbl_ok", "selector": "#tblOk", "pages": ["lists"]}, + { "name": "tbl_html5_ok", "selector": "#tblHtml5Ok", "pages": ["lists"]}, + { "name": "tbl_html_tbodies_ok", "selector": "#tblHtml5OkTbodies", "pages": ["lists"]} ] }, "testlink": { @@ -34,96 +52,5 @@ }, "dev_key": "1bfd2ef4ceda22b482b12f2b25457495", "log_level":"INFO" - }, - "tests": { - "skip":{ - "test_configs": true, - "browsers": { - "chrome": {"local":true, "remote":false}, - "firefox": {"local":true, "remote":true}, - "iexplorer": {"local":true, "remote":true}, - "edge": {"local":true, "remote":true}, - "opera": {"local":true, "remote":false} - }, - "bot_multiple": false, - "bot_unique": false, - "bot_navigations": false, - "web_controls": { - "control_base": false, - "control_form": false, - "control_dropdown": false, - "control_table": false - }, - "web_pages": false, - "benchmarks": true - }, - "apps": [ - { "name": "qadmin", - "pages": [ - { "name": "qacode_login", - "url": "http://netzulo.tk:83/qacode/login", - "locator": "css selector", - "go_url": false, - "wait_url": 0, - "maximize": false, - "controls": [ - { "name": "form_login", "selector": "#frmLogin", "on_instance_search": true, "on_instance_load": false }, - { "name": "txt_username", "selector": "#txtUsername-field", "on_instance_search": true, "on_instance_load": false }, - { "name": "txt_password", "selector": "#txtPassword-field", "on_instance_search": true, "on_instance_load": false }, - { "name": "btn_submit", "selector": "#btnLogin", "on_instance_search": true, "on_instance_load": false } - ] - }, - { "name": "qacode_lists", - "url": "http://netzulo.tk:83/qacode/data/lists", - "locator": "css selector", - "go_url": false, - "wait_url": 0, - "maximize": false, - "controls": [ - { "name": "lst_ordered", "selector": "ol:nth-child(1)"}, - { "name": "lst_ordered_child", "selector": "li"}, - { "name": "dd_menu_data", "selector": "#ddData"}, - { "name": "dd_menu_data_lists", "selector": "a[href='/qacode/data/lists']"}, - { "name": "tbl_ok", "selector": "#tblOk"}, - { "name": "tbl_html5_ok", "selector": "#tblHtml5Ok"}, - { "name": "tbl_html_tbodies_ok", "selector": "#tblHtml5OkTbodies"} - ] - }, - { "name": "qacode_inputs", - "url": "http://netzulo.tk:83/qacode/forms/inputs", - "locator": "css selector", - "go_url": false, - "wait_url": 0, - "maximize": false, - "controls": [ - { "name": "dd_base", "selector": "#txtTest002", "instance": "ControlForm", - "strict_rules": [ - { "tag": "select", "type": "tag", "severity": "hight" } - ] - }, - { "name": "dd_multiple", "selector": "#txtTest003", "instance": "ControlForm", - "strict_rules": [ - { "tag": "select", "type": "tag", "severity": "hight" } - ] - }, - { "name": "btn_click_invisible", "selector": "#btnInvisibleAtTimeout" }, - { "name": "btn_click_visible", "selector": "#btnVisibleAtTimeout" }, - { "name": "title_buttons", "selector": "#titleButtons" } - ] - }, - { "name": "qacode_logout", - "url": "http://netzulo.tk:83/qacode/logout", - "locator": "css selector", - "go_url": true, - "wait_url": 0, - "maximize": false, - "controls": [ - { "name": "btn_logout", "selector": "a[href='/qacode/logout']" }, - { "name": "btn_login", "selector": "a[href='/qacode/login']" } - ] - } - ] - } - ] } -} \ No newline at end of file +} diff --git a/qacode/core/__init__.py b/qacode/core/__init__.py index 0a595103..16036759 100755 --- a/qacode/core/__init__.py +++ b/qacode/core/__init__.py @@ -3,10 +3,18 @@ from qacode.core import bots -from qacode.core import exceptions +from qacode.core import browsers +from qacode.core import controls from qacode.core import loggers +from qacode.core import pages from qacode.core import testing -from qacode.core import webs -__all__ = ['bots', 'exceptions', 'loggers', 'testing', 'webs'] +__all__ = [ + 'bots', + 'browsers', + 'controls', + 'loggers', + 'pages', + 'testing', +] diff --git a/qacode/core/bots/__init__.py b/qacode/core/bots/__init__.py old mode 100755 new mode 100644 index ae8ab09b..b76be624 --- a/qacode/core/bots/__init__.py +++ b/qacode/core/bots/__init__.py @@ -1,9 +1,12 @@ -# -*- coding: utf-8 -*- -"""package qacode.core.bots""" - - -from qacode.core.bots import bot_base -from qacode.core.bots import modules - - -__all__ = ['bot_base', 'modules'] +# -*- coding: utf-8 -*- +"""package qacode.core.bots""" + + +from qacode.core.bots import bot +from qacode.core.bots import bot_config + + +__all__ = [ + 'bot', + 'bot_config', +] diff --git a/qacode/core/bots/bot.py b/qacode/core/bots/bot.py new file mode 100644 index 00000000..2e902d80 --- /dev/null +++ b/qacode/core/bots/bot.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +"""TODO""" + + +from qacode.core.bots.bot_config import BotConfig +from qacode.core.browsers.browser import Browser +from qacode.core.exceptions.bot_error import BotError +from qacode.core.loggers.log import Log +from qacode.core.pages.page import Page + + +class Bot(object): + """Class Base for handle selenium functionality throught this wrapper""" + + def __init__(self, **kwargs): + """TODO: doc method""" + self._browsers = [] + self._pages = [] + self._controls = [] + self._config = BotConfig(**kwargs) + self._log = Log(**{ + "name": self._config.log_name, + "path": self._config.log_path, + "level": self._config.log_level}) + + def __raises__(self, message): + """TODO: doc method""" + self.log.error(message) + raise BotError(message, self) + + def start(self): + """TODO: doc method""" + for _browser in self._config.browsers: + self.browser_create(_browser) + for _page in self._config.pages: + self.page_create(self._browsers[0], _page) + + def browser(self, session_id): + """TODO: doc method""" + for browser in self.browsers: + if browser.session_id == session_id: + return browser + self.__raises__("browser not found") + + def page(self, url): + """TODO: doc method""" + for page in self.pages: + if page.config.url == url: + return page + self.__raises__("page not found") + +# def control(self, selector): +# """TODO: doc method""" +# for control in self.controls: +# if control.selector == selector: +# return control +# raise Exception("control not found") + + def browser_create(self, config): + """TODO: doc method""" + driver_name = self._config.drivers_names[config.get("browser")] + driver_path = self._config.drivers_path + _config = config.copy() + _config.update({ + "driver_path": driver_path, + "driver_name": driver_name, + "hub_url": self._config.hub_url}) + browser = Browser(self.log, **_config) + self._browsers.append(browser) + return browser + + def page_create(self, browser, config): + """TODO: doc method""" + page = Page(browser, **config) + self._pages.append(page) + return page + + @property + def config(self): + """TODO: doc method""" + return self._config + + @property + def browsers(self): + """TODO: doc method""" + return self._browsers + + @property + def pages(self): + """TODO: doc method""" + return self._pages + + @property + def controls(self): + """TODO: doc method""" + return self._controls + + @property + def log(self): + """TODO: doc method""" + return self._log diff --git a/qacode/core/bots/bot_config.py b/qacode/core/bots/bot_config.py new file mode 100644 index 00000000..43b42d4a --- /dev/null +++ b/qacode/core/bots/bot_config.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +"""TODO""" + + +class BotConfig(object): + """TODO: doc class""" + + def __init__(self, **kwargs): + """TODO: doc method + Note1: musn't check win/lin 32/64, use must now + """ + self._log = kwargs.get("log") + self._bot = kwargs.get("bot") + # self._testlink = kwargs.get("testlink") + + @property + def log_name(self): + """TODO: doc method""" + return self._log.get("name") + + @property + def log_path(self): + """TODO: doc method""" + return self._log.get("path") + + @property + def log_level(self): + """TODO: doc method""" + return self._log.get("level") + + @property + def drivers_path(self): + """TODO: doc method""" + return self._bot.get("drivers_path") + + @property + def drivers_names(self): + """TODO: doc method""" + return self._bot.get("drivers_names") + + @property + def browsers(self): + """TODO: doc method""" + return self._bot.get("browsers") + + @property + def pages(self): + """TODO: doc method""" + return self._bot.get("pages") + + @property + def controls(self): + """TODO: doc method""" + return self._bot.get("controls") + + @property + def hub_url(self): + """TODO: doc method""" + return self._bot.get("hub_url") diff --git a/qacode/core/bots/modules/__init__.py b/qacode/core/bots/modules/__init__.py deleted file mode 100755 index c8877335..00000000 --- a/qacode/core/bots/modules/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# -*- coding: utf-8 -*- -"""package qacode.core.bots.modules""" - - -from qacode.core.bots.modules import nav_base - - -__all__ = ['nav_base'] diff --git a/qacode/core/bots/modules/nav_base.py b/qacode/core/bots/modules/nav_base.py deleted file mode 100644 index f2770469..00000000 --- a/qacode/core/bots/modules/nav_base.py +++ /dev/null @@ -1,720 +0,0 @@ -# -*- coding: utf-8 -*- -"""Created on 04 march 2017 - -@author: ntz -""" - - -import sys -from qacode.core.exceptions.core_exception import CoreException -from selenium.common.exceptions import ( - NoSuchElementException, - StaleElementReferenceException, - WebDriverException -) -from selenium.webdriver.common.by import By -from selenium.webdriver.remote.webelement import WebElement -from selenium.webdriver.support import expected_conditions as EC -from selenium.webdriver.support.ui import WebDriverWait - - -class NavBase(object): - """Main navigation methods to use on selenium scripts""" - - driver = None - log = None - driver_wait = None - driver_actions = None - driver_touch = None - - def __init__(self, driver, log, driver_wait=None, driver_actions=None, - driver_touch=None): - """Initialize self properties - - Arguments: - driver {WebDriver} -- instance of any browser type/mode - log {Log} -- Logger for methods usage - - Keyword Arguments: - driver_wait {selenium.webdriver.support.ui.WebDriverWait} - -- web driver wait for conditions (default: {None}) - driver_actions - {from selenium.webdriver.common.action_chains.ActionChains} - -- web driver for perform actions on elements - driver_touch - {selenium.webdriver.common.touch_actions.TouchActions} - -- web driver for perform touch actions on elements - """ - self.driver = driver - self.log = log - self.driver_wait = driver_wait - self.driver_actions = driver_actions - self.driver_touch = driver_touch - - def method_name(self): - """Returns a string with the name of the function it's called from""" - return sys._getframe(1).f_code.co_name - - def get_driver_wait(self, driver_wait=None): - """Allow to obatin an instance of WebDriverWait""" - if driver_wait is None and self.driver_wait is None: - raise CoreException("Nav instanced without driver_wait") - if driver_wait is None: - driver_wait = self.driver_wait - return driver_wait - - def get_url(self, url, wait_for_load=0): - """Do get_url including implicit wait for page load""" - if wait_for_load > 0: - self.driver.implicitly_wait(wait_for_load) - self.driver.get(url) - - def get_maximize_window(self): - """Maximize browser window""" - self.driver.maximize_window() - - def get_window_handle(self): - """Get window object to handle with selenium on scripts""" - return self.driver.current_window_handle - - def add_cookie(self, cookie_dict): - """Adds a cookie to your current session. - - Args: - cookie_dict: A dictionary object, with required - keys - "name" and "value" - optional keys - "path", "domain", "secure", "expiry" - Usage: - driver.add_cookie({ - "name" : "foo", - "value" : "bar"}) - driver.add_cookie({ - 'name' : 'foo', - 'value' : 'bar', - 'path' : '/', - 'secure':True, - 'domain': None}) - """ - method = self.method_name() - valid_keys = ["name", "value"] - if cookie_dict is None: - raise CoreException("Can't add None cookie") - for key in valid_keys: - if cookie_dict.get(key) is None: - msg = "Can't add new cookie without '{}'".format(key) - raise CoreException(msg) - try: - return self.driver.add_cookie(cookie_dict) - except WebDriverException as err: - bot_info = {"err": err, "method": method} - raise CoreException(bot_info=bot_info) - - def get_cookies(self): - """Returns a set of dictionaries, corresponding to cookies - visible in the current session. - """ - return self.driver.get_cookies() - - def delete_cookie_by_key(self, key_name): - """Deletes a single cookie with the given name""" - self.driver.delete_cookie(key_name) - - def delete_cookies(self): - """Delete all cookies in the scope of the session""" - self.driver.delete_all_cookies() - - def get_capabilities(self): - """Retrieve current capabilities applied to selenium driver""" - return self.driver.desired_capabilities - - def execute_js(self, script, *args): - """Execute arbitrary Javascript code - - Arguments: - script {str} -- JS code to be executed on WebDriver - *args {[type]} -- More arguments ( like element selector ) - - Returns: - str -- JS script returns - """ - return self.driver.execute_script(script, *args) - - def set_css_rule(self, css_selector, css_prop, css_value, - css_important=False, index=0): - """Set new value for given CSS property name - - Arguments: - css_selector {str} -- CSS selector to apply rule - css_prop {str} -- CSS property to be applied to rule - css_value {str} -- CSS property value to be applied to rule - - Keyword Arguments: - css_important {bool} -- Allow to include '!important' - to rule (default: {False}) - index {int} -- Position to insert new CSS rule - on first stylesheet (default: {0}) - - Returns: - str -- JS script returns - """ - css_important_text = '' - if css_important: - css_important_text = '!important' - css_rule = " {0!s} {{ {1!s} : {2!s} {3!s}; }}".format( - css_selector, - css_prop, - css_value, - css_important_text) - js_script = "document.styleSheets[0].insertRule(\"{0!s}\", {1:d});".format( # noqa: E501 - css_rule, index) - return self.execute_js(js_script) - - def find_element(self, selector, locator=By.CSS_SELECTOR): - """Just divided execution ways for search web element - throught selenium - - Arguments: - selector {str} -- string selector used to locate one - element or first obtained - - Keyword Arguments: - locator {By} -- locator strategy used to find - WebElement selector (default: {By.CSS_SELECTOR}) - - Raises: - CoreException -- If locator is None - CoreException -- Element selector+locator strategy raises - selenium NoSuchElementException - - Returns: - WebElement -- selenium representation for a web element - """ - method = self.method_name() - msg = "Locator not selected at find_element, selector={}".format( - selector) - if locator is None: - raise CoreException(msg) - try: - return self.driver.find_element(locator, selector) - except NoSuchElementException as err: - info_bot = {"err": err, "method": method} - raise CoreException(info_bot=info_bot) - - def find_elements(self, selector, locator=By.CSS_SELECTOR): - """Just divided execution ways for search web elements - throught selenium - - Arguments: - selector {str} -- string selector used to locate - one or more elements - - Keyword Arguments: - locator {By} -- locator strategy used to find - WebElement selector (default: {By.CSS_SELECTOR}) - - Raises: - CoreException -- If locator is None - CoreException -- Element selector+locator strategy raises - selenium NoSuchElementException - - Returns: - list(WebElement) -- selenium representation for a - list of web elements - """ - method = self.method_name() - msg = "Locator not selected at find_element, selector={}".format( - selector) - if locator is None: - raise CoreException(msg, info_bot={"method": method}) - try: - elements = self.driver.find_elements(locator, selector) - if len(elements) == 0: - raise CoreException( - "0 elements found", info_bot={"method": method}) - return elements - except NoSuchElementException as err: - info_bot = {"err": err, "method": method} - raise CoreException(info_bot=info_bot) - - def find_element_wait(self, selector, - locator=By.CSS_SELECTOR, driver_wait=None): - """Search element using WebDriverWait class - and ElementConditions presence_of_element_located - - Arguments: - selector {str} -- string selector used to locate one - element or first obtained - - Keyword Arguments: - locator {By} -- locator strategy used to find - WebElement selector (default: {By.CSS_SELECTOR}) - driver_wait {WebDriverWait} -- driver that supports - ExpectedConditions methods (default: {None}) - - Raises: - CoreException -- if NavBase instanced - without driver_wait - - Returns: - WebElement -- element through selenium - WebDriverWait class - """ - driver_wait = self.get_driver_wait(driver_wait=driver_wait) - try: - return driver_wait.until( - EC.presence_of_element_located((locator, selector))) - except (NoSuchElementException, StaleElementReferenceException): - return driver_wait.until( - EC.visibility_of_element_located((locator, selector))) - - def find_elements_wait(self, selector, - locator=By.CSS_SELECTOR, driver_wait=None): - """Search elements using WebDriverWait class - and ElementConditions presence_of_all_elements_located - - Arguments: - selector {str} -- string selector used to locate - multiple elements - - Keyword Arguments: - locator {By} -- locator strategy used to find - WebElement selector (default: {By.CSS_SELECTOR}) - driver_wait {WebDriverWait} -- driver that supports - ExpectedConditions methods (default: {None}) - - Raises: - CoreException -- if NavBase instanced - without driver_wait - - Returns: - WebElement -- element through selenium - WebDriverWait class - """ - driver_wait = self.get_driver_wait(driver_wait=driver_wait) - try: - return driver_wait.until( - EC.presence_of_all_elements_located((locator, selector))) - except (NoSuchElementException, StaleElementReferenceException): - return driver_wait.until( - EC.visibility_of_all_elements_located((locator, selector))) - - def find_element_child(self, element, child_selector, - locator=By.CSS_SELECTOR): - """TODO: doc method""" - method = self.method_name() - msg = "Cant find child if not element found" - if element is None or not isinstance(element, WebElement): - raise CoreException(msg, info_bot={"method": method}) - try: - return element.find_element(locator, child_selector) - except (NoSuchElementException, StaleElementReferenceException) as err: - # at Java lang exist 1 expected condition - # named : visibilityOfNestedElementsLocatedBy - # doc : https://selenium-python.readthedocs.io/waits.html - # maybe must exist at python too - # then, create and use new method named: find_element_child_wait() - # raise NotImplementedError("TODO:open an issue at github please") - info_bot = {"err": err, "method": method} - raise CoreException(msg, info_bot=info_bot) - - def find_element_children(self, element, child_selector, - locator=By.CSS_SELECTOR): - """TODO: doc method""" - method = self.method_name() - if element is None or not isinstance(element, WebElement): - raise CoreException("Cant find children if not element found") - try: - return element.find_elements(locator, child_selector) - except (NoSuchElementException, StaleElementReferenceException) as err: - # at Java lang exist 1 expected condition - # named : visibilityOfNestedElementsLocatedBy - # doc : https://selenium-python.readthedocs.io/waits.html - # maybe must exist at python too - # then, create and use new method named: find_element_child_wait() - # raise NotImplementedError("TODO:open an issue at github please") - info_bot = {"err": err, "method": method} - raise CoreException(info_bot=info_bot) - - def find_elements_child(self): - """TODO: doc method""" - raise NotImplementedError("TODO: open an issue at github please") - - def find_elements_children(self): - """TODO: doc method""" - raise NotImplementedError("TODO: open an issue at github please") - - def forward(self): - """Go forward using browser functionality""" - self.driver.forward() - - def reload(self): - """Go reload page using browser functionality""" - self.driver.refresh() - - def get_log(self, log_name='browser', raises=False): - """Get selenium log by name, this depends of - driver mode and browser what it's using each time - - Keyword Arguments: - log_name {str} -- get log type lanes (default: {'browser'}) - - Raises: - CoreException -- if log_name value not in list - of valid values : browser, driver, client, server - - Returns: - list() -- list of messages typed on a log_name - """ - method = self.method_name() - try: - return { - 'browser': self.driver.get_log, - 'driver': self.driver.get_log, - 'client': self.driver.get_log, - 'server': self.driver.get_log, - }[log_name](log_name) - except (KeyError, WebDriverException) as err: - if isinstance(err, KeyError): - raise CoreException( - "Can't use not valid value to get log", - info_bot={"err": err, "method": method}) - self.log.debug(("nav | get_log: Selenium, not all drivers will" - " be handled by them with all optionsvalues")) - self.log.warning("nav | get_log: log_name={}, err={}".format( - log_name, err.msg)) - return list() - - def get_screenshot_as_base64(self): - """Gets the screenshot of the current window as a base64 encoded string - which is useful in embedded images in HTML - """ - return self.driver.get_screenshot_as_base64() - - def get_screenshot_as_file(self, file_name): - """Gets the screenshot of the current window. Returns False - if there is any IOError, else returns True. Use full paths - in your filename. - - Arguments: - file_name {str} -- name of file path where - want to save screenshot - - Returns: - list(byte) -- file binary object of screenshot bytes - """ - return self.driver.get_screenshot_as_file(file_name) - - def get_screenshot_as_png(self): - """Gets the screenshot of the current window as a - binary data. - - Returns: - File -- file binary object of screenshot with PNG format - """ - return self.driver.get_screenshot_as_png() - - def get_screenshot_save(self, file_name): - """Gets the screenshot of the current window. Returns False - if there is any IOError, else returns True. - Use full paths in your filename. - - Arguments: - file_name {str} -- name of file path where - want to save screenshot - - Returns: - list(byte) -- file binary object of screenshot bytes - """ - return self.driver.save_screenshot(file_name) - - def js_set_timeout(self, timeout=60): - """Set the amount of time that the script should wait during an - execute_async_script call before throwing an error. - - Keyword Arguments: - timeout {int} -- seconds to raise script - wait (default: {60}) - """ - self.driver.set_script_timeout(timeout) - - def set_window_size(self, pos_x=800, pos_y=600): - """Sets the width and height of the current - window. (window.resizeTo) - - Keyword Arguments: - pos_x {int} -- width of new window size (default: {800}) - pos_y {int} -- height of new window size (default: {600}) - """ - self.driver.set_window_size(pos_x, pos_y) - - def get_title(self): - """Obtains the title of the current page and return it - - Returns: - str -- title of current page opened - """ - return self.driver.title - - def get_current_url(self): - """Return current url from opened bot - - Raises: - CoreException -- if can't obtains url with this - selenium driver - - Returns: - str -- string representation of current driver url - """ - msg = "Failed at obtain selenium driver property 'current_url'" - try: - return self.driver.current_url - except WebDriverException: - raise CoreException(msg) - - def is_url(self, url, ignore_raises=True): - """Check if url it's the same what selenium current and visible url - - Arguments: - url {str} -- string value used to verify url - - Keyword Arguments: - ignore_raises {bool} -- allows to ignore errors - when executes if raises errors (default: {True}) - - Raises: - exceptions -- [description] - CoreException -- [description] - - Returns: - bool -- if current driver url match with param url, - then returns True, False if not - """ - if self.get_current_url() != url: - if not ignore_raises: - raise CoreException("'Current url' is not 'param url'") - return False - return True - - def set_web_element(self, new_attr_id): - """Create web element using selenium adding to DOM - - Arguments: - new_attr_id {str} -- html attribute ID for - new web element - """ - self.driver.create_web_element(new_attr_id) - - def ele_click(self, element=None, selector=None, locator=By.CSS_SELECTOR): - """Perform click webelement with locator param or search it by default - CSS_SELECTOR value if element it's none but selector - it's not default value - - Keyword Arguments: - element {WebElement} -- selenium object, instance of WebElement - (default: {None}) - selector {str} -- selector to search and element to click - (default: {None}) - locator {By} -- locator selenium strategy - (default: {By.CSS_SELECTOR}) - - Raises: - CoreException -- Bad params combination, need element - or selector to search element - - Returns: - WebElement -- returns element clicked (to allow chaining) - """ - method = self.method_name() - curr_ele = element - curr_selector = selector - can_click = False - - if curr_ele is None and curr_selector is None: - msg = ("Bad arguments: curr_ele={}, curr_selector={}" - "".format(curr_ele, curr_selector)) - raise CoreException(msg, info_bot={"method": method}) - elif curr_ele is None: - curr_ele = self.find_element(curr_selector, locator=locator) - can_click = True - elif curr_ele is not None and isinstance(curr_ele, WebElement): - can_click = True - if can_click: - curr_ele.click() - return curr_ele - - def ele_write(self, element, text=None): - """ - Over element perform send_keys , if not sended text, then will write - empty over element - :param element: WebElement - :return: None - """ - method = self.method_name() - if not isinstance(element, WebElement): - msg = "Param 'element' it's not WebElement" - raise CoreException(msg, info_bot={"method": method}) - if text is not None: - element.send_keys(text) - else: - # it's neccessary because some fields shows validation message and - # color after try to send empty message - element.send_keys() - - def ele_is_displayed(self, element): - """Whether the element is visible to a user - - Webdriver spec to determine if element it's displayed: - https://w3c.github.io/webdriver/webdriver-spec.html#widl-WebElement-isDisplayed-boolean - - Arguments: - element {WebElement} -- selenium web element - - Returns: - bool -- Value based on selenium SPEC to determine if an element - is enabled - """ - return element.is_displayed() - - def ele_is_enabled(self, element): - """Returns whether the element is enabled - - Arguments: - element {WebElement} -- selenium web element - - Returns: - bool -- Value based on selenium SPEC to determine if an element - is enabled - """ - return element.is_enabled() - - def ele_is_selected(self, element): - """Returns whether the element is selected - - Arguments: - element {WebElement} -- selenium web element - - Returns: - bool -- Value based on selenium SPEC to determine if an element - is enabled - """ - return element.is_selected() - - def ele_text(self, element, on_screen=True): - """Get element content text. - If the isDisplayed() method can sometimes trip over when - the element is not really hidden but outside the viewport - get_text() returns an empty string for such an element. - - Keyword Arguments: - on_screen {bool} -- allow to obtain text if element - it not displayed to this element before - read text (default: {True}) - - Returns: - str -- Return element content text (innerText property) - """ - method = self.method_name() - if on_screen: - text = str(element.text) - else: - text = self.ele_attribute(element, 'innerText') - self.log.debug("text obtained from innerText") - if self.ele_is_displayed(element): - msg = ("on_screen param must use when" - "element it's not displayed") - raise CoreException(msg, info_bot={"method": method}) - return text - - def ele_input_value(self, element): - """Return value of value attribute, usefull for inputs""" - return self.ele_attribute(element, 'value') - - def ele_attribute(self, element, attr_name): - """Returns tuple with (attr, value) if founds - This method will first try to return the value of a property with - the given name. If a property with that name doesn't exist, it - returns the value of the attribute with the same name. If there's - no attribute with that name, None is returned. - """ - method = self.method_name() - value = str(element.get_attribute(attr_name)) - if value is None or value == attr_name: - msg = "Attr '{}' not found".format(attr_name) - raise CoreException(msg, info_bot={"method": method}) - return value - - def ele_tag(self, element): - """Returns element.tag_name value""" - return element.tag_name - - def ele_clear(self, element): - """Clear element text""" - return element.clear() - - def ele_css(self, element, prop_name): - """Allows to obtain CSS value based on CSS property name - - Arguments: - element {WebElement} -- WebElement to modify CSS property - prop_name {str} -- CSS property name - - Returns: - str -- Value of CSS property searched - """ - return element.value_of_css_property(prop_name) - - def ele_wait_invisible(self, selector, locator=By.CSS_SELECTOR, timeout=0): - """Wait for invisible element (display:none), returns element""" - if selector is None: - raise CoreException( - "Can't wait invisible element if None selector given") - locator_tuple = (locator, selector) - driver_wait = WebDriverWait(self.driver, timeout) - try: - element = driver_wait.until( - EC.invisibility_of_element_located(locator_tuple)) - except Exception: - raise CoreException("Fails at wait for invisible element") - return element - - def ele_wait_visible(self, element, timeout=0): - """Wait for visible condition element, returns self""" - if element is None: - raise CoreException("Can't wait visible if element is None") - driver_wait = WebDriverWait(self.driver, timeout) - try: - element = driver_wait.until(EC.visibility_of(element)) - except Exception: - raise CoreException("Fails at wait for visible element") - return element - - def ele_wait_text(self, selector, text, - locator=By.CSS_SELECTOR, timeout=0): - """Wait if the given text is present in the specified element""" - locator_tuple = (locator, selector) - driver_wait = WebDriverWait(self.driver, timeout) - try: - return driver_wait.until( - EC.text_to_be_present_in_element(locator_tuple, text)) - except Exception: - raise CoreException("Fails at wait for element text") - - def ele_wait_value(self, selector, value, - locator=By.CSS_SELECTOR, timeout=0): - """Wait if the given value is present in the specified element""" - locator_tuple = (locator, selector) - driver_wait = WebDriverWait(self.driver, timeout) - try: - return driver_wait.until( - EC.text_to_be_present_in_element_value(locator_tuple, value)) - except Exception: - raise CoreException("Fails at wait for element value") - - def __repr__(self): - """Show basic properties for this object""" - return ("ControlBase: drivers instanced are... \n" - " driver_wait={},\n driver_actions={},\n" - " driver_touch={}").format( - self.driver_wait, - self.driver_actions, - self.driver_touch) diff --git a/qacode/core/browsers/__init__.py b/qacode/core/browsers/__init__.py new file mode 100644 index 00000000..b2623382 --- /dev/null +++ b/qacode/core/browsers/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +"""package qacode.core.browsers""" + + +from qacode.core.browsers import browser +from qacode.core.browsers import browser_config + + +__all__ = [ + 'browser', + 'browser_config', +] diff --git a/qacode/core/browsers/browser.py b/qacode/core/browsers/browser.py new file mode 100644 index 00000000..338fe8cc --- /dev/null +++ b/qacode/core/browsers/browser.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- +"""TODO""" + + +import os +import sys +from qacode.core.browsers.browser_config import BrowserConfig +from qacode.core.browsers.modules.commons import ModuleCommons +from qacode.core.browsers.modules.elements import ModuleElements +from qacode.core.browsers.modules.js import ModuleJs +from qacode.core.browsers.modules.screenshots import ModuleScreenshots +from qacode.core.browsers.modules.waits import ModuleWaits +from qacode.core.exceptions.browser_error import BrowserError +from selenium.common.exceptions import SessionNotCreatedException +from selenium.webdriver import (Chrome, Edge, Firefox, Ie) +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 + + +class Browser(object): + """TODO: doc class""" + + def __init__(self, log, **kwargs): + """TODO: doc method""" + self._log = log + self._config = BrowserConfig(**kwargs) + self._capabilities = self.__capabilities__() + self._options = self.__options__() + self._driver_abs_path = self.__driver_abs_path__() + self._driver = None + + def __capabilities__(self): + """TODO: doc method""" + capabilities = None + try: + capabilities = { + "chrome": DesiredCapabilities.CHROME.copy(), + "firefox": DesiredCapabilities.FIREFOX.copy(), + "iexplorer": DesiredCapabilities.INTERNETEXPLORER.copy(), + "edge": DesiredCapabilities.EDGE.copy(), + }[self._config.browser] + except KeyError: + raise BrowserError('Bad browser selected at load options', self) + return capabilities + + def __driver_abs_path__(self): + """TODO: doc method""" + driver_path = "{}/{}".format( + self._config.driver_path, self._config.driver_name) + abs_path = os.path.abspath(driver_path) + sys.path.append(abs_path) + return abs_path + + def __options__(self): + """TODO: doc method""" + is_headless = self._config.options.get("headless") + try: + options = { + "chrome": ChromeOptions(), + "firefox": FirefoxOptions(), + }[self._config.browser] + if is_headless: + options.add_argument("--headless") + return options + except KeyError: + # this browser hasn't options + return None + + def __driver_local__(self): + """TODO: doc method""" + driver = None + try: + driver = { + "chrome": Chrome, + "firefox": Firefox, + "iexplorer": Ie, + "edge": Edge + }[self._config.browser] + except KeyError: + msg = ("Just browser names allowed: " + "chrome, firefox, iexplorer, edge") + raise Exception(msg) + return driver + + def __driver_remote__(self): + """TODO: doc method""" + return RemoteWebDriver + + def __open_local__(self): + """TODO: doc method""" + driver_class = self.__driver_local__() + try: + driver = driver_class( + executable_path=self.driver_abs_path, + capabilities=self.capabilities, + options=self.options) + return driver + except (AttributeError) as err: + raise Exception(str(err)) + + def __open_remote__(self): + """TODO: doc method""" + driver_class = self.__driver_remote__() + try: + driver = driver_class( + command_executor=self._config.hub_url, + desired_capabilities=self.capabilities, + options=self.options) + return driver + except (AttributeError, SessionNotCreatedException) as err: + if err.args[0] == "'NoneType' object has no attribute 'execute'": + raise Exception( + "Check if hub it's running at: {}".format( + self._config.hub_url)) + raise Exception(str(err)) + + def __drivers_selenium__(self): + """TODO: doc method""" + self._driver_wait = WebDriverWait(self._driver, 10) + self._driver_actions = ActionChains(self._driver) + self._driver_touch = TouchActions(self._driver) + + def open(self): + """TODO: doc method""" + if self._config.mode == "local": + self._driver = self.__open_local__() + elif self._config.mode == "remote": + self._driver = self.__open_remote__() + else: + raise BrowserError("Just allowed modes: local, remote", self) + self.__drivers_selenium__() + # modules + self.commons = ModuleCommons(self._driver) + self.elements = ModuleElements(self._driver) + self.waits = ModuleWaits(self.driver, self._driver_wait) + self.screenshots = ModuleScreenshots(self._driver) + self.js = ModuleJs(self.driver) + + def close(self): + """TODO: doc method""" + self.driver.quit() + self._driver = None + self._driver_wait = None + self._driver_actions = None + self._driver_touch = None + self.commons = None + self.elements = None + self.waits = None + self.screenshots = None + self.js = None + + @property + def config(self): + """TODO: doc method""" + return self._config + + @property + def capabilities(self): + """TODO: doc method""" + return self.__capabilities__() + + @property + def options(self): + """TODO: doc method""" + return self.__options__() + + @property + def driver_abs_path(self): + """TODO: doc method""" + return self.__driver_abs_path__() + + @property + def driver(self): + """TODO: doc method""" + return self._driver + + @property + def session_id(self): + """TODO: doc method""" + return self._driver.session_id + + @property + def log(self): + """TODO: doc method""" + return self._log diff --git a/qacode/core/browsers/browser_config.py b/qacode/core/browsers/browser_config.py new file mode 100644 index 00000000..252b0cb5 --- /dev/null +++ b/qacode/core/browsers/browser_config.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +"""TODO""" + + +class BrowserConfig(object): + """TODO: doc class""" + + def __init__(self, **kwargs): + """TODO: doc method""" + self._config = kwargs + + @property + def browser(self): + """TODO: doc method""" + return self._config.get("browser") + + @property + def mode(self): + """TODO: doc method""" + return self._config.get("mode") + + @property + def options(self): + """TODO: doc method""" + return self._config.get("options") + + @property + def driver_path(self): + """TODO: doc method""" + return self._config.get("driver_path") + + @property + def driver_name(self): + """TODO: doc method""" + return self._config.get("driver_name") + + @property + def hub_url(self): + """TODO: doc method""" + return self._config.get("hub_url") diff --git a/qacode/core/browsers/modules/__init__.py b/qacode/core/browsers/modules/__init__.py new file mode 100644 index 00000000..2c3a780c --- /dev/null +++ b/qacode/core/browsers/modules/__init__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +"""package qacode.core.browsers.modules""" + + +from qacode.core.browsers.modules.commons import ModuleCommons +from qacode.core.browsers.modules.elements import ModuleElements +from qacode.core.browsers.modules.js import ModuleJs +from qacode.core.browsers.modules.module import Module +from qacode.core.browsers.modules.screenshots import ModuleScreenshots +from qacode.core.browsers.modules.waits import ModuleWaits + + +__all__ = [ + 'ModuleCommons', + 'ModuleElements', + 'ModuleJs', + 'Module', + 'ModuleScreenshots', + 'ModuleWaits', +] diff --git a/qacode/core/browsers/modules/commons.py b/qacode/core/browsers/modules/commons.py new file mode 100644 index 00000000..6a4c4086 --- /dev/null +++ b/qacode/core/browsers/modules/commons.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +"""TODO""" + + +from qacode.core.browsers.modules.module import Module +from selenium.common.exceptions import WebDriverException + + +class ModuleCommons(Module): + """TODO: doc class""" + + def __init__(self, driver): + """TODO: doc method""" + self._driver = driver + + def get_maximize_window(self): + """Maximize browser window""" + self._driver.maximize_window() + + def get_title(self): + """Obtains the title of the current page and return it""" + return self._driver.title + + def get_window_handle(self): + """Get window object to handle with selenium on scripts""" + return self._driver.current_window_handle + + def get_capabilities(self): + """Retrieve current capabilities applied to selenium driver""" + return self._driver.desired_capabilities + + def forward(self): + """Go forward using browser functionality""" + self._driver.forward() + + def reload(self): + """Go reload page using browser functionality""" + self._driver.refresh() + + def get_url(self, url, wait=0): + """Do get_url including implicit wait for page load""" + if wait > 0: + self._driver.implicitly_wait(wait) + self._driver.get(url) + + def get_current_url(self): + """Return current url from opened bot""" + return self._driver.current_url + + def is_url(self, url): + """Check if url it's the same what selenium + current and visible url + """ + if self.get_current_url() != url: + return False + return True + + def get_log(self, log_name='browser'): + """Get selenium log by name, this depends of + driver mode and browser what it's using each time + """ + if log_name not in ['browser', 'driver', 'client', 'server']: + raise Exception("Can't use not valid value to get log") + try: + return self._driver.get_log(log_name) + except WebDriverException: + # silenced error because some browsers got + # different errors when no logs or obtaining + # server on local driver or client on remote driver + return list() + + def set_window_size(self, x=800, y=600): + """Sets the width and height of the current + window. (window.resizeTo) + + Keyword Arguments: + x {int} -- width of new window size (default: {800}) + y {int} -- height of new window size (default: {600}) + """ + self._driver.set_window_size(x, y) diff --git a/qacode/core/browsers/modules/elements.py b/qacode/core/browsers/modules/elements.py new file mode 100644 index 00000000..1a54bfda --- /dev/null +++ b/qacode/core/browsers/modules/elements.py @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- +"""TODO""" + + +from qacode.core.browsers.modules.module import Module +from selenium.webdriver.common.by import By + + +class ModuleElements(Module): + """TODO: doc class""" + + def __init__(self, driver): + """TODO: doc method""" + self._driver = driver + + def find(self, selector, locator=By.CSS_SELECTOR): + """Just divided execution ways for search + web element throught selenium + """ + self.__check_not_none__("locator", locator) + # Not found: NoSuchElementException, StaleElementReferenceException + return self._driver.find_element(locator, selector) + + def finds(self, selector, locator=By.CSS_SELECTOR, + raises_zero=True): + """Just divided execution ways for search + web elements throught selenium + """ + self.__check_not_none__("locator", locator) + # Not found: NoSuchElementException, StaleElementReferenceException + elements = self._driver.find_elements(locator, selector) + if len(elements) == 0 and raises_zero: + raise Exception("finds: 0 elements found") + return elements + + def find_child(self, element, child_selector, locator=By.CSS_SELECTOR): + """TODO: doc method""" + self.__check_not_none__("locator", locator) + # Not found: NoSuchElementException, StaleElementReferenceException + return element.find_element(locator, child_selector) + + def find_children(self, element, child_selector, locator=By.CSS_SELECTOR): + """TODO: doc method""" + self.__check_not_none__("locator", locator) + # Not found: NoSuchElementException, StaleElementReferenceException + return element.find_elements(locator, child_selector) + + def finds_child(self): + """TODO: doc method""" + # at Java lang exist 1 expected condition + # named : visibilityOfNestedElementsLocatedBy + # doc : https://selenium-python.readthedocs.io/waits.html + # maybe must exist at python too + # then, create and use new method named: find_element_child_wait() + # raise NotImplementedError("TODO:open an issue at github please") + raise NotImplementedError("TODO: open an issue at github please") + + def finds_children(self): + """TODO: doc method""" + # at Java lang exist 1 expected condition + # named : visibilityOfNestedElementsLocatedBy + # doc : https://selenium-python.readthedocs.io/waits.html + # maybe must exist at python too + # then, create and use new method named: find_element_child_wait() + # raise NotImplementedError("TODO:open an issue at github please") + raise NotImplementedError("TODO: open an issue at github please") + + def click(self, element): + """Perform click webelement with element + + Returns: + WebElement -- returns element clicked (to allow chaining) + """ + element.click() + return element + + def write(self, element, text=None): + """Over element perform send_keys , if not sended will + write empty over element + + Returns: + WebElement -- returns element clicked (to allow chaining) + """ + if not text: + # it's neccessary because some fields shows + # validation message and color just after + # try to send empty message + element.send_keys() + else: + element.send_keys(text) + return element + + def attr(self, element, attr_name): + """Returns tuple with (attr, value) if founds + This method will first try to return the value of a property with + the given name. If a property with that name doesn't exist, it + returns the value of the attribute with the same name. If there's + no attribute with that name, None is returned. + """ + value = element.get_attribute(attr_name) + if value is None: + raise Exception("Attr '{}' not found".format(attr_name)) + return value + + def input_value(self, element): + """Return value of value attribute, usefull for inputs""" + return self.attr(element, 'value') + + def clear(self, element): + """Clear element text""" + return element.clear() + + def css(self, element, prop_name): + """Allows to obtain CSS value based on CSS property name""" + return element.value_of_css_property(prop_name) + + def is_displayed(self, element): + """Whether the element is visible to a user + Webdriver spec to determine if element it's displayed: + https://w3c.github.io/webdriver/webdriver-spec.html#widl-WebElement-isDisplayed-boolean + """ + return element.is_displayed() + + def is_enabled(self, element): + """Returns whether the element is enabled""" + return element.is_enabled() + + def is_selected(self, element): + """Returns whether the element is selected""" + return element.is_selected() + + def attr_value(self, element, attr_name): + """Search and attribute name over self.element and get value, + if attr_value is obtained, then compare and raise if not + Arguments: + attr_name {str} -- find an attribute on WebElement + with this name + Returns: + str -- value of html attr_name + """ + return str(self.attr(element, attr_name)) + + def get_text(self, element): + """TODO: doc method""" + if self.is_displayed(element): + return str(element.text) + else: + return self.attr(element, 'innerText') + + def tag(self, element): + """Returns element.tag_name value""" + return element.tag_name diff --git a/qacode/core/browsers/modules/js.py b/qacode/core/browsers/modules/js.py new file mode 100644 index 00000000..dfd807ba --- /dev/null +++ b/qacode/core/browsers/modules/js.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +"""TODO""" + + +from qacode.core.browsers.modules.module import Module +from selenium.common.exceptions import StaleElementReferenceException + + +class ModuleJs(Module): + """TODO: doc class""" + + def __init__(self, driver): + """TODO: doc method""" + self._driver = driver + + def execute_js(self, script, *args): + """Execute arbitrary Javascript code + Arguments: + script {str} -- JS code to be executed on WebDriver + *args {[type]} -- More arguments ( like element selector ) + Returns: + str -- JS script returns + """ + return self._driver.execute_script(script, *args) + + def set_css_rule(self, css_selector, css_prop, css_value, **kwargs): + """Set new value for given CSS property name + Arguments: + css_selector {str} -- CSS selector to apply rule + css_prop {str} -- CSS property to be applied to rule + css_value {str} -- CSS property value to be applied to rule + Keyword Arguments: + css_important {bool} -- Allow to include '!important' + to rule (default: {False}) + index {int} -- Position to insert new CSS rule + on first stylesheet (default: {0}) + Returns: + str -- JS script returns + """ + css_important = kwargs.get("css_important") or False + index = kwargs.get("index") or 0 + css_important_text = '' + if css_important: + css_important_text = '!important' + css_rule = " {0!s} {{ {1!s} : {2!s} {3!s}; }}".format( + css_selector, + css_prop, + css_value, + css_important_text) + js_script = "document.styleSheets[0].insertRule(\"{0!s}\", {1:d});".format( # noqa: E501 + css_rule, index) + return self.execute_js(js_script) + + def ele_is_staled(self, element): + """Returns if an element is staled or not""" + js_script = "return arguments[0].isConnected" + try: + return not self.execute_js(js_script, element) + except StaleElementReferenceException: + return True diff --git a/qacode/core/browsers/modules/module.py b/qacode/core/browsers/modules/module.py new file mode 100644 index 00000000..6c7d0e41 --- /dev/null +++ b/qacode/core/browsers/modules/module.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +"""TODO""" + + +class Module(object): + """TODO: doc class""" + + def __check_not_none__(self, name, value): + """TODO: doc method""" + if value is None: + raise Exception("Not {} provided".format(name)) diff --git a/qacode/core/browsers/modules/screenshots.py b/qacode/core/browsers/modules/screenshots.py new file mode 100644 index 00000000..696a9146 --- /dev/null +++ b/qacode/core/browsers/modules/screenshots.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +"""TODO""" + + +from qacode.core.browsers.modules.module import Module + + +class ModuleScreenshots(Module): + """TODO: doc class""" + + def __init__(self, driver): + """TODO: doc method""" + self._driver = driver + + def as_base64(self): + """Gets the screenshot of the current window as a base64 encoded string + which is useful in embedded images in HTML + """ + return self._driver.get_screenshot_as_base64() + + def as_file(self, file_name): + """Gets the screenshot of the current window. Returns False + if there is any IOError, else returns True. Use full paths + in your filename. + + Arguments: + file_name {str} -- name of file path where + want to save screenshot + + Returns: + list(byte) -- file binary object of screenshot bytes + """ + return self._driver.get_screenshot_as_file(file_name) + + def as_png(self): + """Gets the screenshot of the current window as a + binary data. + + Returns: + File -- file binary object of screenshot with PNG format + """ + return self._driver.get_screenshot_as_png() + + def save(self, file_name): + """Gets the screenshot of the current window. Returns False + if there is any IOError, else returns True. + Use full paths in your filename. + + Arguments: + file_name {str} -- name of file path where + want to save screenshot + + Returns: + list(byte) -- file binary object of screenshot bytes + """ + return self._driver.save_screenshot(file_name) diff --git a/qacode/core/browsers/modules/waits.py b/qacode/core/browsers/modules/waits.py new file mode 100644 index 00000000..b1ab5d2e --- /dev/null +++ b/qacode/core/browsers/modules/waits.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +"""TODO""" + + +from qacode.core.browsers.modules.module import Module +from selenium.webdriver.common.by import By +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.ui import WebDriverWait + + +class ModuleWaits(Module): + """TODO: doc class""" + + def __init__(self, driver, driver_wait): + """TODO: doc method""" + self._driver = driver + self._driver_wait = driver_wait + + def __driver_wait__(self, timeout=None): + """TODO: doc method""" + driver_wait = self._driver_wait + if timeout: + driver_wait = WebDriverWait(self._driver, timeout) + return driver_wait + + def find_wait(self, selector, locator=By.CSS_SELECTOR, timeout=None): + """Search element using WebDriverWait class + and ElementConditions presence_of_element_located + """ + self.__check_not_none__("locator", locator) + # Not found: NoSuchElementException, StaleElementReferenceException + expectation = EC.presence_of_element_located((locator, selector)) + return self.__driver_wait__(timeout=timeout).until(expectation) + + def finds_wait(self, selector, locator=By.CSS_SELECTOR, timeout=None): + """Search elements using WebDriverWait class + and ElementConditions presence_of_all_elements_located + """ + self.__check_not_none__("locator", locator) + # Not found: NoSuchElementException, StaleElementReferenceException + expectation = EC.presence_of_all_elements_located((locator, selector)) + return self.__driver_wait__(timeout=timeout).until(expectation) + + def ele_invisible(self, selector, locator=By.CSS_SELECTOR, timeout=None): + """Wait for invisible element (display:none), returns element""" + self.__check_not_none__("selector", selector) + expectation = EC.invisibility_of_element_located((locator, selector)) + return self.__driver_wait__(timeout=timeout).until(expectation) + + def ele_visible(self, element, timeout=None): + """Wait for visible condition element, returns self""" + self.__check_not_none__("element", element) + expectation = EC.visibility_of(element) + return self.__driver_wait__(timeout=timeout).until(expectation) + + def ele_text(self, selector, text, locator=By.CSS_SELECTOR, timeout=None): + """Wait if the given text is present in the specified element""" + self.__check_not_none__("selector", selector) + expectation = EC.text_to_be_present_in_element( + (locator, selector), text) + return self.__driver_wait__(timeout=timeout).until(expectation) + + def ele_value(self, selector, value, locator=By.CSS_SELECTOR, + timeout=None): + """Wait if the given value is present in the specified element""" + self.__check_not_none__("selector", selector) + expectation = EC.text_to_be_present_in_element_value( + (locator, selector), value) + return self.__driver_wait__(timeout=timeout).until(expectation) diff --git a/qacode/core/controls/__init__.py b/qacode/core/controls/__init__.py new file mode 100644 index 00000000..234b1e1c --- /dev/null +++ b/qacode/core/controls/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +"""package qacode.core.controls""" + + +from qacode.core.controls import control +from qacode.core.controls import control_config + + +__all__ = [ + 'control', + 'control_config', +] diff --git a/qacode/core/controls/control.py b/qacode/core/controls/control.py new file mode 100644 index 00000000..0a37eb9b --- /dev/null +++ b/qacode/core/controls/control.py @@ -0,0 +1,162 @@ +# -*- coding: utf-8 -*- +"""Package module qacode.core.controls.control""" + + +from qacode.core.controls.control_config import ControlConfig +from qacode.core.exceptions.control_error import ControlError + + +class Control(object): + """This class allow to reference remote NodeElement (JS) or Html Tag to + use checks , obtain information or handle actions using this instance. + Wrapper for Selenium class named 'WebElement' using wrapper of + WebDriver named qacode.core.bots.BotBase + """ + + def __init__(self, browser, **kwargs): + """Initialize an instance of ControlBase""" + self._browser = browser + self._log = self._browser.log + self._config = ControlConfig(**kwargs) + self._element = None + self._id = None + if self._config.search: + self.__search__() + + def __search__(self): + """Element must be ensure it's ready before call this""" + if not self._config.timeout: + self._element = self._browser.elements.find( + self._config.selector, locator=self._config.locator + ) + else: + self._element = self._browser.waits.find_wait( + self._config.selector, locator=self._config.locator, + timeout=self._config.timeout + ) + self._id = self._element._id + + def __is_staled__(self): + """TODO: doc method""" + if not self._element: + raise ControlError("Not web element, search it first", self) + return self._browser.js.ele_is_staled(self._element) + + def check_element_ready(self): + """TODO: doc method""" + if self.__is_staled__(): + raise ControlError( + "Staled element, disappeared from the DOM", self) + + def attr(self, name): + """TODO: doc method""" + self.check_element_ready() + return self._browser.elements.attr(self._element, name) + + def attr_value(self, name): + """TODO: doc method""" + self.check_element_ready() + return self._browser.elements.attr_value(self._element, name) + + def css(self, name): + """TODO: doc method""" + self.check_element_ready() + return self._browser.elements.css(self._element, name) + + def clear(self): + """Clear input element text value""" + self.check_element_ready() + self._browser.elements.clear(self._element) + + def type_text(self, text, clear=False): + """Type text on input element""" + self.check_element_ready() + if clear: + self.clear() + self._browser.elements.write(self._element, text) + + def click(self): + """Click on element""" + self.check_element_ready() + self._browser.elements.click(self._element) + + def wait_invisible(self, timeout=1): + """Wait for invisible element, chaining at returns""" + self.check_element_ready() + self._element = self._browser.waits.ele_invisible( + self._config.selector, + locator=self._config.locator, + timeout=timeout) + return self + + def wait_visible(self, timeout=1): + """Wait for invisible element, chaining at returns""" + self.check_element_ready() + self._element = self._browser.waits.ele_visible( + self._element, + timeout=timeout) + return self + + def wait_text(self, text, timeout=1): + """Wait if the given text is present in the specified control""" + self.check_element_ready() + if self.tag == 'input': + wait_method = self._browser.waits.ele_value + else: + wait_method = self._browser.waits.ele_text + is_text = wait_method( + self._config.selector, + text, + locator=self._config.locator, + timeout=timeout) + return is_text + + def wait_blink(self, to_visible=1, to_invisible=1): + """Wait until control pops and dissapears""" + return self.wait_visible( + timeout=to_visible).wait_invisible(timeout=to_invisible) + + def reload(self): + """Reload element, must be responsability of developer""" + self.__search__() + + @property + def config(self): + """TODO: doc method""" + return self._config + + @property + def id(self): + """TODO: doc method""" + self.check_element_ready() + return self._element._id + + @property + def text(self): + """TODO: doc method""" + self.check_element_ready() + return self._browser.elements.get_text(self._element) + + @property + def tag(self): + """TODO: doc method""" + self.check_element_ready() + return self._browser.elements.tag(self._element) + + @property + def is_displayed(self): + """TODO: doc method""" + self.check_element_ready() + return self._browser.elements.is_displayed(self._element) + + @property + def is_enabled(self): + """TODO: doc method""" + self.check_element_ready() + return self._browser.elements.is_enabled(self._element) + + @property + def is_selected(self): + """TODO: doc method""" + self.check_element_ready() + return self._browser.elements.is_selected(self._element) diff --git a/qacode/core/controls/control_config.py b/qacode/core/controls/control_config.py new file mode 100644 index 00000000..95cfb057 --- /dev/null +++ b/qacode/core/controls/control_config.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +"""TODO""" + + +from selenium.webdriver.common.by import By + + +class ControlConfig(object): + """TODO: doc class""" + + def __init__(self, **kwargs): + """TODO: doc method""" + self._config = kwargs + + @property + def name(self): + """TODO: doc method""" + return self._config.get("name") + + @property + def selector(self): + """TODO: doc method""" + return self._config.get("selector") + + @property + def locator(self): + """TODO: doc method""" + return self._config.get("locator") or By.CSS_SELECTOR + + @property + def search(self): + """TODO: doc method""" + return self._config.get("search") or False + + @property + def timeout(self): + """TODO: doc method""" + return self._config.get("timeout") or 0 + + @property + def pages(self): + """TODO: doc method""" + return self._config.get("pages") or [] diff --git a/qacode/core/exceptions/__init__.py b/qacode/core/exceptions/__init__.py old mode 100755 new mode 100644 index c17625c7..530056f0 --- a/qacode/core/exceptions/__init__.py +++ b/qacode/core/exceptions/__init__.py @@ -1,10 +1,16 @@ -# -*- coding: utf-8 -*- -"""package qacode.core.exceptions""" - - -from qacode.core.exceptions import control_exception -from qacode.core.exceptions import core_exception -from qacode.core.exceptions import page_exception - - -__all__ = ['control_exception', 'core_exception', 'page_exception'] +# -*- coding: utf-8 -*- +"""package qacode.core.exceptions""" + + +from qacode.core.exceptions import bot_error +from qacode.core.exceptions import browser_error +from qacode.core.exceptions import control_error +from qacode.core.exceptions import core_error + + +__all__ = [ + 'bot_error', + 'browser_error', + 'control_error', + 'core_error', +] diff --git a/qacode/core/exceptions/bot_error.py b/qacode/core/exceptions/bot_error.py new file mode 100644 index 00000000..5d6bd08e --- /dev/null +++ b/qacode/core/exceptions/bot_error.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +"""Main Exceptions for qacode.bot classes""" + + +from qacode.core.exceptions.core_error import CoreError + + +class BotError(CoreError): + """TODO: doc class""" + + def __init__(self, message, bot): + """TODO: doc method""" + self._message = "{} [at bot: {}]".format(message, str(bot)) + super(BotError, self).__init__(self._message) diff --git a/qacode/core/exceptions/browser_error.py b/qacode/core/exceptions/browser_error.py new file mode 100644 index 00000000..98e1e271 --- /dev/null +++ b/qacode/core/exceptions/browser_error.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +"""Main Exceptions for qacode.bot classes""" + + +from qacode.core.exceptions.core_error import CoreError + + +class BrowserError(CoreError): + """TODO: doc class""" + + def __init__(self, message, browser): + """TODO: doc method""" + self._message = "{} [at browser: {}]".format(message, str(browser)) + super(BrowserError, self).__init__(self._message) diff --git a/qacode/core/exceptions/control_error.py b/qacode/core/exceptions/control_error.py new file mode 100644 index 00000000..4dcea1ea --- /dev/null +++ b/qacode/core/exceptions/control_error.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +"""Main Exceptions for qacode.bot classes""" + + +from qacode.core.exceptions.core_error import CoreError + + +class ControlError(CoreError): + """TODO: doc class""" + + def __init__(self, message, control): + """TODO: doc method""" + self._message = "{} [at control: {}]".format(message, str(control)) + super(ControlError, self).__init__(self._message) diff --git a/qacode/core/exceptions/control_exception.py b/qacode/core/exceptions/control_exception.py deleted file mode 100644 index c0b73a12..00000000 --- a/qacode/core/exceptions/control_exception.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -"""Package qacode.core.exceptions""" - - -from qacode.core.exceptions.core_exception import CoreException - - -NOT_MSG_CONTROL = "Message Exception not defined for ControlException class" - - -class ControlException(CoreException): - """ControlBase and inherit classes can use this exception""" - - def __init__(self, msg=NOT_MSG_CONTROL, log=None, info_bot=None): - """Instance ControlException error to raise message from controls""" - super(ControlException, self).__init__( - msg=msg, log=log, info_bot=info_bot) - self.selector = info_bot.get("selector") or "" - - def __str__(self): - """Representation of class""" - msg = super(ControlException, self).__str__() - msg += "selector={}".format( - self.info_bot.get("selector")) - return msg diff --git a/qacode/core/exceptions/core_error.py b/qacode/core/exceptions/core_error.py new file mode 100644 index 00000000..d296e781 --- /dev/null +++ b/qacode/core/exceptions/core_error.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +"""Main Exceptions for qacode library""" + + +class CoreError(Exception): + """Base Exception class for inherit new exceptions on library""" + + def __init__(self, message): + """Allow to handle Qacode and/or Selenium exceptions + + Arguments: + message {str} -- Exception message + """ + self._message = message + + @property + def message(self): + """GET for 'message' property""" + return self._message diff --git a/qacode/core/exceptions/core_exception.py b/qacode/core/exceptions/core_exception.py deleted file mode 100644 index 51a53164..00000000 --- a/qacode/core/exceptions/core_exception.py +++ /dev/null @@ -1,53 +0,0 @@ -# -*- coding: utf-8 -*- -"""Main Exceptions for qacode library""" - - -from qacode.core.loggers.logger_manager import Log -from selenium.common.exceptions import WebDriverException - - -NOT_MSG = 'Exception without message' - - -class CoreException(Exception): - """Base Exception class for inherit new exceptions on library""" - - def __init__(self, msg=NOT_MSG, log=None, info_bot=None): - """Allow to handle Qacode and/or Selenium exceptions - - Keyword Arguments: - msg {str} -- Exception message (default: {NOT_MSG}) - log {Log} -- Logging class (default: {None}) - info_bot {dict} -- Qacode+Selenium information (default: {None}) - """ - self.is_just_msg = False - self.msg = msg - if info_bot is None: - info_bot = {} - self.is_just_msg = True - self.info_bot = info_bot - self.browser = self.info_bot.get("browser") or "" - self.mode = self.info_bot.get("mode") or "" - self.method = self.info_bot.get("method") or "" - self.err = info_bot.get("err") or {} - if log is None: - self.log = Log() - else: - self.log = log - self.log.error(str(self)) - - def __str__(self): - """Representation of class""" - msg = "" - if self.is_just_msg: - return self.msg - msg += " | " - msg += "browser='{}' ".format(self.browser) - msg += "mode='{}' ".format(self.mode) - msg += "method='{}' ".format(self.method) - if self.err is None: - return msg - if isinstance(self.err, WebDriverException): - msg += "{}{} - args='{}'".format( - "Selenium error: ", type(self.err), self.err.args) - return "{}".format(msg) diff --git a/qacode/core/exceptions/page_exception.py b/qacode/core/exceptions/page_exception.py deleted file mode 100644 index 3f56b07c..00000000 --- a/qacode/core/exceptions/page_exception.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -"""Package qacode.core.exceptions""" - -from qacode.core.exceptions.core_exception import CoreException - - -NOT_MSG_PAGE = "Message Exception not defined for PageException class" - - -class PageException(CoreException): - """Exception class that can be raise from Page classes""" - - def __init__(self, msg=NOT_MSG_PAGE, log=None, info_bot=None): - """This exception must be raised from PageBase or inherit classes""" - self.url = info_bot.get("url") or "" - super(PageException, self).__init__( - msg=msg, log=log, info_bot=info_bot) - - def __str__(self): - """Representation of class""" - return self.msg_info_bot(self.info_bot) - - def msg_info_bot(self, info_bot): - """Generate str message from param 'info_bot'""" - msg = super(PageException, self).msg_info_bot(info_bot) - msg += "url='{}' ".format(self.url) - return msg diff --git a/qacode/core/loggers/__init__.py b/qacode/core/loggers/__init__.py old mode 100755 new mode 100644 index b7d110a3..2775aeb8 --- a/qacode/core/loggers/__init__.py +++ b/qacode/core/loggers/__init__.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- -"""package qacode.core.loggers""" - - -from qacode.core.loggers import logger_manager -from qacode.core.loggers import logger_messages - - -__all__ = ['logger_manager', 'logger_messages'] +# -*- coding: utf-8 -*- +"""package qacode.core.loggers""" + + +from qacode.core.loggers.log import Log + + +__all__ = ['Log'] diff --git a/qacode/core/loggers/logger_manager.py b/qacode/core/loggers/log.py similarity index 88% rename from qacode/core/loggers/logger_manager.py rename to qacode/core/loggers/log.py index fd0e370f..92337226 100644 --- a/qacode/core/loggers/logger_manager.py +++ b/qacode/core/loggers/log.py @@ -21,12 +21,11 @@ class Log(object): def __init__(self, *args, **kwargs): """TODO: doc method""" - self._name = kwargs.get('log_name') or "qacode" + self._name = kwargs.get('name') or "qacode" self._name_file = "{}.log".format(self._name) - self._path = kwargs.get('log_path') or './logs/' - self._path_join = path_format( - file_path=self._path, file_name=self._name_file) - self._level = kwargs.get('log_level') or INFO + self._path = kwargs.get('path') or './logs/' + self._path_join = path_format(path=self._path, name=self._name_file) + self._level = kwargs.get('level') or INFO self._formatter = Formatter( ("[pid:%(process)d][%(asctime)s][%(name)s]" diff --git a/qacode/core/loggers/logger_messages.py b/qacode/core/loggers/logger_messages.py deleted file mode 100644 index 6484cafc..00000000 --- a/qacode/core/loggers/logger_messages.py +++ /dev/null @@ -1,65 +0,0 @@ -# -*- coding: utf-8 -*- -"""Module related with all logging messages""" - -# Commons -NOT_IMPLEMENTED = "Open an issue on github if raise here" # noqa:E501 -BAD_PARAM = "ctl_form | bad param '{}', invalid value" # noqa:E501 -# ControlBase -CB_SETTINGS_LOADING = "ctl | load_settings_keys: loading keys..." # noqa:E501 -CB_SETTINGS_LOADED = "ctl | load_settings_keys: loaded keys" # noqa:E501 -CB_SEARCH_DISABLED = "ctl | _load_search: Disabled element search" # noqa:E501 -CB_SEARCH_LOADING = "ctl | _load_search: searching element..." # noqa:E501 -CB_SEARCH_WAITING = "ctl | _load_search: waiting for element..." # noqa:E501 -CB_SEARCH_FOUND = "ctl | _load_search: element found!" # noqa:E501 -CB_SEARCH_FOUND_CHILD = "ctl | _load_search: element child found!" # noqa:E501 -CB_PROP_DISABLED = "ctl | _load_properties: disabled loading for properties" # noqa:E501 -CB_PROP_LOADING = "ctl | _load_properties: loading properties..." # noqa:E501 -CB_PROP_LOADED = "ctl | _load_properties: loaded properties" # noqa:E501 -CB_FINDCHILD_LOADING = "ctl | find_child: searching for selector='{}'" # noqa:E501 -CB_FINDCHILD_LOADED = "ctl | find_child: child element found" # noqa:E501 -CB_GETTAG_LOADING = "ctl | get_tag: searching for tag" # noqa:E501 -CB_GETTAG_LOADED = "ctl | get_tag: found same tag='{}'" # noqa:E501 -CB_TYPETEXT_LOADING = "ctl | type_text: typing text='{}'" # noqa:E501 -CB_CLEAR_LOADING = "ctl | clear: clearing text..." # noqa:E501 -CB_CLEAR_LOADED = "ctl | clear: cleared text" # noqa:E501 -CB_CLICK_LOADING = "ctl | click: clicking element..." # noqa:E501 -CB_CLICK_RETRY = "ctl | click: retry clicking element..." # noqa:E501 -CB_CLICK_LOADED = "ctl | click: clicked!" # noqa:E501 -CB_GETTEXT_LOADING = "ctl | get_text: obtaining text..." # noqa:E501 -CB_GETTEXT_FAILED = "ctl | get_text: failed at obtain text" # noqa:E501 -CB_GETTEXT_LOADED = "ctl | get_text: text obtained, text='{}'" # noqa:E501 -CB_GETATTRS_LOADING = "ctl | get_attrs: obtaining attrs..." # noqa:E501 -CB_GETATTRVALUE_LOADING = "ctl | get_attr_value: obtaining value for attr_name='{}'" # noqa:E501 -CB_GETATTRVALUE_LOADED = "ctl | get_attr_value: obtained attr_name='{}', value='{}'" # noqa:E501 -CB_GETATTRVALUE_FAILED = "ctl | get_attr_value: fail at obtain value" # noqa:E501 -CB_SETCSSRULE_LOADING = "ctl | set_css_value: setting new CSS rule, prop_name='{}', prop_value='{}'" # noqa:E501 -CB_SETCSSRULE_FAILED = "ctl | set_css_value: failed at set CSS rule" # noqa:E501 -CB_GETCSSRULE_LOADING = "ctl | get_css_value: obtaining css_value..." # noqa:E501 -CB_GETCSSRULE_LOADED = "ctl | get_css_value: css_value='{}'" # noqa:E501 -CB_RELOAD_LOADING = "{} | reload: reloading control..." # noqa:E501 -CB_RELOAD_LOADED = "{} | reload: reloaded control" # noqa:E501 -# ControlForm -CF_RULES_LOADING = "ctl_form | loading rules..." # noqa:E501 -CF_RULES_DISABLED = "ctl_form | disabled rules functionality for this control" # noqa:E501 -CF_RULES_PARSING = "ctl_form | parsing rules..." # noqa:E501 -CF_RULES_PARSED = "ctl_form | parsed rules" # noqa:E501 -CF_RULES_APPLYING = "ctl_form | applying rules..." # noqa:E501 -CF_RULES_APPLIED = "ctl_form | applied rules" # noqa:E501 -CF_RULES_APPLYING_TAG = "ctl_form | applying tag rule..." # noqa:E501 -CF_RULES_APPLIED_TAG = "ctl_form | applied tag rule" # noqa:E501 -CF_RELOAD_LOADED = "ctl_form | reload: reloaded ctl" # noqa:E501 -CF_BADTAG = "ctl_form | This tag can't be loaded due a check, element must contains tag equals to validation rule tag type" # noqa:E501 -# ControlDropdown -CDD_SELECT_LOADING = "ctl_dd | select: selecting..." # noqa:E501 -CDD_SELECT_LOADED = "ctl_dd | select: selected" # noqa:E501 -CDD_SELECT_LOADING = "ctl_dd | deselect: deselecting..." # noqa:E501 -CDD_DESESELECT_LOADED = "ctl_dd | select: deselected" # noqa:E501 -CDD_DESELECTALL_LOADING = "ctl_dd | dropdown_select: deselecting all..." # noqa:E501 -CDD_DESELECTALL_LOADED = "ctl_dd | dropdown_select: deselected all" # noqa:E501 -CDD_BADTAG = "ctl_dd | Can't use this for not