# -*- coding: utf-8 -*-
"""Base module for inherit new Test Suites"""
import os
import re
import time
import pytest
from qacode.core.bots.bot_base import BotBase
from qacode.core.exceptions.core_exception import CoreException
from qacode.core.loggers.logger_manager import LoggerManager
from qatestlink.core.testlink_manager import TLManager
ASSERT_MSG_DEFAULT = "Fails at '{}': actual={}, expected={}"
ASSERT_REGEX_URL = r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+" # noqa: E501
[docs]class TestInfoBase(object):
"""Base class for inherit new Test classes"""
is_loaded = False
log = None
config = None
tlm = None # Testlink Manager class
[docs] @classmethod
def load(cls, config):
"""Load default config dict"""
if config is None and not cls.is_loaded:
raise CoreException(msg="Bad param 'config' provided")
cls.add_property('config', value=config)
if cls.log is None:
config_bot = cls.config.get('bot')
log_path = "{}/".format(
config_bot.get('log_output_file'))
lgm = LoggerManager(
log_path=log_path,
log_name=config_bot.get('log_name'),
log_level=config_bot.get('log_level')
)
cls.add_property('log', lgm.logger)
tl_key = cls.config.get('testlink')
if cls.tlm is None and tl_key is not None:
cls.tlm = TLManager(settings=tl_key)
cls.is_loaded = True
[docs] @classmethod
def bot_open(cls, config):
"""Open browser using BotBase instance
Returns:
BotBase -- wrapper browser handler for selenium
"""
return BotBase(**config)
[docs] @classmethod
def bot_close(cls, bot):
"""Close bot calling bot.close() from param"""
return bot.close()
[docs] @classmethod
def cfg_apps(cls):
"""Obtain inherit dict from 'cls.config' dict named
'config.tests.apps'
"""
if cls.config is None:
raise CoreException(msg="Call to cls.load() first")
return cls.config.get('tests').get('apps')
[docs] @classmethod
def cfg_pages(cls):
"""Obtain inherit dict from 'cls.config' dict named
'config.tests.apps[i].pages'
"""
if cls.config is None:
raise CoreException(msg="Call to cls.load() first")
pages = []
for app in cls.cfg_apps():
pages.extend(app.get('pages'))
return pages
[docs] @classmethod
def cfg_controls(cls):
"""Obtain inherit dict from 'cls.config' dict named
'config.tests.apps[i].pages[j].controls'
"""
if cls.config is None:
raise CoreException(msg="Call to cls.load() first")
controls = []
for page in cls.cfg_pages():
controls.extend(page.get('controls'))
return controls
[docs] @classmethod
def cfg_app(cls, app_name):
"""Obtain inherit dict from 'cls.config' dict named
'config.tests.apps' filtering by 'app_name' param
"""
for app in cls.cfg_apps():
if app.get('name') == app_name:
return app
raise Exception(
"Not found for: app_name={}".format(
app_name))
[docs] @classmethod
def cfg_page(cls, page_name, app_name=None):
"""Obtain inherit dict from 'cls.config' dict named
'config.tests.apps[i].pages' filtering by 'page_name' param
"""
apps = []
if app_name is None:
apps.extend(cls.cfg_apps())
else:
apps.append(cls.cfg_app(app_name))
for app in apps:
for page in app.get('pages'):
if page.get('name') == page_name:
return page
[docs] @classmethod
def cfg_control(cls, control_name, page_name=None, app_name=None):
"""Obtain inherit dict from 'cls.config' dict named
'config.tests.apps[i].pages[j].controls' filtering by
'control_name' param
"""
controls = []
if page_name is None:
controls.extend(cls.cfg_controls())
else:
controls.extend(cls.cfg_page(page_name, app_name=app_name))
for control in controls:
if control.get('name') == control_name:
return control
[docs] @classmethod
def assert_message(cls, assert_name, actual, expected, msg=None):
"""Generate assert message for method that calls for it
Arguments:
assert_name {str} -- Assert method name that call
actual {any} -- Actual value to compare
expected {any} -- Expected value to compare
Keyword Arguments:
msg {[type]} -- [description] (default: {None})
Returns:
str -- Message to be use on Assert method
"""
if msg is not None:
return msg
return ASSERT_MSG_DEFAULT.format(
assert_name,
actual,
expected)
[docs] def setup_method(self, test_method, **kwargs):
"""Configure self.attribute"""
self.load(kwargs.get('config'))
self.log.info("Started testcase named='{}'".format(
test_method.__name__))
[docs] def teardown_method(self, test_method):
"""Unload self.attribute"""
self.log.info("Finished testcase named='{}'".format(
test_method.__name__))
[docs] @classmethod
def add_property(cls, name, value=None):
"""Add property to test instance using param 'name', will setup
None if any value it's passed by param
"""
setattr(cls, name, value)
[docs] def timer(self, wait=5, print_each=5):
"""Timer to sleep browser on testcases
Keyword Arguments:
wait {int} -- seconds to wait (default: {5})
print_each {int} -- print message each seconds, must be divisible
by 5, negatives are accepted (default: {5})
Raises:
Exception -- [description]
"""
msg_err = "Timer can't works if print_each param isn't divisible by 1"
if (print_each % 1) != 0:
raise Exception(msg_err)
while wait > 0:
self.sleep(print_each)
wait -= print_each
[docs] def sleep(self, wait=0):
"""Just call to native python time.sleep method
Keyword Arguments:
wait {int} -- Wait time on Runtime execution before execute
next lane of code (default: {0})
"""
if wait > 0:
time.sleep(wait)
[docs] def assert_equals(self, actual, expected, msg=None):
"""Allow to compare 2 values and check if 1st it's equals to
2nd value
"""
_msg = self.assert_message("assert_equals", actual, expected, msg=msg)
if actual != expected:
raise AssertionError(actual, expected, _msg)
return True
[docs] def assert_not_equals(self, actual, expected, msg=None):
"""Allow to compare 2 value to check if 1st isn't equals to
2nd value
"""
_msg = self.assert_message(
"assert_not_equals", actual, expected, msg=msg)
if actual == expected:
raise AssertionError(actual, expected, _msg)
return True
[docs] def assert_equals_url(self, actual, expected, msg=None, wait=0):
"""Allow to compare 2 urls and check if 1st it's equals to 2nd url
Arguments:
actual {type} -- actual value
expected {type} -- expected value
Keyword Arguments:
wait {int} -- Wait time on Runtime execution before execute
next lane of code (default: {0})
Raises:
AssertionError -- [description]
"""
_msg = self.assert_message(
"assert_equals_url", actual, expected, msg=msg)
self.sleep(wait)
if actual != expected:
raise AssertionError(actual, expected, _msg)
return True
[docs] def assert_not_equals_url(self, actual, expected, msg=None, wait=0):
"""Allow to compare 2 urls to check if 1st isn't equals to 2nd url"""
_msg = self.assert_message(
"assert_not_equals_url", actual, expected, msg=msg)
self.sleep(wait)
if actual == expected:
raise AssertionError(actual, expected, _msg)
return True
[docs] def assert_contains_url(self, actual, contains, msg=None, wait=0):
"""Allow to compare 2 urls and check if 1st contains 2nd url"""
_msg = self.assert_message(
"assert_contains_url", actual, contains, msg=msg)
self.sleep(wait)
if actual not in contains:
raise AssertionError(actual, contains, _msg)
return True
[docs] def assert_not_contains_url(self, actual, contains, msg=None, wait=0):
"""Allow to compare 2 urls and check if 1st not contains 2nd url"""
_msg = self.assert_message(
"assert_not_contains_url", actual, contains, msg=msg)
self.sleep(wait)
if actual in contains:
raise AssertionError(actual, contains, _msg)
return True
[docs] def assert_is_instance(self, instance, class_type, msg=None):
"""Allow to encapsulate method assertIsInstance(obj, cls, msg='')"""
_msg = self.assert_message(
"assert_is_instance", instance, class_type, msg=msg)
if not isinstance(class_type, type):
class_type = type(class_type)
if not isinstance(instance, class_type):
raise AssertionError(instance, class_type, _msg)
return True
[docs] def assert_raises(self, expected_exception, function, *args, **kwargs):
"""Allow to encapsulate pytest.raises method(
*args=(
expected_exception,
function,
),
**kwargs={
msg: ASSERT_MSG_DEFAULT
}
)
"""
_msg = self.assert_message(
"assert_raises",
"TODO:not implemented value",
expected_exception, msg=kwargs.get('msg'))
# https://docs.pytest.org/en/latest/reference.html#pytest-raises
kwargs.update({"message": _msg})
return pytest.raises(expected_exception, function, *args, **kwargs)
[docs] def assert_greater(self, actual, greater, msg=None):
"""Allow to encapsulate method assertGreater(a, b, msg=msg)"""
_msg = self.assert_message(
"assert_greater", actual, greater, msg=msg)
if actual < greater:
raise AssertionError(actual, greater, _msg)
return True
[docs] def assert_lower(self, actual, lower, msg=None):
"""Allow to encapsulate method assertLower(a, b, msg=msg)"""
_msg = self.assert_message(
"assert_lower", actual, lower, msg=msg)
if actual > lower:
raise AssertionError(actual, lower, _msg)
return True
[docs] def assert_in(self, actual, valid_values, msg=None):
"""Allow to compare if value it's in to 2nd list of values"""
_msg = self.assert_message(
"assert_in", actual, valid_values, msg=msg)
if actual not in valid_values:
raise AssertionError(actual, valid_values, _msg)
return True
[docs] def assert_not_in(self, actual, invalid_values, msg=None):
"""Allow to compare if value it's not in to 2nd list of values"""
_msg = self.assert_message(
"assert_not_in", actual, invalid_values, msg=msg)
if actual in invalid_values:
raise AssertionError(actual, invalid_values, _msg)
return True
[docs] def assert_regex(self, actual, pattern, msg=None):
"""Allow to compare if value match pattern"""
_msg = self.assert_message(
"assert_regex", actual, pattern, msg=msg)
is_match = re.match(pattern, actual)
if not is_match:
raise AssertionError(actual, pattern, _msg)
return True
[docs] def assert_not_regex(self, actual, pattern, msg=None):
"""Allow to compare if value not match pattern"""
_msg = self.assert_message(
"assert_not_regex", actual, pattern, msg=msg)
is_match = re.match(pattern, actual)
if is_match:
raise AssertionError(actual, pattern, _msg)
return True
[docs] def assert_regex_url(self, actual, pattern=None, msg=None):
"""Allow to compare if value match url pattern, can use
custom pattern
"""
if not pattern:
pattern = ASSERT_REGEX_URL
return self.assert_regex(actual, pattern, msg=msg)
[docs] def assert_path_exist(self, actual, is_dir=True, msg=None):
"""Allow to check if path exist, can check if is_dir also"""
_msg = self.assert_message(
"assert_path_exist",
actual,
"is_dir={}".format(is_dir),
msg=msg)
if not os.path.exists(actual):
raise AssertionError(actual, "PATH_NOT_EXIST", _msg)
_is_dir = os.path.isdir(actual)
if is_dir:
if not _is_dir:
raise AssertionError(actual, "PATH_NOT_DIR", _msg)
else:
if _is_dir:
raise AssertionError(actual, "PATH_IS_DIR_AND_MUST_NOT", _msg)
return True
[docs] def assert_path_not_exist(self, actual, msg=None):
"""Allow to check if path not exist, can check if is_dir also"""
_msg = self.assert_message(
"assert_path_not_exist", actual, "", msg=msg)
if os.path.exists(actual):
raise AssertionError(actual, "PATH_EXIST_AND_MUST_NOT", _msg)
return True
[docs] def assert_true(self, actual, msg=None):
"""Allow to compare and check if value it's equals to 'True'"""
self.assert_is_instance(actual, bool)
self.assert_equals(actual, True, msg=msg)
return True
[docs] def assert_false(self, actual, msg=None):
"""Allow to compare and check if value it's equals to 'False'"""
self.assert_is_instance(actual, bool)
self.assert_equals(actual, False, msg=msg)
return True
[docs] def assert_none(self, actual, msg=None):
"""Allow to compare and check if value it's equals to 'None'"""
return self.assert_equals(actual, None, msg=msg)
[docs] def assert_not_none(self, actual, msg=None):
"""Allow to compare and check if value it's not equals to 'None'"""
return self.assert_not_equals(actual, None, msg=msg)
[docs]class TestInfoBot(TestInfoBase):
"""Inherit class what implements bot on each testcase"""
bot = None
[docs] def setup_method(self, test_method, **kwargs):
"""Configure self.attribute.
If skipIf mark applied and True as first param for args tuple
then not open bot
"""
super(TestInfoBot, self).setup_method(test_method, **kwargs)
if 'skipIf' in dir(test_method) and test_method.skipIf.args[0]:
pytest.skip(test_method.skipIf.args[1])
return
if not isinstance(self.bot, BotBase):
self.add_property('bot', value=self.bot_open(self.config))
[docs] def teardown_method(self, test_method, close=True):
"""Unload self.attribute, also close bot"""
super(TestInfoBot, self).teardown_method(test_method)
try:
if close:
self.bot_close(self.bot)
else:
self.log.debug(
"Not closing bot by optional param 'close'")
except Exception as err:
self.log.error(
"Fails at try to close bot: {}".format(
err))
[docs]class TestInfoBotUnique(TestInfoBot):
"""Inherit class what implements bot on each testcase"""
[docs] @classmethod
def setup_class(cls, **kwargs):
"""Configure 'cls.attribute'. If name start with 'test_' and have
decorator skipIf with value True, then not open bot
"""
tests_methods = []
skip_methods = []
skip_force = kwargs.get('skip_force')
for method_name in dir(cls):
if method_name.startswith("test_"):
method = getattr(cls, method_name)
tests_methods.append(method)
if 'skipIf' in dir(method) and method.skipIf.args[0]:
skip_methods.append(method)
if tests_methods == skip_methods or skip_force:
pytest.skip("Testsuite skipped")
else:
if not isinstance(cls.bot, BotBase):
cls.load(kwargs.get('config'))
cls.add_property(
'bot', value=cls.bot_open(cls.config))
[docs] @classmethod
def teardown_class(cls):
"""Unload self.attribute, closing bot from 'cls.bot' property"""
if cls.bot:
cls.bot_close(cls.bot)
[docs] def teardown_method(self, test_method, close=False):
"""Unload self.attribute, also disable closing bot from TestInfoBot"""
super(TestInfoBotUnique, self).teardown_method(
test_method, close=close)