Skip to content

NavigationPanelComponent

Модуль компонента панели навигации. Содержит класс для работы с элементами навигации.

NavigationPanelComponent

Bases: BaseComponent

Компонент панели навигации. Предоставляет методы для взаимодействия с ней.

Source code in components\navbar_component.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
class NavigationPanelComponent(BaseComponent):
    """Компонент панели навигации. Предоставляет методы для взаимодействия с ней."""

    def __init__(self, page: Page):
        """Инициализирует компонент панели навигации.

        Args:
            page: Экземпляр страницы Playwright.
        """

        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:
        """Кликает по элементу с указанным текстом.

        Args:
            locator: Локатор элемента или строка с CSS/XPath.
            item_name: Текст элемента для клика.
        """

        loc = self.get_locator(locator)
        loc.get_by_text(item_name).click()

    def click_sub_item(self, node_root_locator: str | Locator, item_name: str) -> None:
        """Кликает по вложенному элементу с указанным текстом.

        Args:
            node_root_locator: Локатор для поиска корневых элементов дерева (Локатор элемента или строка с CSS/XPath).
            item_name: Текст элемента для клика.
        """

        def find_and_click_item(page, root_locator, item_name: str) -> bool:
            # Находим все локаторы корневых узлов на текущем уровне
            root_node = root_locator.locator('>div.v-treeview-node')
            # Получаем список текстов
            root_node_texts = root_node.all_inner_texts()

            # Если искомый элемент находится на данном уровне, вычисляем локатор и делаем клик
            for index, node_text in enumerate(root_node_texts):
                node_text = node_text.replace("expand_more\n", "")
                if item_name == node_text:
                   root_node.nth(index).click()
                   return True

            # Если элемента нет, рекурсивно ищем дальше
            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_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.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

                    # Рекурсивный вызов для дочерних элементов
                    # Ищем дочерние элементы *внутри* текущего узла
                    if has_children and is_expanded:
                        child_nodes_locator = root_locator.locator(f">div:nth-child({index + 1})").locator('>div.v-treeview-node__children')
                        is_found = find_and_click_item(page, child_nodes_locator, item_name)
                        if is_found:
                            return True

                    # закрываем узел, если в нем ничего не нашли
                    if is_expanded:
                        toggle_button = node.locator(NavigationPanelLocators.TOGGLE_BUTTON)
                        toggle_button.click()

            # элемент с заданным именем не найден
            return False

        root_locator = self.get_locator(node_root_locator)
        found = find_and_click_item(self.page, root_locator, item_name)
        assert found, f"Navigation panel item {item_name} is missing"

    def traverse_panel_tree(self, node_root_locator: str | 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:
        """Проверяет видимость элемента с указанным текстом.

        Args:
            locator: Локатор элемента или строка с CSS/XPath.
            item_name: Текст элемента для проверки.

        Note:
            Временная обработка для элементов с текстом 'Шаблоны'.
        """

        msg = f"Navigation panel item '{item_name}' is not visible"

        ## временно: в навигационной панели есть две панели с именем Шаблоны
        ## для их различия добавлены индексы Шаблоны_1 для Настройки/Шаблоны
        ## Шаблоны_2 для Настройки/ZTP/Шаблоны
        loc = self.get_locator(locator)
        if item_name == "Шаблоны_1":
            loc = loc.get_by_text("Шаблоны").first
        elif item_name == "Шаблоны_2":
            loc = loc.get_by_text("Шаблоны").nth(1)
        else:
            loc = loc.get_by_text(item_name)
        self.check_visibility(loc, msg)

__init__(page)

Инициализирует компонент панели навигации.

Parameters:

Name Type Description Default
page Page

Экземпляр страницы Playwright.

required
Source code in components\navbar_component.py
14
15
16
17
18
19
20
21
def __init__(self, page: Page):
    """Инициализирует компонент панели навигации.

    Args:
        page: Экземпляр страницы Playwright.
    """

    super().__init__(page)

check_item_visibility(locator, item_name)

Проверяет видимость элемента с указанным текстом.

Parameters:

Name Type Description Default
locator str | Locator

Локатор элемента или строка с CSS/XPath.

required
item_name str

Текст элемента для проверки.

required
Note

Временная обработка для элементов с текстом 'Шаблоны'.

Source code in components\navbar_component.py
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
def check_item_visibility(self, locator: str | Locator, item_name: str) -> None:
    """Проверяет видимость элемента с указанным текстом.

    Args:
        locator: Локатор элемента или строка с CSS/XPath.
        item_name: Текст элемента для проверки.

    Note:
        Временная обработка для элементов с текстом 'Шаблоны'.
    """

    msg = f"Navigation panel item '{item_name}' is not visible"

    ## временно: в навигационной панели есть две панели с именем Шаблоны
    ## для их различия добавлены индексы Шаблоны_1 для Настройки/Шаблоны
    ## Шаблоны_2 для Настройки/ZTP/Шаблоны
    loc = self.get_locator(locator)
    if item_name == "Шаблоны_1":
        loc = loc.get_by_text("Шаблоны").first
    elif item_name == "Шаблоны_2":
        loc = loc.get_by_text("Шаблоны").nth(1)
    else:
        loc = loc.get_by_text(item_name)
    self.check_visibility(loc, msg)

click_item(locator, item_name)

Кликает по элементу с указанным текстом.

Parameters:

Name Type Description Default
locator str | Locator

Локатор элемента или строка с CSS/XPath.

required
item_name str

Текст элемента для клика.

required
Source code in components\navbar_component.py
38
39
40
41
42
43
44
45
46
47
def click_item(self, locator: str | Locator, item_name: str) -> None:
    """Кликает по элементу с указанным текстом.

    Args:
        locator: Локатор элемента или строка с CSS/XPath.
        item_name: Текст элемента для клика.
    """

    loc = self.get_locator(locator)
    loc.get_by_text(item_name).click()

click_sub_item(node_root_locator, item_name)

Кликает по вложенному элементу с указанным текстом.

Parameters:

Name Type Description Default
node_root_locator str | Locator

Локатор для поиска корневых элементов дерева (Локатор элемента или строка с CSS/XPath).

required
item_name str

Текст элемента для клика.

required
Source code in components\navbar_component.py
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
def click_sub_item(self, node_root_locator: str | Locator, item_name: str) -> None:
    """Кликает по вложенному элементу с указанным текстом.

    Args:
        node_root_locator: Локатор для поиска корневых элементов дерева (Локатор элемента или строка с CSS/XPath).
        item_name: Текст элемента для клика.
    """

    def find_and_click_item(page, root_locator, item_name: str) -> bool:
        # Находим все локаторы корневых узлов на текущем уровне
        root_node = root_locator.locator('>div.v-treeview-node')
        # Получаем список текстов
        root_node_texts = root_node.all_inner_texts()

        # Если искомый элемент находится на данном уровне, вычисляем локатор и делаем клик
        for index, node_text in enumerate(root_node_texts):
            node_text = node_text.replace("expand_more\n", "")
            if item_name == node_text:
               root_node.nth(index).click()
               return True

        # Если элемента нет, рекурсивно ищем дальше
        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_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.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

                # Рекурсивный вызов для дочерних элементов
                # Ищем дочерние элементы *внутри* текущего узла
                if has_children and is_expanded:
                    child_nodes_locator = root_locator.locator(f">div:nth-child({index + 1})").locator('>div.v-treeview-node__children')
                    is_found = find_and_click_item(page, child_nodes_locator, item_name)
                    if is_found:
                        return True

                # закрываем узел, если в нем ничего не нашли
                if is_expanded:
                    toggle_button = node.locator(NavigationPanelLocators.TOGGLE_BUTTON)
                    toggle_button.click()

        # элемент с заданным именем не найден
        return False

    root_locator = self.get_locator(node_root_locator)
    found = find_and_click_item(self.page, root_locator, item_name)
    assert found, f"Navigation panel item {item_name} is missing"

get_item_names(locator)

Возвращает тексты всех элементов по указанному локатору.

Parameters:

Name Type Description Default
locator str | Locator

Локатор элементов или строка с CSS/XPath.

required

Returns:

Type Description
list[str]

Список текстов элементов.

Source code in components\navbar_component.py
24
25
26
27
28
29
30
31
32
33
34
35
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()

traverse_panel_tree(node_root_locator, level=0, debug=False)

Рекурсивно обходит дерево v-treeview и выводит информацию об элементах в режиме отладки (debug=True).

Parameters:

Name Type Description Default
node_root_locator str | Locator

Локатор для поиска корневых элементов дерева (Локатор элемента или строка с CSS/XPath).

required
Source code in components\navbar_component.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
def traverse_panel_tree(self, node_root_locator: str | 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)