# TableComponent - Документация тестов eNODE-Мониторинг

Skip to content

# TableComponent

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

## `TableComponent`

Bases: `BaseComponent`

Компонент таблицы. Предоставляет методы для работы с табличными данными.

Source code in `components\table_component.py`

     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
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298

    class TableComponent(BaseComponent):
        """Компонент таблицы. Предоставляет методы для работы с табличными данными."""

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

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

            super().__init__(page)

        # Действия:
        def click_arrow_button(self, table_locator: str | Locator, index: int) -> None:
            """ Нажатие кнопки-стрелочки вверх/вниз в ячейке заголовка таблицы

            Args:
                table_locator: Локатор таблицы.
                index: Индекс ячейки в заголовке.
            """

            arrow_button = self.get_header_cell_button(table_locator, index)
            assert arrow_button.is_enabled(), f"Arrow button is missing in {index} header cell"
            arrow_button.click()

        def datetime2timestamp(self, date_string: str) -> float|None:
            """ Конвертация строкового представления даты и времени в Unix timestamp
            Args:
                date_string: Строка с датой и временем в формате d.m.Y H:M:S.

            Returns:
                float: Unix timestamp.
                None: конвертация невозможна
            """

            # Формат, соответствующий строке с датой и временем
            format_string = "%d.%m.%Y %H:%M:%S"

            try:
                date_object = datetime.strptime(date_string, format_string)
                return date_object.timestamp()
            except ValueError:
                return None

        def get_arrow_button_state(self, table_locator: str | Locator, index: int) -> str:
            """ Получение состояния кнопки-стрелочки вверх/вниз в ячейке заголовка таблицы

            Args:
                table_locator: Локатор таблицы.
                index: Индекс ячейки в заголовке.

            Returns:
                up, если это стрелочка вверх. down, если это стрелочка вниз.
            """

            arrow_button = self.get_header_cell_button(table_locator, index)
            assert arrow_button.is_enabled(), f"Arrow button is missing in {index} header cell"

            state = arrow_button.inner_text()
            if state == "keyboard_arrow_up":
                return "up"
            elif state == "keyboard_arrow_down":
                return "down"
            else:
                assert False, f"Got unsupported arrow state: {state}"

        def get_header_cell_button(self, table_locator: str | Locator, index: int) -> Locator:
            """ Поиск кнопки в ячейке заголовка таблицы

            Args:
                table_locator: Локатор таблицы.
                index: Индекс ячейки в заголовке.

            Returns:
                Локатор строки кнопки.

            Raises:
                AssertionError: Если индекс вне диапазона.
            """

            table = self.get_locator(table_locator)
            header_cells_count = table.locator("//thead/tr/th").count()
            assert index in range(header_cells_count), "Header cell index is out of range"
            return table.locator("//thead/tr/th").nth(index).get_by_role("button")

        def get_row_locator(self, table_locator: str | Locator, row_index: int) -> Locator | None:
            """Возвращает локатор строки по индексу.

            Args:
                table_locator: Локатор таблицы.
                row_index: Индекс строки.

            Returns:
                Локатор строки или None, если индекс вне диапазона.
            """

            table = self.get_locator(table_locator)

            rows = table.locator("//tbody/tr")

            if row_index in range(rows.count()):
                return rows.nth(row_index)
            else:
                return None

        def get_rows_count(self, locator: str | Locator) -> int:
            """Возвращает количество строк в таблице (без заголовка).

            Returns:
                int: Количество строк с данными.

            Raises:
                AssertionError: Если таблица пуста.
            """

            table_content = self.read(locator)
            rows_count = len(table_content)

            if rows_count == 0:
                assert False, "The contents of the table are missing"

            return rows_count - 1

        def read(self, locator: str | Locator) -> list[list[str]]:
            """Читает данные таблицы, включая заголовки.

            Args:
                locator: Локатор таблицы.

            Returns:
                Двумерный список с данными таблицы.
            """

            table_data = []
            table = self.get_locator(locator)

            # Чтение заголовка таблицы
            header_cells = table.locator("//thead/tr")
            header_cell_text = header_cells.nth(0).inner_text()
            header_data = header_cell_text.split('\n')
            table_data.append(header_data)

            # Чтение ячеек таблицы
            rows = table.locator("//tbody/tr")
            for i in range(rows.count()):
                row = rows.nth(i)
                cells = row.locator("td")
                row_data = []
                for j in range(cells.count()):
                    cell_text = cells.nth(j).inner_text()
                    row_data.append(cell_text)
                table_data.append(row_data)

            return table_data

        # Проверки:
        def check_table_headers(self, actual_headers, expected_headers) -> None:
            """ Проверка соответствия заголовка таблицы ожидаемому"""

            is_equals = True

            arrow_state = ["keyboard_arrow_down", "keyboard_arrow_up"]

            for item in actual_headers:
                item = item.strip()

                if item in arrow_state:
                    continue

                if item not in expected_headers:
                    is_equals = False

            assert is_equals, \
                f"Expected events table headers {expected_headers} are not equal {actual_headers}"

        def check_content(self,
                        locator: str | Locator,
                        expected_headers: list[str],
                        check_table_not_empty: bool = True) -> None:
            """Проверяет содержимое таблицы.

            Проверяет заголовки и наличие данных в таблице.

            Args:
                locator: Локатор таблицы.
                expected_headers: Список ожидаемых заголовков таблицы.
                check_table_not_empty: Флаг проверки, что таблица не пустая.
                                    По умолчанию True.

            Raises:
                AssertionError: Если таблица пуста (при check_table_not_empty=True)
                            или заголовки неверны.
            """

            table_content = self.read(locator)

            if len(table_content) == 0:
                assert False, "The contents of the table are missing"

            # Проверка заголовков таблицы
            self.check_table_headers(table_content[0], expected_headers)

            # Проверка наличия данных в таблице
            if len(table_content) == 1:
                if check_table_not_empty:
                    assert False, "Table body is missing"
                else:
                    logger.info("Таблица пустая (не содержит строк с данными)")

        def check_column_descending_order(self,
                                             locator: str | Locator,
                                             index: int,
                                             convert2timestamp=False) -> bool:
            """Проверка, что заданный столбец таблицы упорядочен по убыванию.

            Args:
                locator: Локатор таблицы.
                index: Индекс столбца.
                convert2timestamp: Конвертировать строковое представление даты и времени в Unix timestamp

            Returns:
                True, если столбец таблицы упорядочен по убыванию. Иначе: False
            """

            table_content = self.read(locator)

            if len(table_content) == 0:
                assert False, "The contents of the table are missing"

            del table_content[0]

            assert index in range(len(table_content[0])), \
                                "Column index is out of range"
            column = []
            for i in range(len(table_content)):
                if convert2timestamp:
                    timestamp = self.datetime2timestamp(table_content[i][index])
                    assert timestamp, f"Error conversation to timestamp for {table_content[i][index]}"
                    column.append(timestamp)
                else:
                    column.append(table_content[i][index])

            return all(column[i] >= column[i+1] for i in range(len(column) - 1))

        def check_first_row_visibility(self, locator: str | Locator) -> None:
            """Проверяет видимость первой строки таблицы.

            Args:
                locator: Локатор таблицы.
            """

            table = self.get_locator(locator)
            first_row = table.locator("//tbody/tr").first
            expect(first_row).to_be_visible(), "The first table row is not visible"

        def check_last_row_visibility(self, locator: str | Locator) -> None:
            """Проверяет видимость последней строки таблицы.

            Args:
                locator: Локатор таблицы.
            """

            table = self.get_locator(locator)
            last_row = table.locator("//tbody/tr").last
            expect(last_row).to_be_visible(), "The last table row is not visible"

        def check_row_highlighting(self, locator: str | Locator, row_index: int) -> None:
            """Проверяет изменение цвета строки при наведении.

            Args:
                locator: Локатор таблицы.
                row_index: Индекс проверяемой строки.
            """

            table = self.get_locator(locator)
            row = table.locator("//tbody/tr").nth(row_index)

            row.scroll_into_view_if_needed()
            hover_element = row.locator(".body-row-hover")
            initial_color = hover_element.evaluate("el => window.getComputedStyle(el).backgroundColor")

            row.hover()
            self.page.wait_for_timeout(300)

            new_color = hover_element.evaluate("el => window.getComputedStyle(el).backgroundColor")
            assert initial_color != new_color, "Color of row did not change when hovering the cursor"

### `__init__(page)`

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

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`page` | `Page` | Экземпляр страницы Playwright. | required

Source code in `components\table_component.py`

    15
    16
    17
    18
    19
    20
    21
    22

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

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

        super().__init__(page)

### `check_column_descending_order(locator, index, convert2timestamp=False)`

Проверка, что заданный столбец таблицы упорядочен по убыванию.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`locator` | `str | Locator` | Локатор таблицы. | required
`index` | `int` | Индекс столбца. | required
`convert2timestamp` |  | Конвертировать строковое представление даты и времени в Unix timestamp | `False`

Returns:

Type | Description
--- | ---
`bool` | True, если столбец таблицы упорядочен по убыванию. Иначе: False

Source code in `components\table_component.py`

    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255

    def check_column_descending_order(self,
                                         locator: str | Locator,
                                         index: int,
                                         convert2timestamp=False) -> bool:
        """Проверка, что заданный столбец таблицы упорядочен по убыванию.

        Args:
            locator: Локатор таблицы.
            index: Индекс столбца.
            convert2timestamp: Конвертировать строковое представление даты и времени в Unix timestamp

        Returns:
            True, если столбец таблицы упорядочен по убыванию. Иначе: False
        """

        table_content = self.read(locator)

        if len(table_content) == 0:
            assert False, "The contents of the table are missing"

        del table_content[0]

        assert index in range(len(table_content[0])), \
                            "Column index is out of range"
        column = []
        for i in range(len(table_content)):
            if convert2timestamp:
                timestamp = self.datetime2timestamp(table_content[i][index])
                assert timestamp, f"Error conversation to timestamp for {table_content[i][index]}"
                column.append(timestamp)
            else:
                column.append(table_content[i][index])

        return all(column[i] >= column[i+1] for i in range(len(column) - 1))

### `check_content(locator, expected_headers, check_table_not_empty=True)`

Проверяет содержимое таблицы.

Проверяет заголовки и наличие данных в таблице.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`locator` | `str | Locator` | Локатор таблицы. | required
`expected_headers` | `list[str]` | Список ожидаемых заголовков таблицы. | required
`check_table_not_empty` | `bool` | Флаг проверки, что таблица не пустая. По умолчанию True. | `True`

Raises:

Type | Description
--- | ---
`AssertionError` | Если таблица пуста (при check_table_not_empty=True) или заголовки неверны.

Source code in `components\table_component.py`

    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
    217
    218
    219
    220

    def check_content(self,
                    locator: str | Locator,
                    expected_headers: list[str],
                    check_table_not_empty: bool = True) -> None:
        """Проверяет содержимое таблицы.

        Проверяет заголовки и наличие данных в таблице.

        Args:
            locator: Локатор таблицы.
            expected_headers: Список ожидаемых заголовков таблицы.
            check_table_not_empty: Флаг проверки, что таблица не пустая.
                                По умолчанию True.

        Raises:
            AssertionError: Если таблица пуста (при check_table_not_empty=True)
                        или заголовки неверны.
        """

        table_content = self.read(locator)

        if len(table_content) == 0:
            assert False, "The contents of the table are missing"

        # Проверка заголовков таблицы
        self.check_table_headers(table_content[0], expected_headers)

        # Проверка наличия данных в таблице
        if len(table_content) == 1:
            if check_table_not_empty:
                assert False, "Table body is missing"
            else:
                logger.info("Таблица пустая (не содержит строк с данными)")

### `check_first_row_visibility(locator)`

Проверяет видимость первой строки таблицы.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`locator` | `str | Locator` | Локатор таблицы. | required

Source code in `components\table_component.py`

    257
    258
    259
    260
    261
    262
    263
    264
    265
    266

    def check_first_row_visibility(self, locator: str | Locator) -> None:
        """Проверяет видимость первой строки таблицы.

        Args:
            locator: Локатор таблицы.
        """

        table = self.get_locator(locator)
        first_row = table.locator("//tbody/tr").first
        expect(first_row).to_be_visible(), "The first table row is not visible"

### `check_last_row_visibility(locator)`

Проверяет видимость последней строки таблицы.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`locator` | `str | Locator` | Локатор таблицы. | required

Source code in `components\table_component.py`

    268
    269
    270
    271
    272
    273
    274
    275
    276
    277

    def check_last_row_visibility(self, locator: str | Locator) -> None:
        """Проверяет видимость последней строки таблицы.

        Args:
            locator: Локатор таблицы.
        """

        table = self.get_locator(locator)
        last_row = table.locator("//tbody/tr").last
        expect(last_row).to_be_visible(), "The last table row is not visible"

### `check_row_highlighting(locator, row_index)`

Проверяет изменение цвета строки при наведении.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`locator` | `str | Locator` | Локатор таблицы. | required
`row_index` | `int` | Индекс проверяемой строки. | required

Source code in `components\table_component.py`

    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298

    def check_row_highlighting(self, locator: str | Locator, row_index: int) -> None:
        """Проверяет изменение цвета строки при наведении.

        Args:
            locator: Локатор таблицы.
            row_index: Индекс проверяемой строки.
        """

        table = self.get_locator(locator)
        row = table.locator("//tbody/tr").nth(row_index)

        row.scroll_into_view_if_needed()
        hover_element = row.locator(".body-row-hover")
        initial_color = hover_element.evaluate("el => window.getComputedStyle(el).backgroundColor")

        row.hover()
        self.page.wait_for_timeout(300)

        new_color = hover_element.evaluate("el => window.getComputedStyle(el).backgroundColor")
        assert initial_color != new_color, "Color of row did not change when hovering the cursor"

### `check_table_headers(actual_headers, expected_headers)`

Проверка соответствия заголовка таблицы ожидаемому

Source code in `components\table_component.py`

    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186

    def check_table_headers(self, actual_headers, expected_headers) -> None:
        """ Проверка соответствия заголовка таблицы ожидаемому"""

        is_equals = True

        arrow_state = ["keyboard_arrow_down", "keyboard_arrow_up"]

        for item in actual_headers:
            item = item.strip()

            if item in arrow_state:
                continue

            if item not in expected_headers:
                is_equals = False

        assert is_equals, \
            f"Expected events table headers {expected_headers} are not equal {actual_headers}"

### `click_arrow_button(table_locator, index)`

Нажатие кнопки-стрелочки вверх/вниз в ячейке заголовка таблицы

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`table_locator` | `str | Locator` | Локатор таблицы. | required
`index` | `int` | Индекс ячейки в заголовке. | required

Source code in `components\table_component.py`

    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35

    def click_arrow_button(self, table_locator: str | Locator, index: int) -> None:
        """ Нажатие кнопки-стрелочки вверх/вниз в ячейке заголовка таблицы

        Args:
            table_locator: Локатор таблицы.
            index: Индекс ячейки в заголовке.
        """

        arrow_button = self.get_header_cell_button(table_locator, index)
        assert arrow_button.is_enabled(), f"Arrow button is missing in {index} header cell"
        arrow_button.click()

### `datetime2timestamp(date_string)`

Конвертация строкового представления даты и времени в Unix timestamp Args: date_string: Строка с датой и временем в формате d.m.Y H:M:S.

Returns:

Name | Type | Description
--- | --- | ---
`float` | `float | None` | Unix timestamp.
`None` | `float | None` | конвертация невозможна

Source code in `components\table_component.py`

    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54

    def datetime2timestamp(self, date_string: str) -> float|None:
        """ Конвертация строкового представления даты и времени в Unix timestamp
        Args:
            date_string: Строка с датой и временем в формате d.m.Y H:M:S.

        Returns:
            float: Unix timestamp.
            None: конвертация невозможна
        """

        # Формат, соответствующий строке с датой и временем
        format_string = "%d.%m.%Y %H:%M:%S"

        try:
            date_object = datetime.strptime(date_string, format_string)
            return date_object.timestamp()
        except ValueError:
            return None

### `get_arrow_button_state(table_locator, index)`

Получение состояния кнопки-стрелочки вверх/вниз в ячейке заголовка таблицы

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`table_locator` | `str | Locator` | Локатор таблицы. | required
`index` | `int` | Индекс ячейки в заголовке. | required

Returns:

Type | Description
--- | ---
`str` | up, если это стрелочка вверх. down, если это стрелочка вниз.

Source code in `components\table_component.py`

    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76

    def get_arrow_button_state(self, table_locator: str | Locator, index: int) -> str:
        """ Получение состояния кнопки-стрелочки вверх/вниз в ячейке заголовка таблицы

        Args:
            table_locator: Локатор таблицы.
            index: Индекс ячейки в заголовке.

        Returns:
            up, если это стрелочка вверх. down, если это стрелочка вниз.
        """

        arrow_button = self.get_header_cell_button(table_locator, index)
        assert arrow_button.is_enabled(), f"Arrow button is missing in {index} header cell"

        state = arrow_button.inner_text()
        if state == "keyboard_arrow_up":
            return "up"
        elif state == "keyboard_arrow_down":
            return "down"
        else:
            assert False, f"Got unsupported arrow state: {state}"

### `get_header_cell_button(table_locator, index)`

Поиск кнопки в ячейке заголовка таблицы

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`table_locator` | `str | Locator` | Локатор таблицы. | required
`index` | `int` | Индекс ячейки в заголовке. | required

Returns:

Type | Description
--- | ---
`Locator` | Локатор строки кнопки.

Raises:

Type | Description
--- | ---
`AssertionError` | Если индекс вне диапазона.

Source code in `components\table_component.py`

    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95

    def get_header_cell_button(self, table_locator: str | Locator, index: int) -> Locator:
        """ Поиск кнопки в ячейке заголовка таблицы

        Args:
            table_locator: Локатор таблицы.
            index: Индекс ячейки в заголовке.

        Returns:
            Локатор строки кнопки.

        Raises:
            AssertionError: Если индекс вне диапазона.
        """

        table = self.get_locator(table_locator)
        header_cells_count = table.locator("//thead/tr/th").count()
        assert index in range(header_cells_count), "Header cell index is out of range"
        return table.locator("//thead/tr/th").nth(index).get_by_role("button")

### `get_row_locator(table_locator, row_index)`

Возвращает локатор строки по индексу.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`table_locator` | `str | Locator` | Локатор таблицы. | required
`row_index` | `int` | Индекс строки. | required

Returns:

Type | Description
--- | ---
`Locator | None` | Локатор строки или None, если индекс вне диапазона.

Source code in `components\table_component.py`

     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115

    def get_row_locator(self, table_locator: str | Locator, row_index: int) -> Locator | None:
        """Возвращает локатор строки по индексу.

        Args:
            table_locator: Локатор таблицы.
            row_index: Индекс строки.

        Returns:
            Локатор строки или None, если индекс вне диапазона.
        """

        table = self.get_locator(table_locator)

        rows = table.locator("//tbody/tr")

        if row_index in range(rows.count()):
            return rows.nth(row_index)
        else:
            return None

### `get_rows_count(locator)`

Возвращает количество строк в таблице (без заголовка).

Returns:

Name | Type | Description
--- | --- | ---
`int` | `int` | Количество строк с данными.

Raises:

Type | Description
--- | ---
`AssertionError` | Если таблица пуста.

Source code in `components\table_component.py`

    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133

    def get_rows_count(self, locator: str | Locator) -> int:
        """Возвращает количество строк в таблице (без заголовка).

        Returns:
            int: Количество строк с данными.

        Raises:
            AssertionError: Если таблица пуста.
        """

        table_content = self.read(locator)
        rows_count = len(table_content)

        if rows_count == 0:
            assert False, "The contents of the table are missing"

        return rows_count - 1

### `read(locator)`

Читает данные таблицы, включая заголовки.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`locator` | `str | Locator` | Локатор таблицы. | required

Returns:

Type | Description
--- | ---
`list[list[str]]` | Двумерный список с данными таблицы.

Source code in `components\table_component.py`

    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

    def read(self, locator: str | Locator) -> list[list[str]]:
        """Читает данные таблицы, включая заголовки.

        Args:
            locator: Локатор таблицы.

        Returns:
            Двумерный список с данными таблицы.
        """

        table_data = []
        table = self.get_locator(locator)

        # Чтение заголовка таблицы
        header_cells = table.locator("//thead/tr")
        header_cell_text = header_cells.nth(0).inner_text()
        header_data = header_cell_text.split('\n')
        table_data.append(header_data)

        # Чтение ячеек таблицы
        rows = table.locator("//tbody/tr")
        for i in range(rows.count()):
            row = rows.nth(i)
            cells = row.locator("td")
            row_data = []
            for j in range(cells.count()):
                cell_text = cells.nth(j).inner_text()
                row_data.append(cell_text)
            table_data.append(row_data)

        return table_data