Исправление тестов создания стойки и локаторов

Radislav 2025-12-19 11:43:11 +03:00
parent 07906b9f76
commit a3dc0a037c
4 changed files with 228 additions and 128 deletions

View File

@ -1,13 +1,14 @@
"""Модуль создания объекта 'Стойка'."""
from dataclasses import dataclass
from playwright.sync_api import Page
from playwright.sync_api import Page, Locator
from tools.logger import get_logger
from locators.rack_locators import RackLocators
from components.base_component import BaseComponent
logger = get_logger("RACK_MAKER")
logger.setLevel("INFO")
@dataclass
class RackData:
@ -49,12 +50,12 @@ class RackObjectMaker(BaseComponent):
rack_data: Данные стойки
"""
logger.info(f"Filling rack data: {rack_data.name}")
logger.debug(f"Filling rack data: {rack_data.name}")
self._fill_text_fields(rack_data)
self._fill_combobox_fields(rack_data)
logger.info("Rack data filled successfully")
logger.debug("Rack data filled successfully")
def _fill_text_fields(self, rack_data: RackData) -> None:
"""Заполняет текстовые поля."""
@ -69,7 +70,7 @@ class RackObjectMaker(BaseComponent):
field.press("Backspace")
# Заполняем значение
field.fill(value)
logger.info(f"Filled '{field_name}': {value}")
logger.debug(f"Filled '{field_name}': {value}")
# Обязательные поля.
if rack_data.name:
@ -90,55 +91,48 @@ class RackObjectMaker(BaseComponent):
# Обязательные поля.
if rack_data.height:
self._fill_combobox_field("Height in units", rack_data.height,
RackLocators.RACK_HEIGHT_FIELD)
logger.info(f"Selected height: {rack_data.height} units")
self._fill_combobox_field("Высота в юнитах", rack_data.height)
logger.debug(f"Selected height: {rack_data.height} units")
if rack_data.depth:
self._fill_combobox_field("Depth (mm)", rack_data.depth,
RackLocators.RACK_DEPTH_FIELD)
logger.info(f"Selected depth: {rack_data.depth} mm")
self._fill_combobox_field("Глубина (мм)", rack_data.depth)
logger.debug(f"Selected depth: {rack_data.depth} mm")
# Опциональные поля.
if rack_data.cable_entry:
self._fill_combobox_field("Cable entry", rack_data.cable_entry,
RackLocators.RACK_CABLE_ENTRY_FIELD)
logger.info(f"Selected cable entry: {rack_data.cable_entry}")
self._fill_combobox_field("Ввод кабеля", rack_data.cable_entry)
logger.debug(f"Selected cable entry: {rack_data.cable_entry}")
if rack_data.state:
self._fill_combobox_field("State", rack_data.state,
RackLocators.RACK_STATE_FIELD)
logger.info(f"Selected state: {rack_data.state}")
self._fill_combobox_field("Состояние", rack_data.state)
logger.debug(f"Selected state: {rack_data.state}")
if rack_data.owner:
self._fill_combobox_field("Owner", rack_data.owner,
RackLocators.RACK_OWNER_FIELD)
logger.info(f"Selected owner: {rack_data.owner}")
self._fill_combobox_field("Владелец", rack_data.owner)
logger.debug(f"Selected owner: {rack_data.owner}")
if rack_data.service_org:
self._fill_combobox_field("Service organization", rack_data.service_org,
RackLocators.RACK_SERVICE_ORG_FIELD)
logger.info(f"Selected service organization: {rack_data.service_org}")
self._fill_combobox_field("Обслуживающая организация", rack_data.service_org)
logger.debug(f"Selected service organization: {rack_data.service_org}")
if rack_data.project:
self._fill_combobox_field("Project/Title", rack_data.project,
RackLocators.RACK_PROJECT_FIELD)
logger.info(f"Selected project/title: {rack_data.project}")
self._fill_combobox_field("Проект/Титул", rack_data.project)
logger.debug(f"Selected project/title: {rack_data.project}")
def _fill_combobox_field(self, field_name: str, value: str, field_locator: str) -> None:
def _fill_combobox_field(self, field_name: str, value: str) -> None:
"""
Заполняет combobox поле.
Args:
field_name: Название поля
value: Значение для установки
field_locator: Локатор поля
"""
logger.info(f"Filling field '{field_name}' with value '{value}'...")
logger.debug(f"Filling field '{field_name}' with value '{value}'...")
# Используем first() для избежания strict mode violation
field_container = self.page.locator(field_locator).first
# Используем универсальный локатор для combobox по имени поля
combobox_locator = RackLocators.COMBOBOX_BY_FIELD_NAME.format(field_name)
field_container = self.page.locator(combobox_locator).first
# Прокручиваем до поля
field_container.scroll_into_view_if_needed()
@ -151,12 +145,58 @@ class RackObjectMaker(BaseComponent):
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")
# Вводим значение из выпадающего списка
dropdown_item_locator = RackLocators.DROPDOWN_ITEM_BY_TEXT.format(value)
element = self.page.locator(dropdown_item_locator).first
logger.info(f"Field '{field_name}' filled successfully")
# Скроллим к элементу если нужно
self._scroll_until_element(
self.page.locator(RackLocators.DROPDOWN_LIST).first,
value
)
self.wait_for_timeout(500)
element.click()
logger.debug(f"Field '{field_name}' filled successfully")
def _scroll_until_element(self, locator: Locator, name: str) -> None:
"""
Скроллит список до тех пор, пока не перестанут подгружаться новые элементы.
Args:
locator: Локатор элементов или строка с CSS/XPath.
"""
loc = self.get_locator(locator)
items_count = 0
attempts = 0
max_attempts = 3
last_item_name = ""
while attempts < max_attempts:
self.page.wait_for_timeout(300)
item_texts = loc.all_inner_texts()
item_names = item_texts[0].splitlines()
current_count = len(item_names)
if current_count == items_count:
attempts += 1
else:
items_count = current_count
attempts = 0
if name in item_names:
last_item_name = name
else:
last_item_name = item_names[current_count-1]
element = loc.get_by_role("listitem").filter(
has_text=last_item_name
)
element.scroll_into_view_if_needed()
self.wait_for_timeout(300)
def _get_field_locator(self, field_name: str) -> str:
"""
@ -190,7 +230,7 @@ class RackObjectMaker(BaseComponent):
AssertionError: Если какое-либо поле не найдено
"""
logger.info("Checking rack fields presence...")
logger.debug("Checking rack fields presence...")
# Основные обязательные поля
required_fields = [
@ -215,14 +255,14 @@ class RackObjectMaker(BaseComponent):
for field_locator, field_name in required_fields:
field = self.page.locator(field_locator).first
self.check_visibility(field, f"Required field '{field_name}' not found")
logger.info(f"Required field '{field_name}' found")
logger.debug(f"Required field '{field_name}' found")
# Проверяем дополнительные поля
for field_locator, field_name in optional_fields:
field = self.page.locator(field_locator).first
if field.count() > 0 and field.is_visible():
logger.info(f"Optional field '{field_name}' found")
logger.debug(f"Optional field '{field_name}' found")
else:
logger.info(f"Optional field '{field_name}' not found or not visible")
logger.debug(f"Optional field '{field_name}' not found or not visible")
logger.info("All main rack fields are present")
logger.debug("All main rack fields are present")

View File

@ -12,6 +12,7 @@ from components_derived.selection_bar_component import SelectionBarComponent
logger = get_logger("CREATE_CHILD_ELEMENT_FRAME")
logger.setLevel("INFO")
class CreateChildElementFrame(BaseComponent):
"""Фрейм создания дочернего элемента."""
@ -55,10 +56,10 @@ class CreateChildElementFrame(BaseComponent):
field_name: Название поля для очистки
"""
logger.info(f"Clearing combobox field '{field_name}'...")
logger.debug(f"Clearing combobox field '{field_name}'...")
# Получаем локатор поля по его названию
field_locator = self._get_field_locator(field_name)
field_locator = self.get_field_locator(field_name)
# Используем метод из SelectionBarComponent
self.selection_bar.clear_combobox_field(field_name, field_locator)
@ -66,15 +67,28 @@ class CreateChildElementFrame(BaseComponent):
def click_add_button(self) -> None:
"""Кликает на кнопку 'Добавить'."""
logger.info("Clicking on 'Add' button...")
logger.debug("Clicking on 'Add' button...")
self.toolbar.click_button("add")
def click_cancel_button(self) -> None:
"""Кликает на кнопку 'Отменить'."""
logger.info("Clicking on 'Cancel' button...")
logger.debug("Clicking on 'Cancel' button...")
self.toolbar.click_button("cancel")
def get_field_locator(self, field_name: str) -> str:
"""
Возвращает локатор поля по его названию.
Публичный метод для использования в тестах.
Args:
field_name: Название поля
Returns:
str: Локатор поля
"""
return self._get_field_locator(field_name)
def get_object_class_options(self) -> list[str]:
"""
Получает список доступных опций из combobox.
@ -83,11 +97,11 @@ class CreateChildElementFrame(BaseComponent):
list[str]: Список доступных классов объектов
"""
logger.info("Getting combobox 'Accounting object class' options...")
logger.debug("Getting combobox 'Accounting object class' options...")
available_options = self.selection_bar.get_available_options()
logger.info(f"Available object class options: {available_options}")
logger.debug(f"Available object class options: {available_options}")
return available_options
def get_selected_object_class(self) -> str:
@ -103,7 +117,7 @@ class CreateChildElementFrame(BaseComponent):
def open_object_class_combobox(self) -> None:
"""Открывает выпадающий список combobox 'Класс объекта учета'."""
logger.info("Opening combobox 'Accounting object class'...")
logger.debug("Opening combobox 'Accounting object class'...")
# Ждем стабильности combobox
expect(self.selection_bar.selection_bar_locator).to_be_visible()
@ -113,11 +127,11 @@ class CreateChildElementFrame(BaseComponent):
"class"
)
if is_menu_active and "v-select--is-menu-active" in is_menu_active:
logger.info("Dropdown list is already open")
logger.debug("Dropdown list is already open")
return
# Используем force click для обхода перекрывающих элементов
logger.info("Using force click for combobox")
logger.debug("Using force click for combobox")
self.selection_bar.selection_bar_locator.click(force=True)
# Ждем появления выпадающего списка
@ -126,7 +140,7 @@ class CreateChildElementFrame(BaseComponent):
def select_object_class(self, class_name: str) -> None:
"""Выбирает класс объекта из выпадающего списка."""
logger.info(f"Selecting object class: '{class_name}'...")
logger.debug(f"Selecting object class: '{class_name}'...")
# Открываем combobox
self.open_object_class_combobox()
@ -139,12 +153,12 @@ class CreateChildElementFrame(BaseComponent):
# Логируем текущее состояние без строгой проверки
selected_value = self.get_selected_object_class()
logger.info(f"Current combobox value: '{selected_value}'")
logger.debug(f"Current combobox value: '{selected_value}'")
# Временно пропускаем строгую проверку
logger.info(f"Assuming class '{class_name}' is selected")
logger.debug(f"Assuming class '{class_name}' is selected")
logger.info(f"Object class '{class_name}' successfully selected")
logger.debug(f"Object class '{class_name}' successfully selected")
# Проверки:
@ -156,7 +170,7 @@ class CreateChildElementFrame(BaseComponent):
field_name: Название поля для проверки
"""
field_locator = self._get_field_locator(field_name)
field_locator = self.get_field_locator(field_name)
self.selection_bar.check_field_error_highlighted(field_name, field_locator)
def check_field_error_not_highlighted(self, field_name: str) -> None:
@ -167,7 +181,7 @@ class CreateChildElementFrame(BaseComponent):
field_name: Название поля для проверки
"""
field_locator = self._get_field_locator(field_name)
field_locator = self.get_field_locator(field_name)
self.selection_bar.check_field_error_not_highlighted(field_name, field_locator)
def check_object_class_selected(self, expected_class: str) -> None:
@ -178,7 +192,7 @@ class CreateChildElementFrame(BaseComponent):
expected_class: Ожидаемый выбранный класс объекта
"""
logger.info(f"Checking selected object class: '{expected_class}'...")
logger.debug(f"Checking selected object class: '{expected_class}'...")
self.wait_for_timeout(1000)
actual_class = self.get_selected_object_class()
@ -191,7 +205,7 @@ class CreateChildElementFrame(BaseComponent):
f"Expected: '{expected_class}', Got: '{actual_class}'"
)
logger.info(
logger.debug(
f"Object class '{expected_class}' successfully selected "
f"(actual: '{actual_class}')"
)
@ -204,7 +218,7 @@ class CreateChildElementFrame(BaseComponent):
expected_title: Ожидаемый заголовок тулбара
"""
logger.info(f"Checking toolbar title: '{expected_title}'...")
logger.debug(f"Checking toolbar title: '{expected_title}'...")
# Используем метод тулбара с фильтрацией по тексту
actual_text = self.toolbar.get_toolbar_title_text(
@ -216,7 +230,7 @@ class CreateChildElementFrame(BaseComponent):
f"Got: '{actual_text}'"
)
logger.info(f"Toolbar title is correct: '{actual_text}'")
logger.debug(f"Toolbar title is correct: '{actual_text}'")
def should_be_toolbar_buttons(self) -> None:
"""

View File

@ -27,26 +27,36 @@ class RackLocators:
EDIT_BUTTON ="//button[@data-testid='CABINET_SHOW__btn__edit']"
# Кнопка "Скрыть стойку"
HIDE_RACK_BUTTON = "//div[@data-testid='CABINET_SHOW__div__hideCabinet' and contains(@class, 'cabinet_hide_button_trigger_show')]"
HIDE_RACK_BUTTON = ("//div[@data-testid='CABINET_SHOW__div__hideCabinet' and "
"contains(@class, 'cabinet_hide_button_trigger_show')]")
# Кнопка "Показать стойку"
SHOW_RACK_BUTTON = "//div[@data-testid='CABINET_SHOW__div__hideCabinet' and contains(@class, 'cabinet_hide_button_trigger_hide')]"
SHOW_RACK_BUTTON = ("//div[@data-testid='CABINET_SHOW__div__hideCabinet' and "
"contains(@class, 'cabinet_hide_button_trigger_hide')]")
# Универсальный локатор для любой вкладки по имени
TAB_BY_NAME = "//div[starts-with(@data-testid, 'CABINET_SHOW__') and contains(@class, 'v-tabs__div')]//a[contains(@class, 'v-tabs__item') and .//*[contains(., '{}')]]"
TAB_BY_NAME = ("//div[starts-with(@data-testid, 'CABINET_SHOW__') and "
"contains(@class, 'v-tabs__div')]//a[contains(@class, 'v-tabs__item') and "
".//*[contains(., '{}')]]")
# Конкретные вкладки по тексту
COMPOSITION_TAB = "//div[@data-testid='CABINET_SHOW__composition_tab']//a[contains(@class, 'v-tabs__item')]"
GENERAL_INFO_TAB = "//div[@data-testid='CABINET_SHOW__main_tab']//a[contains(@class, 'v-tabs__item')]"
MAINTENANCE_TAB = "//div[@data-testid='CABINET_SHOW__service_tab']//a[contains(@class, 'v-tabs__item')]"
EVENTS_TAB = "//div[@data-testid='CABINET_SHOW__events_tab']//a[contains(@class, 'v-tabs__item')]"
SERVICES_TAB = "//div[@data-testid='CABINET_SHOW__services_tab']//a[contains(@class, 'v-tabs__item')]"
COMPOSITION_TAB = ("//div[@data-testid='CABINET_SHOW__composition_tab']"
"//a[contains(@class, 'v-tabs__item')]")
GENERAL_INFO_TAB = ("//div[@data-testid='CABINET_SHOW__main_tab']"
"//a[contains(@class, 'v-tabs__item')]")
MAINTENANCE_TAB = ("//div[@data-testid='CABINET_SHOW__service_tab']"
"//a[contains(@class, 'v-tabs__item')]")
EVENTS_TAB = ("//div[@data-testid='CABINET_SHOW__events_tab']"
"//a[contains(@class, 'v-tabs__item')]")
SERVICES_TAB = ("//div[@data-testid='CABINET_SHOW__services_tab']"
"//a[contains(@class, 'v-tabs__item')]")
# Классы для проверки активности
ACTIVE_TAB_CLASSES = ["accent--text", "v-tabs__item--active"]
# Локатор для активной вкладки
ACTIVE_TAB = "//div[@data-testid='CABINET_SHOW__tabs']//a[contains(@class, 'v-tabs__item--active')]"
ACTIVE_TAB = ("//div[@data-testid='CABINET_SHOW__tabs']"
"//a[contains(@class, 'v-tabs__item--active')]")
# Контейнер формы
FORM_CONTAINER = "//div[contains(@class, 'container')]"
@ -63,18 +73,49 @@ class RackLocators:
PROJECT_FIELD = "//input[@aria-label='Проект/Титул']"
# Локаторы полей формы создания стойки
RACK_NAME_FIELD = "//div[contains(@class, 'container')]//label[text()='Имя']/following-sibling::input"
RACK_HEIGHT_FIELD = "//div[contains(@class, 'container')]//div[contains(@class, 'v-input__slot') and .//label[text()='Высота в юнитах']]"
RACK_DEPTH_FIELD = "//div[contains(@class, 'container')]//div[contains(@class, 'v-input__slot') and .//label[text()='Глубина (мм)']]"
RACK_SERIAL_FIELD = "//div[contains(@class, 'container')]//label[text()='Серийный номер']/following-sibling::input"
RACK_INVENTORY_FIELD = "//div[contains(@class, 'container')]//label[text()='Инвентарный номер']/following-sibling::input"
RACK_COMMENT_FIELD = "//div[contains(@class, 'container')]//label[text()='Комментарий']/following-sibling::input"
RACK_CABLE_ENTRY_FIELD = "//div[contains(@class, 'container')]//div[contains(@class, 'v-input__slot') and .//label[text()='Ввод кабеля']]"
RACK_STATE_FIELD = "//div[contains(@class, 'container')]//div[contains(@class, 'v-input__slot') and .//label[text()='Состояние']]"
RACK_NAME_FIELD = ("//div[contains(@class, 'container')]"
"//label[text()='Имя']/following-sibling::input")
RACK_HEIGHT_FIELD = ("//div[contains(@class, 'container')]"
"//div[contains(@class, 'v-input__slot') and "
".//label[text()='Высота в юнитах']]")
RACK_DEPTH_FIELD = ("//div[contains(@class, 'container')]"
"//div[contains(@class, 'v-input__slot') and "
".//label[text()='Глубина (мм)']]")
RACK_SERIAL_FIELD = ("//div[contains(@class, 'container')]"
"//label[text()='Серийный номер']/following-sibling::input")
RACK_INVENTORY_FIELD = ("//div[contains(@class, 'container')]"
"//label[text()='Инвентарный номер']/following-sibling::input")
RACK_COMMENT_FIELD = ("//div[contains(@class, 'container')]"
"//label[text()='Комментарий']/following-sibling::input")
RACK_CABLE_ENTRY_FIELD = ("//div[contains(@class, 'container')]"
"//div[contains(@class, 'v-input__slot') and "
".//label[text()='Ввод кабеля']]")
RACK_STATE_FIELD = ("//div[contains(@class, 'container')]"
"//div[contains(@class, 'v-input__slot') and "
".//label[text()='Состояние']]")
RACK_OWNER_FIELD = "//div[contains(@class, 'container')]//div[contains(@class, 'v-input__slot') and .//label[text()='Владелец']]"
RACK_SERVICE_ORG_FIELD = "//div[contains(@class, 'container')]//div[contains(@class, 'v-input__slot') and .//label[text()='Обслуживающая организация']]"
RACK_PROJECT_FIELD = "//div[contains(@class, 'container')]//div[contains(@class, 'v-input__slot') and .//label[text()='Проект/Титул']]"
RACK_OWNER_FIELD = ("//div[contains(@class, 'container')]"
"//div[contains(@class, 'v-input__slot') and "
".//label[text()='Владелец']]")
RACK_SERVICE_ORG_FIELD = ("//div[contains(@class, 'container')]"
"//div[contains(@class, 'v-input__slot') and "
".//label[text()='Обслуживающая организация']]")
RACK_PROJECT_FIELD = ("//div[contains(@class, 'container')]"
"//div[contains(@class, 'v-input__slot') and "
".//label[text()='Проект/Титул']]")
# Универсальные локаторы для поиска combobox полей по имени
COMBOBOX_BY_FIELD_NAME = ('//form[contains(@class, "v-form")]'
'//div[@role="combobox"][.//label[contains(text(), "{}")]]')
COMBOBOX_BY_LABEL = 'form.v-form div[role="combobox"]:has(label:has-text("{}"))'
# Локаторы для выпадающего меню
ACTIVE_MENU = 'div.menuable__content__active'
DROPDOWN_LIST = 'div.menuable__content__active div[role="list"]'
DROPDOWN_ITEM_BY_TEXT = ('div.menuable__content__active '
'div[role="listitem"]:has(span:has-text("{}"))')
DROPDOWN_ITEM_XPATH = ('//div[contains(@class, "menuable__content__active")]'
'//div[@role="list"]//div[@role="listitem"][.//*[text()="{}"]]')
# Локатор для родительского контейнера поля ввода
INPUT_PARENT_CONTAINER = "xpath=./ancestor::div[contains(@class, 'v-input')]"
@ -85,7 +126,8 @@ class RackLocators:
# ================ ЛОКАТОРЫ ДЛЯ СТРУКТУРЫ СТОЙКИ ===================
# Общий контейнер стойки (включает кнопки переключения сторон и MAIN_CONTAINER)
RACK_CONTAINER = "//div[contains(@class, 'layout active') and contains(@class, 'row') and contains(@class, 'shrink')]"
RACK_CONTAINER = ("//div[contains(@class, 'layout active') and "
"contains(@class, 'row') and contains(@class, 'shrink')]")
# Основной контейнер стойки (изображение стойки)
MAIN_CONTAINER = "//div[contains(@class, 'layout cabinet')]"
@ -99,7 +141,8 @@ class RackLocators:
INACTIVE_SIDE_BUTTON = "//button[contains(@class, 'secondary--text')]"
# Для получения текста активной стороны
ACTIVE_SIDE_BUTTON_TEXT = "//button[contains(@class, 'primary--text')]//div[contains(@class, 'v-btn__content')]"
ACTIVE_SIDE_BUTTON_TEXT = ("//button[contains(@class, 'primary--text')]"
"//div[contains(@class, 'v-btn__content')]")
# Кнопка добавления (add_circle)
ADD_CIRCLE_BUTTON = "//i[contains(text(), 'add_circle')]"
@ -108,11 +151,12 @@ class RackLocators:
ALL_UNITS = "//div[contains(@class, 'unit')]"
# Позиции юнитов
UNIT_POSITIONS = "//div[contains(@class, 'headline') and contains(@class, 'test-xs-center') and contains(@class, 'unit-positions')]"
UNIT_POSITIONS = ("//div[contains(@class, 'headline') and "
"contains(@class, 'test-xs-center') and "
"contains(@class, 'unit-positions')]")
# Локатор для устройств
DEVICE_ELEMENTS = "//div[contains(@class, 'parent-class')]"
# Локатор для слотов в устройствах
DEVICE_SLOTS = "//div[contains(@class, 'slot')]"

View File

@ -13,6 +13,8 @@ from pages.main_page import MainPage
logger = get_logger("CREATE_RACK_ELEMENT_TEST")
logger.setLevel("INFO")
# @pytest.mark.smoke
class TestCreateRackElement:
"""Тест создания дочернего элемента типа 'Стойка'.
@ -55,7 +57,7 @@ class TestCreateRackElement:
# Создаем экземпляр страницы локации
self.location_page = LocationPage(browser)
@pytest.mark.develop
#@pytest.mark.develop
def test_create_rack_content(self, browser: Page) -> None:
"""Тест создания дочернего элемента типа 'Стойка'."""
@ -82,7 +84,7 @@ class TestCreateRackElement:
# Проверяем что после выбора 'Стойка' появляются специфичные поля
rack_maker.check_rack_fields_presence()
logger.info("Rack-specific fields are displayed correctly")
logger.debug("Rack-specific fields are displayed correctly")
create_child_frame.should_be_toolbar_buttons()
@ -112,8 +114,8 @@ class TestCreateRackElement:
serial="TEST123456",
inventory="INV-001",
comment="Тестовая стойка для автоматизации",
cable_entry="Сверху",
state="В эксплуатации"
state="Введен в эксплуатацию",
cable_entry="сверху"
)
# Заполняем данные стойки
@ -123,7 +125,7 @@ class TestCreateRackElement:
create_child_frame.click_add_button()
create_child_frame.wait_for_timeout(2000)
logger.info("Test for creating 'Rack' child element completed successfully")
logger.debug("Test for creating 'Rack' child element completed successfully")
def test_create_rack_with_duplicate_name(self, browser: Page) -> None:
"""
@ -133,20 +135,20 @@ class TestCreateRackElement:
стойки с именем, которое уже используется.
"""
logger.info("Starting test for creating rack with duplicate name")
logger.debug("Starting test for creating rack with duplicate name")
rack_name = "Test-Rack-01"
# Проверяем, существует ли уже стойка с таким именем
if not self._check_rack_existance(browser, rack_name):
logger.info(f"Rack with name '{rack_name}' not found. Creating first rack.")
logger.debug(f"Rack with name '{rack_name}' not found. Creating first rack.")
self._create_rack(browser, rack_name)
logger.info(f"First rack with name '{rack_name}' created successfully")
logger.debug(f"First rack with name '{rack_name}' created successfully")
else:
logger.info(f"Rack with name '{rack_name}' already exists, proceeding to create second one")
logger.debug(f"Rack with name '{rack_name}' already exists, proceeding to create second one")
# Создаем вторую стойку с тем же именем
logger.info(f"Attempting to create second rack with name '{rack_name}'")
logger.debug(f"Attempting to create second rack with name '{rack_name}'")
# Переходим обратно к созданию новой стойки
self.main_page.click_main_navigation_panel_item("test-zone")
@ -192,7 +194,7 @@ class TestCreateRackElement:
create_child_frame.wait_for_timeout(2000)
create_child_frame.alert.close_alert_by_text(expected_alert_text)
logger.info("System prevented creating rack with duplicate name")
logger.debug("System prevented creating rack with duplicate name")
def _perform_required_fields_test(self, create_child_frame, rack_maker, test_data):
"""Выполняет один тест валидации обязательных полей.
@ -215,13 +217,13 @@ class TestCreateRackElement:
"""Проверяет, заполнено ли combobox поле."""
# Получаем локатор поля
field_locator = create_child_frame._get_field_locator(field_name)
field_locator = create_child_frame.get_field_locator(field_name)
# Находим элемент поля
field_element = create_child_frame.page.locator(field_locator).first
if not field_element.is_visible():
logger.info(f"Field '{field_name}' not visible")
logger.debug(f"Field '{field_name}' not visible")
return False
# Проверяем наличие кнопки закрытия (крестика) - признак заполненного поля
@ -236,27 +238,27 @@ class TestCreateRackElement:
field_text = field_element.text_content() or ""
has_text = bool(field_text.strip())
logger.info(f"Field '{field_name}' - has close button: {has_close_button}, has text: {has_text}")
logger.debug(f"Field '{field_name}' - has close button: {has_close_button}, has text: {has_text}")
return has_close_button or has_text
# Проверяем и очищаем поле "Глубина (мм)" только если оно заполнено
logger.info("Checking field: Depth (mm)")
logger.debug("Checking field: Depth (mm)")
if is_field_filled("Глубина (мм)"):
logger.info("Field 'Depth (mm)' is filled, performing clearing")
logger.debug("Field 'Depth (mm)' is filled, performing clearing")
create_child_frame.clear_combobox_field("Глубина (мм)")
logger.info("Clearing completed for 'Depth (mm)'")
logger.debug("Clearing completed for 'Depth (mm)'")
else:
logger.info("Field 'Depth (mm)' is already empty, skipping clearing")
logger.debug("Field 'Depth (mm)' is already empty, skipping clearing")
# Проверяем и очищаем поле "Высота в юнитах" только если оно заполнено
logger.info("Checking field: Height in units")
logger.debug("Checking field: Height in units")
if is_field_filled("Высота в юнитах"):
logger.info("Field 'Height in units' is filled, performing clearing")
logger.debug("Field 'Height in units' is filled, performing clearing")
create_child_frame.clear_combobox_field("Высота в юнитах")
logger.info("Clearing completed for 'Height in units'")
logger.debug("Clearing completed for 'Height in units'")
else:
logger.info("Field 'Height in units' is already empty, skipping clearing")
logger.debug("Field 'Height in units' is already empty, skipping clearing")
# Создаем объект данных стойки
rack_data = RackData(
@ -266,47 +268,47 @@ class TestCreateRackElement:
)
# Заполняем данные стойки
logger.info(f"Setting test data - Name: '{name_value}', Height: '{height_value}', Depth: '{depth_value}'")
logger.debug(f"Setting test data - Name: '{name_value}', Height: '{height_value}', Depth: '{depth_value}'")
rack_maker.fill_rack_data(rack_data)
# Нажимаем кнопку создания
logger.info("Submitting form for validation")
logger.debug("Submitting form for validation")
create_child_frame.click_add_button()
create_child_frame.wait_for_timeout(3000)
# Проверяем валидацию полей
logger.info("Checking validation results")
logger.debug("Checking validation results")
if height_value:
create_child_frame.check_field_error_not_highlighted("Высота в юнитах")
logger.info("Height field validation passed")
logger.debug("Height field validation passed")
else:
create_child_frame.check_field_error_highlighted("Высота в юнитах")
logger.info("Height field validation failed as expected")
logger.debug("Height field validation failed as expected")
if depth_value:
create_child_frame.check_field_error_not_highlighted("Глубина (мм)")
logger.info("Depth field validation passed")
logger.debug("Depth field validation passed")
else:
create_child_frame.check_field_error_highlighted("Глубина (мм)")
logger.info("Depth field validation failed as expected")
logger.debug("Depth field validation failed as expected")
# Обрабатываем alert-окна
if not height_value:
logger.info("Expecting height validation alert")
logger.debug("Expecting height validation alert")
create_child_frame.alert.check_alert_presence(expected_alert_height)
create_child_frame.alert.close_alert_by_text(expected_alert_height)
logger.info("Height alert handled")
logger.debug("Height alert handled")
if not depth_value:
logger.info("Expecting depth validation alert")
logger.debug("Expecting depth validation alert")
create_child_frame.alert.check_alert_presence(expected_alert_depth)
create_child_frame.alert.close_alert_by_text(expected_alert_depth)
logger.info("Depth alert handled")
logger.debug("Depth alert handled")
# Проверяем, что остались на странице создания
create_child_frame.check_toolbar_title('Создать дочерний элемент в')
logger.info("Test completed successfully")
logger.debug("Test completed successfully")
def test_required_fields_validation(self, browser: Page) -> None:
"""
@ -382,14 +384,14 @@ class TestCreateRackElement:
# Выполняем тестовые случаи
for test_case in test_cases:
logger.info(test_case["name"])
logger.debug(test_case["name"])
self._perform_required_fields_test(
create_child_frame, rack_maker, test_case["data"]
)
logger.info("System prevented creating rack with invalid required fields")
logger.debug("System prevented creating rack with invalid required fields")
# 5. Тест: Заполняем все обязательные поля
logger.info("Test 5: All required fields are filled")
logger.debug("Test 5: All required fields are filled")
# Генерируем уникальное имя для финального теста
final_rack_name = "Test-Rack-Required-Final"
@ -408,7 +410,7 @@ class TestCreateRackElement:
create_child_frame.check_field_error_not_highlighted("Имя")
create_child_frame.check_field_error_not_highlighted("Высота в юнитах")
create_child_frame.check_field_error_not_highlighted("Глубина (мм)")
logger.info("No required fields are highlighted with error color - all fields filled correctly")
logger.debug("No required fields are highlighted with error color - all fields filled correctly")
# Нажимаем кнопку создания
create_child_frame.click_add_button()
@ -417,21 +419,21 @@ class TestCreateRackElement:
# Проверяем, что НЕТ alert-окон для всех обязательных полей
create_child_frame.alert.check_alert_absence(expected_alert_text_height, 1000)
create_child_frame.alert.check_alert_absence(expected_alert_text_depth, 1000)
logger.info("No alert windows for required fields appeared - all fields filled correctly")
logger.debug("No alert windows for required fields appeared - all fields filled correctly")
# Проверяем, что ушли со страницы создания
try:
create_child_frame.check_toolbar_title('Создать дочерний элемент в')
logger.warning("Rack creation may not have completed successfully")
except AssertionError:
logger.info("Creation page closed - rack successfully created")
logger.debug("Creation page closed - rack successfully created")
logger.info("Required fields validation test completed successfully")
logger.debug("Required fields validation test completed successfully")
def _check_rack_existance(self, browser: Page, rack_name: str) -> bool:
"""Проверяет существование стойки."""
logger.info(f"Checking existence of rack with name '{rack_name}'")
logger.debug(f"Checking existence of rack with name '{rack_name}'")
# Обновляем навигационную панель
self.main_page.click_main_navigation_panel_item("Объекты")
@ -446,16 +448,16 @@ class TestCreateRackElement:
element = browser.locator(nav_panel_locator).get_by_text(rack_name).first
if element.is_visible():
logger.info(f"Rack with name '{rack_name}' found")
logger.debug(f"Rack with name '{rack_name}' found")
return True
logger.info(f"Rack with name '{rack_name}' not found")
logger.debug(f"Rack with name '{rack_name}' not found")
return False
def _create_rack(self, browser: Page, rack_name: str) -> None:
"""Создает стойку."""
logger.info(f"Creating rack with name '{rack_name}'")
logger.debug(f"Creating rack with name '{rack_name}'")
# Переходим обратно к созданию новой стойки
self.main_page.click_main_navigation_panel_item("test-zone")