feat: добавление метода is_item_visible в навигационную панель

- Добавлен метод для проверки видимости элемента без исключений
- Возвращает boolean значение для условных проверок
radislav/tests_rack
Radislav 2025-11-25 13:59:33 +03:00
parent 610d13575d
commit 27ca4596fa
1 changed files with 69 additions and 39 deletions

View File

@ -21,20 +21,6 @@ class NavigationPanelComponent(BaseComponent):
super().__init__(page) super().__init__(page)
# Действия: # Действия:
def get_item_names(self, locator: str | Locator) -> list[str]:
"""Возвращает тексты всех элементов по указанному локатору.
Args:
locator: Локатор элементов или строка с CSS/XPath.
Returns:
Список текстов элементов.
"""
loc = self.get_locator(locator)
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:
"""Кликает по элементу с указанным текстом. """Кликает по элементу с указанным текстом.
@ -50,13 +36,12 @@ class NavigationPanelComponent(BaseComponent):
"""Кликает по вложенному элементу с указанным текстом. """Кликает по вложенному элементу с указанным текстом.
Args: Args:
node_root_locator: Локатор для поиска корневых элементов дерева (Локатор элемента или строка с CSS/XPath). node_root_locator: Локатор для поиска корневых элементов дерева.
item_name: Текст элемента для клика. item_name: Текст элемента для клика.
""" """
def find_and_click_item(page, root_locator, item_name: str, parent: None|str) -> Locator|None: def find_and_click_item(page, root_locator, item_name: str, parent: None|str) -> Locator|None:
# Находим все локаторы корневых узлов на текущем уровне # Находим все локаторы корневых узлов на текущем уровне
#root_node = root_locator.locator('>div.v-treeview-node')
nodes_count = root_locator.locator('>div.v-treeview-node').count() nodes_count = root_locator.locator('>div.v-treeview-node').count()
# Если искомый элемент находится на данном уровне, вычисляем локатор и делаем клик # Если искомый элемент находится на данном уровне, вычисляем локатор и делаем клик
@ -69,9 +54,9 @@ class NavigationPanelComponent(BaseComponent):
if item_name == node_text: if item_name == node_text:
node_attr = node.get_attribute('class') node_attr = node.get_attribute('class')
if "v-treeview-node--leaf" not in node_attr: if "v-treeview-node--leaf" not in node_attr:
toggle_button = node.\ toggle_button = node.locator(
locator(NavigationPanelLocators.NODE_ROOT). \ NavigationPanelLocators.NODE_ROOT
locator(NavigationPanelLocators.TOGGLE_BUTTON).first ).locator(NavigationPanelLocators.TOGGLE_BUTTON).first
toogle_class_attr = toggle_button.get_attribute('class') toogle_class_attr = toggle_button.get_attribute('class')
if "v-treeview-node__toggle--open" not in toogle_class_attr: if "v-treeview-node__toggle--open" not in toogle_class_attr:
toggle_button.click() toggle_button.click()
@ -93,17 +78,17 @@ class NavigationPanelComponent(BaseComponent):
# Проверяем лист это или начало поддерева # Проверяем лист это или начало поддерева
if "v-treeview-node--leaf" not in node_class_attr: if "v-treeview-node--leaf" not in node_class_attr:
# Проверяем, является ли узел раскрытым # Проверяем, является ли узел раскрытым
class_attr = node.\ class_attr = node.locator(
locator(NavigationPanelLocators.NODE_ROOT). \ NavigationPanelLocators.NODE_ROOT
locator(NavigationPanelLocators.TOGGLE_BUTTON).first.get_attribute('class') ).locator(NavigationPanelLocators.TOGGLE_BUTTON).first.get_attribute('class')
if "v-treeview-node__toggle--open" in class_attr: if "v-treeview-node__toggle--open" in class_attr:
is_expanded = True is_expanded = True
# Если узел закрыт можем его раскрыть # Если узел закрыт можем его раскрыть
if is_expanded is False: if is_expanded is False:
toggle_button = node.\ toggle_button = node.locator(
locator(NavigationPanelLocators.NODE_ROOT). \ NavigationPanelLocators.NODE_ROOT
locator(NavigationPanelLocators.TOGGLE_BUTTON).first ).locator(NavigationPanelLocators.TOGGLE_BUTTON).first
toggle_button.click() toggle_button.click()
# Ждем, пока дочерние элементы прогрузятся/появятся # Ждем, пока дочерние элементы прогрузятся/появятся
page.wait_for_timeout(1000) page.wait_for_timeout(1000)
@ -118,21 +103,27 @@ class NavigationPanelComponent(BaseComponent):
# Рекурсивный вызов для дочерних элементов # Рекурсивный вызов для дочерних элементов
# Ищем дочерние элементы *внутри* текущего узла # Ищем дочерние элементы *внутри* текущего узла
if has_children and is_expanded: if has_children and is_expanded:
child_nodes_locator = root_locator.locator(f">div:nth-child({index + 1})").locator('>div.v-treeview-node__children') child_nodes_locator = root_locator.locator(
found_loc = find_and_click_item(page, child_nodes_locator, item_name, parent=None) 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 found_loc:
if parent is None: if parent is None:
return found_loc return found_loc
else: else:
root_texts = root_locator.locator(f">div:nth-child({index + 1})").inner_text().splitlines() root_texts = root_locator.locator(
f">div:nth-child({index + 1})"
).inner_text().splitlines()
if parent in root_texts: if parent in root_texts:
return found_loc return found_loc
# закрываем узел, если в нем ничего не нашли # закрываем узел, если в нем ничего не нашли
if is_expanded: if is_expanded:
toggle_button = node.\ toggle_button = node.locator(
locator(NavigationPanelLocators.NODE_ROOT). \ NavigationPanelLocators.NODE_ROOT
locator(NavigationPanelLocators.TOGGLE_BUTTON).first ).locator(NavigationPanelLocators.TOGGLE_BUTTON).first
toggle_button.click() toggle_button.click()
page.wait_for_timeout(1000) page.wait_for_timeout(1000)
@ -142,17 +133,33 @@ class NavigationPanelComponent(BaseComponent):
root_locator = self.get_locator(node_root_locator) root_locator = self.get_locator(node_root_locator)
if parent: if parent:
parent_loc = find_and_click_item(self.page, root_locator, parent, parent=None) parent_loc = find_and_click_item(self.page, root_locator, parent, parent=None)
found = find_and_click_item(self.page, parent_loc.locator('>div.v-treeview-node__children'), item_name, parent=None) found = find_and_click_item(
self.page, parent_loc.locator('>div.v-treeview-node__children'),
item_name, parent=None
)
else: else:
found = find_and_click_item(self.page, root_locator, item_name, parent=None) found = find_and_click_item(self.page, root_locator, item_name, parent=None)
assert found, f"Navigation panel item {item_name} is missing" assert found, f"Navigation panel item {item_name} is missing"
def traverse_panel_tree(self, node_root_locator: str | Locator, level=0, debug=False): def get_item_names(self, locator: str | Locator) -> list[str]:
""" """Возвращает тексты всех элементов по указанному локатору.
Рекурсивно обходит дерево v-treeview и выводит информацию об элементах в режиме отладки (debug=True).
Args: Args:
node_root_locator: Локатор для поиска корневых элементов дерева (Локатор элемента или строка с CSS/XPath). locator: Локатор элементов или строка с CSS/XPath.
Returns:
Список текстов элементов.
"""
loc = self.get_locator(locator)
return loc.all_inner_texts()
def traverse_panel_tree(self, node_root_locator: str | Locator, level=0, debug=False):
"""
Рекурсивно обходит дерево v-treeview и выводит информацию об элементах.
Args:
node_root_locator: Локатор для поиска корневых элементов дерева.
""" """
def traverse_tree(page, root_locator, level=0, debug=False): def traverse_tree(page, root_locator, level=0, debug=False):
# Находим все локаторы корневых узлов на текущем уровне # Находим все локаторы корневых узлов на текущем уровне
@ -171,7 +178,8 @@ class NavigationPanelComponent(BaseComponent):
# Проверяем лист это или начало поддерева # Проверяем лист это или начало поддерева
if "v-treeview-node--leaf" in node_class_attr: if "v-treeview-node--leaf" in node_class_attr:
if debug: if debug:
print(f'[{level}][{index}] {node_text} (LEAF, Expanded: {is_expanded}, Has Children: {has_children})') leaf_msg = f'[{level}][{index}] {node_text} (LEAF, Expanded: {is_expanded}'
print(f"{leaf_msg}, Has Children: {has_children})")
print("-----------------------------------------") print("-----------------------------------------")
else: else:
# Проверяем, является ли узел раскрытым # Проверяем, является ли узел раскрытым
@ -198,13 +206,16 @@ class NavigationPanelComponent(BaseComponent):
if debug: if debug:
# Выводим информацию об узле # Выводим информацию об узле
print(f'[{level}][{index}] {edited_node_text} (NODE, Expanded: {is_expanded}, Has Children: {has_children})') node_msg = f'[{level}][{index}] {edited_node_text} (NODE, Expanded: {is_expanded}'
print(f"{node_msg}, Has Children: {has_children})")
print("-----------------------------------------") print("-----------------------------------------")
# Рекурсивный вызов для дочерних элементов # Рекурсивный вызов для дочерних элементов
# Ищем дочерние элементы *внутри* текущего узла # Ищем дочерние элементы *внутри* текущего узла
if has_children and is_expanded: if has_children and is_expanded:
child_nodes_locator = root_locator.locator(f">div:nth-child({index + 1})").locator('>div.v-treeview-node__children') 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) traverse_tree(page, child_nodes_locator, level+1, debug)
root_locator = self.get_locator(node_root_locator) root_locator = self.get_locator(node_root_locator)
@ -235,3 +246,22 @@ class NavigationPanelComponent(BaseComponent):
else: else:
loc = loc.get_by_text(item_name) loc = loc.get_by_text(item_name)
self.check_visibility(loc, msg) self.check_visibility(loc, msg)
def is_item_visible(self, locator: str | Locator, item_name: str) -> bool:
"""
Проверяет видимость элемента с указанным текстом без выбрасывания исключения.
Args:
locator: Локатор элемента или строка с CSS/XPath.
item_name: Текст элемента для проверки.
Returns:
bool: True если элемент видим, False если нет.
"""
element_locator = self.page.locator(locator).filter(has_text=item_name)
# Сначала проверяем что элемент вообще существует
if element_locator.count() == 0:
return False
return element_locator.is_visible()