"""Модуль создания объекта 'Стойка'.""" 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")