# makers/edit_rack_maker.py """Модуль для работы с модальным окном редактирования стойки.""" import re from typing import Optional, List, Tuple, Any from playwright.sync_api import Page from tools.logger import get_logger from locators.rack_locators import RackLocators from components.modal_window_component import ModalWindowComponent from components.dropdown_list_component import DropdownList from components.confirm_component import ConfirmComponent from elements.text_input_element import TextInput from forms.edit_rack_form import EditRackForm, EditRackData logger = get_logger("EDIT_RACK_MAKER") logger.setLevel("INFO") # Re-export EditRackData for backward compatibility EditRackData = EditRackData __all__ = ['EditRackMaker', 'EditRackData'] class EditRackMaker(ModalWindowComponent): """Компонент для работы с модальным окном редактирования стойки. Предоставляет методы для взаимодействия с элементами окна: - переключение между вкладками - заполнение полей общей информации (через EditRackForm) - работа с изображениями - настройка правил доступа - сохранение/отмена изменений - удаление стойки """ # Константы для названий вкладок TAB_GENERAL = "Общая информация" TAB_IMAGE = "Изображение" TAB_SETTINGS = "Настройки" # Маппинг полей для вкладки "Настройки" ACCESS_RULES_MAPPING = { "Правила доступа для чтения": ( "read_access_rules", "rules_read_input", "rules_read_list" ), "Правила доступа для записи": ( "write_access_rules", "rules_write_input", "rules_write_list" ), "Правила доступа для получения СМС": ( "sms_access_rules", "rules_sms_input", "rules_sms_list" ), "Правила доступа для получения email сообщения": ( "email_access_rules", "rules_email_input", "rules_email_list" ), "Правила доступа для получения push уведомлений": ( "push_access_rules", "rules_push_input", "rules_push_list" ), } # Локаторы для полей правил доступа ACCESS_RULES_LOCATORS = { "Правила доступа для чтения": RackLocators.SETTINGS_READ_RULES, "Правила доступа для записи": RackLocators.SETTINGS_WRITE_RULES, "Правила доступа для получения СМС": RackLocators.SETTINGS_SMS_RULES, "Правила доступа для получения email сообщения": RackLocators.SETTINGS_EMAIL_RULES, "Правила доступа для получения push уведомлений": RackLocators.SETTINGS_PUSH_RULES, } def __init__(self, page: Page, rack_name: str) -> None: """Инициализирует компонент редактирования стойки. Args: page: Экземпляр страницы Playwright. rack_name: Имя редактируемой стойки. """ super().__init__(page) self.rack_name = rack_name self.page = page self.active_tab = self.TAB_GENERAL self.tabs = {} self.content_items = {} self.delete_confirm = None self.edit_form = None # Настройка заголовка и кнопки закрытия self.window_title = rack_name locator_button_toolbar_close = ( self.page.get_by_role("navigation") .filter(has_text=re.compile(self.window_title)) .get_by_role("button") ) self.add_toolbar_title(self.window_title) self.add_toolbar_button(locator_button_toolbar_close, "close") # Инициализация компонента подтверждения удаления self.delete_confirm = ConfirmComponent(page, " Отмена ", " Удалить ") # Инициализация вкладок и содержимого self._init_tabs() self._init_active_tab_content() self._init_toolbar_buttons() def _init_tabs(self) -> None: """Инициализирует вкладки окна редактирования.""" self.tabs = { self.TAB_GENERAL: self.page.locator(RackLocators.MODAL_TAB_GENERAL), self.TAB_IMAGE: self.page.locator(RackLocators.MODAL_TAB_IMAGE), self.TAB_SETTINGS: self.page.locator(RackLocators.MODAL_TAB_SETTINGS), } logger.debug(f"Initialized tabs: {list(self.tabs.keys())}") def _init_active_tab_content(self) -> None: """Инициализирует содержимое активной вкладки.""" self.content_items = {} if self.active_tab == self.TAB_GENERAL: self._init_general_tab_content() elif self.active_tab == self.TAB_IMAGE: self._init_image_tab_content() else: self._init_settings_tab_content() def _init_general_tab_content(self) -> None: """Инициализирует содержимое вкладки 'Общая информация'.""" # Инициализируем форму редактирования self.edit_form = EditRackForm(self.page) # Копируем content_items из формы self.content_items.update(self.edit_form.content_items) logger.debug("General tab content initialized via EditRackForm") def _init_image_tab_content(self) -> None: """Инициализирует содержимое вкладки 'Изображение'.""" try: self._init_image_upload_elements() logger.debug("Image tab content initialized") except Exception as e: logger.error(f"Error initializing image tab content: {e}") def _init_image_upload_elements(self) -> None: """Инициализирует элементы загрузки изображения.""" image_tab_container = self.page.locator(RackLocators.IMAGE_UPLOAD_CONTAINER) upload_icon = image_tab_container.locator(RackLocators.IMAGE_UPLOAD_ICON) self.add_content_item("image_upload_icon", upload_icon) upload_input = image_tab_container.locator(RackLocators.IMAGE_UPLOAD_INPUT) self.add_content_item("image_upload_input", upload_input) def _init_settings_tab_content(self) -> None: """Инициализирует содержимое вкладки 'Настройки доступа'.""" self._init_access_rules_fields() logger.debug("Settings tab content initialized") def _init_access_rules_fields(self) -> None: """Инициализирует поля правил доступа.""" settings_container = self.page.locator(RackLocators.SETTINGS_CONTAINER) # Используем базовый метод для получения всех полей fields_locators = self.get_input_fields_locators(settings_container) # Для каждого поля из маппинга проверяем наличие и инициализируем for field_label, (_, input_name, list_name) in self.ACCESS_RULES_MAPPING.items(): if field_label not in fields_locators: continue self._init_single_access_rule_field( field_label, fields_locators[field_label], input_name, list_name ) logger.debug( f"Settings tab content initialized. Found fields: {list(fields_locators.keys())}" ) def _init_single_access_rule_field( self, field_label: str, input_container: Any, input_name: str, list_name: str ) -> None: """Инициализирует одно поле правила доступа. Args: field_label: Метка поля. input_container: Контейнер поля ввода. input_name: Имя поля ввода. list_name: Имя списка. """ try: # Ищем input внутри контейнера input_element = input_container.locator("input, textarea, select").first if input_element.count() > 0: field_input = TextInput(self.page, input_element, input_name) self.add_content_item(input_name, field_input) self.add_content_item(list_name, DropdownList(self.page)) logger.debug(f"Initialized access rule field: '{field_label}'") except Exception as e: logger.error(f"Error initializing access rule field '{field_label}': {e}") def _init_toolbar_buttons(self) -> None: """Инициализирует кнопки тулбара.""" self.add_button(self.page.locator(RackLocators.TOOLBAR_REPLACE_BUTTON), "replace") self.add_button(self.page.locator(RackLocators.TOOLBAR_DONE_BUTTON), "save") self.add_button(self.page.locator(RackLocators.TOOLBAR_CLOSE_BUTTON), "cancel") self.add_button(self.page.locator(RackLocators.TOOLBAR_REMOVE_BUTTON), "delete") # Делегирование методов форме редактирования def fill_rack_data(self, rack_data: EditRackData) -> dict: """Заполняет поля формы редактирования стойки. Args: rack_data: Данные для заполнения. Returns: Словарь с результатами заполнения. """ if self.active_tab != self.TAB_GENERAL: self.switch_to_tab(self.TAB_GENERAL) if not self.edit_form: logger.error("Edit form not initialized") return { "text_fields_filled": 0, "combobox_fields_filled": 0, "checkboxes_set": 0 } results = self.edit_form.fill_rack_data(rack_data) logger.info(f"Filled rack data via EditRackForm: {results}") return results def clear_field(self, field_name: str) -> None: """Очищает указанное поле формы. Args: field_name: Название поля для очистки. """ if self.edit_form: self.edit_form.clear_field(field_name) def get_field_value(self, field_name: str) -> Optional[str]: """Получает значение поля формы. Args: field_name: Название поля. Returns: Значение поля или None если поле не найдено. """ if self.edit_form: return self.edit_form.get_field_value(field_name) return None def is_field_highlighted_as_error(self, field_name: str) -> bool: """Проверяет, подсвечено ли поле как ошибочное. Args: field_name: Название поля для проверки. Returns: bool: True если поле подсвечено ошибкой. """ if self.edit_form: return self.edit_form.is_field_highlighted_as_error(field_name) return False def wait_for_field_error(self, field_name: str, timeout: int = 5000) -> bool: """Ожидает появления подсветки ошибки на поле. Args: field_name: Название поля. timeout: Таймаут в миллисекундах. Returns: bool: True если ошибка появилась. """ if self.edit_form: return self.edit_form.wait_for_field_error(field_name, timeout) return False # Действия с вкладками def switch_to_tab(self, tab_name: str) -> None: """Переключается на указанную вкладку. Args: tab_name: Название вкладки для переключения. Raises: ValueError: Если указана неизвестная вкладка. """ if tab_name not in self.tabs: raise ValueError( f"Unknown tab: {tab_name}. Available tabs: {list(self.tabs.keys())}" ) self.tabs[tab_name].click() self.wait_for_timeout(1000) self.active_tab = tab_name self._init_active_tab_content() logger.info(f"Switched to tab: {tab_name}") def get_active_tab(self) -> str: """Возвращает название активной вкладки. Returns: Название активной вкладки. """ return self.active_tab def is_tab_active(self, tab_name: str) -> bool: """Проверяет, активна ли указанная вкладка. Args: tab_name: Название вкладки для проверки. Returns: True если вкладка активна, иначе False. """ return self.active_tab == tab_name # Действия с изображениями def upload_image(self, image_path: str) -> None: """Загружает изображение на вкладке 'Изображение'. Args: image_path: Путь к файлу изображения. Raises: RuntimeError: Если не найден элемент для загрузки изображения. """ if self.active_tab != self.TAB_IMAGE: self.switch_to_tab(self.TAB_IMAGE) try: self._perform_image_upload(image_path) except Exception as e: logger.error(f"Error uploading image: {e}") raise def _perform_image_upload(self, image_path: str) -> None: """Выполняет загрузку изображения. Args: image_path: Путь к файлу изображения. Raises: RuntimeError: Если не найден элемент для загрузки изображения. """ upload_icon = self.get_content_item("image_upload_icon") if upload_icon and upload_icon.count() > 0: upload_icon.click() self.wait_for_timeout(500) upload_input = self.get_content_item("image_upload_input") if upload_input and upload_input.count() > 0: upload_input.set_input_files(image_path) logger.info(f"Uploaded image: {image_path}") return # Пробуем найти input напрямую file_input = self.page.locator(RackLocators.IMAGE_UPLOAD_INPUT) if file_input.count() > 0: file_input.first.set_input_files(image_path) logger.info(f"Uploaded image via page input: {image_path}") return raise RuntimeError("Image upload input not found") def has_current_image(self) -> bool: """Проверяет, есть ли текущее изображение у стойки. Returns: True если изображение загружено, иначе False. """ if self.active_tab != self.TAB_IMAGE: self.switch_to_tab(self.TAB_IMAGE) try: return self._check_image_exists() except Exception as e: logger.error(f"Error checking for current image: {e}") return False def _check_image_exists(self) -> bool: """Проверяет наличие изображения. Returns: True если изображение существует, иначе False. """ image_container = self.page.locator(RackLocators.IMAGE_UPLOAD_CONTAINER) img_element = image_container.locator(RackLocators.IMAGE_PREVIEW) upload_icon = image_container.locator(RackLocators.IMAGE_UPLOAD_ICON) if img_element.count() > 0 and img_element.first.is_visible(): return True return False # Действия с настройками доступа def fill_access_rules( self, users_to_add: Optional[List[str]] = None, target_fields: Optional[List[str]] = None ) -> dict: """Заполняет правила доступа в указанных полях. В каждый combobox добавляются указанные пользователи. Args: users_to_add: Список пользователей для добавления. target_fields: Список целевых полей для заполнения. Returns: Словарь с результатами заполнения. """ if self.active_tab != self.TAB_SETTINGS: self.switch_to_tab(self.TAB_SETTINGS) results = self._init_fill_results() if users_to_add is None: users_to_add = self._get_default_users() fields_to_process = self._get_fields_to_process(target_fields) logger.info(f"Processing fields: {[f[0] for f in fields_to_process]}") logger.info(f"Users to add: {users_to_add}") for field_index, (field_label, _, input_name, _) in enumerate(fields_to_process): self._process_single_field( field_index, field_label, users_to_add, results ) self._log_fill_summary(results, len(fields_to_process), len(users_to_add)) return results def _init_fill_results(self) -> dict: """Инициализирует словарь результатов заполнения. Returns: Словарь с начальными значениями результатов. """ return { "access_rules_filled": 0, "errors": [], "fields_processed": [], "field_stats": {} } def _get_default_users(self) -> List[str]: """Возвращает список пользователей по умолчанию. Returns: Список пользователей. """ return [ "admin", "manager", "operator", "sec", "collector" ] def _process_single_field( self, field_index: int, field_label: str, users_to_add: List[str], results: dict ) -> None: """Обрабатывает одно поле правил доступа. Args: field_index: Индекс поля. field_label: Название поля. users_to_add: Список пользователей для добавления. results: Словарь с результатами. """ # Инициализируем статистику для поля results["field_stats"][field_label] = { "expected": len(users_to_add), "added": 0, "failed_users": [] } field_locator = self.ACCESS_RULES_LOCATORS.get(field_label) if not field_locator: results["errors"].append(f"Locator not found for field '{field_label}'") return try: if field_index > 0: self.page.mouse.click(10, 10) self.wait_for_timeout(500) results["fields_processed"].append(field_label) # Находим элемент поля field_element = self.page.locator(field_locator).first if field_element.count() == 0: results["errors"].append(f"Field element not found for '{field_label}'") return self._clear_field(field_element) # Открываем combobox и добавляем пользователей added_count, field_errors = self._open_dropdown_and_add_users( field_element, field_label, users_to_add ) results["access_rules_filled"] += added_count results["field_stats"][field_label]["added"] = added_count results["field_stats"][field_label]["failed_users"] = field_errors results["errors"].extend([f"{field_label}: {error}" for error in field_errors]) # Закрываем dropdown self.page.mouse.click(10, 10) self.wait_for_timeout(500) logger.info(f"Field '{field_label}': added {added_count}/{len(users_to_add)} users") except Exception as e: results["errors"].append(f"Error processing field {field_label}: {str(e)}") def _get_fields_to_process( self, target_fields: Optional[List[str]] = None ) -> List[Tuple[str, str, str, str]]: """Определяет поля для обработки. Args: target_fields: Список целевых полей. Returns: Список кортежей (field_label, attr_name, input_name, list_name). """ if target_fields is None: return list(self.ACCESS_RULES_MAPPING.items()) fields_to_process = [] for field_attr in target_fields: for field_label, (attr_name, input_name, list_name) in self.ACCESS_RULES_MAPPING.items(): if attr_name == field_attr: fields_to_process.append((field_label, attr_name, input_name, list_name)) break return fields_to_process def _clear_field(self, field_element: Any) -> None: """Очищает поле от выбранных значений. Args: field_element: Элемент поля для очистки. """ parent_container = field_element.locator( "xpath=ancestor::div[contains(@class, 'v-input')]" ).first if parent_container.count() == 0: return clear_button = parent_container.locator( ".v-input__icon--clear button, .v-input__icon--append button, i.mdi-close-circle" ).first if clear_button.count() > 0 and clear_button.is_visible(): clear_button.click() self.wait_for_timeout(500) def _open_dropdown_and_add_users( self, field_element: Any, field_label: str, users_to_add: List[str] ) -> Tuple[int, List[str]]: """Открывает выпадающий список и добавляет пользователей. Args: field_element: Элемент поля. field_label: Название поля. users_to_add: Список пользователей для добавления. Returns: Кортеж (количество добавленных, список ошибок). """ added_count = 0 errors = [] field_element.click(force=True) self.wait_for_timeout(1500) dropdown_menu = self._get_dropdown_menu() if not dropdown_menu: errors.append(f"Could not open dropdown for {field_label}") return added_count, errors for username in users_to_add: added, error = self._add_user_to_dropdown(dropdown_menu, username, field_label) if added: added_count += 1 if error: errors.append(error) return added_count, errors def _get_dropdown_menu(self) -> Any: """Возвращает выпадающее меню. Returns: Locator выпадающего меню или None, если меню не найдено. """ dropdown_menu = self.page.locator(RackLocators.MENU_ACTIVE_RACK_FORM).first if dropdown_menu.count() == 0 or not dropdown_menu.is_visible(): dropdown_menu = self.page.locator( ".v-menu__content--active, .menuable__content__active" ).first if dropdown_menu.count() == 0 or not dropdown_menu.is_visible(): return None return dropdown_menu def _add_user_to_dropdown( self, dropdown_menu: Any, username: str, field_label: str ) -> Tuple[bool, Optional[str]]: """Добавляет пользователя из выпадающего списка. Args: dropdown_menu: Выпадающее меню. username: Имя пользователя. field_label: Название поля. Returns: Кортеж (добавлен ли пользователь, сообщение об ошибке или None). """ try: user_item = dropdown_menu.locator(f"[role='listitem']:has-text('{username}')").first if user_item.count() == 0: user_item = dropdown_menu.locator(f"div:has-text('{username}')").first if user_item.count() > 0: user_item.click() self.wait_for_timeout(500) return True, None return False, f"User '{username}' not found in dropdown for {field_label}" except Exception as e: return False, f"Failed to add user '{username}' to {field_label}: {str(e)}" def _log_fill_summary(self, results: dict, fields_count: int, users_count: int) -> None: """Логирует итоговую статистику заполнения. Args: results: Словарь с результатами. fields_count: Количество полей. users_count: Количество пользователей. """ total_expected = fields_count * users_count logger.info(f"Total added: {results['access_rules_filled']}/{total_expected}") for field_label, stats in results["field_stats"].items(): if stats["added"] < stats["expected"]: logger.warning( f"Field '{field_label}' added only {stats['added']}/{stats['expected']} users. " f"Failed: {stats['failed_users']}" ) def verify_access_rules( self, expected_users: Optional[List[str]] = None, target_fields: Optional[List[str]] = None ) -> dict: """Проверяет заполнение правил доступа в указанных полях. Проверяет, что в каждом поле есть все указанные пользователи. Args: expected_users: Список ожидаемых пользователей. target_fields: Список целевых полей для проверки. Returns: Словарь с результатами проверки. """ if self.active_tab != self.TAB_SETTINGS: self.switch_to_tab(self.TAB_SETTINGS) if expected_users is None: expected_users = self._get_default_users() fields_to_verify = self._get_fields_to_process(target_fields) total_expected_fields = len(fields_to_verify) * len(expected_users) results = { "total_expected_fields": total_expected_fields, "correctly_filled": 0, "incorrectly_filled": 0, "field_errors": [], "fields_verified": [field_label for field_label, _, _, _ in fields_to_verify], "expected_users": expected_users } for field_label, _, _, _ in fields_to_verify: self._verify_single_field(field_label, expected_users, results) if results["total_expected_fields"] > 0: success_rate = results["correctly_filled"] / results["total_expected_fields"] * 100 logger.info( f"Access rules verification: {results['correctly_filled']}/" f"{results['total_expected_fields']} ({success_rate:.1f}%)" ) return results def _verify_single_field( self, field_label: str, expected_users: List[str], results: dict ) -> None: """Проверяет одно поле правил доступа. Args: field_label: Название поля. expected_users: Список ожидаемых пользователей. results: Словарь с результатами для обновления. """ field_locator = self.ACCESS_RULES_LOCATORS.get(field_label) if not field_locator: self._add_field_error( results, field_label, expected_users, f"Locator not found for field '{field_label}'" ) return try: field_element = self.page.locator(field_locator).first if field_element.count() == 0: self._add_field_error( results, field_label, expected_users, f"Field '{field_label}' not found" ) return selected_users = self._get_selected_users(field_element) logger.debug(f"Field '{field_label}' selected users: {selected_users}") for expected_user in expected_users: if expected_user in selected_users: results["correctly_filled"] += 1 else: results["incorrectly_filled"] += 1 results["field_errors"].append( f"Field '{field_label}' missing user: {expected_user} " f"(selected: {selected_users})" ) except Exception as e: self._add_field_error( results, field_label, expected_users, f"Error verifying {field_label}: {str(e)}" ) logger.error(f"Error verifying {field_label}: {e}") def _get_selected_users(self, field_element: Any) -> List[str]: """Получает список выбранных пользователей из поля. Args: field_element: Элемент поля. Returns: Список выбранных пользователей. """ selected_users = [] parent_container = field_element.locator( "xpath=ancestor::div[contains(@class, 'v-input')]" ).first if parent_container.count() == 0: return selected_users selections_container = parent_container.locator( ".v-select__selections, .v-chip__content" ).first if selections_container.count() == 0: return selected_users user_elements = selections_container.locator( "span.v-chip__content, span.v-chip, span:not([class])" ).all() for element in user_elements: user_text = self._extract_user_text(element) if user_text: selected_users.append(user_text) return selected_users def _extract_user_text(self, element: Any) -> Optional[str]: """Извлекает текст пользователя из элемента. Args: element: Элемент DOM. Returns: Текст пользователя или None. """ user_text = element.text_content() or "" user_text = user_text.strip() if not user_text or user_text in [",", " ", ""]: return None if user_text.startswith(','): user_text = user_text[1:].strip() return user_text if user_text else None def _add_field_error( self, results: dict, field_label: str, expected_users: List[str], error_msg: str ) -> None: """Добавляет ошибку для поля. Args: results: Словарь с результатами. field_label: Название поля (не используется, оставлен для единообразия). expected_users: Список ожидаемых пользователей. error_msg: Сообщение об ошибке. """ for _ in expected_users: results["incorrectly_filled"] += 1 results["field_errors"].append(error_msg) # Действия с кнопками def click_close_button(self) -> None: """Закрывает окно через кнопку 'Отменить'.""" self.page.mouse.click(10, 10) self.wait_for_timeout(300) cancel_button = self.get_button_by_name("cancel") if cancel_button: cancel_button.click() logger.debug("Clicked close button") def click_remove_button(self) -> None: """Удаляет стойку с подтверждением.""" delete_button = self.get_button_by_name("delete") if not delete_button: return delete_button.click() logger.debug("Clicked remove button") self.wait_for_timeout(1500) self._confirm_deletion() def _confirm_deletion(self) -> None: """Подтверждает удаление стойки.""" expected_title = "Удаление" try: self.delete_confirm.check_title( title=expected_title, msg=f"Expected title: '{expected_title}'" ) except AssertionError as e: logger.warning(f"Dialog title mismatch: {e}") expected_message = f"Удалить {self.window_title}?" try: self.delete_confirm.check_text( text=expected_message, msg=f"Expected message: '{expected_message}'" ) except AssertionError as e: logger.warning(f"Message text mismatch: {e}") self.delete_confirm.should_be_cancel_button() self.delete_confirm.should_be_allow_button() self.delete_confirm.click_allow_button() self.wait_for_timeout(2000) logger.debug("Remove confirmation completed") def click_done_button(self) -> None: """Сохраняет изменения стойки.""" save_button = self.get_button_by_name("save") if save_button: save_button.click() logger.debug("Clicked done button") # Проверки def verify_all_filled_fields( self, rack_data: EditRackData, skip_fields: Optional[List[str]] = None ) -> dict: """Проверяет, что все поля заполнены корректно. Args: rack_data: Данные для проверки. skip_fields: Список полей, которые нужно пропустить. Returns: Словарь с результатами проверки. """ if self.active_tab != self.TAB_GENERAL: self.switch_to_tab(self.TAB_GENERAL) results = { "total_expected_fields": 0, "correctly_filled": 0, "incorrectly_filled": 0, "not_filled": 0, "skipped_fields": 0, "field_errors": [] } if skip_fields is None: skip_fields = [] if not self.edit_form: logger.error("Edit form not initialized") results["field_errors"].append("Edit form not initialized") return results # Проверяем текстовые поля self._verify_text_fields(rack_data, skip_fields, results) # Проверяем combobox поля self._verify_combobox_fields(rack_data, skip_fields, results) # Проверяем чекбокс self._verify_checkbox(rack_data, skip_fields, results) if results["total_expected_fields"] > 0: success_rate = results["correctly_filled"] / results["total_expected_fields"] * 100 logger.info( f"Field check: {results['correctly_filled']}/" f"{results['total_expected_fields']} ({success_rate:.1f}%)" ) return results def _verify_text_fields( self, rack_data: EditRackData, skip_fields: List[str], results: dict ) -> None: """Проверяет текстовые поля. Args: rack_data: Данные для проверки. skip_fields: Список полей для пропуска. results: Словарь с результатами для обновления. """ for field_label, (attr_name, field_name) in self.edit_form.TEXT_FIELDS_MAPPING.items(): expected_value = getattr(rack_data, attr_name, "") if not expected_value or not str(expected_value).strip(): continue results["total_expected_fields"] += 1 if field_label in skip_fields: results["skipped_fields"] += 1 continue self._verify_single_text_field(field_label, field_name, expected_value, results) def _verify_single_text_field( self, field_label: str, field_name: str, expected_value: str, results: dict ) -> None: """Проверяет одно текстовое поле. Args: field_label: Метка поля. field_name: Имя поля. expected_value: Ожидаемое значение. results: Словарь с результатами. """ try: input_field = self.edit_form.get_content_item(field_name) if not input_field: results["not_filled"] += 1 results["field_errors"].append(f"Field '{field_label}' input not found") return actual_value = input_field.get_input_value() if actual_value == expected_value: results["correctly_filled"] += 1 else: results["incorrectly_filled"] += 1 results["field_errors"].append( f"Field '{field_label}': expected '{expected_value}', got '{actual_value}'" ) except Exception as e: results["not_filled"] += 1 results["field_errors"].append(f"Error checking field '{field_label}': {str(e)}") def _verify_combobox_fields( self, rack_data: EditRackData, skip_fields: List[str], results: dict ) -> None: """Проверяет combobox поля. Args: rack_data: Данные для проверки. skip_fields: Список полей для пропуска. results: Словарь с результатами для обновления. """ for field_label, (attr_name, _, _) in self.edit_form.COMBOBOX_FIELDS_MAPPING.items(): expected_value = getattr(rack_data, attr_name, "") if not expected_value or not str(expected_value).strip(): continue results["total_expected_fields"] += 1 if field_label in skip_fields: results["skipped_fields"] += 1 continue self._verify_single_combobox_field(field_label, expected_value, results) def _verify_single_combobox_field( self, field_label: str, expected_value: str, results: dict ) -> None: """Проверяет одно combobox поле. Args: field_label: Метка поля. expected_value: Ожидаемое значение. results: Словарь с результатами. """ try: actual_value = self.edit_form.get_field_value(field_label) or "" actual_clean = actual_value.strip() expected_clean = expected_value.strip() if actual_clean == expected_clean: results["correctly_filled"] += 1 else: results["incorrectly_filled"] += 1 results["field_errors"].append( f"Combobox '{field_label}': expected '{expected_clean}', got '{actual_clean}'" ) except Exception as e: results["not_filled"] += 1 results["field_errors"].append(f"Error checking combobox '{field_label}': {e}") def _verify_checkbox( self, rack_data: EditRackData, skip_fields: List[str], results: dict ) -> None: """Проверяет чекбокс. Args: rack_data: Данные для проверки. skip_fields: Список полей для пропуска. results: Словарь с результатами для обновления. """ if rack_data.ventilation_panel is None: return results["total_expected_fields"] += 1 if "Вентиляционная панель" in skip_fields: results["skipped_fields"] += 1 return try: checkbox = self.edit_form.get_content_item("ventilation_checkbox") if not checkbox: results["not_filled"] += 1 results["field_errors"].append("Checkbox 'Ventilation panel' not found") return checkbox_state = checkbox.is_checked() if checkbox_state == rack_data.ventilation_panel: results["correctly_filled"] += 1 else: results["incorrectly_filled"] += 1 expected_status = "enabled" if rack_data.ventilation_panel else "disabled" actual_status = "enabled" if checkbox_state else "disabled" results["field_errors"].append( f"Checkbox 'Ventilation panel': expected '{expected_status}', " f"got '{actual_status}'" ) except Exception as e: results["not_filled"] += 1 results["field_errors"].append(f"Error checking checkbox: {str(e)}")