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

547 lines
24 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Complete Student Journey Test Script - OPTIMIZED
Systematic end-to-end test covering the entire student flow:
1. Login
2. Password Reset (if new student) → Admin@123
3. Profile Completion (if not 100%)
4. Start Assessment
5. Complete All Domains/Phases
6. Provide All Feedbacks
7. Complete Assessment
OPTIMIZED: Uses smart explicit waits instead of time.sleep()
"""
import sys
from pathlib import Path
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
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.student_nav_page import StudentNavPage
from pages.assessments_page import AssessmentsPage
from pages.domains_page import DomainsPage
from pages.domain_assessment_page import DomainAssessmentPage
from pages.domain_feedback_page import DomainFeedbackPage
from pages.feedback_survey_page import FeedbackSurveyPage
from utils.driver_manager import DriverManager
from config.config import BASE_URL, TEST_NEW_PASSWORD
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from config.config import SHORT_WAIT, MEDIUM_WAIT
def smart_wait_for_url_change(driver, current_url, timeout=MEDIUM_WAIT):
"""Wait for URL to change from current URL"""
wait = WebDriverWait(driver, timeout)
wait.until(lambda d: d.current_url != current_url)
def complete_student_journey(student_cpid, student_password, headless=False):
"""
Complete student journey test - OPTIMIZED with smart waits
Args:
student_cpid: Student CPID
student_password: Student password
headless: Run in headless mode
"""
driver = None
try:
print(f"\n{'='*80}")
print(f"COMPLETE STUDENT JOURNEY TEST (OPTIMIZED)")
print(f"{'='*80}")
print(f"Student: {student_cpid}")
print(f"{'='*80}\n")
# Initialize driver
driver_manager = DriverManager()
driver = driver_manager.get_driver(headless=headless)
# STEP 1: LOGIN
print("[STEP 1] LOGIN")
print("-" * 80)
login_page = LoginPage(driver)
login_page.login(identifier=student_cpid, password=student_password)
# Wait for navigation (handled by login method)
print(f"✅ Login successful - URL: {driver.current_url}\n")
# STEP 2: PASSWORD RESET (if new student)
print("[STEP 2] PASSWORD RESET CHECK")
print("-" * 80)
reset_page = MandatoryResetPage(driver)
if reset_page.is_modal_present():
print(" ✅ Password reset modal found (new student)")
print(f" Resetting password to: {TEST_NEW_PASSWORD}")
reset_page.reset_password(
current_password=student_password,
new_password=TEST_NEW_PASSWORD,
confirm_password=TEST_NEW_PASSWORD
)
print(" ✅ Password reset completed")
student_password = TEST_NEW_PASSWORD
else:
print(" ✅ No password reset required (existing student)")
print()
# STEP 3: PROFILE COMPLETION (if not 100%)
print("[STEP 3] PROFILE COMPLETION CHECK")
print("-" * 80)
profile_incomplete = ProfileIncompletePage(driver)
if profile_incomplete.is_modal_present():
progress = profile_incomplete.get_progress_value()
print(f" Profile incomplete - Current Progress: {progress}")
print(" Clicking Complete Profile button...")
profile_incomplete.click_complete()
# Wait for navigation (handled by click_complete)
print(f" Navigated to: {driver.current_url}")
# Complete profile to 100%
print("\n [STEP 3.1] Completing Profile to 100%...")
profile_editor = ProfileEditorPage(driver)
profile_editor.wait_for_page_load()
# Check progress
progress = profile_editor.get_progress_value()
print(f" Current Profile Progress: {progress}")
if "100%" in progress:
print(" ✅ Profile is 100% complete!")
else:
print(f" ⚠️ Profile is {progress} complete")
print(" Note: Profile completion automation requires student data from Excel")
print(" For now, profile can be completed manually or with process_students.py")
# Navigate back to dashboard
driver.get(f"{BASE_URL}/student/dashboard")
WebDriverWait(driver, SHORT_WAIT).until(
EC.url_contains("/dashboard")
)
else:
print(" ✅ Profile already complete or modal not present")
print()
# STEP 4: NAVIGATE TO ASSESSMENTS
print("[STEP 4] NAVIGATE TO ASSESSMENTS")
print("-" * 80)
try:
nav = StudentNavPage(driver)
nav.click_assessments()
# Wait for navigation (handled by click_assessments)
print(f"✅ Navigated to Assessments via nav link - URL: {driver.current_url}\n")
except Exception as e:
print(f" ⚠️ Could not click nav link: {e}")
print(" Trying direct navigation to assessments...")
# Fallback: Navigate directly
from config.config import ASSESSMENTS_URL
driver.get(ASSESSMENTS_URL)
WebDriverWait(driver, SHORT_WAIT).until(
EC.url_contains("/assessments")
)
print(f"✅ Navigated to Assessments directly - URL: {driver.current_url}\n")
# STEP 5: START ASSESSMENT
print("[STEP 5] START ASSESSMENT")
print("-" * 80)
assessments_page = AssessmentsPage(driver)
assessments_page.wait_for_page_load()
# Get available assessments
try:
assessment_ids = assessments_page.get_assessment_ids()
print(f" Found {len(assessment_ids)} assessment(s): {assessment_ids}")
except Exception as e:
print(f" ⚠️ Could not get assessment IDs: {e}")
print(" Trying to find assessment cards manually...")
assessment_ids = []
try:
cards = assessments_page.get_all_assessment_cards()
print(f" Found {len(cards)} assessment card(s)")
if cards:
import re
test_id = cards[0].get_attribute("data-testid")
if test_id:
match = re.search(r'assessment_card__(\d+)', test_id)
if match:
assessment_ids = [match.group(1)]
except Exception as e2:
print(f" ⚠️ Error finding cards: {e2}")
if not assessment_ids:
print(" ⚠️ No assessments available")
if not headless:
print(" Keeping browser open for 30 seconds for inspection...")
import time
time.sleep(30) # Only use sleep for final inspection delay
return
# Start first available assessment
first_assessment_id = assessment_ids[0]
print(f" Starting assessment ID: {first_assessment_id}")
try:
current_url = driver.current_url
assessments_page.click_begin_assessment(first_assessment_id)
# Wait for URL change (handled by click_begin_assessment)
print(f"✅ Assessment started - URL: {driver.current_url}\n")
except Exception as e:
print(f" ⚠️ Error starting assessment: {e}")
if not headless:
print(" Keeping browser open for 30 seconds for inspection...")
import time
time.sleep(30) # Only use sleep for final inspection delay
return
# STEP 6: COMPLETE ALL DOMAINS
print("[STEP 6] COMPLETE ALL DOMAINS")
print("-" * 80)
domains_page = DomainsPage(driver)
domains_page.wait_for_page_load()
# Get all domains
try:
domain_ids = domains_page.get_all_domain_ids()
print(f" Found {len(domain_ids)} domain(s): {domain_ids}")
except Exception as e:
print(f" ⚠️ Could not get domain IDs: {e}")
if not headless:
print(" Keeping browser open for 30 seconds for inspection...")
import time
time.sleep(30) # Only use sleep for final inspection delay
return
if not domain_ids:
print(" ⚠️ No domains available")
return
for idx, domain_id in enumerate(domain_ids, 1):
print(f"\n [DOMAIN {idx}/{len(domain_ids)}] Domain ID: {domain_id}")
print(" " + "-" * 76)
# Check if domain is unlocked
try:
if domains_page.is_domain_locked(domain_id):
print(f" ⚠️ Domain {domain_id} is locked - skipping")
continue
except:
print(f" ⚠️ Could not check lock status - trying to proceed...")
# Start domain assessment
print(f" Starting domain assessment...")
try:
current_url = driver.current_url
domains_page.click_domain_action(domain_id)
# Wait for URL change (handled by click_domain_action)
print(f" ✅ Domain assessment started")
except Exception as e:
print(f" ⚠️ Error starting domain: {e}")
continue
# STEP 6.1: COMPLETE DOMAIN ASSESSMENT
print(f" [STEP 6.1] Completing Domain Assessment...")
domain_assessment = DomainAssessmentPage(driver)
domain_assessment.wait_for_page_load()
# Dismiss guidance modal if present (non-blocking)
try:
domain_assessment.dismiss_guidance()
print(f" Dismissed guidance modal if present")
except:
pass
# Answer all questions in the domain
questions_answered = 0
max_questions = 50 # Safety limit
question_attempts = 0
while question_attempts < max_questions:
question_attempts += 1
print(f" [Question {question_attempts}] Processing...")
# Get current question ID
current_question_id = domain_assessment.get_current_question_id()
if not current_question_id:
# Try to find question on page
try:
questions = driver.find_elements(By.CSS_SELECTOR, "[data-testid^='domain_question__']")
if questions:
test_id = questions[0].get_attribute("data-testid")
if test_id:
import re
match = re.search(r'domain_question__(\d+)', test_id)
if match:
current_question_id = match.group(1)
except:
pass
if not current_question_id:
print(f" ⚠️ No question found - may have completed all questions")
break
print(f" Question ID: {current_question_id}")
# Try to answer the question (try different question types)
answered = False
try:
# Try multiple choice (option a)
try:
domain_assessment.answer_multiple_choice(current_question_id, "a")
print(f" ✅ Answered: Multiple choice (option a)")
answered = True
except:
# Try true/false
try:
domain_assessment.answer_true_false(current_question_id, True)
print(f" ✅ Answered: True/False (True)")
answered = True
except:
# Try rating
try:
domain_assessment.answer_rating(current_question_id, 3)
print(f" ✅ Answered: Rating (3)")
answered = True
except:
# Try open ended
try:
domain_assessment.answer_open_ended(current_question_id, "Automated test response for question.")
print(f" ✅ Answered: Open-ended")
answered = True
except:
# Try matrix (first cell)
try:
domain_assessment.answer_matrix(current_question_id, 0, 0)
print(f" ✅ Answered: Matrix (0,0)")
answered = True
except Exception as e:
print(f" ⚠️ Could not answer question: {e}")
except Exception as e:
print(f" ⚠️ Error answering question: {e}")
if answered:
questions_answered += 1
# Try to navigate to next question
try:
# Check if next button is visible and clickable
next_button = driver.find_elements(By.CSS_SELECTOR, "[data-testid='domain_assessment__next_button']")
if next_button and next_button[0].is_displayed() and next_button[0].is_enabled():
domain_assessment.click_next()
# Wait for next question to load (handled by click_next)
print(f" Navigated to next question")
continue
else:
# Check if submit button is visible
submit_button = driver.find_elements(By.CSS_SELECTOR, "[data-testid='domain_assessment__submit_button']")
if submit_button and submit_button[0].is_displayed() and submit_button[0].is_enabled():
print(f" All questions answered. Submitting...")
domain_assessment.click_submit()
# Wait for submit modal (handled by click_submit)
break
else:
print(f" ⚠️ No next or submit button found")
break
except Exception as e:
print(f" ⚠️ Error navigating: {e}")
break
print(f" ✅ Answered {questions_answered} question(s)")
# Handle submit confirmation modal if present
try:
submit_modal = driver.find_elements(By.CSS_SELECTOR, "[data-testid='domain_assessment__submit_modal']")
if submit_modal:
print(f" Submit confirmation modal found - confirming...")
domain_assessment.confirm_submit()
# Wait for success modal (handled by confirm_submit)
except Exception as e:
print(f" ⚠️ Error handling submit modal: {e}")
# Wait for success modal (non-blocking, short wait)
try:
success_modal = WebDriverWait(driver, SHORT_WAIT).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "[data-testid='domain_assessment__success_modal']"))
)
print(f" ✅ Domain assessment submitted successfully")
# Wait for modal to auto-close or redirect (short wait)
WebDriverWait(driver, SHORT_WAIT).until(
EC.invisibility_of_element_located((By.CSS_SELECTOR, "[data-testid='domain_assessment__success_modal']"))
)
except:
# Success modal might have already closed or not present
pass
# STEP 6.2: PROVIDE DOMAIN FEEDBACK
print(f" [STEP 6.2] Providing Domain Feedback...")
domain_feedback = DomainFeedbackPage(driver)
try:
# Wait for feedback modal with short timeout
if domain_feedback.is_modal_present():
print(f" Domain feedback modal found")
# Submit feedback with answers
try:
domain_feedback.submit_feedback(
question1_yes=True,
question1_text="Automated feedback response for testing.",
question2_text="This is automated feedback for testing purposes."
)
# Wait for modal close and redirect (handled by submit_feedback)
print(f" ✅ Domain feedback submitted")
except Exception as e:
print(f" ⚠️ Error submitting feedback: {e}")
# Try skip as fallback
try:
domain_feedback.skip_feedback()
print(f" Skipped feedback")
except:
pass
else:
print(f" ⚠️ No domain feedback modal found - may have auto-closed")
except Exception as e:
print(f" ⚠️ Error handling feedback: {e}")
# Navigate back to domains page
print(f" Returning to domains page...")
try:
current_url = driver.current_url
driver.back()
# Wait for URL change
WebDriverWait(driver, SHORT_WAIT).until(
lambda d: d.current_url != current_url
)
domains_page = DomainsPage(driver)
domains_page.wait_for_page_load()
except Exception as e:
print(f" ⚠️ Error returning to domains: {e}")
# Try direct navigation
try:
current_url = driver.current_url
base_url = current_url.split('/assessment/')[0]
assessment_id = current_url.split('/assessment/')[1].split('/')[0]
driver.get(f"{base_url}/assessment/{assessment_id}/domains")
domains_page = DomainsPage(driver)
domains_page.wait_for_page_load()
except:
pass
print(f"\n✅ All domains processed!\n")
# STEP 7: FINAL FEEDBACK (from domains page)
print("[STEP 7] FINAL FEEDBACK")
print("-" * 80)
# Check for final feedback modal on domains page
try:
if domains_page.is_final_feedback_modal_present():
print(" Final feedback modal found (from domains page)")
domains_page.fill_final_feedback(
question1_yes=True,
question1_reason="Automated final feedback response.",
question2_text="This is automated final feedback for testing purposes."
)
# Wait handled by fill_final_feedback
print(" ✅ Final feedback submitted")
else:
print(" ⚠️ Final feedback modal not found on domains page")
except Exception as e:
print(f" ⚠️ Error handling final feedback from domains page: {e}")
# Also check for feedback survey modal
print("\n[STEP 7.1] FEEDBACK SURVEY")
print("-" * 80)
try:
feedback_survey = FeedbackSurveyPage(driver)
# Check for overall modal
if feedback_survey.is_overall_modal_present():
print(" Final feedback modal (overall) found")
try:
feedback_survey.set_overall_rating(4)
print(" Selected overall rating: 4")
feedback_survey.submit_feedback()
# Wait handled by submit_feedback
print(" ✅ Overall feedback submitted")
except Exception as e:
print(f" ⚠️ Error with overall feedback: {e}")
# Check for per-question modal
if feedback_survey.is_per_question_modal_present():
print(" Final feedback modal (per-question) found")
try:
question_ratings = driver.find_elements(By.CSS_SELECTOR, "[data-testid^='feedback_survey__question_']")
question_ids = set()
for element in question_ratings:
test_id = element.get_attribute("data-testid")
if test_id:
import re
match = re.search(r'question_(\d+)_rating', test_id)
if match:
question_ids.add(match.group(1))
for q_id in question_ids:
try:
feedback_survey.set_question_rating(q_id, 4)
feedback_survey.set_question_comment(q_id, "Automated feedback comment")
print(f" Provided feedback for question {q_id}")
except:
pass
feedback_survey.submit_feedback()
# Wait handled by submit_feedback
print(" ✅ Per-question feedback submitted")
except Exception as e:
print(f" ⚠️ Error with per-question feedback: {e}")
if not feedback_survey.is_overall_modal_present() and not feedback_survey.is_per_question_modal_present():
print(" ⚠️ Final feedback modal not available")
except Exception as e:
print(f" ⚠️ Error handling final feedback: {e}")
print(f"\n{'='*80}")
print("COMPLETE JOURNEY TEST FINISHED!")
print(f"{'='*80}\n")
# Keep browser open for inspection (only if not headless)
if not headless:
print("Keeping browser open for 60 seconds for inspection...")
import time
time.sleep(60) # Only use sleep for final inspection delay
except Exception as e:
print(f"\n❌ ERROR: {e}")
import traceback
traceback.print_exc()
if driver and not headless:
print("\nKeeping browser open for 60 seconds for debugging...")
import time
time.sleep(60) # Only use sleep for final inspection delay
finally:
if driver:
driver.quit()
def main():
"""Main entry point"""
import argparse
parser = argparse.ArgumentParser(description='Complete student journey test (OPTIMIZED)')
parser.add_argument('--cpid', type=str, required=True, help='Student CPID')
parser.add_argument('--password', type=str, required=True, help='Student password')
parser.add_argument('--headless', action='store_true', help='Run in headless mode')
args = parser.parse_args()
complete_student_journey(args.cpid, args.password, headless=args.headless)
if __name__ == "__main__":
main()