Рефакторинг

ra3/edit_element_rack
Radislav 2026-01-28 13:26:38 +03:00
parent 6576e4463e
commit c56bf70cfa
8 changed files with 178 additions and 649 deletions

View File

@ -17,31 +17,48 @@ logger = get_logger("CONFIRM_WINDOW")
class ConfirmComponent(BaseComponent): class ConfirmComponent(BaseComponent):
"""Компонент окна подтверждения действий.""" """Компонент окна подтверждения действий."""
def __init__(self, page: Page, cancel_button_text: str, allow_button_text: str): def __init__(self, page: Page, cancel_button_text: str = "", allow_button_text: str = "",
cancel_button_locator: str = None, allow_button_locator: str = None):
"""Инициализация компонента. """Инициализация компонента.
Args: Args:
page: Экземпляр страницы Playwright. page: Экземпляр страницы Playwright.
cancel_button_text: Текст кнопки отмены. cancel_button_text: Текст кнопки отмены (по умолчанию пустая строка).
allow_button_text: Текст кнопки подтверждения. allow_button_text: Текст кнопки подтверждения (по умолчанию пустая строка).
cancel_button_locator: Локатор кнопки отмены (опционально).
allow_button_locator: Локатор кнопки подтверждения (опционально).
""" """
super().__init__(page) super().__init__(page)
self.title = Text(page, ConfirmLocators.TITLE, "confirm title") self.title = Text(page, ConfirmLocators.TITLE, "confirm title")
self.text = Text(page, ConfirmLocators.TEXT, "confirm text") self.text = Text(page, ConfirmLocators.TEXT, "confirm text")
self.close_button = Button(page, ConfirmLocators.BUTTON_CLOSE, "confirm close button") self.close_button = Button(page, ConfirmLocators.BUTTON_CLOSE, "confirm close button")
# Инициализация кнопок с приоритетом локаторам
if cancel_button_locator:
self.cancel_button = Button(page, cancel_button_locator, "confirm cancel button")
elif cancel_button_text:
self.cancel_button = Button( self.cancel_button = Button(
page, page,
page.get_by_role("button", name=cancel_button_text).first, page.get_by_role("button", name=cancel_button_text).first,
"confirm cancel button" "confirm cancel button"
) )
else:
self.cancel_button = None
logger.warning("Cancel button not initialized - neither text nor locator specified")
if allow_button_locator:
self.allow_button = Button(page, allow_button_locator, "confirm allow button")
elif allow_button_text:
self.allow_button = Button( self.allow_button = Button(
page, page,
page.get_by_role("button", name=allow_button_text).first, page.get_by_role("button", name=allow_button_text).first,
"confirm allow button" "confirm allow button"
) )
else:
self.allow_button = None
logger.warning("Allow button not initialized - neither text nor locator specified")
# Действия: # Действия:
def click_allow_button(self) -> None: def click_allow_button(self) -> None:

View File

@ -35,7 +35,6 @@ class AddUserModalWindow(ModalWindowComponent):
super().__init__(page) super().__init__(page)
# Локаторы элементов формы # Локаторы элементов формы
text_field_locator = f"//{ModalWindowLocators.TEXT_FIELD_INPUT_FORM_USER_DATA}"
input_form_locator = ModalWindowLocators.INPUT_FORM_USER_DATA input_form_locator = ModalWindowLocators.INPUT_FORM_USER_DATA
# Настройка заголовка и кнопки закрытия тулбара # Настройка заголовка и кнопки закрытия тулбара
@ -49,7 +48,7 @@ class AddUserModalWindow(ModalWindowComponent):
self.add_toolbar_title(self.window_title) self.add_toolbar_title(self.window_title)
self.add_toolbar_button(locator_button_toolbar_close, "close") self.add_toolbar_button(locator_button_toolbar_close, "close")
elements_locators = self._get_fields_locators(page) elements_locators = self.get_input_fields_locators(page.locator(input_form_locator))
# Поле Тип авторизации # Поле Тип авторизации
loc = elements_locators.get("Тип авторизации") loc = elements_locators.get("Тип авторизации")
@ -58,7 +57,7 @@ class AddUserModalWindow(ModalWindowComponent):
self.add_content_item("auth_type_selector", auth_type_selector) self.add_content_item("auth_type_selector", auth_type_selector)
# Поле Имя # Поле Имя
loc = elements_locators.get("Имя").locator(text_field_locator) loc = elements_locators.get("Имя").locator(ModalWindowLocators.INPUT_FORM_USER_DATA_FIELD_NAME)
name_input = TextInput(page, loc, "name_input") name_input = TextInput(page, loc, "name_input")
self.add_content_item("name_input", name_input) self.add_content_item("name_input", name_input)
@ -76,34 +75,34 @@ class AddUserModalWindow(ModalWindowComponent):
# Чекбокс "Блокировка" # Чекбокс "Блокировка"
checkbox_blocking = Checkbox( checkbox_blocking = Checkbox(
page, page,
label_blocking_locator.locator("../..").get_by_role("checkbox"), page.locator(input_form_locator).locator(ModalWindowLocators.INPUT_FORM_USER_DATA_CHECKBOX_BLOCKED),
"blocking" "blocking"
) )
self.add_content_item("blocking_checkbox", checkbox_blocking) self.add_content_item("blocking_checkbox", checkbox_blocking)
# Поле Роль # Поле Роль
role_loc = elements_locators.get("Роль").get_by_role("combobox") role_loc = elements_locators.get("Роль").get_by_role("combobox").first
role_input = TextInput(page, role_loc, "role_input") role_input = TextInput(page, role_loc, "role_input")
self.add_content_item("role_input", role_input) self.add_content_item("role_input", role_input)
self.add_content_item("roles_list", DropdownList(page)) self.add_content_item("roles_list", DropdownList(page))
# Поле Пароль # Поле Пароль
loc = elements_locators.get("Пароль").locator(text_field_locator) loc = elements_locators.get("Пароль").locator(ModalWindowLocators.INPUT_FORM_USER_DATA_FIELD_PASSWORD)
password_input = TextInput(page, loc, "password_input") password_input = TextInput(page, loc, "password_input")
self.add_content_item("password_input", password_input) self.add_content_item("password_input", password_input)
# Поле Комментарий # Поле Комментарий
loc = elements_locators.get("Комментарий").locator(text_field_locator) loc = elements_locators.get("Комментарий").locator(ModalWindowLocators.INPUT_FORM_USER_DATA_FIELD_COMMENT)
commentary_input = TextInput(page, loc, "commentary_input") commentary_input = TextInput(page, loc, "commentary_input")
self.add_content_item("commentary_input", commentary_input) self.add_content_item("commentary_input", commentary_input)
# Поле E-mail # Поле E-mail
loc = elements_locators.get("E-mail").locator(text_field_locator) loc = elements_locators.get("E-mail").locator(ModalWindowLocators.INPUT_FORM_USER_DATA_FIELD_EMAIL)
email_input = TextInput(page, loc, "email_input") email_input = TextInput(page, loc, "email_input")
self.add_content_item("email_input", email_input) self.add_content_item("email_input", email_input)
# Поле Номер для СМС # Поле Номер для СМС
loc = elements_locators.get("Номер для СМС").locator(text_field_locator) loc = elements_locators.get("Номер для СМС").locator(ModalWindowLocators.INPUT_FORM_USER_DATA_FIELD_SMS)
phone_input = TextInput(page, loc, "phone_input") phone_input = TextInput(page, loc, "phone_input")
self.add_content_item("phone_input", phone_input) self.add_content_item("phone_input", phone_input)
@ -120,7 +119,7 @@ class AddUserModalWindow(ModalWindowComponent):
# Чекбокс "Подписка на Push-уведомления" # Чекбокс "Подписка на Push-уведомления"
checkbox_push = Checkbox( checkbox_push = Checkbox(
page, page,
label_push_locator.locator("../..").get_by_role("checkbox"), page.locator(input_form_locator).locator(ModalWindowLocators.INPUT_FORM_USER_DATA_CHECKBOX_PUSH_ACTIVE),
"push_notification" "push_notification"
) )
self.add_content_item("push_notification_checkbox", checkbox_push) self.add_content_item("push_notification_checkbox", checkbox_push)
@ -192,7 +191,8 @@ class AddUserModalWindow(ModalWindowComponent):
if auth_type == "LDAP": if auth_type == "LDAP":
menu_locator = self.page.locator(ModalWindowLocators.MENU_ACTIVE_INPUT_FORM) menu_locator = self.page.locator(ModalWindowLocators.MENU_ACTIVE_INPUT_FORM)
elements_locators = self._get_fields_locators(self.page) elements_locators = self.get_input_fields_locators(
self.page.locator(ModalWindowLocators.INPUT_FORM_USER_DATA))
# Добавилось поле Группа # Добавилось поле Группа
group_loc = elements_locators.get("Группа").get_by_role("combobox") group_loc = elements_locators.get("Группа").get_by_role("combobox")
@ -223,7 +223,8 @@ class AddUserModalWindow(ModalWindowComponent):
search_button.click() search_button.click()
# Если в группе есть пользователи, открывается новое поле, заново вычисляем локаторы # Если в группе есть пользователи, открывается новое поле, заново вычисляем локаторы
elements_locators = self._get_fields_locators(self.page) elements_locators = self.get_input_fields_locators(
self.page.locator(ModalWindowLocators.INPUT_FORM_USER_DATA))
users_ad_loc = elements_locators.get("Пользователи AD") users_ad_loc = elements_locators.get("Пользователи AD")
# users_ad_loc = elements_locators.get("Пользователи LDAP") # users_ad_loc = elements_locators.get("Пользователи LDAP")
assert users_ad_loc, f"Selected group {group_name} is empty. Use another group." assert users_ad_loc, f"Selected group {group_name} is empty. Use another group."
@ -323,39 +324,28 @@ class AddUserModalWindow(ModalWindowComponent):
def locators_recalculation(self, is_active_directory=False) -> None: def locators_recalculation(self, is_active_directory=False) -> None:
"""Пересчет локаторов полей ввода""" """Пересчет локаторов полей ввода"""
text_field_locator = f"//{ModalWindowLocators.TEXT_FIELD_INPUT_FORM_USER_DATA}" elements_locators = self.get_input_fields_locators(
self.page.locator(ModalWindowLocators.INPUT_FORM_USER_DATA))
elements_locators = self._get_fields_locators(self.page) new_loc = elements_locators.get("Имя").locator(ModalWindowLocators.INPUT_FORM_USER_DATA_FIELD_NAME)
new_loc = elements_locators.get("Имя").locator(text_field_locator)
self.get_content_item("name_input").update_locator(new_loc) self.get_content_item("name_input").update_locator(new_loc)
if not is_active_directory: if not is_active_directory:
new_loc = elements_locators.get("Пароль").locator(text_field_locator) new_loc = elements_locators.get("Пароль").locator(ModalWindowLocators.INPUT_FORM_USER_DATA_FIELD_PASSWORD)
self.get_content_item("password_input").update_locator(new_loc) self.get_content_item("password_input").update_locator(new_loc)
new_loc = elements_locators.get("Роль").get_by_role("combobox") new_loc = elements_locators.get("Роль").get_by_role("combobox").first
self.get_content_item("role_input").update_locator(new_loc) self.get_content_item("role_input").update_locator(new_loc)
new_loc = elements_locators.get("Комментарий").locator(text_field_locator) new_loc = elements_locators.get("Комментарий").locator(ModalWindowLocators.INPUT_FORM_USER_DATA_FIELD_COMMENT)
self.get_content_item("commentary_input").update_locator(new_loc) self.get_content_item("commentary_input").update_locator(new_loc)
new_loc = elements_locators.get("E-mail").locator(text_field_locator) new_loc = elements_locators.get("E-mail").locator(ModalWindowLocators.INPUT_FORM_USER_DATA_FIELD_EMAIL)
self.get_content_item("email_input").update_locator(new_loc) self.get_content_item("email_input").update_locator(new_loc)
new_loc = elements_locators.get("Номер для СМС").locator(text_field_locator) new_loc = elements_locators.get("Номер для СМС").locator(ModalWindowLocators.INPUT_FORM_USER_DATA_FIELD_SMS)
self.get_content_item("phone_input").update_locator(new_loc) self.get_content_item("phone_input").update_locator(new_loc)
def _get_fields_locators(self, page) -> dict:
fields_locators = {}
elements = page.locator(ModalWindowLocators.INPUT_FORM_USER_DATA). \
locator("div.v-text-field__slot > input").all()
for el in elements:
val = el.input_value().strip()
if val:
fields_locators[val] = el.locator("../ancestor::div[5]")
return fields_locators
# Проверки: # Проверки:
def check_content(self): def check_content(self):
"""Проверяет наличие и корректность всех элементов формы создания локального пользователя. """Проверяет наличие и корректность всех элементов формы создания локального пользователя.
@ -419,8 +409,8 @@ class AddUserModalWindow(ModalWindowComponent):
elif name == "roles_list": elif name == "roles_list":
continue continue
else: else:
print(f"check item: {name}") # print(f"check item: {name}")
print(item) # print(item)
item.check_visibility( item.check_visibility(
f"Modal window content item with name '{name}' is missing" f"Modal window content item with name '{name}' is missing"
) )
@ -443,7 +433,8 @@ class AddUserModalWindow(ModalWindowComponent):
if auth_type_selector: if auth_type_selector:
self.select_auth_type("LDAP") self.select_auth_type("LDAP")
elements_locators = self._get_fields_locators(self.page) elements_locators = self.get_input_fields_locators(
self.page.locator(ModalWindowLocators.INPUT_FORM_USER_DATA))
# Добавилось поле Группа # Добавилось поле Группа
group_loc = elements_locators.get("Группа").get_by_role("combobox") group_loc = elements_locators.get("Группа").get_by_role("combobox")

View File

@ -34,8 +34,6 @@ class EditUserModalWindow(ModalWindowComponent):
super().__init__(page) super().__init__(page)
# Локаторы элементов формы # Локаторы элементов формы
# text_field_locator = ModalWindowLocators.TEXT_FIELD_INPUT_FORM_USER_DATA
text_field_locator = f"xpath={ModalWindowLocators.TEXT_FIELD_INPUT_FORM_USER_DATA}"
input_form_locator = ModalWindowLocators.INPUT_FORM_USER_DATA input_form_locator = ModalWindowLocators.INPUT_FORM_USER_DATA
# Настройка заголовка и кнопки закрытия # Настройка заголовка и кнопки закрытия
@ -50,31 +48,32 @@ class EditUserModalWindow(ModalWindowComponent):
self.add_toolbar_button(locator_button_toolbar_close, "close") self.add_toolbar_button(locator_button_toolbar_close, "close")
# Добавление полей формы # Добавление полей формы
elements_locators = self._get_fields_locators(page) elements_locators = self.get_input_fields_locators(
self.page.locator(ModalWindowLocators.INPUT_FORM_USER_DATA))
# Поле Имя # Поле Имя
loc = elements_locators.get("Имя").locator(text_field_locator) loc = elements_locators.get("Имя").locator(ModalWindowLocators.INPUT_FORM_USER_DATA_FIELD_NAME)
name_input = TextInput(page, loc, "name_input") name_input = TextInput(page, loc, "name_input")
self.add_content_item("name_input", name_input) self.add_content_item("name_input", name_input)
# Поле Роль # Поле Роль
role_loc = self.page.locator(input_form_locator).get_by_role("combobox") role_loc = self.page.locator(input_form_locator).get_by_role("combobox").first
role_input = TextInput(page, role_loc, "role_input") role_input = TextInput(page, role_loc, "role_input")
self.add_content_item("role_input", role_input) self.add_content_item("role_input", role_input)
self.add_content_item("roles_list", DropdownList(page)) self.add_content_item("roles_list", DropdownList(page))
# Поле Комментарий # Поле Комментарий
loc = elements_locators.get("Комментарий").locator(text_field_locator) loc = elements_locators.get("Комментарий").locator(ModalWindowLocators.INPUT_FORM_USER_DATA_FIELD_COMMENT)
commentary_input = TextInput(page, loc, "commentary_input") commentary_input = TextInput(page, loc, "commentary_input")
self.add_content_item("commentary_input", commentary_input) self.add_content_item("commentary_input", commentary_input)
# Поле E-mail # Поле E-mail
loc = elements_locators.get("E-mail").locator(text_field_locator) loc = elements_locators.get("E-mail").locator(ModalWindowLocators.INPUT_FORM_USER_DATA_FIELD_EMAIL)
email_input = TextInput(page, loc, "email_input") email_input = TextInput(page, loc, "email_input")
self.add_content_item("email_input", email_input) self.add_content_item("email_input", email_input)
# Поле Номер для СМС # Поле Номер для СМС
loc = elements_locators.get("Номер для СМС").locator(text_field_locator) loc = elements_locators.get("Номер для СМС").locator(ModalWindowLocators.INPUT_FORM_USER_DATA_FIELD_SMS)
phone_input = TextInput(page, loc, "phone_input") phone_input = TextInput(page, loc, "phone_input")
self.add_content_item("phone_input", phone_input) self.add_content_item("phone_input", phone_input)
@ -93,7 +92,7 @@ class EditUserModalWindow(ModalWindowComponent):
# Чекбокс "Блокировка" # Чекбокс "Блокировка"
checkbox_blocking = Checkbox( checkbox_blocking = Checkbox(
page, page,
label_blocking_locator.locator("../..").get_by_role("checkbox"), page.locator(input_form_locator).locator(ModalWindowLocators.INPUT_FORM_USER_DATA_CHECKBOX_BLOCKED),
"blocking" "blocking"
) )
self.add_content_item("blocking_checkbox", checkbox_blocking) self.add_content_item("blocking_checkbox", checkbox_blocking)
@ -111,7 +110,7 @@ class EditUserModalWindow(ModalWindowComponent):
# Чекбокс "Подписка на Push-уведомления" # Чекбокс "Подписка на Push-уведомления"
checkbox_push = Checkbox( checkbox_push = Checkbox(
page, page,
label_push_locator.locator("../..").get_by_role("checkbox"), page.locator(input_form_locator).locator(ModalWindowLocators.INPUT_FORM_USER_DATA_CHECKBOX_PUSH_ACTIVE),
"push_notification" "push_notification"
) )
self.add_content_item("push_notification_checkbox", checkbox_push) self.add_content_item("push_notification_checkbox", checkbox_push)
@ -241,15 +240,15 @@ class EditUserModalWindow(ModalWindowComponent):
reset_password_button = self.get_button_by_name("reset_password") reset_password_button = self.get_button_by_name("reset_password")
reset_password_button.click() reset_password_button.click()
def _get_fields_locators(self, page) -> dict: # def _get_fields_locators(self, page) -> dict:
fields_locators = {} # fields_locators = {}
elements = page.locator(ModalWindowLocators.INPUT_FORM_USER_DATA). \ # elements = page.locator(ModalWindowLocators.INPUT_FORM_USER_DATA). \
locator("div.v-text-field__slot > input").all() # locator("div.v-text-field__slot > input").all()
for el in elements: # for el in elements:
val = el.input_value().strip() # val = el.input_value().strip()
if val: # if val:
fields_locators[val] = el.locator("../ancestor::div[5]") # fields_locators[val] = el.locator("../ancestor::div[5]")
return fields_locators # return fields_locators
# Проверки: # Проверки:
def check_content(self, user_name, role): def check_content(self, user_name, role):

View File

@ -1,442 +0,0 @@
"""Модуль для работы с модальным окном редактирования стойки."""
from dataclasses import dataclass, field
from typing import Optional, Dict
from playwright.sync_api import Page, Locator
from tools.logger import get_logger
from locators.rack_locators import RackLocators
from elements.tooltip_button_element import TooltipButton
from components.toolbar_component import ToolbarComponent
from components.base_component import BaseComponent
logger = get_logger("MODAL_EDIT_RACK")
#logger.setLevel("INFO")
@dataclass
class RackEditData:
"""Класс для хранения данных редактирования стойки."""
# Основные поля (редактируемые)
name: str = ""
serial: str = ""
inventory: str = ""
comment: str = ""
# Combobox поля (редактируемые)
cable_entry: str = ""
state: str = ""
owner: str = ""
service_org: str = ""
project: str = ""
# Дополнительные поля (редактируемые)
power: str = ""
# Checkbox поля (редактируемые) - если есть
ventilation_panel: Optional[bool] = None
# Правила доступа (если есть)
read_access_rules: str = ""
write_access_rules: str = ""
sms_access_rules: str = ""
email_access_rules: str = ""
push_access_rules: str = ""
class ModalEditRack(BaseComponent):
"""Компонент для работы с модальным окном редактирования стойки."""
def __init__(self, page: Page) -> None:
"""
Инициализирует компонент редактирования стойки.
Args:
page (Page): Экземпляр страницы Playwright
"""
super().__init__(page)
self._form_container = None
self._fields_cache = {}
self.toolbar = ToolbarComponent(page, "")
# Кнопка "Переместить" (data-testid)
replace_button_locator = self.page.locator(RackLocators.TOOLBAR_REPLACE_BUTTON)
self.replace_button = TooltipButton(page, replace_button_locator, "replace")
# Кнопка "Сохранить" (data-testid)
done_button_locator = page.locator(RackLocators.TOOLBAR_DONE_BUTTON)
self.done_button = TooltipButton(page, done_button_locator, "done")
# Кнопка "Отменить" (data-testid)
close_button_locator = page.locator(RackLocators.TOOLBAR_CLOSE_BUTTON)
self.close_button = TooltipButton(page, close_button_locator, "close")
# Кнопка "Удалить" (data-testid)
remove_button_locator = page.locator(RackLocators.TOOLBAR_REMOVE_BUTTON)
self.remove_button = TooltipButton(page, remove_button_locator, "remove")
# Добавляем кнопки в тулбар
self.toolbar.add_tooltip_button(replace_button_locator, "replace")
self.toolbar.add_tooltip_button(done_button_locator, "done")
self.toolbar.add_tooltip_button(close_button_locator, "close")
self.toolbar.add_tooltip_button(remove_button_locator, "remove")
def click_remove_button(self) -> None:
"""
Кликает на кнопку 'Удалить' и обрабатывает диалог подтверждения.
"""
logger.debug("Clicking on 'Remove' button...")
# Проверяем видимость кнопки
self.toolbar.check_button_visibility("remove")
self.toolbar.check_button_tooltip("remove", "Удалить")
# Кликаем на кнопку удаления
self.toolbar.get_button_by_name("remove").click()
self.wait_for_timeout(1000)
# Ожидаем появления диалога подтверждения
self._handle_remove_confirmation_dialog()
def click_done_button(self) -> None:
"""
Кликает на кнопку 'Сохранить' и обрабатывает диалог подтверждения.
"""
logger.debug("Clicking on 'Done' button...")
# Проверяем видимость кнопки
self.toolbar.check_button_visibility("done")
self.toolbar.check_button_tooltip("done", "Сохранить")
# Кликаем на кнопку сохранения
self.toolbar.get_button_by_name("done").click()
self.wait_for_timeout(1000)
def confirm_remove_dialog(self, confirm: bool = True) -> None:
"""
Подтверждает или отклоняет удаление в диалоговом окне.
Args:
confirm (bool): Если True - подтвердить удаление, если False - отменить
"""
logger.debug(f"Confirming remove dialog with: {'Да' if confirm else 'Нет'}")
# Ждем немного перед поиском диалога
self.wait_for_timeout(1500)
# Ищем активный диалог
dialog = self.page.locator("div.v-dialog--active")
# Проверяем, что диалог найден и содержит нужный текст
assert dialog.count() > 0, "No active dialog found"
# Проверяем текст диалога
dialog_text = dialog.first.text_content()
logger.debug("Dialog text: %s", dialog_text)
# Должен содержать "Запрос подтверждения" и "Удалить"
assert "Запрос подтверждения" in dialog_text, "Not a confirmation dialog"
# Ищем кнопку по data-testid
if confirm:
button = self.page.locator(RackLocators.CONFIRM_REMOVE_YES_BUTTON)
else:
button = self.page.locator(RackLocators.CONFIRM_REMOVE_NO_BUTTON)
# Проверяем, что кнопка найдена
assert button.count() > 0, "Button not found with selector"
# Кликаем на кнопку
button_text = button.first.text_content()
logger.debug("Clicking button with text: %s", button_text)
button.first.click()
self.wait_for_timeout(2000)
# Проверяем, что диалог закрылся
self.wait_for_timeout(1000)
logger.debug("Remove confirmation completed")
def _handle_remove_confirmation_dialog(self) -> None:
"""Обрабатывает диалог подтверждения удаления."""
logger.debug("Handling remove confirmation dialog...")
self.confirm_remove_dialog(confirm=True)
# Остальные существующие методы остаются без изменений
def _get_form_container(self) -> Locator:
"""
Получает контейнер формы редактирования.
"""
if self._form_container is None:
form_container = self.page.locator("[data-testid='cabinet-bar__cabinet-form']")
try:
form_container.wait_for(state="visible", timeout=10000)
self._form_container = form_container
except:
raise ValueError("Cabinet form container not found")
return self._form_container
def get_available_fields(self) -> list:
"""
Получает список доступных полей.
"""
fields_locators = self._get_form_fields()
return list(fields_locators.keys()) if fields_locators else []
def _get_form_fields(self) -> dict:
"""
Получает все поля формы редактирования стойки.
"""
if self._fields_cache:
return self._fields_cache
form_container = self._get_form_container()
fields_locators = self.get_input_fields_locators(form_container)
self._fields_cache = fields_locators
return fields_locators
def _fill_text_field(self, field_name: str, value: str) -> bool:
"""
Заполняет текстовое поле по полному совпадению названия.
"""
fields_locators = self._get_form_fields()
# Ищем точное совпадение
if field_name not in fields_locators:
logger.debug(f"Text field '{field_name}' not found. Available fields: {list(fields_locators.keys())}")
return False
field_container = fields_locators[field_name]
try:
field_container.scroll_into_view_if_needed()
self.wait_for_timeout(300)
# Ищем input поле
input_field = field_container.locator("input, textarea").first
if input_field.count() == 0:
logger.debug(f"Field '{field_name}' doesn't have input element")
return False
# Очищаем и заполняем
input_field.click()
self.wait_for_timeout(200)
input_field.fill("")
self.wait_for_timeout(200)
input_field.fill(value)
self.wait_for_timeout(500)
# Проверяем что значение установлено
actual_value = input_field.input_value()
if actual_value == value:
logger.debug(f"✓ Text field '{field_name}' filled with: '{value}'")
return True
else:
logger.warning(f"Field '{field_name}' value mismatch: expected '{value}', got '{actual_value}'")
return False
except Exception as e:
logger.error(f"Error filling text field '{field_name}': {e}")
return False
def _fill_combobox_field(self, field_name: str, value: str) -> bool:
"""
Заполняет combobox поле по полному совпадению названия.
"""
fields_locators = self._get_form_fields()
# Ищем точное совпадение
if field_name not in fields_locators:
logger.debug(f"Combobox field '{field_name}' not found. Available fields: {list(fields_locators.keys())}")
return False
field_container = fields_locators[field_name]
try:
field_container.scroll_into_view_if_needed()
self.wait_for_timeout(300)
# Ищем кнопку открытия dropdown
dropdown_button = field_container.locator(".v-input__append-inner, [role='button']").first
if dropdown_button.count() == 0:
# Может быть поле уже открыто
input_field = field_container.locator("input").first
input_field.click()
self.wait_for_timeout(1000)
else:
dropdown_button.click()
self.wait_for_timeout(1000)
# Ищем выпадающий список
active_menu = None
menu_selectors = [
".v-menu__content.menuable__content__active",
".v-select__menu",
".v-autocomplete__content",
".v-menu__content"
]
for selector in menu_selectors:
menu = self.page.locator(selector).first
if menu.count() > 0 and menu.is_visible():
active_menu = menu
break
if not active_menu:
logger.debug(f"No dropdown menu found for '{field_name}'")
return False
# Ищем нужный элемент
dropdown_item = active_menu.locator(f"div[role='listitem'], .v-list-item").filter(
has_text=value
).first
if dropdown_item.count() == 0:
logger.debug(f"Value '{value}' not found in dropdown for '{field_name}'")
self.page.keyboard.press("Escape")
return False
# Выбираем значение
dropdown_item.click()
logger.debug(f"✓ Combobox '{field_name}' set to: '{value}'")
self.wait_for_timeout(1000)
return True
except Exception as e:
logger.error(f"Error filling combobox '{field_name}': {e}")
self.page.keyboard.press("Escape")
return False
def _set_checkbox_field(self, checkbox_label: str, value: bool) -> bool:
"""
Устанавливает состояние checkbox используя input[type="checkbox"].
"""
try:
logger.debug(f"Setting checkbox '{checkbox_label}' to {value}")
# Ищем все checkbox элементы в форме
form_container = self._get_form_container()
checkboxes = form_container.locator("input[type='checkbox']")
if checkboxes.count() == 0:
logger.warning("No checkbox elements found in form")
return False
logger.debug(f"Found {checkboxes.count()} checkbox(es) in form")
# Если несколько чекбоксов, ищем нужный
target_checkbox = None
# Вариант 1: По data-testid
for i in range(checkboxes.count()):
checkbox = checkboxes.nth(i)
testid = checkbox.get_attribute("data-testid")
if testid == "cabinet-bar__main__checkbox__available_ventilation_panel":
target_checkbox = checkbox
logger.debug(f"Found checkbox by data-testid: {testid}")
break
# Вариант 2: По role="checkbox" и aria-checked
if target_checkbox is None:
for i in range(checkboxes.count()):
checkbox = checkboxes.nth(i)
role = checkbox.get_attribute("role")
if role == "checkbox":
target_checkbox = checkbox
logger.debug(f"Found checkbox by role='checkbox'")
break
# Вариант 3: Первый найденный checkbox
if target_checkbox is None:
target_checkbox = checkboxes.first
logger.debug("Using first found checkbox")
# Проверяем состояние
current_aria_checked = target_checkbox.get_attribute("aria-checked")
is_currently_checked = current_aria_checked == "true"
logger.debug(f"Checkbox current state: {is_currently_checked}")
# Если уже в нужном состоянии
if is_currently_checked == value:
logger.debug(f"Checkbox already in desired state ({value})")
return True
# Кликаем на чекбокс
target_checkbox.click(force=True)
self.wait_for_timeout(800)
# Проверяем результат
new_aria_checked = target_checkbox.get_attribute("aria-checked")
is_now_checked = new_aria_checked == "true"
if is_now_checked == value:
logger.info(f"✓ Checkbox '{checkbox_label}' set to {value}")
return True
else:
logger.warning(f"Checkbox state didn't change. Still: {is_now_checked}")
return False
except Exception as e:
logger.error(f"Error setting checkbox '{checkbox_label}': {e}")
return False
def fill_rack_data(self, rack_data: RackEditData) -> Dict[str, int]:
"""
Заполняет все доступные поля в форме редактирования.
"""
logger.debug("Filling rack edit form...")
results = {
"text_fields_filled": 0,
"combobox_fields_filled": 0,
"checkboxes_set": 0
}
# Получаем доступные поля
available_fields = self.get_available_fields()
logger.debug(f"Available fields in form: {available_fields}")
# 1. Заполняем текстовые поля (только если поле существует)
text_fields_mapping = {
"Имя": rack_data.name,
"Серийный номер": rack_data.serial,
"Инвентарный номер": rack_data.inventory,
"Комментарий": rack_data.comment,
"Выделенная мощность": rack_data.power,
"Правила доступа для чтения по умолчанию": rack_data.read_access_rules,
"Правила доступа для записи по умолчанию": rack_data.write_access_rules,
"Правила доступа по умолчанию для получения СМС": rack_data.sms_access_rules,
"Правила доступа по умолчанию для получения email сообщения": rack_data.email_access_rules,
"Правила доступа по умолчанию для получения push уведомлений": rack_data.push_access_rules
}
for field_name, value in text_fields_mapping.items():
if value and value.strip() and field_name in available_fields:
if self._fill_text_field(field_name, value):
results["text_fields_filled"] += 1
# 2. Заполняем combobox поля (только если поле существует)
combobox_fields_mapping = {
"Ввод кабеля": rack_data.cable_entry,
"Состояние": rack_data.state,
"Владелец": rack_data.owner,
"Обслуживающая организация": rack_data.service_org,
"Проект/Титул": rack_data.project
}
for field_name, value in combobox_fields_mapping.items():
if value and value.strip() and field_name in available_fields:
if self._fill_combobox_field(field_name, value):
results["combobox_fields_filled"] += 1
# 3. Устанавливаем checkbox (если есть)
if rack_data.ventilation_panel is not None:
if self._set_checkbox_field("Вентиляционная панель", rack_data.ventilation_panel):
results["checkboxes_set"] += 1
logger.debug(f"Fill results: {results}")
return results

View File

@ -15,6 +15,8 @@ class ConfirmLocators:
""" """
CONFIRM = "//div[contains(@class, 'v-dialog--active')]" CONFIRM = "//div[contains(@class, 'v-dialog--active')]"
TITLE = "//div[@class='v-card__title']/h3" TITLE = f"{CONFIRM}//div[contains(@class, 'v-card__title')]"
#TITLE = "//div[@class='v-card__title']/h3"
BUTTON_CLOSE = "//div[@class='vuedl-layout__closeBtn']" BUTTON_CLOSE = "//div[@class='vuedl-layout__closeBtn']"
TEXT = f"{CONFIRM}/div[2]/div[@class='v-card__text']" #TEXT = f"{CONFIRM}/div[2]/div[@class='v-card__text']"
TEXT = f"{CONFIRM}//div[contains(@class, 'v-card__text')]"

View File

@ -31,38 +31,34 @@ class RackLocators:
# Контейнер формы создания/редактирования стойки # Контейнер формы создания/редактирования стойки
FORM_INPUT_CONTAINER = "//div[contains(@class, 'flex xs6 pa-0')]" FORM_INPUT_CONTAINER = "//div[contains(@class, 'flex xs6 pa-0')]"
# Локаторы полей формы создания стойки # Форма редактирования стойки в модальном окне
RACK_NAME_FIELD = ("//div[contains(@class, 'container')]" RACK_EDIT_FORM = "[data-testid='cabinet-bar__cabinet-form']"
"//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()='Проект/Титул']]")
# Локаторы для выпадающего меню # Локаторы полей формы
INPUT_FORM_RACK_DATA = f"{RACK_EDIT_FORM}"
INPUT_FORM_RACK_DATA_FIELD_NAME = "[data-testid='cabinet-bar__main__text-field__name']"
INPUT_FORM_RACK_DATA_FIELD_COMMENT = "[data-testid='cabinet-bar__main__text-field__comment']"
INPUT_FORM_RACK_DATA_FIELD_SERIAL = "[data-testid='cabinet-bar__main__text-field__serial_number']"
INPUT_FORM_RACK_DATA_FIELD_INVENTORY = "[data-testid='cabinet-bar__main__text-field__inventory_number']"
INPUT_FORM_RACK_DATA_FIELD_POWER = "[data-testid='cabinet-bar__main__text-field__allocated_power']"
# Локаторы для combobox полей
INPUT_FORM_RACK_DATA_FIELD_CABLE_ENTRY = "[data-testid='cabinet-bar__select_enum__select-field__cable_input']"
INPUT_FORM_RACK_DATA_FIELD_CONDITION_TYPE = "[data-testid='cabinet-bar__select_enum__select-field__condition_type']"
INPUT_FORM_RACK_DATA_FIELD_DEPTH = "[data-testid='cabinet-bar__select_enum__select-field__depth']"
INPUT_FORM_RACK_DATA_FIELD_USIZE = "[data-testid='cabinet-bar__select_enum__select-field__usize']"
INPUT_FORM_RACK_DATA_FIELD_OWNER = "[data-testid='cabinet-bar__select__select-field__owner']"
INPUT_FORM_RACK_DATA_FIELD_SERVICE_PROVIDER = "[data-testid='cabinet-bar__select__select-field__service_provider']"
INPUT_FORM_RACK_DATA_FIELD_PROJECT = "[data-testid='cabinet-bar__select__select-field__project']"
# Чекбоксы
INPUT_FORM_RACK_DATA_CHECKBOX_VENTILATION = "[data-testid='cabinet-bar__main__checkbox__available_ventilation_panel'] input[type='checkbox']"
# Локаторы для меню combobox
MENU_ACTIVE_RACK_FORM = "//div[contains(@class, 'menuable__content__active')]"
MENU_ACTIVE_ITEMS = "//div[@role='list']//div[@role='listitem']"
# Локаторы для выпадающего меню (которые использовались в старом коде)
DROPDOWN_LIST = 'div.menuable__content__active div[role="list"]' DROPDOWN_LIST = 'div.menuable__content__active div[role="list"]'
DROPDOWN_ITEM_BY_TEXT = ('div.menuable__content__active ' DROPDOWN_ITEM_BY_TEXT = ('div.menuable__content__active '
'div[role="listitem"]:has(span:has-text("{}"))') 'div[role="listitem"]:has(span:has-text("{}"))')

View File

@ -48,33 +48,27 @@ class RackPage(BasePage):
show_button_locator = self.page.locator(RackLocators.SHOW_RACK_BUTTON) show_button_locator = self.page.locator(RackLocators.SHOW_RACK_BUTTON)
self.show_button = TooltipButton(page, show_button_locator, "show_rack") self.show_button = TooltipButton(page, show_button_locator, "show_rack")
# Кнопка "Переместить"
# replace_button_locator = self.page.locator(RackLocators.TOOLBAR_REPLACE_BUTTON)
# self.replace_button = TooltipButton(page, replace_button_locator, "replace")
# Кнопка "Сохранить"
# done_button_locator = self.page.locator(RackLocators.TOOLBAR_DONE_BUTTON)
# self.done_button = TooltipButton(page, done_button_locator, "done")
# Кнопка "Отменить"
# close_button_locator = self.page.locator(RackLocators.TOOLBAR_CLOSE_BUTTON)
# self.close_button = TooltipButton(page, close_button_locator, "close")
# Кнопка "Удалить"
# remove_button_locator = self.page.locator(RackLocators.TOOLBAR_REMOVE_BUTTON)
# self.remove_button = TooltipButton(page, remove_button_locator, "remove")
self.toolbar = ToolbarComponent(page, "") self.toolbar = ToolbarComponent(page, "")
self.toolbar.add_tooltip_button(locator_button, "edit") self.toolbar.add_tooltip_button(locator_button, "edit")
self.toolbar.add_tooltip_button(hide_button_locator, "hide_rack") self.toolbar.add_tooltip_button(hide_button_locator, "hide_rack")
self.toolbar.add_tooltip_button(show_button_locator, "show_rack") self.toolbar.add_tooltip_button(show_button_locator, "show_rack")
#self.toolbar.add_tooltip_button(replace_button_locator, "replace")
#self.toolbar.add_tooltip_button(done_button_locator, "done")
#self.toolbar.add_tooltip_button(close_button_locator, "close")
#self.toolbar.add_tooltip_button(remove_button_locator, "remove")
# Действия # Действия
def click_edit_button(self) -> None:
"""
Кликает на кнопку 'Изменить'.
"""
logger.debug("Clicking on 'Edit' button...")
# Проверяем видимость кнопки
self.toolbar.check_button_visibility("edit")
self.toolbar.check_button_tooltip("edit", "Изменить")
# Кликаем на кнопку
self.toolbar.get_button_by_name("edit").click()
self.wait_for_timeout(1000)
def get_available_tabs(self) -> list[str]: def get_available_tabs(self) -> list[str]:
""" """
Возвращает список доступных вкладок. Возвращает список доступных вкладок.

View File

@ -120,8 +120,8 @@ class TestRackTab:
# Кликаем на кнопку "Изменить" # Кликаем на кнопку "Изменить"
rack_page.toolbar.get_button_by_name("edit").click() rack_page.toolbar.get_button_by_name("edit").click()
# 3. Создаем экземпляр ModalEditRack # 3. Создаем экземпляр ModalRackEditRack
rack_edit = ModalEditRack(browser) rack_edit = ModalEditRack(browser, rack_name)
# Используем метод для удаления # Используем метод для удаления
rack_edit.click_remove_button() rack_edit.click_remove_button()
@ -282,97 +282,69 @@ class TestRackTab:
# Переходим в режим редактирования # Переходим в режим редактирования
logger.debug("Switching to edit mode...") logger.debug("Switching to edit mode...")
rt.toolbar.check_button_visibility("edit") rt.click_edit_button()
rt.toolbar.get_button_by_name("edit").click() rt.wait_for_timeout(1000)
rt.wait_for_timeout(3000)
# Создаем экземпляр ModalEditRack # Создаем экземпляр ModalEditRack
rack_edit = ModalEditRack(browser) rack_edit = ModalEditRack(browser, RACK_NAME) # ИЗМЕНЕНО: добавлен RACK_NAME
rack_edit.should_be_toolbar_buttons()
# Получаем список доступных полей (используем точные названия из этого списка) # Создаем тестовые данные для заполнения всех полей
available_fields = rack_edit.get_available_fields() rack_edit_data = RackEditData(
logger.info(f"Available fields in form: {available_fields}") # Основные поля
name="Test-Rack-Functionality",
# Создаем маппинг: используем ТОЧНЫЕ названия полей из available_fields serial="SN123456789",
field_mapping = {} inventory="INV987654321",
comment="Тестовый комментарий для стойки (обновленный)",
# Текстовые поля allocated_power="5500",
for field_pattern, test_value in [
("Имя", "Test-Rack-Functionality"),
("Серийный номер", "SN123456789"),
("Инвентарный номер", "INV987654321"),
("Комментарий", "Тестовый комментарий для стойки"),
("Выделенная мощность (Вт/ВА)", "55"),
]:
# Ищем точное совпадение
if field_pattern in available_fields:
field_mapping[field_pattern] = ("text", test_value)
# Combobox поля # Combobox поля
for field_pattern, test_value in [ cable_entry="сверху",
("Ввод кабеля", "Сверху"), state="Введен в эксплуатацию",
("Состояние", "Введен в эксплуатацию"), owner="",
("Владелец", "Тестовый владелец"), service_org="",
("Обслуживающая организация", "Тестовая сервисная организация"), project="",
("Проект/Титул", "Тестовый проект"),
]:
if field_pattern in available_fields:
field_mapping[field_pattern] = ("combobox", test_value)
# Заполняем каждое поле вручную # Checkbox поля
results = { ventilation_panel=True,
"text_fields_filled": 0,
"combobox_fields_filled": 0,
"checkboxes_set": 0
}
logger.info("Filling fields individually...") # Правила доступа (если есть такие поля в форме)
#read_access_rules="admin" if "Правила доступа для чтения по умолчанию" in available_fields else "",
#write_access_rules="admin" if "Правила доступа для записи по умолчанию" in available_fields else "",
#sms_access_rules="admin" if "Правила доступа по умолчанию для получения СМС" in available_fields else "",
#email_access_rules="admin" if "Правила доступа по умолчанию для получения email сообщения" in available_fields else "",
#push_access_rules="admin" if "Правила доступа по умолчанию для получения push уведомлений" in available_fields else "",
)
for field_name, (field_type, value) in field_mapping.items(): # Заполняем все поля формы
logger.info(f"Filling {field_type} field '{field_name}' with '{value}'...") logger.info("Filling rack form using fill_rack_data method...")
results = rack_edit.fill_rack_data(rack_edit_data)
if field_type == "text":
success = rack_edit._fill_text_field(field_name, value)
if success:
results["text_fields_filled"] += 1
logger.info(f"✓ Text field '{field_name}' filled")
else:
logger.warning(f"✗ Failed to fill text field '{field_name}'")
elif field_type == "combobox":
success = rack_edit._fill_combobox_field(field_name, value)
if success:
results["combobox_fields_filled"] += 1
logger.info(f"✓ Combobox field '{field_name}' filled")
else:
logger.warning(f"✗ Failed to fill combobox field '{field_name}'")
# Устанавливаем checkbox
test_ventilation_panel = True
logger.info("Setting ventilation panel checkbox...")
success = rack_edit._set_checkbox_field("Вентиляционная панель", test_ventilation_panel)
if success:
results["checkboxes_set"] += 1
logger.info("✓ Checkbox set")
else:
logger.warning("✗ Failed to set checkbox")
# Проверяем результаты
logger.info(f"Fill results: {results}") logger.info(f"Fill results: {results}")
# Проверяем что хотя бы некоторые поля были заполнены
total_filled = results.get("text_fields_filled", 0) + results.get("combobox_fields_filled", 0)
assert total_filled > 0, f"No fields were filled successfully. Results: {results}"
# Сохраняем изменения # Сохраняем изменения
logger.info("Saving changes...") logger.info("Saving changes...")
# Используем метод из ModalEditRack для кнопки "Сохранить"
rack_edit.click_done_button() rack_edit.click_done_button()
rack_edit.wait_for_timeout(3000) rack_edit.wait_for_timeout(3000)
# Проверяем выход из режима редактирования # Проверяем поля, которые мы заполнили, действительно заполнены
rt.toolbar.check_button_visibility("edit") logger.info("=== Проверка, что все поля корректно заполнены ===")
logger.info("✓ Successfully exited edit mode")
# Вход в режим редактирования
rt.click_edit_button()
# Проверяем поля, пропуская недоступные
verification_results = rack_edit.verify_all_filled_fields(
rack_edit_data,
skip_fields=["Владелец", "Обслуживающая организация", "Проект/Титул"]
)
logger.info(f"Verification results: {verification_results}")
# Проверяем результаты
assert verification_results["incorrectly_filled"] == 0, \
f"Некорректно заполнены доступные поля: {verification_results['field_errors']}"
assert verification_results["not_filled"] == 0, \
f"Не заполнены доступные поля: {verification_results['field_errors']}"
rack_edit.click_close_button()
logger.info("✓ General Info tab fields test completed") logger.info("✓ General Info tab fields test completed")