Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[qacode] new ControlTable class, #248 #264

Merged
merged 7 commits into from
Apr 19, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion qacode/configs/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@
"web_controls": {
"control_base": false,
"control_form": false,
"control_dropdown": false
"control_dropdown": false,
"control_table": false
},
"web_pages": false,
"benchmarks": true
Expand Down
12 changes: 8 additions & 4 deletions qacode/core/loggers/logger_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@
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_form | dropdown_select: deselecting all..." # noqa:E501
CDD_DESELECTALL_LOADED = "ctl_form | dropdown_select: deselected all" # noqa:E501
CDD_LOADED = "ctl_form | ctl.dropdown property for <select>" # noqa:E501
CDD_BADTAG = "Can't use this for not <select> tag element" # 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_LOADED = "ctl_dd | ctl.dropdown property for <select>" # noqa:E501
CDD_BADTAG = "ctl_dd | Can't use this for not <select> tag element" # noqa:E501
# ControlTable
CT_BADTAG = "ctl_tb | Can't use this for not <table> tag element" # noqa:E501
CT_LOADED = "ctl_tb | ctl.table property for <table>" # noqa:E501
CT_TBLNOTCHILD = "ctl_tb | this table haven't got '{}' selector" # noqa:E501
5 changes: 4 additions & 1 deletion qacode/core/webs/controls/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@


from qacode.core.webs.controls import control_base
from qacode.core.webs.controls import control_dropdown
from qacode.core.webs.controls import control_form
from qacode.core.webs.controls import control_table


__all__ = ['control_base', 'control_form']
__all__ = [
'control_base', 'control_dropdown', 'control_form', 'control_table']
4 changes: 2 additions & 2 deletions qacode/core/webs/controls/control_dropdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ def __check_reload__form__(self):
if it's neccessary reload element properties
"""
super(ControlDropdown, self).__check_reload__form__()
reload_dropdown_needed = not self.element or not self.dropdown
if reload_dropdown_needed:
reload_needed = not self.element or not self.dropdown
if reload_needed:
self.reload(**self.settings)

def reload(self, **kwargs):
Expand Down
7 changes: 6 additions & 1 deletion qacode/core/webs/controls/control_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class ControlForm(ControlBase):
strict_tag = None
# tag=select
IS_DROPDOWN = None
# tag=select
IS_TABLE = None

def __init__(self, bot, **kwargs):
"""Instance of ControlForm. Load properties from settings dict.
Expand Down Expand Up @@ -97,14 +99,17 @@ def __load_strict_tag__(self, strict_tag):
instance ControlForm specific properties
"""
self.IS_DROPDOWN = False
self.IS_TABLE = False
self.strict_tag = strict_tag
valid_tags = ['select']
valid_tags = ['select', 'table']
self.bot.log.debug(MSG.CF_STRICTTAG_LOADING)
if self.strict_tag.value not in valid_tags:
raise ControlException(
msg="This tag can be loaded as strict_rule")
if self.tag == HtmlTag.TAG_SELECT.value:
self.IS_DROPDOWN = True
if self.tag == HtmlTag.TAG_TABLE.value:
self.IS_TABLE = True
self.bot.log.debug(MSG.CF_STRICTTAG_LOADED)
return True

Expand Down
133 changes: 133 additions & 0 deletions qacode/core/webs/controls/control_table.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# -*- coding: utf-8 -*-
"""Package module qacode.core.webs.control_form"""


from qacode.core.exceptions.control_exception import ControlException
from qacode.core.exceptions.core_exception import CoreException
from qacode.core.loggers import logger_messages as MSG
from qacode.core.webs.controls.control_base import ControlBase
from qacode.core.webs.controls.control_form import ControlForm
from selenium.common.exceptions import WebDriverException


class ControlTable(ControlForm):
"""TODO: doc class"""

_table = None
_rows = None

# public properties

caption = None
thead = None
tfoot = None
tbodies = None

def __init__(self, bot, **kwargs):
netzulo marked this conversation as resolved.
Show resolved Hide resolved
"""Instance of ControlForm. Load properties from settings dict.
Some elements need to search False to be search at future
"""
kwargs.update({"instance": "ControlTable"})
strict_rules = kwargs.get("strict_rules")
if not bool(strict_rules):
strict_rules.append(
{"tag": "table", "type": "tag", "severity": "hight"})
super(ControlTable, self).__init__(bot, **kwargs)
if not self.IS_TABLE and self.tag is not None:
raise ControlException(msg=MSG.CT_BADTAG)
self.bot.log.debug(MSG.CT_LOADED)

def __load_table__(self, element=None):
netzulo marked this conversation as resolved.
Show resolved Hide resolved
netzulo marked this conversation as resolved.
Show resolved Hide resolved
"""Allow to load all TR > TD items from a TABLE element

Before structure some checks are necessary for some children elements:
caption {ControlBase}-- optional <caption> element

Examples:
Use case 1. TABLE > (TR > TH)+(TR > TD)
Use case 2. TABLE > (THEAD > (TR > TH))+(TBODY > (TR > TH))
"""
if element is None:
element = self.element
self._table = ControlBase(self.bot, **{
"selector": self.selector,
"element": element})
# Preload
self.tbodies = self.__try__("find_children", "tbodies")
is_html5 = False
if bool(self.tbodies):
is_html5 = True
self.caption = self.__try__("find_child", "caption")
self.thead = self.__try__("find_child", "thead")
self.tfoot = self.__try__("find_child", "tfoot")
# Load column headers
if not is_html5:
columns = self._table.find_children("tr :not(td)") # noqa
for column in columns:
column.name = column.text
rows = []
ctls_rows = self._table.find_children("tr")
for index, ctl_row in enumerate(ctls_rows):
if index == 0:
rows.append(self.__get_row__(ctl_row, "th"))
else:
rows.append(self.__get_row__(ctl_row, "td"))
self._rows = rows
else:
# is_hmtl5==True
# raise NotImplementedError("TODO: WIP zone")
pass
# raise NotImplementedError("TODO: WIP zone")
netzulo marked this conversation as resolved.
Show resolved Hide resolved

def __get_row__(self, ctl_row, selector):
"""WARNING: this method just can be used from __load_table__"""
row = []
for cell in ctl_row.find_children(selector):
text = cell.get_text()
cell.settings.update({"name": text})
cell.name = text
row.append(cell)
return row

def __try__(self, method, selector):
"""Allow to exec some method to handle exception"""
try:
return getattr(self._table, method)(selector)
except (ControlException, CoreException, WebDriverException):
self.bot.log.debug(MSG.CT_TBLNOTCHILD.format(selector))
return None

def __check_reload__form__(self):
"""Allow to check before methods calls to ensure
if it's neccessary reload element properties
"""
super(ControlTable, self).__check_reload__form__()
reload_needed = not self.element or not self.table
if reload_needed:
self.reload(**self.settings)
if not self.IS_TABLE and self.tag is not None:
raise ControlException(msg=MSG.CT_BADTAG)

def reload(self, **kwargs):
"""Reload 'self.settings' property:dict and call to instance
logic with new configuration
"""
super(ControlTable, self).reload(**kwargs)
self.__load_table__(element=self.element)

@property
def table(self):
"""GETTER for 'table' property"""
return self._table

@table.setter
def table(self, value):
"""SETTER for 'table' property"""
if value is None or not isinstance(value, ControlBase):
raise ControlException("Can't set not 'Control' instance")
self.__load_table__(element=value)

@property
def rows(self):
"""GETTER for 'rows' property"""
return self._rows
131 changes: 131 additions & 0 deletions tests/001_functionals/suite_008_controltable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# -*- coding: utf-8 -*-
"""Testsuite for package qacode.core.webs.controls"""


import pytest
from qacode.core.testing.test_info import TestInfoBotUnique
from qacode.core.webs.controls.control_base import ControlBase
from qacode.core.webs.controls.control_table import ControlTable
from qautils.files import settings
from selenium.webdriver.remote.webelement import WebElement


SETTINGS = settings(file_path="qacode/configs/")
SKIP_CONTROLS = SETTINGS['tests']['skip']['web_controls']['control_table']
SKIP_CONTROLS_MSG = 'web_controls DISABLED by config file'


class TestControlDropdown(TestInfoBotUnique):
"""Test Suite for ControlBase class"""

# app from config
app = None
# page from config: app
page = None
url = None
# page from config: app
page_inputs = None
url_inputs = None
# elements from config: page
form_login = None
txt_username = None
txt_password = None
btn_submit = None
# elements from config: page_data
dd_base = None
dd_menu_data = None
dd_menu_data_lists = None
tbl_ok = None

@classmethod
def setup_class(cls, **kwargs):
"""TODO: doc method"""
super(TestControlDropdown, cls).setup_class(
config=settings(file_path="qacode/configs/"),
skip_force=SKIP_CONTROLS)
cls.add_property('app', cls.settings_app('qadmin'))
# page
cls.add_property('page', cls.settings_page('qacode_login'))
cls.add_property('url', cls.page.get('url'))
cls.add_property('form_login', cls.settings_control('form_login'))
cls.add_property('txt_username', cls.settings_control('txt_username'))
cls.add_property('txt_password', cls.settings_control('txt_password'))
cls.add_property('btn_submit', cls.settings_control('btn_submit'))
# page_inputs
cls.add_property('page_inputs', cls.settings_page('qacode_inputs'))
cls.add_property('url_inputs', cls.page_inputs.get('url'))
cls.add_property('dd_base', cls.settings_control('dd_base'))
cls.add_property('dd_menu_data', cls.settings_control('dd_menu_data'))
cls.add_property(
'dd_menu_data_lists', cls.settings_control('dd_menu_data_lists'))
cls.add_property('tbl_ok', cls.settings_control('tbl_ok'))

def setup_method(self, test_method):
"""Configure self.attribute"""
super(TestControlDropdown, self).setup_method(
test_method, config=settings(file_path="qacode/configs/"))

def setup_login_to_data(self):
"""Do login before to exec some testcases"""
# setup_login
self.bot.navigation.get_url(self.page.get('url'), wait_for_load=10)
txt_username = self.bot.navigation.find_element(
self.txt_username.get("selector"))
txt_password = self.bot.navigation.find_element(
self.txt_password.get("selector"))
btn_submit = self.bot.navigation.find_element(
self.btn_submit.get("selector"))
self.bot.navigation.ele_write(txt_username, "admin")
self.bot.navigation.ele_write(txt_password, "admin")
self.bot.navigation.ele_click(btn_submit)
self.bot.navigation.ele_click(
self.bot.navigation.find_element_wait(
self.dd_menu_data.get("selector")))
self.bot.navigation.ele_click(
self.bot.navigation.find_element_wait(
self.dd_menu_data_lists.get("selector")))
# end setup_login

@pytest.mark.skipIf(SKIP_CONTROLS, SKIP_CONTROLS_MSG)
@pytest.mark.parametrize("on_instance_search", [True, False])
@pytest.mark.parametrize("auto_reload", [True, False])
@pytest.mark.parametrize("strict_rules", [
[{"tag": "table", "type": "tag", "severity": "hight"}]
])
def test_controltable_instance(self, on_instance_search,
strict_rules, auto_reload):
"""Testcase: test_controltable_instance"""
self.setup_login_to_data()
cfg = self.tbl_ok
cfg.update({
"instance": "ControlTable",
"on_instance_search": on_instance_search,
"auto_reload": auto_reload,
"strict_rules": strict_rules
})
# functional testcases
ctl = ControlTable(self.bot, **cfg)
self.assert_is_instance(ctl, ControlTable)
self.assert_equals(ctl.selector, cfg.get('selector'))
self.assert_equals(ctl.instance, cfg.get('instance'))
self.assert_equals(ctl.name, cfg.get('name'))
self.assert_equals(ctl.locator, 'css selector')
self.assert_equals(
ctl.on_instance_search, cfg.get('on_instance_search'))
self.assert_equals(ctl.auto_reload, cfg.get('auto_reload'))
self.assert_equals(
len(ctl.strict_rules), len(cfg.get('strict_rules')))
if on_instance_search:
self.assert_is_instance(ctl.element, WebElement)
if auto_reload is not None:
self.assert_none(ctl.table)
ctl.reload(**ctl.settings)
self.assert_is_instance(ctl.table, ControlBase)
self.assert_is_instance(ctl.rows, list)
# Use case 1. not html5:: TABLE > (TR > TH)+(TR > TD)
for row in ctl.rows:
self.assert_is_instance(row, list)
for cell in row:
self.assert_is_instance(cell, ControlBase)
# Use case 2. html5:: TABLE > (THEAD > (TR > TH))+(TBODY > (TR > TH))
# raise NotImplementedError("TODO: WIP zone")