"""Модуль dropdown_list_component содержит класс для работы с выпадающими списками. Класс DropdownList наследует базовый функционал BaseComponent и добавляет методы для взаимодействия с выпадающими списками на странице. """ import re from playwright.sync_api import Page, Locator from tools.logger import get_logger from components.base_component import BaseComponent logger = get_logger("DROPDOWN_LIST") class DropdownList(BaseComponent): """Класс для работы с выпадающими списками. Наследует функциональность BaseElement и добавляет специфичные методы для выбора и проверки элементов списка. """ def __init__(self, page: Page): """Инициализирует компонент выпадающего списка. Args: page: Экземпляр страницы Playwright. """ super().__init__(page) # Действия: def click_item_with_text(self, text: str) -> None: """Выбирает элемент списка по указанному тексту. Args: text (str): Текст элемента для выбора. """ element = self.page.get_by_role("listitem").filter(has_text=text) if element.count() > 1: rtext = f"^{text}$" element = self.page.get_by_role("listitem").filter(has_text=re.compile(rtext)) element.click() def get_item_names(self, locator: str | Locator) -> list[str]: """Возвращает тексты всех элементов по указанному локатору. Args: locator: Локатор элементов или строка с CSS/XPath. Returns: Список текстов элементов. """ loc = self.get_locator(locator) texts = loc.all_inner_texts() return texts[0].splitlines() def scroll_until_end(self, locator: str | Locator) -> None: """ Скроллит список до тех пор, пока не перестанут подгружаться новые элементы. Args: locator: Локатор элементов или строка с CSS/XPath. """ loc = self.get_locator(locator) items_count = 0 attempts = 0 max_attempts = 3 last_item_name = "" while attempts < max_attempts: self.page.wait_for_timeout(300) item_names = self.get_item_names(loc) current_count = len(item_names) if current_count == items_count: attempts += 1 else: items_count = current_count attempts = 0 last_item_name = item_names[current_count-1] element = self.page.get_by_role("listitem").filter(has_text=last_item_name) element.scroll_into_view_if_needed() self.page.wait_for_timeout(300) self.check_item_with_text(last_item_name) # Проверки: def check_item_with_text(self, text: str) -> None: """Проверяет наличие и доступность элемента списка. Args: text (str): Текст элемента для проверки. Raises: AssertionError: Если элемент отсутствует или недоступен. """ element = self.page.get_by_role("listitem").filter(has_text=text) if element.count() > 1: rtext = f"^{text}$" element = self.page.get_by_role("listitem").filter(has_text=re.compile(rtext)) enabled = element.is_enabled() if not enabled: assert False, f"Dropdown list item '{text}' is missing or disabled" def check_vertical_scrolling(self, locator: str | Locator) -> bool: """ Проверяет функцию вертикального скроллинга списка. Args: locator: Локатор элементов или строка с CSS/XPath. Returns: True или False значение в зависимости от скроллируемый список или нет. """ loc = self.get_locator(locator) is_scrollable_vertically = self.is_scrollable_vertically(loc) if is_scrollable_vertically: self.scroll_until_end(loc) item_names = self.get_item_names(loc) first_item_name = item_names[0] self.scroll_up(loc) self.page.wait_for_timeout(300) self.check_item_with_text(first_item_name) return is_scrollable_vertically