diff --git a/components/navbar_component.py b/components/navbar_component.py index 989a3bc..51fea43 100644 --- a/components/navbar_component.py +++ b/components/navbar_component.py @@ -49,110 +49,112 @@ class NavigationPanelComponent(BaseComponent): item_name: Текст элемента для клика. """ - def find_and_click_item(page, root_locator, item_name: str, parent: None|str) -> Locator|None: - # Находим все локаторы корневых узлов на текущем уровне - nodes_count = root_locator.locator('>div.v-treeview-node').count() - - # Если искомый элемент находится на данном уровне, вычисляем локатор и делаем клик - if parent is None: - for index in range(nodes_count): - node = root_locator.locator(f">div:nth-child({index + 1})").first - node_content = node.locator('div.v-treeview-node__content') - if node_content.count() > 0: - node_text = node_content.first.inner_text().strip() - node_texts = node_text.splitlines() - if len(node_texts) > 1: - node_text = node_texts[1] - if item_name == node_text: - node_attr = node.get_attribute('class') - if "v-treeview-node--leaf" not in node_attr: - toggle_button = node.locator( - NavigationPanelLocators.NODE_ROOT - ).locator(NavigationPanelLocators.TOGGLE_BUTTON).first - toogle_class_attr = toggle_button.get_attribute('class') - if "v-treeview-node__toggle--open" not in toogle_class_attr: - toggle_button.click() - else: - node.locator(NavigationPanelLocators.NODE_ROOT).click() - page.wait_for_timeout(1000) - return node - - # Если элемента нет, рекурсивно ищем дальше - for index in range(nodes_count): - node = root_locator.locator(f">div:nth-child({index + 1})").first - - # Извлекаем аттрибуты из корневого узла - node_class_attr = node.get_attribute('class') - - is_expanded = False - has_children = False - - # Проверяем лист это или начало поддерева - if "v-treeview-node--leaf" not in node_class_attr: - # Проверяем, является ли узел раскрытым - class_attr = node.locator( - NavigationPanelLocators.NODE_ROOT - ).locator(NavigationPanelLocators.TOGGLE_BUTTON).first.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.NODE_ROOT - ).locator(NavigationPanelLocators.TOGGLE_BUTTON).first - toggle_button.click() - # Ждем, пока дочерние элементы прогрузятся/появятся - page.wait_for_timeout(1000) - 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 - - # Рекурсивный вызов для дочерних элементов - # Ищем дочерние элементы *внутри* текущего узла - if has_children and is_expanded: - child_nodes_locator = root_locator.locator( - f">div:nth-child({index + 1})" - ).locator('>div.v-treeview-node__children') - found_loc = find_and_click_item( - page, child_nodes_locator, item_name, parent=None - ) - if found_loc: - if parent is None: - return found_loc - - root_texts = root_locator.locator( - f">div:nth-child({index + 1})" - ).inner_text().splitlines() - if parent in root_texts: - return found_loc - - # закрываем узел, если в нем ничего не нашли - if is_expanded: - toggle_button = node.locator( - NavigationPanelLocators.NODE_ROOT - ).locator(NavigationPanelLocators.TOGGLE_BUTTON).first - toggle_button.click() - page.wait_for_timeout(1000) - - # элемент с заданным именем не найден - return None - root_locator = self.get_locator(node_root_locator) if parent: - parent_loc = find_and_click_item(self.page, root_locator, parent, parent=None) - found = find_and_click_item( + parent_loc = self._find_and_click_item(self.page, root_locator, parent, parent=None) + found = self._find_and_click_item( self.page, parent_loc.locator('>div.v-treeview-node__children'), item_name, parent=None ) else: - found = find_and_click_item(self.page, root_locator, item_name, parent=None) + found = self._find_and_click_item(self.page, root_locator, item_name, parent=None) assert found, f"Navigation panel item {item_name} is missing" + def _find_and_click_item(self, page, root_locator, item_name: str, parent: None|str) -> Locator|None: + """Поиск вложенного элемента с указанным текстом и локатором корневого элемента""" + + # Находим все локаторы корневых узлов на текущем уровне + nodes_count = root_locator.locator('>div.v-treeview-node').count() + + # Если искомый элемент находится на данном уровне, вычисляем локатор и делаем клик + if parent is None: + for index in range(nodes_count): + node = root_locator.locator(f">div:nth-child({index + 1})").first + node_content = node.locator('div.v-treeview-node__content') + if node_content.count() > 0: + node_text = node_content.first.inner_text().strip() + node_texts = node_text.splitlines() + if len(node_texts) > 1: + node_text = node_texts[1] + if item_name == node_text: + node_attr = node.get_attribute('class') + if "v-treeview-node--leaf" not in node_attr: + toggle_button = node.locator( + NavigationPanelLocators.NODE_ROOT + ).locator(NavigationPanelLocators.TOGGLE_BUTTON).first + toogle_class_attr = toggle_button.get_attribute('class') + if "v-treeview-node__toggle--open" not in toogle_class_attr: + toggle_button.click() + else: + node.locator(NavigationPanelLocators.NODE_ROOT).click() + page.wait_for_timeout(1000) + return node + + # Если элемента нет, рекурсивно ищем дальше + for index in range(nodes_count): + node = root_locator.locator(f">div:nth-child({index + 1})").first + + # Извлекаем аттрибуты из корневого узла + node_class_attr = node.get_attribute('class') + + is_expanded = False + has_children = False + + # Проверяем лист это или начало поддерева + if "v-treeview-node--leaf" not in node_class_attr: + # Проверяем, является ли узел раскрытым + class_attr = node.locator( + NavigationPanelLocators.NODE_ROOT + ).locator(NavigationPanelLocators.TOGGLE_BUTTON).first.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.NODE_ROOT + ).locator(NavigationPanelLocators.TOGGLE_BUTTON).first + toggle_button.click() + # Ждем, пока дочерние элементы прогрузятся/появятся + page.wait_for_timeout(1000) + 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 + + # Рекурсивный вызов для дочерних элементов + # Ищем дочерние элементы *внутри* текущего узла + if has_children and is_expanded: + child_nodes_locator = root_locator.locator( + f">div:nth-child({index + 1})" + ).locator('>div.v-treeview-node__children') + found_loc = self._find_and_click_item( + page, child_nodes_locator, item_name, parent=None + ) + if found_loc: + if parent is None: + return found_loc + + root_texts = root_locator.locator( + f">div:nth-child({index + 1})" + ).inner_text().splitlines() + if parent in root_texts: + return found_loc + + # закрываем узел, если в нем ничего не нашли + if is_expanded: + toggle_button = node.locator( + NavigationPanelLocators.NODE_ROOT + ).locator(NavigationPanelLocators.TOGGLE_BUTTON).first + toggle_button.click() + page.wait_for_timeout(1000) + + # элемент с заданным именем не найден + return None + def get_item_names(self, locator: str | Locator) -> list[str]: """Возвращает тексты всех элементов по указанному локатору. @@ -294,6 +296,32 @@ class NavigationPanelComponent(BaseComponent): return element_locator.is_visible() + def check_sub_item_state(self, node_root_locator: str | Locator, item_name: str, parent: None|str) -> str|None: + """Выполняет рекурсивный поиск по панели навигации + заданного элемента, делает клик по нему, проверяет наличие индикатора состояния. + Если индикатор состояния присутствует, возвращается его цвет. Иначе None""" + + root_locator = self.get_locator(node_root_locator) + if parent: + parent_loc = self._find_and_click_item(self.page, root_locator, parent, parent=None) + found_node_loc = self._find_and_click_item( + self.page, parent_loc.locator('>div.v-treeview-node__children'), + item_name, parent=None + ) + else: + found_node_loc = self._find_and_click_item(self.page, root_locator, item_name, parent=None) + + assert found_node_loc, f"Navigation panel item {item_name} is missing" + + color = None + sub_item_state_loc_str = f"//span[text()='{item_name}']/preceding-sibling::*[name()='svg'][2]" + sub_item_state_locator = found_node_loc.locator("div.v-treeview-node__label").locator(sub_item_state_loc_str) + + if sub_item_state_locator.count() > 0: + color = sub_item_state_locator.get_attribute("fill") + if color: color = color.lstrip('#') + return color + def should_be_expand_workarea_button(self) -> None: """Проверяет наличие кнопки расширения рабочей области страницы. diff --git a/pages/main_page.py b/pages/main_page.py index c510328..7643504 100644 --- a/pages/main_page.py +++ b/pages/main_page.py @@ -217,6 +217,25 @@ class MainPage(BasePage): item_name ) + def check_subpanel_item_state(self, item_name: str, parent=None) -> str|None: + """Выполняет рекурсивный поиск по панели навигации + заданного элемента, делает клик по нему, проверяет наличие индикатора состояния. + Если индикатор состояния присутствует, возвращается его цвет. Иначе None""" + + 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 заданного элемента + # и клик по нему + return self.navigation_panel.check_sub_item_state( + node_locator, item_name, parent + ) + + def check_navigation_panel_verticall_scrolling(self) -> bool: """Проверяет возможность вертикальной прокрутки панели. diff --git a/tests/e2e/test_expand_navigation_panel.py b/tests/e2e/test_expand_navigation_panel.py index 59ca04f..0ab6506 100644 --- a/tests/e2e/test_expand_navigation_panel.py +++ b/tests/e2e/test_expand_navigation_panel.py @@ -102,7 +102,7 @@ class TestNavigationPanel: mp.wait_for_timeout(5000) - mp.click_subpanel_item("test-zone-01") + mp.click_subpanel_item("test-zone") mp.wait_for_timeout(3000) # Переходим к Стойке @@ -128,3 +128,41 @@ class TestNavigationPanel: # # Переходим к Стойка систем питания с указанием родителя # mp.click_subpanel_item("Стойка систем питания", parent="Виртуальные устройства") # mp.wait_for_timeout(5000) + + @pytest.mark.develop + def test_check_sub_panel_item_state(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.click_subpanel_item("Обслуживание и диагностика") + state_color = mp.check_subpanel_item_state("Сеансы") + assert not state_color, "Got state color but subpanel item 'Сеансы' has no state indicator" + mp.wait_for_timeout(2000) + + # Переходим к Объектам + mp.click_main_navigation_panel_item("Объекты") + mp.wait_for_timeout(5000) + + state_color = mp.check_subpanel_item_state("test-zone") + assert state_color, "State indicator has not found for subpanel item 'test-zone'" + + state_color = mp.check_subpanel_item_state("Test-Rack-01") + assert state_color, "State indicator has not found for subpanel item 'Test-Rack-01'"