From e2a4e6c294e9c23f69445585bbdb32ccd261ff21 Mon Sep 17 00:00:00 2001 From: Michal Nasiadka Date: Tue, 21 Oct 2025 15:52:34 +0200 Subject: [PATCH] CI: Use selenium in Horizon tests Change-Id: I814413fdb1a74675846a4ee2d5fbada9592fceef Signed-off-by: Michal Nasiadka --- .../tasks/main.yml | 59 +++++++++ tests/post.yml | 20 +++ tests/testinfra/test_horizon.py | 118 ++++++++++++++++++ zuul.d/scenarios/aio.yaml | 4 +- 4 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 tests/testinfra/test_horizon.py diff --git a/roles/kolla-ansible-test-dashboard/tasks/main.yml b/roles/kolla-ansible-test-dashboard/tasks/main.yml index 561867aedd..e09d62a334 100644 --- a/roles/kolla-ansible-test-dashboard/tasks/main.yml +++ b/roles/kolla-ansible-test-dashboard/tasks/main.yml @@ -19,3 +19,62 @@ until: dashboard_output.content.find('Login') != -1 retries: 30 delay: 10 + +- name: Check if testinfra subdirectory exists + ansible.builtin.stat: + path: "{{ zuul.project.src_dir }}/tests/testinfra" + register: testinfra_dir + +- name: Run testinfra tests + when: testinfra_dir.stat.exists + block: + - name: Ensure testinfra subdirectory exists + ansible.builtin.file: + path: "/home/zuul/testinfra" + state: directory + + - name: Ensure screenshots directory exists + ansible.builtin.file: + path: "/home/zuul/testinfra/screenshots" + state: directory + + - name: Ensure required packages are installed + ansible.builtin.pip: + name: + - pytest-html + - pytest-testinfra + - selenium + virtualenv: "{{ kolla_ansible_venv_path }}" + virtualenv_command: "python3 -m venv" + + - name: Run Selenium Firefox container (Docker) + become: true + when: container_engine == 'docker' + community.docker.docker_container: + name: "selenium" + detach: true + image: "quay.io/opendevmirror/selenium-standalone-firefox:latest" + network_mode: host + + - name: Run Selenium Firefox container (Podman) + become: true + when: container_engine == 'podman' + containers.podman.podman_container: + name: "selenium" + detach: true + image: "quay.io/opendevmirror/selenium-standalone-firefox:latest" + network_mode: host + + - name: Wait for port 444 to be up + ansible.builtin.wait_for: + port: 4444 + + - name: Run testinfra tests + ansible.builtin.shell: + cmd: > + . {{ kolla_ansible_venv_path }}/bin/activate && + py.test + --junit-xml /home/zuul/testinfra/testinfra-junit.xml -o junit_family=xunit1 + --html=/home/zuul/testinfra/test-results-testinfra.html --self-contained-html + -v tests/testinfra + chdir: "{{ zuul.project.src_dir }}" diff --git a/tests/post.yml b/tests/post.yml index 4d99737506..83b34638f5 100644 --- a/tests/post.yml +++ b/tests/post.yml @@ -7,6 +7,18 @@ zuul_work_dir: '/home/zuul/tempest' tasks: + - name: Return artifact to Zuul + zuul_return: + data: + zuul: + artifacts: + - name: "TestInfra Unit Test Report" + url: "testinfra/test-results-testinfra.html" + metadata: + type: unit_test_report + - name: "TestInfra Screenshots" + url: "testinfra/screenshots" + # TODO(mhiner): Currently only Docker to Podman migration is tested. # If we want to test the other direction we have to rework this. - name: Change container engine after the migration @@ -76,6 +88,14 @@ ara_report_local_dir: "{{ zuul.executor.log_root }}/{{ inventory_hostname }}/ara-report" kolla_ansible_local_src_dir: "{{ zuul.executor.work_root }}/src/{{ zuul.project.canonical_hostname }}/openstack/kolla-ansible" tasks: + - name: Download testinfra to executor + synchronize: + src: "/home/zuul/testinfra" + dest: "{{ zuul.executor.log_root }}/" + mode: pull + # TODO(mnasiadka): Remove in G/2026.1 cycle + ignore_errors: true + - name: Check for existence of ara sqlite stat: path: "{{ ansible_env.HOME }}/.ara/server/ansible.sqlite" diff --git a/tests/testinfra/test_horizon.py b/tests/testinfra/test_horizon.py new file mode 100644 index 0000000000..84b101e22d --- /dev/null +++ b/tests/testinfra/test_horizon.py @@ -0,0 +1,118 @@ +# Copyright 2018 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import time +import yaml + +from pathlib import Path +from selenium.common.exceptions import TimeoutException +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait + +home = Path.home() +subpath = '/testinfra/screenshots/' +screenshot_path = str(home) + subpath + +with open("/etc/kolla/passwords.yml", 'r') as file: + passwords = yaml.safe_load(file) + admin_password = passwords.get('keystone_admin_password') + + +def test_horizon_screenshot(host): + + firefox_options = webdriver.FirefoxOptions() + + driver = webdriver.Remote( + command_executor='http://localhost:4444/wd/hub', + options=firefox_options) + + horizon_url = "https://192.0.2.10" + + try: + driver.get(horizon_url) + WebDriverWait(driver, 30).until( + lambda driver: driver.execute_script( + 'return document.readyState') == 'complete') + + time.sleep(5) + + original_size = driver.get_window_size() + required_width = driver.execute_script( + 'return document.body.parentNode.scrollWidth') + required_height = driver.execute_script( + 'return document.body.parentNode.scrollHeight') + 100 + driver.set_window_size(required_width, required_height) + + driver.find_element(By.TAG_NAME, 'body').\ + screenshot(screenshot_path + "horizon-main.png") # nosec B108 + + driver.set_window_size( + original_size['width'], original_size['height']) + + assert 'Login' in driver.title # nosec B101 + + except TimeoutException as e: + raise e + finally: + driver.quit() + + +def test_horizon_login(host): + + firefox_options = webdriver.FirefoxOptions() + + driver = webdriver.Remote( + command_executor='http://localhost:4444/wd/hub', + options=firefox_options) + + horizon_url = "https://192.0.2.10" + logout_url = '/'.join(( + horizon_url, + 'auth', + 'logout')) + + try: + driver.get(logout_url) + user_field = driver.find_element(By.ID, 'id_username') + user_field.send_keys('admin') + pass_field = driver.find_element(By.ID, 'id_password') + pass_field.send_keys(admin_password) + button = driver.find_element(By.CSS_SELECTOR, '.btn-primary') + button.click() + WebDriverWait(driver, 30).until( + lambda driver: driver.execute_script( + 'return document.readyState') == 'complete') + + time.sleep(10) + + original_size = driver.get_window_size() + required_width = driver.execute_script( + 'return document.body.parentNode.scrollWidth') + required_height = driver.execute_script( + 'return document.body.parentNode.scrollHeight') + 100 + driver.set_window_size(required_width, required_height) + + driver.find_element(By.TAG_NAME, 'body').\ + screenshot(screenshot_path + "horizon-logged-in.png") # nosec B108 + + driver.set_window_size( + original_size['width'], original_size['height']) + + assert 'Overview - OpenStack Dashboard' in driver.title # nosec B101 + + except TimeoutException as e: + raise e + finally: + driver.quit() diff --git a/zuul.d/scenarios/aio.yaml b/zuul.d/scenarios/aio.yaml index 39bbe5f05d..2e8252112c 100644 --- a/zuul.d/scenarios/aio.yaml +++ b/zuul.d/scenarios/aio.yaml @@ -8,8 +8,8 @@ - ^ansible/(action_plugins|filter_plugins|library|module_utils)/ - ^ansible/roles/(common|fluentd|glance|haproxy-config|heat|horizon|keystone|loadbalancer|loadbalancer-config|neutron|nova|nova-cell|openvswitch|placement|proxysql|rabbitmq)/ - ^kolla_ansible/ - - ^roles/kolla-ansible-deploy/ - - ^tests/test-(core-openstack|dashboard).sh + - ^roles/kolla-ansible-(deploy|test-dashboard)/ + - ^tests/testinfra/test_horizon.py - ^tools/init-runonce - job: