From 1328ac9cadb2a09e9134b714b1c1f2fdc3a4bf41 Mon Sep 17 00:00:00 2001 From: harsh patadia Date: Wed, 22 Apr 2026 15:39:57 +0530 Subject: [PATCH] fix: stacking of browser sessions and refactor login logic to decrease waiting time --- .../bank_integration/api/bank_api.py | 38 ++++++++++++++++--- .../bank_integration/api/hdfc_bank_api.py | 13 +++++-- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/bank_integration/bank_integration/api/bank_api.py b/bank_integration/bank_integration/api/bank_api.py index 2964e0f..587c7f4 100644 --- a/bank_integration/bank_integration/api/bank_api.py +++ b/bank_integration/bank_integration/api/bank_api.py @@ -52,12 +52,14 @@ def __init__( if getattr(self, "init"): self.init() - - if resume: - self.resume_session() - else: - self.login() - + try: + if resume: + self.resume_session() + else: + self.login() + except Exception as e: + self.throw("Closing browser session. Unexpected error occurred: {}".format(str(e))) + def login(self): pass @@ -306,6 +308,30 @@ def cleanup_download_dir(self, delete_dir=False): ) +class ElementVisibleByJS: + """ + Custom EC that checks actual rendered size via getBoundingClientRect() + instead of Selenium's is_displayed(), which can fail for Angular components + that use CSS opacity/transforms to show/hide elements that stay in the DOM. + Has a .locator attribute so AnyEC._found_element tracking works correctly. + """ + + def __init__(self, by, value): + self.locator = (by, value) + + def __call__(self, driver): + els = driver.find_elements(*self.locator) + if not els: + return False + visible = [ + e + for e in els + if driver.execute_script( + "var r=arguments[0].getBoundingClientRect(); return r.width>0 && r.height>0;", + e, + ) + ] + return visible[0] if visible else False class AnyEC: """Use with WebDriverWait to combine expected_conditions in an OR. diff --git a/bank_integration/bank_integration/api/hdfc_bank_api.py b/bank_integration/bank_integration/api/hdfc_bank_api.py index 5f8fe64..fa6b1c0 100644 --- a/bank_integration/bank_integration/api/hdfc_bank_api.py +++ b/bank_integration/bank_integration/api/hdfc_bank_api.py @@ -9,7 +9,11 @@ from frappe.utils import getdate, today, add_months, add_days, flt from frappe.utils.file_manager import save_file -from bank_integration.bank_integration.api.bank_api import BankAPI, AnyEC +from bank_integration.bank_integration.api.bank_api import ( + BankAPI, + AnyEC, + ElementVisibleByJS, +) # Selenium imports from selenium.webdriver.support import expected_conditions as EC @@ -75,7 +79,7 @@ def _handle_post_login_state(self): ) ), EC.visibility_of_element_located((By.ID, "proceedBtn")), - EC.visibility_of_element_located((By.ID, "mfa-get-otp-btn")), + ElementVisibleByJS(By.ID, "mfa-get-otp-btn"), EC.presence_of_element_located((By.TAG_NAME, "bb-retail-layout")), EC.visibility_of_element_located((By.NAME, "fldOldPass")), EC.visibility_of_element_located((By.NAME, "fldAnswer")), @@ -144,9 +148,12 @@ def _handle_post_login_state(self): self.handle_login_error() return - self.handle_login_error() def process_otp(self): + # here it again checks for the otp button in AnyEC because process_otp is used in two flows + # - login flow + # - payment flow + # both have different GET OTP button elements present try: self.wait_until( AnyEC(