357 lines
16 KiB
Python
357 lines
16 KiB
Python
"""
|
|
Verify UI Team's Implementation of Missing Attributes
|
|
|
|
This script verifies that all 5 missing attributes are actually present in the DOM
|
|
after the UI team's implementation.
|
|
"""
|
|
import sys
|
|
from pathlib import Path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
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.profile_editor_page import ProfileEditorPage
|
|
from pages.domain_assessment_page import DomainAssessmentPage
|
|
from pages.assessments_page import AssessmentsPage
|
|
from pages.domains_page import DomainsPage
|
|
from utils.password_tracker import password_tracker
|
|
from config.config import TEST_USERNAME, TEST_NEW_PASSWORD, BASE_URL
|
|
import time
|
|
|
|
def verify_toast_attribute(driver, test_id, description):
|
|
"""Verify toast attribute is present in DOM"""
|
|
print(f"\n🔍 Verifying: {description}")
|
|
print(f" Looking for: [data-testid='{test_id}']")
|
|
|
|
try:
|
|
# Wait for toast to appear and data-testid to be added (UI team's helper takes max 1 second)
|
|
# Check multiple times as toast helpers add attribute programmatically
|
|
max_attempts = 10
|
|
for attempt in range(max_attempts):
|
|
time.sleep(0.3) # Wait a bit for attribute to be added
|
|
|
|
# Check if attribute exists in DOM
|
|
elements = driver.find_elements(By.CSS_SELECTOR, f"[data-testid='{test_id}']")
|
|
if elements:
|
|
visible_elements = [e for e in elements if e.is_displayed()]
|
|
if visible_elements:
|
|
print(f" ✅ FOUND: {len(visible_elements)} visible element(s) (attempt {attempt + 1})")
|
|
# Also check if it has role="status" (should have both)
|
|
for elem in visible_elements:
|
|
role = elem.get_attribute("role")
|
|
if role == "status":
|
|
print(f" ✅ Also has role='status' (correct)")
|
|
return True
|
|
else:
|
|
if attempt < max_attempts - 1:
|
|
continue # Retry
|
|
print(f" ⚠️ Found but not visible: {len(elements)} element(s)")
|
|
return False
|
|
|
|
# Also check page source as fallback
|
|
if attempt == max_attempts - 1:
|
|
page_source = driver.page_source
|
|
if test_id in page_source:
|
|
print(f" ⚠️ Found in page source but not as DOM element")
|
|
return False
|
|
|
|
print(f" ❌ NOT FOUND in DOM after {max_attempts} attempts")
|
|
return False
|
|
except Exception as e:
|
|
print(f" ❌ ERROR: {e}")
|
|
return False
|
|
|
|
def verify_static_attribute(driver, test_id, description):
|
|
"""Verify static attribute is present in DOM"""
|
|
print(f"\n🔍 Verifying: {description}")
|
|
print(f" Looking for: [data-testid='{test_id}']")
|
|
|
|
try:
|
|
element = WebDriverWait(driver, 5).until(
|
|
EC.presence_of_element_located((By.CSS_SELECTOR, f"[data-testid='{test_id}']"))
|
|
)
|
|
if element.is_displayed():
|
|
print(f" ✅ FOUND and VISIBLE")
|
|
return True
|
|
else:
|
|
print(f" ⚠️ Found but not visible")
|
|
return False
|
|
except Exception as e:
|
|
print(f" ❌ NOT FOUND: {e}")
|
|
return False
|
|
|
|
def main():
|
|
print("=" * 70)
|
|
print("UI TEAM IMPLEMENTATION VERIFICATION")
|
|
print("=" * 70)
|
|
|
|
options = Options()
|
|
options.add_argument('--headless=new')
|
|
options.add_argument('--no-sandbox')
|
|
options.add_argument('--disable-dev-shm-usage')
|
|
driver = webdriver.Chrome(options=options)
|
|
driver.implicitly_wait(5)
|
|
|
|
results = {}
|
|
|
|
try:
|
|
# 1. Verify Login Error (Banner and Toast)
|
|
print("\n" + "=" * 70)
|
|
print("1. LOGIN ERROR (BANNER AND TOAST)")
|
|
print("=" * 70)
|
|
login_page = LoginPage(driver)
|
|
login_page.navigate()
|
|
|
|
# Try invalid login to trigger error
|
|
login_page.send_keys(login_page.IDENTIFIER_INPUT, "INVALID_USER_12345")
|
|
login_page.send_keys(login_page.PASSWORD_INPUT, "INVALID_PASS_12345")
|
|
time.sleep(3) # Allow UI to stabilize
|
|
|
|
# Use JavaScript click to avoid interception
|
|
try:
|
|
submit_button = WebDriverWait(driver, 5).until(
|
|
EC.element_to_be_clickable(login_page.SUBMIT_BUTTON)
|
|
)
|
|
driver.execute_script("arguments[0].click();", submit_button)
|
|
except Exception as e:
|
|
print(f" ⚠️ Error clicking submit button: {e}")
|
|
# Try alternative: find and click via JavaScript
|
|
submit_button = driver.find_element(*login_page.SUBMIT_BUTTON)
|
|
driver.execute_script("arguments[0].click();", submit_button)
|
|
|
|
time.sleep(5) # Wait for error to appear
|
|
|
|
# Check for error banner (inline error - this is what actually appears)
|
|
print("\n Checking for Error Banner (inline error):")
|
|
results['login_error_banner'] = verify_static_attribute(
|
|
driver,
|
|
'student_login__error_banner',
|
|
'Login Error Banner (Inline)'
|
|
)
|
|
|
|
# Check for error toast (toast notification - may not appear)
|
|
print("\n Checking for Error Toast (toast notification):")
|
|
results['login_error_toast'] = verify_toast_attribute(
|
|
driver,
|
|
'student_login__error_toast',
|
|
'Login Error Toast'
|
|
)
|
|
|
|
# Document finding
|
|
if results['login_error_banner'] and not results['login_error_toast']:
|
|
print("\n 📝 NOTE: Login error is displayed as inline banner, not toast.")
|
|
print(" ✅ Automation uses error banner (student_login__error_banner) - WORKING")
|
|
print(" ⚠️ Toast (student_login__error_toast) not appearing - may be UI team issue")
|
|
|
|
# 2. Verify Profile Editor Toasts (requires login and navigation)
|
|
print("\n" + "=" * 70)
|
|
print("2. PROFILE EDITOR TOASTS")
|
|
print("=" * 70)
|
|
|
|
# Login with valid credentials
|
|
driver.get(BASE_URL)
|
|
time.sleep(2)
|
|
|
|
# Use JavaScript click for login to avoid interception
|
|
try:
|
|
login_page.send_keys(login_page.IDENTIFIER_INPUT, TEST_USERNAME)
|
|
login_page.send_keys(login_page.PASSWORD_INPUT, TEST_NEW_PASSWORD)
|
|
time.sleep(2)
|
|
submit_button = WebDriverWait(driver, 5).until(
|
|
EC.element_to_be_clickable(login_page.SUBMIT_BUTTON)
|
|
)
|
|
driver.execute_script("arguments[0].click();", submit_button)
|
|
time.sleep(5) # Wait for login to complete
|
|
except Exception as e:
|
|
print(f" ⚠️ Error during login: {e}")
|
|
# Fallback: try regular login method
|
|
login_page.login(TEST_USERNAME, TEST_NEW_PASSWORD)
|
|
time.sleep(3)
|
|
|
|
# Navigate to profile editor
|
|
profile_editor = ProfileEditorPage(driver)
|
|
profile_editor.navigate()
|
|
profile_editor.wait_for_page_load()
|
|
time.sleep(2)
|
|
|
|
# Check if profile editor page is loaded
|
|
if profile_editor.is_element_visible(profile_editor.PAGE, timeout=5):
|
|
print(" ✅ Profile editor page loaded")
|
|
print(" Note: Profile editor toasts require actual save operation")
|
|
print(" Checking if toast locators are defined correctly...")
|
|
|
|
# Verify locators are defined (code-level check)
|
|
if hasattr(profile_editor, 'SUCCESS_TOAST') and hasattr(profile_editor, 'ERROR_TOAST'):
|
|
print(f" ✅ SUCCESS_TOAST locator defined: {profile_editor.SUCCESS_TOAST}")
|
|
print(f" ✅ ERROR_TOAST locator defined: {profile_editor.ERROR_TOAST}")
|
|
results['profile_editor_success_toast'] = "LOCATOR_DEFINED"
|
|
results['profile_editor_error_toast'] = "LOCATOR_DEFINED"
|
|
else:
|
|
print(" ❌ Toast locators not defined in page object")
|
|
results['profile_editor_success_toast'] = False
|
|
results['profile_editor_error_toast'] = False
|
|
else:
|
|
print(" ❌ Profile editor page not loaded")
|
|
results['profile_editor_success_toast'] = False
|
|
results['profile_editor_error_toast'] = False
|
|
|
|
# 3. Verify Assessment Header Product Name
|
|
print("\n" + "=" * 70)
|
|
print("3. ASSESSMENT HEADER - PRODUCT NAME")
|
|
print("=" * 70)
|
|
|
|
# Navigate to assessments
|
|
assessments_page = AssessmentsPage(driver)
|
|
assessments_page.navigate()
|
|
assessments_page.wait_for_page_load()
|
|
time.sleep(2)
|
|
|
|
# Get first assessment and start it
|
|
assessment_ids = assessments_page.get_assessment_ids()
|
|
if assessment_ids:
|
|
# Use JavaScript click to avoid interception
|
|
try:
|
|
assessment_card = driver.find_element(
|
|
By.CSS_SELECTOR,
|
|
f"[data-testid='assessment_card__{assessment_ids[0]}_action']"
|
|
)
|
|
driver.execute_script("arguments[0].click();", assessment_card)
|
|
except Exception as e:
|
|
print(f" ⚠️ Error clicking assessment: {e}")
|
|
assessments_page.click_begin_assessment(assessment_ids[0])
|
|
time.sleep(3)
|
|
|
|
# Navigate to first domain
|
|
domains_page = DomainsPage(driver)
|
|
domains_page.wait_for_page_load()
|
|
time.sleep(2)
|
|
|
|
domain_ids = domains_page.get_all_domain_ids()
|
|
unlocked_domain_id = None
|
|
for domain_id in domain_ids:
|
|
if domains_page.is_domain_unlocked(domain_id):
|
|
unlocked_domain_id = domain_id
|
|
break
|
|
|
|
if unlocked_domain_id:
|
|
# Use JavaScript click to avoid interception
|
|
try:
|
|
domain_card = driver.find_element(
|
|
By.CSS_SELECTOR,
|
|
f"[data-testid='domain_card__{unlocked_domain_id}_action']"
|
|
)
|
|
driver.execute_script("arguments[0].click();", domain_card)
|
|
except Exception as e:
|
|
print(f" ⚠️ Error clicking domain: {e}")
|
|
domains_page.click_start_domain(unlocked_domain_id)
|
|
time.sleep(5)
|
|
|
|
# Dismiss instructions modal if present
|
|
domain_assessment = DomainAssessmentPage(driver)
|
|
if domain_assessment.is_instructions_modal_present():
|
|
domain_assessment.dismiss_instructions_modal()
|
|
time.sleep(2)
|
|
|
|
results['assessment_header_product_name'] = verify_static_attribute(
|
|
driver,
|
|
'domain_assessment__header__product_name',
|
|
'Assessment Header Product Name'
|
|
)
|
|
|
|
# 4. Verify Question Counter
|
|
print("\n" + "=" * 70)
|
|
print("4. ACTION BAR - QUESTION COUNTER")
|
|
print("=" * 70)
|
|
|
|
results['question_counter'] = verify_static_attribute(
|
|
driver,
|
|
'domain_assessment__action_bar__question_counter',
|
|
'Question Counter in Action Bar'
|
|
)
|
|
else:
|
|
print(" ⚠️ No unlocked domain found")
|
|
results['assessment_header_product_name'] = False
|
|
results['question_counter'] = False
|
|
else:
|
|
print(" ⚠️ No assessments found")
|
|
results['assessment_header_product_name'] = False
|
|
results['question_counter'] = False
|
|
|
|
except Exception as e:
|
|
print(f"\n❌ ERROR during verification: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
finally:
|
|
driver.quit()
|
|
|
|
# Print Summary
|
|
print("\n" + "=" * 70)
|
|
print("VERIFICATION SUMMARY")
|
|
print("=" * 70)
|
|
|
|
summary = {
|
|
'High Priority': {
|
|
'student_login__error_banner': results.get('login_error_banner', False),
|
|
'student_login__error_toast': results.get('login_error_toast', False),
|
|
'profile_editor__success_toast': results.get('profile_editor_success_toast', False),
|
|
'profile_editor__error_toast': results.get('profile_editor_error_toast', False),
|
|
},
|
|
'Medium Priority': {
|
|
'domain_assessment__header__product_name': results.get('assessment_header_product_name', False),
|
|
'domain_assessment__action_bar__question_counter': results.get('question_counter', False),
|
|
}
|
|
}
|
|
|
|
for priority, attrs in summary.items():
|
|
print(f"\n{priority}:")
|
|
for attr, result in attrs.items():
|
|
if result == True:
|
|
print(f" ✅ {attr}")
|
|
elif result == "LOCATOR_DEFINED":
|
|
print(f" ⚠️ {attr} - Locator defined (needs actual toast to verify)")
|
|
elif result == "NEEDS_MANUAL_TEST":
|
|
print(f" ⚠️ {attr} - Needs manual test (requires user action)")
|
|
else:
|
|
print(f" ❌ {attr}")
|
|
|
|
# Calculate completion
|
|
# Note: student_login__error_banner is the actual working locator (inline error)
|
|
# student_login__error_toast is the toast (may not appear for this error type)
|
|
high_priority_verified = sum(1 for v in summary['High Priority'].values() if v == True)
|
|
high_priority_defined = sum(1 for v in summary['High Priority'].values() if v == "LOCATOR_DEFINED")
|
|
medium_priority_verified = sum(1 for v in summary['Medium Priority'].values() if v == True)
|
|
|
|
# Critical attributes (what automation actually uses)
|
|
critical_verified = sum(1 for k, v in summary['High Priority'].items()
|
|
if k in ['student_login__error_banner', 'profile_editor__success_toast', 'profile_editor__error_toast']
|
|
and v == True)
|
|
|
|
print(f"\n📊 Completion:")
|
|
print(f" Critical (for automation): {critical_verified}/3 verified")
|
|
print(f" High Priority: {high_priority_verified}/4 verified, {high_priority_defined}/4 locators defined")
|
|
print(f" Medium Priority: {medium_priority_verified}/2 verified")
|
|
print(f" Total Verified: {high_priority_verified + medium_priority_verified}/6")
|
|
print(f" Total (with locators): {high_priority_verified + high_priority_defined + medium_priority_verified}/6")
|
|
|
|
print(f"\n📝 Notes:")
|
|
print(f" - student_login__error_banner: Inline error (actually used by automation) ✅")
|
|
print(f" - student_login__error_toast: Toast notification (may not appear for 'User not found' error)")
|
|
print(f" - Profile editor toasts: Need actual save/error operation to verify")
|
|
|
|
if critical_verified == 3 and medium_priority_verified == 2:
|
|
print("\n✅ ALL CRITICAL ATTRIBUTES VERIFIED - 100% READY FOR AUTOMATION!")
|
|
elif high_priority_verified >= 3 and medium_priority_verified == 2:
|
|
print("\n✅ ALL REQUIRED ATTRIBUTES IMPLEMENTED - 100% COMPLETE!")
|
|
print("⚠️ Some toast attributes need actual operations to verify")
|
|
else:
|
|
print("\n⚠️ Some attributes need verification")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
|