8386221adf
Adding test method that checks creating/removal of job binary under Project/Data Processing/Job Binaries. The core testing function can be easily extended with other job binary templates; the initial versions allows to test for jobs stored on the Internal DB. As part of this test several items have been created * new pageobject class JobbinariesPage * new form field FileInputFormFieldRegion Co-Authored-By: Luigi Toscano <ltoscano@redhat.com> Partially implements blueprint: selenium-integration-testing Change-Id: I48c992bbfd0b2428be220a4462d5ef76aca5fa45
375 lines
12 KiB
Python
375 lines
12 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 six
|
|
|
|
from selenium.webdriver.common import by
|
|
import selenium.webdriver.support.ui as Support
|
|
|
|
from openstack_dashboard.test.integration_tests.regions import baseregion
|
|
from openstack_dashboard.test.integration_tests.regions import exceptions
|
|
from openstack_dashboard.test.integration_tests.regions import menus
|
|
|
|
|
|
class FieldFactory(baseregion.BaseRegion):
|
|
"""Factory for creating form field objects."""
|
|
|
|
FORM_FIELDS_TYPES = set()
|
|
|
|
def make_form_field(self):
|
|
for form_type in self.FORM_FIELDS_TYPES:
|
|
if self._is_element_present(*form_type._element_locator):
|
|
return form_type(self.driver, self.conf, self.src_elem)
|
|
raise exceptions.UnknownFormFieldTypeException()
|
|
|
|
@classmethod
|
|
def register_field_cls(cls, field_class, base_classes=None):
|
|
"""Register new field class.
|
|
|
|
Add new field class and remove all base classes from the set of
|
|
registered classes as they should not be in.
|
|
"""
|
|
cls.FORM_FIELDS_TYPES.add(field_class)
|
|
cls.FORM_FIELDS_TYPES -= set(base_classes)
|
|
|
|
|
|
class MetaBaseFormFieldRegion(type):
|
|
"""Register form field class in FieldFactory."""
|
|
|
|
def __init__(cls, name, bases, dct):
|
|
FieldFactory.register_field_cls(cls, bases)
|
|
super(MetaBaseFormFieldRegion, cls).__init__(name, bases, dct)
|
|
|
|
|
|
@six.add_metaclass(MetaBaseFormFieldRegion)
|
|
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
|
|
|
|
def is_displayed(self):
|
|
return self.element.is_displayed()
|
|
|
|
|
|
class CheckBoxFormFieldRegion(BaseFormFieldRegion):
|
|
"""Checkbox field."""
|
|
|
|
_element_locator = (by.By.CSS_SELECTOR,
|
|
'div > label > input[type=checkbox]')
|
|
|
|
def is_marked(self):
|
|
return self.element.is_selected()
|
|
|
|
def mark(self):
|
|
if not self.is_marked():
|
|
self.element.click()
|
|
|
|
def unmark(self):
|
|
if self.is_marked():
|
|
self.element.click()
|
|
|
|
|
|
class ChooseFileFormFieldRegion(BaseFormFieldRegion):
|
|
"""Choose file field."""
|
|
|
|
_element_locator = (by.By.CSS_SELECTOR, 'div > input[type=file]')
|
|
|
|
def choose(self, path):
|
|
self.element.send_keys(path)
|
|
|
|
|
|
class BaseTextFormFieldRegion(BaseFormFieldRegion):
|
|
|
|
_element_locator = None
|
|
|
|
@property
|
|
def text(self):
|
|
return self.element.text
|
|
|
|
@text.setter
|
|
def text(self, text):
|
|
self._fill_field_element(text, self.element)
|
|
|
|
|
|
class TextInputFormFieldRegion(BaseTextFormFieldRegion):
|
|
"""Text input box."""
|
|
|
|
_element_locator = (by.By.CSS_SELECTOR, 'div > input[type=text],'
|
|
'div > input[type=None]')
|
|
|
|
|
|
class FileInputFormFieldRegion(BaseFormFieldRegion):
|
|
"""Text input box."""
|
|
|
|
_element_locator = (by.By.CSS_SELECTOR, 'div > input[type=file]')
|
|
|
|
@property
|
|
def path(self):
|
|
return self.element.text
|
|
|
|
@path.setter
|
|
def path(self, path):
|
|
# clear does not work on this kind of element
|
|
# because it is not user editable
|
|
self.element.send_keys(path)
|
|
|
|
|
|
class PasswordInputFormFieldRegion(BaseTextFormFieldRegion):
|
|
"""Password text input box."""
|
|
|
|
_element_locator = (by.By.CSS_SELECTOR, 'div > input[type=password]')
|
|
|
|
|
|
class EmailInputFormFieldRegion(BaseTextFormFieldRegion):
|
|
"""Email text input box."""
|
|
|
|
_element_locator = (by.By.ID, 'id_email')
|
|
|
|
|
|
class TextAreaFormFieldRegion(BaseTextFormFieldRegion):
|
|
"""Multi-line text input box."""
|
|
|
|
_element_locator = (by.By.CSS_SELECTOR, 'div > textarea')
|
|
|
|
|
|
class IntegerFormFieldRegion(BaseFormFieldRegion):
|
|
"""Integer input box."""
|
|
|
|
_element_locator = (by.By.CSS_SELECTOR, 'div > input[type=number]')
|
|
|
|
@property
|
|
def value(self):
|
|
return self.element.get_attribute("value")
|
|
|
|
@value.setter
|
|
def value(self, value):
|
|
self._fill_field_element(value, self.element)
|
|
|
|
|
|
class SelectFormFieldRegion(BaseFormFieldRegion):
|
|
"""Select box field."""
|
|
|
|
_element_locator = (by.By.CSS_SELECTOR, 'div > select')
|
|
|
|
def is_displayed(self):
|
|
return self.element._el.is_displayed()
|
|
|
|
@property
|
|
def element(self):
|
|
select = self._get_element(*self._element_locator)
|
|
return Support.Select(select)
|
|
|
|
@property
|
|
def values(self):
|
|
results = []
|
|
for option in self.element.all_selected_options:
|
|
results.append(option.get_attribute('value'))
|
|
return results
|
|
|
|
@property
|
|
def text(self):
|
|
return self.element.first_selected_option.text
|
|
|
|
@text.setter
|
|
def text(self, text):
|
|
self.element.select_by_visible_text(text)
|
|
|
|
@property
|
|
def value(self):
|
|
return self.element.first_selected_option.get_attribute('value')
|
|
|
|
@value.setter
|
|
def value(self, value):
|
|
self.element.select_by_value(value)
|
|
|
|
|
|
class BaseFormRegion(baseregion.BaseRegion):
|
|
"""Base class for forms."""
|
|
|
|
_submit_locator = (by.By.CSS_SELECTOR, '*.btn.btn-primary')
|
|
_cancel_locator = (by.By.CSS_SELECTOR, '*.btn.cancel')
|
|
_default_form_locator = (by.By.CSS_SELECTOR, 'div.modal-dialog')
|
|
|
|
def __init__(self, driver, conf, src_elem=None):
|
|
"""In most cases forms can be located through _default_form_locator,
|
|
so specifying source element can be skipped.
|
|
"""
|
|
if src_elem is None:
|
|
# 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_form_locator)
|
|
super(BaseFormRegion, self).__init__(driver, conf, src_elem)
|
|
|
|
@property
|
|
def submit(self):
|
|
return self._get_element(*self._submit_locator)
|
|
|
|
@property
|
|
def cancel(self):
|
|
return self._get_element(*self._cancel_locator)
|
|
|
|
|
|
class FormRegion(BaseFormRegion):
|
|
"""Standard form."""
|
|
|
|
_header_locator = (by.By.CSS_SELECTOR, 'div.modal-header > h3')
|
|
_side_info_locator = (by.By.CSS_SELECTOR, 'div.right')
|
|
_fields_locator = (by.By.CSS_SELECTOR, 'fieldset > div.form-group')
|
|
|
|
# private methods
|
|
def __init__(self, driver, conf, src_elem, form_field_names):
|
|
super(FormRegion, self).__init__(driver, conf, src_elem)
|
|
self.form_field_names = form_field_names
|
|
self._init_form_fields()
|
|
|
|
# protected methods
|
|
def _init_form_fields(self):
|
|
self._init_dynamic_properties(self.form_field_names,
|
|
self._get_form_fields)
|
|
|
|
def _get_form_fields(self):
|
|
fields_els = self._get_elements(*self._fields_locator)
|
|
form_fields = []
|
|
try:
|
|
self._turn_off_implicit_wait()
|
|
for elem in fields_els:
|
|
field_factory = FieldFactory(self.driver, self.conf, elem)
|
|
form_fields.append(field_factory.make_form_field())
|
|
finally:
|
|
self._turn_on_implicit_wait()
|
|
return form_fields
|
|
|
|
def set_field_values(self, data):
|
|
"""Set fields values
|
|
|
|
data - {field_name: field_value, field_name: field_value ...}
|
|
"""
|
|
for field_name in data:
|
|
field = getattr(self, field_name, None)
|
|
# Field form does not exist
|
|
if field is None:
|
|
raise AttributeError("Unknown form field name.")
|
|
value = data[field_name]
|
|
# if None - default value is left in field
|
|
if value is not None:
|
|
# all text fields
|
|
if hasattr(field, "text"):
|
|
field.text = value
|
|
# file upload field
|
|
elif hasattr(field, "path"):
|
|
field.path = value
|
|
# integers fields
|
|
elif hasattr(field, "value"):
|
|
field.value = value
|
|
|
|
# properties
|
|
@property
|
|
def header(self):
|
|
"""Form header."""
|
|
return self._get_element(*self._header_locator)
|
|
|
|
@property
|
|
def sideinfo(self):
|
|
"""Right part of form, usually contains description."""
|
|
return self._get_element(*self._side_info_locator)
|
|
|
|
@property
|
|
def fields(self):
|
|
"""List of all fields that form contains."""
|
|
return self._get_form_fields()
|
|
|
|
|
|
class TabbedFormRegion(FormRegion):
|
|
"""Forms that are divided with tabs.
|
|
|
|
As example is taken form under the
|
|
the Project/Network/Networks/Create Network, on initialization form needs
|
|
to have form field names divided into tuples, that represents the tabs
|
|
and the fields located under them.
|
|
|
|
Usage:
|
|
|
|
form_field_names = (("network_name", "admin_state"),
|
|
("create_subnet", "subnet_name", "network_address",
|
|
"ip_version", "gateway_ip", "disable_gateway"),
|
|
("enable_dhcp", "allocation_pools", "dns_name_servers",
|
|
"host_routes"))
|
|
form = TabbedFormRegion(self.conf, self.driver, None, form_field_names)
|
|
form.network_name.text = "test_network_name"
|
|
"""
|
|
|
|
_submit_locator = (by.By.CSS_SELECTOR, '*.btn.btn-primary[type=submit]')
|
|
_side_info_locator = (by.By.CSS_SELECTOR, "td.help_text")
|
|
_fields_locator = (by.By.CSS_SELECTOR, "div.form-group")
|
|
|
|
class GetFieldsMethod(object):
|
|
|
|
def __init__(self, get_fields_method, tab_index, switch_tab_method):
|
|
self.get_fields = get_fields_method
|
|
self.tab_index = tab_index
|
|
self.switch_to_tab = switch_tab_method
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
self.switch_to_tab(self.tab_index)
|
|
return [field for field in self.get_fields()
|
|
if field.is_displayed()]
|
|
|
|
@property
|
|
def tabs(self):
|
|
return menus.TabbedMenuRegion(self.driver, self.conf)
|
|
|
|
def _init_form_fields(self):
|
|
for index, tab_names in enumerate(self.form_field_names):
|
|
get_fields = self.GetFieldsMethod(self._get_form_fields, index,
|
|
self.tabs.switch_to)
|
|
self._init_dynamic_properties(tab_names, get_fields)
|
|
|
|
|
|
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.click()
|
|
|
|
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)
|