"""Модуль фрейма создания дочернего элемента.""" 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") 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)