Добавлена возможность полного открытия панели навигации на основе рекурсивного обхода дерева элементов

pull/1/head
nsubbot 2025-08-25 11:46:47 +03:00
parent eb0246e84a
commit 351b11573a
5 changed files with 135 additions and 4 deletions

View File

@ -34,6 +34,7 @@ class NavigationPanelComponent(BaseComponent):
loc = self.get_locator(locator) loc = self.get_locator(locator)
return loc.all_inner_texts() return loc.all_inner_texts()
def click_item(self, locator: str | Locator, item_name: str) -> None: def click_item(self, locator: str | Locator, item_name: str) -> None:
"""Кликает по элементу с указанным текстом. """Кликает по элементу с указанным текстом.
@ -69,6 +70,69 @@ class NavigationPanelComponent(BaseComponent):
else: else:
raise ValueError("the navigation panel has two levels of nesting only") raise ValueError("the navigation panel has two levels of nesting only")
def traverse_panel_tree(self, node_root_locator, level=0, debug=False):
"""
Рекурсивно обходит дерево v-treeview и выводит информацию об элементах в режиме отладки (debug=True).
Args:
node_root_locator: Локатор для поиска корневых элементов дерева (Локатор элемента или строка с CSS/XPath).
"""
def traverse_tree(page, root_locator, level=0, debug=False):
# Находим все локаторы корневых узлов на текущем уровне
nodes_count = root_locator.locator('>div.v-treeview-node').count()
for index in range(nodes_count):
node = root_locator.locator(f">div:nth-child({index + 1})").first
# Извлекаем текст и аттрибуты из корневого узла
node_text = node.inner_text()
node_class_attr = node.get_attribute('class')
is_expanded = False
has_children = False
# Проверяем лист это или начало поддерева
if "v-treeview-node--leaf" in node_class_attr:
if debug:
print(f'[{level}][{index}] {node_text} (LEAF, Expanded: {is_expanded}, Has Children: {has_children})')
print("-----------------------------------------")
else:
# Проверяем, является ли узел раскрытым
class_attr = node.locator(NavigationPanelLocators.TOGGLE_BUTTON).get_attribute('class')
if "v-treeview-node__toggle--open" in class_attr:
is_expanded = True
# Если узел закрыт можем его раскрыть
if is_expanded is False:
toggle_button = node.locator(NavigationPanelLocators.TOGGLE_BUTTON)
toggle_button.click()
# Ждем, пока дочерние элементы прогрузятся/появятся
page.wait_for_timeout(300)
is_expanded = True
# Проверяем, имеет ли узел дочерние элементы
children_count = node.locator('>div.v-treeview-node__children').count()
content = node.locator('>div.v-treeview-node__children').inner_html()
if children_count > 0 and len(content) != 0:
has_children = True
edited_node_text = node_text.replace("expand_more\n", "")
if debug:
# Выводим информацию об узле
print(f'[{level}][{index}] {edited_node_text} (NODE, Expanded: {is_expanded}, Has Children: {has_children})')
print("-----------------------------------------")
# Рекурсивный вызов для дочерних элементов
# Ищем дочерние элементы *внутри* текущего узла
if has_children and is_expanded:
child_nodes_locator = root_locator.locator(f">div:nth-child({index + 1})").locator('>div.v-treeview-node__children')
traverse_tree(page, child_nodes_locator, level+1, debug)
root_locator = self.get_locator(node_root_locator)
traverse_tree(self.page, root_locator, level=level, debug=debug)
# Проверки: # Проверки:
def check_item_visibility(self, locator: str | Locator, item_name: str) -> None: def check_item_visibility(self, locator: str | Locator, item_name: str) -> None:
"""Проверяет видимость элемента с указанным текстом. """Проверяет видимость элемента с указанным текстом.

View File

@ -10,12 +10,22 @@ class NavigationPanelLocators:
Содержит XPath локаторы для: Содержит XPath локаторы для:
PANEL_MAIN (str): основной панели навигации PANEL_MAIN (str): основной панели навигации
PANEL_SCROLL_CONTAINER (str): контейнера с прокруткой PANEL_SCROLL_CONTAINER (str): контейнера с прокруткой
ACTIVE_CONTAINER(str): активного контейнера, содержащего открываемые элементы панели навигации
TREEVIEW(str): корня объекта v-treeview
NODE(str): узла дерева
NODE_ROOT (str): корневого узла дерева NODE_ROOT (str): корневого узла дерева
NODE_CHILDREN (str): дочерних элементов узла NODE_CHILDREN (str): дочерних элементов узла
TOGGLE_BUTTON(str): кнопки раскрытия элементов панели навигации
""" """
PANEL_MAIN = "//ul[contains(@class, 'v-expansion-panel')]" PANEL_MAIN = "//ul[contains(@class, 'v-expansion-panel')]"
PANEL_SCROLL_CONTAINER = "//div[contains(@class, 'scrollarea__body') and .//ul[contains(@class, 'v-expansion-panel')]]" PANEL_SCROLL_CONTAINER = "//div[contains(@class, 'scrollarea__body') and .//ul[contains(@class, 'v-expansion-panel')]]"
ACTIVE_CONTAINER = "//li[contains(@class, 'v-expansion-panel__container--active')]"
SUB_PANEL_MAIN = "//div[contains(@class,'v-expansion-panel__body')]"
TREEVIEW = "//div[contains(@class,'v-treeview')]"
NODE = "//div[contains(@class,'v-treeview-node')]"
NODE_ROOT = "//div[contains(@class,'v-treeview-node__root')]" NODE_ROOT = "//div[contains(@class,'v-treeview-node__root')]"
NODE_CHILDREN = "//div[contains(@class,'v-treeview-node__children')]" NODE_CHILDREN = "//div[contains(@class,'v-treeview-node__children')]"
TOGGLE_BUTTON = "//i[contains(@class,'v-treeview-node__toggle')]"

View File

@ -79,6 +79,15 @@ class MainPage(BasePage):
self.click_user_button() self.click_user_button()
self.user_card.click_logout_button() self.user_card.click_logout_button()
def expand_navigation_subpanel(self):
"""Выполняет полное открытие активной главной навигационной подпанели."""
active_item_locator = self.page.locator(NavigationPanelLocators.PANEL_MAIN).locator(NavigationPanelLocators.ACTIVE_CONTAINER)
node_locator = active_item_locator.locator(NavigationPanelLocators.SUB_PANEL_MAIN).locator(NavigationPanelLocators.TREEVIEW).first
# Рекурсивный обход дерева v-treeview выбранной подпанели и вывод информации об элементах в режиме отладки (debug=True)
self.navigation_panel.traverse_panel_tree(node_locator, debug=False)
def scroll_navigation_panel_up(self) -> None: def scroll_navigation_panel_up(self) -> None:
"""Прокручивает панель навигации вверх.""" """Прокручивает панель навигации вверх."""

View File

@ -41,10 +41,11 @@ class TestNavigationPanel:
# Открываем все пункты панели # Открываем все пункты панели
mp.click_main_navigation_panel_item("Настройки") mp.click_main_navigation_panel_item("Настройки")
mp.click_configuration_navigation_panel_item("Аутентификация") mp.expand_navigation_subpanel()
mp.click_configuration_navigation_panel_item("Уведомления")
mp.click_configuration_navigation_panel_item("Обслуживание и диагностика") mp.click_main_navigation_panel_item("Объекты")
mp.click_configuration_navigation_panel_item("Zero Touch Provisioning") mp.wait_for_timeout(300)
mp.expand_navigation_subpanel()
# Проверяем возможность вертикальной прокрутки # Проверяем возможность вертикальной прокрутки
is_scrollable = mp.check_navigation_panel_verticall_scrolling() is_scrollable = mp.check_navigation_panel_verticall_scrolling()

View File

@ -0,0 +1,47 @@
"""Модуль тестов панели навигации.
Содержит тесты для проверки функциональности
панели навигации в приложении.
"""
from playwright.sync_api import Page
from pages.main_page import MainPage
from pages.login_page import LoginPage
# @pytest.mark.smoke
class TestNavigationPanel:
"""Класс тестов для проверки панели навигации.
Атрибуты:
browser: Фикстура для работы с браузером.
"""
def test_expand_panel(self, browser: Page):
"""Проверяет полное раскрытие панели навигации.
Args:
browser: Фикстура для работы с браузером.
"""
# Действия:
lp = LoginPage(browser)
lp.do_login()
# Мы на главной странице
mp = MainPage(browser)
# Проверки:
# Проверяем наличие панели навигации
mp.should_be_navigation_panel()
# Открываем все пункты панели
mp.click_main_navigation_panel_item("Настройки")
mp.expand_navigation_subpanel()
mp.click_main_navigation_panel_item("Объекты")
mp.wait_for_timeout(300)
mp.expand_navigation_subpanel()