""" Smart Wait Optimizer - World-Class Precision Optimizes wait times by using intelligent detection: 1. Password tracker - if password was reset, skip reset modal check 2. Profile completion - if profile is 100%, skip incomplete modal check 3. Fast detection using robust locators (data-testid) 4. Animation-aware waits (150ms FAST, 300ms NORMAL, 500ms SLOW) 5. Tiny padding (50-100ms) for safety """ from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from utils.password_tracker import password_tracker from config.config import TEST_NEW_PASSWORD import time class SmartWaitOptimizer: """ World-class wait optimizer that eliminates unnecessary waits. Uses intelligent detection to skip modal checks when not needed: - Password reset modal: Skip if password was already reset - Profile incomplete modal: Skip if profile is already complete """ # Animation durations from UI source code ANIMATION_FAST = 0.15 # 150ms ANIMATION_NORMAL = 0.3 # 300ms ANIMATION_SLOW = 0.5 # 500ms SAFETY_PADDING = 0.05 # 50ms safety padding # Total wait times (animation + padding) MODAL_DETECTION_TIMEOUT = ANIMATION_NORMAL + SAFETY_PADDING # 350ms QUICK_CHECK_TIMEOUT = ANIMATION_FAST + SAFETY_PADDING # 200ms @staticmethod def should_check_password_reset(cpid: str, password_used: str) -> bool: """ Determine if password reset modal check is needed. Logic: - If TEST_NEW_PASSWORD was used to login, reset modal won't appear - If password tracker shows password was reset, skip check Args: cpid: Student CPID password_used: Password that was used for login Returns: bool: True if should check for reset modal, False to skip """ # If TEST_NEW_PASSWORD was used, reset modal won't appear if password_used == TEST_NEW_PASSWORD: return False # Check password tracker - if password was reset, skip check tracked_password = password_tracker._passwords.get(cpid) if tracked_password == TEST_NEW_PASSWORD: return False # Otherwise, need to check return True @staticmethod def should_check_profile_incomplete(driver) -> bool: """ Determine if profile incomplete modal check is needed. Logic: - Check if profile completion is already 100% - If 100%, skip modal check Args: driver: WebDriver instance Returns: bool: True if should check for incomplete modal, False to skip """ try: # Quick check for profile completion indicator # Look for progress value in dashboard or profile editor from selenium.webdriver.common.by import By # Strategy 1: Check for 100% completion indicator completion_indicators = [ "100%", "Complete", "Profile Complete", "profile-complete" ] page_text = driver.page_source.lower() for indicator in completion_indicators: if indicator.lower() in page_text: # Found completion indicator - profile might be complete # But we need to verify it's actually 100% # This is a quick check, not definitive pass # Strategy 2: Check for profile incomplete modal data-testid # If modal is already visible, we need to handle it try: incomplete_modal = driver.find_elements( By.CSS_SELECTOR, "[data-testid='profile_incomplete__modal']" ) if incomplete_modal and any(m.is_displayed() for m in incomplete_modal): return True # Modal is visible, need to handle except: pass # Strategy 3: Check for profile editor page # If we're on profile editor, profile might be incomplete current_url = driver.current_url.lower() if "/profile-builder" in current_url or "/profile" in current_url: # On profile page - might be incomplete # But if we're here, modal might have already been handled return False # Skip check, we're already on profile page # Default: Check for modal (safe approach) return True except Exception as e: # On error, default to checking (safe approach) print(f"⚠️ Error checking profile completion: {e}, will check modal") return True @staticmethod def quick_check_modal_present(driver, modal_locator, timeout=None) -> bool: """ Quick check if modal is present using fast detection. Uses: - Fast animation timeout (200ms) - Robust locator (data-testid) - No unnecessary waits Args: driver: WebDriver instance modal_locator: Locator tuple (By, selector) timeout: Custom timeout (default: QUICK_CHECK_TIMEOUT) Returns: bool: True if modal is present and visible """ if timeout is None: timeout = SmartWaitOptimizer.QUICK_CHECK_TIMEOUT try: # Fast check - wait only for animation + padding element = WebDriverWait(driver, timeout).until( EC.presence_of_element_located(modal_locator) ) # Verify it's visible return element.is_displayed() except: return False @staticmethod def wait_for_modal_animation(driver, modal_locator, timeout=None) -> bool: """ Wait for modal animation to complete. Uses: - Normal animation timeout (350ms) - Waits for modal to be fully visible - Includes safety padding Args: driver: WebDriver instance modal_locator: Locator tuple (By, selector) timeout: Custom timeout (default: MODAL_DETECTION_TIMEOUT) Returns: bool: True if modal is present and visible after animation """ if timeout is None: timeout = SmartWaitOptimizer.MODAL_DETECTION_TIMEOUT try: # Wait for modal to appear and be visible element = WebDriverWait(driver, timeout).until( EC.visibility_of_element_located(modal_locator) ) return element.is_displayed() except: return False @staticmethod def smart_wait_for_dashboard(driver, cpid: str, password_used: str, max_wait: float = 5.0): """ Smart wait for dashboard to load, with intelligent modal detection. Optimizations: 1. Skip password reset check if password was already reset 2. Skip profile incomplete check if profile is complete 3. Use fast detection for modals 4. Minimal waits with animation-aware timing Args: driver: WebDriver instance cpid: Student CPID password_used: Password that was used for login max_wait: Maximum wait time in seconds (default: 5.0) """ start_time = time.time() # Wait for dashboard to load (navigation) try: WebDriverWait(driver, max_wait).until( lambda d: "/dashboard" in d.current_url or "/student" in d.current_url ) except: pass # Continue even if timeout # Quick check: Should we check for password reset modal? if SmartWaitOptimizer.should_check_password_reset(cpid, password_used): # Need to check - use fast detection from pages.mandatory_reset_page import MandatoryResetPage reset_page = MandatoryResetPage(driver) # Quick check (200ms) if SmartWaitOptimizer.quick_check_modal_present( driver, reset_page.MODAL, timeout=SmartWaitOptimizer.QUICK_CHECK_TIMEOUT ): # Modal is present - wait for animation to complete time.sleep(SmartWaitOptimizer.ANIMATION_NORMAL + SmartWaitOptimizer.SAFETY_PADDING) else: # Skip check - password was already reset print(f"⚡ Skipping password reset check (password already reset for {cpid})") # Quick check: Should we check for profile incomplete modal? if SmartWaitOptimizer.should_check_profile_incomplete(driver): # Need to check - use fast detection from pages.profile_incomplete_page import ProfileIncompletePage profile_incomplete = ProfileIncompletePage(driver) # Quick check (200ms) if SmartWaitOptimizer.quick_check_modal_present( driver, profile_incomplete.MODAL, timeout=SmartWaitOptimizer.QUICK_CHECK_TIMEOUT ): # Modal is present - wait for animation to complete time.sleep(SmartWaitOptimizer.ANIMATION_NORMAL + SmartWaitOptimizer.SAFETY_PADDING) else: # Skip check - profile is complete print(f"⚡ Skipping profile incomplete check (profile already complete)") elapsed = time.time() - start_time print(f"⚡ Smart dashboard wait completed in {elapsed:.3f}s")