fix: DDMA Create claim stale element, tooth dropdown select, longer page waits
step2: wait for patient list //tbody//tr then 3s stabilize; wait for patient name link to be element_to_be_clickable before reading href; wait for Create claim button to be element_to_be_clickable (visible+enabled) then 3s for React to finish re-rendering. step3: re-find Create claim button fresh each attempt (avoids stale element from React re-render); try selenium click → js events → js.click() in sequence; verify URL changed before declaring success. step4: open tooth dropdown via JS focus (avoids element-not-interactable on click); select the matching tooth number option directly from 1-32 listbox instead of typing characters. step7: find Submit claim button with individual XPaths to avoid NoneType crash. claims-page: use wouter setLocation for URL param cleanup so internal search state stays in sync. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -341,30 +341,32 @@ class AutomationDDMAClaimSubmit:
|
||||
def step2_open_member_page(self):
|
||||
"""Navigate to member detail page — same approach as DDMA eligibility step2."""
|
||||
try:
|
||||
# Wait for the results table to appear, then let it stabilize
|
||||
try:
|
||||
WebDriverWait(self.driver, 10).until(
|
||||
WebDriverWait(self.driver, 20).until(
|
||||
EC.presence_of_element_located((By.XPATH, "//tbody//tr"))
|
||||
)
|
||||
time.sleep(2)
|
||||
time.sleep(3) # wait for the list to fully stabilize
|
||||
except TimeoutException:
|
||||
print("[DDMA Claim step2] Warning: Results table not found within timeout")
|
||||
|
||||
# Find member-details URL from first row — identical to eligibility step2
|
||||
# Wait until the patient name link in the first row is clickable
|
||||
detail_url = None
|
||||
for selector in [
|
||||
PATIENT_LINK_XPATHS = [
|
||||
"(//table//tbody//tr)[1]//td[1]//a",
|
||||
"(//tbody//tr)[1]//a[contains(@href,'member-details')]",
|
||||
"(//tbody//tr)[1]//a[contains(@href,'member')]",
|
||||
"//a[contains(@href,'member-details')]",
|
||||
]:
|
||||
]
|
||||
for selector in PATIENT_LINK_XPATHS:
|
||||
try:
|
||||
link_el = WebDriverWait(self.driver, 5).until(
|
||||
EC.presence_of_element_located((By.XPATH, selector))
|
||||
link_el = WebDriverWait(self.driver, 20).until(
|
||||
EC.element_to_be_clickable((By.XPATH, selector))
|
||||
)
|
||||
href = link_el.get_attribute("href")
|
||||
if href and "member-details" in href:
|
||||
detail_url = href
|
||||
print(f"[DDMA Claim step2] Found detail URL: {href}")
|
||||
print(f"[DDMA Claim step2] Patient link is clickable: {href}")
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
@@ -384,14 +386,16 @@ class AutomationDDMAClaimSubmit:
|
||||
print(f"[DDMA Claim step2] Warning — URL: {self.driver.current_url}")
|
||||
|
||||
try:
|
||||
# Wait until Create claim button is visible AND enabled (not just in the DOM)
|
||||
WebDriverWait(self.driver, 15).until(
|
||||
EC.presence_of_element_located((By.XPATH, "//button[@aria-label='Create claim']"))
|
||||
EC.element_to_be_clickable((By.XPATH, "//button[@aria-label='Create claim']"))
|
||||
)
|
||||
print("[DDMA Claim step2] 'Create claim' button found")
|
||||
print("[DDMA Claim step2] 'Create claim' button is clickable")
|
||||
time.sleep(3) # let React finish any remaining re-renders
|
||||
except TimeoutException:
|
||||
print("[DDMA Claim step2] Warning: 'Create claim' button not found")
|
||||
print("[DDMA Claim step2] Warning: 'Create claim' button not clickable in time")
|
||||
time.sleep(2)
|
||||
|
||||
time.sleep(2)
|
||||
return "SUCCESS"
|
||||
|
||||
except Exception as e:
|
||||
@@ -406,46 +410,92 @@ class AutomationDDMAClaimSubmit:
|
||||
try:
|
||||
print(f"[DDMA Claim step3] Current URL: {self.driver.current_url}")
|
||||
handles_before = set(self.driver.window_handles)
|
||||
url_before = self.driver.current_url
|
||||
|
||||
self.driver.execute_script("window.scrollTo(0, 0);")
|
||||
time.sleep(0.5)
|
||||
|
||||
# Log all buttons on page for debugging
|
||||
all_btns = self.driver.find_elements(By.XPATH, "//button")
|
||||
print(f"[DDMA Claim step3] Buttons on page: {[b.get_attribute('aria-label') or b.text for b in all_btns]}")
|
||||
# Re-find the button fresh each attempt to avoid stale element references.
|
||||
# The page may re-render after step2, so we do not cache the element.
|
||||
CLAIM_BTN_XPATHS = [
|
||||
"//button[@aria-label='Create claim']",
|
||||
"//button[contains(normalize-space(text()),'Create claim')]",
|
||||
"//button[.//span[contains(text(),'Create claim')]]",
|
||||
]
|
||||
|
||||
btn = WebDriverWait(self.driver, 10).until(
|
||||
EC.element_to_be_clickable((By.XPATH,
|
||||
"//button[@aria-label='Create claim' and @data-react-aria-pressable='true']"
|
||||
))
|
||||
)
|
||||
print(f"[DDMA Claim step3] Found 'Create claim' button, displayed={btn.is_displayed()}, enabled={btn.is_enabled()}")
|
||||
btn = None
|
||||
for xpath in CLAIM_BTN_XPATHS:
|
||||
try:
|
||||
btn = WebDriverWait(self.driver, 8).until(
|
||||
EC.element_to_be_clickable((By.XPATH, xpath))
|
||||
)
|
||||
if btn:
|
||||
print(f"[DDMA Claim step3] Found 'Create claim' via: {xpath}")
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
if not btn:
|
||||
all_btns = self.driver.find_elements(By.XPATH, "//button")
|
||||
print(f"[DDMA Claim step3] Buttons on page: {[b.get_attribute('aria-label') or b.text for b in all_btns]}")
|
||||
return "ERROR: step3 failed: 'Create claim' button not found"
|
||||
|
||||
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", btn)
|
||||
time.sleep(0.5)
|
||||
|
||||
# Try all click methods in sequence until one causes navigation
|
||||
self.driver.execute_script("""
|
||||
var el = arguments[0];
|
||||
el.dispatchEvent(new PointerEvent('pointerover', {bubbles:true, cancelable:true, composed:true}));
|
||||
el.dispatchEvent(new PointerEvent('pointerdown', {bubbles:true, cancelable:true, composed:true}));
|
||||
el.dispatchEvent(new PointerEvent('pointerup', {bubbles:true, cancelable:true, composed:true}));
|
||||
el.dispatchEvent(new MouseEvent('click', {bubbles:true, cancelable:true, composed:true}));
|
||||
""", btn)
|
||||
print("[DDMA Claim step3] Dispatched pointer+click events on 'Create claim'")
|
||||
# Try direct Selenium click first, fall back to JS events
|
||||
clicked = False
|
||||
for attempt, method in enumerate(["selenium", "js_events", "js_click"]):
|
||||
try:
|
||||
# Re-find fresh on each attempt to avoid stale reference
|
||||
for xpath in CLAIM_BTN_XPATHS:
|
||||
try:
|
||||
btn = WebDriverWait(self.driver, 5).until(
|
||||
EC.element_to_be_clickable((By.XPATH, xpath))
|
||||
)
|
||||
if btn:
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
time.sleep(2)
|
||||
if method == "selenium":
|
||||
btn.click()
|
||||
elif method == "js_events":
|
||||
self.driver.execute_script("""
|
||||
var el = arguments[0];
|
||||
el.dispatchEvent(new PointerEvent('pointerover', {bubbles:true, cancelable:true, composed:true}));
|
||||
el.dispatchEvent(new PointerEvent('pointerdown', {bubbles:true, cancelable:true, composed:true}));
|
||||
el.dispatchEvent(new PointerEvent('pointerup', {bubbles:true, cancelable:true, composed:true}));
|
||||
el.dispatchEvent(new MouseEvent('click', {bubbles:true, cancelable:true, composed:true}));
|
||||
""", btn)
|
||||
else:
|
||||
self.driver.execute_script("arguments[0].click();", btn)
|
||||
|
||||
# Switch to new tab if one opened
|
||||
handles_after = set(self.driver.window_handles)
|
||||
new_handles = handles_after - handles_before
|
||||
if new_handles:
|
||||
self.driver.switch_to.window(new_handles.pop())
|
||||
print(f"[DDMA Claim step3] Switched to new tab")
|
||||
print(f"[DDMA Claim step3] Clicked via {method} (attempt {attempt+1})")
|
||||
time.sleep(2)
|
||||
|
||||
print(f"[DDMA Claim step3] Post-click URL: {self.driver.current_url}")
|
||||
# Check if navigation happened (URL changed or new tab opened)
|
||||
handles_after = set(self.driver.window_handles)
|
||||
new_handles = handles_after - handles_before
|
||||
if new_handles:
|
||||
self.driver.switch_to.window(new_handles.pop())
|
||||
print(f"[DDMA Claim step3] Switched to new tab: {self.driver.current_url}")
|
||||
clicked = True
|
||||
break
|
||||
if self.driver.current_url != url_before:
|
||||
print(f"[DDMA Claim step3] URL changed to: {self.driver.current_url}")
|
||||
clicked = True
|
||||
break
|
||||
print(f"[DDMA Claim step3] URL unchanged after {method}, retrying...")
|
||||
except Exception as e:
|
||||
print(f"[DDMA Claim step3] {method} click failed: {e}, retrying...")
|
||||
|
||||
# Wait for claim form — just log, don't fail
|
||||
if not clicked:
|
||||
page_text = self.driver.execute_script("return document.body.innerText;")[:300]
|
||||
print(f"[DDMA Claim step3] No navigation after all click attempts — page: {page_text}")
|
||||
return "ERROR: step3 failed: button clicked but no navigation occurred"
|
||||
|
||||
# Wait for claim form to load
|
||||
try:
|
||||
WebDriverWait(self.driver, 20).until(
|
||||
EC.any_of(
|
||||
@@ -457,7 +507,7 @@ class AutomationDDMAClaimSubmit:
|
||||
)),
|
||||
)
|
||||
)
|
||||
print(f"[DDMA Claim step3] Claim form loaded")
|
||||
print(f"[DDMA Claim step3] Claim form loaded: {self.driver.current_url}")
|
||||
except TimeoutException:
|
||||
page_text = self.driver.execute_script("return document.body.innerText;")[:400]
|
||||
print(f"[DDMA Claim step3] Claim form not detected — page: {page_text}")
|
||||
@@ -629,7 +679,18 @@ class AutomationDDMAClaimSubmit:
|
||||
proc_inp = proc_inputs[idx] if idx < len(proc_inputs) else proc_inputs[-1]
|
||||
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", proc_inp)
|
||||
self._fill_combobox(proc_inp, code, f"procedureCode[{idx}]")
|
||||
time.sleep(0.5)
|
||||
# Wait for the tooth input on the second row to be present AND interactable
|
||||
if tooth:
|
||||
try:
|
||||
WebDriverWait(self.driver, 8).until(
|
||||
lambda d: (
|
||||
len(d.find_elements(By.XPATH, "//input[@aria-label='Tooth']")) > idx and
|
||||
d.find_elements(By.XPATH, "//input[@aria-label='Tooth']")[idx].is_enabled() and
|
||||
d.find_elements(By.XPATH, "//input[@aria-label='Tooth']")[idx].is_displayed()
|
||||
)
|
||||
)
|
||||
except Exception:
|
||||
time.sleep(1) # fallback: just wait a second
|
||||
except Exception as e:
|
||||
print(f"[DDMA Claim step4] Could not fill procedure code: {e}")
|
||||
|
||||
@@ -638,7 +699,31 @@ class AutomationDDMAClaimSubmit:
|
||||
try:
|
||||
tooth_inputs = self.driver.find_elements(By.XPATH, "//input[@aria-label='Tooth']")
|
||||
if idx < len(tooth_inputs):
|
||||
self._fill_combobox(tooth_inputs[idx], tooth, f"tooth[{idx}]")
|
||||
ti = tooth_inputs[idx]
|
||||
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", ti)
|
||||
time.sleep(0.3)
|
||||
# Open the dropdown via JS focus (avoids element-not-interactable on click)
|
||||
self.driver.execute_script("arguments[0].focus();", ti)
|
||||
time.sleep(0.5)
|
||||
# Select the option matching the tooth number from the 1-32 dropdown
|
||||
listbox_id = ti.get_attribute("aria-controls") or ""
|
||||
try:
|
||||
if listbox_id:
|
||||
option = WebDriverWait(self.driver, 5).until(
|
||||
EC.element_to_be_clickable((By.XPATH,
|
||||
f"//*[@id='{listbox_id}']//*[@role='option' and normalize-space(.)='{tooth}']"
|
||||
))
|
||||
)
|
||||
else:
|
||||
option = WebDriverWait(self.driver, 5).until(
|
||||
EC.element_to_be_clickable((By.XPATH,
|
||||
f"//*[@role='listbox']//*[@role='option' and normalize-space(.)='{tooth}']"
|
||||
))
|
||||
)
|
||||
option.click()
|
||||
print(f"[DDMA Claim step4] tooth[{idx}]: selected '{tooth}' from dropdown")
|
||||
except TimeoutException:
|
||||
print(f"[DDMA Claim step4] tooth[{idx}]: dropdown not found, skipping")
|
||||
time.sleep(0.3)
|
||||
except Exception as e:
|
||||
print(f"[DDMA Claim step4] Could not fill tooth: {e}")
|
||||
@@ -663,7 +748,7 @@ class AutomationDDMAClaimSubmit:
|
||||
except Exception as e:
|
||||
print(f"[DDMA Claim step4] Could not fill quad: {e}")
|
||||
|
||||
# ── Surface (free-text combobox — type directly) ──────────────
|
||||
# ── Surface (type directly then Tab to billed amount) ─────────
|
||||
if surface:
|
||||
try:
|
||||
surface_inputs = self.driver.find_elements(By.XPATH, "//input[@aria-label='Surface']")
|
||||
@@ -673,9 +758,6 @@ class AutomationDDMAClaimSubmit:
|
||||
surf_inp.send_keys(Keys.CONTROL + "a")
|
||||
surf_inp.send_keys(Keys.DELETE)
|
||||
surf_inp.send_keys(surface)
|
||||
# Dismiss any listbox with Escape so it doesn't block next field
|
||||
time.sleep(0.3)
|
||||
surf_inp.send_keys(Keys.ESCAPE)
|
||||
print(f"[DDMA Claim step4] surface[{idx}]: typed '{surface}'")
|
||||
time.sleep(0.2)
|
||||
except Exception as e:
|
||||
@@ -812,13 +894,22 @@ class AutomationDDMAClaimSubmit:
|
||||
time.sleep(0.5)
|
||||
|
||||
# Click Submit claim button
|
||||
submit_btn = WebDriverWait(self.driver, 10).until(
|
||||
EC.element_to_be_clickable((By.XPATH,
|
||||
"//button[.//span[contains(text(),'Submit claim')]] | "
|
||||
"//button[contains(normalize-space(text()),'Submit claim')] | "
|
||||
"//button[@aria-label='Submit claim']"
|
||||
))
|
||||
)
|
||||
submit_btn = None
|
||||
for xpath in [
|
||||
"//button[.//span[contains(text(),'Submit claim')]]",
|
||||
"//button[contains(normalize-space(text()),'Submit claim')]",
|
||||
"//button[@aria-label='Submit claim']",
|
||||
]:
|
||||
try:
|
||||
submit_btn = WebDriverWait(self.driver, 8).until(
|
||||
EC.element_to_be_clickable((By.XPATH, xpath))
|
||||
)
|
||||
if submit_btn:
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
if not submit_btn:
|
||||
return "ERROR: step7 failed: Submit claim button not found"
|
||||
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", submit_btn)
|
||||
time.sleep(0.3)
|
||||
self.driver.execute_script("""
|
||||
|
||||
Reference in New Issue
Block a user