e3e25a6e8b
* TabbedFormRegion - that should be used for forms that are divided into tabs, the manipulation with the form is then the same as with the regular Form region, tabs are switched automatically * TabbedMenuRegion - support for another type of menu, that is for example located in certain forms or under the Project/Compute/access & security page * Also _default_src_locator is added to BaseRegion class, that makes it possible for every component to have its own default locator, so it is not necessary to specify region source element all over again Partially implements blueprint: selenium-integration-testing Change-Id: If302385d7d0f8b84512e710fbd70cdbd5bcdffea
105 lines
4.4 KiB
Python
105 lines
4.4 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
|
|
"""
|
|
|
|
_default_src_locator = None
|
|
|
|
# private methods
|
|
def __init__(self, driver, conf, src_elem=None):
|
|
super(BaseRegion, self).__init__(driver, conf)
|
|
if src_elem is None and self._default_src_locator:
|
|
# fake self.src_elem must be set up in
|
|
# order self._get_element work
|
|
self.src_elem = driver
|
|
src_elem = self._get_element(*self._default_src_locator)
|
|
|
|
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)
|