Skip to content

Commit

Permalink
[FT] add find_child/s method + TCs #235 (#237)
Browse files Browse the repository at this point in the history
* [qacode] Add find_child/s method + TCs #235
  • Loading branch information
netzulo authored Mar 10, 2019
1 parent fdf2a1f commit 7de187d
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### Added
- Autodoc for package 'qacode.core.exceptions' #223
- Add find_child/s method + TCs #235

### Changed
- Improve Control Search #222
Expand Down
13 changes: 13 additions & 0 deletions qacode/configs/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@
{ "name": "btn_submit", "selector": "#btnLogin", "on_instance_search": true, "on_instance_load": false }
]
},
{ "name": "qacode_lists",
"url": "http://ntz-qa.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": "qacode_inputs",
"url": "http://ntz-qa.tk:83/qacode/forms/inputs",
"locator": "css selector",
Expand Down
60 changes: 52 additions & 8 deletions qacode/core/bots/modules/nav_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ def __init__(self, driver, log, driver_wait=None, driver_actions=None,
self.driver_actions = driver_actions
self.driver_touch = driver_touch

def get_driver_wait(self, driver_wait=None):
"""TODO: doc method"""
if driver_wait is None and self.driver_wait is None:
raise CoreException(message='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:
Expand Down Expand Up @@ -242,10 +250,7 @@ def find_element_wait(self, selector,
WebElement -- element through selenium
WebDriverWait class
"""
if driver_wait is None and self.driver_wait is None:
raise CoreException(message='Nav instanced without driver_wait')
if driver_wait is None:
driver_wait = self.driver_wait
driver_wait = self.get_driver_wait(driver_wait=driver_wait)
try:
return driver_wait.until(
EC.presence_of_element_located((locator, selector)))
Expand Down Expand Up @@ -276,17 +281,56 @@ def find_elements_wait(self, selector,
WebElement -- element through selenium
WebDriverWait class
"""
if driver_wait is None and self.driver_wait is None:
raise CoreException(message='Nav instanced without driver_wait')
if driver_wait is None:
driver_wait = self.driver_wait
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"""
if element is None or not isinstance(element, WebElement):
raise CoreException(
message="Can't find child if not WebElement found")
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")
raise CoreException(err)

def find_element_children(self, element, child_selector,
locator=By.CSS_SELECTOR):
"""TODO: doc method"""
if element is None or not isinstance(element, WebElement):
raise CoreException(
message="Can't find children if not WebElement 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")
raise CoreException(err)

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()
Expand Down
59 changes: 51 additions & 8 deletions qacode/core/webs/controls/control_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
ElementNotVisibleException, NoSuchElementException
)
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.webelement import WebElement


class ControlBase(object):
Expand All @@ -22,6 +23,7 @@ class ControlBase(object):
CB_SEARCH_LOADING = "control | _load_search: searching element..."
CB_SEARCH_WAITING = "control | _load_search: waiting for element..."
CB_SEARCH_FOUND = "control | _load_search: element found!"
CB_SEARCH_FOUND_CHILD = "control | _load_search: element child found!"
CB_PROP_DISABLED = ("control | _load_properties: "
"disabled loading for properties")
CB_PROP_LOADING = "control | _load_properties: loading properties..."
Expand Down Expand Up @@ -98,7 +100,9 @@ def load(self, **kwargs):
# needed for self._load_* functions
self.load_settings_keys(kwargs.copy(), update=True)
# instance logic
self._load_search(enabled=self.on_instance_search)
self._load_search(
enabled=self.on_instance_search,
element=self.settings.get("element"))
self._load_properties(enabled=self.on_instance_load)

def load_settings_keys(self, settings, update=False, default_keys=None):
Expand All @@ -113,7 +117,8 @@ def load_settings_keys(self, settings, update=False, default_keys=None):
("on_instance_search", False),
("on_instance_load", False),
("auto_reload", True),
("instance", 'ControlBase')
("instance", 'ControlBase'),
("element", None)
]
default_settings = defaultdict(list, default_keys)
updated_settings = {}
Expand All @@ -137,14 +142,21 @@ def load_settings_keys(self, settings, update=False, default_keys=None):
self.settings = updated_settings
self.bot.log.debug(self.CB_SETTINGS_LOADED)

def _load_search(self, enabled=False, selector_multiple_pos=0):
def _load_search(self, enabled=False, element=None):
if not enabled or enabled is None:
self.bot.log.debug(self.CB_SEARCH_DISABLED)
return False
self.bot.log.debug(self.CB_SEARCH_LOADING)
try:
self.element = self.bot.navigation.find_element(
self.selector, locator=self.locator)
if element is not None:
if isinstance(element, WebElement):
msg = "Child is not instance of WebElement"
raise ControlException(message=msg)
self.bot.log.debug(self.CB_SEARCH_FOUND_CHILD)
self.element = element
else:
self.element = self.bot.navigation.find_element(
self.selector, locator=self.locator)
except CoreException:
self.bot.log.warning(self.CB_SEARCH_WAITING)
self.element = self.bot.navigation.find_element_wait(
Expand Down Expand Up @@ -199,11 +211,42 @@ def find_child(self, selector, locator=By.CSS_SELECTOR):
"""
self.bot.log.debug(self.CB_FINDCHILD_LOADING.format(selector))
settings = {"locator": locator, "selector": selector}
ele = self.bot.navigation.find_element_child(
self.element, selector, locator=locator)
settings.update({"element": ele})
ctl = ControlBase(self.bot, **settings)
if ctl:
self.bot.log.debug(self.CB_FINDCHILD_LOADED)
return ctl

def find_children(self, selector, locator=By.CSS_SELECTOR):
"""Find children elements using bot with default By.CSS_SELECTOR
strategy for internal element trought selenium WebElement
Arguments:
selector {str} -- string search for locator type
Keyword Arguments:
locator {[selenium.webdriver.common.by.By]} -- string type to
use on this selenium search request
(default: {By.CSS_SELECTOR})
Returns:
list(ControlBase) -- instanced list of base element using
qacode library object
"""
self.bot.log.debug(self.CB_FINDCHILD_LOADING.format(selector))
settings = {"locator": locator, "selector": selector}
elements = self.bot.navigation.find_element_children(
self.element, selector, locator=locator)
ctls = list()
for ele in elements:
settings.update({"element": ele})
ctls.append(ControlBase(self.bot, **settings))
if bool(ctls):
self.bot.log.debug(self.CB_FINDCHILD_LOADED)
return ctls

def get_tag(self):
"""Returns tag_name from Webelement"""
self.bot.log.debug(self.CB_GETTAG_LOADING)
Expand Down Expand Up @@ -386,9 +429,9 @@ def reload(self, **kwargs):
self.load_settings_keys(config, update=True)
# instance logic
self._load_search(
enabled=self.settings.get('on_instance_search'))
self._load_properties(
enabled=self.settings.get('on_instance_load'))
enabled=self.on_instance_search,
element=self.settings.get("element"))
self._load_properties(enabled=self.on_instance_load)
if class_name == 'ControlBase':
self.bot.log.debug(self.CB_RELOAD_LOADED.format(class_name))

Expand Down
7 changes: 4 additions & 3 deletions qacode/core/webs/controls/control_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ def _load(self, **kwargs):
self.strict_attrs = []
self.strict_css_props = []
# instance logic
self._load_search(enabled=self.on_instance_search)
self._load_search(
enabled=self.on_instance_search,
element=self.settings.get("element"))
self._load_properties(enabled=self.on_instance_load)
self._load_strict(enabled=self.on_instance_strict)

Expand Down Expand Up @@ -313,8 +315,7 @@ def reload(self, **kwargs):
logic with new configuration
"""
super(ControlForm, self).reload(**kwargs)
self._load_strict(
enabled=self.settings.get('on_instance_strict'))
self._load_strict(enabled=self.on_instance_strict)
self.bot.log.debug(self.CF_RELOAD_LOADED)

def dropdown_select(self, text, by_value=False, by_index=False):
Expand Down
8 changes: 6 additions & 2 deletions qacode/core/webs/controls/control_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ def load(self, **kwargs):
self.load_settings_keys(kwargs.copy(), update=True)
# instance logic
if not self.on_instance_group:
self._load_search(enabled=self.on_instance_search)
self._load_search(
enabled=self.on_instance_search,
element=self.settings.get("element"))
self._load_properties(enabled=self.on_instance_load)
else:
self._load_group(enabled=self.on_instance_group)
Expand Down Expand Up @@ -95,7 +97,9 @@ def reload(self, **kwargs):
super(ControlGroup, self).reload(**kwargs)
# instance logic
if not self.on_instance_group:
self._load_search(enabled=self.on_instance_search)
self._load_search(
enabled=self.on_instance_search,
element=self.settings.get("element"))
self._load_properties(enabled=self.on_instance_load)
else:
self._load_group(enabled=self.on_instance_group)
Expand Down
22 changes: 22 additions & 0 deletions tests/001_functionals/suite_001_botbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

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 qacode.core.testing.test_info import TestInfoBase
from qautils.files import settings
Expand Down Expand Up @@ -99,3 +100,24 @@ def test_bot_modes_headless(self, driver_mode, browser_name):
settings.get('bot').get('browser'))
self.assert_equals(self.bot.settings.get('mode'), driver_mode)
self.assert_equals(self.bot.curr_caps['browserName'], browser_name)

def test_botbase_invalidsettingskey(self):
"""Testcase: test_botbase_invalidsettingskey"""
settings = SETTINGS.copy()
settings.get('bot').update({"must_raises": "test"})
with pytest.raises(CoreException):
BotBase(**settings)

def test_botbase_invalidmode(self):
"""Testcase: test_botbase_invalidmode"""
settings = SETTINGS.copy()
settings.get('bot').update({"mode": "must_raises"})
with pytest.raises(CoreException):
self.bot = BotBase(**settings)

def test_botbase_invalidbrowser(self):
"""Testcase: test_botbase_invalidbrowser"""
settings = SETTINGS.copy()
settings.get('bot').update({"browser": "must_raises"})
with pytest.raises(CoreException):
self.bot = BotBase(**settings)
Loading

0 comments on commit 7de187d

Please sign in to comment.