"""Модуль создания объекта 'Стойка'.""" 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: """Заполняет текстовые поля.""" 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): """Очищает поле и заполняет его значением.""" 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 # Очищаем поле 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 _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: Значение для установки """ # Получаем контейнер формы (второй элемент) container_locator = self.page.locator(RackLocators.FORM_INPUT_CONTAINER).nth(1) # Используем метод из базового класса 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() self.wait_for_timeout(500) # Проверяем видимость поля 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(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 check_rack_fields_presence(self) -> None: """ Проверяет наличие полей специфичных для стойки. Raises: AssertionError: Если какое-либо поле не найдено """ logger.debug("Checking rack fields presence...") # Получаем контейнер формы (второй элемент) 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 = [ "Имя", "Высота в юнитах", "Глубина (мм)", "Серийный номер", "Инвентарный номер", "Комментарий", "Ввод кабеля", "Состояние", "Владелец", "Обслуживающая организация", "Проект/Титул" ] # Проверяем наличие обязательных полей с помощью 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")