From d555a2c6b0b9bee6df16b84f291bf6a02e4c1dbb Mon Sep 17 00:00:00 2001 From: Radislav Date: Tue, 25 Nov 2025 10:25:15 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D0=BE?= =?UTF-8?q?=D0=B2=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D1=81=20combob?= =?UTF-8?q?ox=20=D0=B2=20dropdown=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлены методы open_combobox, get_combobox_options, get_selected_combobox_value - Улучшено логирование и обработка состояний combobox --- components/dropdown_list_component.py | 233 ++++++++++++++------------ 1 file changed, 122 insertions(+), 111 deletions(-) diff --git a/components/dropdown_list_component.py b/components/dropdown_list_component.py index a139953..9df5267 100644 --- a/components/dropdown_list_component.py +++ b/components/dropdown_list_component.py @@ -17,7 +17,7 @@ class DropdownList(BaseComponent): методы для выбора и проверки элементов списка. """ - def __init__(self, page: Page): + def __init__(self, page: Page) -> None: """Инициализирует компонент выпадающего списка. Args: @@ -37,9 +37,39 @@ class DropdownList(BaseComponent): 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 = self.page.get_by_role("listitem").filter( + has_text=re.compile(rtext) + ) element.click() + def get_combobox_options(self, combobox_locator: str | Locator, + listbox_locator: str | Locator, + icon_locator: str | Locator = None) -> list[str]: + """ + Получает список доступных опций из combobox. + + Args: + combobox_locator: Локатор combobox + listbox_locator: Локатор выпадающего списка + icon_locator: Локатор иконки для клика (опционально) + + Returns: + list[str]: Список доступных опций + """ + logger.info("Getting combobox options list...") + + # Открываем combobox (если еще не открыт) + self.open_combobox(combobox_locator, listbox_locator, icon_locator) + + options_list = self.get_item_names(listbox_locator) + + # Закрываем combobox (кликаем вне его) + self.page.mouse.click(10, 10) + self.page.wait_for_timeout(500) + + logger.info(f"Found options: {len(options_list)} - {options_list}") + return options_list + def get_item_names(self, locator: str | Locator) -> list[str]: """Возвращает тексты всех элементов по указанному локатору. @@ -54,6 +84,90 @@ class DropdownList(BaseComponent): texts = loc.all_inner_texts() return texts[0].splitlines() + def get_selected_combobox_value(self, combobox_locator: str | Locator, + value_locator: str | Locator = None) -> str: + """ + Получает выбранное значение из combobox. + + Args: + combobox_locator: Локатор combobox + value_locator: Локатор элемента с выбранным значением (опционально) + + Returns: + str: Выбранное значение или пустая строка если ничего не выбрано + """ + combobox = self.get_locator(combobox_locator) + + selected_value = "" + + if value_locator: + # Используем переданный локатор для значения + value_element = combobox.locator(value_locator) + if value_element.count() > 0: + selected_value = value_element.first.text_content().strip() + else: + # Ищем в span элементах по умолчанию + span_locator = combobox.locator("span") + if span_locator.count() > 0: + for i in range(span_locator.count()): + span_text = span_locator.nth(i).text_content().strip() + if span_text and span_text not in ["Класс объекта учета"]: + selected_value = span_text + break + + logger.info(f"Selected combobox value: '{selected_value}'") + return selected_value + + def open_combobox(self, combobox_locator: str | Locator, + listbox_locator: str | Locator, + icon_locator: str | Locator = None) -> None: + """ + Открывает выпадающий список combobox. + + Args: + combobox_locator: Локатор combobox + listbox_locator: Локатор выпадающего списка + icon_locator: Локатор иконки для клика (опционально) + """ + logger.info("Opening combobox...") + + combobox = self.get_locator(combobox_locator) + listbox = self.get_locator(listbox_locator) + + # Прокручиваем до combobox + combobox.scroll_into_view_if_needed() + self.page.wait_for_timeout(1000) + + # Проверяем, не открыт ли уже список + listbox_already_open = False + listbox_count = listbox.count() + + if listbox_count > 0: + listbox_already_open = listbox.first.is_visible() + + if not listbox_already_open: + # Если указан локатор иконки, кликаем на него, иначе на сам combobox + if icon_locator: + icon = combobox.locator(icon_locator) + icon.scroll_into_view_if_needed() + icon.click(timeout=10000) + else: + combobox.click(timeout=10000) + logger.info("Combobox click completed") + self.page.wait_for_timeout(1000) + + # Проверяем что список открылся + listbox_count_after = listbox.count() + listbox_visible = False + + if listbox_count_after > 0: + listbox_visible = listbox.first.is_visible() + + if listbox_visible: + logger.info("Dropdown list found and opened") + else: + logger.warning("Failed to open dropdown list") + def scroll_until_end(self, locator: str | Locator) -> None: """ Скроллит список до тех пор, пока не перестанут подгружаться новые элементы. @@ -82,119 +196,14 @@ class DropdownList(BaseComponent): attempts = 0 last_item_name = item_names[current_count-1] - element = self.page.get_by_role("listitem").filter(has_text=last_item_name) + 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 open_combobox(self, combobox_locator: str | Locator, listbox_locator: str | Locator, icon_locator: str | Locator = None) -> None: - """ - Открывает выпадающий список combobox. - - Args: - combobox_locator: Локатор combobox - listbox_locator: Локатор выпадающего списка - icon_locator: Локатор иконки для клика (опционально) - """ - logger.info("Открытие combobox...") - - combobox = self.get_locator(combobox_locator) - listbox = self.get_locator(listbox_locator) - - # Прокручиваем до combobox - combobox.scroll_into_view_if_needed() - self.page.wait_for_timeout(1000) - - # Проверяем, не открыт ли уже список - listbox_already_open = False - listbox_count = listbox.count() - - if listbox_count > 0: - listbox_already_open = listbox.first.is_visible() - - if not listbox_already_open: - # Если указан локатор иконки, кликаем на него, иначе на сам combobox - if icon_locator: - icon = combobox.locator(icon_locator) - icon.scroll_into_view_if_needed() - icon.click(timeout=10000) - else: - combobox.click(timeout=10000) - logger.info("Клик на combobox выполнен") - self.page.wait_for_timeout(1000) - - # Проверяем что список открылся - listbox_count_after = listbox.count() - listbox_visible = False - - if listbox_count_after > 0: - listbox_visible = listbox.first.is_visible() - - if listbox_visible: - logger.info("Выпадающий список найден и открыт") - else: - logger.warning("Не удалось открыть выпадающий список") - - def get_combobox_options(self, combobox_locator: str | Locator, listbox_locator: str | Locator, icon_locator: str | Locator = None) -> list[str]: - """ - Получает список доступных опций из combobox. - - Args: - combobox_locator: Локатор combobox - listbox_locator: Локатор выпадающего списка - icon_locator: Локатор иконки для клика (опционально) - - Returns: - list[str]: Список доступных опций - """ - logger.info("Получение списка опций combobox...") - - # Открываем combobox (если еще не открыт) - self.open_combobox(combobox_locator, listbox_locator, icon_locator) - - options_list = self.get_item_names(listbox_locator) - - # Закрываем combobox (кликаем вне его) - self.page.mouse.click(10, 10) - self.page.wait_for_timeout(500) - - logger.info(f"Найдено опций: {len(options_list)} - {options_list}") - return options_list - - def get_selected_combobox_value(self, combobox_locator: str | Locator, value_locator: str | Locator = None) -> str: - """ - Получает выбранное значение из combobox. - - Args: - combobox_locator: Локатор combobox - value_locator: Локатор элемента с выбранным значением (опционально) - - Returns: - str: Выбранное значение или пустая строка если ничего не выбрано - """ - combobox = self.get_locator(combobox_locator) - - selected_value = "" - - if value_locator: - # Используем переданный локатор для значения - value_element = combobox.locator(value_locator) - if value_element.count() > 0: - selected_value = value_element.first.text_content().strip() - else: - # Ищем в span элементах по умолчанию - span_locator = combobox.locator("span") - if span_locator.count() > 0: - for i in range(span_locator.count()): - span_text = span_locator.nth(i).text_content().strip() - if span_text and span_text not in ["Класс объекта учета"]: # Можно сделать исключения параметром - selected_value = span_text - break - - logger.info(f"Выбранное значение combobox: '{selected_value}'") - return selected_value - # Проверки: def check_item_with_text(self, text: str) -> None: """Проверяет наличие и доступность элемента списка. @@ -209,7 +218,9 @@ class DropdownList(BaseComponent): 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 = 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"