CP_AUTOMATION/scripts/verify_data_testid_implementation.py
2025-12-12 19:54:54 +05:30

452 lines
20 KiB
Python

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