Реализация создания элемента rack

- Рефакторинг компонентов для работы с rack элементами
- Удаление устаревшего компонента modal_add_local_user
- Обновление локаторов и тестов
Radislav 2026-01-11 12:29:56 +03:00
parent a3dc0a037c
commit 4cc43f08a6
8 changed files with 416 additions and 597 deletions

View File

@ -28,6 +28,41 @@ class BaseComponent:
self.page = page
# Действия:
def get_input_fields_locators(self, container_locator: Locator, search_class: str = "layout") -> dict:
"""Получение объекта словаря имя поля ввода : Locator.
Args:
container_locator: объект Locator.
search_class: css класс для поиска (по умолчанию layout)
Returns:
dict: словарь имя поля ввода : Locator xs8 контейнера
"""
fields_locators = {}
if search_class == "layout":
# Поиск по структуре layout -> xs4 (label) + xs8 (input контейнер)
layout_containers = container_locator.locator("div.layout")
for i in range(layout_containers.count()):
layout_container = layout_containers.nth(i)
xs4_div = layout_container.locator("div.flex.xs4").first
xs8_div = layout_container.locator("div.flex.xs8").first
if xs4_div.count() > 0 and xs8_div.count() > 0:
# Ищем input в label_container
label_input = xs4_div.locator("div.v-text-field__slot > input").first
if label_input.count() > 0:
label_text = label_input.input_value().strip()
if label_text:
# Возвращаем xs8 контейнер
fields_locators[label_text] = xs8_div
return fields_locators
def get_locator(self, locator: str | Locator) -> Locator:
"""Получение объекта Locator из строки или существующего Locator.

View File

@ -8,7 +8,7 @@ from components.base_component import BaseComponent
logger = get_logger("RACK_MAKER")
logger.setLevel("INFO")
#logger.setLevel("INFO")
@dataclass
class RackData:
@ -60,31 +60,79 @@ class RackObjectMaker(BaseComponent):
def _fill_text_fields(self, rack_data: RackData) -> None:
"""Заполняет текстовые поля."""
def clear_and_fill(locator, value: str, field_name: str):
logger.debug("Filling text fields...")
# Получаем контейнер формы (второй элемент)
container_locator = self.page.locator(RackLocators.FORM_INPUT_CONTAINER).nth(1)
if container_locator.count() == 0:
logger.error("Form container not found")
raise ValueError("Form container not found")
# Используем метод из базового класса для получения всех полей
fields_locators = self.get_input_fields_locators(container_locator, "layout")
logger.debug(f"Available text fields: {list(fields_locators.keys())}")
def clear_and_fill(field_name: str, value: str):
"""Очищает поле и заполняет его значением."""
field = self.page.locator(locator).first
if not value:
logger.debug(f"Skipping empty value for field '{field_name}'")
return
# Получаем xs8 контейнер поля
field_container = fields_locators.get(field_name)
if not field_container:
logger.warning(f"Field '{field_name}' not found in form. Available fields: {list(fields_locators.keys())}")
return
# Находим input внутри контейнера
input_field = field_container.locator("input").first
if input_field.count() == 0:
logger.warning(f"Input element not found in container for field '{field_name}'")
return
# Проверяем видимость
if not input_field.is_visible():
logger.debug(f"Field '{field_name}' is not visible, scrolling into view...")
input_field.scroll_into_view_if_needed()
self.wait_for_timeout(500)
# Проверяем, не disabled ли поле
is_disabled = input_field.get_attribute("disabled")
is_readonly = input_field.get_attribute("readonly")
if is_disabled or is_readonly:
logger.warning(f"Field '{field_name}' is disabled or readonly")
return
# Очищаем поле
field.click()
field.press("Control+A")
field.press("Backspace")
input_field.click()
input_field.press("Control+A")
input_field.press("Backspace")
# Заполняем значение
field.fill(value)
input_field.fill(value)
logger.debug(f"Filled '{field_name}': {value}")
# Обязательные поля.
# Обязательные поля
if rack_data.name:
clear_and_fill(RackLocators.RACK_NAME_FIELD, rack_data.name, "Name")
clear_and_fill("Имя", rack_data.name)
# Опциональные поля.
# Опциональные поля
if rack_data.serial:
clear_and_fill(RackLocators.RACK_SERIAL_FIELD, rack_data.serial, "Serial number")
clear_and_fill("Серийный номер", rack_data.serial)
if rack_data.inventory:
clear_and_fill(RackLocators.RACK_INVENTORY_FIELD, rack_data.inventory, "Inventory number")
clear_and_fill("Инвентарный номер", rack_data.inventory)
if rack_data.comment:
clear_and_fill(RackLocators.RACK_COMMENT_FIELD, rack_data.comment, "Comment")
clear_and_fill("Комментарий", rack_data.comment)
logger.debug("Text fields filled successfully")
def _fill_combobox_fields(self, rack_data: RackData) -> None:
"""Заполняет combobox поля."""
@ -128,11 +176,20 @@ class RackObjectMaker(BaseComponent):
value: Значение для установки
"""
logger.debug(f"Filling field '{field_name}' with value '{value}'...")
# Получаем контейнер формы (второй элемент)
container_locator = self.page.locator(RackLocators.FORM_INPUT_CONTAINER).nth(1)
# Используем универсальный локатор для combobox по имени поля
combobox_locator = RackLocators.COMBOBOX_BY_FIELD_NAME.format(field_name)
field_container = self.page.locator(combobox_locator).first
# Используем метод из базового класса BaseComponent
fields_locators = self.get_input_fields_locators(container_locator, "layout")
# Получаем контейнер поля по его названию
field_container = fields_locators.get(field_name)
if not field_container:
logger.error(f"Field '{field_name}' not found in form. Available fields: {list(fields_locators.keys())}")
raise ValueError(f"Field '{field_name}' not found in form")
logger.debug(f"Filling field '{field_name}' with value '{value}'...")
# Прокручиваем до поля
field_container.scroll_into_view_if_needed()
@ -141,8 +198,11 @@ class RackObjectMaker(BaseComponent):
# Проверяем видимость поля
self.check_visibility(field_container, f"Field '{field_name}' not found")
# Кликаем и вводим значение
field_container.click(force=True)
# Находим кнопку открытия выпадающего списка внутри контейнера поля
open_button = field_container.locator(".v-input__append-inner").first
# Кликаем для открытия выпадающего списка
open_button.click(force=True)
self.wait_for_timeout(1000)
# Вводим значение из выпадающего списка
@ -198,28 +258,6 @@ class RackObjectMaker(BaseComponent):
self.wait_for_timeout(300)
def _get_field_locator(self, field_name: str) -> str:
"""
Возвращает локатор поля по его названию.
Args:
field_name: Название поля
Returns:
str: Локатор поля
"""
field_map = {
"Имя": RackLocators.RACK_NAME_FIELD,
"Высота в юнитах": RackLocators.RACK_HEIGHT_FIELD,
"Глубина (мм)": RackLocators.RACK_DEPTH_FIELD
}
if field_name not in field_map:
raise ValueError(f"Field '{field_name}' is not supported")
return field_map[field_name]
# Проверки:
def check_rack_fields_presence(self) -> None:
@ -232,37 +270,53 @@ class RackObjectMaker(BaseComponent):
logger.debug("Checking rack fields presence...")
# Основные обязательные поля
required_fields = [
(RackLocators.RACK_NAME_FIELD, "Name"),
(RackLocators.RACK_HEIGHT_FIELD, "Height in units"),
(RackLocators.RACK_DEPTH_FIELD, "Depth (mm)")
# Получаем контейнер формы (второй элемент)
container_locator = self.page.locator(RackLocators.FORM_INPUT_CONTAINER).nth(1)
# Проверяем наличие контейнера
assert container_locator.count() > 0, "Form container not found"
# Используем метод из базового класса BaseComponent для получения всех полей
fields_locators = self.get_input_fields_locators(container_locator, "layout")
logger.debug(f"Found fields in form: {list(fields_locators.keys())}")
# Список ожидаемых полей для стойки
expected_fields = [
"Имя",
"Высота в юнитах",
"Глубина (мм)",
"Серийный номер",
"Инвентарный номер",
"Комментарий",
"Ввод кабеля",
"Состояние",
"Владелец",
"Обслуживающая организация",
"Проект/Титул"
]
# Дополнительные поля
optional_fields = [
(RackLocators.RACK_SERIAL_FIELD, "Serial number"),
(RackLocators.RACK_INVENTORY_FIELD, "Inventory number"),
(RackLocators.RACK_COMMENT_FIELD, "Comment"),
(RackLocators.RACK_CABLE_ENTRY_FIELD, "Cable entry"),
(RackLocators.RACK_STATE_FIELD, "State"),
(RackLocators.RACK_OWNER_FIELD, "Owner"),
(RackLocators.RACK_SERVICE_ORG_FIELD, "Service organization"),
(RackLocators.RACK_PROJECT_FIELD, "Project/Title")
]
# Проверяем наличие обязательных полей с помощью assert
required_fields = ["Имя", "Высота в юнитах", "Глубина (мм)"]
# Проверяем обязательные поля
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.debug(f"Required field '{field_name}' found")
for field_name in required_fields:
# Проверяем наличие поля в словаре
assert field_name in fields_locators, f"Required field '{field_name}' not 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.debug(f"Optional field '{field_name}' found")
field_container = fields_locators[field_name]
# check_visibility внутри использует expect, который тоже вызывает AssertionError
self.check_visibility(field_container, f"Required field '{field_name}' not visible")
logger.debug(f"Required field '{field_name}' found and visible")
# Проверяем наличие дополнительных полей (только логгирование)
for field_name in expected_fields:
if field_name in fields_locators:
field_container = fields_locators[field_name]
if field_container.is_visible():
logger.debug(f"Optional field '{field_name}' found and visible")
else:
logger.debug(f"Optional field '{field_name}' not found or not visible")
logger.debug(f"Optional field '{field_name}' found but not visible")
else:
logger.debug(f"Optional field '{field_name}' not found in form")
logger.debug("All main rack fields are present")

View File

@ -1,9 +1,10 @@
"""Модуль фрейма создания дочернего элемента."""
import re
from playwright.sync_api import expect, Page
from playwright.sync_api import expect, Page, Locator
from tools.logger import get_logger
from locators.rack_locators import RackLocators
from locators.selection_bar_locators import SelectionBarLocators
from components.alert_component import AlertComponent
from components.base_component import BaseComponent
from components.toolbar_component import ToolbarComponent
@ -12,8 +13,6 @@ from components_derived.selection_bar_component import SelectionBarComponent
logger = get_logger("CREATE_CHILD_ELEMENT_FRAME")
logger.setLevel("INFO")
class CreateChildElementFrame(BaseComponent):
"""Фрейм создания дочернего элемента."""
@ -58,11 +57,41 @@ class CreateChildElementFrame(BaseComponent):
logger.debug(f"Clearing combobox field '{field_name}'...")
# Получаем локатор поля по его названию
field_locator = self.get_field_locator(field_name)
# Получаем контейнер формы
container_locator = self.page.locator(RackLocators.FORM_INPUT_CONTAINER).nth(1)
# Используем метод из SelectionBarComponent
self.selection_bar.clear_combobox_field(field_name, field_locator)
# Используем метод get_input_fields_locators для получения всех полей
fields_locators = self.get_input_fields_locators(container_locator, "layout")
if field_name not in fields_locators:
logger.warning(f"Field '{field_name}' not found in form")
return
# Получаем xs8 контейнер поля
field_container = fields_locators[field_name]
# Прокручиваем до поля
field_container.scroll_into_view_if_needed()
self.wait_for_timeout(500)
# Проверяем видимость
if not field_container.is_visible():
logger.debug(f"Field '{field_name}' is not visible after scrolling")
return
# Ищем кнопку закрытия (крестик) внутри контейнера поля
close_button = field_container.locator("i.mdi-close").first
# Проверяем наличие и видимость кнопки закрытия
if close_button.count() > 0:
logger.debug(f"Found close button for field '{field_name}'")
# Если кнопка закрытия видима - кликаем на нее
close_button.click(force=True)
self.wait_for_timeout(500)
logger.debug(f"Combobox field '{field_name}' cleared using close button")
else:
logger.debug(f"Close button (i.mdi-close) not found for field '{field_name}'")
def click_add_button(self) -> None:
"""Кликает на кнопку 'Добавить'."""
@ -76,34 +105,6 @@ class CreateChildElementFrame(BaseComponent):
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.
Returns:
list[str]: Список доступных классов объектов
"""
logger.debug("Getting combobox 'Accounting object class' options...")
available_options = self.selection_bar.get_available_options()
logger.debug(f"Available object class options: {available_options}")
return available_options
def get_selected_object_class(self) -> str:
"""
Получает выбранный класс объекта учета.
@ -115,27 +116,28 @@ class CreateChildElementFrame(BaseComponent):
return self.selection_bar.get_selection_bar_title()
def open_object_class_combobox(self) -> None:
"""Открывает выпадающий список combobox 'Класс объекта учета'."""
"""Открывает выпадающий список combobox."""
logger.debug("Opening combobox 'Accounting object class'...")
container_locator = self.page.locator(RackLocators.FORM_INPUT_CONTAINER)
# Ждем стабильности combobox
expect(self.selection_bar.selection_bar_locator).to_be_visible()
# Используем метод из базового класса BaseComponent
fields_locators = self.get_input_fields_locators(container_locator, "layout")
combobox_container = fields_locators.get("Класс объекта учета")
# Проверяем, не открыт ли уже выпадающий список
is_menu_active = self.selection_bar.selection_bar_locator.get_attribute(
"class"
)
if is_menu_active and "v-select--is-menu-active" in is_menu_active:
logger.debug("Dropdown list is already open")
if not combobox_container:
logger.error("Combobox 'Класс объекта учета' not found")
return
# Используем force click для обхода перекрывающих элементов
logger.debug("Using force click for combobox")
self.selection_bar.selection_bar_locator.click(force=True)
# Проверяем, не открыт ли уже выпадающий список
menu_selector = "div.v-menu__content.menuable__content__active"
is_menu_open = self.page.locator(menu_selector).count() > 0
# Ждем появления выпадающего списка
self.wait_for_timeout(1500)
if not is_menu_open:
# Используем OPEN_PARAMETERS_LIST_BUTTON из SelectionBarLocators
open_button = combobox_container.locator(SelectionBarLocators.OPEN_PARAMETERS_LIST_BUTTON)
open_button.click(force=True, timeout=5000)
else:
logger.debug("Combobox menu is already open")
def select_object_class(self, class_name: str) -> None:
"""Выбирает класс объекта из выпадающего списка."""
@ -151,13 +153,6 @@ class CreateChildElementFrame(BaseComponent):
# Даем время на применение выбора
self.wait_for_timeout(3000)
# Логируем текущее состояние без строгой проверки
selected_value = self.get_selected_object_class()
logger.debug(f"Current combobox value: '{selected_value}'")
# Временно пропускаем строгую проверку
logger.debug(f"Assuming class '{class_name}' is selected")
logger.debug(f"Object class '{class_name}' successfully selected")
# Проверки:
@ -170,8 +165,30 @@ class CreateChildElementFrame(BaseComponent):
field_name: Название поля для проверки
"""
field_locator = self.get_field_locator(field_name)
self.selection_bar.check_field_error_highlighted(field_name, field_locator)
logger.debug(f"Checking field '{field_name}' for error highlighting...")
# Получаем контейнеры всех полей
container_locator = self.page.locator(RackLocators.FORM_INPUT_CONTAINER)
fields_locators = self.get_input_fields_locators(container_locator, "layout")
# Получаем контейнер конкретного поля
field_container = fields_locators.get(field_name)
if not field_container:
raise ValueError(f"Field '{field_name}' not found in form")
# Ищем элементы с классами ошибки внутри контейнера поля
error_elements = field_container.locator(SelectionBarLocators.ERROR_CSS_SELECTORS)
# Проверяем, что есть хотя бы один элемент с классом ошибки
has_error = error_elements.count() > 0
assert has_error, (
f"Field '{field_name}' has no elements with error classes. "
f"Expected to find elements matching: {SelectionBarLocators.ERROR_CSS_SELECTORS}"
)
logger.debug(f"Field '{field_name}' is correctly highlighted with error color")
def check_field_error_not_highlighted(self, field_name: str) -> None:
"""
@ -181,8 +198,30 @@ class CreateChildElementFrame(BaseComponent):
field_name: Название поля для проверки
"""
field_locator = self.get_field_locator(field_name)
self.selection_bar.check_field_error_not_highlighted(field_name, field_locator)
logger.debug(f"Checking field '{field_name}' for absence of error highlighting...")
# Получаем контейнеры всех полей
container_locator = self.page.locator(RackLocators.FORM_INPUT_CONTAINER)
fields_locators = self.get_input_fields_locators(container_locator, "layout")
# Получаем контейнер конкретного поля
field_container = fields_locators.get(field_name)
if not field_container:
raise ValueError(f"Field '{field_name}' not found in form")
# Ищем элементы с классами ошибки внутри контейнера поля
error_elements = field_container.locator(SelectionBarLocators.ERROR_CSS_SELECTORS)
# Проверяем, что нет элементов с классами ошибки
has_error = error_elements.count() > 0
assert not has_error, (
f"Field '{field_name}' has {error_elements.count()} elements with error classes. "
f"Expected no elements matching: {SelectionBarLocators.ERROR_CSS_SELECTORS}"
)
logger.debug(f"Field '{field_name}' correctly has no error highlighting")
def check_object_class_selected(self, expected_class: str) -> None:
"""
@ -245,33 +284,3 @@ class CreateChildElementFrame(BaseComponent):
self.toolbar.check_button_tooltip("cancel", "Отменить")
self.toolbar.click_button("cancel")
self.wait_for_timeout(2000)
def _get_field_locator(self, field_name: str) -> str:
"""
Возвращает локатор поля по его названию.
Args:
field_name: Название поля
Returns:
str: Локатор поля
"""
field_map = {
"Имя": RackLocators.RACK_NAME_FIELD,
"Высота в юнитах": RackLocators.RACK_HEIGHT_FIELD,
"Глубина (мм)": RackLocators.RACK_DEPTH_FIELD,
"Серийный номер": RackLocators.RACK_SERIAL_FIELD,
"Инвентарный номер": RackLocators.RACK_INVENTORY_FIELD,
"Комментарий": RackLocators.RACK_COMMENT_FIELD,
"Ввод кабеля": RackLocators.RACK_CABLE_ENTRY_FIELD,
"Состояние": RackLocators.RACK_STATE_FIELD,
"Владелец": RackLocators.RACK_OWNER_FIELD,
"Обслуживающая организация": RackLocators.RACK_SERVICE_ORG_FIELD,
"Проект/Титул": RackLocators.RACK_PROJECT_FIELD
}
if field_name not in field_map:
raise ValueError(f"Locator for field '{field_name}' not found")
return field_map[field_name]

View File

@ -1,297 +0,0 @@
"""Модуль modal_add_local_user содержит класс для работы
с модальным окном добавления локального пользователя.
Класс AddLocalUserModalWindow наследует базовый функционал ModalWindowComponent
и реализует специфичные методы для работы с формами добавления пользователей.
"""
import re
from playwright.sync_api import Page
from tools.logger import get_logger
from locators.modal_window_locators import ModalWindowLocators
from elements.text_input_element import TextInput
from elements.text_element import Text
from elements.checkbox_element import Checkbox
from data.roles_dict import roles_dict
from components.modal_window_component import ModalWindowComponent
from components.dropdown_list_component import DropdownList
from components.confirm_component import ConfirmComponent
logger = get_logger("ADD_LOCAL_USER_MODAL_WINDOW")
class AddLocalUserModalWindow(ModalWindowComponent):
"""Модальное окно добавления нового пользователя.
Наследует ModalWindowComponent и добавляет элементы формы:
- Поля ввода (имя, пароль, email и др.)
- Чекбоксы (Active Directory, Блокировка, Push-уведомления)
- Выпадающий список ролей
- Кнопки действий
"""
def __init__(self, page: Page):
"""Инициализирует элементы формы добавления пользователя."""
super().__init__(page)
# Локаторы элементов формы
text_field_locator = ModalWindowLocators.TEXT_FIELD_INPUT_FORM_USER_DATA
input_form_locator = ModalWindowLocators.INPUT_FORM_USER_DATA
# Настройка заголовка и кнопки закрытия тулбара
self.window_title = "Добавить нового пользователя"
locator_button_toolbar_close = (
self.page.get_by_role("navigation")
.filter(has_text=re.compile(self.window_title))
.get_by_role("button")
)
self.add_toolbar_title(self.window_title)
self.add_toolbar_button(locator_button_toolbar_close, "close")
# Поле Имя
loc = f"{input_form_locator}/div[2]/{text_field_locator}"
name_input = TextInput(page, self.page.locator(loc), "name_input")
self.add_content_item("name_input", name_input)
# Метка "Блокировка"
label_blocking_locator = self.page.locator(input_form_locator). \
locator("//label").get_by_text("Блокировка")
label_blocking = Text(
page,
label_blocking_locator,
"blocking_checkbox_label"
)
self.add_content_item("blocking_checkbox_label", label_blocking)
# Чекбокс "Блокировка"
checkbox_blocking = Checkbox(
page,
label_blocking_locator.locator("../..").get_by_role("checkbox"),
"blocking"
)
self.add_content_item("blocking_checkbox", checkbox_blocking)
# Поле Роль
role_loc = self.page.locator(input_form_locator).get_by_role("combobox").nth(0)
role_input = TextInput(page, role_loc, "role_input")
self.add_content_item("role_input", role_input)
self.add_content_item("roles_list", DropdownList(page))
# Поле Пароль
loc = f"{input_form_locator}/div[5]/{text_field_locator}"
password_input = TextInput(page, self.page.locator(loc), "password_input")
self.add_content_item("password_input", password_input)
# Поле Комментарий
loc = f"{input_form_locator}/div[6]/{text_field_locator}"
commentary_input = TextInput(page, self.page.locator(loc), "commentary_input")
self.add_content_item("commentary_input", commentary_input)
# Поле E-mail
loc = f"{input_form_locator}/div[7]/{text_field_locator}"
email_input = TextInput(page, self.page.locator(loc), "email_input")
self.add_content_item("email_input", email_input)
# Поле Номер для СМС
loc = f"{input_form_locator}/div[8]/{text_field_locator}"
phone_input = TextInput(page, self.page.locator(loc), "phone_input")
self.add_content_item("phone_input", phone_input)
# Метка "Подписка на Push-уведомления"
label_push_locator = self.page.locator(input_form_locator). \
locator("//label").get_by_text("Подписка на Push-уведомления")
label_push = Text(
page,
label_push_locator,
"push_notification_checkbox_label"
)
self.add_content_item("push_notification_checkbox_label", label_push)
# Чекбокс "Подписка на Push-уведомления" - индекс 2
checkbox_push = Checkbox(
page,
label_push_locator.locator("../..").get_by_role("checkbox"),
"push_notification"
)
self.add_content_item("push_notification_checkbox", checkbox_push)
# Добавление кнопок действий
locator_button_add = self.page.get_by_role("button", name="Добавить")
self.add_button(locator_button_add, "add")
locator_button_close = self.page.get_by_role("button", name="Закрыть")
self.add_button(locator_button_close, "close")
# Добавление компонента подтверждения/отмены заведения пользователя
self.new_user_confirm = ConfirmComponent(page, " Отмена ", " Добавить ")
# Действия:
def check_blocking_checkbox(self):
"""Включает чек-бокс Блокировка."""
self.get_content_item("blocking_checkbox").check(force=True)
def uncheck_blocking_checkbox(self):
"""Выключает чек-бокс Блокировка."""
self.get_content_item("blocking_checkbox").uncheck(force=True)
def check_push_notification_checkbox(self):
"""Включает чек-бокс Push-уведомления."""
self.get_content_item("push_notification_checkbox").check(force=True)
def uncheck_push_notification_checkbox(self):
"""Выключает чек-бокс Push-уведомления."""
self.get_content_item("push_notification_checkbox").uncheck(force=True)
def new_user(self, user_data):
"""Заполняет форму и добавляет нового пользователя.
Args:
user_data (dict): Данные пользователя (имя, роль, пароль и др.)
"""
fields = user_data.keys()
if "name" in fields:
input_field = self.get_content_item("name_input")
input_field.input_value(user_data["name"])
if "role" in fields:
role_field = self.get_content_item("role_input")
role_field.click()
roles_list = self.get_content_item("roles_list")
roles_list.check_item_with_text(user_data["role"])
roles_list.click_item_with_text(user_data["role"])
if "password" in fields:
input_field = self.get_content_item("password_input")
input_field.input_value(user_data["password"])
if "commentary" in fields:
input_field = self.get_content_item("commentary_input")
input_field.input_value(user_data["commentary"])
if "email" in fields:
input_field = self.get_content_item("email_input")
input_field.input_value(user_data["email"])
if "phone_number" in fields:
input_field = self.get_content_item("phone_input")
input_field.input_value(user_data["phone_number"])
if "blocking_checked" in fields:
checkbox = self.get_content_item("blocking_checkbox")
if user_data["blocking_checked"]:
checkbox.check()
else:
checkbox.uncheck()
if "push_notification_checked" in fields:
checkbox = self.get_content_item("push_notification_checkbox")
if user_data["push_notification_checked"]:
checkbox.check()
else:
checkbox.uncheck()
# Отправка формы
add_button = self.get_button_by_name("add")
add_button.click()
# Подтверждение действия
title = "Добавить нового пользователя"
self.new_user_confirm.check_title(
title,
f"Confirmation dialog window with title '{title}' is missing"
)
self.new_user_confirm.click_allow_button()
def close_window(self):
"""Закрывает модальное окно через кнопку 'Закрыть'."""
close_button = self.get_button_by_name("close")
close_button.click()
def close_window_by_toolbar_button(self):
"""Закрывает модальное окно через кнопку в тулбаре."""
self.click_toolbar_close_button()
# Проверки:
def check_content(self):
"""Проверяет наличие и корректность всех элементов формы."""
menu_locator = self.page.locator(ModalWindowLocators.MENU_INPUT_FORM_USER_DATA)
self.check_by_window_title()
self.check_toolbar_button_visibility("close")
self.check_toolbar_button_tooltip("close", "Закрыть")
input_fields = ["name_input", "password_input",
"commentary_input", "email_input", "phone_input"]
for name in self.content_items:
item = self.get_content_item(name)
if name == "blocking_checkbox_label":
item.check_have_text(
"Блокировка",
"Label 'Блокировка' is missing"
)
elif name == "push_notification_checkbox_label":
item.check_have_text(
"Подписка на Push-уведомления",
"Label 'Подписка на Push-уведомления' is missing"
)
elif name == "role_input":
item.click()
roles_list = self.get_content_item("roles_list")
roles_list.check_visibility(menu_locator, "Roles list is missing")
is_scrollable_vertically = roles_list.check_vertical_scrolling(menu_locator)
assert not is_scrollable_vertically, (
"Roles list should not be scrollable_vertically"
)
for role in roles_dict.values():
# временно, пока есть несоответствие со списком ролей в вкладке Сессии
if role == "Пользователь":
continue
roles_list.check_item_with_text(role)
elif name in input_fields:
item.check_editable_input(
f"Input field with name '{name}' should be editable"
)
elif name == "roles_list":
continue
else:
print(f"check item: {name}")
print(item)
item.check_visibility(
f"Modal window content item with name '{name}' is missing"
)
# Дополнительная проверка состояния чекбоксов
blocking_checkbox = self.get_content_item("blocking_checkbox")
is_blocking_checked = blocking_checkbox.is_checked()
assert not is_blocking_checked, (
"Checkbox 'Блокировка' should not be checked by default"
)
push_checkbox = self.get_content_item("push_notification_checkbox")
is_push_checked = push_checkbox.is_checked()
assert not is_push_checked, (
"Checkbox 'Подписка на Push-уведомления' should not be checked by default"
)
self.check_button_visibility("add")
self.check_button_visibility("close")

View File

@ -113,27 +113,35 @@ class SelectionBarComponent(BaseComponent):
# Проверяем что поле видимо
if not field_container.is_visible():
logger.info(f"Field '{field_name}' is not visible, skipping clearing")
return
logger.info(f"Field '{field_name}' is not visible, trying to make it visible...")
# Прокручиваем до поля
field_container.scroll_into_view_if_needed()
self.wait_for_timeout(500)
# Если все еще не видимо, пробуем кликнуть
if not field_container.is_visible():
logger.info(f"Field '{field_name}' still not visible after scrolling, skipping")
return
# **ИСПРАВЛЕНИЕ: Используем правильный локатор для кнопки закрытия из combobox_locators**
# Ищем кнопку закрытия (крестик) внутри контейнера поля
close_button = field_container.locator(
ComboboxLocators.COMBOBOX_CLOSE_BUTTON
)
close_button = field_container.locator("i.mdi-close").first
# Проверяем наличие и видимость кнопки закрытия
if close_button.count() > 0 and close_button.is_visible():
if close_button.count() > 0:
logger.info(f"Found {close_button.count()} close button(s) for field '{field_name}'")
# Прокручиваем до кнопки
close_button.scroll_into_view_if_needed()
self.wait_for_timeout(500)
# Если кнопка закрытия видима - кликаем на нее
close_button.click()
close_button.click(force=True)
self.wait_for_timeout(500)
logger.info(f"Combobox field '{field_name}' cleared using close button")
else:
# Если кнопки закрытия нет, просто логируем этот факт
msg = f"Close button not found for field '{field_name}', clearing not performed"
msg = f"Close button (i.mdi-close) not found for field '{field_name}', clearing not performed"
logger.info(msg)
def open_values_list(self) -> None:
@ -172,19 +180,22 @@ class SelectionBarComponent(BaseComponent):
logger.info(f"Checking field '{field_name}' for error highlighting...")
field_element = self.page.locator(field_locator).first
# Получаем контейнер поля
field_container = self.page.locator(field_locator).first
# Проверяем что поле видимо
self.check_visibility(field_element, f"Field '{field_name}' not found")
# Проверяем что контейнер поля видимо
self.check_visibility(field_container, f"Field container '{field_name}' not found")
# Ищем родительский контейнер
parent_container = field_element.locator(SelectionBarLocators.INPUT_PARENT_CONTAINER).first
# Ищем элементы с классами ошибки внутри контейнера поля
error_elements = field_container.locator(SelectionBarLocators.ERROR_CSS_SELECTORS)
# Проверка классов ошибки с использованием локатора из SelectionBarLocators
if parent_container.count() > 0:
has_error = parent_container.locator(SelectionBarLocators.ERROR_CSS_SELECTORS).count() > 0
# Проверяем, что есть хотя бы один элемент с классом ошибки
has_error = error_elements.count() > 0
assert has_error, f"Field '{field_name}' is not highlighted with error color"
assert has_error, (
f"Field '{field_name}' has no elements with error classes. "
f"Expected to find elements matching: {SelectionBarLocators.ERROR_CSS_SELECTORS}"
)
logger.info(f"Field '{field_name}' is correctly highlighted with error color")
@ -198,18 +209,21 @@ class SelectionBarComponent(BaseComponent):
logger.info(f"Checking field '{field_name}' for absence of error highlighting...")
field_element = self.page.locator(field_locator).first
# Получаем контейнер поля
field_container = self.page.locator(field_locator).first
# Проверяем что поле видимо
self.check_visibility(field_element, f"Field '{field_name}' not found")
# Проверяем что контейнер поля видимо
self.check_visibility(field_container, f"Field container '{field_name}' not found")
# Ищем родительский контейнер
parent_container = field_element.locator(SelectionBarLocators.INPUT_PARENT_CONTAINER).first
# Ищем элементы с классами ошибки внутри контейнера поля
error_elements = field_container.locator(SelectionBarLocators.ERROR_CSS_SELECTORS)
# Проверяем отсутствие классов ошибки
if parent_container.count() > 0:
has_error = parent_container.locator(SelectionBarLocators.ERROR_CSS_SELECTORS).count() > 0
# Проверяем, что нет элементов с классами ошибки
has_error = error_elements.count() > 0
assert not has_error, f"Field '{field_name}' is highlighted with error"
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.info(f"Field '{field_name}' correctly has no error highlighting")

View File

@ -16,61 +16,20 @@ class RackLocators:
- Контейнеры и структурные элементы
"""
# Основной контейнер вкладок стойки (верхние вкладки)
TABS_CONTAINER = "//div[@data-testid='CABINET_SHOW__tabs' and contains(@class, 'v-tabs')]"
# Все элементы верхних вкладок стойки
ALL_TABS = "//div[@data-testid='CABINET_SHOW__tabs']//a[contains(@class, 'v-tabs__item')]"
# Кнопка редактирования свойств стойки
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')]")
# Кнопка "Показать стойку"
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(., '{}')]]")
# Конкретные вкладки по тексту
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')]")
# Контейнер формы
FORM_CONTAINER = "//div[contains(@class, 'container')]"
# Локаторы полей формы редактирования стойки
NAME_FIELD = "//input[@aria-label='Имя']"
SERIAL_NUMBER_FIELD = "//input[@aria-label='Серийный номер']"
INVENTORY_NUMBER_FIELD = "//input[@aria-label='Инвентарный номер']"
CABLE_ENTRY_FIELD = "//input[@aria-label='Ввод кабеля']"
STATUS_FIELD = "//input[@aria-label='Состояние']"
HEIGHT_FIELD = "//input[@aria-label='Высота в юнитах']"
OWNER_FIELD = "//input[@aria-label='Владелец']"
SERVICE_ORG_FIELD = "//input[@aria-label='Обслуживающая организация']"
PROJECT_FIELD = "//input[@aria-label='Проект/Титул']"
# Контейнер формы создания/редактирования стойки
FORM_INPUT_CONTAINER = "//div[contains(@class, 'flex xs6 pa-0')]"
# Локаторы полей формы создания стойки
RACK_NAME_FIELD = ("//div[contains(@class, 'container')]"
@ -93,7 +52,6 @@ class RackLocators:
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()='Владелец']]")
@ -104,21 +62,10 @@ class RackLocators:
"//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')]"
# CSS селекторы для ошибок валидации
ERROR_CSS_SELECTORS = ".error--text, .v-input--error"
@ -138,11 +85,6 @@ class RackLocators:
# Локаторы для определения активной стороны
ACTIVE_SIDE_BUTTON = "//button[contains(@class, 'primary--text')]"
INACTIVE_SIDE_BUTTON = "//button[contains(@class, 'secondary--text')]"
# Для получения текста активной стороны
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')]"
@ -160,3 +102,14 @@ class RackLocators:
# Локатор для слотов в устройствах
DEVICE_SLOTS = "//div[contains(@class, 'slot')]"
# Кнопка редактирования свойств стойки
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')]")
# Кнопка "Показать стойку"
SHOW_RACK_BUTTON = ("//div[@data-testid='CABINET_SHOW__div__hideCabinet' and "
"contains(@class, 'cabinet_hide_button_trigger_hide')]")

View File

@ -19,8 +19,8 @@ class SelectionBarLocators:
PARAMETERS_SELECTED = "div.v-select__selections"
# Локаторы для элементов выпадающего списка
LISTBOX = "//div[@role='listbox']"
LIST_ITEMS = "//div[@role='listbox']//div[@role='listitem']"
LISTBOX = "//div[@role='list']"
LIST_ITEMS = "//div[@role='list']//div[@role='listitem']"
# Локатор для родительского контейнера поля ввода
INPUT_PARENT_CONTAINER = "xpath=./ancestor::div[contains(@class, 'v-input')]"

View File

@ -4,6 +4,7 @@ import pytest
from playwright.sync_api import Page
from tools.logger import get_logger
from locators.navigation_panel_locators import NavigationPanelLocators
from locators.rack_locators import RackLocators
from components_derived.accounting_objects.rack_maker import RackObjectMaker, RackData
from components_derived.frames.create_child_element_frame import CreateChildElementFrame
from pages.location_page import LocationPage
@ -13,7 +14,7 @@ from pages.main_page import MainPage
logger = get_logger("CREATE_RACK_ELEMENT_TEST")
logger.setLevel("INFO")
#logger.setLevel("INFO")
# @pytest.mark.smoke
class TestCreateRackElement:
@ -88,6 +89,7 @@ class TestCreateRackElement:
create_child_frame.should_be_toolbar_buttons()
#@pytest.mark.develop
def test_create_rack_child_element(self, browser: Page) -> None:
"""Тест создания дочернего элемента типа 'Стойка'."""
@ -127,6 +129,7 @@ class TestCreateRackElement:
logger.debug("Test for creating 'Rack' child element completed successfully")
#@pytest.mark.develop
def test_create_rack_with_duplicate_name(self, browser: Page) -> None:
"""
Тест создания стойки с уже существующим именем.
@ -150,10 +153,6 @@ class TestCreateRackElement:
# Создаем вторую стойку с тем же именем
logger.debug(f"Attempting to create second rack with name '{rack_name}'")
# Переходим обратно к созданию новой стойки
self.main_page.click_main_navigation_panel_item("test-zone")
self.main_page.wait_for_timeout(2000)
# Нажимаем кнопку "Создать" на тулбаре
self.location_page.click_create_button()
@ -173,7 +172,7 @@ class TestCreateRackElement:
rack_data = RackData(
name=rack_name,
height="42",
depth="1000"
depth="450"
)
# Пытаемся создать вторую стойку с тем же именем
@ -212,53 +211,60 @@ class TestCreateRackElement:
expected_alert_height = test_data["expected_alert_height"]
expected_alert_depth = test_data["expected_alert_depth"]
# Получаем контейнер формы
container_locator = create_child_frame.page.locator(RackLocators.FORM_INPUT_CONTAINER).nth(1)
# Используем метод get_input_fields_locators для получения всех полей
fields_locators = create_child_frame.get_input_fields_locators(container_locator, "layout")
logger.debug(f"Available fields: {list(fields_locators.keys())}")
# Функция для проверки заполненности combobox поля
def is_field_filled(field_name: str) -> bool:
"""Проверяет, заполнено ли combobox поле."""
# Получаем локатор поля
field_locator = create_child_frame.get_field_locator(field_name)
if field_name not in fields_locators:
logger.debug(f"Field '{field_name}' not found in fields_locators")
return False
# Находим элемент поля
field_element = create_child_frame.page.locator(field_locator).first
# Получаем xs8 контейнер поля
field_container = fields_locators[field_name]
if not field_element.is_visible():
if not field_container.is_visible():
logger.debug(f"Field '{field_name}' not visible")
return False
# Проверяем наличие кнопки закрытия (крестика) - признак заполненного поля
close_button = field_element.locator(
".v-select__selections" # Или другой локатор для кнопки закрытия
)
# Проверяем наличие выбранного значения через v-chip (чип выбранного значения в combobox)
selected_chip = field_container.locator(".v-chip").first
# Если есть кнопка закрытия, поле заполнено
has_close_button = close_button.count() > 0 and close_button.is_visible()
# Также можно проверить по тексту в поле
field_text = field_element.text_content() or ""
# Проверяем наличие текста в поле
field_text = field_container.text_content() or ""
has_text = bool(field_text.strip())
logger.debug(f"Field '{field_name}' - has close button: {has_close_button}, has text: {has_text}")
# Проверяем наличие чипа
has_chip = selected_chip.count() > 0 and selected_chip.is_visible()
return has_close_button or has_text
logger.debug(f"Field '{field_name}' - has chip: {has_chip}, has text: {has_text}")
return has_chip or has_text
# Проверяем и очищаем поле "Глубина (мм)" только если оно заполнено
logger.debug("Checking field: Depth (mm)")
logger.debug("Checking field: Глубина (мм)")
if is_field_filled("Глубина (мм)"):
logger.debug("Field 'Depth (mm)' is filled, performing clearing")
logger.debug("Field 'Глубина (мм)' is filled, performing clearing")
create_child_frame.clear_combobox_field("Глубина (мм)")
logger.debug("Clearing completed for 'Depth (mm)'")
logger.debug("Clearing completed for 'Глубина (мм)'")
else:
logger.debug("Field 'Depth (mm)' is already empty, skipping clearing")
logger.debug("Field 'Глубина (мм)' is already empty, skipping clearing")
# Проверяем и очищаем поле "Высота в юнитах" только если оно заполнено
logger.debug("Checking field: Height in units")
logger.debug("Checking field: Высота в юнитах")
if is_field_filled("Высота в юнитах"):
logger.debug("Field 'Height in units' is filled, performing clearing")
logger.debug("Field 'Высота в юнитах' is filled, performing clearing")
create_child_frame.clear_combobox_field("Высота в юнитах")
logger.debug("Clearing completed for 'Height in units'")
logger.debug("Clearing completed for 'Высота в юнитах'")
else:
logger.debug("Field 'Height in units' is already empty, skipping clearing")
logger.debug("Field 'Высота в юнитах' is already empty, skipping clearing")
# Создаем объект данных стойки
rack_data = RackData(
@ -274,25 +280,11 @@ class TestCreateRackElement:
# Нажимаем кнопку создания
logger.debug("Submitting form for validation")
create_child_frame.click_add_button()
create_child_frame.wait_for_timeout(3000)
create_child_frame.wait_for_timeout(1000)
# Проверяем валидацию полей
logger.debug("Checking validation results")
if height_value:
create_child_frame.check_field_error_not_highlighted("Высота в юнитах")
logger.debug("Height field validation passed")
else:
create_child_frame.check_field_error_highlighted("Высота в юнитах")
logger.debug("Height field validation failed as expected")
if depth_value:
create_child_frame.check_field_error_not_highlighted("Глубина (мм)")
logger.debug("Depth field validation passed")
else:
create_child_frame.check_field_error_highlighted("Глубина (мм)")
logger.debug("Depth field validation failed as expected")
# Обрабатываем alert-окна
if not height_value:
logger.debug("Expecting height validation alert")
@ -306,10 +298,26 @@ class TestCreateRackElement:
create_child_frame.alert.close_alert_by_text(expected_alert_depth)
logger.debug("Depth alert handled")
# Проверяем подсветку обязательных полей
if height_value:
create_child_frame.check_field_error_not_highlighted("Высота в юнитах")
logger.debug("Height field validation passed")
else:
create_child_frame.check_field_error_highlighted("Высота в юнитах")
logger.debug("Height field validation failed as expected")
if depth_value:
create_child_frame.check_field_error_not_highlighted("Глубина (мм)")
logger.debug("Depth field validation passed")
else:
create_child_frame.check_field_error_highlighted("Глубина (мм)")
logger.debug("Depth field validation failed as expected")
# Проверяем, что остались на странице создания
create_child_frame.check_toolbar_title('Создать дочерний элемент в')
logger.debug("Test completed successfully")
@pytest.mark.develop
def test_required_fields_validation(self, browser: Page) -> None:
"""
Тест проверки обязательных полей при создании стойки.
@ -396,6 +404,49 @@ class TestCreateRackElement:
# Генерируем уникальное имя для финального теста
final_rack_name = "Test-Rack-Required-Final"
# **ВАЖНО: Очищаем поля перед заполнением**
logger.debug("Clearing fields before filling...")
# Получаем контейнер формы
container_locator = create_child_frame.page.locator(RackLocators.FORM_INPUT_CONTAINER).nth(1)
# Используем метод get_input_fields_locators для получения всех полей
fields_locators = create_child_frame.get_input_fields_locators(container_locator, "layout")
# Очищаем поле "Высота в юнитах" если оно заполнено
if "Высота в юнитах" in fields_locators:
field_container = fields_locators["Высота в юнитах"]
# Проверяем наличие текста в поле
field_text = field_container.inner_text() or ""
if field_text.strip():
logger.debug("Clearing 'Высота в юнитах' field...")
create_child_frame.clear_combobox_field("Высота в юнитах")
create_child_frame.wait_for_timeout(500)
# Очищаем поле "Глубина (мм)" если оно заполнено
if "Глубина (мм)" in fields_locators:
field_container = fields_locators["Глубина (мм)"]
# Проверяем наличие текста в поле
field_text = field_container.inner_text() or ""
if field_text.strip():
logger.debug("Clearing 'Глубина (мм)' field...")
create_child_frame.clear_combobox_field("Глубина (мм)")
create_child_frame.wait_for_timeout(500)
# Очищаем поле "Имя" если оно заполнено
if "Имя" in fields_locators:
field_container = fields_locators["Имя"]
# Находим input внутри контейнера
input_field = field_container.locator("input").first
if input_field.count() > 0:
current_value = input_field.input_value()
if current_value.strip():
logger.debug("Clearing 'Имя' field...")
input_field.click()
input_field.press("Control+A")
input_field.press("Backspace")
create_child_frame.wait_for_timeout(500)
# Создаем объект данных стойки
rack_data = RackData(
name=final_rack_name,
@ -414,12 +465,12 @@ class TestCreateRackElement:
# Нажимаем кнопку создания
create_child_frame.click_add_button()
create_child_frame.wait_for_timeout(3000)
create_child_frame.wait_for_timeout(1000)
# Проверяем, что НЕТ 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.debug("No alert windows for required fields appeared - all fields filled correctly")
#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.debug("No alert windows for required fields appeared - all fields filled correctly")
# Проверяем, что ушли со страницы создания
try: