e-nms_qa_automation/components/dropdown_list_component.py

242 lines
9.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""Модуль 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 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:
"""Проверяет наличие и доступность элемента списка.
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