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
    211
    212
    213
    214
    215
    216
    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, parent: None|str) -> None:
            """Кликает по вложенному элементу с указанным текстом.

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

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

                # Если искомый элемент находится на данном уровне, вычисляем локатор и делаем клик
                if parent is None:
                    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, parent=None)
                            if is_found:
                                if parent is None:
                                    return True
                                else:
                                    root_texts = root_locator.locator(f">div:nth-child({index + 1})").inner_text().splitlines()
                                    if parent in root_texts:
                                        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, parent)
            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`

    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    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, parent)`

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

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
    122
    123
    124
    125
    126
    127
    def click_sub_item(self, node_root_locator: str | Locator, item_name: str, parent: None|str) -> None:
        """Кликает по вложенному элементу с указанным текстом.

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

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

            # Если искомый элемент находится на данном уровне, вычисляем локатор и делаем клик
            if parent is None:
                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, parent=None)
                        if is_found:
                            if parent is None:
                                return True
                            else:
                                root_texts = root_locator.locator(f">div:nth-child({index + 1})").inner_text().splitlines()
                                if parent in root_texts:
                                    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, parent)
        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`

    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
    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)