Добавлены тесты и страницы для вкладки стойки
- Добавлен тест test_rack_tab.py для проверки функциональности вкладки стойки - Добавлен Page Object rack_tab.py с методами работы со стойкой - Добавлены локаторы rack_locators.py для элементов стойки
parent
eb7fe13b02
commit
3b0bb8b752
|
|
@ -0,0 +1,87 @@
|
||||||
|
"""Модуль rack_locators содержит локаторы элементов страницы стойки.
|
||||||
|
|
||||||
|
Класс RackLocators хранит XPath локаторы для взаимодействия
|
||||||
|
с элементами интерфейса стойки оборудования в тестах.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class RackLocators:
|
||||||
|
"""Класс для хранения локаторов элементов страницы стойки.
|
||||||
|
|
||||||
|
Содержит локаторы в формате XPath для поиска элементов:
|
||||||
|
- Вкладки стойки (верхние вкладки)
|
||||||
|
- Секции лицевой и обратной сторон стойки
|
||||||
|
- Юниты и устройства на стойке
|
||||||
|
- Поля формы редактирования стойки
|
||||||
|
- Контейнеры и структурные элементы
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Основной контейнер вкладок стойки (верхние вкладки)
|
||||||
|
TABS_CONTAINER = "//div[@class='v-tabs__container']"
|
||||||
|
|
||||||
|
# Все элементы верхних вкладок стойки
|
||||||
|
ALL_TABS = "//div[@class='v-tabs__container']//a[contains(@class, 'v-tabs__item')]"
|
||||||
|
|
||||||
|
# Универсальный локатор для любой вкладки по имени
|
||||||
|
TAB_BY_NAME = "//div[@class='v-tabs__container']//a[contains(@class, 'v-tabs__item') and contains(., '{}')]"
|
||||||
|
|
||||||
|
# Конкретные вкладки по тексту
|
||||||
|
GENERAL_INFO_TAB = "//div[@class='v-tabs__container']//a[contains(@class, 'v-tabs__item') and contains(., 'Общая информация')]"
|
||||||
|
MAINTENANCE_TAB = "//div[@class='v-tabs__container']//a[contains(@class, 'v-tabs__item') and contains(., 'Обслуживание')]"
|
||||||
|
EVENTS_TAB = "//div[@class='v-tabs__container']//a[contains(@class, 'v-tabs__item') and contains(., 'События')]"
|
||||||
|
SERVICES_TAB = "//div[@class='v-tabs__container']//a[contains(@class, 'v-tabs__item') and contains(., 'Сервисы')]"
|
||||||
|
|
||||||
|
# Классы для проверки активности
|
||||||
|
ACTIVE_TAB_CLASSES = ["accent--text", "v-tabs__item--active"]
|
||||||
|
|
||||||
|
# Локатор для активной вкладки
|
||||||
|
ACTIVE_TAB = "//div[@class='v-tabs__container']//a[contains(@class, 'v-tabs__item--active')]"
|
||||||
|
|
||||||
|
# Контейнер формы
|
||||||
|
FORM_CONTAINER = "//div[contains(@class, 'container')]"
|
||||||
|
|
||||||
|
# Локаторы полей
|
||||||
|
NAME_FIELD = "//input[@aria-label='Имя']"
|
||||||
|
SERIAL_NUMBER_FIELD = "//input[@aria-label='Серийный номер']"
|
||||||
|
INVENTORY_NUMBER_FIELD = "//input[@aria-label='Инвентарный номер']"
|
||||||
|
CABLE_ENTRY_FIELD = "//input[@aria-label='Ввод кабеля']"
|
||||||
|
STATUS_FIELD = "//input[@aria-label='Состояние']"
|
||||||
|
HEIGHT_FIELD = "//input[@aria-label='Высота в юнитах']"
|
||||||
|
OWNER_FIELD = "//input[@aria-label='Владелец']"
|
||||||
|
SERVICE_ORG_FIELD = "//input[@aria-label='Обслуживающая организация']"
|
||||||
|
PROJECT_FIELD = "//input[@aria-label='Проект/Титул']"
|
||||||
|
|
||||||
|
# Локаторы для отображения сторон стойки
|
||||||
|
FRONT_SIDE_CONTAINER = "//div[contains(@class, 'cabinet') and not(contains(@class, 'back'))]"
|
||||||
|
BACK_SIDE_CONTAINER = "//div[contains(@class, 'cabinet') and contains(@class, 'back')]"
|
||||||
|
|
||||||
|
FRONT_SIDE_TITLE = "//span[contains(@class, 'subheading') and contains(text(), 'Лицевая сторона')]"
|
||||||
|
BACK_SIDE_TITLE = "//span[contains(@class, 'subheading') and contains(text(), 'Обратная сторона')]"
|
||||||
|
|
||||||
|
# Юниты на лицевой стороне
|
||||||
|
FRONT_SIDE_UNITS = ".cabinet:not(.back) .unit, .unit:not(.back)"
|
||||||
|
|
||||||
|
# Юниты на обратной стороне
|
||||||
|
BACK_SIDE_UNITS = ".cabinet.back .unit"
|
||||||
|
|
||||||
|
# Локатор для всех юнитов
|
||||||
|
ALL_UNITS = ".unit"
|
||||||
|
|
||||||
|
# Устройства на лицевой стороне
|
||||||
|
FRONT_SIDE_DEVICES = "//*[contains(@class, 'parent-class')]"
|
||||||
|
|
||||||
|
# Устройства на обратной стороне
|
||||||
|
BACK_SIDE_DEVICES = "//*[contains(@class, 'parent-class')]"
|
||||||
|
|
||||||
|
# Позиции юнитов
|
||||||
|
UNIT_POSITIONS = "//div[contains(@class, 'unit-positions')]"
|
||||||
|
|
||||||
|
# Контейнер с обеими сторонами
|
||||||
|
SIDES_CONTAINER = "//div[contains(@class, 'layout row shrink fill-height ma-0 pa-0')]"
|
||||||
|
|
||||||
|
# ЛОКАТОРЫ ДЛЯ СТРУКТУРЫ СТОРОН
|
||||||
|
# Основные секции сторон
|
||||||
|
FRONT_SIDE_SECTION = "//span[contains(text(), 'Лицевая сторона')]//ancestor::div[contains(@class, 'flex shrink')]"
|
||||||
|
BACK_SIDE_SECTION = "//span[contains(text(), 'Обратная сторона')]//ancestor::div[contains(@class, 'flex shrink')]"
|
||||||
|
|
||||||
|
# Основной контейнер стойки
|
||||||
|
MAIN_CONTAINER = "//div[contains(@class, 'layout row')]"
|
||||||
|
|
@ -0,0 +1,466 @@
|
||||||
|
"""Модуль тестов вкладки 'Стойка'.
|
||||||
|
|
||||||
|
Содержит тесты для проверки функциональности
|
||||||
|
работы со стойкой оборудования.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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} определенных вкладок успешно переключены!")
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
"""Модуль тестов вкладки 'Стойка' в модуле Объекты.
|
||||||
|
|
||||||
|
Содержит тесты для проверки функциональности
|
||||||
|
работы со стойкой оборудования.
|
||||||
|
"""
|
||||||
|
import pytest
|
||||||
|
from playwright.sync_api import Page
|
||||||
|
from pages.rack_pages.rack_tab import RackTab
|
||||||
|
from pages.login_page import LoginPage
|
||||||
|
from pages.main_page import MainPage
|
||||||
|
|
||||||
|
|
||||||
|
# @pytest.mark.smoke
|
||||||
|
class TestRackTab:
|
||||||
|
"""Набор тестов для вкладки 'Стойка' в модуле Объекты.
|
||||||
|
|
||||||
|
Проверяет корректность отображения, функциональность элементов интерфейса
|
||||||
|
и переключение между вкладками стойки оборудования.
|
||||||
|
|
||||||
|
Тесты покрывают следующие функциональные области:
|
||||||
|
1. test_rack_tab_content - Базовая структура и содержимое вкладки стойки
|
||||||
|
2. test_rack_tab_switching - Функциональность переключения между вкладками стойки
|
||||||
|
"""
|
||||||
|
|
||||||
|
@pytest.fixture(scope="function", autouse=True)
|
||||||
|
def setup(self, browser: Page) -> None:
|
||||||
|
"""Фикстура для подготовки тестового окружения.
|
||||||
|
|
||||||
|
Выполняет:
|
||||||
|
1. Авторизацию в системе
|
||||||
|
2. Переход к стойке оборудования через панель навигации:
|
||||||
|
- Объекты → Физические устройства с опросом → Здание ЦОД 4 → Стойка КСПД
|
||||||
|
|
||||||
|
Args:
|
||||||
|
browser (Page): Экземпляр страницы Playwright для взаимодействия с UI
|
||||||
|
"""
|
||||||
|
# Авторизация в системе
|
||||||
|
lp = LoginPage(browser)
|
||||||
|
lp.do_login()
|
||||||
|
|
||||||
|
# Мы на главной странице
|
||||||
|
mp = MainPage(browser)
|
||||||
|
mp.should_be_navigation_panel()
|
||||||
|
mp.wait_for_timeout(3000)
|
||||||
|
|
||||||
|
# Переходим к Объектам
|
||||||
|
mp.click_main_navigation_panel_item("Объекты")
|
||||||
|
mp.wait_for_timeout(3000)
|
||||||
|
|
||||||
|
mp.click_subpanel_item("Физические устройства с опросом")
|
||||||
|
mp.wait_for_timeout(3000)
|
||||||
|
|
||||||
|
# Переходим Здание ЦОД 4
|
||||||
|
mp.click_subpanel_item("Здание ЦОД 4")
|
||||||
|
mp.wait_for_timeout(3000)
|
||||||
|
|
||||||
|
# Переходим к Стойка КСПД с указанием родителя
|
||||||
|
mp.click_subpanel_item("Стойка КСПД", parent="Здание ЦОД 4")
|
||||||
|
mp.wait_for_timeout(10000)
|
||||||
|
|
||||||
|
@pytest.mark.develop
|
||||||
|
def test_rack_tab_content(self, browser: Page) -> None:
|
||||||
|
"""Тест содержимого вкладки 'Стойка'.
|
||||||
|
|
||||||
|
Проверяет:
|
||||||
|
1. Наличие и корректность заголовка панели с навигационной цепочкой
|
||||||
|
2. Отображение и структуру обеих сторон стойки (лицевой и обратной)
|
||||||
|
3. Наличие и функциональность кнопок панели инструментов
|
||||||
|
4. Корректность отображения юнитов и устройств на стойке
|
||||||
|
|
||||||
|
Args:
|
||||||
|
browser (Page): Экземпляр страницы Playwright для взаимодействия с UI
|
||||||
|
"""
|
||||||
|
expected_toolbar_subtitles = [
|
||||||
|
"Мониторинг и инвентаризация",
|
||||||
|
'chevron_right',
|
||||||
|
"Физические устройства с опросом",
|
||||||
|
'chevron_right',
|
||||||
|
"Здание ЦОД 4",
|
||||||
|
'chevron_right',
|
||||||
|
"Стойка КСПД"
|
||||||
|
]
|
||||||
|
|
||||||
|
rt = RackTab(browser)
|
||||||
|
rt.should_be_header_panel(expected_toolbar_subtitles)
|
||||||
|
|
||||||
|
# Комплексная проверка отображения обеих сторон стойки с детальной информацией
|
||||||
|
rt.should_be_rack_sides_displayed()
|
||||||
|
|
||||||
|
# Переход в режим редактирования
|
||||||
|
rt.should_be_toolbar_buttons()
|
||||||
|
rt.wait_for_timeout(2000)
|
||||||
|
|
||||||
|
def test_rack_tab_switching(self, browser: Page) -> None:
|
||||||
|
"""Тест переключения между вкладками стойки оборудования.
|
||||||
|
|
||||||
|
Проверяет функциональность переключения на все доступные вкладки:
|
||||||
|
1. Общая информация
|
||||||
|
2. Обслуживание
|
||||||
|
3. События
|
||||||
|
4. Сервисы
|
||||||
|
|
||||||
|
Проверяет:
|
||||||
|
1. Наличие и доступность всех вкладок
|
||||||
|
2. Корректность активации вкладок после переключения
|
||||||
|
3. Отсутствие ошибок при последовательном переключении
|
||||||
|
|
||||||
|
Args:
|
||||||
|
browser (Page): Экземпляр страницы Playwright для взаимодействия с UI
|
||||||
|
"""
|
||||||
|
rt = RackTab(browser)
|
||||||
|
|
||||||
|
# Проверяем переключение между всеми вкладками стойки
|
||||||
|
rt.check_tab_switching()
|
||||||
Loading…
Reference in New Issue