From 6462d93ea3027080be8f4b8471f7de80ec0918f3 Mon Sep 17 00:00:00 2001 From: Ashish Gupta Date: Tue, 9 Jan 2024 23:53:34 +0530 Subject: [PATCH] pytest-based selenium tests add images tests Added test_image_filtration_admin test_remove_protected_image_admin test_edit_image_description_admin Change-Id: I375d2adcbc3d4e8af86519897a87c3b965228b65 --- .../test/selenium/integration/test_images.py | 147 ++++++++++++++++++ openstack_dashboard/test/selenium/widgets.py | 35 +++-- 2 files changed, 168 insertions(+), 14 deletions(-) diff --git a/openstack_dashboard/test/selenium/integration/test_images.py b/openstack_dashboard/test/selenium/integration/test_images.py index d56d13d1ad..9279a7bc76 100644 --- a/openstack_dashboard/test/selenium/integration/test_images.py +++ b/openstack_dashboard/test/selenium/integration/test_images.py @@ -12,6 +12,7 @@ import os import tempfile +import time import pytest @@ -58,6 +59,21 @@ def new_image_admin(image_names, temporary_file, openstack_admin): openstack_admin.delete_image(img) +@pytest.fixture +def new_protected_image_admin(image_names, temporary_file, openstack_admin): + for img in image_names: + image = openstack_admin.create_image( + img, + disk_format="qcow2", + filename=temporary_file, + is_protected=True, + wait=True, + ) + yield image + for img in image_names: + openstack_admin.delete_image(img) + + @pytest.fixture def clear_image_demo(image_names, openstack_demo): yield None @@ -90,6 +106,35 @@ def temporary_file(tmp_path): yield tmp_file.name +def wait_for_steady_state_of_unprotected_image(openstack, image_name): + for attempt in range(3): + image_attributes_details = openstack.image.find_image(image_name) + if image_attributes_details["status"] == "active" \ + and image_attributes_details["protected"] is False: + break + else: + time.sleep(2) + + +def wait_for_angular_readiness(driver): + driver.set_script_timeout(10) + driver.execute_async_script(""" + var callback = arguments[arguments.length - 1]; + var element = document.querySelector('div.btn-group[name="protected"]'); + if (!window.angular) { + callback(false) + } + if (angular.getTestability) { + angular.getTestability(element).whenStable(function(){callback(true)}); + } else { + if (!angular.element(element).injector()) { + callback(false) + } + var browser = angular.element(element).injector().get('$browser'); + browser.notifyWhenNoOutstandingRequests(function(){callback(true)}); + };""") + + def test_image_create_from_local_file_demo(login, driver, image_names, temporary_file, clear_image_demo, config, openstack_demo): @@ -284,3 +329,105 @@ def test_image_pagination_admin(login, driver, image_names, openstack_admin, actual_page1_definition = widgets.get_image_table_definition(driver, sorting=True) assert first_page_definition == actual_page1_definition + + +def test_image_filtration_admin(login, driver, new_image_admin, config): + image_name = new_image_admin.name + login('admin', 'admin') + url = '/'.join(( + config.dashboard.dashboard_url, + 'project', + 'images', + )) + driver.get(url) + filter_input_field = driver.find_element_by_css_selector(".search-input") + filter_input_field.send_keys(image_name) + # Fetch page definition after filtration + current_page_definition = widgets.get_image_table_definition(driver) + assert vars(current_page_definition)['names'][0] == image_name + assert vars(current_page_definition)['count'] == 1 + filter_input_field.clear() + # Generate random non existent image name + random_img_name = 'horizon_img_%s' % uuidutils.generate_uuid(dashed=False) + filter_input_field.send_keys(random_img_name) + # Fetch page definition after filtration + current_page_definition = widgets.get_image_table_definition(driver) + assert current_page_definition is None + + +def test_remove_protected_image_admin(login, driver, image_names, + new_protected_image_admin, config, + openstack_admin): + image_name = new_protected_image_admin.name + login('admin', 'admin') + url = '/'.join(( + config.dashboard.dashboard_url, + 'project', + 'images', + )) + driver.get(url) + rows = driver.find_elements_by_xpath(f"//a[text()='{image_name}']") + assert len(rows) == 1 + actions_column = rows[0].find_element_by_xpath( + ".//ancestor::tr/td[contains(@class,'actions_column')]") + menu_button = actions_column.find_element_by_css_selector( + ".dropdown-toggle" + ) + menu_button.click() + options = actions_column.find_elements_by_css_selector( + "ul.dropdown-menu li") + for option in options: + if option.text == "Delete Image": + pytest.fail("Delete option should not exist") + actions_column.find_element_by_xpath( + f".//*[normalize-space()='Edit Image']").click() + wait_for_angular_readiness(driver) + image_form = driver.find_element_by_css_selector(".ng-wizard") + image_form.find_element_by_xpath(".//label[text()='No']").click() + image_form.find_element_by_xpath( + ".//button[@class='btn btn-primary finish']").click() + messages = widgets.get_and_dismiss_messages(driver) + assert f"Success: Image {image_name} was successfully updated." in messages + wait_for_steady_state_of_unprotected_image(openstack_admin, image_name) + rows = driver.find_elements_by_xpath(f"//a[text()='{image_name}']") + actions_column = rows[0].find_element_by_xpath( + ".//ancestor::tr/td[contains(@class,'actions_column')]") + widgets.select_from_dropdown(actions_column, "Delete Image") + widgets.confirm_modal(driver) + messages = widgets.get_and_dismiss_messages(driver) + assert f"Success: Deleted Image: {image_name}." in messages + assert openstack_admin.compute.find_image(image_name) is None + + +def test_edit_image_description_admin(login, driver, image_names, + new_image_admin, config, + openstack_admin): + image_name = new_image_admin.name + new_description = "new_description_text" + login('admin', 'admin') + url = '/'.join(( + config.dashboard.dashboard_url, + 'project', + 'images', + )) + driver.get(url) + rows = driver.find_elements_by_xpath(f"//a[text()='{image_name}']") + assert len(rows) == 1 + actions_column = rows[0].find_element_by_xpath( + ".//ancestor::tr/td[contains(@class,'actions_column')]") + widgets.select_from_dropdown(actions_column, "Edit Image") + wait_for_angular_readiness(driver) + image_form = driver.find_element_by_css_selector(".ng-wizard") + desc_field = image_form.find_element_by_css_selector( + "#imageForm-description") + + desc_field.clear() + desc_field.send_keys(new_description) + image_form.find_element_by_xpath( + ".//button[@class='btn btn-primary finish']").click() + messages = widgets.get_and_dismiss_messages(driver) + assert f"Success: Image {image_name} " \ + f"was successfully updated." in messages + image_id = new_image_admin.id + assert (openstack_admin.compute.get(f"/images/{image_id}").json( + )['image']['metadata']['description'] == new_description) diff --git a/openstack_dashboard/test/selenium/widgets.py b/openstack_dashboard/test/selenium/widgets.py index c00e76e023..fb57ba0be3 100644 --- a/openstack_dashboard/test/selenium/widgets.py +++ b/openstack_dashboard/test/selenium/widgets.py @@ -87,24 +87,31 @@ def is_prev_link_available(driver): def get_table_definition(driver, sorting=False): - names = driver.find_elements_by_css_selector('table tr td:nth-child(2)') - if sorting: - names.sort() - actual_table = TableDefinition(next=is_next_link_available(driver), - prev=is_prev_link_available(driver), - count=len(names), names=[names[0].text]) - return actual_table + try: + names = driver.find_elements_by_css_selector('table tr td:nth-child(2)') + if sorting: + names.sort() + actual_table = TableDefinition(next=is_next_link_available(driver), + prev=is_prev_link_available(driver), + count=len(names), names=[names[0].text]) + return actual_table + except exceptions.WebDriverException: + return None def get_image_table_definition(driver, sorting=False): """This function is specific to Image table definition""" - names = driver.find_elements_by_css_selector("td[class='rsp-p1 word-wrap']") - if sorting: - names.sort() - actual_table = TableDefinition(next=is_next_link_available(driver), - prev=is_prev_link_available(driver), - count=len(names), names=[names[0].text]) - return actual_table + try: + names = driver.find_elements_by_css_selector( + "td[class='rsp-p1 word-wrap']") + if sorting: + names.sort() + actual_table = TableDefinition(next=is_next_link_available(driver), + prev=is_prev_link_available(driver), + count=len(names), names=[names[0].text]) + return actual_table + except (exceptions.WebDriverException, IndexError): + return None def select_from_transfer_table(element, label):