e-nms_qa_automation/pages/rack_page.py

586 lines
24 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""Модуль тестов вкладки 'Стойка'.
Содержит тесты для проверки функциональности
работы со стойкой оборудования.
"""
from playwright.sync_api import Page, expect
from tools.logger import get_logger
from locators.rack_locators import RackLocators
from elements.tooltip_button_element import TooltipButton
from components.toolbar_component import ToolbarComponent
from pages.base_page import BasePage
logger = get_logger("RACK_PAGE")
# Специфичные локаторы оставленые в основном коде
PANEL_HEADER = "//span[text()='Объекты']/following-sibling::i"
TOOLBAR_CONTENT = "//div[@class='v-toolbar__content']"
PANEL_HEADER_ANCESTOR_DIV2 = "xpath=/ancestor::div[2]"
class RackPage(BasePage):
"""Класс для работы с вкладкой стойки оборудования."""
def __init__(self, page: Page) -> None:
"""
Инициализирует объект вкладки стойки.
Args:
page: Экземпляр страницы Playwright
"""
super().__init__(page)
# Кнопка "Изменить"
locator_button = self.page.locator(RackLocators.EDIT_BUTTON)
self.edit_button = TooltipButton(page, locator_button, "edit")
# Кнопка "Скрыть стойку"
hide_button_locator = self.page.locator(RackLocators.HIDE_RACK_BUTTON)
self.hide_button = TooltipButton(page, hide_button_locator, "hide_rack")
# Кнопка "Показать стойку"
show_button_locator = self.page.locator(RackLocators.SHOW_RACK_BUTTON)
self.show_button = TooltipButton(page, show_button_locator, "show_rack")
self.toolbar = ToolbarComponent(page, "")
self.toolbar.add_tooltip_button(locator_button, "edit")
self.toolbar.add_tooltip_button(hide_button_locator, "hide_rack")
self.toolbar.add_tooltip_button(show_button_locator, "show_rack")
# Действия
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]:
"""
Возвращает список доступных вкладок.
Returns:
list[str]: Список названий доступных вкладок
"""
tabs = []
# Используем локатор для верхних вкладок
tab_elements = self.page.locator(RackLocators.ALL_TABS)
# Ждем появления элементов
tab_elements.first.wait_for(state="visible", timeout=5000)
total_count = tab_elements.count()
logger.info(f"Total top tab elements found: {total_count}")
for i in range(total_count):
element = tab_elements.nth(i)
# Проверяем видимость и доступность элемента
if element.is_visible() and element.is_enabled():
tab_text = element.text_content()
if tab_text:
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.info(f"Available top tabs found: {tabs}")
return tabs
def get_current_active_side(self) -> str:
"""
Возвращает текущую активную сторону стойки.
Returns:
str: Название активной стороны ('Лицевая сторона' или 'Обратная сторона')
"""
active_button = self.page.locator(RackLocators.ACTIVE_SIDE_BUTTON_TEXT)
if active_button.count() > 0:
text = active_button.first.text_content()
if text:
return text.strip()
return ""
def get_toolbar_title(self) -> list[str]:
"""
Получает заголовок панели инструментов.
Returns:
list[str]: Список элементов заголовка панели инструментов
"""
toolbar_title_locator = self.page.locator(PANEL_HEADER).\
locator(PANEL_HEADER_ANCESTOR_DIV2).\
get_by_role("navigation").\
locator(TOOLBAR_CONTENT)
return self.toolbar.get_toolbar_composite_title_text(toolbar_title_locator)
def switch_to_events_tab(self) -> None:
"""Переключается на вкладку 'События'."""
self.switch_to_tab("События")
def switch_to_general_info_tab(self) -> None:
"""Переключается на вкладку 'Общая информация'."""
self.switch_to_tab("Общая информация")
def switch_to_maintenance_tab(self) -> None:
"""Переключается на вкладку 'Обслуживание'."""
self.switch_to_tab("Обслуживание")
def switch_to_services_tab(self) -> None:
"""Переключается на вкладку 'Сервисы'."""
self.switch_to_tab("Сервисы")
def switch_to_tab(self, tab_name: str) -> None:
"""
Переключается на указанную вкладку.
Args:
tab_name: Название вкладки для переключения
Raises:
AssertionError: Если вкладка не найдена или недоступна
"""
logger.info(f"Switching to tab '{tab_name}'...")
tab = self.page.locator(RackLocators.TAB_BY_NAME.format(tab_name))
assert tab.count() > 0, f"Tab '{tab_name}' not found"
# Проверяем активность ДО клика
if self.is_tab_active(tab_name):
logger.info(f"Tab '{tab_name}' is already active")
return
# Находим первую видимую вкладку с нужным именем
target_tab = None
for i in range(tab.count()):
element = tab.nth(i)
if element.is_visible() and element.is_enabled():
target_tab = element
break
assert target_tab is not None, f"No visible/available tab '{tab_name}' found"
# Кликаем на вкладку
logger.info(f"Clicking on tab '{tab_name}'...")
target_tab.click()
# Ждем изменения активной вкладки
self._wait_for_tab_activation(tab_name)
# Ждем загрузки контента
self.wait_for_timeout(500)
def wait_for_rack_loading(self) -> None:
"""Ожидает загрузки интерфейса стойки."""
logger.info("Waiting for rack interface to load...")
# Проверяем наличие кнопок переключения сторон
front_button = self.page.locator(RackLocators.FRONT_SIDE_BUTTON)
back_button = self.page.locator(RackLocators.BACK_SIDE_BUTTON)
expect(front_button).to_be_visible(timeout=5000)
expect(back_button).to_be_visible(timeout=5000)
logger.info("Side toggle buttons loaded")
# Проверяем, что есть активная кнопка
active_button = self.page.locator(RackLocators.ACTIVE_SIDE_BUTTON)
if active_button.count() > 0:
logger.info("Active side button found")
else:
logger.warning("No active side button found")
# Проверяем наличие основного контейнера
main_container = self.page.locator(RackLocators.MAIN_CONTAINER)
if main_container.count() == 0:
logger.warning("Main rack container not found")
else:
logger.info(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()}")
if unit_positions.first.text_content():
content = unit_positions.first.text_content().strip()
logger.info(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.info("Rack interface loaded")
# Проверки
def check_tab_switching(self) -> None:
"""
Проверяет переключение между вкладками стойки в соответствии с локаторами.
Raises:
AssertionError: Если переключение на одну или более вкладок не удалось
"""
logger.info("Testing rack tab switching functionality...")
# Вкладки
defined_tabs = [
"Общая информация",
"Обслуживание",
"События",
"Сервисы"
]
logger.info(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}'...")
# Проверяем существование локатора для этой вкладки
tab_locator = RackLocators.TAB_BY_NAME.format(tab_name)
tab_elements = self.page.locator(tab_locator)
# Проверяем наличие элементов через count()
if tab_elements.count() == 0:
logger.warning(f"Tab '{tab_name}' not found on page")
failed_switches.append(f"Tab '{tab_name}' not found")
continue
# Находим видимую и доступную вкладку
target_tab = None
for i in range(tab_elements.count()):
element = tab_elements.nth(i)
# Проверки видимости и доступности
if element.is_visible() and element.is_enabled():
target_tab = element
break
if not target_tab:
logger.warning(f"No visible/available tab '{tab_name}' found")
failed_switches.append(f"Tab '{tab_name}' is not visible/available")
continue
# Переключаемся на вкладку
logger.info(f"Switching to tab '{tab_name}'...")
# Проверяем активность ДО клика
if self.is_tab_active(tab_name):
logger.info(f"Tab '{tab_name}' is already active")
successful_switches += 1
continue
# Кликаем на вкладку с таймаутом
target_tab.click(timeout=5000)
# Ждем изменения активной вкладки
self._wait_for_tab_activation(tab_name)
# Проверяем, что вкладка активна
if not self.is_tab_active(tab_name):
logger.warning(f"Tab '{tab_name}' not active after switching")
failed_switches.append(f"Tab '{tab_name}' is not active after click")
continue
logger.info(f"Successfully switched to tab '{tab_name}'")
successful_switches += 1
# Небольшая пауза между переключениями
self.wait_for_timeout(1000)
# Формируем итоговый отчет
logger.info("=== TAB SWITCHING RESULTS ===")
logger.info(f"Successful switches: {successful_switches}/{len(defined_tabs)}")
if failed_switches:
logger.info("Failed switches:")
for failure in failed_switches:
logger.info(f" - {failure}")
# Требуем успешного переключения на все определенные вкладки
assert successful_switches == len(defined_tabs), (
f"Tab switching test failed. "
f"Only {successful_switches} out of {len(defined_tabs)} defined tabs "
f"were successfully switched. "
f"Errors: {', '.join(failed_switches)}"
)
logger.info(f"All {successful_switches} defined tabs successfully switched!")
def is_tab_active(self, tab_name: str) -> bool:
"""
Проверяет, активна ли указанная вкладка.
Args:
tab_name: Название вкладки для проверки
Returns:
bool: True если вкладка активна, False в противном случае
"""
# Проверяем по активному классу и тексту
active_tab = self.page.locator(RackLocators.ACTIVE_TAB)
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)")
return True
logger.info(f"Tab '{tab_name}' is not active")
return False
def should_be_header_panel(self, expected_toolbar_title_items: list[str]) -> None:
"""
Проверяет наличие и корректность заголовка панели.
Args:
expected_toolbar_title_items: Ожидаемые элементы заголовка
Raises:
AssertionError: Если заголовок панели не соответствует ожиданиям
"""
panel_header_locator = self.page.locator(PANEL_HEADER)
expect(panel_header_locator).to_be_visible(), "Panel header 'Объекты'"
if panel_header_locator.inner_text() != 'chevron_right':
assert False, "No separator 'chevron_right' after header 'Объекты'"
actual_toolbar_title_items = self.get_toolbar_title()
self.check_lists_equals(
actual_toolbar_title_items,
expected_toolbar_title_items,
f"Miscomparison actual {actual_toolbar_title_items} "
f"and expected {expected_toolbar_title_items}"
)
self.toolbar.check_button_visibility("edit")
def should_be_rack_sides_displayed(self) -> None:
"""Проверка отображения и структуры сторон стойки."""
logger.info("Checking rack sides display and structure...")
# Ожидаем загрузки
self.wait_for_rack_loading()
# БАЗОВАЯ ПРОВЕРКА: наличие кнопок переключения сторон
front_side_button = self.page.locator(RackLocators.FRONT_SIDE_BUTTON)
back_side_button = self.page.locator(RackLocators.BACK_SIDE_BUTTON)
# Проверяем наличие кнопок
expect(front_side_button).to_be_visible(timeout=5000), \
"Front side button not found"
expect(back_side_button).to_be_visible(timeout=5000), \
"Back side button not found"
logger.info("Side toggle buttons found")
# Проверяем, какая сторона активна по умолчанию
current_active = self.get_current_active_side()
logger.info(f"Current active side: {current_active}")
# Дополнительная проверка устройств
self.check_physical_devices_presence()
# ПРОВЕРКА ЛИЦЕВОЙ СТОРОНЫ
self._check_side_details("Лицевая сторона", front_side_button)
# ПРОВЕРКА ОБРАТНОЙ СТОРОНЫ
self._check_side_details("Обратная сторона", back_side_button)
# Возвращаемся на исходную активную сторону
if current_active:
logger.info(f"Returning to original active side: {current_active}")
if current_active == "Лицевая сторона":
front_side_button.click()
else:
back_side_button.click()
self.wait_for_timeout(1000)
final_active = self.get_current_active_side()
logger.info(f"Final active side: {final_active}")
logger.info("All rack sides checks passed successfully")
def should_be_toolbar_buttons(self) -> None:
"""
Проверяет наличие и функциональность кнопок тулбара.
Raises:
AssertionError: Если кнопки недоступны или подсказки неверны.
"""
logger.info("Checking toolbar buttons...")
self.toolbar.check_button_visibility("edit")
self.toolbar.check_button_tooltip("edit", "Изменить")
self.toolbar.get_button_by_name("edit").click()
def should_have_hide_rack_button(self) -> None:
"""
Упрощенная проверка кнопки "Скрыть стойку".
Проверяет только кликабельность и наличие элемента.
"""
logger.info("Checking 'Hide rack' button...")
# Проверяем видимость
self.toolbar.check_button_visibility("hide_rack")
self.toolbar.check_button_tooltip("hide_rack", "Скрыть стойку")
# Проверяем, что кнопка кликабельна
self.toolbar.get_button_by_name("hide_rack").click()
self.wait_for_timeout(1000)
def should_have_show_rack_button(self) -> None:
"""
Проверка кнопки "Показать стойку".
Проверяет наличие, тултип и кликабельность.
"""
logger.info("Checking 'Show rack' button...")
# Проверяем видимость
self.toolbar.check_button_visibility("show_rack")
self.toolbar.check_button_tooltip("show_rack", "Показать стойку")
# Проверяем кликабельность
self.toolbar.get_button_by_name("show_rack").click()
self.wait_for_timeout(1000)
# Вспомогательные методы
def _check_side_details(self, side_name: str, side_button) -> None:
"""
Проверка структуры конкретной стороны стойки.
Args:
side_name: Название стороны для логов
side_button: Локатор кнопки стороны
Raises:
AssertionError: Если структура стороны некорректна
"""
logger.info(f"Checking {side_name}...")
# Проверяем, активна ли уже эта сторона
current_active = self.get_current_active_side()
if current_active == side_name:
logger.info(f"{side_name} is already active")
else:
# Если не активна, кликаем для переключения
logger.info(f"Switching to {side_name}...")
side_button.click()
# Даем время на перерисовку
self.wait_for_timeout(1500)
# Проверяем, что сторона активирована
active_button_after = self.page.locator(RackLocators.ACTIVE_SIDE_BUTTON_TEXT)
assert active_button_after.count() > 0, \
f"No active button found after clicking on {side_name}"
active_text_after = active_button_after.first.text_content().strip()
assert active_text_after == side_name, \
f"Wrong side is active: '{active_text_after}', expected: '{side_name}'"
logger.info(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}")
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}")
# Проверяем устройства
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"
device_classes = first_device.get_attribute("class") or "No classes"
# Ищем слоты внутри устройства
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}")
else:
logger.info("No devices detected")
logger.info(f"{side_name} check completed successfully")
def _wait_for_tab_activation(self, tab_name: str, timeout: int = 5000) -> None:
"""
Ожидает активации вкладки.
Args:
tab_name: Название вкладки для ожидания
timeout: Время ожидания в миллисекундах
Raises:
AssertionError: Если вкладка не активирована в течение таймаута
"""
logger.info(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")
return
self.wait_for_timeout(100)
assert False, f"Tab '{tab_name}' not activated within {timeout}ms"