751 lines
24 KiB
Python
751 lines
24 KiB
Python
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 check_captcha_error(driver) -> str:
|
|
"""Check if there's a CAPTCHA error message on the page."""
|
|
try:
|
|
# First handle any alerts
|
|
try:
|
|
alert = driver.switch_to.alert
|
|
alert_text = alert.text
|
|
print(f" DEBUG: Alert detected in check_captcha_error: {alert_text}")
|
|
if "logout" in alert_text.lower() or "session" in alert_text.lower() or "previous" in alert_text.lower():
|
|
alert.accept()
|
|
print(" DEBUG: Accepted session/logout alert")
|
|
time.sleep(2)
|
|
except:
|
|
pass
|
|
|
|
error_selectors = [
|
|
"//div[@id='error']//strong[normalize-space()='Invalid Captcha']",
|
|
"//div[@id='error']//span[contains(@class,'help-block')]//strong[contains(text(),'Invalid')]",
|
|
"//*[contains(@class,'error') and contains(text(),'Invalid Captcha')]",
|
|
]
|
|
for selector in error_selectors:
|
|
elements = driver.find_elements(By.XPATH, selector)
|
|
for el in elements:
|
|
if el.is_displayed() and el.text.strip():
|
|
return el.text.strip()
|
|
except Exception as e:
|
|
print(f" DEBUG: check_captcha_error exception: {e}")
|
|
return ""
|
|
|
|
|
|
def check_login_success(driver) -> bool:
|
|
"""Check if login was successful using multiple indicators."""
|
|
try:
|
|
# Handle any pending alerts first
|
|
try:
|
|
alert = driver.switch_to.alert
|
|
alert_text = alert.text
|
|
print(f" DEBUG: Alert detected: {alert_text}")
|
|
if "logout" in alert_text.lower() or "session" in alert_text.lower() or "previous" in alert_text.lower():
|
|
alert.accept()
|
|
print(" DEBUG: Accepted session/logout alert")
|
|
time.sleep(2)
|
|
except:
|
|
pass
|
|
|
|
# Wait a bit for page to load after submit
|
|
time.sleep(3)
|
|
|
|
# Print current URL and page title for debugging
|
|
print(f" DEBUG: Current URL: {driver.current_url}")
|
|
print(f" DEBUG: Page title: {driver.title}")
|
|
|
|
# Multiple dashboard indicators
|
|
dashboard_indicators = [
|
|
"//span[normalize-space()='TRADING MAINTENANCE']",
|
|
"//a[contains(@href, 'tradingMaintenance')]",
|
|
"//span[contains(text(), 'Dashboard')]",
|
|
"//h1[contains(text(), 'Dashboard')]",
|
|
"//div[contains(@class, 'dashboard')]",
|
|
"//a[contains(text(), 'Trading')]",
|
|
"//div[contains(@class, 'navbar')]//span[contains(text(), 'Trading')]",
|
|
]
|
|
|
|
wait = WebDriverWait(driver, 15)
|
|
for indicator in dashboard_indicators:
|
|
try:
|
|
wait.until(EC.visibility_of_element_located((By.XPATH, indicator)))
|
|
print(f" DEBUG: Found dashboard indicator: {indicator}")
|
|
return True
|
|
except:
|
|
continue
|
|
|
|
# URL-based check
|
|
if "home" in driver.current_url.lower() or "dashboard" in driver.current_url.lower():
|
|
print(" DEBUG: Login success detected via URL")
|
|
return True
|
|
|
|
return False
|
|
except Exception as e:
|
|
print(f" DEBUG: check_login_success failed: {e}")
|
|
return False
|
|
|
|
|
|
def login(
|
|
driver,
|
|
username,
|
|
password,
|
|
max_captcha_retries=5,
|
|
captcha_metrics=None,
|
|
max_login_duration_seconds=1200,
|
|
):
|
|
|
|
wait = WebDriverWait(driver, 20)
|
|
|
|
# --- AUTOMATED CAPTCHA SOLVING WITH RETRY ON FAILURE ---
|
|
from tests.captcha_solver import solve_and_fill_captcha, find_refresh_button, wait_for_new_captcha
|
|
|
|
login_deadline = time.monotonic() + max_login_duration_seconds
|
|
captcha_attempt = 0
|
|
while True:
|
|
captcha_attempt += 1
|
|
|
|
# Refreshing after a failed CAPTCHA clears the login form, so refill
|
|
# both credentials before every automatic or manual retry.
|
|
userid_field = wait.until(
|
|
EC.visibility_of_element_located((By.XPATH, Locator.userid))
|
|
)
|
|
userid_field.clear()
|
|
userid_field.send_keys(username)
|
|
|
|
password_field = wait.until(
|
|
EC.visibility_of_element_located((By.XPATH, Locator.password))
|
|
)
|
|
password_field.clear()
|
|
password_field.send_keys(password)
|
|
|
|
if captcha_metrics is not None:
|
|
captcha_metrics["tries"] = captcha_attempt
|
|
captcha_metrics["max_retries"] = max_captcha_retries
|
|
print(f"Solving CAPTCHA (attempt {captcha_attempt})...")
|
|
|
|
# Once automatic solving has run for the configured duration, ask for
|
|
# the CAPTCHA value in a browser prompt and continue the same login flow.
|
|
if time.monotonic() >= login_deadline:
|
|
print("Automatic CAPTCHA retries timed out; waiting for manual CAPTCHA entry.")
|
|
manual_value = driver.execute_script(
|
|
"return window.prompt('Automatic CAPTCHA solving timed out. Enter the CAPTCHA shown on the page:');"
|
|
)
|
|
if not manual_value:
|
|
raise RuntimeError("Manual CAPTCHA entry was cancelled")
|
|
|
|
captcha_input = wait.until(
|
|
EC.visibility_of_element_located((By.NAME, "captcha"))
|
|
)
|
|
captcha_input.clear()
|
|
captcha_input.send_keys(manual_value.strip())
|
|
wait.until(EC.element_to_be_clickable((By.XPATH, Locator.submitbtn))).click()
|
|
time.sleep(LOGIN_POST_SUBMIT_WAIT_SECONDS)
|
|
accept_browser_alert_if_present(driver)
|
|
handle_transient_popups(driver)
|
|
|
|
captcha_error = check_captcha_error(driver)
|
|
if captcha_error:
|
|
print(f" Manual CAPTCHA rejected: {captcha_error}")
|
|
continue
|
|
|
|
if check_login_success(driver):
|
|
print("Login successful after manual CAPTCHA entry")
|
|
if captcha_metrics is not None:
|
|
captcha_metrics["correct_attempt"] = captcha_attempt
|
|
captcha_metrics.setdefault("captcha_attempts", []).append({
|
|
"attempt": captcha_attempt,
|
|
"captcha_text": "manual",
|
|
"variant": manual_value.strip(),
|
|
"status": "manual",
|
|
"screenshot": "",
|
|
"error": "",
|
|
})
|
|
close_tac_expiry_modal_if_present(driver)
|
|
return True
|
|
continue
|
|
|
|
def attempt_callback(attempt_num, captcha_text, variant, status, screenshot_path="", error=""):
|
|
if captcha_metrics is not None:
|
|
captcha_metrics.setdefault("captcha_attempts", []).append({
|
|
"attempt": attempt_num,
|
|
"captcha_text": captcha_text,
|
|
"variant": variant,
|
|
"status": status,
|
|
"screenshot": screenshot_path,
|
|
"error": error
|
|
})
|
|
|
|
if not solve_and_fill_captcha(driver, max_retries=1, attempt_callback=attempt_callback):
|
|
print(" Automatic CAPTCHA solve failed; retrying until the login deadline.")
|
|
driver.refresh()
|
|
time.sleep(3)
|
|
continue
|
|
|
|
# solve_and_fill_captcha already clicks submit, so just wait and check result
|
|
time.sleep(LOGIN_POST_SUBMIT_WAIT_SECONDS)
|
|
|
|
accept_browser_alert_if_present(driver)
|
|
|
|
# Handle any popups that appear
|
|
handle_transient_popups(driver)
|
|
|
|
# Check for CAPTCHA error (invalid captcha message)
|
|
captcha_error = check_captcha_error(driver)
|
|
if captcha_error:
|
|
print(f" CAPTCHA validation failed: {captcha_error}")
|
|
# Reload the page to get a fresh CAPTCHA and keep retrying until
|
|
# the overall login deadline is reached.
|
|
print(f" Reloading page to get fresh CAPTCHA (attempt {captcha_attempt + 1})...")
|
|
driver.refresh()
|
|
time.sleep(3)
|
|
continue
|
|
|
|
# Check for successful login - use improved detection
|
|
if check_login_success(driver):
|
|
print("Login successful")
|
|
if captcha_metrics is not None:
|
|
captcha_metrics["correct_attempt"] = captcha_attempt
|
|
close_tac_expiry_modal_if_present(driver)
|
|
return True
|
|
|
|
# If we get here, something else is wrong
|
|
print(" Login not confirmed, retrying...")
|
|
driver.refresh()
|
|
time.sleep(3)
|
|
|
|
|
|
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)
|
|
|
|
def visible_elements(xpath):
|
|
return [element for element in driver.find_elements(By.XPATH, xpath) if element.is_displayed()]
|
|
|
|
submenu_visible = any(
|
|
item.is_enabled() for item in visible_elements(menu_xpath)
|
|
)
|
|
|
|
if not submenu_visible:
|
|
maintenance_items = visible_elements(Locator.tradingmaintenance)
|
|
if not maintenance_items:
|
|
wait.until(lambda current_driver: visible_elements(Locator.tradingmaintenance))
|
|
maintenance_items = visible_elements(Locator.tradingmaintenance)
|
|
|
|
maintenance = maintenance_items[0]
|
|
driver.execute_script(
|
|
"arguments[0].scrollIntoView({block: 'center'});", maintenance
|
|
)
|
|
try:
|
|
wait.until(lambda current_driver: maintenance.is_enabled())
|
|
maintenance.click()
|
|
except Exception:
|
|
driver.execute_script("arguments[0].click();", maintenance)
|
|
|
|
menu_item = wait.until(
|
|
lambda current_driver: next(
|
|
(item for item in visible_elements(menu_xpath) if item.is_enabled()),
|
|
False,
|
|
)
|
|
)
|
|
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)
|