289 lines
12 KiB
Python
289 lines
12 KiB
Python
"""Модуль фрейма создания дочернего элемента."""
|
||
|
||
import re
|
||
from playwright.sync_api import expect, Page, Locator
|
||
from tools.logger import get_logger
|
||
from locators.rack_locators import RackLocators
|
||
from locators.selection_bar_locators import SelectionBarLocators
|
||
from components.alert_component import AlertComponent
|
||
from components.base_component import BaseComponent
|
||
from components.toolbar_component import ToolbarComponent
|
||
from components_derived.selection_bar_component import SelectionBarComponent
|
||
|
||
|
||
logger = get_logger("CREATE_CHILD_ELEMENT_FRAME")
|
||
|
||
logger.setLevel("INFO")
|
||
|
||
class CreateChildElementFrame(BaseComponent):
|
||
"""Фрейм создания дочернего элемента."""
|
||
|
||
def __init__(self, page: Page) -> None:
|
||
"""
|
||
Инициализирует фрейм создания дочернего элемента.
|
||
|
||
Args:
|
||
page: Экземпляр страницы Playwright
|
||
"""
|
||
|
||
super().__init__(page)
|
||
|
||
# Инициализация компонентов
|
||
self.toolbar = ToolbarComponent(page, "Создать дочерний элемент в")
|
||
self.selection_bar = SelectionBarComponent(page, "Класс объекта учета")
|
||
self.alert = AlertComponent(page)
|
||
|
||
# Кнопка "Добавить" - первая кнопка в тулбаре фрейма создания
|
||
add_button_locator = self.page.get_by_role("navigation").filter(
|
||
has_text="Создать дочерний элемент в"
|
||
).get_by_role("button").nth(0)
|
||
|
||
# Кнопка "Отменить" - используем рабочий локатор из старой версии
|
||
cancel_button_locator = self.page.get_by_role("navigation").filter(
|
||
has_text=re.compile('Создать дочерний элемент в')
|
||
).get_by_role("button").nth(1)
|
||
|
||
# Инициализация кнопок
|
||
self.toolbar.add_tooltip_button(add_button_locator, "add")
|
||
self.toolbar.add_tooltip_button(cancel_button_locator, "cancel")
|
||
|
||
# Действия:
|
||
|
||
def clear_combobox_field(self, field_name: str) -> None:
|
||
"""
|
||
Очищает combobox поле по его названию.
|
||
|
||
Args:
|
||
field_name: Название поля для очистки
|
||
"""
|
||
|
||
logger.debug(f"Clearing combobox field '{field_name}'...")
|
||
|
||
# Получаем контейнер формы
|
||
container_locator = self.page.locator(RackLocators.FORM_INPUT_CONTAINER).nth(1)
|
||
|
||
# Используем метод get_input_fields_locators для получения всех полей
|
||
fields_locators = self.get_input_fields_locators_(container_locator, "layout")
|
||
|
||
if field_name not in fields_locators:
|
||
logger.warning(f"Field '{field_name}' not found in form")
|
||
return
|
||
|
||
# Получаем xs8 контейнер поля
|
||
field_container = fields_locators[field_name]
|
||
|
||
# Прокручиваем до поля
|
||
field_container.scroll_into_view_if_needed()
|
||
self.wait_for_timeout(500)
|
||
|
||
# Проверяем видимость
|
||
if not field_container.is_visible():
|
||
logger.debug(f"Field '{field_name}' is not visible after scrolling")
|
||
return
|
||
|
||
# Ищем кнопку закрытия (крестик) внутри контейнера поля
|
||
close_button = field_container.locator("i.mdi-close").first
|
||
|
||
# Проверяем наличие и видимость кнопки закрытия
|
||
if close_button.count() > 0:
|
||
logger.debug(f"Found close button for field '{field_name}'")
|
||
|
||
# Если кнопка закрытия видима - кликаем на нее
|
||
close_button.click(force=True)
|
||
self.wait_for_timeout(500)
|
||
logger.debug(f"Combobox field '{field_name}' cleared using close button")
|
||
else:
|
||
logger.debug(f"Close button (i.mdi-close) not found for field '{field_name}'")
|
||
|
||
def click_add_button(self) -> None:
|
||
"""Кликает на кнопку 'Добавить'."""
|
||
|
||
logger.debug("Clicking on 'Add' button...")
|
||
self.toolbar.click_button("add")
|
||
|
||
def click_cancel_button(self) -> None:
|
||
"""Кликает на кнопку 'Отменить'."""
|
||
|
||
logger.debug("Clicking on 'Cancel' button...")
|
||
self.toolbar.click_button("cancel")
|
||
|
||
def get_selected_object_class(self) -> str:
|
||
"""
|
||
Получает выбранный класс объекта учета.
|
||
|
||
Returns:
|
||
str: Выбранный класс объекта или пустая строка если ничего не выбрано
|
||
"""
|
||
|
||
return self.selection_bar.get_selection_bar_title()
|
||
|
||
def open_object_class_combobox(self) -> None:
|
||
"""Открывает выпадающий список combobox."""
|
||
|
||
container_locator = self.page.locator(RackLocators.FORM_INPUT_CONTAINER)
|
||
|
||
# Используем метод из базового класса BaseComponent
|
||
fields_locators = self.get_input_fields_locators_(container_locator, "layout")
|
||
combobox_container = fields_locators.get("Класс объекта учета")
|
||
|
||
if not combobox_container:
|
||
logger.error("Combobox 'Класс объекта учета' not found")
|
||
return
|
||
|
||
# Проверяем, не открыт ли уже выпадающий список
|
||
menu_selector = "div.v-menu__content.menuable__content__active"
|
||
is_menu_open = self.page.locator(menu_selector).count() > 0
|
||
|
||
if not is_menu_open:
|
||
# Используем OPEN_PARAMETERS_LIST_BUTTON из SelectionBarLocators
|
||
open_button = combobox_container.locator(SelectionBarLocators.OPEN_PARAMETERS_LIST_BUTTON)
|
||
open_button.click(force=True, timeout=5000)
|
||
else:
|
||
logger.debug("Combobox menu is already open")
|
||
|
||
def select_object_class(self, class_name: str) -> None:
|
||
"""Выбирает класс объекта из выпадающего списка."""
|
||
|
||
logger.debug(f"Selecting object class: '{class_name}'...")
|
||
|
||
# Открываем combobox
|
||
self.open_object_class_combobox()
|
||
|
||
# Выбираем значение из списка
|
||
self.selection_bar.select_value(class_name)
|
||
|
||
# Даем время на применение выбора
|
||
self.wait_for_timeout(3000)
|
||
|
||
logger.debug(f"Object class '{class_name}' successfully selected")
|
||
|
||
# Проверки:
|
||
|
||
def check_field_error_highlighted(self, field_name: str) -> None:
|
||
"""
|
||
Проверяет, что поле подсвечено цветом ошибки (валидация не пройдена).
|
||
|
||
Args:
|
||
field_name: Название поля для проверки
|
||
"""
|
||
|
||
logger.debug(f"Checking field '{field_name}' for error highlighting...")
|
||
|
||
# Получаем контейнеры всех полей
|
||
container_locator = self.page.locator(RackLocators.FORM_INPUT_CONTAINER)
|
||
fields_locators = self.get_input_fields_locators_(container_locator, "layout")
|
||
|
||
# Получаем контейнер конкретного поля
|
||
field_container = fields_locators.get(field_name)
|
||
|
||
if not field_container:
|
||
raise ValueError(f"Field '{field_name}' not found in form")
|
||
|
||
# Ищем элементы с классами ошибки внутри контейнера поля
|
||
error_elements = field_container.locator(SelectionBarLocators.ERROR_CSS_SELECTORS)
|
||
|
||
# Проверяем, что есть хотя бы один элемент с классом ошибки
|
||
has_error = error_elements.count() > 0
|
||
|
||
assert has_error, (
|
||
f"Field '{field_name}' has no elements with error classes. "
|
||
f"Expected to find elements matching: {SelectionBarLocators.ERROR_CSS_SELECTORS}"
|
||
)
|
||
|
||
logger.debug(f"Field '{field_name}' is correctly highlighted with error color")
|
||
|
||
def check_field_error_not_highlighted(self, field_name: str) -> None:
|
||
"""
|
||
Проверяет, что поле НЕ подсвечено цветом ошибки (валидация успешна).
|
||
|
||
Args:
|
||
field_name: Название поля для проверки
|
||
"""
|
||
|
||
logger.debug(f"Checking field '{field_name}' for absence of error highlighting...")
|
||
|
||
# Получаем контейнеры всех полей
|
||
container_locator = self.page.locator(RackLocators.FORM_INPUT_CONTAINER)
|
||
fields_locators = self.get_input_fields_locators_(container_locator, "layout")
|
||
|
||
# Получаем контейнер конкретного поля
|
||
field_container = fields_locators.get(field_name)
|
||
|
||
if not field_container:
|
||
raise ValueError(f"Field '{field_name}' not found in form")
|
||
|
||
# Ищем элементы с классами ошибки внутри контейнера поля
|
||
error_elements = field_container.locator(SelectionBarLocators.ERROR_CSS_SELECTORS)
|
||
|
||
# Проверяем, что нет элементов с классами ошибки
|
||
has_error = error_elements.count() > 0
|
||
|
||
assert not has_error, (
|
||
f"Field '{field_name}' has {error_elements.count()} elements with error classes. "
|
||
f"Expected no elements matching: {SelectionBarLocators.ERROR_CSS_SELECTORS}"
|
||
)
|
||
|
||
logger.debug(f"Field '{field_name}' correctly has no error highlighting")
|
||
|
||
def check_object_class_selected(self, expected_class: str) -> None:
|
||
"""
|
||
Проверяет что выбран указанный класс объекта.
|
||
|
||
Args:
|
||
expected_class: Ожидаемый выбранный класс объекта
|
||
"""
|
||
|
||
logger.debug(f"Checking selected object class: '{expected_class}'...")
|
||
|
||
self.wait_for_timeout(1000)
|
||
actual_class = self.get_selected_object_class()
|
||
|
||
is_match = (expected_class.lower() in actual_class.lower() or
|
||
actual_class.lower() in expected_class.lower())
|
||
|
||
assert is_match, (
|
||
f"Selected class does not match expected. "
|
||
f"Expected: '{expected_class}', Got: '{actual_class}'"
|
||
)
|
||
|
||
logger.debug(
|
||
f"Object class '{expected_class}' successfully selected "
|
||
f"(actual: '{actual_class}')"
|
||
)
|
||
|
||
def check_toolbar_title(self, expected_title: str) -> None:
|
||
"""
|
||
Проверяет заголовок тулбара.
|
||
|
||
Args:
|
||
expected_title: Ожидаемый заголовок тулбара
|
||
"""
|
||
|
||
logger.debug(f"Checking toolbar title: '{expected_title}'...")
|
||
|
||
# Используем метод тулбара с фильтрацией по тексту
|
||
actual_text = self.toolbar.get_toolbar_title_text(
|
||
filter_text="Создать дочерний элемент в"
|
||
)
|
||
|
||
assert expected_title in actual_text, (
|
||
f"Title does not match. Expected: '{expected_title}', "
|
||
f"Got: '{actual_text}'"
|
||
)
|
||
|
||
logger.debug(f"Toolbar title is correct: '{actual_text}'")
|
||
|
||
def should_be_toolbar_buttons(self) -> None:
|
||
"""
|
||
Проверяет наличие и функциональность кнопок тулбара.
|
||
"""
|
||
|
||
self.wait_for_timeout(2000)
|
||
|
||
self.toolbar.check_button_visibility("add")
|
||
self.toolbar.check_button_tooltip("add", "Добавить")
|
||
self.toolbar.check_button_visibility("cancel")
|
||
self.toolbar.check_button_tooltip("cancel", "Отменить")
|
||
self.toolbar.click_button("cancel")
|
||
self.wait_for_timeout(2000)
|