From 351b11573aeebfd89041d18536724fb0a6a8412e Mon Sep 17 00:00:00 2001 From: nsubbot Date: Mon, 25 Aug 2025 11:46:47 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B2=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6=D0=BD=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D1=8C=20=D0=BF=D0=BE=D0=BB=D0=BD=D0=BE=D0=B3=D0=BE?= =?UTF-8?q?=20=D0=BE=D1=82=D0=BA=D1=80=D1=8B=D1=82=D0=B8=D1=8F=20=D0=BF?= =?UTF-8?q?=D0=B0=D0=BD=D0=B5=D0=BB=D0=B8=20=D0=BD=D0=B0=D0=B2=D0=B8=D0=B3?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BD=D0=B0=20=D0=BE=D1=81=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=B5=20=D1=80=D0=B5=D0=BA=D1=83=D1=80=D1=81=D0=B8?= =?UTF-8?q?=D0=B2=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=BE=D0=B1=D1=85=D0=BE=D0=B4?= =?UTF-8?q?=D0=B0=20=D0=B4=D0=B5=D1=80=D0=B5=D0=B2=D0=B0=20=D1=8D=D0=BB?= =?UTF-8?q?=D0=B5=D0=BC=D0=B5=D0=BD=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/navbar_component.py | 64 +++++++++++++++++++++++ locators/navigation_panel_locators.py | 10 ++++ pages/main_page.py | 9 ++++ tests/components/test_navigation_panel.py | 9 ++-- tests/e2e/test_expand_navigation_panel.py | 47 +++++++++++++++++ 5 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 tests/e2e/test_expand_navigation_panel.py diff --git a/components/navbar_component.py b/components/navbar_component.py index 4c2d99b..7119d90 100644 --- a/components/navbar_component.py +++ b/components/navbar_component.py @@ -34,6 +34,7 @@ class NavigationPanelComponent(BaseComponent): loc = self.get_locator(locator) return loc.all_inner_texts() + def click_item(self, locator: str | Locator, item_name: str) -> None: """Кликает по элементу с указанным текстом. @@ -69,6 +70,69 @@ class NavigationPanelComponent(BaseComponent): else: 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: """Проверяет видимость элемента с указанным текстом. diff --git a/locators/navigation_panel_locators.py b/locators/navigation_panel_locators.py index a299cea..f0d2ffe 100644 --- a/locators/navigation_panel_locators.py +++ b/locators/navigation_panel_locators.py @@ -10,12 +10,22 @@ class NavigationPanelLocators: Содержит XPath локаторы для: PANEL_MAIN (str): основной панели навигации PANEL_SCROLL_CONTAINER (str): контейнера с прокруткой + ACTIVE_CONTAINER(str): активного контейнера, содержащего открываемые элементы панели навигации + TREEVIEW(str): корня объекта v-treeview + NODE(str): узла дерева NODE_ROOT (str): корневого узла дерева NODE_CHILDREN (str): дочерних элементов узла + TOGGLE_BUTTON(str): кнопки раскрытия элементов панели навигации """ PANEL_MAIN = "//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_CHILDREN = "//div[contains(@class,'v-treeview-node__children')]" + TOGGLE_BUTTON = "//i[contains(@class,'v-treeview-node__toggle')]" diff --git a/pages/main_page.py b/pages/main_page.py index eda1406..bd199bb 100644 --- a/pages/main_page.py +++ b/pages/main_page.py @@ -79,6 +79,15 @@ class MainPage(BasePage): self.click_user_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: """Прокручивает панель навигации вверх.""" diff --git a/tests/components/test_navigation_panel.py b/tests/components/test_navigation_panel.py index 2434e96..560e356 100644 --- a/tests/components/test_navigation_panel.py +++ b/tests/components/test_navigation_panel.py @@ -41,10 +41,11 @@ class TestNavigationPanel: # Открываем все пункты панели mp.click_main_navigation_panel_item("Настройки") - mp.click_configuration_navigation_panel_item("Аутентификация") - mp.click_configuration_navigation_panel_item("Уведомления") - mp.click_configuration_navigation_panel_item("Обслуживание и диагностика") - mp.click_configuration_navigation_panel_item("Zero Touch Provisioning") + mp.expand_navigation_subpanel() + + mp.click_main_navigation_panel_item("Объекты") + mp.wait_for_timeout(300) + mp.expand_navigation_subpanel() # Проверяем возможность вертикальной прокрутки is_scrollable = mp.check_navigation_panel_verticall_scrolling() diff --git a/tests/e2e/test_expand_navigation_panel.py b/tests/e2e/test_expand_navigation_panel.py new file mode 100644 index 0000000..963b624 --- /dev/null +++ b/tests/e2e/test_expand_navigation_panel.py @@ -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()