Add regions module to integration tests
Regions module should contains basic functionality that should be shared among multiple page objects: common tables, forms, notifications etc.. Basically every piece of code that can be reused by multiple classes in a form of a new object should be placed in here. * I would suggest that already created regions should be moved into this module. * I would consider region everything that can be reused by at least two page objects and aim for maximal code reusability in the future. * Every new region should inherit from class BaseRegion located in baseregion.py. Partially implements blueprint: selenium-integration-testing Change-Id: Ib8c20d8833e0830c85030633091663d33de6e3ab
This commit is contained in:
parent
eab348a7a3
commit
6107972e53
@ -22,14 +22,14 @@ class BaseWebObject(object):
|
||||
|
||||
def _is_element_present(self, *locator):
|
||||
try:
|
||||
self.driver.find_element(*locator)
|
||||
self._get_element(*locator)
|
||||
return True
|
||||
except Exceptions.NoSuchElementException:
|
||||
return False
|
||||
|
||||
def _is_element_visible(self, *locator):
|
||||
try:
|
||||
return self.driver.find_element(*locator).is_displayed()
|
||||
return self._get_element(*locator).is_displayed()
|
||||
except (Exceptions.NoSuchElementException,
|
||||
Exceptions.ElementNotVisibleException):
|
||||
return False
|
||||
@ -37,6 +37,9 @@ class BaseWebObject(object):
|
||||
def _get_element(self, *locator):
|
||||
return self.driver.find_element(*locator)
|
||||
|
||||
def _get_elements(self, *locator):
|
||||
return self.driver.find_elements(*locator)
|
||||
|
||||
def _fill_field_element(self, data, field_element):
|
||||
field_element.clear()
|
||||
field_element.send_keys(data)
|
||||
|
@ -14,12 +14,15 @@ from selenium.webdriver.common import by
|
||||
|
||||
from openstack_dashboard.test.integration_tests import basewebobject
|
||||
from openstack_dashboard.test.integration_tests.pages import pageobject
|
||||
from openstack_dashboard.test.integration_tests.regions import bars
|
||||
from openstack_dashboard.test.integration_tests.regions import messages
|
||||
|
||||
|
||||
class BasePage(pageobject.PageObject):
|
||||
"""Base class for all dashboard page objects."""
|
||||
|
||||
_heading_locator = (by.By.CSS_SELECTOR, "div.page-header > h2")
|
||||
_heading_locator = (by.By.CSS_SELECTOR, 'div.page-header > h2')
|
||||
_error_msg_locator = (by.By.CSS_SELECTOR, 'div.alert-danger.alert')
|
||||
|
||||
@property
|
||||
def heading(self):
|
||||
@ -27,7 +30,7 @@ class BasePage(pageobject.PageObject):
|
||||
|
||||
@property
|
||||
def topbar(self):
|
||||
return BasePage.TopBarRegion(self.driver, self.conf)
|
||||
return bars.TopBarRegion(self.driver, self.conf)
|
||||
|
||||
@property
|
||||
def is_logged_in(self):
|
||||
@ -37,6 +40,13 @@ class BasePage(pageobject.PageObject):
|
||||
def navaccordion(self):
|
||||
return BasePage.NavigationAccordionRegion(self.driver, self.conf)
|
||||
|
||||
def error_message(self):
|
||||
src_elem = self._get_element(*self._error_msg_locator)
|
||||
return messages.ErrorMessageRegion(self.driver, self.conf, src_elem)
|
||||
|
||||
def is_error_message_present(self):
|
||||
return self._is_element_present(*self._error_msg_locator)
|
||||
|
||||
def go_to_login_page(self):
|
||||
self.driver.get(self.login_url)
|
||||
|
||||
@ -52,50 +62,6 @@ class BasePage(pageobject.PageObject):
|
||||
self.topbar.user_dropdown_menu.click()
|
||||
self.topbar.help_link.click()
|
||||
|
||||
class TopBarRegion(basewebobject.BaseWebObject):
|
||||
_user_dropdown_menu_locator = (by.By.CSS_SELECTOR,
|
||||
'div#profile_editor_switcher'
|
||||
' > button')
|
||||
_settings_link_locator = (by.By.CSS_SELECTOR,
|
||||
'a[href*="/settings/"]')
|
||||
_help_link_locator = (by.By.CSS_SELECTOR,
|
||||
'ul#editor_list li:nth-of-type(2) > a')
|
||||
_logout_link_locator = (by.By.CSS_SELECTOR,
|
||||
'a[href*="/auth/logout/"]')
|
||||
_openstack_brand_locator = (by.By.CSS_SELECTOR, 'a[href*="/home/"]')
|
||||
|
||||
@property
|
||||
def user(self):
|
||||
return self._get_element(*self._user_dropdown_menu_locator)
|
||||
|
||||
@property
|
||||
def brand(self):
|
||||
return self._get_element(*self._openstack_brand_locator)
|
||||
|
||||
@property
|
||||
def logout_link(self):
|
||||
return self._get_element(*self._logout_link_locator)
|
||||
|
||||
@property
|
||||
def user_dropdown_menu(self):
|
||||
return self._get_element(*self._user_dropdown_menu_locator)
|
||||
|
||||
@property
|
||||
def settings_link(self):
|
||||
return self._get_element(*self._settings_link_locator)
|
||||
|
||||
@property
|
||||
def help_link(self):
|
||||
return self._get_element(*self._help_link_locator)
|
||||
|
||||
@property
|
||||
def is_logout_visible(self):
|
||||
return self._is_element_visible(*self._logout_link_locator)
|
||||
|
||||
@property
|
||||
def is_logged_in(self):
|
||||
return self._is_element_visible(*self._user_dropdown_menu_locator)
|
||||
|
||||
class NavigationAccordionRegion(basewebobject.BaseWebObject):
|
||||
# TODO(sunlim): change Xpath to CSS
|
||||
_project_bar_locator = (
|
||||
|
@ -9,13 +9,22 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from selenium.webdriver.common import by
|
||||
|
||||
from openstack_dashboard.test.integration_tests.pages import accesssecuritypage
|
||||
from openstack_dashboard.test.integration_tests.pages import basepage
|
||||
from openstack_dashboard.test.integration_tests.pages import settingspage
|
||||
from openstack_dashboard.test.integration_tests.regions import forms
|
||||
from openstack_dashboard.test.integration_tests.regions import tables
|
||||
|
||||
|
||||
class ProjectPage(basepage.BasePage):
|
||||
|
||||
_usage_table_locator = (by.By.CSS_SELECTOR, 'table#project_usage')
|
||||
_date_form_locator = (by.By.CSS_SELECTOR, 'form#date_form')
|
||||
|
||||
USAGE_TABLE_ACTIONS = ("download_csv",)
|
||||
|
||||
def __init__(self, driver, conf):
|
||||
super(ProjectPage, self).__init__(driver, conf)
|
||||
self._page_title = 'Instance Overview'
|
||||
@ -33,3 +42,14 @@ class ProjectPage(basepage.BasePage):
|
||||
self.navaccordion.access_security.click()
|
||||
return accesssecuritypage.AccessSecurityPage(
|
||||
self.driver, self.conf)
|
||||
|
||||
@property
|
||||
def usage_table(self):
|
||||
src_elem = self._get_element(*self._usage_table_locator)
|
||||
return tables.ActionsTableRegion(self.driver, self.conf, src_elem,
|
||||
self.USAGE_TABLE_ACTIONS)
|
||||
|
||||
@property
|
||||
def date_form(self):
|
||||
src_elem = self._get_element(*self._date_form_locator)
|
||||
return forms.DateFormRegion(self.driver, self.conf, src_elem)
|
||||
|
60
openstack_dashboard/test/integration_tests/regions/bars.py
Normal file
60
openstack_dashboard/test/integration_tests/regions/bars.py
Normal file
@ -0,0 +1,60 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from selenium.webdriver.common import by
|
||||
|
||||
from openstack_dashboard.test.integration_tests.regions import baseregion
|
||||
|
||||
|
||||
class TopBarRegion(baseregion.BaseRegion):
|
||||
_user_dropdown_menu_locator = (by.By.CSS_SELECTOR,
|
||||
'div#profile_editor_switcher'
|
||||
' > button')
|
||||
_settings_link_locator = (by.By.CSS_SELECTOR,
|
||||
'a[href*="/settings/"]')
|
||||
_help_link_locator = (by.By.CSS_SELECTOR,
|
||||
'ul#editor_list li:nth-of-type(2) > a')
|
||||
_logout_link_locator = (by.By.CSS_SELECTOR,
|
||||
'a[href*="/auth/logout/"]')
|
||||
_openstack_brand_locator = (by.By.CSS_SELECTOR, 'a[href*="/home/"]')
|
||||
|
||||
@property
|
||||
def user(self):
|
||||
return self._get_element(*self._user_dropdown_menu_locator)
|
||||
|
||||
@property
|
||||
def brand(self):
|
||||
return self._get_element(*self._openstack_brand_locator)
|
||||
|
||||
@property
|
||||
def logout_link(self):
|
||||
return self._get_element(*self._logout_link_locator)
|
||||
|
||||
@property
|
||||
def user_dropdown_menu(self):
|
||||
return self._get_element(*self._user_dropdown_menu_locator)
|
||||
|
||||
@property
|
||||
def settings_link(self):
|
||||
return self._get_element(*self._settings_link_locator)
|
||||
|
||||
@property
|
||||
def help_link(self):
|
||||
return self._get_element(*self._help_link_locator)
|
||||
|
||||
@property
|
||||
def is_logout_visible(self):
|
||||
return self._is_element_visible(*self._logout_link_locator)
|
||||
|
||||
@property
|
||||
def is_logged_in(self):
|
||||
return self._is_element_visible(*self._user_dropdown_menu_locator)
|
@ -0,0 +1,94 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import types
|
||||
|
||||
from openstack_dashboard.test.integration_tests import basewebobject
|
||||
|
||||
|
||||
class BaseRegion(basewebobject.BaseWebObject):
|
||||
"""Base class for region module
|
||||
* there is necessity to override some basic methods for obtaining elements
|
||||
as in content of regions it is required to do relative searches
|
||||
|
||||
* self.driver cannot be easily replaced with self.src_elem because that
|
||||
would result in functionality loss, self.driver is WebDriver and
|
||||
src_elem is WebElement its usage is different.
|
||||
|
||||
* this does not mean that self.src_elem cannot be self.driver
|
||||
"""
|
||||
|
||||
# private methods
|
||||
def __init__(self, driver, conf, src_elem=None):
|
||||
super(BaseRegion, self).__init__(driver, conf)
|
||||
self.src_elem = src_elem or driver
|
||||
# variable for storing names of dynamic properties and
|
||||
# associated 'getters' - meaning method that are supplying
|
||||
# regions or web elements
|
||||
self._dynamic_properties = {}
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""It is not possible to create property bounded just to object
|
||||
and not class at runtime, therefore it is necessary to
|
||||
override __getattr__ and make fake 'properties' by storing them in
|
||||
the protected attribute _dynamic_attributes and returning result
|
||||
of the method associated with the specified attribute.
|
||||
|
||||
This way the feeling of having regions accessed as 'properties'
|
||||
is created, which is one of the requirement of page object pattern.
|
||||
"""
|
||||
try:
|
||||
return self._dynamic_properties[name]()
|
||||
except KeyError:
|
||||
msg = "'{0}' object has no attribute '{1}'"
|
||||
raise AttributeError(msg.format(type(self).__name__, name))
|
||||
|
||||
# protected methods and classes
|
||||
class _DynamicProperty(object):
|
||||
"""Serves as new property holder."""
|
||||
|
||||
def __init__(self, method, index=None):
|
||||
"""In case object was created with index != None,
|
||||
it is assumed that the result of self.method should be tuple()
|
||||
and just certain index should be returned
|
||||
"""
|
||||
self.method = method
|
||||
self.index = index
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
result = self.method()
|
||||
return result if self.index is None else result[self.index]
|
||||
|
||||
def _init_dynamic_properties(self, new_attr_names, method):
|
||||
"""Create new object's 'properties' at runtime."""
|
||||
for index, new_attr_name in enumerate(new_attr_names):
|
||||
self._init_dynamic_property(new_attr_name, method, index)
|
||||
|
||||
def _init_dynamic_property(self, new_attr_name, method, index=None):
|
||||
"""Create new object's property at runtime. If index argument is
|
||||
supplied it is assumed that method returns tuple() and only element
|
||||
on ${index} position is returned.
|
||||
"""
|
||||
if (new_attr_name in dir(self) or
|
||||
new_attr_name in self._dynamic_properties):
|
||||
raise AttributeError("%s class has already attribute %s."
|
||||
"The new property could not be "
|
||||
"created." % (self.__class__.__name__,
|
||||
new_attr_name))
|
||||
new_method = self.__class__._DynamicProperty(method, index)
|
||||
inst_method = types.MethodType(new_method, self)
|
||||
self._dynamic_properties[new_attr_name] = inst_method
|
||||
|
||||
def _get_element(self, *locator):
|
||||
return self.src_elem.find_element(*locator)
|
||||
|
||||
def _get_elements(self, *locator):
|
||||
return self.src_elem.find_elements(*locator)
|
73
openstack_dashboard/test/integration_tests/regions/forms.py
Normal file
73
openstack_dashboard/test/integration_tests/regions/forms.py
Normal file
@ -0,0 +1,73 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from selenium.webdriver.common import by
|
||||
|
||||
from openstack_dashboard.test.integration_tests.regions import baseregion
|
||||
|
||||
|
||||
class BaseFormFieldRegion(baseregion.BaseRegion):
|
||||
"""Base class for form fields classes."""
|
||||
|
||||
_label_locator = None
|
||||
_element_locator = None
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
return self._get_element(*self._label_locator)
|
||||
|
||||
@property
|
||||
def element(self):
|
||||
return self._get_element(*self._element_locator)
|
||||
|
||||
def is_required(self):
|
||||
classes = self.driver.get_attribute('class')
|
||||
return 'required' in classes
|
||||
|
||||
|
||||
class BaseFormRegion(baseregion.BaseRegion):
|
||||
"""Base class for forms."""
|
||||
|
||||
_submit_locator = (by.By.CSS_SELECTOR, 'button.btn.btn-primary,'
|
||||
' a.btn.btn-primary')
|
||||
|
||||
def submit(self):
|
||||
self._get_element(*self._submit_locator).click()
|
||||
|
||||
|
||||
class DateFormRegion(BaseFormRegion):
|
||||
"""Form that queries data to table that is regularly below the form,
|
||||
typical example is located on Project/Compute/Overview page.
|
||||
"""
|
||||
|
||||
_from_field_locator = (by.By.CSS_SELECTOR, 'input#id_start')
|
||||
_to_field_locator = (by.By.CSS_SELECTOR, 'input#id_end')
|
||||
|
||||
@property
|
||||
def from_date(self):
|
||||
return self._get_element(*self._from_field_locator)
|
||||
|
||||
@property
|
||||
def to_date(self):
|
||||
return self._get_element(*self._to_field_locator)
|
||||
|
||||
def query(self, start, end):
|
||||
self._set_from_field(start)
|
||||
self._set_to_field(end)
|
||||
self.submit()
|
||||
|
||||
def _set_from_field(self, value):
|
||||
self._fill_field_element(value, self.from_date)
|
||||
|
||||
def _set_to_field(self, value):
|
||||
self._fill_field_element(value, self.to_date)
|
@ -0,0 +1,23 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from selenium.webdriver.common import by
|
||||
|
||||
from openstack_dashboard.test.integration_tests.regions import baseregion
|
||||
|
||||
|
||||
class ErrorMessageRegion(baseregion.BaseRegion):
|
||||
|
||||
_close_locator = (by.By.CSS_SELECTOR, 'a.close')
|
||||
|
||||
def close(self):
|
||||
self._get_element(*self._close_locator).click()
|
221
openstack_dashboard/test/integration_tests/regions/tables.py
Normal file
221
openstack_dashboard/test/integration_tests/regions/tables.py
Normal file
@ -0,0 +1,221 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from selenium.webdriver.common import by
|
||||
|
||||
from openstack_dashboard.test.integration_tests.regions import baseregion
|
||||
|
||||
|
||||
class RowRegion(baseregion.BaseRegion):
|
||||
"""Classic table row."""
|
||||
|
||||
_cell_locator = (by.By.CSS_SELECTOR, 'td.normal_column')
|
||||
|
||||
@property
|
||||
def cells(self):
|
||||
return self._get_elements(*self._cell_locator)
|
||||
|
||||
|
||||
class BaseActionRowRegion(RowRegion):
|
||||
"""Base class for creating ActionRow class derivative."""
|
||||
|
||||
_row_checkbox_locator = (by.By.CSS_SELECTOR, 'td > input')
|
||||
|
||||
def mark(self):
|
||||
chck_box = self._get_element(*self._row_checkbox_locator)
|
||||
chck_box.click()
|
||||
|
||||
|
||||
class BtnActionRowRegion(BaseActionRowRegion):
|
||||
"""Row with buttons in action column."""
|
||||
|
||||
_action_locator = (by.By.CSS_SELECTOR, 'td.actions_column > button')
|
||||
|
||||
def __init__(self, driver, conf, src_elem, action_name):
|
||||
super(BtnActionRowRegion, self).__init__(driver, conf, src_elem)
|
||||
self.action_name = action_name
|
||||
self._init_action()
|
||||
|
||||
def _init_action(self):
|
||||
self._init_dynamic_property(self.action_name, self._get_action)
|
||||
|
||||
def _get_action(self):
|
||||
return self._get_element(*self._action_locator)
|
||||
|
||||
@property
|
||||
def action(self):
|
||||
return self._get_action()
|
||||
|
||||
|
||||
class ComplexActionRowRegion(BaseActionRowRegion):
|
||||
"""Row with button and select box in action column."""
|
||||
|
||||
_primary_action_locator = (by.By.CSS_SELECTOR,
|
||||
'td.actions_column > div.btn-group > *.btn')
|
||||
_secondary_actions_dropdown_locator = (by.By.CSS_SELECTOR,
|
||||
'div.btn-group')
|
||||
|
||||
PRIMARY_ACTION = "primary_action"
|
||||
SECONDARY_ACTIONS = "secondary_actions"
|
||||
|
||||
ACTIONS_ERROR_MSG = ("Actions must be supplied in dictionary:"
|
||||
" {%s: 'action_name', '%s': ('action_name',...)}"
|
||||
% (PRIMARY_ACTION, SECONDARY_ACTIONS))
|
||||
|
||||
def __init__(self, driver, conf, src_elem, action_names):
|
||||
super(ComplexActionRowRegion, self).__init__(driver, conf, src_elem)
|
||||
try:
|
||||
self.primary_action_name = action_names[self.PRIMARY_ACTION]
|
||||
self.secondary_action_names = action_names[self.SECONDARY_ACTIONS]
|
||||
self._init_actions()
|
||||
except (TypeError, KeyError):
|
||||
raise AttributeError(self.ACTIONS_ERROR_MSG)
|
||||
|
||||
def _init_actions(self):
|
||||
self._init_dynamic_property(self.primary_action_name,
|
||||
self._get_primary_action)
|
||||
self._init_dynamic_properties(self.secondary_action_names,
|
||||
self._get_secondary_actions)
|
||||
|
||||
def _get_primary_action(self):
|
||||
return self._get_element(*self._primary_action_locator)
|
||||
|
||||
def _get_secondary_actions(self):
|
||||
return self._get_elements(*self._secondary_actions_dropdown_locator)
|
||||
|
||||
@property
|
||||
def primary_action(self):
|
||||
return self._get_primary_action()
|
||||
|
||||
@property
|
||||
def secondary_actions(self):
|
||||
return self._get_secondary_actions()
|
||||
|
||||
|
||||
class BasicTableRegion(baseregion.BaseRegion):
|
||||
"""Basic class representing table object."""
|
||||
|
||||
_heading_locator = (by.By.CSS_SELECTOR, 'h3.table_title')
|
||||
_columns_names_locator = (by.By.CSS_SELECTOR, 'thead > tr > th')
|
||||
_footer_locator = (by.By.CSS_SELECTOR, 'tfoot > tr > td > span')
|
||||
_rows_locator = (by.By.CSS_SELECTOR, 'tbody > tr')
|
||||
_empty_table_locator = (by.By.CSS_SELECTOR, 'tbody > tr.empty')
|
||||
_search_field_locator = (by.By.CSS_SELECTOR,
|
||||
'div.table_search.client > input')
|
||||
_search_button_locator = (by.By.CSS_SELECTOR,
|
||||
'div.table_search.client > button')
|
||||
|
||||
@property
|
||||
def heading(self):
|
||||
return self._get_element(*self._heading_locator)
|
||||
|
||||
@property
|
||||
def rows(self):
|
||||
if self._is_element_present(*self._empty_table_locator):
|
||||
return []
|
||||
else:
|
||||
return self._get_rows()
|
||||
|
||||
@property
|
||||
def column_names(self):
|
||||
return self._get_elements(*self._columns_names_locator)
|
||||
|
||||
@property
|
||||
def footer(self):
|
||||
return self._get_element(*self._footer_locator)
|
||||
|
||||
def filter(self, value):
|
||||
self._set_search_field(value)
|
||||
self._click_search_btn()
|
||||
|
||||
def get_row(self, column_index, text):
|
||||
"""Get row that contains in specified column specified text."""
|
||||
for row in self.rows:
|
||||
if text in row.cells[column_index].text:
|
||||
return row
|
||||
return None
|
||||
|
||||
def _set_search_field(self, value):
|
||||
srch_field = self._get_element(*self._search_field_locator)
|
||||
srch_field.send_keys(value)
|
||||
|
||||
def _click_search_btn(self):
|
||||
btn = self._get_element(*self._search_button_locator)
|
||||
btn.click()
|
||||
|
||||
def _get_rows(self):
|
||||
rows = []
|
||||
for elem in self._get_elements(*self._rows_locator):
|
||||
rows.append(RowRegion(self.driver, self.conf, elem))
|
||||
|
||||
|
||||
class ActionsTableRegion(BasicTableRegion):
|
||||
"""Base class for creating derivative of BasicTableRegion that
|
||||
has some actions.
|
||||
"""
|
||||
|
||||
_actions_locator = (by.By.CSS_SELECTOR, 'div.table_actions > button,'
|
||||
' div.table_actions > a')
|
||||
|
||||
# private methods
|
||||
def __init__(self, driver, conf, src_elm, action_names):
|
||||
super(ActionsTableRegion, self).__init__(driver, conf, src_elm)
|
||||
self.action_names = action_names
|
||||
self._init_actions()
|
||||
|
||||
# protected methods
|
||||
def _init_actions(self):
|
||||
"""Create new methods that corresponds to picking table's
|
||||
action buttons.
|
||||
"""
|
||||
self._init_dynamic_properties(self.action_names, self._get_actions)
|
||||
|
||||
def _get_actions(self):
|
||||
return self._get_elements(*self._actions_locator)
|
||||
|
||||
# properties
|
||||
@property
|
||||
def actions(self):
|
||||
return self._get_actions()
|
||||
|
||||
|
||||
class SimpleActionsTableRegion(ActionsTableRegion):
|
||||
"""Table which rows has buttons in action column."""
|
||||
|
||||
def __init__(self, driver, conf, src_elm, action_names, row_action_name):
|
||||
super(SimpleActionsTableRegion, self).__init__(driver, conf, src_elm,
|
||||
action_names)
|
||||
self.row_action_name = row_action_name
|
||||
|
||||
def _get_rows(self):
|
||||
rows = []
|
||||
for elem in self._get_elements(*self._rows_locator):
|
||||
rows.append(BtnActionRowRegion(self.driver, self.conf, elem,
|
||||
self.row_action_name))
|
||||
return rows
|
||||
|
||||
|
||||
class ComplexActionTableRegion(ActionsTableRegion):
|
||||
"""Table which has button and selectbox in the action column."""
|
||||
|
||||
def __init__(self, driver, conf, src_elm, action_names, row_action_names):
|
||||
super(ComplexActionTableRegion, self).__init__(driver, conf, src_elm,
|
||||
action_names)
|
||||
self.row_action_names = row_action_names
|
||||
|
||||
def _get_rows(self):
|
||||
rows = []
|
||||
for elem in self._get_elements(*self._rows_locator):
|
||||
rows.append(ComplexActionRowRegion(self.driver, self.conf, elem,
|
||||
self.row_action_names))
|
||||
return rows
|
Loading…
Reference in New Issue
Block a user