e-nms_qa_automation/frames/create_element_frame.py

299 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""Модуль фрейма создания дочернего элемента."""
import re
from playwright.sync_api import 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_ELEMENT_FRAME")
logger.setLevel("INFO")
class CreateElementFrame(BaseComponent):
"""Фрейм создания дочернего элемента."""
def __init__(self, page: Page) -> None:
"""
Инициализирует фрейм создания дочернего элемента.
Args:
page (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 (str): Название поля для очистки
"""
logger.debug(f"Clearing combobox field '{field_name}'...")
# Получаем контейнер формы
container_locator = self.page.locator(RackLocators.CREATE_RACK_FORM_CONTAINER).nth(1)
fields_locators = self.get_input_fields_locators(container_locator)
if field_name not in fields_locators:
logger.warning(f"Field '{field_name}' not found in form")
return
field_container = fields_locators[field_name]
field_container.scroll_into_view_if_needed()
self.wait_for_timeout(300)
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:
close_button.click(force=True)
self.wait_for_timeout(300)
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 is_field_filled(self, field_name: str, container_locator: Locator = None) -> bool:
"""
Проверяет, заполнено ли combobox или текстовое поле.
Args:
field_name (str): Название поля для проверки
container_locator (Locator, optional): Локатор контейнера формы
Returns:
bool: True если поле заполнено, False в противном случае
"""
logger.debug(f"Checking if field '{field_name}' is filled...")
if container_locator is None:
container_locator = self.page.locator(RackLocators.CREATE_RACK_FORM_CONTAINER).nth(1)
fields_locators = self.get_input_fields_locators(container_locator)
if field_name not in fields_locators:
logger.debug(f"Field '{field_name}' not found in fields_locators")
return False
field_container = fields_locators[field_name]
if not field_container.is_visible():
logger.debug(f"Field '{field_name}' not visible")
return False
selected_chip = field_container.locator(".v-chip").first
field_text = field_container.text_content() or ""
has_text = bool(field_text.strip())
has_chip = selected_chip.count() > 0 and selected_chip.is_visible()
if not has_chip:
input_field = field_container.locator("input").first
if input_field.count() > 0:
input_value = input_field.input_value() or ""
has_input_value = bool(input_value.strip())
logger.debug(f"Field '{field_name}' - has input value: {has_input_value}")
has_text = has_text or has_input_value
logger.debug(f"Field '{field_name}' - has chip: {has_chip}, has text: {has_text}")
return has_chip or has_text
def open_object_class_combobox(self) -> None:
"""Открывает выпадающий список combobox."""
container_locator = self.page.locator(RackLocators.CREATE_RACK_FORM_CONTAINER)
fields_locators = self.get_input_fields_locators(container_locator)
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_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:
"""
Выбирает класс объекта из выпадающего списка.
Args:
class_name (str): Название класса объекта для выбора
"""
logger.debug(f"Selecting object class: '{class_name}'...")
self.open_object_class_combobox()
self.selection_bar.select_value(class_name)
self.wait_for_timeout(300)
logger.debug(f"Object class '{class_name}' successfully selected")
# Проверки:
def check_field_error_highlighted(self, field_name: str) -> None:
"""
Проверяет, что поле подсвечено цветом ошибки (валидация не пройдена).
Args:
field_name (str): Название поля для проверки
Raises:
ValueError: Если поле не найдено в форме
AssertionError: Если поле не подсвечено ошибкой
"""
logger.debug(f"Checking field '{field_name}' for error highlighting...")
container_locator = self.page.locator(RackLocators.CREATE_RACK_FORM_CONTAINER)
fields_locators = self.get_input_fields_locators(container_locator)
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 (str): Название поля для проверки
Raises:
ValueError: Если поле не найдено в форме
AssertionError: Если поле подсвечено ошибкой
"""
logger.debug(f"Checking field '{field_name}' for absence of error highlighting...")
container_locator = self.page.locator(RackLocators.CREATE_RACK_FORM_CONTAINER)
fields_locators = self.get_input_fields_locators(container_locator)
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 (str): Ожидаемый выбранный класс объекта
Raises:
AssertionError: Если выбранный класс не соответствует ожидаемому
"""
logger.debug(f"Checking selected object class: '{expected_class}'...")
self.wait_for_timeout(500)
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 (actual: '{actual_class}')")
def check_toolbar_title(self, expected_title: str) -> None:
"""
Проверяет заголовок тулбара.
Args:
expected_title (str): Ожидаемый заголовок тулбара
Raises:
AssertionError: Если заголовок не соответствует ожидаемому
"""
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}', Got: '{actual_text}'"
)
logger.debug(f"Toolbar title is correct: '{actual_text}'")
def should_be_toolbar_buttons(self) -> None:
"""
Проверяет наличие и функциональность кнопок тулбара.
"""
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(500)