# components/dynamic_form_component.py """Универсальный компонент для работы с динамическими формами.""" from typing import Optional, Dict, List, Any from playwright.sync_api import Page, Locator from tools.logger import get_logger logger = get_logger("DYNAMIC_FORM_COMPONENT") class DynamicFormComponent: """Компонент для работы с формами, находит поля по меткам.""" def __init__(self, page: Page, form_selector: str = "form, .v-form"): self.page = page self.form_selector = form_selector self._form_container = None self._field_labels_cache = {} def _get_form_container(self) -> Locator: """Получает контейнер формы.""" if self._form_container is None: container = self.page.locator(self.form_selector) try: container.wait_for(state="visible", timeout=5000) self._form_container = container except: raise ValueError(f"Form container not found: {self.form_selector}") return self._form_container def get_all_field_labels(self) -> List[str]: """Получает все метки полей в форме.""" self._load_field_labels() return list(self._field_labels_cache.keys()) def _load_field_labels(self) -> None: """Загружает метки полей формы.""" if self._field_labels_cache: return form = self._get_form_container() labels = {} # Ищем все элементы с текстом, которые могут быть метками # Адаптируйте селекторы под вашу структуру DOM label_elements = form.locator( ".v-label, label, .field-label, [class*='label']" ) for i in range(label_elements.count()): elem = label_elements.nth(i) label_text = elem.text_content().strip() if label_text and len(label_text) < 100: # Исключаем большие тексты labels[label_text] = elem self._field_labels_cache = labels def get_field_by_label(self, label_text: str) -> Optional[Locator]: """Находит поле по метке.""" self._load_field_labels() # Прямое совпадение if label_text in self._field_labels_cache: return self._get_field_input(self._field_labels_cache[label_text]) # Частичное совпадение for label, element in self._field_labels_cache.items(): if label_text in label or label in label_text: return self._get_field_input(element) logger.warning(f"Поле с меткой '{label_text}' не найдено") return None def _get_field_input(self, label_element: Locator) -> Optional[Locator]: """Получает элемент ввода рядом с меткой.""" # Разные стратегии поиска input элемента strategies = [ lambda: label_element.locator("+ input, + textarea").first, lambda: label_element.locator("../..").locator("input, textarea").first, lambda: self.page.locator(f"input[aria-label*='{label_element.text_content()}']"), lambda: self.page.locator(f"input[placeholder*='{label_element.text_content()}']"), ] for strategy in strategies: try: input_elem = strategy() if input_elem.count() > 0: return input_elem except: continue return None def get_field_type_by_label(self, label_text: str) -> str: """Определяет тип поля по метке.""" field_element = self.get_field_by_label(label_text) if not field_element: return "unknown" # Определяем тип по атрибутам input_type = field_element.get_attribute("type") role = field_element.get_attribute("role") if input_type == "checkbox" or role == "checkbox": return "checkbox" elif role == "combobox" or field_element.get_attribute("aria-haspopup") == "listbox": return "combobox" else: return "text" def fill_field_by_label(self, label_text: str, value: Any) -> bool: """Заполняет поле по метке.""" field_type = self.get_field_type_by_label(label_text) if field_type == "text": return self._fill_text_field_by_label(label_text, str(value)) elif field_type == "combobox": return self._fill_combobox_by_label(label_text, str(value)) elif field_type == "checkbox": return self._set_checkbox_by_label(label_text, bool(value)) else: logger.warning(f"Неизвестный тип поля для '{label_text}'") return False def _fill_text_field_by_label(self, label_text: str, value: str) -> bool: """Заполняет текстовое поле по метке.""" field = self.get_field_by_label(label_text) if not field: return False try: field.click() field.fill("") field.fill(value) # Проверяем результат actual_value = field.input_value() if actual_value == value: logger.debug(f"✓ Заполнено поле '{label_text}': '{value}'") return True else: logger.warning(f"Несоответствие значения для '{label_text}'") return False except Exception as e: logger.error(f"Ошибка при заполнении поля '{label_text}': {e}") return False def _fill_combobox_by_label(self, label_text: str, value: str) -> bool: """Заполняет combobox по метке.""" # Реализация аналогичная modal_rack_edit.py # ...