269 lines
9.5 KiB
Python
269 lines
9.5 KiB
Python
"""Модуль создания объекта 'Стойка'."""
|
||
|
||
from dataclasses import dataclass
|
||
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:
|
||
"""Класс для хранения данных стойки."""
|
||
|
||
name: str
|
||
height: str = "42"
|
||
depth: str = "1000"
|
||
serial: str = ""
|
||
inventory: str = ""
|
||
comment: str = ""
|
||
cable_entry: str = ""
|
||
state: str = ""
|
||
owner: str = ""
|
||
service_org: str = ""
|
||
project: str = ""
|
||
|
||
|
||
class RackObjectMaker(BaseComponent):
|
||
"""Компонент для создания и настройки стойки."""
|
||
|
||
def __init__(self, page: Page) -> None:
|
||
"""
|
||
Инициализирует компонент создания стойки.
|
||
|
||
Args:
|
||
page: Экземпляр страницы Playwright
|
||
"""
|
||
|
||
super().__init__(page)
|
||
|
||
# Действия:
|
||
|
||
def fill_rack_data(self, rack_data: RackData) -> None:
|
||
"""
|
||
Заполняет данные для создания стойки.
|
||
|
||
Args:
|
||
rack_data: Данные стойки
|
||
"""
|
||
|
||
logger.debug(f"Filling rack data: {rack_data.name}")
|
||
|
||
self._fill_text_fields(rack_data)
|
||
self._fill_combobox_fields(rack_data)
|
||
|
||
logger.debug("Rack data filled successfully")
|
||
|
||
def _fill_text_fields(self, rack_data: RackData) -> None:
|
||
"""Заполняет текстовые поля."""
|
||
|
||
def clear_and_fill(locator, value: str, field_name: str):
|
||
"""Очищает поле и заполняет его значением."""
|
||
|
||
field = self.page.locator(locator).first
|
||
# Очищаем поле
|
||
field.click()
|
||
field.press("Control+A")
|
||
field.press("Backspace")
|
||
# Заполняем значение
|
||
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")
|
||
|
||
# Опциональные поля.
|
||
if rack_data.serial:
|
||
clear_and_fill(RackLocators.RACK_SERIAL_FIELD, rack_data.serial, "Serial number")
|
||
|
||
if rack_data.inventory:
|
||
clear_and_fill(RackLocators.RACK_INVENTORY_FIELD, rack_data.inventory, "Inventory number")
|
||
|
||
if rack_data.comment:
|
||
clear_and_fill(RackLocators.RACK_COMMENT_FIELD, rack_data.comment, "Comment")
|
||
|
||
def _fill_combobox_fields(self, rack_data: RackData) -> None:
|
||
"""Заполняет combobox поля."""
|
||
|
||
# Обязательные поля.
|
||
if rack_data.height:
|
||
self._fill_combobox_field("Высота в юнитах", rack_data.height)
|
||
logger.debug(f"Selected height: {rack_data.height} units")
|
||
|
||
if rack_data.depth:
|
||
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("Ввод кабеля", rack_data.cable_entry)
|
||
logger.debug(f"Selected cable entry: {rack_data.cable_entry}")
|
||
|
||
if 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("Владелец", rack_data.owner)
|
||
logger.debug(f"Selected owner: {rack_data.owner}")
|
||
|
||
if 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("Проект/Титул", rack_data.project)
|
||
logger.debug(f"Selected project/title: {rack_data.project}")
|
||
|
||
def _fill_combobox_field(self, field_name: str, value: str) -> None:
|
||
"""
|
||
Заполняет combobox поле.
|
||
|
||
Args:
|
||
field_name: Название поля
|
||
value: Значение для установки
|
||
"""
|
||
|
||
logger.debug(f"Filling field '{field_name}' with value '{value}'...")
|
||
|
||
# Используем универсальный локатор для 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()
|
||
self.wait_for_timeout(500)
|
||
|
||
# Проверяем видимость поля
|
||
self.check_visibility(field_container, f"Field '{field_name}' not found")
|
||
|
||
# Кликаем и вводим значение
|
||
field_container.click(force=True)
|
||
self.wait_for_timeout(1000)
|
||
|
||
# Вводим значение из выпадающего списка
|
||
dropdown_item_locator = RackLocators.DROPDOWN_ITEM_BY_TEXT.format(value)
|
||
element = self.page.locator(dropdown_item_locator).first
|
||
|
||
# Скроллим к элементу если нужно
|
||
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:
|
||
"""
|
||
Возвращает локатор поля по его названию.
|
||
|
||
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:
|
||
"""
|
||
Проверяет наличие полей специфичных для стойки.
|
||
|
||
Raises:
|
||
AssertionError: Если какое-либо поле не найдено
|
||
"""
|
||
|
||
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)")
|
||
]
|
||
|
||
# Дополнительные поля
|
||
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")
|
||
]
|
||
|
||
# Проверяем обязательные поля
|
||
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_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")
|
||
else:
|
||
logger.debug(f"Optional field '{field_name}' not found or not visible")
|
||
|
||
logger.debug("All main rack fields are present")
|