Initial commit - Selenium automation framework
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
from pathlib import Path
|
||||
import time
|
||||
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
|
||||
|
||||
def upload_bid_file(driver, upload_path):
|
||||
|
||||
file_path = Path(upload_path)
|
||||
if not file_path.exists():
|
||||
raise FileNotFoundError(f"Upload file not found: {file_path}")
|
||||
|
||||
wait = WebDriverWait(driver, 30)
|
||||
existing_inputs = _file_inputs(driver)
|
||||
|
||||
file_input = _preferred_file_input(existing_inputs)
|
||||
if file_input is None:
|
||||
upload_button = wait.until(
|
||||
EC.element_to_be_clickable(
|
||||
(
|
||||
By.XPATH,
|
||||
"//*[self::button or self::a or self::input]"
|
||||
"[normalize-space()='Upload' or contains(normalize-space(),'Upload') or @value='Upload']"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
upload_button.click()
|
||||
except Exception:
|
||||
driver.execute_script("arguments[0].click();", upload_button)
|
||||
|
||||
file_input = wait.until(
|
||||
lambda _driver: _find_new_or_existing_file_input(_driver, existing_inputs)
|
||||
)
|
||||
|
||||
if file_input is None:
|
||||
raise RuntimeError(
|
||||
"No HTML file input was found for the Upload action. "
|
||||
"If this page only exposes the native Windows file picker, we will need a Windows GUI automation fallback."
|
||||
)
|
||||
|
||||
driver.execute_script("arguments[0].style.display='block';", file_input)
|
||||
file_input.send_keys(str(file_path.resolve()))
|
||||
|
||||
wait.until(
|
||||
lambda _driver: _upload_acknowledged(_driver, file_path.name)
|
||||
)
|
||||
|
||||
|
||||
def submit_uploaded_bid(driver, post_upload_delay_seconds=0, submit_to_fetch_delay_seconds=0):
|
||||
|
||||
wait = WebDriverWait(driver, 30)
|
||||
previous_status = _current_status_text(driver)
|
||||
|
||||
if post_upload_delay_seconds > 0:
|
||||
time.sleep(post_upload_delay_seconds)
|
||||
|
||||
submit_button = wait.until(
|
||||
EC.element_to_be_clickable((By.ID, "btnSubmitOrder"))
|
||||
)
|
||||
driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", submit_button)
|
||||
try:
|
||||
submit_button.click()
|
||||
except Exception:
|
||||
driver.execute_script("arguments[0].click();", submit_button)
|
||||
|
||||
if submit_to_fetch_delay_seconds > 0:
|
||||
time.sleep(submit_to_fetch_delay_seconds)
|
||||
|
||||
fetch_button = wait.until(
|
||||
EC.element_to_be_clickable((By.ID, "btnFetch"))
|
||||
)
|
||||
driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", fetch_button)
|
||||
try:
|
||||
fetch_button.click()
|
||||
except Exception:
|
||||
driver.execute_script("arguments[0].click();", fetch_button)
|
||||
|
||||
status_text = wait.until(
|
||||
lambda _driver: _refreshed_status_text(_driver, previous_status) or False
|
||||
)
|
||||
|
||||
lowered_status = status_text.lower()
|
||||
if "reject" in lowered_status:
|
||||
raise AssertionError(f"Uploaded order was rejected. Status: {status_text}")
|
||||
|
||||
if "submit" not in lowered_status and "open" not in lowered_status and "accept" not in lowered_status:
|
||||
raise AssertionError(f"Unexpected uploaded order status after fetch: {status_text}")
|
||||
|
||||
return status_text
|
||||
|
||||
|
||||
def _find_new_or_existing_file_input(driver, existing_inputs):
|
||||
|
||||
current_inputs = _file_inputs(driver)
|
||||
|
||||
for current in current_inputs:
|
||||
if current not in existing_inputs:
|
||||
return current
|
||||
|
||||
preferred = _preferred_file_input(current_inputs)
|
||||
if preferred is not None:
|
||||
return preferred
|
||||
|
||||
for current in current_inputs:
|
||||
if current.is_displayed():
|
||||
return current
|
||||
|
||||
if current_inputs:
|
||||
return current_inputs[0]
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _file_inputs(driver):
|
||||
|
||||
return driver.find_elements(By.XPATH, "//input[@type='file' and not(@webkitdirectory)]")
|
||||
|
||||
|
||||
def _preferred_file_input(inputs):
|
||||
|
||||
if not inputs:
|
||||
return None
|
||||
|
||||
for current in inputs:
|
||||
element_id = (current.get_attribute("id") or "").lower()
|
||||
element_name = (current.get_attribute("name") or "").lower()
|
||||
outer_html = (current.get_attribute("outerHTML") or "").lower()
|
||||
|
||||
if any(token in text for token in ("upload", "file") for text in (element_id, element_name, outer_html)):
|
||||
return current
|
||||
|
||||
for current in inputs:
|
||||
if current.is_displayed():
|
||||
return current
|
||||
|
||||
return inputs[0]
|
||||
|
||||
|
||||
def _upload_acknowledged(driver, file_name):
|
||||
|
||||
page_text = driver.find_element(By.TAG_NAME, "body").text
|
||||
if file_name in page_text:
|
||||
return True
|
||||
|
||||
if "Upload Logs" in page_text or "View Upload Logs" in page_text:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _current_status_text(driver):
|
||||
|
||||
by_id = driver.find_elements(By.ID, "lblStatus")
|
||||
for candidate in by_id:
|
||||
text = candidate.text.strip()
|
||||
if text:
|
||||
return text
|
||||
|
||||
candidates = driver.find_elements(
|
||||
By.XPATH,
|
||||
"//*[contains(normalize-space(),'Status :') or contains(normalize-space(),'Status:')]"
|
||||
)
|
||||
for candidate in candidates:
|
||||
text = candidate.text.strip()
|
||||
if text:
|
||||
return text
|
||||
return ""
|
||||
|
||||
|
||||
def _refreshed_status_text(driver, previous_status):
|
||||
|
||||
current_status = _current_status_text(driver).strip()
|
||||
if not current_status:
|
||||
return ""
|
||||
|
||||
normalized_current = " ".join(current_status.split()).lower()
|
||||
normalized_previous = " ".join((previous_status or "").split()).lower()
|
||||
|
||||
terminal_tokens = ("reject", "submitted", "open", "accept")
|
||||
if any(token in normalized_current for token in terminal_tokens):
|
||||
if normalized_current != normalized_previous:
|
||||
return current_status
|
||||
|
||||
if "order id" in normalized_current:
|
||||
return current_status
|
||||
|
||||
return ""
|
||||
Reference in New Issue
Block a user