Introduce field mappings for FormRegion descendants in i9n tests
This change is similar to the change that introduced decorators for binding table-level and row-level actions to real table buttons - this time for forms. Its aim is to provide means to reference a form field in test using a name different from the one that exists in real html. Now it is possible to provide a dictionary for simple FormRegion, where key is the name to be used in test and the value is used for binding this name to a real html field widget. Also it is feasible to provide values other than strings in that dictionary - in this case they are mean to be a Python class. This Python class will be initialized as any BaseRegion is usually initialized and then the value's key will be used for referencing this object. This is useful when dealing with non-standard widgets in forms (like Membership widget in Create/Edit Project form or Networks widget in Launch Instance form). The old syntax for fields names in tests (the tuple of strings) is still compatible with new FormRegion-s. It is treated as we had simply used the same name both for referencing field and for binding it to a real widget. Changing TabbedForm region's tabs to be referenced by name instead of index is out of the scope of this change, but should be done in future. If not done, we're going to encounter problems with different tabs being shown or not shown based on RBAC/ enabled settings/ etc. Implements blueprint: integration-tests-improvements-part1 Change-Id: If3658119176d03dcd0742101ae2922f8e61ba757
This commit is contained in:
parent
726ea9992f
commit
c573b5b316
@ -27,7 +27,7 @@ class FlavorsTable(tables.TableRegion):
|
|||||||
create_button.click()
|
create_button.click()
|
||||||
return forms.TabbedFormRegion(
|
return forms.TabbedFormRegion(
|
||||||
self.driver, self.conf,
|
self.driver, self.conf,
|
||||||
form_field_names=self.CREATE_FLAVOR_FORM_FIELDS)
|
field_mappings=self.CREATE_FLAVOR_FORM_FIELDS)
|
||||||
|
|
||||||
@tables.bind_table_action('delete')
|
@tables.bind_table_action('delete')
|
||||||
def delete_flavor(self, delete_button):
|
def delete_flavor(self, delete_button):
|
||||||
|
@ -24,7 +24,7 @@ class ProjectsTable(tables.TableRegion):
|
|||||||
create_button.click()
|
create_button.click()
|
||||||
return forms.TabbedFormRegion(
|
return forms.TabbedFormRegion(
|
||||||
self.driver, self.conf,
|
self.driver, self.conf,
|
||||||
form_field_names=self.CREATE_PROJECT_FORM_FIELDS)
|
field_mappings=self.CREATE_PROJECT_FORM_FIELDS)
|
||||||
|
|
||||||
@tables.bind_table_action('delete')
|
@tables.bind_table_action('delete')
|
||||||
def delete_project(self, delete_button):
|
def delete_project(self, delete_button):
|
||||||
|
@ -24,7 +24,7 @@ class UsersTable(tables.TableRegion):
|
|||||||
def create_user(self, create_button):
|
def create_user(self, create_button):
|
||||||
create_button.click()
|
create_button.click()
|
||||||
return forms.FormRegion(self.driver, self.conf,
|
return forms.FormRegion(self.driver, self.conf,
|
||||||
form_field_names=self.CREATE_USER_FORM_FIELDS)
|
field_mappings=self.CREATE_USER_FORM_FIELDS)
|
||||||
|
|
||||||
@tables.bind_table_action('delete')
|
@tables.bind_table_action('delete')
|
||||||
def delete_user(self, delete_button):
|
def delete_user(self, delete_button):
|
||||||
|
@ -27,7 +27,7 @@ class KeypairsTable(tables.TableRegion):
|
|||||||
create_button.click()
|
create_button.click()
|
||||||
return forms.FormRegion(
|
return forms.FormRegion(
|
||||||
self.driver, self.conf,
|
self.driver, self.conf,
|
||||||
form_field_names=self.CREATE_KEY_PAIR_FORM_FIELDS)
|
field_mappings=self.CREATE_KEY_PAIR_FORM_FIELDS)
|
||||||
|
|
||||||
@tables.bind_row_action('delete', primary=True)
|
@tables.bind_row_action('delete', primary=True)
|
||||||
def delete_keypair(self, delete_button, row):
|
def delete_keypair(self, delete_button, row):
|
||||||
|
@ -24,7 +24,7 @@ class SecurityGroupsTable(tables.TableRegion):
|
|||||||
create_button.click()
|
create_button.click()
|
||||||
return forms.FormRegion(
|
return forms.FormRegion(
|
||||||
self.driver, self.conf,
|
self.driver, self.conf,
|
||||||
form_field_names=self.CREATE_SECURITYGROUP_FORM_FIELDS)
|
field_mappings=self.CREATE_SECURITYGROUP_FORM_FIELDS)
|
||||||
|
|
||||||
@tables.bind_table_action('delete')
|
@tables.bind_table_action('delete')
|
||||||
def delete_group(self, delete_button):
|
def delete_group(self, delete_button):
|
||||||
|
@ -30,7 +30,7 @@ class ImagesTable(tables.TableRegion):
|
|||||||
def create_image(self, create_button):
|
def create_image(self, create_button):
|
||||||
create_button.click()
|
create_button.click()
|
||||||
return forms.FormRegion(self.driver, self.conf,
|
return forms.FormRegion(self.driver, self.conf,
|
||||||
form_field_names=self.CREATE_IMAGE_FORM_FIELDS)
|
field_mappings=self.CREATE_IMAGE_FORM_FIELDS)
|
||||||
|
|
||||||
@tables.bind_table_action('delete')
|
@tables.bind_table_action('delete')
|
||||||
def delete_image(self, delete_button):
|
def delete_image(self, delete_button):
|
||||||
|
@ -33,7 +33,7 @@ class InstancesTable(tables.TableRegion):
|
|||||||
launch_button.click()
|
launch_button.click()
|
||||||
return forms.TabbedFormRegion(
|
return forms.TabbedFormRegion(
|
||||||
self.driver, self.conf,
|
self.driver, self.conf,
|
||||||
form_field_names=self.CREATE_INSTANCE_FORM_FIELDS)
|
field_mappings=self.CREATE_INSTANCE_FORM_FIELDS)
|
||||||
|
|
||||||
@tables.bind_table_action('delete')
|
@tables.bind_table_action('delete')
|
||||||
def delete_instance(self, delete_button):
|
def delete_instance(self, delete_button):
|
||||||
|
@ -26,8 +26,9 @@ class ChangepasswordPage(basepage.BaseNavigationPage):
|
|||||||
@property
|
@property
|
||||||
def password_form(self):
|
def password_form(self):
|
||||||
src_elem = self._get_element(*self._password_form_locator)
|
src_elem = self._get_element(*self._password_form_locator)
|
||||||
return forms.FormRegion(self.driver, self.conf, src_elem,
|
return forms.FormRegion(
|
||||||
self.CHANGE_PASSWORD_FORM_FIELDS)
|
self.driver, self.conf, src_elem=src_elem,
|
||||||
|
field_mappings=self.CHANGE_PASSWORD_FORM_FIELDS)
|
||||||
|
|
||||||
def change_password(self, current, new):
|
def change_password(self, current, new):
|
||||||
self.password_form.current_password.text = current
|
self.password_form.current_password.text = current
|
||||||
|
@ -44,8 +44,9 @@ class UsersettingsPage(basepage.BaseNavigationPage):
|
|||||||
@property
|
@property
|
||||||
def settings_form(self):
|
def settings_form(self):
|
||||||
src_elem = self._get_element(*self._settings_form_locator)
|
src_elem = self._get_element(*self._settings_form_locator)
|
||||||
return forms.FormRegion(self.driver, self.conf, src_elem,
|
return forms.FormRegion(
|
||||||
self.SETTINGS_FORM_FIELDS)
|
self.driver, self.conf, src_elem=src_elem,
|
||||||
|
field_mappings=self.SETTINGS_FORM_FIELDS)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def changepassword(self):
|
def changepassword(self):
|
||||||
|
@ -265,16 +265,28 @@ class FormRegion(BaseFormRegion):
|
|||||||
_fields_locator = (by.By.CSS_SELECTOR, 'fieldset')
|
_fields_locator = (by.By.CSS_SELECTOR, 'fieldset')
|
||||||
|
|
||||||
# private methods
|
# private methods
|
||||||
def __init__(self, driver, conf, src_elem=None, form_field_names=None):
|
def __init__(self, driver, conf, src_elem=None, field_mappings=None):
|
||||||
super(FormRegion, self).__init__(driver, conf, src_elem)
|
super(FormRegion, self).__init__(driver, conf, src_elem)
|
||||||
self.form_field_names = form_field_names
|
self.field_mappings = self._prepare_mappings(field_mappings)
|
||||||
self.wait_till_spinner_disappears()
|
self.wait_till_spinner_disappears()
|
||||||
self._init_form_fields()
|
self._init_form_fields()
|
||||||
|
|
||||||
|
def _prepare_mappings(self, field_mappings):
|
||||||
|
if isinstance(field_mappings, tuple):
|
||||||
|
return {item: item for item in field_mappings}
|
||||||
|
else:
|
||||||
|
return field_mappings
|
||||||
|
|
||||||
# protected methods
|
# protected methods
|
||||||
def _init_form_fields(self):
|
def _init_form_fields(self):
|
||||||
self.fields_src_elem = self._get_element(*self._fields_locator)
|
self.fields_src_elem = self._get_element(*self._fields_locator)
|
||||||
self._dynamic_properties.update(self._get_form_fields())
|
fields = self._get_form_fields()
|
||||||
|
for accessor_name, accessor_expr in self.field_mappings.items():
|
||||||
|
if isinstance(accessor_expr, six.string_types):
|
||||||
|
self._dynamic_properties[accessor_name] = fields[accessor_expr]
|
||||||
|
else: # it is a class
|
||||||
|
self._dynamic_properties[accessor_name] = accessor_expr(
|
||||||
|
self.driver, self.conf)
|
||||||
|
|
||||||
def _get_form_fields(self):
|
def _get_form_fields(self):
|
||||||
factory = FieldFactory(self.driver, self.conf, self.fields_src_elem)
|
factory = FieldFactory(self.driver, self.conf, self.fields_src_elem)
|
||||||
@ -346,18 +358,33 @@ class TabbedFormRegion(FormRegion):
|
|||||||
_submit_locator = (by.By.CSS_SELECTOR, '*.btn.btn-primary[type=submit]')
|
_submit_locator = (by.By.CSS_SELECTOR, '*.btn.btn-primary[type=submit]')
|
||||||
_side_info_locator = (by.By.CSS_SELECTOR, "td.help_text")
|
_side_info_locator = (by.By.CSS_SELECTOR, "td.help_text")
|
||||||
|
|
||||||
def __init__(self, driver, conf, form_field_names=None, default_tab=0):
|
def __init__(self, driver, conf, field_mappings=None, default_tab=0):
|
||||||
self.current_tab = default_tab
|
self.current_tab = default_tab
|
||||||
super(TabbedFormRegion, self).__init__(
|
super(TabbedFormRegion, self).__init__(
|
||||||
driver, conf, form_field_names=form_field_names)
|
driver, conf, field_mappings=field_mappings)
|
||||||
|
|
||||||
|
def _prepare_mappings(self, field_mappings):
|
||||||
|
return [super(TabbedFormRegion, self)._prepare_mappings(tab_mappings)
|
||||||
|
for tab_mappings in field_mappings]
|
||||||
|
|
||||||
def _init_form_fields(self):
|
def _init_form_fields(self):
|
||||||
self._init_tab_fields(self.current_tab)
|
self.switch_to(self.current_tab)
|
||||||
|
|
||||||
def _init_tab_fields(self, tab_index):
|
def _init_tab_fields(self, tab_index):
|
||||||
fieldsets = self._get_elements(*self._fields_locator)
|
fieldsets = self._get_elements(*self._fields_locator)
|
||||||
self.fields_src_elem = fieldsets[tab_index]
|
self.fields_src_elem = fieldsets[tab_index]
|
||||||
self._dynamic_properties.update(self._get_form_fields())
|
fields = self._get_form_fields()
|
||||||
|
current_tab_mappings = self.field_mappings[tab_index]
|
||||||
|
for accessor_name, accessor_expr in current_tab_mappings.items():
|
||||||
|
if isinstance(accessor_expr, six.string_types):
|
||||||
|
self._dynamic_properties[accessor_name] = fields[accessor_expr]
|
||||||
|
else: # it is a class
|
||||||
|
self._dynamic_properties[accessor_name] = accessor_expr(
|
||||||
|
self.driver, self.conf)
|
||||||
|
|
||||||
|
def switch_to(self, tab_index=0):
|
||||||
|
self.tabs.switch_to(index=tab_index)
|
||||||
|
self._init_tab_fields(tab_index)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tabs(self):
|
def tabs(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user