e-nms_qa_automation/pages/rack_pages/rack_tab.py

467 lines
22 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 elements.tooltip_button_element import TooltipButton
from components.toolbar_component import ToolbarComponent
from pages.base_page import BasePage
from locators.rack_locators import RackLocators
from tools.logger import get_logger
logger = get_logger("RACK_TAB")
# Специфичные локаторы оставленые в основном коде
PANEL_HEADER = "//span[text()='Объекты']/following-sibling::i"
TOOLBAR_CONTENT = "//div[@class='v-toolbar__content']"
EDIT_BUTTON_ANCESTOR_DIV3 = "xpath=/ancestor::div[3]//button"
PANEL_HEADER_ANCESTOR_DIV2 = "xpath=/ancestor::div[2]"
class RackTab(BasePage):
"""Класс для работы с вкладкой стойки оборудования."""
def __init__(self, page: Page) -> None:
"""
Инициализирует объект вкладки стойки.
Args:
page: Экземпляр страницы Playwright
"""
super().__init__(page)
locator_button = self.page.locator(PANEL_HEADER).\
locator(EDIT_BUTTON_ANCESTOR_DIV3).nth(0)
self.edit_button = TooltipButton(page, locator_button, "edit")
self.toolbar = ToolbarComponent(page, "")
self.toolbar.add_tooltip_button(locator_button, "edit")
def wait_for_rack_loading(self, timeout: int = 15000) -> None:
"""
Ожидает загрузки интерфейса стойки.
Args:
timeout: Время ожидания в миллисекундах (по умолчанию 15000)
Raises:
TimeoutError: Если загрузка не завершилась в указанное время
"""
logger.info("Ожидание загрузки интерфейса стойки...")
# Ждем появления основного контейнера
main_container = self.page.locator(RackLocators.MAIN_CONTAINER)
expect(main_container).to_be_visible(timeout=timeout)
# Ждем появления юнитов
units = self.page.locator(RackLocators.ALL_UNITS)
expect(units).to_have_count(20, timeout=timeout)
logger.info("Интерфейс стойки загружен")
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_tab(self, tab_name: str) -> None:
"""
Переключается на указанную вкладку.
Args:
tab_name: Название вкладки для переключения
Raises:
AssertionError: Если вкладка не найдена или недоступна
"""
logger.info(f"Переключение на вкладку '{tab_name}'...")
tab = self.page.locator(RackLocators.TAB_BY_NAME.format(tab_name))
if tab.count() == 0:
raise AssertionError(f"Вкладка '{tab_name}' не найдена")
# Проверяем активность ДО клика
if self.is_tab_active(tab_name):
logger.info(f"Вкладка '{tab_name}' уже активна")
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
if not target_tab:
raise AssertionError(f"Не найдена видимая/доступная вкладка '{tab_name}'")
# Кликаем на вкладку
logger.info(f"Клик на вкладку '{tab_name}'...")
target_tab.click()
# Ждем изменения активной вкладки
self._wait_for_tab_activation(tab_name)
# Ждем загрузки контента
self.page.wait_for_timeout(500)
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_events_tab(self) -> None:
"""Переключается на вкладку 'События'."""
self.switch_to_tab("События")
def switch_to_services_tab(self) -> None:
"""Переключается на вкладку 'Сервисы'."""
self.switch_to_tab("Сервисы")
def is_tab_active(self, tab_name: str) -> bool:
"""
Проверяет, активна ли указанная вкладка.
Args:
tab_name: Название вкладки для проверки
Returns:
bool: True если вкладка активна, False в противном случае
"""
# Метод 1: Проверяем по активному классу и тексту, метод быстый, если надо универсальный оставояем метод 2 - медленный
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_name}' активна (через класс активной вкладки)")
return True
# Метод 2: Проверяем по классам у конкретной вкладки
tab = self.page.locator(RackLocators.TAB_BY_NAME.format(tab_name))
if tab.count() > 0:
for i in range(tab.count()):
element = tab.nth(i)
if element.is_visible() and element.is_enabled():
element_class = element.get_attribute("class") or ""
is_active = any(
active_class in element_class
for active_class in RackLocators.ACTIVE_TAB_CLASSES
)
if is_active:
logger.info(f"Вкладка '{tab_name}' активна (классы: {element_class})")
return True
logger.info(f"Вкладка '{tab_name}' не активна")
return False
def get_available_tabs(self) -> list[str]:
"""
Возвращает список доступных вкладок используя DOM структуру.
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_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"Найдена верхняя вкладка: '{tab_text}'")
logger.info(f"Найдены доступные верхние вкладки: {tabs}")
return tabs
def _wait_for_tab_activation(self, tab_name: str, timeout: int = 5000) -> None:
"""
Ожидает активации вкладки.
Args:
tab_name: Название вкладки для ожидания
timeout: Время ожидания в миллисекундах
Raises:
AssertionError: Если вкладка не активирована в течение таймаута
"""
logger.info(f"Ожидание активации вкладки '{tab_name}'...")
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_name}' успешно активирована")
return
self.page.wait_for_timeout(100)
raise AssertionError(f"Вкладка '{tab_name}' не активирована в течение {timeout}мс")
def should_be_toolbar_buttons(self) -> None:
"""
Проверяет наличие и функциональность кнопок тулбара.
Raises:
AssertionError: Если кнопки недоступны или подсказки неверны.
"""
logger.info("Проверка кнопок панели инструментов...")
self.toolbar.check_button_visibility("edit")
self.toolbar.check_button_tooltip("edit", "Изменить")
self.toolbar.get_button_by_name("edit").click()
def should_be_rack_sides_displayed(self) -> None:
"""
Проверка отображения и структуры сторон стойки.
Raises:
AssertionError: Если стороны стойки не отображаются корректно
"""
logger.info("Проверка отображения и структуры сторон стойки...")
# Ожидаем загрузки
self.wait_for_rack_loading()
# БАЗОВАЯ ПРОВЕРКА: обе стороны отображаются
logger.info("--- Базовая проверка отображения сторон ---")
front_side_section = self.page.locator(RackLocators.FRONT_SIDE_SECTION).first
expect(front_side_section).to_be_visible(timeout=10000)
logger.info("Секция лицевой стороны найдена")
back_side_section = self.page.locator(RackLocators.BACK_SIDE_SECTION).first
expect(back_side_section).to_be_visible(timeout=10000)
logger.info("Секция обратной стороны найдена")
# Проверяем заголовки
front_side_title = front_side_section.locator(RackLocators.FRONT_SIDE_TITLE)
expect(front_side_title).to_be_visible(timeout=5000), "Заголовок 'Лицевая сторона' не отображается"
logger.info("Заголовок 'Лицевая сторона' отображается")
back_side_title = back_side_section.locator(RackLocators.BACK_SIDE_TITLE)
expect(back_side_title).to_be_visible(timeout=5000), "Заголовок 'Обратная сторона' не отображается"
logger.info("Заголовок 'Обратная сторона' отображается")
# Проверяем позиции юнитов
unit_positions = self.page.locator(RackLocators.UNIT_POSITIONS)
total_positions = unit_positions.count()
logger.info(f"Всего позиций юнитов: {total_positions}")
assert total_positions > 0, "Не найдено позиций юнитов"
# Детальная проверка лицевой стороны
logger.info("--- Детальная проверка лицевой стороны ---")
self._check_front_side_details(front_side_section)
# Детальная проверка обратной стороны
logger.info("--- Детальная проверка обратной стороны ---")
self._check_back_side_details(back_side_section)
logger.info("Все проверки сторон стойки пройдены успешно")
def _check_front_side_details(self, front_side_section) -> None:
"""
Проверка структуры лицевой стороны стойки.
Args:
front_side_section: Локатор секции лицевой стороны
Raises:
AssertionError: Если структура лицевой стороны некорректна
"""
# Проверяем юниты в секции лицевой стороны
front_side_units = front_side_section.locator(RackLocators.FRONT_SIDE_UNITS)
unit_count = front_side_units.count()
logger.info(f"Найдено юнитов на лицевой стороне: {unit_count}")
assert unit_count >= 1, f"Не найдено юнитов на лицевой стороне. Ожидалось минимум 1, найдено {unit_count}"
# Проверяем наличие устройств на лицевой стороне
front_side_devices = front_side_section.locator(RackLocators.FRONT_SIDE_DEVICES)
device_count = front_side_devices.count()
logger.info(f"Найдено физических устройств на лицевой стороне: {device_count}")
if device_count > 0:
for i in range(device_count):
device = front_side_devices.nth(i)
device_title = device.get_attribute("title")
device_classes = device.get_attribute("class") or ""
logger.info(f" Устройство {i}: title='{device_title}', classes='{device_classes}'")
def _check_back_side_details(self, back_side_section) -> None:
"""
Проверка структуры обратной стороны стойки.
Args:
back_side_section: Локатор секции обратной стороны
Raises:
AssertionError: Если структура обратной стороны некорректна
"""
# Проверяем юниты в секции обратной стороны
back_side_units = back_side_section.locator(RackLocators.BACK_SIDE_UNITS)
unit_count = back_side_units.count()
logger.info(f"Найдено юнитов на обратной стороне: {unit_count}")
assert unit_count >= 1, f"Не найдено юнитов на обратной стороне. Ожидалось минимум 1, найдено {unit_count}"
# Проверяем наличие устройств на обратной стороне
back_side_devices = back_side_section.locator(RackLocators.BACK_SIDE_DEVICES)
device_count = back_side_devices.count()
logger.info(f"Найдено физических устройств на обратной стороне: {device_count}")
if device_count > 0:
for i in range(device_count):
device = back_side_devices.nth(i)
device_title = device.get_attribute("title")
device_classes = device.get_attribute("class") or ""
logger.info(f" Устройство {i}: title='{device_title}', classes='{device_classes}'")
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} and expected {expected_toolbar_title_items}")
self.toolbar.check_button_visibility("edit")
def check_tab_switching(self) -> None:
"""
Проверяет переключение между вкладками стойки в соответствии с локаторами.
Raises:
AssertionError: Если переключение на одну или более вкладок не удалось
"""
logger.info("Тестирование функциональности переключения вкладок стойки...")
# Вкладки
defined_tabs = [
"Общая информация",
"Обслуживание",
"События",
"Сервисы"
]
logger.info(f"Тестируемые определенные вкладки: {defined_tabs}")
successful_switches = 0
failed_switches = []
# Тестируем переключение на каждую определенную вкладку
for tab_name in defined_tabs:
logger.info(f"Тестирование переключения на вкладку '{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_name}' не найдена на странице")
failed_switches.append(f"Вкладка '{tab_name}' не найдена")
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"Не найдена видимая/доступная вкладка '{tab_name}'")
failed_switches.append(f"Вкладка '{tab_name}' не видима/не доступна")
continue
# Переключаемся на вкладку
logger.info(f"Переключение на вкладку '{tab_name}'...")
# Проверяем активность ДО клика
if self.is_tab_active(tab_name):
logger.info(f"Вкладка '{tab_name}' уже активна")
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_name}' не активна после переключения")
failed_switches.append(f"Вкладка '{tab_name}' не активна после клика")
continue
logger.info(f"Успешно переключено на вкладку '{tab_name}'")
successful_switches += 1
# Небольшая пауза между переключениями для стабильности
self.page.wait_for_timeout(1000)
# Формируем итоговый отчет
logger.info("=== РЕЗУЛЬТАТЫ ПЕРЕКЛЮЧЕНИЯ ВКЛАДОК ===")
logger.info(f"Успешных переключений: {successful_switches}/{len(defined_tabs)}")
if failed_switches:
logger.info("Неудачные переключения:")
for failure in failed_switches:
logger.info(f" - {failure}")
# Требуем успешного переключения на все определенные вкладки
if successful_switches < len(defined_tabs):
raise AssertionError(
f"Тест переключения вкладок не пройден. "
f"Только {successful_switches} из {len(defined_tabs)} определенных вкладок переключены успешно. "
f"Ошибки: {', '.join(failed_switches)}"
)
logger.info(f"Все {successful_switches} определенных вкладок успешно переключены!")