"""Тест создания дочернего элемента 'Стойка'.""" import pytest from playwright.sync_api import Page from tools.logger import get_logger from locators.navigation_panel_locators import NavigationPanelLocators from frames.create_element_frame import CreateElementFrame from forms.create_rack_form import CreateRackForm, CreateRackData from pages.location_page import LocationPage from makers.edit_rack_maker import EditRackMaker from pages.login_page import LoginPage from pages.main_page import MainPage from pages.rack_page import RackPage from components.alert_component import AlertComponent logger = get_logger("CREATE_RACK_TEST") logger.setLevel("INFO") class TestCreateRack: """Тест создания дочернего элемента типа 'Стойка'.""" # Единое имя для тестовой стойки TEST_RACK_NAME = "Test-Rack-Create" # Для теста с дубликатом используем отдельное имя DUPLICATE_RACK_NAME = "Test-Rack-Duplicate" # Инициализируем атрибуты main_page: MainPage = None location_page: LocationPage = None alert: AlertComponent = None create_child_frame: CreateElementFrame = None rack_form: CreateRackForm = None @pytest.fixture(scope="function", autouse=True) def setup(self, browser: Page) -> None: """Фикстура для подготовки тестового окружения. Args: browser: Экземпляр страницы Playwright для взаимодействия с UI """ # Авторизация в системе login_page = LoginPage(browser) login_page.do_login() # Мы на главной странице self.main_page = MainPage(browser) self.main_page.should_be_navigation_panel() # Переходим к Объектам self.main_page.click_main_navigation_panel_item("Объекты") self.main_page.wait_for_timeout(2000) self.main_page.click_main_navigation_panel_item("test-zone") # Создаем экземпляр страницы локации self.location_page = LocationPage(browser) # Инициализируем компонент алертов self.alert = AlertComponent(browser) # Инициализируем фрейм создания дочернего элемента self.create_child_frame = CreateElementFrame(browser) # Инициализируем форму создания Стойки self.rack_form = CreateRackForm(browser) @pytest.fixture def cleanup_racks(self, browser: Page): """Фикстура для очистки созданных стоек.""" created_racks = [] yield created_racks # После завершения теста удаляем созданные стойки if created_racks: logger.debug(f"Cleaning up racks: {created_racks}") self.main_page.wait_for_timeout(500) self.main_page.click_subpanel_item("test-zone") self.main_page.wait_for_timeout(1000) for rack_name in created_racks: if self._check_rack_existance(browser, rack_name): logger.debug(f"Deleting rack '{rack_name}'...") self.main_page.click_subpanel_item(rack_name, parent="test-zone") self.main_page.wait_for_timeout(1000) self._delete_rack(browser, rack_name) self.main_page.click_subpanel_item("test-zone") self.main_page.wait_for_timeout(500) def _create_rack(self, browser: Page, rack_data: CreateRackData) -> None: """Создает стойку с использованием унифицированного подхода. Args: browser: Страница Playwright rack_data: Данные стойки для создания """ logger.debug(f"Creating rack with name '{rack_data.name}'") # Нажимаем кнопку "Создать" на тулбаре self.location_page.click_create_button() # Нажимаем на плашку "Класс объекта учета" self.create_child_frame.open_object_class_combobox() # Из выпадающего меню выбираем пункт "Стойка" self.create_child_frame.select_object_class("Стойка") # Создаем форму создания стойки rack_form = CreateRackForm(browser) # Заполняем данные стойки fill_results = rack_form.fill_rack_data(rack_data) logger.debug(f"Fill results: {fill_results}") # Нажимаем кнопку создания self.create_child_frame.click_add_button() # Ждем появления alert с текстом expected_alert_text = f"Элемент {rack_data.name} создан" self.alert.check_alert_presence(expected_alert_text, timeout=7000) self.alert.check_alert_absence(expected_alert_text, timeout=7000) # Закрываем alert с текстом кнопкой 'Закрыть' try: self.alert.close_alert_by_text(expected_alert_text) logger.debug("Alert forcibly closed") except AssertionError: # Если уже закрылся - игнорируем logger.debug("Alert already closed by the time forcible close was attempted") logger.info(f"Rack '{rack_data.name}' created successfully") def _delete_rack(self, browser: Page, rack_name: str) -> None: """Удаляет стойку через контекстное меню. Args: browser: Страница Playwright rack_name: Имя стойки для удаления """ # Находим элемент стойки в навигационной панели rack_element = browser.locator( NavigationPanelLocators.TREEVIEW ).get_by_text(rack_name, exact=True).first # Прокручиваем до элемента если нужно rack_element.scroll_into_view_if_needed() self.main_page.wait_for_timeout(500) # Проверяем и нажимаем кнопку "Изменить" rack_page = RackPage(browser) # Проверяем видимость и тултип кнопки rack_page.should_be_toolbar_buttons() # Кликаем на кнопку "Изменить" rack_page.click_edit_button() self.main_page.wait_for_timeout(1000) # Создаем экземпляр EditRackMaker rack_edit = EditRackMaker(browser, rack_name) # Используем метод для удаления rack_edit.click_remove_button() # Проверяем уведомление об успешном удалении expected_alert_text = "Успешно удалено" self.alert.check_alert_presence(expected_alert_text, timeout=7000) self.alert.check_alert_absence(expected_alert_text, timeout=7000) # Закрываем alert с текстом кнопкой 'Закрыть' try: self.alert.close_alert_by_text(expected_alert_text) logger.debug("Alert forcibly closed") except AssertionError: # Если уже закрылся - игнорируем logger.debug("Alert already closed by the time forcible close was attempted") logger.info(f"Rack '{rack_name}' deleted successfully") def _check_rack_existance(self, browser: Page, rack_name: str) -> bool: """Проверяет существование стойки. Args: browser: Страница Playwright rack_name: Имя стойки для проверки Returns: bool: True если стойка существует, False в противном случае """ logger.debug(f"Checking existence of rack with name '{rack_name}'") self.main_page.click_subpanel_item("test-zone") nav_panel_locator = NavigationPanelLocators.TREEVIEW element = browser.locator(nav_panel_locator).get_by_text(rack_name, exact=True).first if element.is_visible(): logger.debug(f"Rack with name '{rack_name}' found") return True logger.debug(f"Rack with name '{rack_name}' not found") return False def test_create_rack_content(self, browser: Page) -> None: """Тест проверки содержимого формы создания стойки.""" # Проверяем что кнопка "Создать" доступна self.location_page.should_be_toolbar_buttons() # Нажимаем кнопку "Создать" на тулбаре self.location_page.click_create_button() # Проверяем заголовок формы создания self.create_child_frame.check_toolbar_title('Создать дочерний элемент в') # Нажимаем на плашку "Класс объекта учета" self.create_child_frame.open_object_class_combobox() # Из выпадающего меню выбираем пункт "Стойка" self.create_child_frame.select_object_class("Стойка") # Создаем форму создания стойки и проверяем наличие полей rack_form = CreateRackForm(browser) # Проверяем, что основные поля присутствуют assert rack_form.get_content_item("name_input") is not None, "Name field not initialized" assert rack_form.get_content_item("usize_input") is not None, "Height field not initialized" assert rack_form.get_content_item("depth_input") is not None, "Depth field not initialized" logger.debug("Rack-specific fields are displayed correctly") self.create_child_frame.should_be_toolbar_buttons() def test_create_rack(self, browser: Page, cleanup_racks) -> None: """Тест создания дочернего элемента типа 'Стойка'.""" logger.debug(f"Starting test with rack name: {self.TEST_RACK_NAME}") # Создаем данные стойки с расширенным набором полей rack_data = CreateRackData( name=self.TEST_RACK_NAME, usize="42", depth="1000", serial="TEST123456", inventory="INV-001", comment="Тестовая стойка для автоматизации", cable_entry="сверху", state="Введен в эксплуатацию", # owner="Владелец", # service_org="Обслуживающая организация", # project="Проект/Титул" ) # Сохраняем имя стойки для очистки cleanup_racks.append(rack_data.name) # Проверяем, существует ли уже стойка с таким именем if self._check_rack_existance(browser, rack_data.name): logger.warning(f"Rack '{rack_data.name}' already exists. Deleting it before creating new one...") # Переходим к стойке для удаления self.main_page.click_subpanel_item(rack_data.name, parent="test-zone") self.main_page.wait_for_timeout(1000) # Удаляем существующую стойку self._delete_rack(browser, rack_data.name) logger.debug(f"Existing rack '{rack_data.name}' deleted successfully") # Создаем новую стойку self._create_rack(browser, rack_data) # Проверяем, что стойка создана и отображается logger.debug(f"Verifying that rack '{rack_data.name}' was created...") self.main_page.click_main_navigation_panel_item("test-zone") rack_exists = self._check_rack_existance(browser, rack_data.name) assert rack_exists, f"Rack '{rack_data.name}' should be visible in navigation panel after creation" logger.debug(f"Rack '{rack_data.name}' is visible in navigation panel") logger.debug("Test for creating 'Rack' child element completed successfully") def test_create_rack_with_duplicate_name(self, browser: Page, cleanup_racks) -> None: """Тест создания стойки с уже существующим именем.""" logger.debug(f"Starting test for creating rack with duplicate name: {self.DUPLICATE_RACK_NAME}") rack_name = self.DUPLICATE_RACK_NAME # Создаем первую стойку если её нет if not self._check_rack_existance(browser, rack_name): logger.debug(f"Creating first rack with name '{rack_name}'") first_rack_data = CreateRackData( name=rack_name, usize="42", depth="1000" ) self._create_rack(browser, first_rack_data) cleanup_racks.append(rack_name) # Пытаемся создать вторую стойку с тем же именем logger.debug(f"Attempting to create second rack with name '{rack_name}'") self.location_page.click_create_button() # Нажимаем на плашку "Класс объекта учета" self.create_child_frame.open_object_class_combobox() # Из выпадающего меню выбираем пункт "Стойка" self.create_child_frame.select_object_class("Стойка") rack_form = CreateRackForm(browser) duplicate_rack_data = CreateRackData( name=rack_name, usize="42", depth="450" ) self.create_child_frame.check_toolbar_title('Создать дочерний элемент в') rack_form.fill_rack_data(duplicate_rack_data) self.create_child_frame.click_add_button() expected_alert_text = f"Имя {rack_name} уже используется" self.alert.check_alert_presence(expected_alert_text, timeout=7000) self.alert.check_alert_absence(expected_alert_text, timeout=7000) # Закрываем alert с текстом кнопкой 'Закрыть' try: self.alert.close_alert_by_text(expected_alert_text) logger.debug("Alert forcibly closed") except AssertionError: # Если уже закрылся - игнорируем logger.debug("Alert already closed by the time forcible close was attempted") logger.debug("System prevented creating rack with duplicate name") def test_required_fields_validation(self, browser: Page, cleanup_racks) -> None: """Тест проверки обязательных полей при создании стойки.""" logger.debug("Starting required fields validation test") expected_alert_text_height = "поле Высота в юнитах должно быть заполнено" expected_alert_text_depth = "поле Глубина (мм) должно быть заполнено" expected_alert_text_name = "Поле Имя должно быть установлено" self.main_page.click_main_navigation_panel_item("test-zone") # Открываем форму создания self.location_page.click_create_button() # Нажимаем на плашку "Класс объекта учета" self.create_child_frame.open_object_class_combobox() # Из выпадающего меню выбираем пункт "Стойка" self.create_child_frame.select_object_class("Стойка") rack_form = CreateRackForm(browser) # ========== Тест 1: Обязательные поля высота и глубина пустые ========== logger.debug("Test 1: Both required fields (height, depth) are empty") # Очищаем поля высоты и глубины перед заполнением rack_form.clear_field("Высота в юнитах") rack_form.clear_field("Глубина (мм)") test_data_1 = CreateRackData( name=self.TEST_RACK_NAME, usize="", depth="" ) rack_form.fill_rack_data(test_data_1) self.create_child_frame.click_add_button() self.create_child_frame.wait_for_timeout(500) # Проверяем alert для высоты, глубины self.alert.check_alert_presence(expected_alert_text_height, timeout=7000) self.alert.check_alert_presence(expected_alert_text_depth, timeout=7000) # Проверяем, закрылся ли автоматически alert для высоты, глубины self.alert.check_alert_absence(expected_alert_text_height, timeout=7000) self.alert.check_alert_absence(expected_alert_text_depth, timeout=500) # Проверяем подсветку полей field_status = rack_form.verify_required_fields_highlighted(["Высота в юнитах", "Глубина (мм)"]) logger.debug(f"Field status after test 1: {field_status}") assert field_status.get("Высота в юнитах"), f"Height field should be highlighted, got: {field_status}" assert field_status.get("Глубина (мм)"), f"Depth field should be highlighted, got: {field_status}" # ========== Тест 2: Только высота заполнена ========== logger.debug("Test 2: Only height field is filled") # Очищаем поле глубины перед новым заполнением rack_form.clear_field("Глубина (мм)") test_data_2 = CreateRackData( name=self.TEST_RACK_NAME, usize="42", depth="" ) rack_form.fill_rack_data(test_data_2) self.create_child_frame.click_add_button() # Проверяем alert для глубины self.alert.check_alert_presence(expected_alert_text_depth, timeout=7000) # Проверяем, закрылся ли автоматически alert для глубины self.alert.check_alert_absence(expected_alert_text_depth, timeout=7000) # Проверяем подсветку полей field_status = rack_form.verify_required_fields_highlighted(["Глубина (мм)"]) logger.debug(f"Field status after test 2: {field_status}") assert field_status.get("Глубина (мм)"), f"Depth field should be highlighted, got: {field_status}" # ========== Тест 3: Только глубина заполнена ========== logger.debug("Test 3: Only depth field is filled") # Очищаем поле высоты перед новым заполнением rack_form.clear_field("Высота в юнитах") test_data_3 = CreateRackData( name=self.TEST_RACK_NAME, usize="", depth="1000" ) rack_form.fill_rack_data(test_data_3) self.create_child_frame.click_add_button() # Проверяем alert для высоты self.alert.check_alert_presence(expected_alert_text_height, timeout=7000) # Проверяем, закрылся ли автоматически alert для высоты self.alert.check_alert_absence(expected_alert_text_height, timeout=7000) # Проверяем подсветку полей field_status = rack_form.verify_required_fields_highlighted(["Высота в юнитах"]) logger.debug(f"Field status after test 3: {field_status}") assert field_status.get("Высота в юнитах"), f"Height field should be highlighted, got: {field_status}" # ========== Тест 4: Поле "Имя" не заполнено ========== logger.debug("Test 4: Name field is empty") # Очищаем поле имени rack_form.clear_field("Имя") test_data_4 = CreateRackData( name="", usize="42", depth="1000" ) rack_form.fill_rack_data(test_data_4) self.create_child_frame.click_add_button() self.create_child_frame.wait_for_timeout(500) # Проверяем alert для имени self.alert.check_alert_presence(expected_alert_text_name, timeout=7000) # Проверяем, закрылся ли автоматически alert для высоты self.alert.check_alert_absence(expected_alert_text_name, timeout=7000) logger.debug("Test 4 completed: System correctly validates empty name field")