fix: update DDMA eligibility check worker for new deltadentalma.com sign-in and search flow
Delta Dental MA moved their provider login from providers.deltadentalma.com/onboarding to www.deltadentalma.com/sign-in with new form field selectors, and moved eligibility search behind a "Group/Health Connector" search page keyed by subscriber ID + birthdate instead of the old member-search form. Updates login, OTP/MFA handling, and member search/selection across the eligibility and claim DDMA workers and their session helpers.
This commit is contained in:
@@ -10,6 +10,7 @@ import time
|
||||
import os
|
||||
import re
|
||||
import base64
|
||||
import calendar
|
||||
|
||||
from ddma_browser_manager import get_browser_manager
|
||||
|
||||
@@ -45,7 +46,7 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
||||
|
||||
# Try to click logout button if visible
|
||||
try:
|
||||
self.driver.get("https://providers.deltadentalma.com/")
|
||||
self.driver.get("https://www.deltadentalma.com/")
|
||||
time.sleep(2)
|
||||
|
||||
logout_selectors = [
|
||||
@@ -99,27 +100,27 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
||||
current_url = self.driver.current_url
|
||||
print(f"[login] Current URL: {current_url}")
|
||||
|
||||
logged_in_patterns = ["member", "dashboard", "eligibility", "search", "patients"]
|
||||
logged_in_patterns = ["member", "dashboard", "eligibility", "search", "patients", "private/provider"]
|
||||
is_logged_in_url = any(pattern in current_url.lower() for pattern in logged_in_patterns)
|
||||
|
||||
if is_logged_in_url and "onboarding" not in current_url.lower():
|
||||
if is_logged_in_url and "sign-in" not in current_url.lower():
|
||||
print("[login] Already on logged-in page - skipping login entirely")
|
||||
if "member" not in current_url.lower():
|
||||
try:
|
||||
member_search = WebDriverWait(self.driver, 5).until(
|
||||
EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]'))
|
||||
EC.presence_of_element_located((By.XPATH, "//a[contains(@href, 'check-patient-benefits-search')]"))
|
||||
)
|
||||
print("[login] Found member search input - returning ALREADY_LOGGED_IN")
|
||||
return "ALREADY_LOGGED_IN"
|
||||
except TimeoutException:
|
||||
members_url = "https://providers.deltadentalma.com/members"
|
||||
members_url = "https://www.deltadentalma.com/private/provider/dashboard"
|
||||
print(f"[login] Navigating to members page: {members_url}")
|
||||
self.driver.get(members_url)
|
||||
time.sleep(2)
|
||||
|
||||
try:
|
||||
member_search = WebDriverWait(self.driver, 5).until(
|
||||
EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]'))
|
||||
EC.presence_of_element_located((By.XPATH, "//a[contains(@href, 'check-patient-benefits-search')]"))
|
||||
)
|
||||
print("[login] Member search found - ALREADY_LOGGED_IN")
|
||||
return "ALREADY_LOGGED_IN"
|
||||
@@ -137,9 +138,9 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
||||
current_url = self.driver.current_url
|
||||
print(f"[login] URL after navigation: {current_url}")
|
||||
|
||||
if "onboarding" not in current_url.lower():
|
||||
if "sign-in" not in current_url.lower():
|
||||
member_search = WebDriverWait(self.driver, 3).until(
|
||||
EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]'))
|
||||
EC.presence_of_element_located((By.XPATH, "//a[contains(@href, 'check-patient-benefits-search')]"))
|
||||
)
|
||||
if member_search:
|
||||
print("[login] Session valid - skipping login")
|
||||
@@ -172,7 +173,7 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
||||
try:
|
||||
otp_candidate = WebDriverWait(self.driver, 10).until(
|
||||
EC.presence_of_element_located(
|
||||
(By.XPATH, "//input[contains(@aria-lable,'Verification code') or contains(@placeholder,'Enter your verification code') or contains(@aria-label,'Verification code')]")
|
||||
(By.XPATH, "//input[@data-test-id='MfaCode']")
|
||||
)
|
||||
)
|
||||
if otp_candidate:
|
||||
@@ -189,7 +190,7 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
||||
time.sleep(2)
|
||||
try:
|
||||
member_search = WebDriverWait(self.driver, 5).until(
|
||||
EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]'))
|
||||
EC.presence_of_element_located((By.XPATH, "//a[contains(@href, 'check-patient-benefits-search')]"))
|
||||
)
|
||||
if member_search:
|
||||
print("[login] Already authenticated after modal dismiss")
|
||||
@@ -200,30 +201,21 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
||||
# Fill login form
|
||||
try:
|
||||
email_field = WebDriverWait(self.driver, 10).until(
|
||||
EC.element_to_be_clickable((By.XPATH, "//input[@name='username' and @type='text']"))
|
||||
EC.element_to_be_clickable((By.XPATH, "//input[@data-test-id='Username']"))
|
||||
)
|
||||
except TimeoutException:
|
||||
print("[login] Could not find login form - page may have changed")
|
||||
return "ERROR: Login form not found"
|
||||
|
||||
email_field = wait.until(EC.presence_of_element_located((By.XPATH, "//input[@name='username' and @type='text']")))
|
||||
email_field = wait.until(EC.presence_of_element_located((By.XPATH, "//input[@data-test-id='Username']")))
|
||||
email_field.clear()
|
||||
email_field.send_keys(self.massddma_username)
|
||||
|
||||
password_field = wait.until(EC.presence_of_element_located((By.XPATH, "//input[@name='password' and @type='password']")))
|
||||
password_field = wait.until(EC.presence_of_element_located((By.XPATH, "//input[@data-test-id='Password']")))
|
||||
password_field.clear()
|
||||
password_field.send_keys(self.massddma_password)
|
||||
|
||||
# Remember me
|
||||
try:
|
||||
remember_me_checkbox = wait.until(EC.element_to_be_clickable(
|
||||
(By.XPATH, "//label[.//span[contains(text(),'Remember me')]]")
|
||||
))
|
||||
remember_me_checkbox.click()
|
||||
except Exception:
|
||||
print("[login] Remember me checkbox not found (continuing).")
|
||||
|
||||
login_button = wait.until(EC.element_to_be_clickable((By.XPATH, "//button[@type='submit' and @aria-label='Sign in']")))
|
||||
login_button = wait.until(EC.element_to_be_clickable((By.XPATH, "//button[@data-test-id='SigninSubmitButton']")))
|
||||
login_button.click()
|
||||
|
||||
# Save credentials hash after login attempt
|
||||
@@ -234,7 +226,7 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
||||
try:
|
||||
otp_candidate = WebDriverWait(self.driver, 30).until(
|
||||
EC.presence_of_element_located(
|
||||
(By.XPATH, "//input[contains(@aria-lable,'Verification code') or contains(@placeholder,'Enter your verification code')]")
|
||||
(By.XPATH, "//input[@data-test-id='MfaCode']")
|
||||
)
|
||||
)
|
||||
if otp_candidate:
|
||||
@@ -247,7 +239,7 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
||||
current_url = self.driver.current_url.lower()
|
||||
if "member" in current_url or "dashboard" in current_url:
|
||||
member_search = WebDriverWait(self.driver, 5).until(
|
||||
EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]'))
|
||||
EC.presence_of_element_located((By.XPATH, "//a[contains(@href, 'check-patient-benefits-search')]"))
|
||||
)
|
||||
print("[login] Login successful - now on member search page")
|
||||
return "SUCCESS"
|
||||
@@ -264,7 +256,7 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
||||
except TimeoutException:
|
||||
pass
|
||||
|
||||
if "onboarding" in self.driver.current_url.lower() or "login" in self.driver.current_url.lower():
|
||||
if "sign-in" in self.driver.current_url.lower():
|
||||
print("[login] Login failed - still on login page")
|
||||
return "ERROR:LOGIN FAILED: Still on login page"
|
||||
|
||||
@@ -275,103 +267,66 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
||||
print("[login] Exception during login:", e)
|
||||
return f"ERROR:LOGIN FAILED: {e}"
|
||||
|
||||
def _select_provider_dropdown(self):
|
||||
"""
|
||||
Click the provider dropdown on the member search page and select the option
|
||||
matching self.providerName (case-insensitive). Falls back to the first option.
|
||||
The button data-testid is 'member-search_provider_select-btn'.
|
||||
"""
|
||||
try:
|
||||
short_wait = WebDriverWait(self.driver, 5)
|
||||
try:
|
||||
provider_btn = short_wait.until(
|
||||
EC.element_to_be_clickable(
|
||||
(By.XPATH, '//button[@data-testid="member-search_provider_select-btn"]')
|
||||
)
|
||||
)
|
||||
except TimeoutException:
|
||||
print("[DDMA step1] No provider dropdown found — skipping")
|
||||
return
|
||||
|
||||
provider_btn.click()
|
||||
time.sleep(0.5)
|
||||
|
||||
try:
|
||||
WebDriverWait(self.driver, 5).until(
|
||||
EC.presence_of_element_located((By.XPATH, "//*[@role='option']"))
|
||||
)
|
||||
except TimeoutException:
|
||||
print("[DDMA step1] Provider listbox did not open")
|
||||
return
|
||||
|
||||
options = self.driver.find_elements(By.XPATH, "//*[@role='option']")
|
||||
print(f"[DDMA step1] Provider options: {[o.text.strip() for o in options]}")
|
||||
|
||||
target = (self.providerName or "").strip().lower()
|
||||
selected = False
|
||||
|
||||
if target:
|
||||
for opt in options:
|
||||
if target in opt.text.lower():
|
||||
opt.click()
|
||||
print(f"[DDMA step1] Selected provider: '{opt.text.strip()}'")
|
||||
selected = True
|
||||
break
|
||||
|
||||
if not selected and options:
|
||||
options[0].click()
|
||||
print(f"[DDMA step1] No match for '{self.providerName}', selected first: '{options[0].text.strip()}'")
|
||||
|
||||
time.sleep(0.3)
|
||||
|
||||
except Exception as e:
|
||||
print(f"[DDMA step1] Provider selection error (non-fatal): {e}")
|
||||
|
||||
def step1(self):
|
||||
"""Fill search form with all available fields (flexible search)."""
|
||||
"""Open the Group/Health Connector search page and search by Subscriber ID + Birthdate."""
|
||||
wait = WebDriverWait(self.driver, 30)
|
||||
|
||||
try:
|
||||
fields = []
|
||||
if self.memberId:
|
||||
fields.append(f"ID: {self.memberId}")
|
||||
if self.firstName:
|
||||
fields.append(f"FirstName: {self.firstName}")
|
||||
if self.lastName:
|
||||
fields.append(f"LastName: {self.lastName}")
|
||||
fields.append(f"SubscriberID: {self.memberId}")
|
||||
if self.dateOfBirth:
|
||||
fields.append(f"DOB: {self.dateOfBirth}")
|
||||
print(f"[DDMA step1] Starting search with: {', '.join(fields)}")
|
||||
|
||||
# Wait for the search page to be fully loaded before interacting
|
||||
wait.until(EC.presence_of_element_located(
|
||||
(By.XPATH, '//input[@placeholder="Search by member ID"]')
|
||||
))
|
||||
time.sleep(1)
|
||||
search_page_url = "https://www.deltadentalma.com/private/provider/check-patient-benefits-search"
|
||||
|
||||
# Select provider from dropdown based on NPI settings
|
||||
self._select_provider_dropdown()
|
||||
# Try navigating straight to the search page first — avoids any click/visibility issues
|
||||
print(f"[DDMA step1] Navigating directly to search page: {search_page_url}")
|
||||
self.driver.get(search_page_url)
|
||||
time.sleep(2)
|
||||
|
||||
def replace_with_sendkeys(el, value):
|
||||
el.click()
|
||||
el.send_keys(Keys.CONTROL, "a")
|
||||
el.send_keys(Keys.BACKSPACE)
|
||||
el.send_keys(value)
|
||||
if "check-patient-benefits-search" not in self.driver.current_url.lower():
|
||||
# Direct deep-link didn't stick (e.g. bounced back to sign-in/dashboard) —
|
||||
# fall back to clicking the Group/Health Connector link from the dashboard.
|
||||
print(f"[DDMA step1] Direct navigation redirected to: {self.driver.current_url} — falling back to clicking the link")
|
||||
|
||||
# 1. Fill Member ID if provided
|
||||
if self.memberId:
|
||||
if "private/provider/dashboard" not in self.driver.current_url.lower():
|
||||
dashboard_url = "https://www.deltadentalma.com/private/provider/dashboard"
|
||||
print(f"[DDMA step1] Navigating to dashboard: {dashboard_url}")
|
||||
self.driver.get(dashboard_url)
|
||||
time.sleep(2)
|
||||
|
||||
link_xpath = "//a[contains(@href, 'check-patient-benefits-search')]"
|
||||
try:
|
||||
member_id_input = wait.until(EC.presence_of_element_located(
|
||||
(By.XPATH, '//input[@placeholder="Search by member ID"]')
|
||||
))
|
||||
member_id_input.clear()
|
||||
member_id_input.send_keys(self.memberId)
|
||||
print(f"[DDMA step1] Entered Member ID: {self.memberId}")
|
||||
time.sleep(0.2)
|
||||
except Exception as e:
|
||||
print(f"[DDMA step1] Warning: Could not fill Member ID: {e}")
|
||||
connector_link = WebDriverWait(self.driver, 20).until(
|
||||
EC.element_to_be_clickable((By.XPATH, link_xpath))
|
||||
)
|
||||
connector_link.click()
|
||||
except TimeoutException:
|
||||
# Element may exist but be hidden (e.g. a responsive duplicate) — try any match via JS click
|
||||
candidates = self.driver.find_elements(By.XPATH, link_xpath)
|
||||
print(f"[DDMA step1] element_to_be_clickable timed out; found {len(candidates)} matching link(s) in DOM")
|
||||
if candidates:
|
||||
try:
|
||||
self.driver.execute_script("arguments[0].click();", candidates[0])
|
||||
except Exception as js_err:
|
||||
print(f"[DDMA step1] JS click failed: {js_err}")
|
||||
print(f"[DDMA step1] Current URL: {self.driver.current_url}")
|
||||
return "ERROR: Could not open Group/Health Connector search page"
|
||||
else:
|
||||
print(f"[DDMA step1] No Group/Health Connector link found in DOM. Current URL: {self.driver.current_url}")
|
||||
return "ERROR: Could not open Group/Health Connector search page"
|
||||
|
||||
# 2. Fill DOB if provided
|
||||
try:
|
||||
wait.until(lambda d: "check-patient-benefits-search" in d.current_url)
|
||||
except TimeoutException:
|
||||
print(f"[DDMA step1] Click did not navigate to search page. Current URL: {self.driver.current_url}")
|
||||
return "ERROR: Could not open Group/Health Connector search page"
|
||||
|
||||
print(f"[DDMA step1] On Group/Health Connector search page: {self.driver.current_url}")
|
||||
|
||||
# Fill Birthdate (masked mm/dd/yyyy input — send digits only, mask inserts slashes)
|
||||
if self.dateOfBirth:
|
||||
try:
|
||||
dob_parts = self.dateOfBirth.split("-")
|
||||
@@ -379,54 +334,47 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
||||
month = dob_parts[1].zfill(2)
|
||||
day = dob_parts[2].zfill(2)
|
||||
|
||||
dob_container = wait.until(
|
||||
EC.presence_of_element_located(
|
||||
(By.XPATH, "//div[@data-testid='member-search_date-of-birth']")
|
||||
)
|
||||
)
|
||||
|
||||
month_elem = dob_container.find_element(By.XPATH, ".//span[@data-type='month' and @contenteditable='true']")
|
||||
day_elem = dob_container.find_element(By.XPATH, ".//span[@data-type='day' and @contenteditable='true']")
|
||||
year_elem = dob_container.find_element(By.XPATH, ".//span[@data-type='year' and @contenteditable='true']")
|
||||
|
||||
replace_with_sendkeys(month_elem, month)
|
||||
time.sleep(0.05)
|
||||
replace_with_sendkeys(day_elem, day)
|
||||
time.sleep(0.05)
|
||||
replace_with_sendkeys(year_elem, year)
|
||||
print(f"[DDMA step1] Filled DOB: {month}/{day}/{year}")
|
||||
birthdate_input = wait.until(EC.element_to_be_clickable(
|
||||
(By.XPATH, "//input[@data-test-id='InStateSearchBirthdate']")
|
||||
))
|
||||
birthdate_input.click()
|
||||
birthdate_input.send_keys(Keys.CONTROL, "a")
|
||||
birthdate_input.send_keys(Keys.BACKSPACE)
|
||||
birthdate_input.send_keys(f"{month}{day}{year}")
|
||||
print(f"[DDMA step1] Filled Birthdate: {month}/{day}/{year}")
|
||||
except Exception as e:
|
||||
print(f"[DDMA step1] Warning: Could not fill DOB: {e}")
|
||||
print(f"[DDMA step1] Warning: Could not fill Birthdate: {e}")
|
||||
|
||||
# Fill Subscriber ID (== member ID)
|
||||
if self.memberId:
|
||||
try:
|
||||
subscriber_input = wait.until(EC.element_to_be_clickable(
|
||||
(By.XPATH, "//input[@data-test-id='InStateSearchSubscriberId']")
|
||||
))
|
||||
subscriber_input.clear()
|
||||
subscriber_input.send_keys(self.memberId)
|
||||
print(f"[DDMA step1] Filled Subscriber ID: {self.memberId}")
|
||||
except Exception as e:
|
||||
print(f"[DDMA step1] Warning: Could not fill Subscriber ID: {e}")
|
||||
|
||||
time.sleep(0.3)
|
||||
|
||||
# Click Search button
|
||||
continue_btn = wait.until(EC.element_to_be_clickable(
|
||||
(By.XPATH, '//button[@data-testid="member-search_search-button"]')
|
||||
search_btn = wait.until(EC.element_to_be_clickable(
|
||||
(By.XPATH, "//button[@data-test-id='InStateSearchButton']")
|
||||
))
|
||||
continue_btn.click()
|
||||
search_btn.click()
|
||||
print("[DDMA step1] Clicked Search button")
|
||||
|
||||
# Wait for either results row or no-results message (up to 15s)
|
||||
# Wait for the Members results table to render
|
||||
try:
|
||||
WebDriverWait(self.driver, 15).until(
|
||||
EC.any_of(
|
||||
EC.presence_of_element_located((By.XPATH, "//tbody//tr")),
|
||||
EC.presence_of_element_located((By.XPATH, '//div[@data-testid="member-search-result-no-results"]')),
|
||||
)
|
||||
EC.presence_of_element_located((By.XPATH, "//table//tbody//tr"))
|
||||
)
|
||||
time.sleep(3)
|
||||
time.sleep(2)
|
||||
except TimeoutException:
|
||||
pass # proceed and let step2 handle missing results
|
||||
|
||||
# Check for no-results error
|
||||
try:
|
||||
error_msg = self.driver.find_element(By.XPATH, '//div[@data-testid="member-search-result-no-results"]')
|
||||
if error_msg:
|
||||
print("[DDMA step1] Error: No results found")
|
||||
return "ERROR: INVALID SEARCH CRITERIA"
|
||||
except Exception:
|
||||
pass
|
||||
print("[DDMA step1] Warning: No results table appeared within timeout")
|
||||
return "ERROR: INVALID SEARCH CRITERIA"
|
||||
|
||||
print("[DDMA step1] Search completed successfully")
|
||||
return "Success"
|
||||
@@ -455,99 +403,93 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
||||
foundMemberId = self.memberId or ""
|
||||
patientName = ""
|
||||
|
||||
# Extract patient name from search results row
|
||||
try:
|
||||
first_row = self.driver.find_element(By.XPATH, "(//tbody//tr)[1]")
|
||||
row_text = first_row.text.strip()
|
||||
print(f"[DDMA step2] First row text: {row_text[:150]}...")
|
||||
# The Members table lists the whole family sharing this subscriber ID —
|
||||
# find the row matching this patient's DOB (falls back to last name, then first row).
|
||||
rows = self.driver.find_elements(By.XPATH, "//table//tbody//tr")
|
||||
print(f"[DDMA step2] Found {len(rows)} family member row(s)")
|
||||
|
||||
lines = row_text.split('\n') if row_text else []
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
if re.match(r"^[A-Za-z\s\-']{2,60}$", line):
|
||||
patientName = line
|
||||
print(f"[DDMA step2] Extracted patient name from row: '{patientName}'")
|
||||
target_row = None
|
||||
|
||||
if self.dateOfBirth and rows:
|
||||
try:
|
||||
dob_parts = self.dateOfBirth.split("-")
|
||||
dob_year, dob_month, dob_day = dob_parts[0], int(dob_parts[1]), int(dob_parts[2])
|
||||
expected_dob = f"{calendar.month_abbr[dob_month]} {dob_day}, {dob_year}".lower()
|
||||
for row in rows:
|
||||
cells = row.find_elements(By.XPATH, "./td")
|
||||
if len(cells) >= 3 and cells[2].text.strip().lower() == expected_dob:
|
||||
target_row = row
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"[DDMA step2] DOB matching error (non-fatal): {e}")
|
||||
|
||||
if target_row is None and self.lastName and rows:
|
||||
for row in rows:
|
||||
if self.lastName.strip().lower() in row.text.lower():
|
||||
target_row = row
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"[DDMA step2] Error reading first row: {e}")
|
||||
|
||||
if target_row is None and rows:
|
||||
target_row = rows[0]
|
||||
print("[DDMA step2] No DOB/name match — defaulting to first family member row")
|
||||
|
||||
if target_row is not None:
|
||||
try:
|
||||
cells = target_row.find_elements(By.XPATH, "./td")
|
||||
if cells:
|
||||
patientName = cells[0].text.strip()
|
||||
print(f"[DDMA step2] Extracted patient name from row: '{patientName}'")
|
||||
if len(cells) >= 2 and cells[1].text.strip():
|
||||
foundMemberId = cells[1].text.strip()
|
||||
if len(cells) >= 7:
|
||||
status_text = cells[6].text.strip().lower()
|
||||
if "active" in status_text:
|
||||
eligibilityText = "active"
|
||||
elif "inactive" in status_text or "termed" in status_text:
|
||||
eligibilityText = "inactive"
|
||||
else:
|
||||
eligibilityText = status_text
|
||||
print(f"[DDMA step2] Found eligibility status: {eligibilityText}")
|
||||
except Exception as e:
|
||||
print(f"[DDMA step2] Error reading matched row cells: {e}")
|
||||
|
||||
# Click into the member's detail page
|
||||
try:
|
||||
name_link = target_row.find_element(By.XPATH, ".//a")
|
||||
name_link.click()
|
||||
print(f"[DDMA step2] Clicked into detail page for: {patientName}")
|
||||
except Exception as e:
|
||||
print(f"[DDMA step2] Warning: Could not click patient name link: {e}")
|
||||
else:
|
||||
print("[DDMA step2] Warning: No family member rows found in search results")
|
||||
|
||||
# Wait for the detail page to load (URL moves away from the search page)
|
||||
try:
|
||||
short_wait = WebDriverWait(self.driver, 3)
|
||||
status_link = short_wait.until(EC.presence_of_element_located((
|
||||
By.XPATH,
|
||||
"(//tbody//tr)[1]//a[contains(@href, 'member-eligibility-search')]"
|
||||
)))
|
||||
eligibilityText = status_link.text.strip().lower()
|
||||
print(f"[DDMA step2] Found eligibility status: {eligibilityText}")
|
||||
WebDriverWait(self.driver, 15).until(
|
||||
lambda d: "check-patient-benefits-search" not in d.current_url
|
||||
)
|
||||
print(f"[DDMA step2] Navigated to detail page: {self.driver.current_url}")
|
||||
except Exception:
|
||||
try:
|
||||
alt_status = self.driver.find_element(By.XPATH, "//*[contains(text(),'Active') or contains(text(),'Inactive') or contains(text(),'Eligible')]")
|
||||
eligibilityText = alt_status.text.strip().lower()
|
||||
if "active" in eligibilityText or "eligible" in eligibilityText:
|
||||
eligibilityText = "active"
|
||||
elif "inactive" in eligibilityText:
|
||||
eligibilityText = "inactive"
|
||||
print(f"[DDMA step2] Found eligibility via alternative: {eligibilityText}")
|
||||
except Exception:
|
||||
pass
|
||||
print(f"[DDMA step2] Warning: Still on search page: {self.driver.current_url}")
|
||||
|
||||
# Find the member-details URL from the first row
|
||||
print("[DDMA step2] Looking for patient detail link...")
|
||||
detail_url = None
|
||||
|
||||
link_selectors = [
|
||||
"(//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 link_selectors:
|
||||
# Wait for meaningful content on the detail page
|
||||
for selector in [
|
||||
"//*[contains(text(),'Date of Birth') or contains(text(),'Address') or contains(text(),'Member ID')]",
|
||||
"//table", "//h1", "//h2",
|
||||
]:
|
||||
try:
|
||||
link_el = WebDriverWait(self.driver, 5).until(
|
||||
WebDriverWait(self.driver, 15).until(
|
||||
EC.presence_of_element_located((By.XPATH, selector))
|
||||
)
|
||||
href = link_el.get_attribute("href")
|
||||
if href and "member-details" in href:
|
||||
detail_url = href
|
||||
print(f"[DDMA step2] Found detail URL: {href}")
|
||||
break
|
||||
print(f"[DDMA step2] Detail page content loaded: {selector}")
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
if detail_url:
|
||||
# Always navigate via driver.get() — this blocks until the new page loads,
|
||||
# unlike click() which returns immediately and causes a race condition.
|
||||
print(f"[DDMA step2] Navigating to detail page: {detail_url}")
|
||||
self.driver.get(detail_url)
|
||||
time.sleep(3) # Let JavaScript finish rendering all sections
|
||||
|
||||
# Confirm we actually landed on the detail page (not redirected away)
|
||||
try:
|
||||
WebDriverWait(self.driver, 15).until(
|
||||
lambda d: "member-details" in d.current_url
|
||||
)
|
||||
print(f"[DDMA step2] Confirmed on detail page: {self.driver.current_url}")
|
||||
except Exception:
|
||||
print(f"[DDMA step2] Warning: URL after navigation: {self.driver.current_url}")
|
||||
|
||||
# Wait for meaningful content on the detail page
|
||||
for selector in [
|
||||
"//*[contains(text(),'Date of Birth') or contains(text(),'Address') or contains(text(),'Member ID')]",
|
||||
"//table", "//h1", "//h2",
|
||||
]:
|
||||
try:
|
||||
WebDriverWait(self.driver, 15).until(
|
||||
EC.presence_of_element_located((By.XPATH, selector))
|
||||
)
|
||||
print(f"[DDMA step2] Detail page content loaded: {selector}")
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
time.sleep(3) # Let JavaScript finish rendering all sections
|
||||
|
||||
# Get full page text as lines
|
||||
# Fallback: refine patient name from the detail page text if row extraction failed
|
||||
if not patientName:
|
||||
try:
|
||||
page_lines = self.driver.execute_script(
|
||||
"return document.body.innerText;"
|
||||
@@ -602,8 +544,6 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
||||
break
|
||||
if patientName:
|
||||
break
|
||||
else:
|
||||
print("[DDMA step2] Warning: Could not find patient detail link")
|
||||
|
||||
# Clean patient name — strip any remaining date artifacts
|
||||
if patientName:
|
||||
|
||||
Reference in New Issue
Block a user