From 4083994b868df88cf80ec0c39612bb537ef6f897 Mon Sep 17 00:00:00 2001 From: nrao Date: Tue, 10 Dec 2024 14:51:31 +0530 Subject: [PATCH 1/2] Add UI locators and utility functions for login automation Signed-off-by: nrao --- ceph/UI/config/login.yaml | 23 +++++++++++++++++++++++ ceph/UI/ids.py | 34 ++++++++++++++++++++++++++++++++++ ceph/UI/utilities.py | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 ceph/UI/config/login.yaml create mode 100644 ceph/UI/ids.py create mode 100644 ceph/UI/utilities.py diff --git a/ceph/UI/config/login.yaml b/ceph/UI/config/login.yaml new file mode 100644 index 000000000..8649e846a --- /dev/null +++ b/ceph/UI/config/login.yaml @@ -0,0 +1,23 @@ +login_username: + id: "username" + name: "username" + xpath: "//input[@name='username']" + css: "#username" + +login_password: + id: "password" + name: "password" + xpath: "//input[@name='password']" + css: "#password" + +login_button: + id: "login-btn" + name: "login" + xpath: "//button[@type='submit']" + css: ".btn-login" + +search_box: + id: "search" + name: "q" + xpath: "//input[@name='q']" + css: ".search-box" diff --git a/ceph/UI/ids.py b/ceph/UI/ids.py new file mode 100644 index 000000000..bfdc27c57 --- /dev/null +++ b/ceph/UI/ids.py @@ -0,0 +1,34 @@ +import yaml + + +class ElementIDs: + def __init__(self, yaml_file_path: str): + """ + Initialize ElementIDs with the path to the YAML file containing element identifiers. + :param yaml_file_path: Path to the YAML file. + """ + self.yaml_file_path = yaml_file_path + self.elements = self._load_ids() + + def _load_ids(self) -> dict: + """ + Load element IDs from a YAML file. + :return: A dictionary of element identifiers. + """ + try: + with open(self.yaml_file_path, "r") as file: + return yaml.safe_load(file) + except FileNotFoundError: + raise RuntimeError(f"YAML file not found: {self.yaml_file_path}") + except yaml.YAMLError as e: + raise RuntimeError(f"Error parsing YAML file: {e}") + + def get_element(self, key: str) -> dict: + """ + Get the locator types for a given key. + :param key: The key for the desired element. + :return: A dictionary with locator types (id, name, xpath, etc.) for the element. + """ + if key not in self.elements: + raise KeyError(f"Element key '{key}' not found in YAML.") + return self.elements[key] diff --git a/ceph/UI/utilities.py b/ceph/UI/utilities.py new file mode 100644 index 000000000..68fa32a8f --- /dev/null +++ b/ceph/UI/utilities.py @@ -0,0 +1,39 @@ +from selenium.webdriver.common.by import By + + +def read_table(driver, table_ref: str, locator_type: str = "id") -> list: + """ + Reads the data from a table on a webpage. + :param driver: WebDriver instance. + :param table_ref: The reference to locate the table (e.g., ID, class, XPath, etc.). + :param locator_type: The type of locator ('id', 'name', 'class', 'xpath', 'css'). Defaults to 'id'. + :return: A list of lists, where each sublist represents a row in the table. + """ + by_types = { + "id": By.ID, + "name": By.NAME, + "class": By.CLASS_NAME, + "xpath": By.XPATH, + "css": By.CSS_SELECTOR, + } + + if locator_type not in by_types: + raise ValueError(f"Unsupported locator type: {locator_type}") + + try: + + table_element = driver.find_element(by_types[locator_type], table_ref) + + rows = table_element.find_elements(By.TAG_NAME, "tr") + + table_data = [] + for row in rows: + cells = row.find_elements(By.TAG_NAME, "td") + if not cells: + cells = row.find_elements(By.TAG_NAME, "th") + row_data = [cell.text.strip() for cell in cells] + table_data.append(row_data) + + return table_data + except Exception as e: + raise RuntimeError(f"Failed to read table with reference '{table_ref}': {e}") From 8237e60476596d1335eed7ed53ae84afdd691600 Mon Sep 17 00:00:00 2001 From: nrao Date: Mon, 25 Nov 2024 13:13:27 +0530 Subject: [PATCH 2/2] Add operations and helper modules for improved UI automation Signed-off-by: nrao --- ceph/UI/config/login.yaml | 23 ------------------ ceph/UI/helper.py | 50 +++++++++++++++++++++++++++++++++++++++ ceph/UI/ids.py | 34 -------------------------- ceph/UI/operations.py | 33 ++++++++++++++++++++++++++ ceph/UI/utilities.py | 39 ------------------------------ 5 files changed, 83 insertions(+), 96 deletions(-) delete mode 100644 ceph/UI/config/login.yaml create mode 100644 ceph/UI/helper.py delete mode 100644 ceph/UI/ids.py create mode 100644 ceph/UI/operations.py delete mode 100644 ceph/UI/utilities.py diff --git a/ceph/UI/config/login.yaml b/ceph/UI/config/login.yaml deleted file mode 100644 index 8649e846a..000000000 --- a/ceph/UI/config/login.yaml +++ /dev/null @@ -1,23 +0,0 @@ -login_username: - id: "username" - name: "username" - xpath: "//input[@name='username']" - css: "#username" - -login_password: - id: "password" - name: "password" - xpath: "//input[@name='password']" - css: "#password" - -login_button: - id: "login-btn" - name: "login" - xpath: "//button[@type='submit']" - css: ".btn-login" - -search_box: - id: "search" - name: "q" - xpath: "//input[@name='q']" - css: ".search-box" diff --git a/ceph/UI/helper.py b/ceph/UI/helper.py new file mode 100644 index 000000000..c829345b8 --- /dev/null +++ b/ceph/UI/helper.py @@ -0,0 +1,50 @@ +from selenium.webdriver.common.by import By +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.ui import WebDriverWait + + +def capture_screen(driver, filename: str) -> str: + """ + Captures a screenshot and saves it to the specified filename. + :param driver: WebDriver instance. + :param filename: The filename to save the screenshot as. + :return: The path of the saved screenshot. + """ + try: + if driver.save_screenshot(filename): + return filename + else: + raise RuntimeError(f"Screenshot capture failed for file: {filename}") + except Exception as e: + raise RuntimeError(f"Failed to capture screenshot: {e}") + + +def wait_for_element(driver, locator: str, locator_type: str = "id", timeout: int = 10): + """ + Waits for a specific web element to become visible. + :param driver: WebDriver instance. + :param locator: The locator for the element. + :param locator_type: The type of locator ('id', 'name', 'class', 'xpath', 'css'). + :param timeout: Maximum time to wait for the element to become visible (default: 10 seconds). + :return: The WebElement if found within the timeout period. + """ + by_types = { + "id": By.ID, + "name": By.NAME, + "class": By.CLASS_NAME, + "xpath": By.XPATH, + "css": By.CSS_SELECTOR, + } + + if locator_type not in by_types: + raise ValueError(f"Unsupported locator type: {locator_type}") + + try: + element = WebDriverWait(driver, timeout).until( + EC.visibility_of_element_located((by_types[locator_type], locator)) + ) + return element + except Exception as e: + raise RuntimeError( + f"Element with locator '{locator}' and type '{locator_type}' not visible within {timeout} seconds: {e}" + ) diff --git a/ceph/UI/ids.py b/ceph/UI/ids.py deleted file mode 100644 index bfdc27c57..000000000 --- a/ceph/UI/ids.py +++ /dev/null @@ -1,34 +0,0 @@ -import yaml - - -class ElementIDs: - def __init__(self, yaml_file_path: str): - """ - Initialize ElementIDs with the path to the YAML file containing element identifiers. - :param yaml_file_path: Path to the YAML file. - """ - self.yaml_file_path = yaml_file_path - self.elements = self._load_ids() - - def _load_ids(self) -> dict: - """ - Load element IDs from a YAML file. - :return: A dictionary of element identifiers. - """ - try: - with open(self.yaml_file_path, "r") as file: - return yaml.safe_load(file) - except FileNotFoundError: - raise RuntimeError(f"YAML file not found: {self.yaml_file_path}") - except yaml.YAMLError as e: - raise RuntimeError(f"Error parsing YAML file: {e}") - - def get_element(self, key: str) -> dict: - """ - Get the locator types for a given key. - :param key: The key for the desired element. - :return: A dictionary with locator types (id, name, xpath, etc.) for the element. - """ - if key not in self.elements: - raise KeyError(f"Element key '{key}' not found in YAML.") - return self.elements[key] diff --git a/ceph/UI/operations.py b/ceph/UI/operations.py new file mode 100644 index 000000000..e2a8d79f4 --- /dev/null +++ b/ceph/UI/operations.py @@ -0,0 +1,33 @@ +from ceph.UI.dashboard import Dashboard + + +class Operations: + def __init__(self, browser_type: str): + self.dashboard = Dashboard(browser_type) + + def login( + self, + username_key: str, + password_key: str, + login_button_key: str, + username: str, + password: str, + ): + username_field = self.dashboard.find_element(username_key) + username_field.clear() + username_field.send_keys(username) + + password_field = self.dashboard.find_element(password_key) + password_field.clear() + password_field.send_keys(password) + + self.dashboard.click(login_button_key) + print("Login successful!") + + def perform_click(self, element_key: str): + self.dashboard.click(element_key) + + def perform_input(self, element_key: str, text: str): + element = self.dashboard.find_element(element_key) + element.clear() + element.send_keys(text) diff --git a/ceph/UI/utilities.py b/ceph/UI/utilities.py deleted file mode 100644 index 68fa32a8f..000000000 --- a/ceph/UI/utilities.py +++ /dev/null @@ -1,39 +0,0 @@ -from selenium.webdriver.common.by import By - - -def read_table(driver, table_ref: str, locator_type: str = "id") -> list: - """ - Reads the data from a table on a webpage. - :param driver: WebDriver instance. - :param table_ref: The reference to locate the table (e.g., ID, class, XPath, etc.). - :param locator_type: The type of locator ('id', 'name', 'class', 'xpath', 'css'). Defaults to 'id'. - :return: A list of lists, where each sublist represents a row in the table. - """ - by_types = { - "id": By.ID, - "name": By.NAME, - "class": By.CLASS_NAME, - "xpath": By.XPATH, - "css": By.CSS_SELECTOR, - } - - if locator_type not in by_types: - raise ValueError(f"Unsupported locator type: {locator_type}") - - try: - - table_element = driver.find_element(by_types[locator_type], table_ref) - - rows = table_element.find_elements(By.TAG_NAME, "tr") - - table_data = [] - for row in rows: - cells = row.find_elements(By.TAG_NAME, "td") - if not cells: - cells = row.find_elements(By.TAG_NAME, "th") - row_data = [cell.text.strip() for cell in cells] - table_data.append(row_data) - - return table_data - except Exception as e: - raise RuntimeError(f"Failed to read table with reference '{table_ref}': {e}")