Рефакторинг: перенос методов работы с combobox в DropdownList

radislav/tests_rack
Radislav 2025-11-17 15:21:59 +03:00
parent e4aba34421
commit c0459e0905
19 changed files with 614 additions and 452 deletions

View File

@ -88,6 +88,113 @@ class DropdownList(BaseComponent):
self.page.wait_for_timeout(300) self.page.wait_for_timeout(300)
self.check_item_with_text(last_item_name) self.check_item_with_text(last_item_name)
def open_combobox(self, combobox_locator: str | Locator, listbox_locator: str | Locator, icon_locator: str | Locator = None) -> None:
"""
Открывает выпадающий список combobox.
Args:
combobox_locator: Локатор combobox
listbox_locator: Локатор выпадающего списка
icon_locator: Локатор иконки для клика (опционально)
"""
logger.info("Открытие combobox...")
combobox = self.get_locator(combobox_locator)
listbox = self.get_locator(listbox_locator)
# Прокручиваем до combobox
combobox.scroll_into_view_if_needed()
self.page.wait_for_timeout(1000)
# Проверяем, не открыт ли уже список
listbox_already_open = False
listbox_count = listbox.count()
if listbox_count > 0:
listbox_already_open = listbox.first.is_visible()
if not listbox_already_open:
# Если указан локатор иконки, кликаем на него, иначе на сам combobox
if icon_locator:
icon = combobox.locator(icon_locator)
icon.scroll_into_view_if_needed()
icon.click(timeout=10000)
else:
combobox.click(timeout=10000)
logger.info("Клик на combobox выполнен")
self.page.wait_for_timeout(1000)
# Проверяем что список открылся
listbox_count_after = listbox.count()
listbox_visible = False
if listbox_count_after > 0:
listbox_visible = listbox.first.is_visible()
if listbox_visible:
logger.info("Выпадающий список найден и открыт")
else:
logger.warning("Не удалось открыть выпадающий список")
def get_combobox_options(self, combobox_locator: str | Locator, listbox_locator: str | Locator, icon_locator: str | Locator = None) -> list[str]:
"""
Получает список доступных опций из combobox.
Args:
combobox_locator: Локатор combobox
listbox_locator: Локатор выпадающего списка
icon_locator: Локатор иконки для клика (опционально)
Returns:
list[str]: Список доступных опций
"""
logger.info("Получение списка опций combobox...")
# Открываем combobox (если еще не открыт)
self.open_combobox(combobox_locator, listbox_locator, icon_locator)
options_list = self.get_item_names(listbox_locator)
# Закрываем combobox (кликаем вне его)
self.page.mouse.click(10, 10)
self.page.wait_for_timeout(500)
logger.info(f"Найдено опций: {len(options_list)} - {options_list}")
return options_list
def get_selected_combobox_value(self, combobox_locator: str | Locator, value_locator: str | Locator = None) -> str:
"""
Получает выбранное значение из combobox.
Args:
combobox_locator: Локатор combobox
value_locator: Локатор элемента с выбранным значением (опционально)
Returns:
str: Выбранное значение или пустая строка если ничего не выбрано
"""
combobox = self.get_locator(combobox_locator)
selected_value = ""
if value_locator:
# Используем переданный локатор для значения
value_element = combobox.locator(value_locator)
if value_element.count() > 0:
selected_value = value_element.first.text_content().strip()
else:
# Ищем в span элементах по умолчанию
span_locator = combobox.locator("span")
if span_locator.count() > 0:
for i in range(span_locator.count()):
span_text = span_locator.nth(i).text_content().strip()
if span_text and span_text not in ["Класс объекта учета"]: # Можно сделать исключения параметром
selected_value = span_text
break
logger.info(f"Выбранное значение combobox: '{selected_value}'")
return selected_value
# Проверки: # Проверки:
def check_item_with_text(self, text: str) -> None: def check_item_with_text(self, text: str) -> None:
"""Проверяет наличие и доступность элемента списка. """Проверяет наличие и доступность элемента списка.

View File

@ -14,6 +14,7 @@ class AlertLocators:
ALERT_MESSAGE (str): текстового сообщения в alert-окне. ALERT_MESSAGE (str): текстового сообщения в alert-окне.
ALERT_DISMISS_BUTTON (str): кнопки закрытия alert-окна. ALERT_DISMISS_BUTTON (str): кнопки закрытия alert-окна.
ALERT_BY_TEXT (str): alert-окна с определенным текстом (шаблон). ALERT_BY_TEXT (str): alert-окна с определенным текстом (шаблон).
ERROR_CLASSES (list): классы для подсветки ошибок валидации.
""" """
ALERT_ROLE: str = "alert" ALERT_ROLE: str = "alert"
@ -21,3 +22,6 @@ class AlertLocators:
ALERT_MESSAGE: str = f"{ALERT_BASE}/div" ALERT_MESSAGE: str = f"{ALERT_BASE}/div"
ALERT_DISMISS_BUTTON: str = "//a[@class='v-alert__dismissible']" ALERT_DISMISS_BUTTON: str = "//a[@class='v-alert__dismissible']"
ALERT_BY_TEXT: str = f"{ALERT_BASE}[contains(., '{{text}}')]" ALERT_BY_TEXT: str = f"{ALERT_BASE}[contains(., '{{text}}')]"
# Классы для подсветки ошибок валидации полей
ERROR_CLASSES: list = ["error--text"]

View File

@ -11,7 +11,7 @@ class RackLocators:
- Вкладки стойки (верхние вкладки) - Вкладки стойки (верхние вкладки)
- Секции лицевой и обратной сторон стойки - Секции лицевой и обратной сторон стойки
- Юниты и устройства на стойке - Юниты и устройства на стойке
- Поля формы редактирования стойки - Поля формы редактирования и создания стойки
- Контейнеры и структурные элементы - Контейнеры и структурные элементы
""" """
@ -39,7 +39,7 @@ class RackLocators:
# Контейнер формы # Контейнер формы
FORM_CONTAINER = "//div[contains(@class, 'container')]" FORM_CONTAINER = "//div[contains(@class, 'container')]"
# Локаторы полей # Локаторы полей формы редактирования стойки
NAME_FIELD = "//input[@aria-label='Имя']" NAME_FIELD = "//input[@aria-label='Имя']"
SERIAL_NUMBER_FIELD = "//input[@aria-label='Серийный номер']" SERIAL_NUMBER_FIELD = "//input[@aria-label='Серийный номер']"
INVENTORY_NUMBER_FIELD = "//input[@aria-label='Инвентарный номер']" INVENTORY_NUMBER_FIELD = "//input[@aria-label='Инвентарный номер']"
@ -50,6 +50,22 @@ class RackLocators:
SERVICE_ORG_FIELD = "//input[@aria-label='Обслуживающая организация']" SERVICE_ORG_FIELD = "//input[@aria-label='Обслуживающая организация']"
PROJECT_FIELD = "//input[@aria-label='Проект/Титул']" PROJECT_FIELD = "//input[@aria-label='Проект/Титул']"
# Локаторы полей формы создания стойки
RACK_NAME_FIELD = "//label[text()='Имя']/following-sibling::input"
RACK_HEIGHT_FIELD = "//div[contains(@class, 'v-input__slot') and .//label[text()='Высота в юнитах']]"
RACK_DEPTH_FIELD = "//div[contains(@class, 'v-input__slot') and .//label[text()='Глубина (мм)']]"
RACK_SERIAL_FIELD = "//label[text()='Серийный номер']/following-sibling::input"
RACK_INVENTORY_FIELD = "//label[text()='Инвентарный номер']/following-sibling::input"
RACK_COMMENT_FIELD = "//label[text()='Комментарий']/following-sibling::input"
RACK_CABLE_ENTRY_FIELD = "//div[contains(@class, 'v-input__slot') and .//label[text()='Ввод кабеля']]"
RACK_STATE_FIELD = "//div[contains(@class, 'container')]//div[contains(@class, 'v-input__slot white') and .//label[text()='Состояние']]"
RACK_OWNER_FIELD = "//div[contains(@class, 'v-input__slot') and .//label[text()='Владелец']]"
RACK_SERVICE_ORG_FIELD = "//div[contains(@class, 'v-input__slot') and .//label[text()='Обслуживающая организация']]"
RACK_PROJECT_FIELD = "//div[contains(@class, 'v-input__slot') and .//label[text()='Проект/Титул']]"
# Локатор для родительского контейнера поля ввода
INPUT_PARENT_CONTAINER = "xpath=./ancestor::div[contains(@class, 'v-input')]"
# Локаторы для отображения сторон стойки # Локаторы для отображения сторон стойки
FRONT_SIDE_CONTAINER = "//div[contains(@class, 'cabinet') and not(contains(@class, 'back'))]" FRONT_SIDE_CONTAINER = "//div[contains(@class, 'cabinet') and not(contains(@class, 'back'))]"
BACK_SIDE_CONTAINER = "//div[contains(@class, 'cabinet') and contains(@class, 'back')]" BACK_SIDE_CONTAINER = "//div[contains(@class, 'cabinet') and contains(@class, 'back')]"

View File

@ -5,7 +5,6 @@
import re import re
from playwright.sync_api import Page, expect from playwright.sync_api import Page, expect
from elements.tooltip_button_element import TooltipButton from elements.tooltip_button_element import TooltipButton
from components.toolbar_component import ToolbarComponent from components.toolbar_component import ToolbarComponent
from components.dropdown_list_component import DropdownList from components.dropdown_list_component import DropdownList
@ -15,37 +14,33 @@ from components.base_component import BaseComponent
from components.alert_component import AlertComponent from components.alert_component import AlertComponent
from components.navbar_component import NavigationPanelComponent from components.navbar_component import NavigationPanelComponent
from locators.navigation_panel_locators import NavigationPanelLocators from locators.navigation_panel_locators import NavigationPanelLocators
from locators.combobox_locators import ComboboxLocators # Новый импорт from locators.combobox_locators import ComboboxLocators
from locators.rack_locators import RackLocators
from locators.alert_locators import AlertLocators
from tools.logger import get_logger from tools.logger import get_logger
logger = get_logger("CREATE_RACK_ELEMENT") logger = get_logger("CREATE_RACK_ELEMENT")
# =============== Локаторы ================================================
# Локаторы для полей стойки
RACK_NAME_FIELD = "//label[text()='Имя']/following-sibling::input"
RACK_HEIGHT_FIELD = "//div[contains(@class, 'v-input__slot') and .//label[text()='Высота в юнитах']]"
RACK_DEPTH_FIELD = "//div[contains(@class, 'v-input__slot') and .//label[text()='Глубина (мм)']]"
RACK_SERIAL_FIELD = "//label[text()='Серийный номер']/following-sibling::input"
RACK_INVENTORY_FIELD = "//label[text()='Инвентарный номер']/following-sibling::input"
RACK_COMMENT_FIELD = "//label[text()='Комментарий']/following-sibling::input"
RACK_CABLE_ENTRY_FIELD = "//div[contains(@class, 'v-input__slot') and .//label[text()='Ввод кабеля']]"
RACK_STATE_FIELD = "//div[contains(@class, 'v-input__slot') and .//label[text()='Состояние']]"
RACK_OWNER_FIELD = "//div[contains(@class, 'v-input__slot') and .//label[text()='Владелец']]"
RACK_SERVICE_ORG_FIELD = "//div[contains(@class, 'v-input__slot') and .//label[text()='Обслуживающая организация']]"
RACK_PROJECT_FIELD = "//div[contains(@class, 'v-input__slot') and .//label[text()='Проект/Титул']]"
# Словарь для сопоставления названий полей с локаторами # Словарь для сопоставления названий полей с локаторами
COMBOBOX_FIELDS_MAP = { COMBOBOX_FIELDS_MAP = {
"Высота в юнитах": RACK_HEIGHT_FIELD, # Обязательные поля
"Глубина (мм)": RACK_DEPTH_FIELD, "Имя": RackLocators.RACK_NAME_FIELD,
"Ввод кабеля": RACK_CABLE_ENTRY_FIELD, "Высота в юнитах": RackLocators.RACK_HEIGHT_FIELD,
"Состояние": RACK_STATE_FIELD, "Глубина (мм)": RackLocators.RACK_DEPTH_FIELD,
"Владелец": RACK_OWNER_FIELD,
"Обслуживающая организация": RACK_SERVICE_ORG_FIELD, # Дополнительные текстовые поля
"Проект/Титул": RACK_PROJECT_FIELD "Серийный номер": RackLocators.RACK_SERIAL_FIELD,
"Инвентарный номер": RackLocators.RACK_INVENTORY_FIELD,
"Комментарий": RackLocators.RACK_COMMENT_FIELD,
# Combobox поля
"Ввод кабеля": RackLocators.RACK_CABLE_ENTRY_FIELD,
"Состояние": RackLocators.RACK_STATE_FIELD,
"Владелец": RackLocators.RACK_OWNER_FIELD,
"Обслуживающая организация": RackLocators.RACK_SERVICE_ORG_FIELD,
"Проект/Титул": RackLocators.RACK_PROJECT_FIELD
} }
#========================================================================================================
class CreateRackElementTab(BasePage): class CreateRackElementTab(BasePage):
@ -90,6 +85,326 @@ class CreateRackElementTab(BasePage):
# Инициализация компонента выпадающего списка # Инициализация компонента выпадающего списка
self.dropdown = DropdownList(page) self.dropdown = DropdownList(page)
# =============== МЕТОДЫ ДЕЙСТВИЙ ========================
def click_add_button(self) -> None:
"""
Кликает на кнопку 'Добавить'.
"""
self.toolbar.click_button("add")
def click_cancel_button(self) -> None:
"""
Кликает на кнопку 'Отменить'.
"""
self.toolbar.click_button("cancel")
def open_object_class_combobox(self) -> None:
"""
Открывает выпадающий список combobox 'Класс объекта учета'.
"""
logger.info("Открытие combobox 'Класс объекта учета'...")
self.dropdown.open_combobox(
ComboboxLocators.OBJECT_CLASS_COMBOBOX,
ComboboxLocators.LISTBOX_SELECTOR,
ComboboxLocators.COMBOBOX_ICON
)
def select_object_class(self, class_name: str) -> None:
"""
Выбирает класс объекта из выпадающего списка.
Args:
class_name: Название класса объекта для выбора
Raises:
AssertionError: Если класс не найден в списке
"""
logger.info(f"Выбор класса объекта: '{class_name}'...")
# Открываем combobox
self.open_object_class_combobox()
self.dropdown.click_item_with_text(class_name)
# Проверяем что выбор произошел
self.wait_for_timeout(1000)
selected_value = self.get_selected_object_class()
if class_name.lower() not in selected_value.lower() and selected_value.lower() not in class_name.lower():
# Если выбор не произошел, получаем доступные опции для отладки
available_options = self.get_object_class_options()
logger.warning(f"Класс '{class_name}' не выбран. Текущее значение: '{selected_value}'. Доступные опции: {available_options}")
raise AssertionError(f"Не удалось выбрать класс объекта '{class_name}'")
logger.info(f"Класс объекта '{class_name}' успешно выбран")
def fill_rack_data(self, name: str, height: str = "42", depth: str = "1000",
serial: str = "", inventory: str = "", comment: str = "",
cable_entry: str = "", state: str = "", owner: str = "",
service_org: str = "", project: str = "") -> None:
"""
Заполняет данные для создания стойки.
Args:
name: Наименование стойки
height: Высота в юнитах (по умолчанию 42)
depth: Глубина в мм (по умолчанию 1000)
serial: Серийный номер
inventory: Инвентарный номер
comment: Комментарий
cable_entry: Ввод кабеля
state: Состояние
owner: Владелец
service_org: Обслуживающая организация
project: Проект/Титул
"""
logger.info(f"Заполнение данных стойки: {name}")
# Заполняем обязательные поля
name_field = self.page.locator(RackLocators.RACK_NAME_FIELD)
name_field.fill(name)
logger.info(f"Заполнено поле 'Имя': {name}")
self._select_combobox("Высота в юнитах", height)
logger.info(f"Выбрана высота: {height} юнитов")
self._select_combobox("Глубина (мм)", depth)
logger.info(f"Выбрана глубина: {depth} мм")
# Заполняем опциональные поля
if serial:
serial_field = self.page.locator(RackLocators.RACK_SERIAL_FIELD)
serial_field.fill(serial)
logger.info(f"Заполнен серийный номер: {serial}")
if inventory:
inventory_field = self.page.locator(RackLocators.RACK_INVENTORY_FIELD)
inventory_field.fill(inventory)
logger.info(f"Заполнен инвентарный номер: {inventory}")
if comment:
comment_field = self.page.locator(RackLocators.RACK_COMMENT_FIELD)
comment_field.fill(comment)
logger.info(f"Добавлен комментарий: {comment}")
# Заполняем дополнительные combobox поля
if cable_entry:
self._select_combobox("Ввод кабеля", cable_entry)
logger.info(f"Выбран ввод кабеля: {cable_entry}")
if state:
self._select_combobox("Состояние", state)
logger.info(f"Выбрано состояние: {state}")
if owner:
self._select_combobox("Владелец", owner)
logger.info(f"Выбран владелец: {owner}")
if service_org:
self._select_combobox("Обслуживающая организация", service_org)
logger.info(f"Выбрана обслуживающая организация: {service_org}")
if project:
self._select_combobox("Проект/Титул", project)
logger.info(f"Выбран проект/титул: {project}")
logger.info("Данные стойки заполнены")
def _select_combobox(self, field_name: str, value: str) -> None:
"""
Выбор значения в combobox.
Args:
field_name: Название поля
value: Значение для выбора
"""
logger.info(f"Выбор '{value}' в поле '{field_name}'...")
# Получаем статический локатор из словаря
if field_name not in COMBOBOX_FIELDS_MAP:
raise ValueError(f"Локатор для поля '{field_name}' не найден в COMBOBOX_FIELDS_MAP")
field_locator = COMBOBOX_FIELDS_MAP[field_name]
# Для всех полей используем first() чтобы избежать strict mode violation
field_container = self.page.locator(field_locator).first
# Прокручиваем до поля
field_container.scroll_into_view_if_needed()
self.wait_for_timeout(500)
# Проверяем видимость поля
self.base_component.check_visibility(field_container, f"Поле '{field_name}' не найдено")
# Универсальный клик с force=True для всех полей
field_container.click(force=True)
self.wait_for_timeout(1000)
# Вводим значение
self.page.keyboard.type(value)
self.wait_for_timeout(500)
self.page.keyboard.press("Enter")
logger.info(f"Поле '{field_name}' заполнено")
def create_rack(self, rack_name: str, **kwargs) -> None:
"""
Полный процесс создания стойки.
Args:
rack_name: Наименование стойки
**kwargs: Дополнительные параметры стойки
"""
logger.info(f"Начало процесса создания стойки: {rack_name}")
# Выбираем класс объекта "Стойка"
self.select_object_class("Стойка")
self.wait_for_timeout(1000)
# Проверяем наличие полей стойки
self.check_rack_fields_presence()
# Заполняем данные
self.fill_rack_data(rack_name, **kwargs)
# Создаем стойку
self.click_add_button()
logger.info(f"Процесс создания стойки '{rack_name}' завершен")
def clear_combobox_field(self, field_name: str) -> None:
"""
Очищает значение в combobox поле с помощью кнопки закрытия (крестика).
Args:
field_name: Название поля для очистки
"""
logger.info(f"Очистка combobox поля '{field_name}' с помощью кнопки закрытия...")
if field_name not in COMBOBOX_FIELDS_MAP:
logger.warning(f"Локатор для поля '{field_name}' не найден в COMBOBOX_FIELDS_MAP")
return
field_locator = COMBOBOX_FIELDS_MAP[field_name]
# Находим поле по локатору
field_container = self.page.locator(field_locator).first
# Проверяем что поле видимо
if not field_container.is_visible():
logger.info(f"Поле '{field_name}' не видимо, пропускаем очистку")
return
# Прокручиваем до поля
field_container.scroll_into_view_if_needed()
self.wait_for_timeout(500)
# Ищем кнопку закрытия (крестик) внутри контейнера поля
close_button = field_container.locator(ComboboxLocators.COMBOBOX_CLOSE_BUTTON)
# Проверяем наличие и видимость кнопки закрытия
if close_button.count() > 0 and close_button.is_visible():
# Если кнопка закрытия видима - кликаем на нее
close_button.click()
self.wait_for_timeout(500)
logger.info(f"Combobox поле '{field_name}' очищено с помощью кнопки закрытия")
else:
# Если кнопки закрытия нет, просто логируем этот факт
logger.info(f"Кнопка закрытия не найдена для поля '{field_name}', очистка не выполнена")
def clear_rack_fields(self) -> None:
"""
Очищает все поля формы создания стойки.
"""
logger.info("Очистка всех полей формы стойки...")
# Очищаем текстовые поля
text_fields = [
(RackLocators.RACK_NAME_FIELD, "Имя"),
(RackLocators.RACK_SERIAL_FIELD, "Серийный номер"),
(RackLocators.RACK_INVENTORY_FIELD, "Инвентарный номер"),
(RackLocators.RACK_COMMENT_FIELD, "Комментарий")
]
for field_locator, field_name in text_fields:
field = self.page.locator(field_locator)
if field.count() > 0 and field.first.is_visible():
field.fill("")
logger.info(f"Текстовое поле '{field_name}' очищено")
# Очищаем combobox поля
combobox_fields = [
"Высота в юнитах",
"Глубина (мм)",
"Ввод кабеля",
"Состояние",
"Владелец",
"Обслуживающая организация",
"Проект/Титул"
]
for field_name in combobox_fields:
self.clear_combobox_field(field_name)
logger.info("Все поля формы стойки очищены")
def get_object_class_options(self) -> list[str]:
"""
Получает список доступных опций из combobox.
Returns:
list[str]: Список доступных классов объектов
"""
return self.dropdown.get_combobox_options(
ComboboxLocators.OBJECT_CLASS_COMBOBOX,
ComboboxLocators.LISTBOX_SELECTOR,
ComboboxLocators.COMBOBOX_ICON
)
def get_selected_object_class(self) -> str:
"""
Получает выбранный класс объекта учета.
Returns:
str: Выбранный класс объекта или пустая строка если ничего не выбрано
"""
return self.dropdown.get_selected_combobox_value(
ComboboxLocators.OBJECT_CLASS_COMBOBOX,
ComboboxLocators.SELECTED_VALUE_SPAN
)
# =============== МЕТОДЫ ПРОВЕРОК ========================
def check_rack_exists(self, rack_name: str) -> bool:
"""
Проверяет, существует ли уже стойка с указанным именем в навигационной панели.
Args:
rack_name: Имя стойки для проверки
Returns:
bool: True если стойка существует, False если нет
"""
logger.info(f"Проверка существования стойки с именем '{rack_name}'")
self.main_page.click_main_navigation_panel_item("Объекты")
self.main_page.click_main_navigation_panel_item("Объекты")
self.wait_for_timeout(1000)
self.main_page.click_subpanel_item("test-zone")
self.wait_for_timeout(3000)
nav_panel_locator = NavigationPanelLocators.TREEVIEW
# Проверяем видимость элемента через is_visible
element = self.page.locator(nav_panel_locator).get_by_text(rack_name).first
if element.is_visible():
logger.info(f"Стойка с именем '{rack_name}' найдена")
return True
else:
logger.info(f"Стойки с именем '{rack_name}' не найдена")
return False
def should_be_toolbar_buttons(self) -> None: def should_be_toolbar_buttons(self) -> None:
""" """
Проверяет наличие и функциональность кнопок тулбара. Проверяет наличие и функциональность кнопок тулбара.
@ -108,18 +423,6 @@ class CreateRackElementTab(BasePage):
self.toolbar.click_button("cancel") self.toolbar.click_button("cancel")
self.wait_for_timeout(2000) self.wait_for_timeout(2000)
def click_add_button(self) -> None:
"""
Кликает на кнопку 'Добавить'.
"""
self.toolbar.click_button("add")
def click_cancel_button(self) -> None:
"""
Кликает на кнопку 'Отменить'.
"""
self.toolbar.click_button("cancel")
def check_toolbar_title(self, expected_title: str) -> None: def check_toolbar_title(self, expected_title: str) -> None:
""" """
Проверяет заголовок тулбара. Проверяет заголовок тулбара.
@ -130,7 +433,7 @@ class CreateRackElementTab(BasePage):
Raises: Raises:
AssertionError: Если заголовок не соответствует ожидаемому AssertionError: Если заголовок не соответствует ожидаемому
""" """
logger.info(f"Проверка заголовка тулбара: '{expected_title}'...") logger.info(f"Проверка заголовок тулбара: '{expected_title}'...")
# Используем метод тулбара с фильтрацией по тексту # Используем метод тулбара с фильтрацией по тексту
actual_text = self.toolbar.get_toolbar_title_text( actual_text = self.toolbar.get_toolbar_title_text(
@ -184,120 +487,6 @@ class CreateRackElementTab(BasePage):
logger.info("Содержимое combobox 'Класс объекта учета' корректно") logger.info("Содержимое combobox 'Класс объекта учета' корректно")
def open_object_class_combobox(self) -> None:
"""
Открывает выпадающий список combobox 'Класс объекта учета'.
"""
logger.info("Открытие combobox 'Класс объекта учета'...")
combobox_locator = self.page.locator(ComboboxLocators.OBJECT_CLASS_COMBOBOX)
listbox_locator = self.page.locator(ComboboxLocators.LISTBOX_SELECTOR)
icon_locator = combobox_locator.locator(ComboboxLocators.COMBOBOX_ICON)
# Прокручиваем до combobox
combobox_locator.scroll_into_view_if_needed()
self.wait_for_timeout(1000)
# Проверяем, не открыт ли уже список
listbox_already_open = False
listbox_count = listbox_locator.count()
if listbox_count > 0:
listbox_already_open = listbox_locator.first.is_visible()
if not listbox_already_open:
# Только если список не открыт, кликаем на иконку
icon_locator.scroll_into_view_if_needed()
icon_locator.click(timeout=10000)
logger.info("Клик на иконку combobox выполнен")
self.wait_for_timeout(1000)
# Проверяем что список открылся
listbox_count_after = listbox_locator.count()
listbox_visible = False
if listbox_count_after > 0:
listbox_visible = listbox_locator.first.is_visible()
if listbox_visible:
logger.info("Выпадающий список найден и открыт")
else:
logger.warning("Не удалось открыть выпадающий список")
def get_object_class_options(self) -> list[str]:
"""
Получает список доступных опций из combobox.
Returns:
list[str]: Список доступных классов объектов
"""
logger.info("Получение списка опций combobox 'Класс объекта учета'...")
# Открываем combobox (если еще не открыт)
self.open_object_class_combobox()
# Используем метод get_item_names из DropdownList
options_list = self.dropdown.get_item_names(ComboboxLocators.LISTBOX_SELECTOR)
# Закрываем combobox (кликаем вне его)
self.page.mouse.click(10, 10)
self.wait_for_timeout(500)
logger.info(f"Найдено опций: {len(options_list)} - {options_list}")
return options_list
def select_object_class(self, class_name: str) -> None:
"""
Выбирает класс объекта из выпадающего списка.
Args:
class_name: Название класса объекта для выбора
Raises:
AssertionError: Если класс не найден в списке
"""
logger.info(f"Выбор класса объекта: '{class_name}'...")
# Открываем combobox
self.open_object_class_combobox()
self.dropdown.click_item_with_text(class_name)
# Проверяем что выбор произошел
self.wait_for_timeout(1000)
selected_value = self.get_selected_object_class()
if class_name.lower() not in selected_value.lower() and selected_value.lower() not in class_name.lower():
# Если выбор не произошел, получаем доступные опции для отладки
available_options = self.get_object_class_options()
logger.warning(f"Класс '{class_name}' не выбран. Текущее значение: '{selected_value}'. Доступные опции: {available_options}")
raise AssertionError(f"Не удалось выбрать класс объекта '{class_name}'")
logger.info(f"Класс объекта '{class_name}' успешно выбран")
def get_selected_object_class(self) -> str:
"""
Получает выбранный класс объекта учета.
Returns:
str: Выбранный класс объекта или пустая строка если ничего не выбрано
"""
combobox_locator = self.page.locator(ComboboxLocators.OBJECT_CLASS_COMBOBOX)
selected_value = ""
# Ищем в span элементах
span_locator = combobox_locator.locator(ComboboxLocators.SELECTED_VALUE_SPAN)
if span_locator.count() > 0:
for i in range(span_locator.count()):
span_text = span_locator.nth(i).text_content().strip()
if span_text and span_text not in ["Класс объекта учета"]:
selected_value = span_text
break
logger.info(f"Выбранный класс объекта: '{selected_value}'")
return selected_value
def check_object_class_selected(self, expected_class: str) -> None: def check_object_class_selected(self, expected_class: str) -> None:
""" """
Проверяет что выбран указанный класс объекта. Проверяет что выбран указанный класс объекта.
@ -365,8 +554,6 @@ class CreateRackElementTab(BasePage):
logger.info(f"Элемент '{item_text}' присутствует в списке") logger.info(f"Элемент '{item_text}' присутствует в списке")
# =============== МЕТОДЫ ДЛЯ РАБОТЫ СО СТОЙКОЙ ========================
def check_rack_fields_presence(self) -> None: def check_rack_fields_presence(self) -> None:
""" """
Проверяет наличие полей специфичных для стойки. Проверяет наличие полей специфичных для стойки.
@ -378,21 +565,21 @@ class CreateRackElementTab(BasePage):
# Основные обязательные поля # Основные обязательные поля
required_fields = [ required_fields = [
(RACK_NAME_FIELD, "Имя"), (RackLocators.RACK_NAME_FIELD, "Имя"),
(RACK_HEIGHT_FIELD, "Высота в юнитах"), (RackLocators.RACK_HEIGHT_FIELD, "Высота в юнитах"),
(RACK_DEPTH_FIELD, "Глубина (мм)") (RackLocators.RACK_DEPTH_FIELD, "Глубина (мм)")
] ]
# Дополнительные поля # Дополнительные поля
optional_fields = [ optional_fields = [
(RACK_SERIAL_FIELD, "Серийный номер"), (RackLocators.RACK_SERIAL_FIELD, "Серийный номер"),
(RACK_INVENTORY_FIELD, "Инвентарный номер"), (RackLocators.RACK_INVENTORY_FIELD, "Инвентарный номер"),
(RACK_COMMENT_FIELD, "Комментарий"), (RackLocators.RACK_COMMENT_FIELD, "Комментарий"),
(RACK_CABLE_ENTRY_FIELD, "Ввод кабеля"), (RackLocators.RACK_CABLE_ENTRY_FIELD, "Ввод кабеля"),
(RACK_STATE_FIELD, "Состояние"), (RackLocators.RACK_STATE_FIELD, "Состояние"),
(RACK_OWNER_FIELD, "Владелец"), (RackLocators.RACK_OWNER_FIELD, "Владелец"),
(RACK_SERVICE_ORG_FIELD, "Обслуживающая организация"), (RackLocators.RACK_SERVICE_ORG_FIELD, "Обслуживающая организация"),
(RACK_PROJECT_FIELD, "Проект/Титул") (RackLocators.RACK_PROJECT_FIELD, "Проект/Титул")
] ]
# Проверяем обязательные поля # Проверяем обязательные поля
@ -410,250 +597,86 @@ class CreateRackElementTab(BasePage):
logger.info("Все основные поля для стойки присутствуют") logger.info("Все основные поля для стойки присутствуют")
def fill_rack_data(self, name: str, height: str = "42", depth: str = "1000", def check_field_highlighted_error(self, field_name: str) -> None:
serial: str = "", inventory: str = "", comment: str = "",
cable_entry: str = "", state: str = "", owner: str = "",
service_org: str = "", project: str = "") -> None:
""" """
Заполняет данные для создания стойки. Проверяет, что поле подсвечено цветом ошибки (валидация не пройдена).
Args: Args:
name: Наименование стойки field_name: Название поля для проверки
height: Высота в юнитах (по умолчанию 42)
depth: Глубина в мм (по умолчанию 1000)
serial: Серийный номер
inventory: Инвентарный номер
comment: Комментарий
cable_entry: Ввод кабеля
state: Состояние
owner: Владелец
service_org: Обслуживающая организация
project: Проект/Титул
""" """
logger.info(f"Заполнение данных стойки: {name}") logger.info(f"Проверка подсветки поля '{field_name}' цветом ошибки...")
# Заполняем обязательные поля # Локаторы только для обязательных полей
name_field = self.page.locator(RACK_NAME_FIELD) required_fields = {
name_field.fill(name) "Имя": RackLocators.RACK_NAME_FIELD,
logger.info(f"Заполнено поле 'Имя': {name}") "Высота в юнитах": RackLocators.RACK_HEIGHT_FIELD,
"Глубина (мм)": RackLocators.RACK_DEPTH_FIELD
}
self._select_combobox("Высота в юнитах", height) if field_name not in required_fields:
logger.info(f"Выбрана высота: {height} юнитов") raise ValueError(f"Поле '{field_name}' не является обязательным или не поддерживается")
self._select_combobox("Глубина (мм)", depth) field_locator = required_fields[field_name]
logger.info(f"Выбрана глубина: {depth} мм") field_element = self.page.locator(field_locator)
# Заполняем опциональные поля
if serial:
serial_field = self.page.locator(RACK_SERIAL_FIELD)
serial_field.fill(serial)
logger.info(f"Заполнен серийный номер: {serial}")
if inventory:
inventory_field = self.page.locator(RACK_INVENTORY_FIELD)
inventory_field.fill(inventory)
logger.info(f"Заполнен инвентарный номер: {inventory}")
if comment:
comment_field = self.page.locator(RACK_COMMENT_FIELD)
comment_field.fill(comment)
logger.info(f"Добавлен комментарий: {comment}")
# Заполняем дополнительные combobox поля
if cable_entry:
self._select_combobox("Ввод кабеля", cable_entry)
logger.info(f"Выбран ввод кабеля: {cable_entry}")
if state:
self._select_combobox("Состояние", state)
logger.info(f"Выбрано состояние: {state}")
if owner:
self._select_combobox("Владелец", owner)
logger.info(f"Выбран владелец: {owner}")
if service_org:
self._select_combobox("Обслуживающая организация", service_org)
logger.info(f"Выбрана обслуживающая организация: {service_org}")
if project:
self._select_combobox("Проект/Титул", project)
logger.info(f"Выбран проект/титул: {project}")
logger.info("Данные стойки заполнены")
def _select_combobox(self, field_name: str, value: str) -> None:
"""
Выбор значения в combobox.
Args:
field_name: Название поля
value: Значение для выбора
"""
logger.info(f"Выбор '{value}' в поле '{field_name}'...")
# Получаем статический локатор из словаря
if field_name not in COMBOBOX_FIELDS_MAP:
raise ValueError(f"Локатор для поля '{field_name}' не найден в COMBOBOX_FIELDS_MAP")
field_locator = COMBOBOX_FIELDS_MAP[field_name]
field_container = self.page.locator(field_locator)
# Прокручиваем до поля
field_container.scroll_into_view_if_needed()
self.wait_for_timeout(500)
# Проверяем видимость поля
self.base_component.check_visibility(field_container, f"Поле '{field_name}' не найдено")
# Кликаем на контейнер чтобы активировать поле
field_container.click()
self.wait_for_timeout(1000)
# Вводим значение
self.page.keyboard.type(value)
self.wait_for_timeout(500)
self.page.keyboard.press("Enter")
logger.info(f"Поле '{field_name}' заполнено")
def create_rack(self, rack_name: str, **kwargs) -> None:
"""
Полный процесс создания стойки.
Args:
rack_name: Наименование стойки
**kwargs: Дополнительные параметры стойки
"""
logger.info(f"Начало процесса создания стойки: {rack_name}")
# Выбираем класс объекта "Стойка"
self.select_object_class("Стойка")
self.wait_for_timeout(1000)
# Проверяем наличие полей стойки
self.check_rack_fields_presence()
# Заполняем данные
self.fill_rack_data(rack_name, **kwargs)
# Создаем стойку
self.click_add_button()
logger.info(f"Процесс создания стойки '{rack_name}' завершен")
def clear_name_field(self) -> None:
"""
Очищает поле 'Имя'.
"""
logger.info("Очистка поля 'Имя'...")
name_field = self.page.locator(RACK_NAME_FIELD)
name_field.fill("")
logger.info("Поле 'Имя' очищено")
def check_rack_exists(self, rack_name: str) -> bool:
"""
Проверяет, существует ли уже стойка с указанным именем в навигационной панели.
Args:
rack_name: Имя стойки для проверки
Returns:
bool: True если стойка существует, False если нет
"""
logger.info(f"Проверка существования стойки с именем '{rack_name}'")
self.main_page.click_main_navigation_panel_item("Объекты")
self.main_page.click_main_navigation_panel_item("Объекты")
self.wait_for_timeout(1000)
self.main_page.click_subpanel_item("test-zone")
self.wait_for_timeout(1000)
# Используем TREEVIEW локатор из NavigationPanelLocators
nav_panel_locator = NavigationPanelLocators.TREEVIEW
# Проверяем видимость элемента через is_visible
element = self.page.locator(nav_panel_locator).get_by_text(rack_name).first
if element.is_visible():
logger.info(f"Стойка с именем '{rack_name}' найдена")
return True
else:
logger.info(f"Стойки с именем '{rack_name}' не найдена")
return False
def clear_combobox_field(self, field_name: str) -> None:
"""
Очищает значение в combobox поле с помощью кнопки закрытия (крестика).
Args:
field_name: Название поля для очистки
"""
logger.info(f"Очистка combobox поля '{field_name}' с помощью кнопки закрытия...")
if field_name not in COMBOBOX_FIELDS_MAP:
logger.warning(f"Локатор для поля '{field_name}' не найден в COMBOBOX_FIELDS_MAP")
return
field_locator = COMBOBOX_FIELDS_MAP[field_name]
# Находим поле по локатору
field_container = self.page.locator(field_locator).first
# Проверяем что поле видимо # Проверяем что поле видимо
if not field_container.is_visible(): self.base_component.check_visibility(field_element, f"Поле '{field_name}' не найдено")
logger.info(f"Поле '{field_name}' не видимо, пропускаем очистку")
return
# Прокручиваем до поля # Ищем родительский контейнер с использованием константы
field_container.scroll_into_view_if_needed() parent_container = field_element.locator(RackLocators.INPUT_PARENT_CONTAINER).first
self.wait_for_timeout(500)
# Ищем кнопку закрытия (крестик) внутри контейнера поля # Проверка классов ошибки
close_button = field_container.locator(ComboboxLocators.COMBOBOX_CLOSE_BUTTON) if parent_container.count() > 0:
error_classes = AlertLocators.ERROR_CLASSES
# Проверяем наличие и видимость кнопки закрытия is_error_highlighted = False
if close_button.count() > 0 and close_button.is_visible(): for error_class in error_classes:
# Если кнопка закрытия видима - кликаем на нее error_element = parent_container.locator(f".{error_class}")
close_button.click() if error_element.count() > 0:
self.wait_for_timeout(500) is_error_highlighted = True
logger.info(f"Combobox поле '{field_name}' очищено с помощью кнопки закрытия") logger.info(f"Поле '{field_name}' подсвечено ошибкой")
else: break
# Если кнопки закрытия нет, просто логируем этот факт
logger.info(f"Кнопка закрытия не найдена для поля '{field_name}', очистка не выполнена")
def clear_rack_fields(self) -> None: if not is_error_highlighted:
raise AssertionError(f"Поле '{field_name}' не подсвечено цветом ошибки ")
logger.info(f"Поле '{field_name}' корректно подсвечено цветом ошибки")
def check_field_not_highlighted_error(self, field_name: str) -> None:
""" """
Очищает все поля формы создания стойки. Проверяет, что поле НЕ подсвечено цветом ошибки (валидация успешна).
Args:
field_name: Название поля для проверки
""" """
logger.info("Очистка всех полей формы стойки...") logger.info(f"Проверка отсутствия подсветки ошибки у поля '{field_name}'...")
# Очищаем текстовые поля # Локаторы только для обязательных полей
text_fields = [ required_fields = {
(RACK_NAME_FIELD, "Имя"), "Имя": RackLocators.RACK_NAME_FIELD,
(RACK_SERIAL_FIELD, "Серийный номер"), "Высота в юнитах": RackLocators.RACK_HEIGHT_FIELD,
(RACK_INVENTORY_FIELD, "Инвентарный номер"), "Глубина (мм)": RackLocators.RACK_DEPTH_FIELD
(RACK_COMMENT_FIELD, "Комментарий") }
]
for field_locator, field_name in text_fields: if field_name not in required_fields:
field = self.page.locator(field_locator) raise ValueError(f"Поле '{field_name}' не является обязательным или не поддерживается")
if field.count() > 0 and field.first.is_visible():
field.fill("")
logger.info(f"Текстовое поле '{field_name}' очищено")
# Очищаем combobox поля field_locator = required_fields[field_name]
combobox_fields = [ field_element = self.page.locator(field_locator)
"Высота в юнитах",
"Глубина (мм)",
"Ввод кабеля",
"Состояние",
"Владелец",
"Обслуживающая организация",
"Проект/Титул"
]
for field_name in combobox_fields: # Проверяем что поле видимо
self.clear_combobox_field(field_name) self.base_component.check_visibility(field_element, f"Поле '{field_name}' не найдено")
logger.info("Все поля формы стойки очищены") # Ищем родительский контейнер с использованием константы
parent_container = field_element.locator(RackLocators.INPUT_PARENT_CONTAINER).first
# Поверка отсутствия классов ошибки
if parent_container.count() > 0:
error_classes = AlertLocators.ERROR_CLASSES
for error_class in error_classes:
error_element = parent_container.locator(f".{error_class}")
if error_element.count() > 0:
raise AssertionError(f"Поле '{field_name}' подсвечено ошибкой")
logger.info(f"Поле '{field_name}' корректно не подсвечено цветом ошибки")

View File

@ -37,17 +37,6 @@ class TestCreateRackElement:
main_page.click_main_navigation_panel_item("test-zone") main_page.click_main_navigation_panel_item("test-zone")
main_page.wait_for_timeout(2000) main_page.wait_for_timeout(2000)
# Создаем экземпляр страницы и переходим к созданию дочернего элемента
#child_element_page = CreateChildElementTab(browser)
#child_element_page.click_create_button()
#child_element_page.select_object_class("Стойка")
#child_element_page.check_object_class_selected("Стойка")
#child_element_page.wait_for_timeout(2000)
#@pytest.mark.develop
def test_create_rack_content(self, browser: Page) -> None:
"""Тест создания дочернего элемента типа 'Стойка'."""
# Создаем экземпляр страницы и переходим к созданию дочернего элемента # Создаем экземпляр страницы и переходим к созданию дочернего элемента
child_element_page = CreateChildElementTab(browser) child_element_page = CreateChildElementTab(browser)
child_element_page.click_create_button() child_element_page.click_create_button()
@ -55,6 +44,10 @@ class TestCreateRackElement:
child_element_page.check_object_class_selected("Стойка") child_element_page.check_object_class_selected("Стойка")
child_element_page.wait_for_timeout(2000) child_element_page.wait_for_timeout(2000)
#@pytest.mark.develop
def test_create_rack_content(self, browser: Page) -> None:
"""Тест создания дочернего элемента типа 'Стойка'."""
rack_element_page = CreateRackElementTab(browser) rack_element_page = CreateRackElementTab(browser)
# Проверяем заголовок формы создания # Проверяем заголовок формы создания
@ -66,17 +59,9 @@ class TestCreateRackElement:
rack_element_page.should_be_toolbar_buttons() rack_element_page.should_be_toolbar_buttons()
#@pytest.mark.develop
def test_create_rack_child_element(self, browser: Page) -> None: def test_create_rack_child_element(self, browser: Page) -> None:
"""Тест создания дочернего элемента типа 'Стойка'.""" """Тест создания дочернего элемента типа 'Стойка'."""
# Создаем экземпляр страницы и переходим к созданию дочернего элемента
child_element_page = CreateChildElementTab(browser)
child_element_page.click_create_button()
child_element_page.select_object_class("Стойка")
child_element_page.check_object_class_selected("Стойка")
child_element_page.wait_for_timeout(2000)
rack_element_page = CreateRackElementTab(browser) rack_element_page = CreateRackElementTab(browser)
# Проверяем что после выбора 'Стойка' появляются специфичные поля # Проверяем что после выбора 'Стойка' появляются специфичные поля
@ -92,7 +77,9 @@ class TestCreateRackElement:
depth="1000", depth="1000",
serial="TEST123456", serial="TEST123456",
inventory="INV-001", inventory="INV-001",
comment="Тестовая стойка для автоматизации" comment="Тестовая стойка для автоматизации",
cable_entry="Сверху",
state="В эксплуатации"
) )
# Нажимаем кнопку создания # Нажимаем кнопку создания
@ -102,7 +89,6 @@ class TestCreateRackElement:
logger.info("Тест создания дочернего элемента 'Стойка' завершен успешно") logger.info("Тест создания дочернего элемента 'Стойка' завершен успешно")
#@pytest.mark.develop
def test_create_rack_with_duplicate_name(self, browser: Page) -> None: def test_create_rack_with_duplicate_name(self, browser: Page) -> None:
""" """
Тест создания стойки с уже существующим именем. Тест создания стойки с уже существующим именем.
@ -113,7 +99,9 @@ class TestCreateRackElement:
logger.info("Запуск теста создания стойки с дублирующимся именем") logger.info("Запуск теста создания стойки с дублирующимся именем")
rack_name = "Test-Rack-01" rack_name = "Test-Rack-01"
rack_element_page = CreateRackElementTab(browser) rack_element_page = CreateRackElementTab(browser)
rack_element_page.click_cancel_button()
# Проверяем, существует ли уже стойка с таким именем # Проверяем, существует ли уже стойка с таким именем
if not rack_element_page.check_rack_exists(rack_name): if not rack_element_page.check_rack_exists(rack_name):
@ -138,12 +126,12 @@ class TestCreateRackElement:
# Создаем первую стойку # Создаем первую стойку
rack_element_page.click_add_button() rack_element_page.click_add_button()
rack_element_page.wait_for_timeout(3000) rack_element_page.wait_for_timeout(2000)
logger.info(f"Первая стойка с именем '{rack_name}' успешно создана") logger.info(f"Первая стойка с именем '{rack_name}' успешно создана")
else: else:
logger.info(f"Стойка с именем '{rack_name}' уже существует, переходим к созданию второй") logger.info(f"Стойка с именем '{rack_name}' уже существует, переходим к созданию второй")
# Теперь пытаемся создать вторую стойку с тем же именем # Cоздаем вторую стойку с тем же именем
logger.info(f"Пытаемся создать вторую стойку с именем '{rack_name}'") logger.info(f"Пытаемся создать вторую стойку с именем '{rack_name}'")
# Переходим обратно к созданию новой стойки # Переходим обратно к созданию новой стойки
@ -171,20 +159,16 @@ class TestCreateRackElement:
# Проверяем наличие alert-окна с сообщением о дублирующемся имени # Проверяем наличие alert-окна с сообщением о дублирующемся имени
expected_alert_text = f"Имя {rack_name} уже используется" expected_alert_text = f"Имя {rack_name} уже используется"
rack_element_page.alert.check_alert_presence(expected_alert_text) rack_element_page.alert.check_alert_presence(expected_alert_text)
logger.info(f"Alert-окно с текстом '{expected_alert_text}' успешно отображено")
# Проверяем, что остались на странице создания (стойка не создана) # Проверяем, что остались на странице создания (стойка не создана)
rack_element_page.check_toolbar_title('Создать дочерний элемент в') rack_element_page.check_toolbar_title('Создать дочерний элемент в')
# Закрываем alert-окно с помощью кнопки закрытия
rack_element_page.wait_for_timeout(2000)
rack_element_page.alert.close_alert_by_text(expected_alert_text)
logger.info("Система не позволила создать стойку с дублирующимся именем") logger.info("Система не позволила создать стойку с дублирующимся именем")
# Проверяем исчезновение alert-окна через некоторое время
rack_element_page.wait_for_timeout(5000)
rack_element_page.alert.check_alert_absence(expected_alert_text, timeout=20000)
logger.info("Alert-окно успешно исчезло")
logger.info("Тест создания стойки с дублирующимся именем завершен успешно")
@pytest.mark.develop
def test_required_fields_validation(self, browser: Page) -> None: def test_required_fields_validation(self, browser: Page) -> None:
""" """
Тест проверки обязательных полей при создании стойки. Тест проверки обязательных полей при создании стойки.
@ -194,33 +178,35 @@ class TestCreateRackElement:
- Поле 'Высота в юнитах' должно быть заполнено - Поле 'Высота в юнитах' должно быть заполнено
- Поле 'Глубина (мм)' должно быть заполнено - Поле 'Глубина (мм)' должно быть заполнено
""" """
# Текст сообщения alert-окна # Текст сообщения alert-окна
#expected_alert_text_name = f"поле Имя должно быть заполнено" # Ошибка, поле не отслеживается expected_alert_text_name = f"поле Имя должно быть заполнено"
expected_alert_text_height = f"поле Высота в юнитах должно быть заполнено" expected_alert_text_height = f"поле Высота в юнитах должно быть заполнено"
expected_alert_text_depth = f"поле Глубина (мм) должно быть заполнено" expected_alert_text_depth = f"поле Глубина (мм) должно быть заполнено"
# Создаем экземпляр страницы и переходим к созданию дочернего элемента
child_element_page = CreateChildElementTab(browser)
child_element_page.click_create_button()
child_element_page.select_object_class("Стойка")
child_element_page.check_object_class_selected("Стойка")
logger.info("Класс объекта 'Стойка' успешно выбран")
child_element_page.wait_for_timeout(2000)
rack_element_page = CreateRackElementTab(browser) rack_element_page = CreateRackElementTab(browser)
# Проверяем наличие полей стойки # Проверяем наличие полей стойки
rack_element_page.check_rack_fields_presence() rack_element_page.check_rack_fields_presence()
logger.info("Специфичные поля для стойки отображаются корректно")
# 1. Тест: Попытка создания стойки поля - default # 1. Тест: Попытка создания стойки поля - default
logger.info("Тест 1: Попытка создания стойки без заполнения обязательных полей") logger.info("Тест 1: Создание стойки заполнене полей - default")
# Нажимаем кнопку создания без заполнения данных # Нажимаем кнопку создания без заполнения данных
rack_element_page.click_add_button() rack_element_page.click_add_button()
rack_element_page.wait_for_timeout(2000) rack_element_page.wait_for_timeout(2000)
# Проверяем подсветку обязательных полей 'Высота в юнитах' и 'Глубина (мм)' цветом ошибки
#rack_element_page.check_field_not_highlighted_error("Имя")
rack_element_page.check_field_highlighted_error("Высота в юнитах")
rack_element_page.check_field_highlighted_error("Глубина (мм)")
logger.info("Проверка валидации поля 'Имя' временно отключена - ожидаем фикс от разработчика")
# Проверяем наличие alert-окна с сообщением о заполнении Имя
#rack_element_page.alert.check_alert_presence(expected_alert_text_name)
# Закрываем alert-окно Имя с помощью кнопки закрытия
#rack_element_page.alert.close_alert_by_text(expected_alert_text_name)
# Проверяем наличие alert-окна с сообщением о заполнении Высота в юнитах # Проверяем наличие alert-окна с сообщением о заполнении Высота в юнитах
rack_element_page.alert.check_alert_presence(expected_alert_text_height) rack_element_page.alert.check_alert_presence(expected_alert_text_height)
# Закрываем alert-окно Высота в юнитах с помощью кнопки закрытия # Закрываем alert-окно Высота в юнитах с помощью кнопки закрытия
@ -249,10 +235,16 @@ class TestCreateRackElement:
rack_element_page.click_add_button() rack_element_page.click_add_button()
rack_element_page.wait_for_timeout(2000) rack_element_page.wait_for_timeout(2000)
# Проверяем подсветку всех обязательных полей цветом ошибки
#rack_element_page.check_field_highlighted_error("Имя")
rack_element_page.check_field_highlighted_error("Высота в юнитах")
rack_element_page.check_field_highlighted_error("Глубина (мм)")
logger.info("Проверка валидации поля 'Имя' временно отключена - ожидаем фикс от разработчика")
# Проверяем наличие alert-окна с сообщением о заполнении Имя # Проверяем наличие alert-окна с сообщением о заполнении Имя
#rack_element_page.alert.check_alert_presence(expected_alert_text_name) # Ошибка, поле не отслеживается #rack_element_page.alert.check_alert_presence(expected_alert_text_name)
# Закрываем alert-окно Имя с помощью кнопки закрытия # Закрываем alert-окно Имя с помощью кнопки закрытия
#rack_element_page.alert.close_alert_by_text(expected_alert_text_name) # Ошибка, поле не отслеживается #rack_element_page.alert.close_alert_by_text(expected_alert_text_name)
# Проверяем наличие alert-окна с сообщением о заполнении Высота в юнитах # Проверяем наличие alert-окна с сообщением о заполнении Высота в юнитах
rack_element_page.alert.check_alert_presence(expected_alert_text_height) rack_element_page.alert.check_alert_presence(expected_alert_text_height)
@ -267,6 +259,7 @@ class TestCreateRackElement:
# Проверяем, что остались на той же странице # Проверяем, что остались на той же странице
rack_element_page.check_toolbar_title('Создать дочерний элемент в') rack_element_page.check_toolbar_title('Создать дочерний элемент в')
logger.info("Система не позволила создать стойку без имени, высоты и глубины") logger.info("Система не позволила создать стойку без имени, высоты и глубины")
rack_element_page.wait_for_timeout(2000)
# 3. Тест: Заполняем только поле 'Высота в юнитах' # 3. Тест: Заполняем только поле 'Высота в юнитах'
logger.info("Тест 3: Заполняем только поле 'Высота в юнитах'") logger.info("Тест 3: Заполняем только поле 'Высота в юнитах'")
@ -285,10 +278,13 @@ class TestCreateRackElement:
rack_element_page.click_add_button() rack_element_page.click_add_button()
rack_element_page.wait_for_timeout(2000) rack_element_page.wait_for_timeout(2000)
# Проверяем наличие alert-окна с сообщением о заполнении Имя # Проверяем подсветку полей 'Имя' и 'Глубина (мм)' цветом ошибки
#rack_element_page.alert.check_alert_presence(expected_alert_text_name) # Ошибка, поле не отслеживается #rack_element_page.check_field_highlighted_error("Имя")
# Закрываем alert-окно Имя с помощью кнопки закрытия rack_element_page.check_field_not_highlighted_error("Высота в юнитах")
#rack_element_page.alert.close_alert_by_text(expected_alert_text_name) # Ошибка, поле не отслеживается rack_element_page.check_field_highlighted_error("Глубина (мм)")
# Проверяем, что НЕТ alert-окна для поля 'Высота в юнитах' (оно заполнено)
rack_element_page.alert.check_alert_absence(expected_alert_text_height, 1000)
# Проверяем наличие alert-окна с сообщением о заполнении Глубины (мм) # Проверяем наличие alert-окна с сообщением о заполнении Глубины (мм)
rack_element_page.alert.check_alert_presence(expected_alert_text_depth) rack_element_page.alert.check_alert_presence(expected_alert_text_depth)
@ -298,6 +294,7 @@ class TestCreateRackElement:
# Проверяем, что остались на той же странице # Проверяем, что остались на той же странице
rack_element_page.check_toolbar_title('Создать дочерний элемент в') rack_element_page.check_toolbar_title('Создать дочерний элемент в')
logger.info("Система не позволила создать стойку без имени и глубины") logger.info("Система не позволила создать стойку без имени и глубины")
rack_element_page.wait_for_timeout(2000)
# 4. Тест: Заполняем только поле 'Глубина (мм)' # 4. Тест: Заполняем только поле 'Глубина (мм)'
logger.info("Тест 4: Заполняем только поле 'Глубина (мм)'") logger.info("Тест 4: Заполняем только поле 'Глубина (мм)'")
@ -316,10 +313,15 @@ class TestCreateRackElement:
rack_element_page.click_add_button() rack_element_page.click_add_button()
rack_element_page.wait_for_timeout(2000) rack_element_page.wait_for_timeout(2000)
# Проверяем наличие alert-окна с сообщением о заполнении Имя # Проверяем подсветку полей 'Имя' и 'Высота в юнитах' цветом ошибки
#rack_element_page.alert.check_alert_presence(expected_alert_text_name) # Ошибка, поле не отслеживается #rack_element_page.check_field_highlighted_error("Имя")
# Закрываем alert-окно Имя с помощью кнопки закрытия rack_element_page.check_field_highlighted_error("Высота в юнитах")
#rack_element_page.alert.close_alert_by_text(expected_alert_text_name) # Ошибка, поле не отслеживается rack_element_page.check_field_not_highlighted_error("Глубина (мм)")
logger.info("Проверка отсутствия alert-окна для поля 'Глубина (мм)' временно отключена - ожидаем фикс от разработчика")
# Проверяем, что НЕТ alert-окна для поля 'Глубина (мм)' (оно заполнено)
#rack_element_page.alert.check_alert_absence(expected_alert_text_depth, 1000)
#logger.info("Alert-окно для поля 'Глубина (мм)' не появилось - поле заполнено корректно")
# Проверяем наличие alert-окна с сообщением о заполнении Высота в юнитах # Проверяем наличие alert-окна с сообщением о заполнении Высота в юнитах
rack_element_page.alert.check_alert_presence(expected_alert_text_height) rack_element_page.alert.check_alert_presence(expected_alert_text_height)
@ -328,7 +330,7 @@ class TestCreateRackElement:
# Проверяем, что остались на той же странице # Проверяем, что остались на той же странице
rack_element_page.check_toolbar_title('Создать дочерний элемент в') rack_element_page.check_toolbar_title('Создать дочерний элемент в')
logger.info("Система не позволила создать стойку без высоты") logger.info("Система не позволила создать стойку без имени и высоты")
rack_element_page.wait_for_timeout(2000) rack_element_page.wait_for_timeout(2000)
# 5. Тест: Заполняем все обязательные поля # 5. Тест: Заполняем все обязательные поля
@ -344,12 +346,24 @@ class TestCreateRackElement:
depth="1000" depth="1000"
) )
# Проверяем, что ни одно поле не подсвечено цветом ошибки (все заполнены корректно)
rack_element_page.check_field_not_highlighted_error("Имя")
rack_element_page.check_field_not_highlighted_error("Высота в юнитах")
rack_element_page.check_field_not_highlighted_error("Глубина (мм)")
logger.info("Ни одно обязательное поле не подсвечено цветом ошибки - все поля заполнены корректно")
# Нажимаем кнопку создания # Нажимаем кнопку создания
rack_element_page.click_add_button() rack_element_page.click_add_button()
# Ждем завершения создания (должны перейти на другую страницу) # Ждем завершения создания (должны перейти на другую страницу)
rack_element_page.wait_for_timeout(3000) rack_element_page.wait_for_timeout(3000)
# Проверяем, что НЕТ alert-окон для всех обязательных полей (все заполнены корректно)
rack_element_page.alert.check_alert_absence(expected_alert_text_name, 1000)
rack_element_page.alert.check_alert_absence(expected_alert_text_height, 1000)
rack_element_page.alert.check_alert_absence(expected_alert_text_depth, 1000)
logger.info("Alert-окна для обязательных полей не появились - все поля заполнены корректно")
# Проверяем, что ушли со страницы создания (косвенная проверка успешного создания) # Проверяем, что ушли со страницы создания (косвенная проверка успешного создания)
try: try:
rack_element_page.check_toolbar_title('Создать дочерний элемент в') rack_element_page.check_toolbar_title('Создать дочерний элемент в')
@ -358,5 +372,3 @@ class TestCreateRackElement:
logger.info("Страница создания закрыта - стойка успешно создана") logger.info("Страница создания закрыта - стойка успешно создана")
logger.info("Тест проверки обязательных полей завершен успешно") logger.info("Тест проверки обязательных полей завершен успешно")