579 lines
19 KiB
Python
Executable File
579 lines
19 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Current DOM Verification Script
|
|
|
|
Visits the site with fresh credentials and checks ALL data-testid attributes
|
|
in the actual rendered DOM to verify UI Dev Team's implementation.
|
|
"""
|
|
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 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 pages.dashboard_page import DashboardPage
|
|
from pages.assessments_page import AssessmentsPage
|
|
from utils.driver_manager import DriverManager
|
|
from utils.password_tracker import password_tracker
|
|
from config.config import TEST_NEW_PASSWORD, BASE_URL
|
|
import re
|
|
|
|
|
|
def get_all_data_testids_from_dom(driver):
|
|
"""Extract ALL data-testid attributes from the current DOM"""
|
|
testids = {}
|
|
|
|
# Get all elements with data-testid
|
|
elements = driver.find_elements(By.CSS_SELECTOR, "[data-testid]")
|
|
|
|
for element in elements:
|
|
testid = element.get_attribute("data-testid")
|
|
tag = element.tag_name
|
|
element_type = element.get_attribute("type") or tag
|
|
visible = element.is_displayed()
|
|
text = element.text[:100] if element.text else ""
|
|
value = element.get_attribute("value")[:100] if element.get_attribute("value") else ""
|
|
|
|
if testid:
|
|
testids[testid] = {
|
|
"tag": tag,
|
|
"type": element_type,
|
|
"visible": visible,
|
|
"text": text,
|
|
"value": value,
|
|
"location": f"{tag}.{element_type}"
|
|
}
|
|
|
|
return testids
|
|
|
|
|
|
def verify_login_page(driver):
|
|
"""Verify login page attributes"""
|
|
print("\n" + "="*80)
|
|
print("VERIFYING LOGIN PAGE")
|
|
print("="*80)
|
|
|
|
driver.get(f"{BASE_URL}/")
|
|
time.sleep(2)
|
|
|
|
testids = get_all_data_testids_from_dom(driver)
|
|
|
|
expected = [
|
|
"student_login__form",
|
|
"student_login__identifier_input",
|
|
"student_login__password_input",
|
|
"student_login__remember_checkbox",
|
|
"student_login__error_banner",
|
|
"student_login__submit_button",
|
|
"student_login__error_toast"
|
|
]
|
|
|
|
found = []
|
|
missing = []
|
|
|
|
for attr in expected:
|
|
if attr in testids:
|
|
found.append(attr)
|
|
print(f"✅ {attr}")
|
|
else:
|
|
missing.append(attr)
|
|
print(f"❌ MISSING: {attr}")
|
|
|
|
# Check for unexpected
|
|
login_testids = [tid for tid in testids if tid.startswith("student_login__")]
|
|
unexpected = [tid for tid in login_testids if tid not in expected]
|
|
|
|
if unexpected:
|
|
print(f"\n⚠️ UNEXPECTED (but present): {len(unexpected)}")
|
|
for tid in unexpected[:10]:
|
|
print(f" - {tid}")
|
|
|
|
print(f"\n📊 Login Page: {len(found)}/{len(expected)} found")
|
|
|
|
return {"found": found, "missing": missing, "unexpected": unexpected, "all": testids}
|
|
|
|
|
|
def verify_dashboard(driver):
|
|
"""Verify dashboard attributes"""
|
|
print("\n" + "="*80)
|
|
print("VERIFYING DASHBOARD PAGE")
|
|
print("="*80)
|
|
|
|
dashboard = DashboardPage(driver)
|
|
dashboard.navigate()
|
|
time.sleep(2)
|
|
|
|
testids = get_all_data_testids_from_dom(driver)
|
|
|
|
expected = [
|
|
"dashboard__welcome_message",
|
|
"profile_incomplete__modal",
|
|
"profile_incomplete__progress_value",
|
|
"profile_incomplete__complete_button"
|
|
]
|
|
|
|
found = []
|
|
missing = []
|
|
|
|
for attr in expected:
|
|
if attr in testids:
|
|
found.append(attr)
|
|
print(f"✅ {attr}")
|
|
else:
|
|
missing.append(attr)
|
|
print(f"❌ MISSING: {attr}")
|
|
|
|
print(f"\n📊 Dashboard: {len(found)}/{len(expected)} found")
|
|
|
|
return {"found": found, "missing": missing, "all": testids}
|
|
|
|
|
|
def verify_student_nav(driver):
|
|
"""Verify student navigation attributes"""
|
|
print("\n" + "="*80)
|
|
print("VERIFYING STUDENT NAVIGATION")
|
|
print("="*80)
|
|
|
|
testids = get_all_data_testids_from_dom(driver)
|
|
|
|
expected = [
|
|
"student_nav__dashboard_link",
|
|
"student_nav__assessments_link",
|
|
"student_nav__profile_button",
|
|
"student_nav__profile_dropdown",
|
|
"student_nav__edit_profile_button",
|
|
"student_nav__reset_password_button",
|
|
"student_nav__sign_out_button"
|
|
]
|
|
|
|
found = []
|
|
missing = []
|
|
|
|
for attr in expected:
|
|
if attr in testids:
|
|
found.append(attr)
|
|
print(f"✅ {attr}")
|
|
else:
|
|
missing.append(attr)
|
|
print(f"❌ MISSING: {attr}")
|
|
|
|
# Check for unexpected
|
|
nav_testids = [tid for tid in testids if tid.startswith("student_nav__")]
|
|
unexpected = [tid for tid in nav_testids if tid not in expected]
|
|
|
|
if unexpected:
|
|
print(f"\n⚠️ UNEXPECTED (but present): {len(unexpected)}")
|
|
for tid in unexpected[:10]:
|
|
print(f" - {tid}")
|
|
|
|
print(f"\n📊 Student Nav: {len(found)}/{len(expected)} found")
|
|
|
|
return {"found": found, "missing": missing, "unexpected": unexpected, "all": testids}
|
|
|
|
|
|
def verify_profile_editor(driver):
|
|
"""Verify profile editor attributes"""
|
|
print("\n" + "="*80)
|
|
print("VERIFYING PROFILE EDITOR PAGE")
|
|
print("="*80)
|
|
|
|
profile_editor = ProfileEditorPage(driver)
|
|
profile_editor.navigate()
|
|
profile_editor.wait_for_page_load()
|
|
time.sleep(3) # Wait for full render
|
|
|
|
testids = get_all_data_testids_from_dom(driver)
|
|
|
|
print(f"📋 Found {len(testids)} total data-testid attributes in DOM\n")
|
|
|
|
# Base attributes
|
|
base_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",
|
|
"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",
|
|
]
|
|
|
|
# Step 1: Personal Information
|
|
step1_expected = [
|
|
"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"
|
|
]
|
|
|
|
# Step 2: Contact Information
|
|
step2_expected = [
|
|
"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"
|
|
]
|
|
|
|
# Step 3: Parent/Guardian
|
|
step3_expected = [
|
|
"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"
|
|
]
|
|
|
|
# Step 4: Education Details
|
|
step4_expected = [
|
|
"profile_editor__full_name_input",
|
|
"profile_editor__current_grade_input",
|
|
"profile_editor__section_input",
|
|
"profile_editor__board_stream_select"
|
|
]
|
|
|
|
# Step 8: Achievements
|
|
step8_expected = [
|
|
"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"
|
|
]
|
|
|
|
all_expected = base_expected + step1_expected + step2_expected + step3_expected + step4_expected + step8_expected
|
|
|
|
found = []
|
|
missing = []
|
|
|
|
print("🔍 Checking base attributes...")
|
|
for attr in all_expected:
|
|
if attr in testids:
|
|
found.append(attr)
|
|
print(f"✅ {attr}")
|
|
else:
|
|
missing.append(attr)
|
|
print(f"❌ MISSING: {attr}")
|
|
|
|
# Check dynamic checkboxes
|
|
print("\n🔍 Checking dynamic checkboxes...")
|
|
patterns = {
|
|
"short_term_focus": "profile_editor__short_term_focus__",
|
|
"long_term_focus": "profile_editor__long_term_focus__",
|
|
"strength": "profile_editor__strength__",
|
|
"improvement": "profile_editor__improvement__",
|
|
"hobby": "profile_editor__hobby__",
|
|
"club": "profile_editor__club_",
|
|
"expectation": "profile_editor__expectation__"
|
|
}
|
|
|
|
dynamic_counts = {}
|
|
for name, pattern in patterns.items():
|
|
matching = [tid for tid in testids if tid.startswith(pattern)]
|
|
dynamic_counts[name] = len(matching)
|
|
if matching:
|
|
print(f"✅ {name}: {len(matching)} checkboxes found")
|
|
for tid in matching[:5]:
|
|
print(f" - {tid}")
|
|
if len(matching) > 5:
|
|
print(f" ... and {len(matching) - 5} more")
|
|
else:
|
|
print(f"❌ {name}: No checkboxes found")
|
|
|
|
# Check "Others" text inputs
|
|
print("\n🔍 Checking 'Others' text inputs...")
|
|
others_expected = [
|
|
"profile_editor__short_term_focus_others_text",
|
|
"profile_editor__long_term_focus_others_text",
|
|
"profile_editor__strength_others_text",
|
|
"profile_editor__improvement_others_text",
|
|
"profile_editor__hobby_other_text",
|
|
"profile_editor__club_other_text",
|
|
"profile_editor__expectation_others_text"
|
|
]
|
|
|
|
for attr in others_expected:
|
|
if attr in testids:
|
|
print(f"✅ {attr}")
|
|
else:
|
|
print(f"❌ MISSING: {attr}")
|
|
|
|
# Check tab scroll buttons
|
|
print("\n🔍 Checking tab scroll buttons...")
|
|
scroll_expected = [
|
|
"profile_editor__tabs_scroll_right_button",
|
|
"profile_editor__tabs_scroll_left_button"
|
|
]
|
|
|
|
for attr in scroll_expected:
|
|
if attr in testids:
|
|
print(f"✅ {attr}")
|
|
else:
|
|
print(f"❌ MISSING: {attr}")
|
|
|
|
# Check toast messages
|
|
print("\n🔍 Checking toast messages...")
|
|
toast_expected = [
|
|
"profile_editor__toast_message",
|
|
"profile_editor__success_toast",
|
|
"profile_editor__error_toast"
|
|
]
|
|
|
|
toast_found = [attr for attr in toast_expected if attr in testids]
|
|
if toast_found:
|
|
for attr in toast_found:
|
|
print(f"✅ {attr}")
|
|
else:
|
|
print("❌ No toast message attributes found")
|
|
|
|
# Get all profile_editor testids
|
|
all_profile_testids = [tid for tid in testids if tid.startswith("profile_editor__")]
|
|
unexpected = [tid for tid in all_profile_testids if tid not in all_expected and not any(tid.startswith(p) for p in patterns.values()) and tid not in others_expected and tid not in scroll_expected and tid not in toast_expected]
|
|
|
|
if unexpected:
|
|
print(f"\n⚠️ UNEXPECTED profile_editor attributes ({len(unexpected)}):")
|
|
for tid in unexpected[:20]:
|
|
print(f" - {tid}")
|
|
if len(unexpected) > 20:
|
|
print(f" ... and {len(unexpected) - 20} more")
|
|
|
|
total_dynamic = sum(dynamic_counts.values())
|
|
total_found = len(found) + total_dynamic
|
|
|
|
print(f"\n📊 Profile Editor Summary:")
|
|
print(f" Base attributes: {len(found)}/{len(all_expected)}")
|
|
print(f" Dynamic checkboxes: {total_dynamic}")
|
|
print(f" Total found: {total_found}")
|
|
print(f" Unexpected: {len(unexpected)}")
|
|
|
|
return {
|
|
"found": found,
|
|
"missing": missing,
|
|
"dynamic": dynamic_counts,
|
|
"unexpected": unexpected,
|
|
"all": testids,
|
|
"total_found": total_found
|
|
}
|
|
|
|
|
|
def verify_assessments_page(driver):
|
|
"""Verify assessments page attributes"""
|
|
print("\n" + "="*80)
|
|
print("VERIFYING ASSESSMENTS PAGE")
|
|
print("="*80)
|
|
|
|
assessments = AssessmentsPage(driver)
|
|
assessments.navigate()
|
|
assessments.wait_for_page_load()
|
|
time.sleep(2)
|
|
|
|
testids = get_all_data_testids_from_dom(driver)
|
|
|
|
# Check for assessment card pattern
|
|
assessment_cards = [tid for tid in testids if tid.startswith("assessment_card__")]
|
|
|
|
if assessment_cards:
|
|
print(f"✅ Found {len(assessment_cards)} assessment card attributes")
|
|
for tid in assessment_cards[:5]:
|
|
print(f" - {tid}")
|
|
if len(assessment_cards) > 5:
|
|
print(f" ... and {len(assessment_cards) - 5} more")
|
|
else:
|
|
print("❌ No assessment card attributes found")
|
|
|
|
return {"found": assessment_cards, "all": testids}
|
|
|
|
|
|
def main():
|
|
"""Main verification function"""
|
|
driver = None
|
|
try:
|
|
print("🚀 Starting Current DOM Verification...")
|
|
print(f"🌐 Environment: {BASE_URL}\n")
|
|
print(f"🔑 Using credentials: MAHMAH812B\n")
|
|
|
|
# Initialize driver
|
|
driver_manager = DriverManager()
|
|
driver = driver_manager.get_driver(headless=False)
|
|
|
|
results = {}
|
|
|
|
# Step 1: Login
|
|
print("\n" + "="*80)
|
|
print("STEP 1: LOGIN")
|
|
print("="*80)
|
|
login_page = LoginPage(driver)
|
|
login_page.login(identifier="MAHMAH812B", password="s&OI0m9rJAUb")
|
|
print(f"✅ Login successful - URL: {driver.current_url}\n")
|
|
|
|
# Step 2: Verify Login Page (if redirected back)
|
|
# Actually, we're logged in, so verify current page
|
|
|
|
# Step 3: Handle Password Reset if needed
|
|
reset_page = MandatoryResetPage(driver)
|
|
if reset_page.is_modal_present():
|
|
print("⚠️ Password reset modal present - resetting password...")
|
|
reset_page.reset_password(
|
|
current_password="s&OI0m9rJAUb",
|
|
new_password=TEST_NEW_PASSWORD,
|
|
confirm_password=TEST_NEW_PASSWORD,
|
|
student_cpid="MAHMAH812B"
|
|
)
|
|
print("✅ Password reset completed\n")
|
|
time.sleep(2)
|
|
|
|
# Step 4: Verify Dashboard
|
|
results["dashboard"] = verify_dashboard(driver)
|
|
|
|
# Step 5: Verify Student Navigation
|
|
results["student_nav"] = verify_student_nav(driver)
|
|
|
|
# Step 6: Navigate to Profile Editor
|
|
profile_incomplete = ProfileIncompletePage(driver)
|
|
if profile_incomplete.is_modal_present():
|
|
print("\n⚠️ 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)
|
|
|
|
# Step 7: Verify Profile Editor
|
|
results["profile_editor"] = verify_profile_editor(driver)
|
|
|
|
# Step 8: Navigate to Assessments
|
|
dashboard = DashboardPage(driver)
|
|
dashboard.navigate()
|
|
time.sleep(2)
|
|
dashboard.click_assessments()
|
|
time.sleep(3)
|
|
|
|
# Step 9: Verify Assessments Page
|
|
results["assessments"] = verify_assessments_page(driver)
|
|
|
|
# Step 10: Verify Login Page (logout and check)
|
|
print("\n" + "="*80)
|
|
print("STEP 10: VERIFYING LOGIN PAGE (After Logout)")
|
|
print("="*80)
|
|
from pages.student_nav_page import StudentNavPage
|
|
nav = StudentNavPage(driver)
|
|
try:
|
|
nav.logout()
|
|
time.sleep(2)
|
|
results["login"] = verify_login_page(driver)
|
|
except Exception as e:
|
|
print(f"⚠️ Could not logout: {e}")
|
|
# Navigate directly to login
|
|
driver.get(f"{BASE_URL}/")
|
|
time.sleep(2)
|
|
results["login"] = verify_login_page(driver)
|
|
|
|
# Final Summary
|
|
print("\n" + "="*80)
|
|
print("FINAL VERIFICATION SUMMARY")
|
|
print("="*80)
|
|
|
|
total_found = 0
|
|
total_expected = 0
|
|
|
|
if "login" in results:
|
|
found_count = len(results["login"]["found"])
|
|
total_found += found_count
|
|
total_expected += 7
|
|
print(f"Login Page: {found_count}/7")
|
|
|
|
if "dashboard" in results:
|
|
found_count = len(results["dashboard"]["found"])
|
|
total_found += found_count
|
|
total_expected += 4
|
|
print(f"Dashboard: {found_count}/4")
|
|
|
|
if "student_nav" in results:
|
|
found_count = len(results["student_nav"]["found"])
|
|
total_found += found_count
|
|
total_expected += 7
|
|
print(f"Student Nav: {found_count}/7")
|
|
|
|
if "profile_editor" in results:
|
|
base_found = len(results["profile_editor"]["found"])
|
|
dynamic_found = sum(results["profile_editor"]["dynamic"].values())
|
|
total_profile = base_found + dynamic_found
|
|
total_found += total_profile
|
|
print(f"Profile Editor: {total_profile} (Base: {base_found}, Dynamic: {dynamic_found})")
|
|
|
|
if "assessments" in results:
|
|
found_count = len(results["assessments"]["found"])
|
|
total_found += found_count
|
|
print(f"Assessments: {found_count} cards found")
|
|
|
|
print(f"\n📊 TOTAL: {total_found} attributes found")
|
|
|
|
# Save results
|
|
results_file = project_root / "analysis" / "current_dom_verification_results.json"
|
|
with open(results_file, 'w') as f:
|
|
json.dump(results, f, indent=2, default=str)
|
|
|
|
print(f"\n📄 Results saved to: {results_file}")
|
|
|
|
# Keep browser open for inspection
|
|
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()
|
|
|
|
|
|
|
|
|
|
|
|
|