""" DOM Verification Script - Verify data-testid Implementation Purpose: Verify actual DOM implementation against UI team's claims Method: Inspect actual DOM structure at runtime Approach: World-class systematic verification with zero tolerance for assumptions """ import sys import os sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from pages.login_page import LoginPage from pages.mandatory_reset_page import MandatoryResetPage from pages.profile_editor_page import ProfileEditorPage from config.config import TEST_USERNAME, TEST_PASSWORD, BASE_URL import time import json from collections import defaultdict class DataTestIdVerifier: """World-Class DOM Verifier for data-testid Attributes""" def __init__(self, driver): self.driver = driver self.results = { 'profile_editor': { 'found': [], 'missing': [], 'expected': [], 'unexpected': [] }, 'mandatory_reset': { 'found': [], 'missing': [], 'expected': [], 'unexpected': [] } } def verify_profile_editor_attributes(self): """Verify all Profile Editor data-testid attributes""" print("\n" + "="*80) print("πŸ” VERIFYING: Profile Editor Attributes") print("="*80) # First, login to ensure we have access print("\nπŸ“‹ Logging in first...") self.driver.get(BASE_URL + "/login") time.sleep(2) login_page = LoginPage(self.driver) login_page.login(identifier=TEST_USERNAME, password=TEST_PASSWORD) time.sleep(3) # Check for password reset modal and handle it reset_page = MandatoryResetPage(self.driver) if reset_page.is_modal_present(): print("⚠️ Password reset modal detected - handling it...") try: reset_page.click_continue() time.sleep(2) # Use old password for reset reset_page.reset_password( current_password=TEST_PASSWORD, new_password=TEST_PASSWORD # Keep same for now ) time.sleep(3) except: print(" ⚠️ Could not handle password reset - continuing anyway") # Navigate to profile editor print("\nπŸ“‹ Navigating to Profile Editor...") self.driver.get(BASE_URL + "/student/profile-builder") time.sleep(5) # Give more time for page load # Check if we're actually on the profile editor page current_url = self.driver.current_url print(f" Current URL: {current_url}") if "profile" not in current_url.lower(): print(" ⚠️ May not be on profile editor page - checking page content...") page_source = self.driver.page_source if "profile" in page_source.lower() or "editor" in page_source.lower(): print(" βœ… Profile editor content detected in page source") else: print(" ❌ Profile editor content NOT found - page may have redirected") # Wait for page to fully load print("\n⏳ Waiting for page to fully load...") try: WebDriverWait(self.driver, 10).until( lambda d: d.execute_script("return document.readyState") == "complete" ) time.sleep(2) # Additional wait for React to render except: print(" ⚠️ Page load timeout, continuing anyway...") # Try to find any profile_editor attributes to confirm page is loaded initial_check = self.driver.find_elements(By.CSS_SELECTOR, "[data-testid^='profile_editor__']") print(f" Initial check: Found {len(initial_check)} profile_editor attributes") # Expected attributes from UI team's implementation status # Based on 10_FINAL_IMPLEMENTATION_STATUS.md expected_attributes = { # Page-level (3 according to UI team, not 4) 'profile_editor__page': 'Page container', 'profile_editor__progress_value': 'Progress value', 'profile_editor__back_button': 'Back button', # Note: UI team doesn't mention missing_fields_toggle # Tab navigation 'profile_editor__tabs_container': 'Tabs container', 'profile_editor__tabs_scroll_left_button': 'Scroll left button', 'profile_editor__tabs_scroll_right_button': 'Scroll right button', 'profile_editor__tab_personal_information': 'Tab: Personal Information', 'profile_editor__tab_contact_information': 'Tab: Contact Information', 'profile_editor__tab_parent_guardian': 'Tab: Parent/Guardian', 'profile_editor__tab_education_details': 'Tab: Education Details', 'profile_editor__tab_focus_areas': 'Tab: Focus Areas', 'profile_editor__tab_self_assessment': 'Tab: Self-Assessment', 'profile_editor__tab_hobbies_clubs': 'Tab: Hobbies & Clubs', 'profile_editor__tab_achievements': 'Tab: Achievements', 'profile_editor__tab_expectations': 'Tab: Expectations', # Personal Information (11 according to UI team, includes age_input) 'profile_editor__first_name_input': 'First Name input', 'profile_editor__last_name_input': 'Last Name input', 'profile_editor__gender_select': 'Gender select', 'profile_editor__dob_input': 'DOB input', 'profile_editor__age_input': 'Age input', # UI team mentions this 'profile_editor__nationality_input': 'Nationality input', 'profile_editor__language_input': 'Language input', 'profile_editor__student_id_input': 'Student ID input', 'profile_editor__student_cpid_input': 'Student CPID input', 'profile_editor__specially_abled_checkbox': 'Specially Abled checkbox', 'profile_editor__specially_abled_details_textarea': 'Specially Abled details', # Contact Information 'profile_editor__email_input': 'Email input', 'profile_editor__phone_input': 'Phone input', 'profile_editor__address_input': 'Address input', 'profile_editor__city_input': 'City input', 'profile_editor__state_input': 'State input', 'profile_editor__zip_code_input': 'ZIP Code input', 'profile_editor__native_state_input': 'Native State input', # Navigation buttons 'profile_editor__prev_button': 'Previous button', 'profile_editor__next_button': 'Next button', 'profile_editor__cancel_button': 'Cancel button', 'profile_editor__save_button': 'Save button', } # Find all profile_editor attributes in DOM print("\nπŸ” Searching for profile_editor attributes in DOM...") all_elements = self.driver.find_elements(By.CSS_SELECTOR, "[data-testid^='profile_editor__']") found_attributes = set() for elem in all_elements: try: test_id = elem.get_attribute('data-testid') if test_id: found_attributes.add(test_id) self.results['profile_editor']['found'].append({ 'attribute': test_id, 'tag': elem.tag_name, 'visible': elem.is_displayed() }) except: pass # Check for expected attributes print(f"\nπŸ“Š Found {len(found_attributes)} profile_editor attributes") for attr, description in expected_attributes.items(): if attr in found_attributes: print(f" βœ… {attr} - {description}") self.results['profile_editor']['expected'].append(attr) else: print(f" ❌ {attr} - {description} - MISSING") self.results['profile_editor']['missing'].append({ 'attribute': attr, 'description': description }) # Check for unexpected attributes unexpected = found_attributes - set(expected_attributes.keys()) for attr in unexpected: print(f" ⚠️ {attr} - UNEXPECTED (not in requirements)") self.results['profile_editor']['unexpected'].append(attr) # Check for dynamic attributes (MultiSelectPicker) print("\nπŸ” Checking for dynamic attributes (MultiSelectPicker)...") dynamic_patterns = [ 'profile_editor__short_term_focus__', 'profile_editor__long_term_focus__', 'profile_editor__strength__', 'profile_editor__improvement__', 'profile_editor__hobby__', 'profile_editor__club_', 'profile_editor__expectation__', ] dynamic_found = defaultdict(list) for attr in found_attributes: for pattern in dynamic_patterns: if attr.startswith(pattern): dynamic_found[pattern].append(attr) for pattern, attrs in dynamic_found.items(): print(f" βœ… {pattern}: Found {len(attrs)} attributes") if len(attrs) > 0: print(f" Examples: {attrs[:3]}") return found_attributes def verify_mandatory_reset_attributes(self): """Verify all Mandatory Password Reset Modal data-testid attributes""" print("\n" + "="*80) print("πŸ” VERIFYING: Mandatory Password Reset Modal Attributes") print("="*80) # Navigate to login and trigger password reset modal print("\nπŸ“‹ Navigating to login page...") self.driver.get(BASE_URL + "/login") time.sleep(2) # Login to trigger password reset modal print("πŸ”‘ Logging in to trigger password reset modal...") login_page = LoginPage(self.driver) login_page.login(identifier=TEST_USERNAME, password=TEST_PASSWORD) time.sleep(3) # Check if modal is present reset_page = MandatoryResetPage(self.driver) if not reset_page.is_modal_present(): print("⚠️ Password reset modal not present - password may already be reset") print(" Skipping mandatory reset verification") return set() print("βœ… Password reset modal detected") # Expected attributes expected_attributes = { 'mandatory_reset__modal': 'Modal overlay', 'mandatory_reset__modal_content': 'Modal content', 'mandatory_reset__continue_button': 'Continue button', 'mandatory_reset__form': 'Form container', 'mandatory_reset__current_password_input': 'Current password input', 'mandatory_reset__current_password_toggle': 'Current password toggle', 'mandatory_reset__current_password_error': 'Current password error', 'mandatory_reset__new_password_input': 'New password input', 'mandatory_reset__new_password_toggle': 'New password toggle', 'mandatory_reset__new_password_error': 'New password error', 'mandatory_reset__confirm_password_input': 'Confirm password input', 'mandatory_reset__confirm_password_toggle': 'Confirm password toggle', 'mandatory_reset__confirm_password_error': 'Confirm password error', 'mandatory_reset__back_button': 'Back button', 'mandatory_reset__submit_button': 'Submit button', } # Find all mandatory_reset attributes in DOM print("\nπŸ” Searching for mandatory_reset attributes in DOM...") all_elements = self.driver.find_elements(By.CSS_SELECTOR, "[data-testid^='mandatory_reset__']") found_attributes = set() for elem in all_elements: try: test_id = elem.get_attribute('data-testid') if test_id: found_attributes.add(test_id) self.results['mandatory_reset']['found'].append({ 'attribute': test_id, 'tag': elem.tag_name, 'visible': elem.is_displayed() }) except: pass # Check for expected attributes print(f"\nπŸ“Š Found {len(found_attributes)} mandatory_reset attributes") for attr, description in expected_attributes.items(): if attr in found_attributes: print(f" βœ… {attr} - {description}") self.results['mandatory_reset']['expected'].append(attr) else: print(f" ❌ {attr} - {description} - MISSING") self.results['mandatory_reset']['missing'].append({ 'attribute': attr, 'description': description }) # Check for unexpected attributes unexpected = found_attributes - set(expected_attributes.keys()) for attr in unexpected: print(f" ⚠️ {attr} - UNEXPECTED (not in requirements)") self.results['mandatory_reset']['unexpected'].append(attr) # Click Continue to see Step 2 (form) try: continue_btn = self.driver.find_element(By.CSS_SELECTOR, "[data-testid='mandatory_reset__continue_button']") if continue_btn.is_displayed(): print("\nπŸ“‹ Clicking Continue button to verify Step 2 attributes...") continue_btn.click() time.sleep(2) # Re-check for form attributes form_elements = self.driver.find_elements(By.CSS_SELECTOR, "[data-testid^='mandatory_reset__']") form_attributes = set() for elem in form_elements: try: test_id = elem.get_attribute('data-testid') if test_id: form_attributes.add(test_id) except: pass print(f"\nπŸ“Š After clicking Continue: Found {len(form_attributes)} attributes") new_attrs = form_attributes - found_attributes if new_attrs: print(f" βœ… New attributes found in Step 2: {new_attrs}") found_attributes.update(new_attrs) except: print(" ⚠️ Could not click Continue button (may already be on Step 2)") return found_attributes def generate_verification_report(self): """Generate comprehensive verification report""" print("\n" + "="*80) print("πŸ“Š VERIFICATION REPORT SUMMARY") print("="*80) # Profile Editor profile_expected = len(self.results['profile_editor']['expected']) profile_missing = len(self.results['profile_editor']['missing']) profile_unexpected = len(self.results['profile_editor']['unexpected']) profile_total_found = len(self.results['profile_editor']['found']) print(f"\nπŸ“‹ Profile Editor:") print(f" βœ… Found: {profile_total_found} attributes") print(f" βœ… Expected: {profile_expected} attributes") print(f" ❌ Missing: {profile_missing} attributes") print(f" ⚠️ Unexpected: {profile_unexpected} attributes") if profile_missing > 0: print(f"\n ❌ Missing Attributes:") for item in self.results['profile_editor']['missing']: print(f" - {item['attribute']}: {item['description']}") # Mandatory Reset reset_expected = len(self.results['mandatory_reset']['expected']) reset_missing = len(self.results['mandatory_reset']['missing']) reset_unexpected = len(self.results['mandatory_reset']['unexpected']) reset_total_found = len(self.results['mandatory_reset']['found']) print(f"\nπŸ“‹ Mandatory Password Reset Modal:") print(f" βœ… Found: {reset_total_found} attributes") print(f" βœ… Expected: {reset_expected} attributes") print(f" ❌ Missing: {reset_missing} attributes") print(f" ⚠️ Unexpected: {reset_unexpected} attributes") if reset_missing > 0: print(f"\n ❌ Missing Attributes:") for item in self.results['mandatory_reset']['missing']: print(f" - {item['attribute']}: {item['description']}") # Overall Status total_expected = profile_expected + reset_expected total_missing = profile_missing + reset_missing total_found = profile_total_found + reset_total_found print(f"\nπŸ“Š OVERALL STATUS:") print(f" βœ… Total Found: {total_found} attributes") print(f" βœ… Total Expected: {total_expected} attributes") print(f" ❌ Total Missing: {total_missing} attributes") if total_missing == 0: print(f"\n πŸŽ‰ SUCCESS: All required attributes are present!") else: print(f"\n ⚠️ WARNING: {total_missing} attributes are still missing") return self.results def verify_implementation(): """Main verification function""" print("="*80) print("πŸ” DATA-TESTID IMPLEMENTATION VERIFICATION") print("="*80) print("\n🎯 Purpose: Verify UI team's implementation against actual DOM") print("πŸ’‘ Method: Systematic DOM inspection - Zero assumptions") print("πŸ“‹ Approach: World-class verification with 100% accuracy\n") # Setup Chrome driver chrome_options = Options() chrome_options.add_argument("--start-maximized") chrome_options.add_argument("--disable-blink-features=AutomationControlled") chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"]) chrome_options.add_experimental_option('useAutomationExtension', False) driver = webdriver.Chrome(options=chrome_options) verifier = DataTestIdVerifier(driver) try: # Step 1: Verify Profile Editor profile_attrs = verifier.verify_profile_editor_attributes() # Step 2: Verify Password Reset Modal reset_attrs = verifier.verify_mandatory_reset_attributes() # Step 3: Generate Report results = verifier.generate_verification_report() # Step 4: Save detailed report report_file = os.path.join(os.path.dirname(__file__), '..', 'documentation', 'verification-reports', 'DOM_VERIFICATION_REPORT.json') os.makedirs(os.path.dirname(report_file), exist_ok=True) with open(report_file, 'w') as f: json.dump(results, f, indent=2) print(f"\nπŸ’Ύ Detailed report saved to: {report_file}") print(f"\n⏸️ Browser will stay open for 30 seconds for manual inspection...") time.sleep(30) except Exception as e: print(f"\n❌ ERROR: {e}") import traceback traceback.print_exc() print(f"\n⏸️ Browser will stay open for 30 seconds for manual inspection...") time.sleep(30) finally: print(f"\n{'='*80}") print("CLEANUP") print(f"{'='*80}") driver.quit() print("βœ… Driver closed") if __name__ == "__main__": verify_implementation()