#!/usr/bin/env python3 """ Comprehensive DOM Verification Script Systematically verifies ALL data-testid attributes by: 1. Logging in 2. Navigating through each tab of profile editor 3. Waiting for elements to load 4. Scrolling to make elements visible 5. Checking all attributes in each step 6. Reporting detailed findings """ import sys from pathlib import Path import json import time project_root = Path(__file__).parent.parent sys.path.insert(0, str(project_root)) from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.action_chains import ActionChains from pages.login_page import LoginPage from pages.mandatory_reset_page import MandatoryResetPage from pages.profile_incomplete_page import ProfileIncompletePage from pages.profile_editor_page import ProfileEditorPage from utils.driver_manager import DriverManager from config.config import TEST_NEW_PASSWORD, BASE_URL def get_all_data_testids(driver, scroll_into_view=True): """Get all data-testid attributes from DOM, optionally scrolling elements into view""" elements = driver.find_elements(By.CSS_SELECTOR, "[data-testid]") testids = {} for elem in elements: try: testid = elem.get_attribute("data-testid") if testid: # Scroll into view if requested if scroll_into_view: try: driver.execute_script("arguments[0].scrollIntoView({block: 'center', behavior: 'smooth'});", elem) time.sleep(0.1) # Brief wait for scroll except: pass testids[testid] = { "tag": elem.tag_name, "type": elem.get_attribute("type") or elem.tag_name, "visible": elem.is_displayed(), "enabled": elem.is_enabled() if hasattr(elem, 'is_enabled') else True, "text": elem.text[:50] if elem.text else "", "value": elem.get_attribute("value")[:50] if elem.get_attribute("value") else "" } except Exception as e: # Element might have been removed from DOM continue return testids def wait_for_tab_to_load(driver, tab_name, timeout=10): """Wait for a specific tab to be fully loaded""" try: # Wait for tab to be active/visible tab_locator = (By.CSS_SELECTOR, f"[data-testid='profile_editor__tab_{tab_name}']") WebDriverWait(driver, timeout).until( EC.presence_of_element_located(tab_locator) ) # Wait a bit more for content to render time.sleep(1) # Scroll to top of form driver.execute_script("window.scrollTo(0, 0);") time.sleep(0.5) return True except Exception as e: print(f"⚠️ Warning: Could not verify tab {tab_name} loaded: {e}") return False def navigate_to_tab_and_check(driver, tab_index, tab_name, expected_attributes): """Navigate to a specific tab and check for expected attributes""" print(f"\n{'='*80}") print(f"TAB {tab_index + 1}: {tab_name.upper().replace('_', ' ')}") print(f"{'='*80}") try: # Navigate to tab profile_editor = ProfileEditorPage(driver) profile_editor.navigate_to_tab(tab_index) time.sleep(2) # Wait for tab content to load # Scroll through the tab to trigger lazy loading driver.execute_script("window.scrollTo(0, 0);") time.sleep(0.5) driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") time.sleep(1) driver.execute_script("window.scrollTo(0, 0);") time.sleep(0.5) # Get all testids in this tab all_testids = get_all_data_testids(driver, scroll_into_view=True) # Filter for profile_editor testids profile_testids = {tid: info for tid, info in all_testids.items() if tid.startswith("profile_editor__")} print(f"πŸ“‹ Found {len(profile_testids)} profile_editor attributes in this tab") # Check expected attributes found = [] missing = [] for attr in expected_attributes: if attr in profile_testids: info = profile_testids[attr] status = "βœ…" if info["visible"] else "⚠️ (hidden)" print(f"{status} {attr}") found.append(attr) else: print(f"❌ MISSING: {attr}") missing.append(attr) # Check for unexpected attributes unexpected = [tid for tid in profile_testids if tid not in expected_attributes] if unexpected: print(f"\n⚠️ UNEXPECTED attributes found ({len(unexpected)}):") for tid in sorted(unexpected)[:10]: print(f" - {tid}") if len(unexpected) > 10: print(f" ... and {len(unexpected) - 10} more") return { "tab_name": tab_name, "found": found, "missing": missing, "unexpected": unexpected, "all_testids": list(profile_testids.keys()) } except Exception as e: print(f"❌ Error checking tab {tab_name}: {e}") import traceback traceback.print_exc() return { "tab_name": tab_name, "found": [], "missing": expected_attributes, "unexpected": [], "all_testids": [], "error": str(e) } def check_dynamic_checkboxes(driver, pattern_prefix, expected_count=None): """Check for dynamic checkboxes matching a pattern""" try: # Scroll through page to load all checkboxes driver.execute_script("window.scrollTo(0, 0);") time.sleep(0.3) # Find all checkboxes with matching pattern checkboxes = driver.find_elements( By.CSS_SELECTOR, f"input[type='checkbox'][data-testid^='{pattern_prefix}']" ) found_testids = [] for cb in checkboxes: try: # Scroll checkbox into view driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", cb) time.sleep(0.1) testid = cb.get_attribute("data-testid") if testid and testid.startswith(pattern_prefix): found_testids.append(testid) except: continue # Also try finding by CSS selector with wildcard all_elements = driver.find_elements(By.CSS_SELECTOR, "[data-testid]") for elem in all_elements: testid = elem.get_attribute("data-testid") if testid and testid.startswith(pattern_prefix) and testid not in found_testids: found_testids.append(testid) found_count = len(found_testids) status = "βœ…" if found_count > 0 else "❌" if expected_count: status = "βœ…" if found_count >= expected_count else "⚠️" print(f"{status} {pattern_prefix}: {found_count}/{expected_count} found") else: print(f"{status} {pattern_prefix}: {found_count} found") if found_testids: for tid in sorted(found_testids)[:5]: print(f" - {tid}") if len(found_testids) > 5: print(f" ... and {len(found_testids) - 5} more") return found_testids except Exception as e: print(f"❌ Error checking {pattern_prefix}: {e}") return [] def main(): driver = None try: print("πŸš€ COMPREHENSIVE DOM VERIFICATION") print("="*80) print(f"🌐 Environment: {BASE_URL}") print(f"πŸ”‘ Credentials: BATBAT311A") print("="*80) # Initialize driver driver_manager = DriverManager() driver = driver_manager.get_driver(headless=False) driver.maximize_window() results = { "login": {}, "dashboard": {}, "student_nav": {}, "password_reset": {}, "profile_editor": { "tabs": {}, "dynamic_checkboxes": {}, "summary": {} } } # ============================================================ # STEP 1: LOGIN # ============================================================ print("\n" + "="*80) print("STEP 1: LOGIN") print("="*80) login_page = LoginPage(driver) login_page.login(identifier="BATBAT311A", password="17k*o@yReCUA") time.sleep(3) # Check login page attributes (if still on login) if "/login" in driver.current_url or driver.current_url.rstrip("/") == BASE_URL.rstrip("/"): login_testids = get_all_data_testids(driver) login_expected = [ "student_login__form", "student_login__identifier_input", "student_login__password_input", "student_login__remember_checkbox", "student_login__error_banner", "student_login__submit_button" ] found = [tid for tid in login_expected if tid in login_testids] print(f"πŸ“Š Login Page: {len(found)}/{len(login_expected)} found") results["login"] = {"found": found, "missing": [tid for tid in login_expected if tid not in found]} else: print("βœ… Login successful - redirected to dashboard") results["login"] = {"status": "success"} # ============================================================ # STEP 2: PASSWORD RESET (if needed) # ============================================================ print("\n" + "="*80) print("STEP 2: PASSWORD RESET CHECK") print("="*80) reset_page = MandatoryResetPage(driver) if reset_page.is_modal_present(): print("πŸ”„ Password reset modal present - resetting...") reset_page.reset_password("17k*o@yReCUA", TEST_NEW_PASSWORD, TEST_NEW_PASSWORD, "BATBAT311A") time.sleep(3) results["password_reset"] = {"status": "completed"} else: print("βœ… No password reset required") results["password_reset"] = {"status": "not_required"} # ============================================================ # STEP 3: NAVIGATE TO PROFILE EDITOR # ============================================================ print("\n" + "="*80) print("STEP 3: NAVIGATING TO PROFILE EDITOR") print("="*80) # Handle profile incomplete modal profile_incomplete = ProfileIncompletePage(driver) if profile_incomplete.is_modal_present(): print("⚠️ Profile incomplete modal present - clicking Complete Profile...") profile_incomplete.click_complete() time.sleep(3) else: profile_editor = ProfileEditorPage(driver) profile_editor.navigate() time.sleep(3) # Wait for profile editor to fully load profile_editor = ProfileEditorPage(driver) profile_editor.wait_for_page_load() time.sleep(2) # ============================================================ # STEP 4: CHECK PAGE-LEVEL ATTRIBUTES # ============================================================ print("\n" + "="*80) print("STEP 4: PAGE-LEVEL ATTRIBUTES") print("="*80) all_testids = get_all_data_testids(driver) page_level_expected = [ "profile_editor__page", "profile_editor__progress_value", "profile_editor__missing_fields_toggle", "profile_editor__prev_button", "profile_editor__next_button", "profile_editor__cancel_button", "profile_editor__save_button" ] page_level_found = [] for attr in page_level_expected: if attr in all_testids: print(f"βœ… {attr}") page_level_found.append(attr) else: print(f"❌ MISSING: {attr}") print(f"\nπŸ“Š Page-Level: {len(page_level_found)}/{len(page_level_expected)} found") # ============================================================ # STEP 5: CHECK TAB NAVIGATION # ============================================================ print("\n" + "="*80) print("STEP 5: TAB NAVIGATION") print("="*80) tab_expected = [ "profile_editor__tab_personal_information", "profile_editor__tab_contact_information", "profile_editor__tab_parent_guardian", "profile_editor__tab_education_details", "profile_editor__tab_focus_areas", "profile_editor__tab_self_assessment", "profile_editor__tab_hobbies_clubs", "profile_editor__tab_achievements", "profile_editor__tab_expectations", "profile_editor__tabs_scroll_left_button", "profile_editor__tabs_scroll_right_button" ] tab_found = [] for attr in tab_expected: if attr in all_testids: print(f"βœ… {attr}") tab_found.append(attr) else: print(f"❌ MISSING: {attr}") print(f"\nπŸ“Š Tab Navigation: {len(tab_found)}/{len(tab_expected)} found") # ============================================================ # STEP 6: SYSTEMATICALLY CHECK EACH TAB # ============================================================ # Tab 0: Personal Information tab0_result = navigate_to_tab_and_check(driver, 0, "personal_information", [ "profile_editor__first_name_input", "profile_editor__last_name_input", "profile_editor__gender_select", "profile_editor__dob_input", "profile_editor__roll_number_input", "profile_editor__nationality_input", "profile_editor__language_input", "profile_editor__student_id_input", "profile_editor__student_cpid_input", "profile_editor__specially_abled_checkbox", "profile_editor__specially_abled_details_textarea" ]) results["profile_editor"]["tabs"]["personal_information"] = tab0_result # Tab 1: Contact Information tab1_result = navigate_to_tab_and_check(driver, 1, "contact_information", [ "profile_editor__email_input", "profile_editor__phone_input", "profile_editor__address_input", "profile_editor__city_input", "profile_editor__state_input", "profile_editor__zip_code_input", "profile_editor__native_state_input" ]) results["profile_editor"]["tabs"]["contact_information"] = tab1_result # Tab 2: Parent/Guardian tab2_result = navigate_to_tab_and_check(driver, 2, "parent_guardian", [ "profile_editor__father_full_name_input", "profile_editor__father_age_range_select", "profile_editor__father_occupation_input", "profile_editor__father_email_input", "profile_editor__mother_full_name_input", "profile_editor__mother_age_range_select", "profile_editor__mother_occupation_input", "profile_editor__mother_email_input", "profile_editor__guardian_different_checkbox", "profile_editor__guardian_full_name_input", "profile_editor__guardian_relationship_input", "profile_editor__guardian_phone_input", "profile_editor__guardian_email_input", "profile_editor__guardian_address_input" ]) results["profile_editor"]["tabs"]["parent_guardian"] = tab2_result # Tab 3: Education Details tab3_result = navigate_to_tab_and_check(driver, 3, "education_details", [ "profile_editor__full_name_input", "profile_editor__current_grade_input", "profile_editor__section_input", "profile_editor__board_stream_select" ]) results["profile_editor"]["tabs"]["education_details"] = tab3_result # Tab 4: Focus Areas (with dynamic checkboxes) print("\n" + "="*80) print("TAB 5: FOCUS AREAS") print("="*80) profile_editor.navigate_to_tab(4) time.sleep(2) # Scroll to load all checkboxes driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") time.sleep(1) driver.execute_script("window.scrollTo(0, 0);") time.sleep(1) # Check dynamic checkboxes print("\nπŸ” Checking Short-term Focus Areas...") short_term = check_dynamic_checkboxes(driver, "profile_editor__short_term_focus__", expected_count=11) print("\nπŸ” Checking Long-term Focus Areas...") long_term = check_dynamic_checkboxes(driver, "profile_editor__long_term_focus__", expected_count=11) # Check "Others" text inputs others_expected = [ "profile_editor__short_term_focus_others_text", "profile_editor__long_term_focus_others_text" ] all_testids_tab4 = get_all_data_testids(driver) for attr in others_expected: if attr in all_testids_tab4: print(f"βœ… {attr}") else: print(f"❌ MISSING: {attr}") results["profile_editor"]["tabs"]["focus_areas"] = { "short_term_focus": short_term, "long_term_focus": long_term, "others_text": [tid for tid in others_expected if tid in all_testids_tab4] } # Tab 5: Self-Assessment (with dynamic checkboxes) print("\n" + "="*80) print("TAB 6: SELF-ASSESSMENT") print("="*80) profile_editor.navigate_to_tab(5) time.sleep(2) # Scroll to load all checkboxes driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") time.sleep(1) driver.execute_script("window.scrollTo(0, 0);") time.sleep(1) print("\nπŸ” Checking Strengths...") strengths = check_dynamic_checkboxes(driver, "profile_editor__strength__", expected_count=19) print("\nπŸ” Checking Areas of Improvement...") improvements = check_dynamic_checkboxes(driver, "profile_editor__improvement__", expected_count=19) # Check "Others" text inputs others_expected = [ "profile_editor__strength_others_text", "profile_editor__improvement_others_text" ] all_testids_tab5 = get_all_data_testids(driver) for attr in others_expected: if attr in all_testids_tab5: print(f"βœ… {attr}") else: print(f"❌ MISSING: {attr}") results["profile_editor"]["tabs"]["self_assessment"] = { "strengths": strengths, "improvements": improvements, "others_text": [tid for tid in others_expected if tid in all_testids_tab5] } # Tab 6: Hobbies & Clubs (with dynamic checkboxes) print("\n" + "="*80) print("TAB 7: HOBBIES & CLUBS") print("="*80) profile_editor.navigate_to_tab(6) time.sleep(2) # Scroll to load all checkboxes driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") time.sleep(1) driver.execute_script("window.scrollTo(0, 0);") time.sleep(1) print("\nπŸ” Checking Hobbies...") hobbies = check_dynamic_checkboxes(driver, "profile_editor__hobby__", expected_count=12) print("\nπŸ” Checking Clubs...") clubs = check_dynamic_checkboxes(driver, "profile_editor__club_", expected_count=12) # Single underscore # Check "Others" text inputs others_expected = [ "profile_editor__hobby_other_text", "profile_editor__club_other_text" ] all_testids_tab6 = get_all_data_testids(driver) for attr in others_expected: if attr in all_testids_tab6: print(f"βœ… {attr}") else: print(f"❌ MISSING: {attr}") results["profile_editor"]["tabs"]["hobbies_clubs"] = { "hobbies": hobbies, "clubs": clubs, "others_text": [tid for tid in others_expected if tid in all_testids_tab6] } # Tab 7: Achievements tab7_result = navigate_to_tab_and_check(driver, 7, "achievements", [ "profile_editor__achievement_academics_textarea", "profile_editor__achievement_sports_textarea", "profile_editor__achievement_cultural_textarea", "profile_editor__achievement_trained_textarea", "profile_editor__achievement_others_textarea" ]) results["profile_editor"]["tabs"]["achievements"] = tab7_result # Tab 8: Expectations (with dynamic checkboxes) print("\n" + "="*80) print("TAB 9: EXPECTATIONS") print("="*80) profile_editor.navigate_to_tab(8) time.sleep(2) # Scroll to load all checkboxes driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") time.sleep(1) driver.execute_script("window.scrollTo(0, 0);") time.sleep(1) print("\nπŸ” Checking Expectations...") expectations = check_dynamic_checkboxes(driver, "profile_editor__expectation__", expected_count=10) # Check "Others" text input others_expected = ["profile_editor__expectation_others_text"] all_testids_tab8 = get_all_data_testids(driver) for attr in others_expected: if attr in all_testids_tab8: print(f"βœ… {attr}") else: print(f"❌ MISSING: {attr}") results["profile_editor"]["tabs"]["expectations"] = { "expectations": expectations, "others_text": [tid for tid in others_expected if tid in all_testids_tab8] } # ============================================================ # FINAL SUMMARY # ============================================================ print("\n" + "="*80) print("FINAL VERIFICATION SUMMARY") print("="*80) # Count all found attributes total_found = 0 total_expected = 0 # Page-level total_found += len(page_level_found) total_expected += len(page_level_expected) # Tab navigation total_found += len(tab_found) total_expected += len(tab_expected) # Tab-specific attributes for tab_name, tab_result in results["profile_editor"]["tabs"].items(): if "found" in tab_result: total_found += len(tab_result["found"]) total_expected += len(tab_result.get("missing", [])) + len(tab_result["found"]) if "short_term_focus" in tab_result: total_found += len(tab_result["short_term_focus"]) if "long_term_focus" in tab_result: total_found += len(tab_result["long_term_focus"]) if "strengths" in tab_result: total_found += len(tab_result["strengths"]) if "improvements" in tab_result: total_found += len(tab_result["improvements"]) if "hobbies" in tab_result: total_found += len(tab_result["hobbies"]) if "clubs" in tab_result: total_found += len(tab_result["clubs"]) if "expectations" in tab_result: total_found += len(tab_result["expectations"]) print(f"\nπŸ“Š TOTAL ATTRIBUTES FOUND: {total_found}") print(f"πŸ“Š TOTAL ATTRIBUTES EXPECTED: ~250+") print(f"πŸ“Š COVERAGE: {(total_found/250)*100:.1f}%") # Save results results["profile_editor"]["summary"] = { "total_found": total_found, "total_expected": total_expected, "page_level_found": len(page_level_found), "tab_navigation_found": len(tab_found) } results_file = project_root / "analysis" / "comprehensive_dom_verification_results.json" with open(results_file, 'w') as f: json.dump(results, f, indent=2, default=str) print(f"\nπŸ“„ Detailed results saved to: {results_file}") print("\n⏸️ Browser will remain open for 120 seconds for manual inspection...") print(" Press Ctrl+C to close early\n") time.sleep(120) except KeyboardInterrupt: print("\n\n⚠️ Verification interrupted by user") except Exception as e: print(f"\n\n❌ Error during verification: {e}") import traceback traceback.print_exc() finally: if driver: driver.quit() print("\nβœ… Browser closed") if __name__ == "__main__": main()