192 lines
5.6 KiB
Python
192 lines
5.6 KiB
Python
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 ""
|