""" Domain Assessment Tests Tests for answering questions in domain assessments. Covers all 5 question types and navigation. """ import pytest from pages.domain_assessment_page import DomainAssessmentPage from pages.domain_feedback_page import DomainFeedbackPage from pages.domains_page import DomainsPage from utils.question_answer_helper import QuestionAnswerHelper from utils.wait_helpers import WaitHelpers from utils.randomized_wait import RandomizedWait from selenium.webdriver.common.by import By import time @pytest.mark.assessment @pytest.mark.domain_assessment class TestDomainAssessment: """Test class for domain assessment functionality""" @pytest.fixture(autouse=True) def setup(self, smart_assessment_setup): """ Setup fixture - gets student ready for assessment Args: smart_assessment_setup: Smart fixture that handles login, password reset, profile completion """ self.driver = smart_assessment_setup['driver'] self.cpid = smart_assessment_setup['cpid'] self.assessments_page = smart_assessment_setup['assessments_page'] self.wait = WaitHelpers(self.driver) # Navigate to domains page assessment_ids = self.assessments_page.get_assessment_ids() if not assessment_ids: pytest.skip("No assessments available") # Start first assessment self.assessments_page.click_begin_assessment(assessment_ids[0]) # Get domains page self.domains_page = DomainsPage(self.driver) self.domains_page.wait_for_page_load() # Get first unlocked domain domain_ids = self.domains_page.get_all_domain_ids() if not domain_ids: pytest.skip("No domains available") # Find first unlocked domain unlocked_domain_id = None for domain_id in domain_ids: if self.domains_page.is_domain_unlocked(domain_id): unlocked_domain_id = domain_id break if not unlocked_domain_id: pytest.skip("No unlocked domains available") # Start domain assessment self.domains_page.click_start_domain(unlocked_domain_id) # Store current domain ID for reference self.current_domain_id = unlocked_domain_id # Get domain assessment page self.domain_assessment_page = DomainAssessmentPage(self.driver) # Wait for page to load (handles instructions modal) self.domain_assessment_page.wait_for_page_load() # Wait a moment for page to stabilize (randomized) RandomizedWait.wait_for_page_load('initial') # Dismiss instructions modal if present (must be done before accessing questions) if self.domain_assessment_page.is_instructions_modal_present(): print("šŸ“‹ Instructions modal detected - dismissing...") self.domain_assessment_page.dismiss_instructions_modal() # Wait for modal to close and page to load (randomized) RandomizedWait.wait_for_page_load('modal') # Now wait for actual assessment page to be visible try: self.wait.wait_for_element_visible(self.domain_assessment_page.PAGE, timeout=10) except: # If page not visible, check if we're on the right URL current_url = self.driver.current_url if "/domain/" in current_url: print(f"āš ļø Page element not found but URL is correct: {current_url}") # Try waiting for action bar as fallback try: self.wait.wait_for_element_visible(self.domain_assessment_page.ACTION_BAR, timeout=5) except: pass # Dismiss guidance modal if present self.domain_assessment_page.dismiss_guidance() # Initialize question answer helper self.question_helper = QuestionAnswerHelper(self.driver) yield # Cleanup if needed pass def test_instructions_modal_appears(self): """Test that instructions modal appears when starting a NEW domain assessment""" # Navigate back to assessments page (setup already started one domain) from pages.assessments_page import AssessmentsPage assessments_page = AssessmentsPage(self.driver) assessments_page.navigate() assessments_page.wait_for_page_load() # Get assessment IDs assessment_ids = assessments_page.get_assessment_ids() if not assessment_ids: pytest.skip("No assessments available") # Start assessment assessments_page.click_begin_assessment(assessment_ids[0]) domains_page = DomainsPage(self.driver) domains_page.wait_for_page_load() domain_ids = domains_page.get_all_domain_ids() if not domain_ids: pytest.skip("No domains available") # Find a domain that hasn't been started yet (different from setup's domain) unlocked_domain_id = None setup_domain_id = getattr(self, 'current_domain_id', None) for domain_id in domain_ids: if domains_page.is_domain_unlocked(domain_id): # Prefer a domain that wasn't started in setup if domain_id != setup_domain_id: unlocked_domain_id = domain_id break # If all domains were started, use any unlocked domain (modal might still appear) if not unlocked_domain_id: for domain_id in domain_ids: if domains_page.is_domain_unlocked(domain_id): unlocked_domain_id = domain_id break if not unlocked_domain_id: pytest.skip("No unlocked domains available") # Start domain assessment domains_page.click_start_domain(unlocked_domain_id) domain_assessment_page = DomainAssessmentPage(self.driver) # Wait for page load - this will detect if instructions modal is present domain_assessment_page.wait_for_page_load() # Verify instructions modal is present (it should appear for new domains) # Note: If domain was already started, modal won't appear - that's expected behavior modal_present = domain_assessment_page.is_instructions_modal_present() if not modal_present: # If modal not present, it means domain was already started - skip test pytest.skip(f"Instructions modal not present - domain {unlocked_domain_id} may have been already started") assert modal_present, "Instructions modal should be present when starting a new domain assessment" def test_instructions_modal_dismiss(self): """Test dismissing instructions modal""" # Navigate back to assessments page (setup already started one domain) from pages.assessments_page import AssessmentsPage assessments_page = AssessmentsPage(self.driver) assessments_page.navigate() assessments_page.wait_for_page_load() # Get assessment IDs assessment_ids = assessments_page.get_assessment_ids() if not assessment_ids: pytest.skip("No assessments available") # Start assessment assessments_page.click_begin_assessment(assessment_ids[0]) domains_page = DomainsPage(self.driver) domains_page.wait_for_page_load() domain_ids = domains_page.get_all_domain_ids() if not domain_ids: pytest.skip("No domains available") # Find a domain that hasn't been started yet (different from setup's domain) unlocked_domain_id = None setup_domain_id = getattr(self, 'current_domain_id', None) for domain_id in domain_ids: if domains_page.is_domain_unlocked(domain_id): # Prefer a domain that wasn't started in setup if domain_id != setup_domain_id: unlocked_domain_id = domain_id break # If all domains were started, use any unlocked domain if not unlocked_domain_id: for domain_id in domain_ids: if domains_page.is_domain_unlocked(domain_id): unlocked_domain_id = domain_id break if not unlocked_domain_id: pytest.skip("No unlocked domains available") # Start domain assessment domains_page.click_start_domain(unlocked_domain_id) domain_assessment_page = DomainAssessmentPage(self.driver) domain_assessment_page.wait_for_page_load() # Check if instructions modal is present modal_present = domain_assessment_page.is_instructions_modal_present() if not modal_present: # If modal not present, domain was already started - skip test pytest.skip(f"Instructions modal not present - domain {unlocked_domain_id} may have been already started") assert modal_present, "Instructions modal should be present when starting a new domain assessment" # Dismiss modal domain_assessment_page.dismiss_instructions_modal() # Wait for modal to close RandomizedWait.wait_for_page_load('modal') # Verify modal is dismissed assert not domain_assessment_page.is_instructions_modal_present(), "Instructions modal should be dismissed after clicking continue" def test_answer_single_question(self): """Test answering a single question""" # Get current question ID question_id = self.question_helper.get_question_id() assert question_id is not None, "Should have a current question" # Get question type question_type = self.question_helper.get_question_type(question_id) assert question_type != "unknown", f"Should detect question type, got: {question_type}" # Answer the question answer_result = self.question_helper.answer_question(question_id, question_type) assert answer_result['answer'] is not None, "Should have answered the question" assert answer_result['question_type'] == question_type, "Question type should match" def test_answer_multiple_choice_question(self): """Test answering a multiple choice question""" # Get current question question_id = self.question_helper.get_question_id() if not question_id: pytest.skip("No question available") question_type = self.question_helper.get_question_type(question_id) if question_type != "multiple_choice": pytest.skip(f"Current question is not multiple choice, it's {question_type}") # Answer with option A option_label = self.question_helper.answer_multiple_choice(question_id, option_label="A") assert option_label == "A", "Should have selected option A" def test_answer_true_false_question(self): """Test answering a true/false question""" # Get current question question_id = self.question_helper.get_question_id() if not question_id: pytest.skip("No question available") question_type = self.question_helper.get_question_type(question_id) if question_type != "true_false": pytest.skip(f"Current question is not true/false, it's {question_type}") # Answer with True value = self.question_helper.answer_true_false(question_id, value=True) assert value == "True", "Should have selected True" def test_answer_rating_scale_question(self): """Test answering a rating scale question""" # Get current question question_id = self.question_helper.get_question_id() if not question_id: pytest.skip("No question available") question_type = self.question_helper.get_question_type(question_id) if question_type != "rating_scale": pytest.skip(f"Current question is not rating scale, it's {question_type}") # Answer with any valid rating (rating scale can have labels like "Sometimes" not just numbers) score = self.question_helper.answer_rating_scale(question_id, score=None) # Let it pick any valid option assert score is not None and len(score) > 0, f"Should have selected a valid rating, got: {score}" def test_answer_open_ended_question(self): """Test answering an open-ended question""" # Get current question question_id = self.question_helper.get_question_id() if not question_id: pytest.skip("No question available") question_type = self.question_helper.get_question_type(question_id) if question_type != "open_ended": pytest.skip(f"Current question is not open-ended, it's {question_type}") # Answer with custom text text = "This is a thoughtful response to demonstrate understanding." entered_text = self.question_helper.answer_open_ended(question_id, text=text) assert entered_text == text, "Should have entered the text" def test_answer_matrix_question(self): """Test answering a matrix question""" # Get current question question_id = self.question_helper.get_question_id() if not question_id: pytest.skip("No question available") question_type = self.question_helper.get_question_type(question_id) if question_type != "matrix": pytest.skip(f"Current question is not matrix, it's {question_type}") # Answer with row 0, column 0 row_idx, col_idx = self.question_helper.answer_matrix(question_id, row_index=0, column_index=0) assert row_idx == 0 and col_idx == 0, "Should have selected matrix cell (0, 0)" def test_navigate_questions(self): """Test navigating between questions using Next/Previous buttons""" # Answer current question first question_id = self.question_helper.get_question_id() if not question_id: pytest.skip("No question available") question_type = self.question_helper.get_question_type(question_id) if question_type != "unknown": self.question_helper.answer_question(question_id, question_type) RandomizedWait.wait_for_question_answer(question_type) # Check if Next button is available if not self.domain_assessment_page.is_next_button_visible(): pytest.skip("Next button not visible - may be on last question or submit required") # Store original question ID original_question_id = question_id # Click Next self.domain_assessment_page.click_next() RandomizedWait.wait_for_navigation('next') # Wait for question to load # Verify we're on a new question new_question_id = self.question_helper.get_question_id() if not new_question_id: pytest.fail("Could not get question ID after clicking Next") assert new_question_id != original_question_id, f"Should be on a different question after clicking Next. Original: {original_question_id}, New: {new_question_id}" # Click Previous self.domain_assessment_page.click_previous() RandomizedWait.wait_for_navigation('previous') # Wait for question to load # Verify we're back to original question returned_question_id = self.question_helper.get_question_id() if not returned_question_id: pytest.fail("Could not get question ID after clicking Previous") assert returned_question_id == original_question_id, f"Should be back to original question after clicking Previous. Expected: {original_question_id}, Got: {returned_question_id}" def test_answer_all_questions_in_domain(self): """ Test answering all questions in a domain (single domain test - Option 2) This is the main test for single domain completion """ questions_answered = 0 max_questions = 100 # Safety limit print(f"\nšŸŽÆ Starting single domain assessment test") # Answer questions until we can submit consecutive_failures = 0 max_consecutive_failures = 3 while questions_answered < max_questions: # Wait a moment for question to load (randomized) RandomizedWait.wait_for_page_load('navigation') # Get current question question_id = self.question_helper.get_question_id() if not question_id: print(f"āš ļø Could not detect question ID, waiting...") RandomizedWait.wait_for_error_recovery('wait') question_id = self.question_helper.get_question_id() if not question_id: consecutive_failures += 1 print(f"āš ļø Still no question ID after retry (failure {consecutive_failures}/{max_consecutive_failures})") if consecutive_failures >= max_consecutive_failures: print(f"āŒ Too many consecutive failures, breaking") break # Try clicking next anyway if self.domain_assessment_page.is_next_button_visible(): try: self.domain_assessment_page.click_next() RandomizedWait.wait_for_navigation('next') except: pass continue print(f"šŸ” Detected question ID: {question_id}") # Get question type question_type = self.question_helper.get_question_type(question_id) if question_type == "unknown": print(f"āš ļø Unknown question type for question {question_id}") print(f" Attempting to scroll and wait...") # Scroll to question element try: question_elem = self.driver.find_element(By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}']") self.driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", question_elem) RandomizedWait.wait_for_page_load('navigation') question_type = self.question_helper.get_question_type(question_id) except: pass if question_type == "unknown": print(f"āŒ Still unknown after scroll, skipping question {question_id}") # Try to move to next question if self.domain_assessment_page.is_next_button_visible(): self.domain_assessment_page.click_next() RandomizedWait.wait_for_navigation('next') continue # Answer the question try: answer_result = self.question_helper.answer_question(question_id, question_type) questions_answered += 1 consecutive_failures = 0 # Reset failure counter on success # Realistic wait after answering (varies by question type) wait_time = RandomizedWait.wait_for_question_answer(question_type) print(f"āœ… Answered question {questions_answered}: {question_type} (ID: {question_id}) [waited {wait_time:.1f}s]") except Exception as e: consecutive_failures += 1 print(f"āŒ Error answering question {question_id}: {e} (failure {consecutive_failures}/{max_consecutive_failures})") if consecutive_failures >= max_consecutive_failures: print(f"āŒ Too many consecutive failures answering questions, breaking") import traceback traceback.print_exc() break # Try to continue to next question if self.domain_assessment_page.is_next_button_visible(): try: self.domain_assessment_page.click_next() RandomizedWait.wait_for_navigation('next') except: pass continue # Check if we can submit (all questions answered) submit_ready = False try: submit_button = self.driver.find_element(*self.domain_assessment_page.SUBMIT_BUTTON) is_enabled = submit_button.is_enabled() is_displayed = submit_button.is_displayed() print(f"šŸ” Submit button check: enabled={is_enabled}, displayed={is_displayed}, questions_answered={questions_answered}") if is_enabled and is_displayed: print(f"āœ… All questions answered! Ready to submit ({questions_answered} questions)") submit_ready = True break except Exception as e: print(f"āš ļø Submit button check failed: {e}") # Submit button not found or not enabled - continue to next question if submit_ready: break # Click Next if available next_visible = self.domain_assessment_page.is_next_button_visible() print(f"šŸ” Next button visible: {next_visible}") if next_visible: try: print(f"āž”ļø Clicking Next button...") self.domain_assessment_page.click_next() wait_time = RandomizedWait.wait_for_navigation('next') print(f"āœ… Moved to next question [waited {wait_time:.1f}s]") except Exception as e: print(f"āŒ Error clicking Next: {e}") import traceback traceback.print_exc() # Try to continue anyway - might be last question break else: # No next button - check if submit is available print(f"āš ļø Next button not visible - checking if this is the last question...") try: submit_button = self.driver.find_element(*self.domain_assessment_page.SUBMIT_BUTTON) is_enabled = submit_button.is_enabled() is_displayed = submit_button.is_displayed() print(f"šŸ” Submit button (last question): enabled={is_enabled}, displayed={is_displayed}") if is_enabled and is_displayed: print(f"āœ… Last question answered! Ready to submit ({questions_answered} questions)") break else: print(f"āš ļø No Next button and Submit not enabled. Questions answered: {questions_answered}") # Wait a bit more for submit to become enabled (randomized) print(f"ā³ Waiting for submit button to become enabled...") wait_time = RandomizedWait.wait_for_error_recovery('wait') print(f" Waited {wait_time:.1f}s") try: submit_button = self.driver.find_element(*self.domain_assessment_page.SUBMIT_BUTTON) if submit_button.is_enabled() and submit_button.is_displayed(): print(f"āœ… Submit button now enabled! Ready to submit ({questions_answered} questions)") break else: print(f"āš ļø Submit still not enabled after wait. Proceeding anyway...") break except: print(f"āš ļø Could not find submit button after wait") break except Exception as e: print(f"āš ļø No Next button and Submit button not found: {e}. Questions answered: {questions_answered}") break # Submit the assessment assert questions_answered > 0, f"Should have answered at least one question. Only answered: {questions_answered}" print(f"\nšŸ“Š Assessment Summary:") print(f" Total questions answered: {questions_answered}") print(f" Ready to submit: Yes") # Wait a moment for UI to update (randomized) RandomizedWait.wait_for_page_load('modal') # Verify submit button is enabled before clicking try: submit_button = self.driver.find_element(*self.domain_assessment_page.SUBMIT_BUTTON) if not submit_button.is_enabled(): print(f"āš ļø Submit button not enabled yet, waiting...") for i in range(5): wait_time = RandomizedWait.wait_for_error_recovery('wait') submit_button = self.driver.find_element(*self.domain_assessment_page.SUBMIT_BUTTON) if submit_button.is_enabled(): print(f"āœ… Submit button now enabled! [waited {wait_time:.1f}s]") break else: print(f"āš ļø Submit button still not enabled, attempting to click anyway...") except Exception as e: print(f"āš ļø Could not verify submit button state: {e}") print(f"\nšŸ“¤ Submitting domain assessment...") try: self.domain_assessment_page.click_submit() print(f"āœ… Submit button clicked successfully") except Exception as e: print(f"āŒ Error clicking submit: {e}") raise # Confirm submission print(f"ā³ Waiting for submit confirmation modal...") try: assert self.domain_assessment_page.is_submit_modal_present(), "Submit confirmation modal should appear" print(f"āœ… Submit confirmation modal appeared") self.domain_assessment_page.confirm_submit() print(f"āœ… Submission confirmed") except Exception as e: print(f"āŒ Error in submission confirmation: {e}") raise # Wait for success modal print(f"ā³ Waiting for success modal...") try: assert self.domain_assessment_page.is_success_modal_present(), "Success modal should appear after submission" print(f"āœ… Success modal appeared") except Exception as e: print(f"āš ļø Success modal check failed: {e}") # Continue anyway - modal might have closed quickly # Wait for feedback modal print(f"ā³ Waiting for domain feedback modal...") feedback_page = DomainFeedbackPage(self.driver) self.driver.implicitly_wait(5) # Wait for feedback modal to appear # Wait up to 10 seconds for feedback modal (with randomized checks) feedback_modal_present = False for i in range(10): if feedback_page.is_modal_present(): feedback_modal_present = True break RandomizedWait.wait_for_page_load('modal') if feedback_modal_present: print(f"āœ… Domain feedback modal appeared") print(f"šŸ“ Submitting domain feedback...") try: feedback_page.submit_feedback( question1_yes=True, question2_text="The assessment was clear and well-structured. All questions were relevant and the interface was user-friendly." ) print(f"āœ… Domain feedback submitted successfully") except Exception as e: print(f"āŒ Error submitting feedback: {e}") raise else: print(f"āš ļø Domain feedback modal did not appear (may not be required for this domain)") print(f"\nšŸŽ‰ Single domain assessment completed successfully!") print(f" Questions answered: {questions_answered}") print(f" Assessment submitted: Yes") print(f" Feedback submitted: {'Yes' if feedback_modal_present else 'N/A'}")