import json import os from datetime import datetime from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.keys import Keys from selenium.common.exceptions import NoAlertPresentException, TimeoutException, StaleElementReferenceException from .locators import Locator from .config_reader import ( get_login_captcha_wait_seconds, get_login_post_submit_wait_seconds, get_step_delay_seconds, ) import time import msvcrt STEP_DELAY_SECONDS = get_step_delay_seconds() LOGIN_CAPTCHA_WAIT_SECONDS = get_login_captcha_wait_seconds() LOGIN_POST_SUBMIT_WAIT_SECONDS = get_login_post_submit_wait_seconds() def pause_after_step(seconds=STEP_DELAY_SECONDS): time.sleep(seconds) def wait_for_manual_input(prompt="Press Enter in the terminal when you want to finish this run..."): print(prompt) try: input() except EOFError as exc: raise RuntimeError( "Manual input is required for this test. Run pytest with -s so Enter can be captured." ) from exc def clear_pending_console_input(): while msvcrt.kbhit(): msvcrt.getwch() def enter_pressed(): if not msvcrt.kbhit(): return False pressed = False while msvcrt.kbhit(): key = msvcrt.getwch() if key in ("\r", "\n"): pressed = True return pressed def login(driver, username, password): wait = WebDriverWait(driver, 20) # username userid_field = wait.until( EC.visibility_of_element_located((By.XPATH, Locator.userid)) ) userid_field.send_keys(username) # password password_field = driver.find_element(By.XPATH, Locator.password) password_field.send_keys(password) print(f"Solve captcha within {LOGIN_CAPTCHA_WAIT_SECONDS} seconds...") # wait for manual captcha solve time.sleep(LOGIN_CAPTCHA_WAIT_SECONDS) # click submit submit_btn = wait.until( EC.element_to_be_clickable((By.XPATH, Locator.submitbtn)) ) submit_btn.click() time.sleep(LOGIN_POST_SUBMIT_WAIT_SECONDS) accept_browser_alert_if_present(driver) # wait for dashboard wait.until( EC.visibility_of_element_located((By.XPATH, Locator.dashboard_marker)) ) close_tac_expiry_modal_if_present(driver) print("Login successful") def accept_browser_alert_if_present(driver, timeout=5): try: alert = WebDriverWait(driver, timeout).until(EC.alert_is_present()) print(f"Browser alert accepted: {alert.text}") alert.accept() return True except (TimeoutException, NoAlertPresentException): return False def close_tac_expiry_modal_if_present(driver, timeout=5): try: close_button = WebDriverWait(driver, timeout).until( EC.element_to_be_clickable((By.XPATH, Locator.tacexpiryclosebtn)) ) except TimeoutException: return False try: close_button.click() except Exception: driver.execute_script("arguments[0].click();", close_button) print("Closed TAC Expiry modal") return True def handle_transient_popups(driver): accepted_alert = accept_browser_alert_if_present(driver, timeout=2) closed_modal = close_tac_expiry_modal_if_present(driver, timeout=2) return accepted_alert or closed_modal def backup_current_session_token(driver): session_backup = { "current_url": driver.current_url, "cookies": driver.get_cookies(), } print(f"Backed up current browser session with {len(session_backup['cookies'])} cookies") return session_backup def save_session_backup(driver, test_context, name="session_backup"): backup = backup_current_session_token(driver) backup["created_at"] = datetime.now().isoformat(timespec="seconds") path = os.path.join(test_context["folder"], f"{name}.json") with open(path, "w", encoding="utf-8") as backup_file: json.dump(backup, backup_file, indent=2) test_context.setdefault("extra_folders", []) print(f"Session backup saved: {path}") return backup def logout(driver): wait = WebDriverWait(driver, 20) handle_transient_popups(driver) try: user_menu = wait.until( EC.element_to_be_clickable((By.XPATH, Locator.usermenutoggle)) ) driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", user_menu) try: user_menu.click() except Exception: driver.execute_script("arguments[0].click();", user_menu) wait.until( EC.visibility_of_element_located( ( By.XPATH, "//li[contains(@class,'dropdown') and contains(@class,'navbar-user') and contains(@class,'open')]" ) ) ) logout_link = wait.until( EC.element_to_be_clickable((By.XPATH, Locator.logoutlink)) ) try: logout_link.click() except Exception: driver.execute_script("arguments[0].click();", logout_link) accept_browser_alert_if_present(driver, timeout=3) wait.until( EC.visibility_of_element_located((By.XPATH, Locator.userid)) ) print("Logout successful") return True except TimeoutException: print("Logout link was not found; browser will still close from fixture teardown") return False def select_ui_dropdown(driver, dropdown_label, value): wait = WebDriverWait(driver, 20) # 1️⃣ Open dropdown dropdown = wait.until( EC.element_to_be_clickable( (By.XPATH, f"//span[@aria-label='{dropdown_label} activate']") ) ) dropdown.click() # 2️⃣ Find search box search_box = wait.until( EC.visibility_of_element_located((By.CSS_SELECTOR, "input.ui-select-search")) ) # 3️⃣ Type product name search_box.clear() search_box.send_keys(value) # 4️⃣ Wait for dropdown options to load option_xpath = f"//div[contains(@class,'ui-select-choices-row')]//span[contains(text(),'{value}')]" option = wait.until( EC.element_to_be_clickable((By.XPATH, option_xpath)) ) # 5️⃣ Click option option.click() print(f"{value} selected successfully") def select_labeled_ui_dropdown(driver, field_label, value): wait = WebDriverWait(driver, 20) dropdown_xpath = ( f"//label[normalize-space()='{field_label}']" "/following::span[contains(@class,'ui-select-toggle')][1]" ) dropdown = wait.until( EC.element_to_be_clickable( ( By.XPATH, dropdown_xpath ) ) ) driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", dropdown) try: dropdown.click() except Exception: driver.execute_script("arguments[0].click();", dropdown) search_xpath = ( f"//label[normalize-space()='{field_label}']" "/following::input[contains(@class,'ui-select-search')][1]" ) search_box = wait.until( EC.visibility_of_element_located( (By.XPATH, search_xpath) ) ) search_box.click() search_box.send_keys(Keys.CONTROL, "a") search_box.clear() search_box.send_keys(value) wait.until( EC.presence_of_element_located( (By.XPATH, "//div[contains(@class,'ui-select-choices-row')]") ) ) def _visible_choice_rows(): rows = driver.find_elements(By.XPATH, "//div[contains(@class,'ui-select-choices-row')]") return [row for row in rows if row.is_displayed()] option = None visible_rows = _visible_choice_rows() for row in visible_rows: if row.text.strip() == value: option = row break if option is None: available_options = [row.text.strip() for row in visible_rows if row.text.strip()] raise AssertionError( f"Exact option '{value}' was not available in {field_label}. " f"Visible options: {available_options}" ) driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", option) try: option.click() except Exception: driver.execute_script("arguments[0].click();", option) print(f"{value} selected successfully from {field_label}") def open_trading_maintenance_page(driver, menu_xpath): wait = WebDriverWait(driver, 20) trading_maintenance = wait.until( EC.presence_of_element_located((By.XPATH, Locator.tradingmaintenance)) ) driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", trading_maintenance) submenu = driver.find_elements(By.XPATH, menu_xpath) submenu_visible = any(item.is_displayed() for item in submenu) if not submenu_visible: try: wait.until(EC.element_to_be_clickable((By.XPATH, Locator.tradingmaintenance))) trading_maintenance.click() except Exception: driver.execute_script("arguments[0].click();", trading_maintenance) menu_item = wait.until( EC.element_to_be_clickable((By.XPATH, menu_xpath)) ) try: menu_item.click() except Exception: driver.execute_script("arguments[0].click();", menu_item) def click_visible_menu_item(driver, menu_xpath): wait = WebDriverWait(driver, 20) menu_items = driver.find_elements(By.XPATH, menu_xpath) for menu_item in menu_items: if menu_item.is_displayed(): driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", menu_item) try: menu_item.click() except Exception: driver.execute_script("arguments[0].click();", menu_item) return menu_item = wait.until( EC.element_to_be_clickable((By.XPATH, menu_xpath)) ) try: menu_item.click() except Exception: driver.execute_script("arguments[0].click();", menu_item) def type_into_field(driver, locator, value): wait = WebDriverWait(driver, 20) field = wait.until( EC.visibility_of_element_located(locator) ) field.clear() field.send_keys(value) def select_bootstrap_dropdown_by_id(driver, element_id, value, exact=False): wait = WebDriverWait(driver, 20) container = wait.until( EC.presence_of_element_located((By.ID, element_id)) ) driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", container) toggle_xpath = ( f"//div[@id='{element_id}']//span[contains(@class,'ui-select-toggle')]" f"|//div[@id='{element_id}']//span[contains(@class,'btn-default')]" f"|//div[@id='{element_id}']//span[contains(@class,'ui-select-placeholder')]" f"|//div[@id='{element_id}']//input[contains(@class,'ui-select-search')]" ) toggle = wait.until( EC.presence_of_element_located((By.XPATH, toggle_xpath)) ) try: wait.until(EC.element_to_be_clickable((By.XPATH, toggle_xpath))) toggle.click() except Exception: driver.execute_script("arguments[0].click();", toggle) try: search_box = wait.until( EC.visibility_of_element_located( (By.XPATH, f"//div[@id='{element_id}']//input[contains(@class,'ui-select-search')]") ) ) except TimeoutException: search_box = wait.until( EC.visibility_of_element_located( (By.XPATH, "//input[contains(@class,'ui-select-search') and not(@aria-hidden='true')]") ) ) search_box.click() search_box.send_keys(Keys.CONTROL, "a") search_box.clear() search_box.send_keys(value) option_xpath = ( f"//div[@id='{element_id}']//div[contains(@class,'ui-select-choices-row')]" f"[normalize-space()='{value}']" if exact else "//div[contains(@class,'ui-select-choices-row')]" f"//*[normalize-space()='{value}' or starts-with(normalize-space(),'{value}')]" ) option = wait.until( EC.element_to_be_clickable( ( By.XPATH, option_xpath ) ) ) option.click() print(f"{value} selected successfully from {element_id}") def click_grid_button_for_row_text(driver, row_text, button_text): wait = WebDriverWait(driver, 20) button_xpath = ( "//div[contains(@class,'ui-grid-row')]" f"[contains(.,'{row_text}')]" f"//button[normalize-space()='{button_text}']" ) last_error = None for _ in range(3): try: button = wait.until( EC.presence_of_element_located((By.XPATH, button_xpath)) ) driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", button) wait.until(EC.element_to_be_clickable((By.XPATH, button_xpath))) refreshed_button = driver.find_element(By.XPATH, button_xpath) try: refreshed_button.click() except Exception: driver.execute_script("arguments[0].click();", refreshed_button) print(f"Clicked {button_text} for row containing {row_text}") return except StaleElementReferenceException as exc: last_error = exc time.sleep(1) if last_error: raise last_error def click_first_grid_button(driver, button_text): wait = WebDriverWait(driver, 20) button_xpath = f"(//button[normalize-space()='{button_text}' and not(contains(@class,'ng-hide'))])[1]" last_error = None for _ in range(3): try: button = wait.until( EC.presence_of_element_located((By.XPATH, button_xpath)) ) driver.execute_script("arguments[0].scrollIntoView({block: 'start'});", button) refreshed_button = driver.find_element(By.XPATH, button_xpath) try: refreshed_button.click() except Exception: driver.execute_script("arguments[0].click();", refreshed_button) print(f"Clicked first visible {button_text}") return except StaleElementReferenceException as exc: last_error = exc time.sleep(1) if last_error: raise last_error def type_first_visible_input(driver, xpath, value): wait = WebDriverWait(driver, 20) def _find_visible_input(_driver): elements = _driver.find_elements(By.XPATH, xpath) for element in elements: if element.is_displayed() and element.is_enabled(): return element return False element = wait.until(_find_visible_input) driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", element) element.click() element.clear() element.send_keys(value) def toggle_bootstrap_dropdown_by_id(driver, element_id): wait = WebDriverWait(driver, 20) toggle_xpath = ( f"//div[@id='{element_id}']//span[contains(@class,'ui-select-toggle')]" f"|//div[@id='{element_id}']//span[contains(@class,'btn-default')]" f"|//div[@id='{element_id}']//span[contains(@class,'ui-select-placeholder')]" f"|//div[@id='{element_id}']//input[contains(@class,'ui-select-search')]" ) toggle = wait.until( EC.presence_of_element_located((By.XPATH, toggle_xpath)) ) driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", toggle) try: wait.until(EC.element_to_be_clickable((By.XPATH, toggle_xpath))) toggle.click() except Exception: driver.execute_script("arguments[0].click();", toggle) def click_page_body(driver): body = WebDriverWait(driver, 20).until( EC.presence_of_element_located((By.TAG_NAME, "body")) ) body.click() def toggle_labeled_ui_dropdown(driver, field_label): wait = WebDriverWait(driver, 20) dropdown_xpath = ( f"//label[normalize-space()='{field_label}']" "/following::span[contains(@class,'ui-select-toggle')][1]" ) dropdown = wait.until( EC.element_to_be_clickable((By.XPATH, dropdown_xpath)) ) driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", dropdown) try: dropdown.click() except Exception: driver.execute_script("arguments[0].click();", dropdown)