From c573b5b316402e9a18a4e573296a102c90ddb623 Mon Sep 17 00:00:00 2001 From: Timur Sufiev Date: Wed, 30 Dec 2015 18:17:59 +0300 Subject: [PATCH] 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 --- .../pages/admin/system/flavorspage.py | 2 +- .../pages/identity/projectspage.py | 2 +- .../pages/identity/userspage.py | 2 +- .../access_and_security/keypairspage.py | 2 +- .../access_and_security/securitygroupspage.py | 2 +- .../pages/project/compute/imagespage.py | 2 +- .../pages/project/compute/instancespage.py | 2 +- .../pages/settings/changepasswordpage.py | 5 ++- .../pages/settings/usersettingspage.py | 5 ++- .../test/integration_tests/regions/forms.py | 41 +++++++++++++++---- 10 files changed, 47 insertions(+), 18 deletions(-) diff --git a/openstack_dashboard/test/integration_tests/pages/admin/system/flavorspage.py b/openstack_dashboard/test/integration_tests/pages/admin/system/flavorspage.py index 1b5ab0d2e..3e72d2993 100644 --- a/openstack_dashboard/test/integration_tests/pages/admin/system/flavorspage.py +++ b/openstack_dashboard/test/integration_tests/pages/admin/system/flavorspage.py @@ -27,7 +27,7 @@ class FlavorsTable(tables.TableRegion): create_button.click() return forms.TabbedFormRegion( self.driver, self.conf, - form_field_names=self.CREATE_FLAVOR_FORM_FIELDS) + field_mappings=self.CREATE_FLAVOR_FORM_FIELDS) @tables.bind_table_action('delete') def delete_flavor(self, delete_button): diff --git a/openstack_dashboard/test/integration_tests/pages/identity/projectspage.py b/openstack_dashboard/test/integration_tests/pages/identity/projectspage.py index 3c6bd6c76..fff32bf9e 100644 --- a/openstack_dashboard/test/integration_tests/pages/identity/projectspage.py +++ b/openstack_dashboard/test/integration_tests/pages/identity/projectspage.py @@ -24,7 +24,7 @@ class ProjectsTable(tables.TableRegion): create_button.click() return forms.TabbedFormRegion( self.driver, self.conf, - form_field_names=self.CREATE_PROJECT_FORM_FIELDS) + field_mappings=self.CREATE_PROJECT_FORM_FIELDS) @tables.bind_table_action('delete') def delete_project(self, delete_button): diff --git a/openstack_dashboard/test/integration_tests/pages/identity/userspage.py b/openstack_dashboard/test/integration_tests/pages/identity/userspage.py index b66b6a7c9..1f35bcf71 100644 --- a/openstack_dashboard/test/integration_tests/pages/identity/userspage.py +++ b/openstack_dashboard/test/integration_tests/pages/identity/userspage.py @@ -24,7 +24,7 @@ class UsersTable(tables.TableRegion): def create_user(self, create_button): create_button.click() 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') def delete_user(self, delete_button): diff --git a/openstack_dashboard/test/integration_tests/pages/project/compute/access_and_security/keypairspage.py b/openstack_dashboard/test/integration_tests/pages/project/compute/access_and_security/keypairspage.py index e28ef8484..ab2372e8b 100644 --- a/openstack_dashboard/test/integration_tests/pages/project/compute/access_and_security/keypairspage.py +++ b/openstack_dashboard/test/integration_tests/pages/project/compute/access_and_security/keypairspage.py @@ -27,7 +27,7 @@ class KeypairsTable(tables.TableRegion): create_button.click() return forms.FormRegion( 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) def delete_keypair(self, delete_button, row): diff --git a/openstack_dashboard/test/integration_tests/pages/project/compute/access_and_security/securitygroupspage.py b/openstack_dashboard/test/integration_tests/pages/project/compute/access_and_security/securitygroupspage.py index 4917b7d9b..a56cb4d47 100644 --- a/openstack_dashboard/test/integration_tests/pages/project/compute/access_and_security/securitygroupspage.py +++ b/openstack_dashboard/test/integration_tests/pages/project/compute/access_and_security/securitygroupspage.py @@ -24,7 +24,7 @@ class SecurityGroupsTable(tables.TableRegion): create_button.click() return forms.FormRegion( self.driver, self.conf, - form_field_names=self.CREATE_SECURITYGROUP_FORM_FIELDS) + field_mappings=self.CREATE_SECURITYGROUP_FORM_FIELDS) @tables.bind_table_action('delete') def delete_group(self, delete_button): diff --git a/openstack_dashboard/test/integration_tests/pages/project/compute/imagespage.py b/openstack_dashboard/test/integration_tests/pages/project/compute/imagespage.py index fd5322c46..2298ae87c 100644 --- a/openstack_dashboard/test/integration_tests/pages/project/compute/imagespage.py +++ b/openstack_dashboard/test/integration_tests/pages/project/compute/imagespage.py @@ -30,7 +30,7 @@ class ImagesTable(tables.TableRegion): def create_image(self, create_button): create_button.click() 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') def delete_image(self, delete_button): diff --git a/openstack_dashboard/test/integration_tests/pages/project/compute/instancespage.py b/openstack_dashboard/test/integration_tests/pages/project/compute/instancespage.py index 825dbfcdb..96eb3ec2a 100644 --- a/openstack_dashboard/test/integration_tests/pages/project/compute/instancespage.py +++ b/openstack_dashboard/test/integration_tests/pages/project/compute/instancespage.py @@ -33,7 +33,7 @@ class InstancesTable(tables.TableRegion): launch_button.click() return forms.TabbedFormRegion( self.driver, self.conf, - form_field_names=self.CREATE_INSTANCE_FORM_FIELDS) + field_mappings=self.CREATE_INSTANCE_FORM_FIELDS) @tables.bind_table_action('delete') def delete_instance(self, delete_button): diff --git a/openstack_dashboard/test/integration_tests/pages/settings/changepasswordpage.py b/openstack_dashboard/test/integration_tests/pages/settings/changepasswordpage.py index d795a2172..f4e0f5469 100644 --- a/openstack_dashboard/test/integration_tests/pages/settings/changepasswordpage.py +++ b/openstack_dashboard/test/integration_tests/pages/settings/changepasswordpage.py @@ -26,8 +26,9 @@ class ChangepasswordPage(basepage.BaseNavigationPage): @property def password_form(self): src_elem = self._get_element(*self._password_form_locator) - return forms.FormRegion(self.driver, self.conf, src_elem, - self.CHANGE_PASSWORD_FORM_FIELDS) + return forms.FormRegion( + self.driver, self.conf, src_elem=src_elem, + field_mappings=self.CHANGE_PASSWORD_FORM_FIELDS) def change_password(self, current, new): self.password_form.current_password.text = current diff --git a/openstack_dashboard/test/integration_tests/pages/settings/usersettingspage.py b/openstack_dashboard/test/integration_tests/pages/settings/usersettingspage.py index b850593da..683ff550b 100644 --- a/openstack_dashboard/test/integration_tests/pages/settings/usersettingspage.py +++ b/openstack_dashboard/test/integration_tests/pages/settings/usersettingspage.py @@ -44,8 +44,9 @@ class UsersettingsPage(basepage.BaseNavigationPage): @property def settings_form(self): src_elem = self._get_element(*self._settings_form_locator) - return forms.FormRegion(self.driver, self.conf, src_elem, - self.SETTINGS_FORM_FIELDS) + return forms.FormRegion( + self.driver, self.conf, src_elem=src_elem, + field_mappings=self.SETTINGS_FORM_FIELDS) @property def changepassword(self): diff --git a/openstack_dashboard/test/integration_tests/regions/forms.py b/openstack_dashboard/test/integration_tests/regions/forms.py index a1e1b8c40..88b97ae1d 100644 --- a/openstack_dashboard/test/integration_tests/regions/forms.py +++ b/openstack_dashboard/test/integration_tests/regions/forms.py @@ -265,16 +265,28 @@ class FormRegion(BaseFormRegion): _fields_locator = (by.By.CSS_SELECTOR, 'fieldset') # 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) - self.form_field_names = form_field_names + self.field_mappings = self._prepare_mappings(field_mappings) self.wait_till_spinner_disappears() 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 def _init_form_fields(self): 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): 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]') _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 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): - self._init_tab_fields(self.current_tab) + self.switch_to(self.current_tab) def _init_tab_fields(self, tab_index): fieldsets = self._get_elements(*self._fields_locator) 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 def tabs(self):