375 lines
14 KiB
Python
375 lines
14 KiB
Python
"""Модуль вкладки 'Шаблоны'.
|
||
|
||
Содержит класс TemplatesTab для работы с таблицей шаблонов.
|
||
Позволяет проверять состояние и взаимодействовать с элементами вкладки.
|
||
"""
|
||
|
||
import json
|
||
from pathlib import Path
|
||
from playwright.sync_api import Page
|
||
from tools.logger import get_logger
|
||
from locators.table_locators import TableLocators
|
||
from locators.modal_window_locators import ModalWindowLocators
|
||
from locators.json_container_locators import JsonContainerLocators
|
||
from components_derived.modal_view_template import ViewTemplateModalWindow
|
||
from components.toolbar_component import ToolbarComponent
|
||
from components.table_component import TableComponent
|
||
from components.json_container_component import JsonContainerComponent
|
||
from components.alert_component import AlertComponent
|
||
from pages.base_page import BasePage
|
||
|
||
logger = get_logger("TEMPLATES_TAB")
|
||
|
||
|
||
class TemplatesTab(BasePage):
|
||
"""Класс для работы с вкладкой 'Шаблоны'.
|
||
|
||
Предоставляет методы для взаимодействия с таблицей шаблонов и проверки
|
||
её состояния.
|
||
|
||
Args:
|
||
page: Экземпляр страницы Playwright.
|
||
"""
|
||
|
||
def __init__(self, page: Page) -> None:
|
||
"""Инициализирует компоненты вкладки 'Шаблоны'."""
|
||
|
||
super().__init__(page)
|
||
|
||
self.toolbar = ToolbarComponent(page, "Шаблоны")
|
||
|
||
self.templates_table = TableComponent(page)
|
||
self.modal_windows = {}
|
||
|
||
self.json_container = JsonContainerComponent(page)
|
||
self.alert = AlertComponent(page)
|
||
|
||
def add_modal_window(self, title: str) -> None:
|
||
"""Добавляет модальное окно в коллекцию.
|
||
|
||
Args:
|
||
title: Заголовок окна.
|
||
"""
|
||
|
||
self.modal_windows[title] = ViewTemplateModalWindow(self.page, title)
|
||
|
||
def get_modal_window(self, title: str):
|
||
"""Возвращает модальное окно по заголовку.
|
||
|
||
Args:
|
||
title: Заголовок окна.
|
||
|
||
Returns:
|
||
ModalWindowComponent: Экземпляр модального окна.
|
||
|
||
Raises:
|
||
AssertionError: Если окно не найдено.
|
||
"""
|
||
|
||
modal_window = self.modal_windows.get(title)
|
||
|
||
if modal_window is None:
|
||
assert False, f"Modal window with title '{title}' not found"
|
||
return modal_window
|
||
|
||
def delete_modal_window(self, title: str) -> None:
|
||
"""Удаляет модальное окно из коллекции.
|
||
|
||
Args:
|
||
title: Заголовок окна.
|
||
|
||
Raises:
|
||
AssertionError: Если окно не найдено.
|
||
"""
|
||
|
||
if self.modal_windows.get(title) is None:
|
||
assert False, f"Modal window with title '{title}' not found"
|
||
self.modal_windows[title] = None
|
||
|
||
def open_template_modal(self, row_index: int = 0) -> str:
|
||
"""Открывает модальное окно шаблона по клику на строку таблицы.
|
||
|
||
Args:
|
||
row_index: Индекс строки для клика (по умолчанию 0 - первая строка).
|
||
|
||
Returns:
|
||
str: Имя шаблона.
|
||
"""
|
||
row_locator = self.templates_table.get_row_locator(
|
||
TableLocators.TABLE_WORK_AREA,
|
||
row_index
|
||
)
|
||
row_locator.click()
|
||
|
||
# Получаем имя шаблона из выбранной строки
|
||
table_content = self.templates_table.read(TableLocators.TABLE_WORK_AREA)
|
||
# +1 потому что первая строка - заголовки
|
||
template_name = table_content[row_index + 1][0]
|
||
|
||
# Добавляем модальное окно в коллекцию после открытия
|
||
self.add_modal_window(template_name)
|
||
|
||
return template_name
|
||
|
||
def close_modal_window_by_toolbar_button(self, title: str) -> None:
|
||
"""Закрывает модальное окно через кнопку в тулбаре.
|
||
|
||
Args:
|
||
title: Заголовок окна.
|
||
"""
|
||
|
||
modal_window = self.get_modal_window(title)
|
||
modal_window.click_toolbar_close_button()
|
||
self.delete_modal_window(title)
|
||
|
||
def get_rows_count(self) -> int:
|
||
"""Возвращает количество строк в таблице (без заголовка).
|
||
|
||
Returns:
|
||
int: Количество строк с данными.
|
||
|
||
Raises:
|
||
AssertionError: Если таблица пуста.
|
||
"""
|
||
|
||
return self.templates_table.get_rows_count(TableLocators.TABLE_WORK_AREA)
|
||
|
||
def scroll_templates_table_up(self) -> None:
|
||
"""Прокручивает таблицу шаблонов вверх."""
|
||
|
||
self.templates_table.scroll_up(TableLocators.TABLE_SCROLL_CONTAINER)
|
||
|
||
def scroll_templates_table_down(self) -> None:
|
||
"""Прокручивает таблицу шаблонов вниз."""
|
||
|
||
self.templates_table.scroll_down(TableLocators.TABLE_SCROLL_CONTAINER)
|
||
|
||
def scroll_modal_up(self) -> None:
|
||
"""Прокручивает содержимое модального окна вверх."""
|
||
self.templates_table.scroll_up(
|
||
ModalWindowLocators.MODAL_WINDOW_SCROLL_CONTAINER
|
||
)
|
||
|
||
def scroll_modal_down(self) -> None:
|
||
"""Прокручивает содержимое модального окна вниз."""
|
||
self.templates_table.scroll_down(
|
||
ModalWindowLocators.MODAL_WINDOW_SCROLL_CONTAINER
|
||
)
|
||
|
||
def extract_specific_template(self, template_name: str, response_data: dict) -> dict:
|
||
"""Извлекает структуру конкретного шаблона по template_name из данных API.
|
||
|
||
Args:
|
||
template_name: Имя шаблона для извлечения.
|
||
response_data: Данные ответа от API.
|
||
|
||
Returns:
|
||
dict: Структура конкретного шаблона.
|
||
|
||
Raises:
|
||
AssertionError: Если шаблон с указанным именем не найден
|
||
или структура ответа некорректна.
|
||
"""
|
||
# Проверяем, что ответ является списком шаблонов
|
||
assert isinstance(response_data, list), "API response is not a list of templates"
|
||
|
||
# Ищем шаблон с указанным именем
|
||
for template in response_data:
|
||
if template.get('id') == template_name:
|
||
logger.info("Found template: %s", template_name)
|
||
return template
|
||
|
||
# Если шаблон не найден
|
||
available_templates = [t.get('id', 'Unknown') for t in response_data]
|
||
|
||
# Генерируем понятное сообщение об ошибке
|
||
error_msg = (
|
||
f"Template '{template_name}' not found. "
|
||
f"Available templates: {available_templates}"
|
||
)
|
||
logger.error(error_msg)
|
||
assert False, error_msg
|
||
|
||
def save_template_data_to_file(self, template_data: dict, filename: str = None) -> str:
|
||
"""Инструмент отладки. Сохраняет данные шаблона в JSON файл в текущей директории.
|
||
|
||
Args:
|
||
template_data: Данные шаблона для сохранения.
|
||
filename: Имя файла (если None, генерируется автоматически).
|
||
|
||
Returns:
|
||
str: Путь к сохраненному файлу.
|
||
"""
|
||
# Генерируем имя файла если не указано
|
||
if filename is None:
|
||
template_name = template_data.get('id', 'unknown_template')
|
||
filename = f"{template_name}_data.json"
|
||
|
||
# Сохраняем файл в текущей директории
|
||
file_path = Path(filename)
|
||
|
||
# Сохраняем данные в файл с форматированием
|
||
with open(file_path, 'w', encoding='utf-8') as file:
|
||
json.dump(template_data, file, ensure_ascii=False, indent=2)
|
||
|
||
logger.info("Template data saved to: %s", file_path)
|
||
return str(file_path)
|
||
|
||
def check_templates_modal_content(self, template_name: str) -> None:
|
||
"""Проверяет наличие и корректность элементов модального окна шаблона.
|
||
|
||
Args:
|
||
template_name: Имя шаблона для проверки заголовка окна.
|
||
|
||
Raises:
|
||
AssertionError: Если элементы окна некорректны.
|
||
"""
|
||
modal_window = self.get_modal_window(template_name)
|
||
modal_window.check_content()
|
||
|
||
def check_templates_table_content(self) -> None:
|
||
"""Проверяет содержимое таблицы шаблонов.
|
||
|
||
Проверяет заголовки и наличие данных в таблице.
|
||
|
||
Raises:
|
||
AssertionError: Если таблица пуста или заголовки неверны.
|
||
"""
|
||
|
||
expected_headers = [
|
||
'Имя',
|
||
'Описание',
|
||
'Тип устройства',
|
||
'Производитель'
|
||
]
|
||
|
||
self.templates_table.check_content(TableLocators.TABLE_WORK_AREA, expected_headers)
|
||
|
||
def check_templates_table_verticall_scrolling(self) -> bool:
|
||
"""Проверяет возможность вертикальной прокрутки таблицы.
|
||
|
||
Returns:
|
||
bool: True если прокрутка возможна, иначе False.
|
||
"""
|
||
|
||
return self.templates_table.is_scrollable_vertically(
|
||
TableLocators.TABLE_SCROLL_CONTAINER
|
||
)
|
||
|
||
def check_templates_table_first_row_visibility(self) -> None:
|
||
"""Проверяет видимость первой строки таблицы.
|
||
|
||
Raises:
|
||
AssertionError: Если строка не видна.
|
||
"""
|
||
|
||
self.templates_table.check_first_row_visibility(TableLocators.TABLE_WORK_AREA)
|
||
|
||
def check_templates_table_last_row_visibility(self) -> None:
|
||
"""Проверяет видимость последней строки таблицы.
|
||
|
||
Raises:
|
||
AssertionError: Если строка не видна.
|
||
"""
|
||
|
||
self.templates_table.check_last_row_visibility(TableLocators.TABLE_WORK_AREA)
|
||
|
||
def check_templates_table_row_highlighting(self, row_index: int) -> None:
|
||
"""Проверяет выделение указанной строки таблицы.
|
||
|
||
Args:
|
||
row_index: Индекс проверяемой строки.
|
||
|
||
Raises:
|
||
AssertionError: Если строка не выделена.
|
||
"""
|
||
|
||
self.templates_table.check_row_highlighting(
|
||
TableLocators.TABLE_WORK_AREA,
|
||
row_index
|
||
)
|
||
|
||
def should_be_toolbar(self) -> None:
|
||
"""Проверяет наличие тулбара на вкладке.
|
||
|
||
Raises:
|
||
AssertionError: Если тулбар отсутствует.
|
||
"""
|
||
|
||
self.toolbar.check_toolbar_presence("Toolbar is missing")
|
||
|
||
def should_be_templates_table(self) -> None:
|
||
"""Проверяет наличие таблицы шаблонов.
|
||
|
||
Raises:
|
||
AssertionError: Если таблица отсутствует.
|
||
"""
|
||
|
||
self.templates_table.check_visibility(
|
||
TableLocators.TABLE_WORK_AREA,
|
||
"Templates table is missing"
|
||
)
|
||
|
||
def should_be_modal_window(self) -> None:
|
||
"""Проверяет наличие модального окна.
|
||
|
||
Raises:
|
||
AssertionError: Если модальное окно отсутствует.
|
||
"""
|
||
self.templates_table.check_visibility(
|
||
ModalWindowLocators.MODAL_WINDOW,
|
||
"Modal window is not visible"
|
||
)
|
||
|
||
def should_not_be_modal_window(self) -> None:
|
||
"""Проверяет, что модальное окно отсутствует.
|
||
|
||
Raises:
|
||
AssertionError: Если модальное окно все еще видно.
|
||
"""
|
||
is_visible = self.page.locator(
|
||
ModalWindowLocators.MODAL_WINDOW
|
||
).is_visible(timeout=1000)
|
||
if is_visible:
|
||
assert False, "Modal window should not be visible"
|
||
|
||
def check_modal_vertical_scrolling(self) -> bool:
|
||
"""Проверяет возможность вертикального скроллинга в модальном окне.
|
||
|
||
Returns:
|
||
bool: True если скроллинг возможен, иначе False.
|
||
"""
|
||
return self.templates_table.is_scrollable_vertically(
|
||
ModalWindowLocators.MODAL_WINDOW_SCROLL_CONTAINER
|
||
)
|
||
|
||
def verify_json_container_content(self, template_name: str, save_to_file: bool = False) -> None:
|
||
"""Проверяет соответствие данных контейнера данным из API.
|
||
|
||
Args:
|
||
template_name: Имя шаблона для проверки.
|
||
save_to_file: Флаг для сохранения данных в файл.
|
||
"""
|
||
|
||
# Читаем данные из контейнера
|
||
actual_data = self.json_container.read_data(JsonContainerLocators.CONTAINER)
|
||
|
||
# Отправляем запрос к backend для получения информации о шаблоне
|
||
response = self.send_get_api_request("e-cmdb/api/device/template")
|
||
response_body = self.get_response_body(response)
|
||
|
||
# Извлекаем конкретный шаблон по имени из ответа API
|
||
template_data = self.extract_specific_template(template_name, response_body)
|
||
|
||
# Сохраняем данные в файл если требуется
|
||
if save_to_file:
|
||
file_path = self.save_template_data_to_file(template_data)
|
||
logger.info("Template data saved to: %s", file_path)
|
||
|
||
# Сравниваем actual_data с данными конкретного шаблона
|
||
self.json_container.check_json_equals(
|
||
actual_data,
|
||
template_data,
|
||
"Expected json content is not equal actual:"
|
||
)
|