From de27c54445f861d9b541a0c10a96e77e13bbca84 Mon Sep 17 00:00:00 2001 From: Ashish Gupta Date: Mon, 18 Sep 2023 20:55:43 +0530 Subject: [PATCH] pytest based selenium tests : add_instance_pagination tests This patch add the test for instance pagination and : - modify generic instance fixtures to include the option for multiple instance creation and deletion. - Added fixture to change page_size and added helper methods. - Added data class to create actual table_definition data object. Change-Id: Id3324e604c13af9a446e551112501553520237fd --- .../test/selenium/integration/conftest.py | 57 ++++++++ .../selenium/integration/test_instances.py | 130 +++++++++++++++++- .../test/selenium/integration/test_volumes.py | 1 + openstack_dashboard/test/selenium/widgets.py | 38 +++++ 4 files changed, 219 insertions(+), 7 deletions(-) diff --git a/openstack_dashboard/test/selenium/integration/conftest.py b/openstack_dashboard/test/selenium/integration/conftest.py index 3a86b5259b..852954c484 100644 --- a/openstack_dashboard/test/selenium/integration/conftest.py +++ b/openstack_dashboard/test/selenium/integration/conftest.py @@ -13,6 +13,8 @@ import openstack as openstack_sdk import pytest +from openstack_dashboard.test.selenium import widgets + def create_conn(username, password, project, domain, auth_url): if not domain: @@ -57,3 +59,58 @@ def openstack_demo(config): ) yield conn conn.close() + + +@pytest.fixture() +def change_page_size_admin(login, config, driver): + default_page_size = 20 + new_page_size = 1 + def change_size(page_size): + + login('admin') + url = '/'.join(( + config.dashboard.dashboard_url, + 'settings', + )) + driver.get(url) + element = driver.find_element_by_xpath( + ".//input[@id='id_pagesize']") + element.clear() + element.send_keys(page_size) + driver.find_element_by_xpath(".//input[@value='Save']").click() + + change_size(new_page_size) + yield + change_size(default_page_size) + + +@pytest.fixture() +def change_page_size_demo(login, config, driver): + default_page_size = 20 + new_page_size = 1 + def change_size(page_size): + + login('user') + url = '/'.join(( + config.dashboard.dashboard_url, + 'settings', + )) + driver.get(url) + element = driver.find_element_by_xpath( + ".//input[@id='id_pagesize']") + element.clear() + element.send_keys(page_size) + driver.find_element_by_xpath(".//input[@value='Save']").click() + + change_size(new_page_size) + yield + change_size(default_page_size) + + +def pytest_assertrepr_compare(op, left, right): + if isinstance(left, widgets.TableDefinition) and \ + isinstance(right, widgets.TableDefinition) and op == "==": + return [ + "Comparing TableDefinition instances:", + " vals: {} != {}".format(left, right), + ] diff --git a/openstack_dashboard/test/selenium/integration/test_instances.py b/openstack_dashboard/test/selenium/integration/test_instances.py index f361449296..8d1f580ba9 100644 --- a/openstack_dashboard/test/selenium/integration/test_instances.py +++ b/openstack_dashboard/test/selenium/integration/test_instances.py @@ -9,6 +9,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. + from oslo_utils import uuidutils import pytest from selenium.common import exceptions @@ -25,12 +26,13 @@ new_volume_demo = test_volumes.new_volume_demo @pytest.fixture def instance_name(): - return 'xhorizon_instance_%s' % uuidutils.generate_uuid(dashed=False) + return 'horizon_instance_%s' % uuidutils.generate_uuid(dashed=False) -@pytest.fixture -def new_instance_demo(instance_name, openstack_demo, config): +@pytest.fixture(params=[1]) +def new_instance_demo(request, instance_name, openstack_demo, config): + count = request.param instance = openstack_demo.create_server( instance_name, image=config.image.images_list[0], @@ -38,14 +40,20 @@ def new_instance_demo(instance_name, openstack_demo, config): availability_zone=config.launch_instances.available_zone, network=config.network.external_network, wait=True, + max_count=count, ) yield instance - openstack_demo.delete_server(instance_name) + if count > 1: + for instance in range(0, count): + openstack_demo.delete_server(f"{instance_name}-{instance+1}") + else: + openstack_demo.delete_server(instance_name) -@pytest.fixture -def new_instance_admin(instance_name, openstack_admin, config): +@pytest.fixture(params=[1]) +def new_instance_admin(request, instance_name, openstack_admin, config): + count = request.param instance = openstack_admin.create_server( instance_name, image=config.image.images_list[0], @@ -53,9 +61,14 @@ def new_instance_admin(instance_name, openstack_admin, config): availability_zone=config.launch_instances.available_zone, network=config.network.external_network, wait=True, + max_count=count, ) yield instance - openstack_admin.delete_server(instance_name) + if count > 1: + for instance in range(0, count): + openstack_admin.delete_server(f"{instance_name}-{instance+1}") + else: + openstack_admin.delete_server(instance_name) @pytest.fixture @@ -228,6 +241,58 @@ def test_delete_instance_demo(login, driver, instance_name, openstack_demo, assert f"Info: Scheduled deletion of Instance: {instance_name}" in messages assert openstack_demo.compute.find_server(instance_name) is None + +@pytest.mark.parametrize('new_instance_demo', [2], indirect=True) +def test_instance_pagination_demo(login, driver, instance_name, + new_instance_demo, change_page_size_demo, + config): + """This test checks instance pagination for demo user + + Steps: + 1) Login to Horizon Dashboard as demo user + 2) Create 2 instances + 3) Navigate to user settings page + 4) Change 'Items Per Page' value to 1 + 5) Go to Instances page + 6) Check that only 'Next' link is available, only one instance is + available (and it has correct name) on the first page + 7) Click 'Next' and check that on the second page only one instance is + available (and it has correct name), there is no 'Next' link on page + 8) Click 'Prev' and check result (should be the same as for step6) + 9) Go to user settings page and restore 'Items Per Page' to default + 10) Delete created instances + """ + items_per_page = 1 + instance_count = 2 + instance_list = ["{0}-{1}".format(instance_name, item) + for item in range(1, instance_count + 1)] + first_page_definition = widgets.TableDefinition(next=True, prev=False, + count=items_per_page, + names=[instance_list[1]]) + second_page_definition = widgets.TableDefinition(next=False, prev=True, + count=items_per_page, + names=[instance_list[0]]) + url = '/'.join(( + config.dashboard.dashboard_url, + 'project', + 'instances' + )) + driver.get(url) + actual_page1_definition = widgets.get_table_definition(driver, + sorting=True) + assert first_page_definition == actual_page1_definition + # Turning to next page + driver.find_element_by_link_text("Next »").click() + actual_page2_definition = widgets.get_table_definition(driver, + sorting=True) + assert second_page_definition == actual_page2_definition + # Turning back to previous page + driver.find_element_by_link_text("« Prev").click() + actual_page1_definition = widgets.get_table_definition(driver, + sorting=True) + assert first_page_definition == actual_page1_definition + + # Admin tests @@ -294,3 +359,54 @@ def test_delete_instance_admin(login, driver, instance_name, openstack_admin, messages = widgets.get_and_dismiss_messages(driver) assert f"Info: Scheduled deletion of Instance: {instance_name}" in messages assert openstack_admin.compute.find_server(instance_name) is None + + +@pytest.mark.parametrize('new_instance_admin', [2], indirect=True) +def test_instance_pagination_admin(login, driver, instance_name, + new_instance_admin, change_page_size_admin, + config): + """This test checks instance pagination for admin user + + Steps: + 1) Login to Horizon Dashboard as admin user + 2) Create 2 instances + 3) Navigate to user settings page + 4) Change 'Items Per Page' value to 1 + 5) Go to Instances page + 6) Check that only 'Next' link is available, only one instance is + available (and it has correct name) on the first page + 7) Click 'Next' and check that on the second page only one instance is + available (and it has correct name), there is no 'Next' link on page + 8) Click 'Prev' and check result (should be the same as for step6) + 9) Go to user settings page and restore 'Items Per Page' to default + 10) Delete created instances + """ + items_per_page = 1 + instance_count = 2 + instance_list = ["{0}-{1}".format(instance_name, item) + for item in range(1, instance_count + 1)] + first_page_definition = widgets.TableDefinition(next=True, prev=False, + count=items_per_page, + names=[instance_list[1]]) + second_page_definition = widgets.TableDefinition(next=False, prev=True, + count=items_per_page, + names=[instance_list[0]]) + url = '/'.join(( + config.dashboard.dashboard_url, + 'project', + 'instances' + )) + driver.get(url) + actual_page1_definition = widgets.get_table_definition(driver, + sorting=True) + assert first_page_definition == actual_page1_definition + # Turning to next page + driver.find_element_by_link_text("Next »").click() + actual_page2_definition = widgets.get_table_definition(driver, + sorting=True) + assert second_page_definition == actual_page2_definition + # Turning back to previous page + driver.find_element_by_link_text("« Prev").click() + actual_page1_definition = widgets.get_table_definition(driver, + sorting=True) + assert first_page_definition == actual_page1_definition diff --git a/openstack_dashboard/test/selenium/integration/test_volumes.py b/openstack_dashboard/test/selenium/integration/test_volumes.py index 0b0ee189c4..87e95902e9 100644 --- a/openstack_dashboard/test/selenium/integration/test_volumes.py +++ b/openstack_dashboard/test/selenium/integration/test_volumes.py @@ -9,6 +9,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. + from oslo_utils import uuidutils import pytest diff --git a/openstack_dashboard/test/selenium/widgets.py b/openstack_dashboard/test/selenium/widgets.py index b2eb4f0272..e8c2b37483 100644 --- a/openstack_dashboard/test/selenium/widgets.py +++ b/openstack_dashboard/test/selenium/widgets.py @@ -10,11 +10,22 @@ # License for the specific language governing permissions and limitations # under the License. +from dataclasses import dataclass +from selenium.common import exceptions from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait import WebDriverWait +@dataclass +class TableDefinition: + """Class for keeping track of fields on the page""" + next: bool + prev: bool + count: int + names: list + + def get_and_dismiss_messages(element): messages = element.find_elements_by_css_selector("div.messages div.alert") collect = [] @@ -47,3 +58,30 @@ def confirm_modal(element): ".modal-dialog .btn-danger" ) confirm.click() + + +def is_next_link_available(driver): + try: + return driver.find_element_by_link_text("Next »").is_displayed() + except (exceptions.NoSuchElementException, + exceptions.ElementNotVisibleException): + return False + + +def is_prev_link_available(driver): + try: + return driver.find_element_by_link_text("« Prev").is_displayed() + except (exceptions.NoSuchElementException, + exceptions.ElementNotVisibleException): + return False + + +def get_table_definition(driver, sorting=False): + names = driver.find_elements_by_css_selector('table tr td:nth-child(2)') + rows = driver.find_elements_by_css_selector("tr[data-display-key='name']") + if sorting: + names.sort() + actual_table = TableDefinition(next=is_next_link_available(driver), + prev=is_prev_link_available(driver), + count=len(rows), names=[names[0].text]) + return actual_table