272 lines
10 KiB
Python
272 lines
10 KiB
Python
"""Модуль компонента таблицы. Содержит класс для работы с табличными данными."""
|
||
|
||
from playwright.sync_api import Page, expect, Locator
|
||
from tools.logger import get_logger
|
||
from components.base_component import BaseComponent
|
||
|
||
|
||
logger = get_logger("TABLE")
|
||
|
||
|
||
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 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) -> bool:
|
||
"""Проверка, что заданный столбец таблицы упорядочен по убыванию.
|
||
|
||
Args:
|
||
locator: Локатор таблицы.
|
||
index: Индекс столбца.
|
||
|
||
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)):
|
||
column.append(table_content[i][index])
|
||
|
||
return all([x > y for x, y in zip(column, 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"
|