CP_AUTOMATION/utils/smart_wait_optimizer.py
2025-12-12 19:54:54 +05:30

256 lines
9.7 KiB
Python

"""
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")