From 59717143bcc3c5823ca262b2cc3239a8ad6608a4 Mon Sep 17 00:00:00 2001 From: Timur Sufiev Date: Wed, 19 Aug 2015 18:18:31 +0300 Subject: [PATCH] Integration tests no longer use form's fields ordering for matching Relying on suggestion that some predefined list of field names in test will always be the same in actual forms rendered by Horizon is perilous. It leads to a test failure every time the order of fields in some form being tested changes or a new field appears at the beginning of the form. Tests which are using 'name' attribute for matching predefined fields to the actual form fields are much more durable since field names change less frequently. Closes-Bug: #1467950 Related-Bug: #1456294 Related-Bug: #1447432 Implements blueprint: integration-tests-hardening Change-Id: Ia39c003a628f5abbb73ff824329b2a31a0b6e179 --- .../pages/admin/system/flavorspage.py | 15 ++++++------- .../pages/identity/userspage.py | 4 ++-- .../pages/project/compute/imagespage.py | 20 ++++++++--------- .../pages/settings/changepasswordpage.py | 4 ++-- .../pages/settings/usersettingspage.py | 5 +++-- .../integration_tests/regions/baseregion.py | 11 +++++++--- .../test/integration_tests/regions/forms.py | 22 +++++++++++++++---- .../tests/test_user_settings.py | 2 +- 8 files changed, 51 insertions(+), 32 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 029b226994..05d2fd649a 100644 --- a/openstack_dashboard/test/integration_tests/pages/admin/system/flavorspage.py +++ b/openstack_dashboard/test/integration_tests/pages/admin/system/flavorspage.py @@ -30,9 +30,8 @@ class FlavorsPage(basepage.BaseNavigationPage): "modify_access", "update_metadata", "delete_flavor") } - CREATE_FLAVOR_FORM_FIELDS = (("name", "id_", "vcpus", "ram", - "root_disk", "ephemeral_disk", - "swap_disk"), + CREATE_FLAVOR_FORM_FIELDS = (("name", "flavor_id", "vcpus", "memory_mb", + "disk_gb", "eph_gb", "swap_mb"), ("all_projects", "selected_projects")) def __init__(self, driver, conf): @@ -65,12 +64,12 @@ class FlavorsPage(basepage.BaseNavigationPage): self.flavors_table.create_flavor.click() self.create_flavor_form.name.text = name if id_ is not None: - self.create_flavor_form.id_.text = id_ + self.create_flavor_form.flavor_id.text = id_ self.create_flavor_form.vcpus.value = vcpus - self.create_flavor_form.ram.value = ram - self.create_flavor_form.root_disk.value = root_disk - self.create_flavor_form.ephemeral_disk.value = ephemeral_disk - self.create_flavor_form.swap_disk.value = swap_disk + self.create_flavor_form.memory_mb.value = ram + self.create_flavor_form.disk_gb.value = root_disk + self.create_flavor_form.eph_gb.value = ephemeral_disk + self.create_flavor_form.swap_mb.value = swap_disk self.create_flavor_form.submit.click() self._wait_till_spinner_disappears() diff --git a/openstack_dashboard/test/integration_tests/pages/identity/userspage.py b/openstack_dashboard/test/integration_tests/pages/identity/userspage.py index e4ea99c65f..d037995e87 100644 --- a/openstack_dashboard/test/integration_tests/pages/identity/userspage.py +++ b/openstack_dashboard/test/integration_tests/pages/identity/userspage.py @@ -32,7 +32,7 @@ class UsersPage(basepage.BaseNavigationPage): } CREATE_USER_FORM_FIELDS = ("name", "email", "password", - "confirm_password", "project", "role") + "confirm_password", "project", "role_id") def __init__(self, driver, conf): super(UsersPage, self).__init__(driver, conf) @@ -69,7 +69,7 @@ class UsersPage(basepage.BaseNavigationPage): self.create_user_form.password.text = password self.create_user_form.confirm_password.text = password self.create_user_form.project.text = project - self.create_user_form.role.text = role + self.create_user_form.role_id.text = role self.create_user_form.submit.click() self._wait_till_spinner_disappears() 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 790ec7adf4..2d1978c84f 100644 --- a/openstack_dashboard/test/integration_tests/pages/project/compute/imagespage.py +++ b/openstack_dashboard/test/integration_tests/pages/project/compute/imagespage.py @@ -36,10 +36,10 @@ class ImagesPage(basepage.BaseNavigationPage): } CREATE_IMAGE_FORM_FIELDS = ( - "name", "description", "image_source", "image_location", + "name", "description", "source_type", "image_url", "image_file", "kernel", "ramdisk", - "format", "architecture", "minimum_disk", - "minimum_ram", "public", "protected" + "disk_format", "architecture", "minimum_disk", + "minimum_ram", "is_public", "protected" ) def __init__(self, driver, conf): @@ -69,7 +69,7 @@ class ImagesPage(basepage.BaseNavigationPage): return forms.BaseFormRegion(self.driver, self.conf, None) def create_image(self, name, description=None, - image_source=DEFAULT_IMAGE_SOURCE, + image_source_type=DEFAULT_IMAGE_SOURCE, location=None, image_file=None, image_format=DEFAULT_IMAGE_FORMAT, is_public=DEFAULT_ACCESSIBILITY, @@ -78,18 +78,18 @@ class ImagesPage(basepage.BaseNavigationPage): self.create_image_form.name.text = name if description is not None: self.create_image_form.description.text = description - self.create_image_form.image_source.value = image_source - if image_source == 'url': + self.create_image_form.source_type.value = image_source_type + if image_source_type == 'url': if location is None: - self.create_image_form.image_location.text = \ + self.create_image_form.image_url.text = \ self.conf.image.http_image else: - self.create_image_form.location.text = location + self.create_image_form.image_url.text = location else: self.create_image_form.image_file.choose(image_file) - self.create_image_form.format.value = image_format + self.create_image_form.disk_format.value = image_format if is_public: - self.create_image_form.public.mark() + self.create_image_form.is_public.mark() if is_protected: self.create_image_form.protected.mark() self.create_image_form.submit.click() diff --git a/openstack_dashboard/test/integration_tests/pages/settings/changepasswordpage.py b/openstack_dashboard/test/integration_tests/pages/settings/changepasswordpage.py index 528b167d3f..6de34061a9 100644 --- a/openstack_dashboard/test/integration_tests/pages/settings/changepasswordpage.py +++ b/openstack_dashboard/test/integration_tests/pages/settings/changepasswordpage.py @@ -21,7 +21,7 @@ class ChangepasswordPage(basepage.BaseNavigationPage): _password_form_locator = (by.By.ID, 'change_password_modal') CHANGE_PASSWORD_FORM_FIELDS = ("current_password", "new_password", - "confirm_new_password") + "confirm_password") @property def password_form(self): @@ -32,7 +32,7 @@ class ChangepasswordPage(basepage.BaseNavigationPage): def change_password(self, current, new): self.password_form.current_password.text = current self.password_form.new_password.text = new - self.password_form.confirm_new_password.text = new + self.password_form.confirm_password.text = new self.password_form.submit.click() def reset_to_default_password(self, current): diff --git a/openstack_dashboard/test/integration_tests/pages/settings/usersettingspage.py b/openstack_dashboard/test/integration_tests/pages/settings/usersettingspage.py index 93092fb715..a103e87fbd 100644 --- a/openstack_dashboard/test/integration_tests/pages/settings/usersettingspage.py +++ b/openstack_dashboard/test/integration_tests/pages/settings/usersettingspage.py @@ -30,7 +30,8 @@ class UsersettingsPage(basepage.BaseNavigationPage): "loglines": DEFAULT_LOGLINES } - SETTINGS_FORM_FIELDS = ("language", "timezone", "pagesize", "loglines") + SETTINGS_FORM_FIELDS = ( + "language", "timezone", "pagesize", "instance_log_length") _settings_form_locator = (by.By.ID, 'user_settings_modal') _change_password_tab_locator = (by.By.CSS_SELECTOR, @@ -67,7 +68,7 @@ class UsersettingsPage(basepage.BaseNavigationPage): self.settings_form.submit.click() def change_loglines(self, lines=DEFAULT_LOGLINES): - self.settings_form.loglines.value = lines + self.settings_form.instance_log_length.value = lines self.settings_form.submit.click() def return_to_default_settings(self): diff --git a/openstack_dashboard/test/integration_tests/regions/baseregion.py b/openstack_dashboard/test/integration_tests/regions/baseregion.py index f6362be6d2..e36a6830ab 100644 --- a/openstack_dashboard/test/integration_tests/regions/baseregion.py +++ b/openstack_dashboard/test/integration_tests/regions/baseregion.py @@ -65,17 +65,21 @@ class BaseRegion(basewebobject.BaseWebObject): class _DynamicProperty(object): """Serves as new property holder.""" - def __init__(self, method, index=None): + def __init__(self, method, index=None, name=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 + self.name = name def __call__(self, *args, **kwargs): result = self.method() - return result if self.index is None else result[self.index] + if isinstance(result, dict): + return result if self.name is None else result[self.name] + else: + 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.""" @@ -93,7 +97,8 @@ class BaseRegion(basewebobject.BaseWebObject): "The new property could not be " "created." % (self.__class__.__name__, new_attr_name)) - new_method = self.__class__._DynamicProperty(method, index) + new_method = self.__class__._DynamicProperty(method, index, + new_attr_name) inst_method = types.MethodType(new_method, self) self._dynamic_properties[new_attr_name] = inst_method diff --git a/openstack_dashboard/test/integration_tests/regions/forms.py b/openstack_dashboard/test/integration_tests/regions/forms.py index 80925ee03d..ab0e3b87ab 100644 --- a/openstack_dashboard/test/integration_tests/regions/forms.py +++ b/openstack_dashboard/test/integration_tests/regions/forms.py @@ -64,6 +64,10 @@ class BaseFormFieldRegion(baseregion.BaseRegion): def element(self): return self._get_element(*self._element_locator) + @property + def name(self): + return self.element.get_attribute('name') + def is_required(self): classes = self.driver.get_attribute('class') return 'required' in classes @@ -205,6 +209,10 @@ class SelectFormFieldRegion(BaseFormFieldRegion): results.append(option.get_attribute('value')) return results + @property + def name(self): + return self.element._el.get_attribute('name') + @property def text(self): return self.element.first_selected_option.text @@ -254,6 +262,7 @@ class FormRegion(BaseFormRegion): _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') + _input_locator = (by.By.CSS_SELECTOR, 'input,select,textarea') # private methods def __init__(self, driver, conf, src_elem, form_field_names): @@ -268,12 +277,13 @@ class FormRegion(BaseFormRegion): def _get_form_fields(self): fields_els = self._get_elements(*self._fields_locator) - form_fields = [] + 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()) + field = field_factory.make_form_field() + form_fields[field.name] = field finally: self._turn_on_implicit_wait() return form_fields @@ -350,8 +360,12 @@ class TabbedFormRegion(FormRegion): def __call__(self, *args, **kwargs): self.switch_to_tab(self.tab_index) - return [field for field in self.get_fields() - if field.is_displayed()] + fields = self.get_fields() + if isinstance(fields, dict): + return dict([(key, field) for (key, field) + in fields.iteritems() if field.is_displayed()]) + else: + return [field for field in fields if field.is_displayed()] @property def tabs(self): diff --git a/openstack_dashboard/test/integration_tests/tests/test_user_settings.py b/openstack_dashboard/test/integration_tests/tests/test_user_settings.py index 215e9e7e59..f09a93e01e 100644 --- a/openstack_dashboard/test/integration_tests/tests/test_user_settings.py +++ b/openstack_dashboard/test/integration_tests/tests/test_user_settings.py @@ -19,7 +19,7 @@ class TestUserSettings(helpers.TestCase): language = self.settings_page.settings_form.language.value timezone = self.settings_page.settings_form.timezone.value pagesize = self.settings_page.settings_form.pagesize.value - loglines = self.settings_page.settings_form.loglines.value + loglines = self.settings_page.settings_form.instance_log_length.value user_settings = (("Language", changed_settings["language"], language), ("Timezone", changed_settings["timezone"], timezone),