""" Domain Assessment Page Object Model Handles in-progress domain test experience. Scope: domain_assessment, domain_question """ import time from selenium.webdriver.common.by import By from pages.base_page import BasePage class DomainAssessmentPage(BasePage): """Page Object for Domain Assessment Page""" # Locators using data-testid (scope: domain_assessment) PAGE = (By.CSS_SELECTOR, "[data-testid='domain_assessment__page']") BACK_BUTTON = (By.CSS_SELECTOR, "[data-testid='domain_assessment__back_button']") PROGRESS_VALUE = (By.CSS_SELECTOR, "[data-testid='domain_assessment__progress_value']") TIMER_VALUE = (By.CSS_SELECTOR, "[data-testid='domain_assessment__timer_value']") # Navigation buttons PREV_BUTTON = (By.CSS_SELECTOR, "[data-testid='domain_assessment__prev_button']") NEXT_BUTTON = (By.CSS_SELECTOR, "[data-testid='domain_assessment__next_button']") SUBMIT_BUTTON = (By.CSS_SELECTOR, "[data-testid='domain_assessment__submit_button']") # Modals INSTRUCTIONS_MODAL = (By.CSS_SELECTOR, "[data-testid='domain_assessment__instructions_modal']") INSTRUCTIONS_MODAL_CONTENT = (By.CSS_SELECTOR, "[data-testid='domain_assessment__instructions_modal__content']") INSTRUCTIONS_CONTINUE_BUTTON = (By.CSS_SELECTOR, "[data-testid='domain_assessment__instructions_modal__continue_button']") SUBMIT_MODAL = (By.CSS_SELECTOR, "[data-testid='domain_assessment__submit_modal']") SUBMIT_MODAL_CONTENT = (By.CSS_SELECTOR, "[data-testid='domain_assessment__submit_modal__content']") SUBMIT_MODAL_REVIEW_BUTTON = (By.CSS_SELECTOR, "[data-testid='domain_assessment__submit_modal__review_button']") SUBMIT_MODAL_CONFIRM_BUTTON = (By.CSS_SELECTOR, "[data-testid='domain_assessment__submit_modal__confirm_button']") SUBMIT_MODAL_CANCEL_BUTTON = (By.CSS_SELECTOR, "[data-testid='domain_assessment__submit_modal__cancel_button']") GUIDANCE_MODAL = (By.CSS_SELECTOR, "[data-testid='domain_assessment__guidance_modal']") GUIDANCE_MODAL_CONTENT = (By.CSS_SELECTOR, "[data-testid='domain_assessment__guidance_modal__content']") GUIDANCE_DISMISS_BUTTON = (By.CSS_SELECTOR, "[data-testid='domain_assessment__guidance_modal__dismiss_button']") SUCCESS_MODAL = (By.CSS_SELECTOR, "[data-testid='domain_assessment__success_modal']") SUCCESS_MODAL_CONTENT = (By.CSS_SELECTOR, "[data-testid='domain_assessment__success_modal__content']") SUCCESS_MODAL_MESSAGE = (By.CSS_SELECTOR, "[data-testid='domain_assessment__success_modal__message']") # Action bar ACTION_BAR = (By.CSS_SELECTOR, "[data-testid='domain_assessment__action_bar']") def __init__(self, driver): """Initialize Domain Assessment Page""" super().__init__(driver) def wait_for_page_load(self): """Wait for domain assessment page to load""" super().wait_for_page_load() # Call parent method # Wait for either the page container OR instructions modal (both indicate page loaded) try: # First check if instructions modal is present (that's also a valid state) if self.is_instructions_modal_present(): return # Page loaded, instructions modal showing # Otherwise wait for the actual page self.wait.wait_for_element_visible(self.PAGE, timeout=15) return # Page element found except: # If page not found, check if instructions modal is there try: if self.is_instructions_modal_present(): return # Instructions modal is present, page is loaded except: pass # If neither found, try waiting for action bar or header as fallback try: self.wait.wait_for_element_visible(self.ACTION_BAR, timeout=5) return # Action bar found, page is loaded except: pass # Try to find a question element (if questions are visible, page is loaded) try: from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC WebDriverWait(self.driver, 5).until( EC.presence_of_element_located((By.CSS_SELECTOR, "[data-testid^='domain_question__']")) ) return # Questions found, page is loaded except: pass # Last resort: try back button (but don't fail if not found) try: self.wait.wait_for_element_visible(self.BACK_BUTTON, timeout=3) return # Back button found, page is loaded except: pass # Final check: if URL is correct, consider page loaded (even if elements not found yet) current_url = self.driver.current_url if "/assessment/" in current_url and "/domain/" in current_url: # URL indicates we're on the right page - give it a moment and return import time time.sleep(1) # Brief wait for elements to appear return # URL indicates we're on the right page # If we get here, page might still be loading - don't raise exception # Let the caller handle it or wait longer pass def click_back(self): """Click back button - returns to domains page""" self.click_element(self.BACK_BUTTON) self.wait.wait_for_url_contains("/domains") def get_progress(self): """Get current progress value""" try: return self.get_text(self.PROGRESS_VALUE) except: return "0" def get_timer_value(self): """Get timer value""" try: return self.get_text(self.TIMER_VALUE) except: return "" def get_current_question_id(self): """ Get current question ID from URL or page Returns: str: Question ID or None """ import re current_url = self.driver.current_url match = re.search(r'/question/(\d+)', current_url) if match: return match.group(1) return None def get_question_element(self, question_id): """ Get question shell element Args: question_id: Question ID Returns: WebElement: Question element """ question_locator = (By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}']") return self.find_element(question_locator) def get_question_text(self, question_id): """Get question text""" text_locator = (By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}__text']") return self.get_text(text_locator) def answer_multiple_choice(self, question_id, option_label): """ Answer multiple choice question Args: question_id: Question ID option_label: Option label (a, b, c, etc.) """ option_locator = (By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}__option_{option_label}']") self.click_element(option_locator) def answer_true_false(self, question_id, value): """ Answer true/false question Args: question_id: Question ID value: True or False (boolean or string "True"/"False") """ # Convert boolean to string if needed if isinstance(value, bool): value_str = "True" if value else "False" else: value_str = str(value) if value_str.lower() in ['true', 'yes']: value_str = "True" elif value_str.lower() in ['false', 'no']: value_str = "False" tf_locator = (By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}__truefalse_{value_str}']") self.click_element(tf_locator) def answer_rating_scale(self, question_id, score): """ Answer rating scale question Args: question_id: Question ID score: Rating score (1-5, etc.) """ rating_locator = (By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}__rating_{score}']") self.click_element(rating_locator) def answer_rating(self, question_id, score): """Alias for answer_rating_scale""" self.answer_rating_scale(question_id, score) def answer_open_ended(self, question_id, text): """ Answer open-ended question Args: question_id: Question ID text: Answer text """ textarea_locator = (By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}__textarea']") self.send_keys(textarea_locator, text) def answer_matrix(self, question_id, row_index, column_index): """ Answer matrix question Args: question_id: Question ID row_index: Row index column_index: Column index """ matrix_locator = (By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}__matrix_{row_index}_{column_index}']") self.click_element(matrix_locator) def click_previous(self): """Click Previous button""" self.click_element(self.PREV_BUTTON) # Wait for previous question to load self.wait_for_page_load() def click_next(self): """Click Next button""" self.click_element(self.NEXT_BUTTON) # Wait for next question to load self.wait_for_page_load() def click_submit(self): """Click Submit button - opens submit confirmation modal""" self.click_element(self.SUBMIT_BUTTON) # Wait for submit modal self.wait.wait_for_element_visible(self.SUBMIT_MODAL) def confirm_submit(self): """Confirm submission - clicks confirm in submit modal""" self.click_element(self.SUBMIT_MODAL_CONFIRM_BUTTON) # Wait for success modal self.wait.wait_for_element_visible(self.SUCCESS_MODAL) def review_before_submit(self): """Click review button in submit modal""" self.click_element(self.SUBMIT_MODAL_REVIEW_BUTTON) # Modal closes, back to questions def dismiss_guidance(self): """Dismiss guidance modal if present""" try: if self.is_element_visible(self.GUIDANCE_MODAL, timeout=2): self.click_element(self.GUIDANCE_DISMISS_BUTTON) # Wait for modal to close self.wait.wait_for_element_invisible(self.GUIDANCE_MODAL, timeout=5) except: pass def is_instructions_modal_present(self): """Check if instructions modal is present""" try: return self.is_element_visible(self.INSTRUCTIONS_MODAL, timeout=3) except: return False def dismiss_instructions_modal(self): """Dismiss instructions modal by clicking continue button""" try: if self.is_instructions_modal_present(): self.click_element(self.INSTRUCTIONS_CONTINUE_BUTTON) # Wait for modal to close self.wait.wait_for_element_invisible(self.INSTRUCTIONS_MODAL, timeout=5) except: pass def wait_for_success_modal(self): """Wait for success modal after submission""" self.wait.wait_for_element_visible(self.SUCCESS_MODAL) def is_submit_modal_present(self): """Check if submit confirmation modal is present""" try: return self.is_element_visible(self.SUBMIT_MODAL, timeout=3) except: return False def is_success_modal_present(self): """Check if success modal is present""" try: return self.is_element_visible(self.SUCCESS_MODAL, timeout=5) except: return False def close_success_modal(self): """Close success modal if present""" try: # Success modal usually auto-closes or redirects # Wait for it to disappear (no fixed sleep needed) from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from config.config import SHORT_WAIT WebDriverWait(self.driver, SHORT_WAIT).until( EC.invisibility_of_element_located(self.SUCCESS_MODAL) ) except: # Modal might have already closed or redirected pass def is_next_button_visible(self): """Check if next button is visible and enabled""" try: next_btn = self.find_element(self.NEXT_BUTTON) return next_btn.is_displayed() and next_btn.is_enabled() except: return False def is_submit_button_visible(self): """Check if submit button is visible and enabled""" try: submit_btn = self.find_element(self.SUBMIT_BUTTON) return submit_btn.is_displayed() and submit_btn.is_enabled() except: return False def get_all_questions(self): """ Get all question IDs on current page Returns: list: List of question IDs """ import re questions = self.driver.find_elements(By.CSS_SELECTOR, "[data-testid^='domain_question__']") question_ids = [] for question in questions: test_id = question.get_attribute("data-testid") if test_id: match = re.search(r'domain_question__(\d+)', test_id) if match: q_id = match.group(1) if q_id not in question_ids: question_ids.append(q_id) return question_ids def get_question_type(self, question_id): """ Determine question type by checking available answer options Args: question_id: Question ID Returns: str: Question type (multiple_choice, true_false, rating_scale, open_ended, matrix) """ try: # Check for multiple choice options (A, B, C, D, E) if self.is_element_present((By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}__option_A']"), timeout=1): return "multiple_choice" # Check for true/false if self.is_element_present((By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}__truefalse_True']"), timeout=1): return "true_false" # Check for rating scale if self.is_element_present((By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}__rating_1']"), timeout=1): return "rating_scale" # Check for open ended if self.is_element_present((By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}__textarea']"), timeout=1): return "open_ended" # Check for matrix if self.is_element_present((By.CSS_SELECTOR, f"[data-testid^='domain_question__{question_id}__matrix_']"), timeout=1): return "matrix" return "unknown" except: return "unknown" def get_question_options(self, question_id): """ Get available options for multiple choice question Args: question_id: Question ID Returns: list: List of option labels (a, b, c, etc.) """ import re options = self.driver.find_elements(By.CSS_SELECTOR, f"[data-testid^='domain_question__{question_id}__option_']") option_labels = [] for option in options: test_id = option.get_attribute("data-testid") if test_id: match = re.search(r'option_([a-z0-9]+)', test_id) if match: option_labels.append(match.group(1)) return option_labels def select_option(self, question_id, option_label): """Select an option for multiple choice question""" self.answer_multiple_choice(question_id, option_label) def select_true_false(self, question_id, value): """Select true/false value""" self.answer_true_false(question_id, value) def select_rating(self, question_id, score): """Select rating score""" self.answer_rating(question_id, score) def enter_open_ended(self, question_id, text): """Enter text for open-ended question""" self.answer_open_ended(question_id, text) def select_matrix_cell(self, question_id, row_index, column_index, value=True): """Select matrix cell""" self.answer_matrix(question_id, row_index, column_index) def get_matrix_dimensions(self, question_id): """ Get matrix dimensions Returns: tuple: (rows, cols) """ import re matrix_cells = self.driver.find_elements(By.CSS_SELECTOR, f"[data-testid^='domain_question__{question_id}__matrix_']") rows = set() cols = set() for cell in matrix_cells: test_id = cell.get_attribute("data-testid") if test_id: match = re.search(r'matrix_(\d+)_(\d+)', test_id) if match: rows.add(int(match.group(1))) cols.add(int(match.group(2))) return (len(rows) if rows else 0, len(cols) if cols else 0)