
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
95 lines
4.1 KiB
Python
95 lines
4.1 KiB
Python
# 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)
|