#!/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()