332 lines
12 KiB
Python
332 lines
12 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 (Page): Экземпляр страницы Playwright
|
||
"""
|
||
|
||
super().__init__(page)
|
||
|
||
# Действия:
|
||
|
||
def _fill_combobox_field(self, field_name: str, value: str, fields_locators: dict) -> None:
|
||
"""
|
||
Заполняет combobox поле.
|
||
|
||
Args:
|
||
field_name (str): Название поля
|
||
value (str): Значение для установки
|
||
fields_locators (dict): Словарь с найденными полями формы
|
||
|
||
Raises:
|
||
ValueError: Если поле не найдено в форме
|
||
"""
|
||
|
||
# Получаем контейнер поля по его названию
|
||
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()
|
||
self.wait_for_timeout(300)
|
||
|
||
# Проверяем видимость поля
|
||
self.check_visibility(field_container, f"Field '{field_name}' not found")
|
||
|
||
# Находим кнопку открытия выпадающего списка внутри контейнера поля
|
||
open_button = field_container.locator(".v-input__append-inner").first
|
||
|
||
# Кликаем для открытия выпадающего списка
|
||
open_button.click(force=True)
|
||
self.wait_for_timeout(300)
|
||
|
||
# Вводим значение из выпадающего списка
|
||
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(300)
|
||
element.click()
|
||
|
||
logger.debug(f"Field '{field_name}' filled successfully")
|
||
|
||
def _fill_combobox_fields(self, rack_data: RackData) -> None:
|
||
"""Заполняет combobox поля."""
|
||
|
||
# Получаем все поля формы
|
||
fields_locators = self._get_form_fields()
|
||
|
||
# Обязательные поля.
|
||
if rack_data.height:
|
||
self._fill_combobox_field("Высота в юнитах", rack_data.height, fields_locators)
|
||
logger.debug(f"Selected height: {rack_data.height} units")
|
||
|
||
if rack_data.depth:
|
||
self._fill_combobox_field("Глубина (мм)", rack_data.depth, fields_locators)
|
||
logger.debug(f"Selected depth: {rack_data.depth} mm")
|
||
|
||
# Опциональные поля.
|
||
if rack_data.cable_entry:
|
||
self._fill_combobox_field("Ввод кабеля", rack_data.cable_entry, fields_locators)
|
||
logger.debug(f"Selected cable entry: {rack_data.cable_entry}")
|
||
|
||
if rack_data.state:
|
||
self._fill_combobox_field("Состояние", rack_data.state, fields_locators)
|
||
logger.debug(f"Selected state: {rack_data.state}")
|
||
|
||
if rack_data.owner:
|
||
self._fill_combobox_field("Владелец", rack_data.owner, fields_locators)
|
||
logger.debug(f"Selected owner: {rack_data.owner}")
|
||
|
||
if rack_data.service_org:
|
||
self._fill_combobox_field("Обслуживающая организация", rack_data.service_org, fields_locators)
|
||
logger.debug(f"Selected service organization: {rack_data.service_org}")
|
||
|
||
if rack_data.project:
|
||
self._fill_combobox_field("Проект/Титул", rack_data.project, fields_locators)
|
||
logger.debug(f"Selected project/title: {rack_data.project}")
|
||
|
||
def _fill_text_fields(self, rack_data: RackData) -> None:
|
||
"""Заполняет текстовые поля."""
|
||
|
||
logger.debug("Filling text fields...")
|
||
|
||
# Получаем все поля формы
|
||
fields_locators = self._get_form_fields()
|
||
|
||
logger.debug(f"Available text fields: {list(fields_locators.keys())}")
|
||
|
||
def clear_and_fill(field_name: str, value: str):
|
||
"""Очищает поле и заполняет его значением."""
|
||
|
||
if not value:
|
||
logger.debug(f"Skipping empty value for field '{field_name}'")
|
||
return
|
||
|
||
# Получаем контейнер поля
|
||
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(300)
|
||
|
||
# Проверяем, не 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
|
||
|
||
# Очищаем поле
|
||
input_field.click()
|
||
input_field.press("Control+A")
|
||
input_field.press("Backspace")
|
||
|
||
# Заполняем значение
|
||
input_field.fill(value)
|
||
logger.debug(f"Filled '{field_name}': {value}")
|
||
|
||
# Обязательные поля
|
||
if rack_data.name:
|
||
clear_and_fill("Имя", rack_data.name)
|
||
|
||
# Опциональные поля
|
||
if rack_data.serial:
|
||
clear_and_fill("Серийный номер", rack_data.serial)
|
||
|
||
if rack_data.inventory:
|
||
clear_and_fill("Инвентарный номер", rack_data.inventory)
|
||
|
||
if rack_data.comment:
|
||
clear_and_fill("Комментарий", rack_data.comment)
|
||
|
||
logger.debug("Text fields filled successfully")
|
||
|
||
def _get_form_fields(self) -> dict:
|
||
"""
|
||
Получает все поля формы стойки.
|
||
|
||
Returns:
|
||
dict: Словарь {название поля: Locator контейнера поля}
|
||
|
||
Raises:
|
||
ValueError: Если контейнер формы не найден
|
||
"""
|
||
|
||
# Получаем контейнер формы (второй элемент)
|
||
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")
|
||
|
||
return self.get_input_fields_locators(container_locator)
|
||
|
||
def _scroll_until_element(self, locator: Locator, name: str) -> None:
|
||
"""
|
||
Скроллит список до тех пор, пока не перестанут подгружаться новые элементы.
|
||
|
||
Args:
|
||
locator (Locator): Локатор элементов или строка с CSS/XPath
|
||
name (str): Имя элемента для поиска
|
||
"""
|
||
|
||
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 fill_rack_data(self, rack_data: RackData) -> None:
|
||
"""
|
||
Заполняет данные для создания стойки.
|
||
|
||
Args:
|
||
rack_data (RackData): Данные стойки
|
||
"""
|
||
|
||
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 check_rack_fields_presence(self) -> None:
|
||
"""
|
||
Проверяет наличие полей специфичных для стойки.
|
||
|
||
Raises:
|
||
AssertionError: Если какое-либо поле не найдено
|
||
"""
|
||
|
||
logger.debug("Checking rack fields presence...")
|
||
|
||
# Получаем все поля формы
|
||
fields_locators = self._get_form_fields()
|
||
|
||
logger.debug(f"Found fields in form: {list(fields_locators.keys())}")
|
||
|
||
# Список ожидаемых полей для стойки
|
||
expected_fields = [
|
||
"Имя",
|
||
"Высота в юнитах",
|
||
"Глубина (мм)",
|
||
"Серийный номер",
|
||
"Инвентарный номер",
|
||
"Комментарий",
|
||
"Ввод кабеля",
|
||
"Состояние",
|
||
"Владелец",
|
||
"Обслуживающая организация",
|
||
"Проект/Титул"
|
||
]
|
||
|
||
# Проверяем наличие обязательных полей с помощью assert
|
||
required_fields = ["Имя", "Высота в юнитах", "Глубина (мм)"]
|
||
|
||
for field_name in required_fields:
|
||
# Проверяем наличие поля в словаре
|
||
assert field_name in fields_locators, f"Required field '{field_name}' not 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}' found but not visible")
|
||
else:
|
||
logger.debug(f"Optional field '{field_name}' not found in form")
|
||
|
||
logger.debug("All main rack fields are present")
|