From 558f6701abd5052472e45f54eee51fe1098de816 Mon Sep 17 00:00:00 2001 From: Pallav Gupta Date: Fri, 27 Sep 2019 12:11:24 +0530 Subject: [PATCH] Fix router integration tests The aim of this PS is to fix skipped router integration tests listed below: - test_router_add_delete_interface (Partial-Bug: #1792028) - test_router_delete_interface_by_row (Partial-Bug: #1792028) - test_router_overview_data (Partial-Bug: #1792028) - test_router_create_admin (Partial-Bug: #1792028) Change-Id: I82b15d347831762808b17fbda8c944a1ee5de564 --- .../test/integration_tests/config.py | 6 +++ .../test/integration_tests/horizon.conf | 4 ++ .../project/network/networkoverviewpage.py | 13 +++--- .../project/network/routerinterfacespage.py | 42 ++++++++++++------- .../pages/project/network/routerspage.py | 34 +++++++-------- .../test/integration_tests/regions/forms.py | 21 ++++++---- .../integration_tests/tests/test_router.py | 38 ++++++++++++----- 7 files changed, 100 insertions(+), 58 deletions(-) diff --git a/openstack_dashboard/test/integration_tests/config.py b/openstack_dashboard/test/integration_tests/config.py index ed6c1f835a..92cab0df33 100644 --- a/openstack_dashboard/test/integration_tests/config.py +++ b/openstack_dashboard/test/integration_tests/config.py @@ -88,6 +88,12 @@ NetworkGroup = [ cfg.StrOpt('network_cidr', default='10.100.0.0/16', help='The cidr block to allocate tenant ipv4 subnets from'), + cfg.StrOpt( + 'external_network', + # Devstack default external network is 'public' but it + # can be changed as per available external network. + default='public', + help='The external network for a router creation.'), ] AvailableServiceGroup = [ diff --git a/openstack_dashboard/test/integration_tests/horizon.conf b/openstack_dashboard/test/integration_tests/horizon.conf index 1809c1e82b..cc8a4f7414 100644 --- a/openstack_dashboard/test/integration_tests/horizon.conf +++ b/openstack_dashboard/test/integration_tests/horizon.conf @@ -80,6 +80,10 @@ unique_last_password_count=2 [network] # The cidr block to allocate tenant ipv4 subnets from (string value) network_cidr=10.100.0.0/16 +# The external network for a router creation(string value) +# Devstack default external network is 'public' but it can be changed +# as per available external network. +external_network=public [service_available] # Whether is Neutron expected to be available (boolean value) diff --git a/openstack_dashboard/test/integration_tests/pages/project/network/networkoverviewpage.py b/openstack_dashboard/test/integration_tests/pages/project/network/networkoverviewpage.py index 4dda03203d..ec3b92dc50 100644 --- a/openstack_dashboard/test/integration_tests/pages/project/network/networkoverviewpage.py +++ b/openstack_dashboard/test/integration_tests/pages/project/network/networkoverviewpage.py @@ -17,19 +17,18 @@ from openstack_dashboard.test.integration_tests.pages import basepage class NetworkOverviewPage(basepage.BaseNavigationPage): - DEFAULT_NETWORK_NAME = 'public' + _network_dd_name_locator = (by.By.CSS_SELECTOR, 'dt[title*="Name"]+dd') - _network_dd_name_locator = (by.By.CSS_SELECTOR, - 'dt[title*="Name"]+dd') - - _network_dd_status_locator = (by.By.CSS_SELECTOR, - 'dt[title*="Status"]+dd') + _network_dd_status_locator = (by.By.CSS_SELECTOR, 'dt[title*="Status"]+dd') def __init__(self, driver, conf): super(NetworkOverviewPage, self).__init__(driver, conf) self._page_title = 'Network Details' + self._external_network = conf.network.external_network - def is_network_name_present(self, network_name=DEFAULT_NETWORK_NAME): + def is_network_name_present(self, network_name=None): + if network_name is None: + network_name = self._external_network dd_text = self._get_element(*self._network_dd_name_locator).text return dd_text == network_name diff --git a/openstack_dashboard/test/integration_tests/pages/project/network/routerinterfacespage.py b/openstack_dashboard/test/integration_tests/pages/project/network/routerinterfacespage.py index 2f2f2f68f8..6840dd02cc 100644 --- a/openstack_dashboard/test/integration_tests/pages/project/network/routerinterfacespage.py +++ b/openstack_dashboard/test/integration_tests/pages/project/network/routerinterfacespage.py @@ -10,7 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. - from selenium.webdriver.common import by from openstack_dashboard.test.integration_tests.pages import basepage @@ -28,8 +27,7 @@ class InterfacesTable(tables.TableRegion): return forms.FormRegion( self.driver, self.conf, - field_mappings=self.CREATE_INTERFACE_FORM_FIELDS - ) + field_mappings=self.CREATE_INTERFACE_FORM_FIELDS) @tables.bind_table_action('delete') def delete_interface(self, delete_button): @@ -46,37 +44,49 @@ class RouterInterfacesPage(basepage.BaseNavigationPage): INTERFACES_TABLE_STATUS_COLUMN = 'Status' INTERFACES_TABLE_NAME_COLUMN = 'Name' - DEFAULT_IPv4_ADDRESS = '10.0.0.10' - DEFAULT_SUBNET = 'private: 10.0.0.0/26 (private-subnet)' - - _breadcrumb_routers_locator = (by.By.CSS_SELECTOR, - 'ol.breadcrumb>li>' + - 'a[href*="/dashboard/project/routers"]') + INTERFACES_TABLE_FIXED_IPS_COLUMN = 'Fixed IPs' + DEFAULT_IPv4_ADDRESS = '10.100.0.1' + _interface_subnet_selector = (by.By.CSS_SELECTOR, 'div > .themable-select') + _breadcrumb_routers_locator = ( + by.By.CSS_SELECTOR, + 'ol.breadcrumb>li>' + 'a[href*="/project/routers"]') def __init__(self, driver, conf, router_name): super(RouterInterfacesPage, self).__init__(driver, conf) self._page_title = router_name def _get_row_with_interface_name(self, name): + return self.interfaces_table.get_row(self.INTERFACES_TABLE_NAME_COLUMN, + name) + + def _get_row_with_ip_address(self): return self.interfaces_table.get_row( - self.INTERFACES_TABLE_NAME_COLUMN, name) + self.INTERFACES_TABLE_FIXED_IPS_COLUMN, self.DEFAULT_IPv4_ADDRESS) + + @property + def subnet_selector(self): + src_elem = self._get_element(*self._interface_subnet_selector) + return forms.ThemableSelectFormFieldRegion( + self.driver, + self.conf, + src_elem=src_elem, + strict_options_match=False) @property def interfaces_table(self): return InterfacesTable(self.driver, self.conf) @property - def interfaces_names(self): - return map(lambda row: row.cells[self. - INTERFACES_TABLE_NAME_COLUMN].text, - self.interfaces_table.rows) + def interface_name(self): + row = self._get_row_with_ip_address() + return row.cells[self.INTERFACES_TABLE_NAME_COLUMN].text def switch_to_routers_page(self): self._get_element(*self._breadcrumb_routers_locator).click() - def create_interface(self): + def create_interface(self, subnet): interface_form = self.interfaces_table.create_interface() - interface_form.subnet_id.text = self.DEFAULT_SUBNET + self.subnet_selector.text = subnet interface_form.ip_address.text = self.DEFAULT_IPv4_ADDRESS interface_form.submit() diff --git a/openstack_dashboard/test/integration_tests/pages/project/network/routerspage.py b/openstack_dashboard/test/integration_tests/pages/project/network/routerspage.py index fb08f699d0..cd26f9f306 100644 --- a/openstack_dashboard/test/integration_tests/pages/project/network/routerspage.py +++ b/openstack_dashboard/test/integration_tests/pages/project/network/routerspage.py @@ -10,7 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. - from selenium.common import exceptions from selenium.webdriver.common import by @@ -26,14 +25,14 @@ from openstack_dashboard.test.integration_tests.regions import tables class RoutersTable(tables.TableRegion): name = "routers" - CREATE_ROUTER_FORM_FIELDS = ("name", "admin_state_up", - "external_network") + CREATE_ROUTER_FORM_FIELDS = ("name", "admin_state_up", "external_network") SET_GATEWAY_FORM_FIELDS = ("network_id",) @tables.bind_table_action('create') def create_router(self, create_button): create_button.click() - return forms.FormRegion(self.driver, self.conf, + return forms.FormRegion(self.driver, + self.conf, field_mappings=self.CREATE_ROUTER_FORM_FIELDS) @tables.bind_table_action('delete') @@ -49,14 +48,14 @@ class RoutersTable(tables.TableRegion): @tables.bind_row_action('setgateway') def set_gateway(self, set_gateway_button, row): set_gateway_button.click() - return forms.FormRegion(self.driver, self.conf, + return forms.FormRegion(self.driver, + self.conf, field_mappings=self.SET_GATEWAY_FORM_FIELDS) class RoutersPage(basepage.BaseNavigationPage): DEFAULT_ADMIN_STATE_UP = 'True' - DEFAULT_EXTERNAL_NETWORK = 'public' ROUTERS_TABLE_NAME_COLUMN = 'Name' ROUTERS_TABLE_STATUS_COLUMN = 'Status' ROUTERS_TABLE_NETWORK_COLUMN = 'External Network' @@ -67,29 +66,26 @@ class RoutersPage(basepage.BaseNavigationPage): def __init__(self, driver, conf): super(RoutersPage, self).__init__(driver, conf) self._page_title = "Routers" + self._external_network = conf.network.external_network def _get_row_with_router_name(self, name): - return self.routers_table.get_row( - self.ROUTERS_TABLE_NAME_COLUMN, name) + return self.routers_table.get_row(self.ROUTERS_TABLE_NAME_COLUMN, name) @property def routers_table(self): return RoutersTable(self.driver, self.conf) - def create_router(self, name, admin_state_up=DEFAULT_ADMIN_STATE_UP, - external_network=DEFAULT_EXTERNAL_NETWORK): + def create_router(self, name, admin_state_up=DEFAULT_ADMIN_STATE_UP): create_router_form = self.routers_table.create_router() create_router_form.name.text = name create_router_form.admin_state_up.value = admin_state_up - create_router_form.external_network.text = external_network + create_router_form.external_network.text = self._external_network create_router_form.submit() - def set_gateway(self, router_id, - network_name=DEFAULT_EXTERNAL_NETWORK): + def set_gateway(self, router_id): row = self._get_row_with_router_name(router_id) - set_gateway_form = self.routers_table.set_gateway(row) - set_gateway_form.network_id.text = network_name + set_gateway_form.network_id.text = self._external_network set_gateway_form.submit() def clear_gateway(self, name): @@ -111,6 +107,7 @@ class RoutersPage(basepage.BaseNavigationPage): def cell_getter(): return row.cells[self.ROUTERS_TABLE_STATUS_COLUMN] + try: self._wait_till_text_present_in_element(cell_getter, 'Active') except exceptions.TimeoutException: @@ -122,19 +119,22 @@ class RoutersPage(basepage.BaseNavigationPage): def cell_getter(): return row.cells[self.ROUTERS_TABLE_NETWORK_COLUMN] + try: self._wait_till_text_present_in_element(cell_getter, '-') except exceptions.TimeoutException: return False return True - def is_gateway_set(self, name, network_name=DEFAULT_EXTERNAL_NETWORK): + def is_gateway_set(self, name): row = self._get_row_with_router_name(name) def cell_getter(): return row.cells[self.ROUTERS_TABLE_NETWORK_COLUMN] + try: - self._wait_till_text_present_in_element(cell_getter, network_name) + self._wait_till_text_present_in_element(cell_getter, + self._external_network) except exceptions.TimeoutException: return False return True diff --git a/openstack_dashboard/test/integration_tests/regions/forms.py b/openstack_dashboard/test/integration_tests/regions/forms.py index 2d5ed38e22..b017a7d533 100644 --- a/openstack_dashboard/test/integration_tests/regions/forms.py +++ b/openstack_dashboard/test/integration_tests/regions/forms.py @@ -54,7 +54,6 @@ class FieldFactory(baseregion.BaseRegion): 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) @@ -231,8 +230,8 @@ class ThemableSelectFormFieldRegion(BaseFormFieldRegion): _dropdown_menu_locator = (by.By.CSS_SELECTOR, 'ul.dropdown-menu > li > a') def __init__(self, driver, conf, strict_options_match=True, **kwargs): - super(ThemableSelectFormFieldRegion, self).__init__( - driver, conf, **kwargs) + super(ThemableSelectFormFieldRegion, + self).__init__(driver, conf, **kwargs) self.strict_options_match = strict_options_match @property @@ -264,7 +263,7 @@ class ThemableSelectFormFieldRegion(BaseFormFieldRegion): if self.strict_options_match: match = text == str(option.text.strip()) else: - match = option.text.startswith(text) + match = text in str(option.text.strip()) if match: option.click() return @@ -348,6 +347,8 @@ class FormRegion(BaseFormRegion): self.fields_src_elem = self._get_element(*self._fields_locator) fields = self._get_form_fields() for accessor_name, accessor_expr in self.field_mappings.items(): + if accessor_expr not in fields: + continue if isinstance(accessor_expr, six.string_types): self._dynamic_properties[accessor_name] = fields[accessor_expr] else: # it is a class @@ -426,8 +427,9 @@ class TabbedFormRegion(FormRegion): def __init__(self, driver, conf, field_mappings=None, default_tab=0): self.current_tab = default_tab - super(TabbedFormRegion, self).__init__( - driver, conf, field_mappings=field_mappings) + super(TabbedFormRegion, self).__init__(driver, + conf, + field_mappings=field_mappings) def _prepare_mappings(self, field_mappings): return [ @@ -444,6 +446,8 @@ class TabbedFormRegion(FormRegion): fields = self._get_form_fields() current_tab_mappings = self.field_mappings[tab_index] for accessor_name, accessor_expr in current_tab_mappings.items(): + if accessor_expr not in fields: + continue if isinstance(accessor_expr, six.string_types): self._dynamic_properties[accessor_name] = fields[accessor_expr] else: # it is a class @@ -456,8 +460,9 @@ class TabbedFormRegion(FormRegion): @property def tabs(self): - return menus.TabbedMenuRegion( - self.driver, self.conf, src_elem=self.src_elem) + return menus.TabbedMenuRegion(self.driver, + self.conf, + src_elem=self.src_elem) class DateFormRegion(BaseFormRegion): diff --git a/openstack_dashboard/test/integration_tests/tests/test_router.py b/openstack_dashboard/test/integration_tests/tests/test_router.py index 7ad598894f..9e016d01ec 100644 --- a/openstack_dashboard/test/integration_tests/tests/test_router.py +++ b/openstack_dashboard/test/integration_tests/tests/test_router.py @@ -19,6 +19,8 @@ from openstack_dashboard.test.integration_tests.regions import messages @decorators.services_required("neutron") class TestRouters(helpers.TestCase): ROUTER_NAME = helpers.gen_random_resource_name("router") + NETWORK_NAME = helpers.gen_random_resource_name("network") + SUBNET_NAME = helpers.gen_random_resource_name("subnet") @property def routers_page(self): @@ -54,15 +56,15 @@ class TestRouters(helpers.TestCase): self._delete_router() def _create_interface(self, interfaces_page): - interfaces_page.create_interface() - interface_name = interfaces_page.interfaces_names[0] + interfaces_page.create_interface(self.SUBNET_NAME) self.assertTrue( interfaces_page.find_message_and_dismiss(messages.SUCCESS)) self.assertFalse( interfaces_page.find_message_and_dismiss(messages.ERROR)) + interface_name = interfaces_page.interface_name self.assertTrue(interfaces_page.is_interface_present(interface_name)) - self.assertTrue(interfaces_page.is_interface_status( - interface_name, 'Down')) + self.assertTrue( + interfaces_page.is_interface_status(interface_name, 'Down')) def _delete_interface(self, interfaces_page, interface_name): interfaces_page.delete_interface(interface_name) @@ -72,7 +74,18 @@ class TestRouters(helpers.TestCase): interfaces_page.find_message_and_dismiss(messages.ERROR)) self.assertFalse(interfaces_page.is_interface_present(interface_name)) - @decorators.skip_because(bugs=['1792028']) + def _create_subnet(self): + networks_page = self.home_pg.go_to_project_network_networkspage() + networks_page.create_network(self.NETWORK_NAME, self.SUBNET_NAME) + self.assertTrue( + networks_page.find_message_and_dismiss(messages.SUCCESS)) + + def _delete_subnet(self): + networks_page = self.home_pg.go_to_project_network_networkspage() + networks_page.delete_network(self.NETWORK_NAME) + self.assertTrue( + networks_page.find_message_and_dismiss(messages.SUCCESS)) + def test_router_add_delete_interface(self): """Tests the router interface creation and deletion functionalities: @@ -87,6 +100,8 @@ class TestRouters(helpers.TestCase): * Switches to the routers view by clicking on the breadcrumb link * Follows the steps to delete the router """ + self._create_subnet() + self._create_router() routers_page = self.routers_page @@ -96,7 +111,7 @@ class TestRouters(helpers.TestCase): self._create_interface(router_interfaces_page) - interface_name = router_interfaces_page.interfaces_names[0] + interface_name = router_interfaces_page.interface_name self._delete_interface(router_interfaces_page, interface_name) @@ -104,7 +119,8 @@ class TestRouters(helpers.TestCase): self._delete_router() - @decorators.skip_because(bugs=['1792028']) + self._delete_subnet() + def test_router_delete_interface_by_row(self): """Tests the router interface creation and deletion by row action: @@ -118,6 +134,8 @@ class TestRouters(helpers.TestCase): * Switches to the routers view by clicking on the breadcrumb link * Follows the steps to delete the router """ + self._create_subnet() + self._create_router() routers_page = self.routers_page @@ -127,7 +145,7 @@ class TestRouters(helpers.TestCase): self._create_interface(router_interfaces_page) - interface_name = router_interfaces_page.interfaces_names[0] + interface_name = router_interfaces_page.interface_name router_interfaces_page.delete_interface_by_row_action(interface_name) @@ -135,7 +153,8 @@ class TestRouters(helpers.TestCase): self._delete_router() - @decorators.skip_because(bugs=['1792028']) + self._delete_subnet() + def test_router_overview_data(self): self._create_router() @@ -162,7 +181,6 @@ class TestRouters(helpers.TestCase): class TestAdminRouters(helpers.AdminTestCase): ROUTER_NAME = helpers.gen_random_resource_name("router") - @decorators.skip_because(bugs=['1792028']) @decorators.services_required("neutron") def test_router_create_admin(self): """tests the router creation and deletion functionalities: