diff --git a/components/toolbar_component.py b/components/toolbar_component.py index 10cc257..862366b 100644 --- a/components/toolbar_component.py +++ b/components/toolbar_component.py @@ -27,6 +27,7 @@ class ToolbarComponent(BaseComponent): def __init__(self, page: Page, title: str) -> None: """Инициализирует компонент тулбара с указанным заголовком.""" + super().__init__(page) self.title = title self.buttons = [] @@ -38,6 +39,7 @@ class ToolbarComponent(BaseComponent): Args: title (str): Новый заголовок """ + self.title = title def add_tooltip_button(self, locator: Locator, name: str) -> None: @@ -47,6 +49,7 @@ class ToolbarComponent(BaseComponent): locator (Locator): Локатор кнопки name (str): Уникальное имя кнопки """ + self.buttons.append(TooltipButton(self.page, locator, name)) def add_tab_button(self, locator: Locator, name: str) -> None: @@ -56,6 +59,7 @@ class ToolbarComponent(BaseComponent): locator (Locator): Локатор кнопки name (str): Уникальное имя кнопки """ + self.buttons.append(TabButton(self.page, locator, name)) def add_button(self, locator: Locator, name: str) -> None: @@ -65,6 +69,7 @@ class ToolbarComponent(BaseComponent): locator (Locator): Локатор кнопки name (str): Уникальное имя кнопки """ + self.buttons.append(Button(self.page, locator, name)) def get_button_by_name(self, name: str @@ -77,6 +82,7 @@ class ToolbarComponent(BaseComponent): Returns: TooltipButton | TabButton | Button | None: Найденная кнопка или None """ + for button in self.buttons: if button.name == name: return button @@ -91,6 +97,7 @@ class ToolbarComponent(BaseComponent): Raises: AssertionError: Если кнопка не найдена """ + button = self.get_button_by_name(name) if button is None: raise AssertionError(f"Unsupported button name {name}") @@ -112,6 +119,7 @@ class ToolbarComponent(BaseComponent): Raises: Exception: Если не удалось получить заголовок """ + # Получаем локатор заголовка title_locator = self.get_locator(locator) @@ -134,7 +142,6 @@ class ToolbarComponent(BaseComponent): Args: locator: Локатор для заголовка тулбара - separator: Разделитель подзаголовков, по умолчанию галочка вправо (chevron_right) timeout: Таймаут ожидания в миллисекундах Returns: @@ -176,6 +183,7 @@ class ToolbarComponent(BaseComponent): Raises: AssertionError: Если имя кнопки не поддерживается """ + button = self.get_button_by_name(name) if button is None: raise AssertionError(f"Unsupported button name {name}") @@ -193,6 +201,7 @@ class ToolbarComponent(BaseComponent): Raises: AssertionError: Если имя кнопки не поддерживается """ + button = self.get_button_by_name(name) if button is None: raise AssertionError(f"Unsupported button name {name}") @@ -204,6 +213,7 @@ class ToolbarComponent(BaseComponent): Args: message (str): Сообщение об ошибке если тулбар не виден """ + locator = self.get_locator(ToolbarLocators.TITLE).filter( has_text=self.title ) @@ -217,6 +227,7 @@ class ToolbarComponent(BaseComponent): locator: Локатор тулбара message (str): Сообщение об ошибке если тулбар не виден """ + locator = self.get_locator(locator) expect(locator).to_be_visible(), message @@ -228,6 +239,7 @@ class ToolbarComponent(BaseComponent): locator: Локатор тулбара message (str): Сообщение об ошибке если тулбар не виден """ + locator = self.get_locator(locator).filter(has_text=self.title) expect(locator).to_be_visible(), message @@ -240,6 +252,7 @@ class ToolbarComponent(BaseComponent): Raises: AssertionError: Если кнопка не найдена или не видна """ + button = self.get_button_by_name(name) if button is None: @@ -258,6 +271,7 @@ class ToolbarComponent(BaseComponent): Raises: AssertionError: Если текст подсказки не совпадает """ + button = self.get_button_by_name(name) if button is None: raise AssertionError(f"Unsupported button name {name}") diff --git a/pages/rack_page.py b/pages/rack_page.py index 3489663..536c247 100644 --- a/pages/rack_page.py +++ b/pages/rack_page.py @@ -6,6 +6,7 @@ """ from playwright.sync_api import Page, expect +from typing import Optional from tools.logger import get_logger from locators.rack_locators import RackLocators from elements.tooltip_button_element import TooltipButton @@ -15,6 +16,8 @@ from pages.base_page import BasePage logger = get_logger("RACK_PAGE") +logger.setLevel("INFO") + # Специфичные локаторы оставленые в основном коде PANEL_HEADER = "//span[text()='Объекты']/following-sibling::i" TOOLBAR_CONTENT = "//div[@class='v-toolbar__content']" @@ -53,26 +56,6 @@ class RackPage(BasePage): # Действия - def check_physical_devices_presence(self) -> None: - """Проверяет наличие физических устройств на стойке.""" - - # Поиск устройств по классу parent-class - devices = self.page.locator(RackLocators.DEVICE_ELEMENTS) - device_count = devices.count() - - if device_count > 0: - # Выводим информацию только о первом устройстве - first_device = devices.first - device_id = first_device.get_attribute("id") or "No id" - device_title = first_device.get_attribute("title") or "No title" - - logger.info( - f"Devices found: {device_count} " - f"(first: ID={device_id}, Title={device_title})" - ) - else: - logger.info("No devices detected") - def get_available_tabs(self) -> list[str]: """ Возвращает список доступных вкладок. @@ -90,7 +73,7 @@ class RackPage(BasePage): tab_elements.first.wait_for(state="visible", timeout=5000) total_count = tab_elements.count() - logger.info(f"Total top tab elements found: {total_count}") + logger.debug(f"Total top tab elements found: {total_count}") for i in range(total_count): element = tab_elements.nth(i) @@ -102,15 +85,17 @@ class RackPage(BasePage): tab_text = tab_text.strip() if tab_text and tab_text not in tabs: tabs.append(tab_text) - logger.info(f"Top tab found: '{tab_text}'") + logger.debug(f"Top tab found: '{tab_text}'") - logger.info(f"Available top tabs found: {tabs}") + logger.debug(f"Available top tabs found: {tabs}") return tabs - def get_current_active_side(self) -> str: + def get_current_active_side(self) -> Optional[str]: """ Возвращает текущую активную сторону стойки. - Проверяет конкретные кнопки стойки напрямую. + + Returns: + Optional[str]: "Лицевая сторона", "Обратная сторона" или None если ни одна не активна """ # Проверяем конкретно кнопку "Лицевая сторона" @@ -127,7 +112,7 @@ class RackPage(BasePage): if "primary--text" in classes.split(): return "Обратная сторона" - return "" + return None def get_toolbar_title(self) -> list[str]: """ @@ -180,7 +165,7 @@ class RackPage(BasePage): AssertionError: Если вкладка не найдена или недоступна """ - logger.info(f"Switching to tab '{tab_name}'...") + logger.debug(f"Switching to tab '{tab_name}'...") tab = self.page.locator(RackLocators.TAB_BY_NAME.format(tab_name)) @@ -188,7 +173,7 @@ class RackPage(BasePage): # Проверяем активность ДО клика if self.is_tab_active(tab_name): - logger.info(f"Tab '{tab_name}' is already active") + logger.debug(f"Tab '{tab_name}' is already active") return # Находим первую видимую вкладку с нужным именем @@ -202,7 +187,7 @@ class RackPage(BasePage): assert target_tab is not None, f"No visible/available tab '{tab_name}' found" # Кликаем на вкладку - logger.info(f"Clicking on tab '{tab_name}'...") + logger.debug(f"Clicking on tab '{tab_name}'...") target_tab.click() # Ждем изменения активной вкладки @@ -214,7 +199,7 @@ class RackPage(BasePage): def wait_for_rack_loading(self) -> None: """Ожидает загрузки интерфейса стойки.""" - logger.info("Waiting for rack interface to load...") + logger.debug("Waiting for rack interface to load...") # Проверяем наличие кнопок переключения сторон front_button = self.page.locator(RackLocators.FRONT_SIDE_BUTTON) @@ -222,12 +207,12 @@ class RackPage(BasePage): expect(front_button).to_be_visible(timeout=5000) expect(back_button).to_be_visible(timeout=5000) - logger.info("Side toggle buttons loaded") + logger.debug("Side toggle buttons loaded") # Проверяем, что есть активная кнопка active_button = self.page.locator(RackLocators.ACTIVE_SIDE_BUTTON) if active_button.count() > 0: - logger.info("Active side button found") + logger.debug("Active side button found") else: logger.warning("No active side button found") @@ -236,32 +221,52 @@ class RackPage(BasePage): if main_container.count() == 0: logger.warning("Main rack container not found") else: - logger.info(f"Main rack container found (count: {main_container.count()})") + logger.debug(f"Main rack container found (count: {main_container.count()})") expect(main_container.first).to_be_attached() # Проверяем наличие позиций юнитов unit_positions = self.page.locator(RackLocators.UNIT_POSITIONS) if unit_positions.count() > 0: - logger.info(f"Unit positions found: {unit_positions.count()}") + logger.debug(f"Unit positions found: {unit_positions.count()}") if unit_positions.first.text_content(): content = unit_positions.first.text_content().strip() - logger.info(f"First position: {content}") + logger.debug(f"First position: {content}") # Проверяем наличие кнопок добавления open_buttons = self.page.locator(RackLocators.ADD_CIRCLE_BUTTON) if open_buttons.count() > 0: - logger.info(f"'add_circle' buttons found: {open_buttons.count()}") + logger.debug(f"'add_circle' buttons found: {open_buttons.count()}") - logger.info("Rack interface loaded") + logger.debug("Rack interface loaded") # Проверки + def check_physical_devices_presence(self) -> None: + """Проверяет наличие физических устройств на стойке.""" + + # Поиск устройств по классу parent-class + devices = self.page.locator(RackLocators.DEVICE_ELEMENTS) + device_count = devices.count() + + if device_count > 0: + # Выводим информацию только о первом устройстве + first_device = devices.first + device_id = first_device.get_attribute("id") or "No id" + device_title = first_device.get_attribute("title") or "No title" + + logger.debug( + f"Devices found: {device_count} " + f"(first: ID={device_id}, Title={device_title})" + ) + else: + logger.debug("No devices detected") + def check_tab_switching(self) -> None: """ Проверяет переключение между вкладками стойки. """ - logger.info("Testing rack tab switching functionality...") + logger.debug("Testing rack tab switching functionality...") # Вкладки в правильном порядке defined_tabs = [ @@ -272,14 +277,14 @@ class RackPage(BasePage): "Сервисы" ] - logger.info(f"Defined tabs to test: {defined_tabs}") + logger.debug(f"Defined tabs to test: {defined_tabs}") successful_switches = 0 failed_switches = [] # Тестируем переключение на каждую определенную вкладку for tab_name in defined_tabs: - logger.info(f"Testing switch to tab '{tab_name}'...") + logger.debug(f"Testing switch to tab '{tab_name}'...") try: # Переключаемся на вкладку @@ -287,7 +292,7 @@ class RackPage(BasePage): # Проверяем, что вкладка активна if self.is_tab_active(tab_name): - logger.info(f"Successfully switched to tab '{tab_name}'") + logger.debug(f"Successfully switched to tab '{tab_name}'") successful_switches += 1 else: logger.warning(f"Tab '{tab_name}' not active after switching") @@ -301,13 +306,13 @@ class RackPage(BasePage): self.wait_for_timeout(1000) # Формируем итоговый отчет - logger.info("=== TAB SWITCHING RESULTS ===") - logger.info(f"Successful switches: {successful_switches}/{len(defined_tabs)}") + logger.debug("=== TAB SWITCHING RESULTS ===") + logger.debug(f"Successful switches: {successful_switches}/{len(defined_tabs)}") if failed_switches: - logger.info("Failed switches:") + logger.debug("Failed switches:") for failure in failed_switches: - logger.info(f" - {failure}") + logger.debug(f" - {failure}") # Требуем успешного переключения на все определенные вкладки assert successful_switches == len(defined_tabs), ( @@ -317,7 +322,7 @@ class RackPage(BasePage): f"Errors: {', '.join(failed_switches)}" ) - logger.info(f"All {successful_switches} defined tabs successfully switched!") + logger.debug(f"All {successful_switches} defined tabs successfully switched!") def is_tab_active(self, tab_name: str) -> bool: """ @@ -336,13 +341,13 @@ class RackPage(BasePage): if active_tab.count() > 0 and active_tab.first.is_visible(): active_text = active_tab.first.text_content() if active_text and active_text.strip() == tab_name: - logger.info(f"Tab '{tab_name}' is active (via active tab class)") + logger.debug(f"Tab '{tab_name}' is active (via active tab class)") return True - logger.info(f"Tab '{tab_name}' is not active") + logger.debug(f"Tab '{tab_name}' is not active") return False - def should_be_header_panel(self, expected_toolbar_title_items: list[str]) -> None: + def should_be_panel_header(self, expected_toolbar_title_items: list[str]) -> None: """ Проверяет наличие и корректность заголовка панели. @@ -372,7 +377,7 @@ class RackPage(BasePage): def should_be_rack_sides_displayed(self) -> None: """Проверка отображения и структуры сторон стойки.""" - logger.info("Checking rack sides display and structure...") + logger.debug("Checking rack sides display and structure...") # Ожидаем загрузки self.wait_for_rack_loading() @@ -388,11 +393,11 @@ class RackPage(BasePage): expect(back_side_button).to_be_visible(timeout=5000), \ "Back side button not found" - logger.info("Side toggle buttons found") + logger.debug("Side toggle buttons found") # Проверяем, какая сторона активна по умолчанию current_active = self.get_current_active_side() - logger.info(f"Current active side: {current_active}") + logger.debug(f"Current active side: {current_active}") # Дополнительная проверка устройств self.check_physical_devices_presence() @@ -405,7 +410,7 @@ class RackPage(BasePage): # Возвращаемся на исходную активную сторону if current_active: - logger.info(f"Returning to original active side: {current_active}") + logger.debug(f"Returning to original active side: {current_active}") if current_active == "Лицевая сторона": front_side_button.click() else: @@ -413,9 +418,9 @@ class RackPage(BasePage): self.wait_for_timeout(1000) final_active = self.get_current_active_side() - logger.info(f"Final active side: {final_active}") + logger.debug(f"Final active side: {final_active}") - logger.info("All rack sides checks passed successfully") + logger.debug("All rack sides checks passed successfully") def should_be_toolbar_buttons(self) -> None: """ @@ -425,7 +430,7 @@ class RackPage(BasePage): AssertionError: Если кнопки недоступны или подсказки неверны. """ - logger.info("Checking toolbar buttons...") + logger.debug("Checking toolbar buttons...") self.toolbar.check_button_visibility("edit") self.toolbar.check_button_tooltip("edit", "Изменить") @@ -436,7 +441,7 @@ class RackPage(BasePage): Проверка кнопки "Скрыть стойку". Проверяет видимость, тултип, кликабельность и эффект скрытия стойки. """ - logger.info("Checking 'Hide rack' button...") + logger.debug("Checking 'Hide rack' button...") # Проверяем видимость кнопки self.toolbar.check_button_visibility("hide_rack") @@ -450,20 +455,20 @@ class RackPage(BasePage): # Кликаем на кнопку "Скрыть стойку" self.toolbar.get_button_by_name("hide_rack").click() - self.wait_for_timeout(2000) # Даем время для применения стилей + self.wait_for_timeout(2000) # Проверяем, что общий контейнер стойки теперь скрыт (имеет display: none) expect(rack_container).to_have_css("display", "none", timeout=5000) - logger.info("Rack container successfully hidden (display: none)") + logger.debug("Rack container successfully hidden (display: none)") - logger.info("'Hide rack' button test completed successfully") + logger.debug("'Hide rack' button test completed successfully") def should_have_show_rack_button(self) -> None: """ Проверка кнопки "Показать стойку". Проверяет наличие, тултип, кликабельность и эффект показа стойки. """ - logger.info("Checking 'Show rack' button...") + logger.debug("Checking 'Show rack' button...") # Проверяем видимость кнопки self.toolbar.check_button_visibility("show_rack") @@ -481,9 +486,9 @@ class RackPage(BasePage): # Проверяем, что общий контейнер стойки теперь видим (display не равен "none") expect(rack_container).not_to_have_css("display", "none", timeout=5000) - logger.info("Rack container successfully shown (display is not 'none')") + logger.debug("Rack container successfully shown (display is not 'none')") - logger.info("'Show rack' button test completed successfully") + logger.debug("'Show rack' button test completed successfully") # Вспомогательные методы @@ -499,49 +504,49 @@ class RackPage(BasePage): AssertionError: Если структура стороны некорректна """ - logger.info(f"Checking {side_name}...") + logger.debug(f"Checking {side_name}...") # Логируем текущее состояние кнопки перед кликом button_classes = side_button.get_attribute("class") or "" - logger.info(f"Button classes before click: {button_classes}") + logger.debug(f"Button classes before click: {button_classes}") # Проверяем, активна ли уже эта сторона current_active = self.get_current_active_side() - logger.info(f"Current active side (before click): '{current_active}'") + logger.debug(f"Current active side (before click): '{current_active}'") if current_active == side_name: - logger.info(f"{side_name} is already active") + logger.debug(f"{side_name} is already active") else: # Если не активна, кликаем для переключения - logger.info(f"Switching to {side_name}...") + logger.debug(f"Switching to {side_name}...") side_button.click() # Даем время на перерисовку классов (увеличиваем время) self.wait_for_timeout(2500) # Проверяем классы после клика button_classes_after = side_button.get_attribute("class") or "" - logger.info(f"Button classes after click: {button_classes_after}") + logger.debug(f"Button classes after click: {button_classes_after}") # Проверяем, что нужная сторона стала активной active_side = self.get_current_active_side() - logger.info(f"Active side after switching: '{active_side}'") + logger.debug(f"Active side after switching: '{active_side}'") assert active_side == side_name, \ f"Wrong side is active: '{active_side}', expected: '{side_name}'" - logger.info(f"{side_name} successfully activated") + logger.debug(f"{side_name} successfully activated") # Проверяем позиции юнитов unit_positions = self.page.locator(RackLocators.UNIT_POSITIONS) total_positions = unit_positions.count() - logger.info(f"Total unit positions: {total_positions}") + logger.debug(f"Total unit positions: {total_positions}") assert total_positions > 0, f"No unit positions found on {side_name}" # Проверяем юниты all_units = self.page.locator(RackLocators.ALL_UNITS) all_units_count = all_units.count() units_per_side = all_units_count // 2 - logger.info(f"Units on {side_name}: {units_per_side}") + logger.debug(f"Units on {side_name}: {units_per_side}") # Проверяем устройства devices = self.page.locator(RackLocators.DEVICE_ELEMENTS) @@ -558,15 +563,15 @@ class RackPage(BasePage): slots = first_device.locator(RackLocators.DEVICE_SLOTS) slot_count = slots.count() - logger.info(f"Devices found: {device_count} (showing first)") - logger.info(f" Device: ID={device_id}") - logger.info(f" Title: {device_title}") - logger.info(f" Classes: {device_classes}") - logger.info(f" Slots: {slot_count}") + logger.debug(f"Devices found: {device_count} (showing first)") + logger.debug(f" Device: ID={device_id}") + logger.debug(f" Title: {device_title}") + logger.debug(f" Classes: {device_classes}") + logger.debug(f" Slots: {slot_count}") else: - logger.info("No devices detected") + logger.debug("No devices detected") - logger.info(f"{side_name} check completed successfully") + logger.debug(f"{side_name} check completed successfully") def _wait_for_tab_activation(self, tab_name: str, timeout: int = 5000) -> None: """ @@ -580,12 +585,12 @@ class RackPage(BasePage): AssertionError: Если вкладка не активирована в течение таймаута """ - logger.info(f"Waiting for tab '{tab_name}' activation...") + logger.debug(f"Waiting for tab '{tab_name}' activation...") start_time = self.page.evaluate("Date.now()") while self.page.evaluate("Date.now()") - start_time < timeout: if self.is_tab_active(tab_name): - logger.info(f"Tab '{tab_name}' successfully activated") + logger.debug(f"Tab '{tab_name}' successfully activated") return self.wait_for_timeout(100) diff --git a/tests/e2e/elements/test_element_rack.py b/tests/e2e/elements/test_element_rack.py index 29512df..e3c6770 100644 --- a/tests/e2e/elements/test_element_rack.py +++ b/tests/e2e/elements/test_element_rack.py @@ -78,7 +78,7 @@ class TestRackTab: ] rt = RackPage(browser) - rt.should_be_header_panel(expected_toolbar_subtitles) + rt.should_be_panel_header(expected_toolbar_subtitles) # Комплексная проверка отображения обеих сторон стойки с детальной информацией rt.should_be_rack_sides_displayed()