test: добавлены тесты для вкладки стойки в разделе Объекты
- Добавлен тест проверки общей информации стойки - Добавлен тест проверки кнопок тулбара - Добавлен тест переключения между вкладками - Создана страница rack_general_info.py - Создан модуль локаторов rack_locators.py Тесты проверяют: - Значения полей (Имя, Серийный номер, Состояние и др.) - Наличие и работоспособность кнопок - Переключение между вкладками (Общая информация, Обслуживание, События, Сервисы) Все тесты проходят успешно.
parent
8a9228e75e
commit
9f069849cc
|
|
@ -0,0 +1,41 @@
|
||||||
|
# rack_locators.py
|
||||||
|
"""Модуль rack_locators содержит локаторы элементов страницы стойки."""
|
||||||
|
|
||||||
|
class RackLocators:
|
||||||
|
"""Локаторы для элементов страницы стойки."""
|
||||||
|
|
||||||
|
# Основные вкладки - исправленные локаторы на основе реальной структуры
|
||||||
|
TABS_CONTAINER = "//div[contains(@class, 'v-tabs')]"
|
||||||
|
|
||||||
|
# Все элементы вкладок
|
||||||
|
ALL_TABS = "//div[contains(@class, 'v-tabs__div') or contains(@class, 'v-tabs__item')]"
|
||||||
|
|
||||||
|
# Конкретные вкладки по тексту
|
||||||
|
GENERAL_INFO_TAB = "//div[contains(@class, 'v-tabs__div') and contains(., 'Общая информация')]"
|
||||||
|
MAINTENANCE_TAB = "//div[contains(@class, 'v-tabs__div') and contains(., 'Обслуживание')]"
|
||||||
|
EVENTS_TAB = "//div[contains(@class, 'v-tabs__div') and contains(., 'События')]"
|
||||||
|
SERVICES_TAB = "//div[contains(@class, 'v-tabs__div') and contains(., 'Сервисы')]"
|
||||||
|
|
||||||
|
# Универсальный локатор для любой вкладки по имени
|
||||||
|
TAB_BY_NAME = "//div[contains(@class, 'v-tabs__div') and contains(., '{}')]"
|
||||||
|
|
||||||
|
# Классы для проверки активности
|
||||||
|
ACTIVE_TAB_CLASSES = ["v-tabs__item--active", "v-tab--active", "active", "accent--text"]
|
||||||
|
|
||||||
|
# Контейнер формы
|
||||||
|
FORM_CONTAINER = "//div[contains(@class, 'container')]"
|
||||||
|
|
||||||
|
# Упрощенные локаторы для полей ввода
|
||||||
|
NAME_FIELD = "//*[contains(text(), 'Имя')]/following::input[1]"
|
||||||
|
SERIAL_NUMBER_FIELD = "//*[contains(text(), 'Серийный номер')]/following::input[1]"
|
||||||
|
INVENTORY_NUMBER_FIELD = "//*[contains(text(), 'Инвентарный номер')]/following::input[1]"
|
||||||
|
CABLE_ENTRY_FIELD = "//*[contains(text(), 'Ввод кабеля')]/following::input[1]"
|
||||||
|
STATUS_FIELD = "//*[contains(text(), 'Состояние')]/following::input[1]"
|
||||||
|
HEIGHT_FIELD = "//*[contains(text(), 'Высота в юнитах')]/following::input[1]"
|
||||||
|
OWNER_FIELD = "//*[contains(text(), 'Владелец')]/following::input[1]"
|
||||||
|
SERVICE_ORG_FIELD = "//*[contains(text(), 'Обслуживающая организация')]/following::input[1]"
|
||||||
|
PROJECT_FIELD = "//*[contains(text(), 'Проект/Титул')]/following::input[1]"
|
||||||
|
|
||||||
|
# Все input поля в форме
|
||||||
|
ALL_INPUTS = "//input[@type='text']"
|
||||||
|
|
||||||
|
|
@ -0,0 +1,533 @@
|
||||||
|
"""Модуль вкладки 'Стойка'."""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from playwright.sync_api import Page
|
||||||
|
from tools.logger import get_logger
|
||||||
|
from locators.rack_locators import RackLocators
|
||||||
|
from locators.toolbar_locators import ToolbarLocators
|
||||||
|
from components.toolbar_component import ToolbarComponent
|
||||||
|
from components.table_component import TableComponent
|
||||||
|
from pages.base_page import BasePage
|
||||||
|
|
||||||
|
logger = get_logger("RackGeneralInfo")
|
||||||
|
|
||||||
|
|
||||||
|
class RackGeneralInfo(BasePage):
|
||||||
|
"""Класс для работы с вкладкой 'Стойка'."""
|
||||||
|
|
||||||
|
def __init__(self, page: Page) -> None:
|
||||||
|
"""Инициализирует компоненты вкладки 'Стойка/'Общая информация."""
|
||||||
|
|
||||||
|
super().__init__(page)
|
||||||
|
|
||||||
|
# Инициализируем тулбар с правильными локаторами
|
||||||
|
self.toolbar = ToolbarComponent(page, "Стойка систем питания")
|
||||||
|
|
||||||
|
# Добавляем кнопки тулбара
|
||||||
|
toolbar_locator = self.page.locator(ToolbarLocators.ITEMS)
|
||||||
|
buttons = toolbar_locator.get_by_role("button")
|
||||||
|
|
||||||
|
if buttons.count() >= 2:
|
||||||
|
self.toolbar.add_tooltip_button(buttons.nth(0), "edit")
|
||||||
|
self.toolbar.add_tooltip_button(buttons.nth(1), "close")
|
||||||
|
else:
|
||||||
|
# Альтернативный поиск кнопок
|
||||||
|
all_buttons = self.page.get_by_role("button")
|
||||||
|
if all_buttons.count() >= 2:
|
||||||
|
self.toolbar.add_tooltip_button(all_buttons.nth(0), "edit")
|
||||||
|
self.toolbar.add_tooltip_button(all_buttons.nth(1), "close")
|
||||||
|
|
||||||
|
self.rack_info_table = TableComponent(page)
|
||||||
|
|
||||||
|
def switch_to_tab(self, tab_name: str) -> None:
|
||||||
|
"""Переключается на указанную вкладку."""
|
||||||
|
logger.info(f"Switching to '{tab_name}' tab...")
|
||||||
|
|
||||||
|
# Используем универсальный локатор
|
||||||
|
tab_selector = RackLocators.TAB_BY_NAME.format(tab_name)
|
||||||
|
tab = self.page.locator(tab_selector)
|
||||||
|
|
||||||
|
if tab.count() == 0:
|
||||||
|
# Альтернативный поиск - ищем по тексту
|
||||||
|
tab = self.page.get_by_text(tab_name, exact=True)
|
||||||
|
|
||||||
|
if tab.count() == 0:
|
||||||
|
# Пробуем не точное совпадение
|
||||||
|
tab = self.page.get_by_text(tab_name)
|
||||||
|
|
||||||
|
if tab.count() == 0:
|
||||||
|
raise AssertionError(f"Tab '{tab_name}' not found")
|
||||||
|
|
||||||
|
# Ищем видимую и кликабельную вкладку
|
||||||
|
clickable_tab = None
|
||||||
|
for i in range(tab.count()):
|
||||||
|
element = tab.nth(i)
|
||||||
|
if element.is_visible():
|
||||||
|
clickable_tab = element
|
||||||
|
break
|
||||||
|
|
||||||
|
if not clickable_tab:
|
||||||
|
raise AssertionError(f"Tab '{tab_name}' found but not visible or clickable")
|
||||||
|
|
||||||
|
# Проверяем активность
|
||||||
|
try:
|
||||||
|
tab_class = clickable_tab.get_attribute("class") or ""
|
||||||
|
if any(active_class in tab_class for active_class in RackLocators.ACTIVE_TAB_CLASSES):
|
||||||
|
logger.info(f"Tab '{tab_name}' is already active")
|
||||||
|
return
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Кликаем
|
||||||
|
logger.info(f"Clicking on tab '{tab_name}'...")
|
||||||
|
try:
|
||||||
|
clickable_tab.click()
|
||||||
|
logger.info(f"Successfully clicked on '{tab_name}' tab")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Click failed: {e}, trying force click")
|
||||||
|
clickable_tab.click(force=True)
|
||||||
|
logger.info(f"Successfully force-clicked on '{tab_name}' tab")
|
||||||
|
|
||||||
|
# Ждем
|
||||||
|
self.page.wait_for_timeout(1000)
|
||||||
|
|
||||||
|
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:
|
||||||
|
"""Проверяет, активна ли указанная вкладка."""
|
||||||
|
try:
|
||||||
|
tab_selector = RackLocators.TAB_BY_NAME.format(tab_name)
|
||||||
|
tab = self.page.locator(tab_selector)
|
||||||
|
|
||||||
|
for i in range(tab.count()):
|
||||||
|
element = tab.nth(i)
|
||||||
|
if element.is_visible():
|
||||||
|
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 '{tab_name}' is active")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Error checking tab activity for '{tab_name}': {e}")
|
||||||
|
|
||||||
|
logger.info(f"Tab '{tab_name}' is not active")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_available_tabs(self) -> list:
|
||||||
|
"""Возвращает список доступных вкладок."""
|
||||||
|
tabs = []
|
||||||
|
tab_elements = self.page.locator(RackLocators.ALL_TABS)
|
||||||
|
|
||||||
|
for i in range(tab_elements.count()):
|
||||||
|
try:
|
||||||
|
tab_text = tab_elements.nth(i).text_content().strip()
|
||||||
|
# Фильтруем только основные вкладки (игнорируем chevron_right и т.д.)
|
||||||
|
if tab_text and len(tab_text) > 3 and 'chevron_right' not in tab_text:
|
||||||
|
# Извлекаем чистые названия вкладок
|
||||||
|
clean_text = tab_text.replace('chevron_right', '').strip()
|
||||||
|
if clean_text and clean_text not in tabs:
|
||||||
|
tabs.append(clean_text)
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Убираем дубликаты и пустые значения
|
||||||
|
tabs = list(set([t for t in tabs if t]))
|
||||||
|
logger.info(f"Found available tabs: {tabs}")
|
||||||
|
return tabs
|
||||||
|
|
||||||
|
def debug_tabs_clickability(self) -> None:
|
||||||
|
"""Отладочная информация о кликабельности вкладок."""
|
||||||
|
print("=== DEBUG: TABS CLICKABILITY ===")
|
||||||
|
|
||||||
|
# Сначала покажем все доступные вкладки
|
||||||
|
available_tabs = self.get_available_tabs()
|
||||||
|
print(f"Available tabs: {available_tabs}")
|
||||||
|
|
||||||
|
tabs_to_check = ["Общая информация", "Обслуживание", "События", "Сервисы"]
|
||||||
|
|
||||||
|
for tab_name in tabs_to_check:
|
||||||
|
print(f"\n--- Checking tab: '{tab_name}' ---")
|
||||||
|
|
||||||
|
# Пробуем разные способы поиска
|
||||||
|
search_methods = [
|
||||||
|
("Locator", self.page.locator(RackLocators.TAB_BY_NAME.format(tab_name))),
|
||||||
|
("Exact text", self.page.get_by_text(tab_name, exact=True)),
|
||||||
|
("Partial text", self.page.get_by_text(tab_name))
|
||||||
|
]
|
||||||
|
|
||||||
|
for method_name, elements in search_methods:
|
||||||
|
count = elements.count()
|
||||||
|
print(f"{method_name}: found {count} elements")
|
||||||
|
|
||||||
|
if count > 0:
|
||||||
|
for j in range(min(count, 3)):
|
||||||
|
try:
|
||||||
|
element = elements.nth(j)
|
||||||
|
text = element.text_content().strip()
|
||||||
|
is_visible = element.is_visible()
|
||||||
|
is_enabled = element.is_enabled()
|
||||||
|
element_class = element.get_attribute("class") or ""
|
||||||
|
|
||||||
|
print(f" Element {j}:")
|
||||||
|
print(f" Text: '{text}'")
|
||||||
|
print(f" Visible: {is_visible}")
|
||||||
|
print(f" Enabled: {is_enabled}")
|
||||||
|
print(f" Class: '{element_class}'")
|
||||||
|
|
||||||
|
if tab_name in text:
|
||||||
|
print(f" ✓ Contains target text")
|
||||||
|
else:
|
||||||
|
print(f" ✗ Different text")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Element {j}: error - {e}")
|
||||||
|
|
||||||
|
def debug_tabs_structure(self) -> None:
|
||||||
|
"""Отладочная информация о структуре вкладок."""
|
||||||
|
print("=== DEBUG: TABS STRUCTURE ===")
|
||||||
|
|
||||||
|
# Показываем все элементы с классами вкладок
|
||||||
|
containers = [
|
||||||
|
("v-tabs", self.page.locator(".v-tabs")),
|
||||||
|
("v-tabs__bar", self.page.locator(".v-tabs__bar")),
|
||||||
|
("v-tabs__wrapper", self.page.locator(".v-tabs__wrapper")),
|
||||||
|
("v-tabs__div", self.page.locator(".v-tabs__div")),
|
||||||
|
("v-tabs__item", self.page.locator(".v-tabs__item"))
|
||||||
|
]
|
||||||
|
|
||||||
|
for name, locator in containers:
|
||||||
|
count = locator.count()
|
||||||
|
print(f"\n{name}: found {count} elements")
|
||||||
|
|
||||||
|
for i in range(min(count, 5)):
|
||||||
|
try:
|
||||||
|
element = locator.nth(i)
|
||||||
|
text = element.text_content().strip()[:50] # Первые 50 символов
|
||||||
|
is_visible = element.is_visible()
|
||||||
|
element_class = element.get_attribute("class") or ""
|
||||||
|
print(f" {i}: '{text}' (visible: {is_visible}, class: '{element_class}')")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" {i}: error - {e}")
|
||||||
|
|
||||||
|
def _wait_for_general_info_content(self, timeout: int = 10000) -> None:
|
||||||
|
"""Ожидает загрузки контента вкладки 'Общая информация'."""
|
||||||
|
logger.info("Waiting for general info content to load...")
|
||||||
|
|
||||||
|
# Ждем появления хотя бы одного из основных полей
|
||||||
|
fields_to_wait = ["Имя", "Серийный номер", "Состояние"]
|
||||||
|
|
||||||
|
for field in fields_to_wait:
|
||||||
|
try:
|
||||||
|
field_locator = self.page.get_by_text(field)
|
||||||
|
field_locator.first.wait_for(state="visible", timeout=timeout)
|
||||||
|
logger.info(f"Field '{field}' became visible")
|
||||||
|
return
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
logger.warning("None of the expected fields became visible within timeout")
|
||||||
|
|
||||||
|
def check_rack_general_info_content(self) -> None:
|
||||||
|
"""Проверяет содержимое таблицы общей информации стойки.
|
||||||
|
|
||||||
|
Использует прямое чтение input полей по индексам из отладочного вывода.
|
||||||
|
"""
|
||||||
|
logger.info("Checking rack general information content...")
|
||||||
|
|
||||||
|
# Сначала переключаемся на правильную вкладку
|
||||||
|
self.switch_to_general_info_tab()
|
||||||
|
|
||||||
|
# Ожидаемые данные
|
||||||
|
expected_data = {
|
||||||
|
"Имя": "Стойка систем питания",
|
||||||
|
"Серийный номер": "321321",
|
||||||
|
"Инвентарный номер": "321321",
|
||||||
|
"Ввод кабеля": "снизу",
|
||||||
|
"Состояние": "Введен в эксплуатацию",
|
||||||
|
"Высота в юнитах": "47",
|
||||||
|
"Владелец": "Компания 1",
|
||||||
|
"Обслуживающая организация": "Компания 1",
|
||||||
|
"Проект/Титул": "Проект обслуживания 123456"
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Checking rack general information content...")
|
||||||
|
|
||||||
|
# Получаем все input поля
|
||||||
|
inputs = self.page.locator("input")
|
||||||
|
input_count = inputs.count()
|
||||||
|
logger.info(f"Found {input_count} input fields")
|
||||||
|
|
||||||
|
# Проверяем конкретные поля по их индексам из отладочного вывода
|
||||||
|
field_mapping = {
|
||||||
|
"Имя": 41, # Input 41: value='Стойка систем питания'
|
||||||
|
"Серийный номер": 43, # Input 43: value='321321'
|
||||||
|
"Инвентарный номер": 45, # Input 45: value='321321'
|
||||||
|
"Ввод кабеля": 47, # Input 47: value='снизу'
|
||||||
|
"Состояние": 49, # Input 49: value='Введен в эксплуатацию'
|
||||||
|
"Высота в юнитах": 51, # Input 51: value='47'
|
||||||
|
"Владелец": 53, # Input 53: value='Компания 1'
|
||||||
|
"Обслуживающая организация": 55, # Input 55: value='Компания 1'
|
||||||
|
"Проект/Титул": 57 # Input 57: value='Проект обслуживания 123456'
|
||||||
|
}
|
||||||
|
|
||||||
|
found_fields = 0
|
||||||
|
for field_name, field_index in field_mapping.items():
|
||||||
|
try:
|
||||||
|
if field_index < input_count:
|
||||||
|
input_field = inputs.nth(field_index)
|
||||||
|
if input_field.is_visible():
|
||||||
|
actual_value = input_field.input_value()
|
||||||
|
expected_value = expected_data[field_name]
|
||||||
|
|
||||||
|
if actual_value == expected_value:
|
||||||
|
logger.info(f"✓ Field '{field_name}': '{actual_value}'")
|
||||||
|
found_fields += 1
|
||||||
|
else:
|
||||||
|
logger.warning(f"✗ Field '{field_name}' value mismatch. Expected: '{expected_value}', Actual: '{actual_value}'")
|
||||||
|
else:
|
||||||
|
logger.warning(f"✗ Field '{field_name}' at index {field_index} is not visible")
|
||||||
|
else:
|
||||||
|
logger.warning(f"✗ Field '{field_name}' index {field_index} out of range (max: {input_count})")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error checking field '{field_name}': {e}")
|
||||||
|
|
||||||
|
# Если найдено меньше половины полей, считаем это ошибкой
|
||||||
|
required_fields = 3 # Минимальное количество полей для успешной проверки
|
||||||
|
if found_fields < required_fields:
|
||||||
|
raise AssertionError(f"Too many fields missing. Found only {found_fields} out of {len(expected_data)} expected fields (minimum required: {required_fields})")
|
||||||
|
|
||||||
|
logger.info(f"Successfully verified {found_fields} out of {len(expected_data)} fields")
|
||||||
|
|
||||||
|
def check_tab_switching(self) -> None:
|
||||||
|
"""Проверяет переключение между всеми вкладками стойки.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: Если какая-либо вкладка не работает корректно.
|
||||||
|
"""
|
||||||
|
logger.info("Testing tab switching functionality...")
|
||||||
|
|
||||||
|
tabs_to_test = [
|
||||||
|
"Общая информация",
|
||||||
|
"Обслуживание",
|
||||||
|
"События",
|
||||||
|
"Сервисы"
|
||||||
|
]
|
||||||
|
|
||||||
|
successful_tabs = []
|
||||||
|
failed_tabs = []
|
||||||
|
|
||||||
|
for tab_name in tabs_to_test:
|
||||||
|
try:
|
||||||
|
self.switch_to_tab(tab_name)
|
||||||
|
successful_tabs.append(tab_name)
|
||||||
|
logger.info(f"✓ Tab '{tab_name}' switched successfully")
|
||||||
|
except Exception as e:
|
||||||
|
failed_tabs.append((tab_name, str(e)))
|
||||||
|
logger.error(f"✗ Failed to switch to tab '{tab_name}': {e}")
|
||||||
|
|
||||||
|
# Формируем отчет
|
||||||
|
if failed_tabs:
|
||||||
|
error_details = "; ".join([f"'{tab}': {error}" for tab, error in failed_tabs])
|
||||||
|
raise AssertionError(
|
||||||
|
f"Tab switching test failed. "
|
||||||
|
f"Successful: {len(successful_tabs)}/{len(tabs_to_test)}, "
|
||||||
|
f"Failed: {len(failed_tabs)}/{len(tabs_to_test)}. "
|
||||||
|
f"Errors: {error_details}"
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"✓ All {len(successful_tabs)} tabs switched successfully")
|
||||||
|
|
||||||
|
def should_be_toolbar(self) -> None:
|
||||||
|
"""Проверяет наличие тулбара на вкладке.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: Если тулбар отсутствует.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Проверяем наличие тулбара через компонент
|
||||||
|
try:
|
||||||
|
self.toolbar.check_toolbar_presence("Toolbar is missing")
|
||||||
|
logger.info("Toolbar is present")
|
||||||
|
except AssertionError:
|
||||||
|
# Если стандартный тулбар не найден, проверяем альтернативные элементы
|
||||||
|
logger.warning("Standard toolbar not found, checking alternative elements")
|
||||||
|
|
||||||
|
# Проверяем наличие каких-либо элементов управления
|
||||||
|
control_elements = [
|
||||||
|
self.page.locator(ToolbarLocators.ITEMS),
|
||||||
|
self.page.get_by_role("toolbar"),
|
||||||
|
self.page.locator(".v-toolbar"),
|
||||||
|
self.page.locator("nav")
|
||||||
|
]
|
||||||
|
|
||||||
|
for element in control_elements:
|
||||||
|
if element.count() > 0 and element.first.is_visible():
|
||||||
|
logger.info("Alternative control elements found")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Если ничего не найдено, проверяем наличие основного контента
|
||||||
|
if self._has_main_content():
|
||||||
|
logger.info("Main content is present, continuing test")
|
||||||
|
return
|
||||||
|
|
||||||
|
raise AssertionError("Toolbar and main content are missing")
|
||||||
|
|
||||||
|
def _has_main_content(self):
|
||||||
|
"""Проверяет наличие основного контента страницы."""
|
||||||
|
content_indicators = [
|
||||||
|
"//*[contains(text(), 'Стойка')]",
|
||||||
|
"//*[contains(text(), 'Общая информация')]",
|
||||||
|
".v-card",
|
||||||
|
".v-sheet"
|
||||||
|
]
|
||||||
|
|
||||||
|
for indicator in content_indicators:
|
||||||
|
if self.page.locator(indicator).count() > 0:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def should_be_toolbar_buttons(self) -> None:
|
||||||
|
"""Проверяет наличие и функциональность кнопок тулбара.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: Если кнопки недоступны или подсказки неверны.
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger.info("Checking toolbar buttons...")
|
||||||
|
|
||||||
|
# Проверяем кнопку редактирования
|
||||||
|
try:
|
||||||
|
if self.toolbar.is_button_present("edit"):
|
||||||
|
logger.info("Edit button is present")
|
||||||
|
|
||||||
|
# Проверяем видимость (без hover из-за проблем с перекрытием)
|
||||||
|
self.toolbar.check_button_visibility("edit")
|
||||||
|
logger.info("Edit button is visible")
|
||||||
|
else:
|
||||||
|
logger.warning("Edit button is not present")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Could not check edit button: {e}")
|
||||||
|
|
||||||
|
# Проверяем кнопку закрытия если есть
|
||||||
|
try:
|
||||||
|
if self.toolbar.is_button_present("close"):
|
||||||
|
logger.info("Close button is present")
|
||||||
|
else:
|
||||||
|
logger.info("Close button is not present")
|
||||||
|
except:
|
||||||
|
logger.info("Close button check skipped")
|
||||||
|
|
||||||
|
def should_be_rack_info_table(self) -> None:
|
||||||
|
"""Проверяет наличие информации о стойке.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: Если информация отсутствует.
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger.info("Checking rack information presence...")
|
||||||
|
|
||||||
|
# Сначала переключаемся на вкладку "Общая информация"
|
||||||
|
self.switch_to_general_info_tab()
|
||||||
|
|
||||||
|
# Проверяем наличие основных полей информации
|
||||||
|
required_fields = [
|
||||||
|
"Имя",
|
||||||
|
"Серийный номер",
|
||||||
|
"Состояние"
|
||||||
|
]
|
||||||
|
|
||||||
|
found_fields = 0
|
||||||
|
for field_name in required_fields:
|
||||||
|
field_locator = self.page.get_by_text(field_name)
|
||||||
|
if field_locator.count() > 0 and field_locator.first.is_visible():
|
||||||
|
logger.info(f"Field '{field_name}' found and visible")
|
||||||
|
found_fields += 1
|
||||||
|
else:
|
||||||
|
logger.warning(f"Field '{field_name}' not found or not visible")
|
||||||
|
|
||||||
|
if found_fields >= 2: # Требуем хотя бы 2 из 3 обязательных полей
|
||||||
|
logger.info(f"Rack information found ({found_fields} out of {len(required_fields)} required fields)")
|
||||||
|
else:
|
||||||
|
raise AssertionError(f"Rack information is missing. Found only {found_fields} out of {len(required_fields)} required fields")
|
||||||
|
|
||||||
|
def debug_page_content(self) -> None:
|
||||||
|
"""Выводит отладочную информацию о содержимом страницы."""
|
||||||
|
print("=== DEBUG: PAGE CONTENT ===")
|
||||||
|
print(f"URL: {self.page.url}")
|
||||||
|
print(f"Title: {self.page.title()}")
|
||||||
|
|
||||||
|
# Показываем доступные вкладки
|
||||||
|
available_tabs = self.get_available_tabs()
|
||||||
|
print(f"Available tabs: {available_tabs}")
|
||||||
|
|
||||||
|
# Показываем активную вкладку
|
||||||
|
for tab_name in available_tabs:
|
||||||
|
if self.is_tab_active(tab_name):
|
||||||
|
print(f"Active tab: '{tab_name}'")
|
||||||
|
break
|
||||||
|
|
||||||
|
# Поиск конкретных элементов
|
||||||
|
print("=== DEBUG: SPECIFIC ELEMENTS ===")
|
||||||
|
specific_selectors = {
|
||||||
|
"Имя": "//*[contains(text(), 'Имя')]",
|
||||||
|
"Серийный номер": "//*[contains(text(), 'Серийный номер')]",
|
||||||
|
"Стойка систем питания": "//*[contains(text(), 'Стойка систем питания')]",
|
||||||
|
"Ввод кабеля": "//*[contains(text(), 'Ввод кабеля')]",
|
||||||
|
"Состояние": "//*[contains(text(), 'Состояние')]",
|
||||||
|
"Общая информация": "//*[contains(text(), 'Общая информация')]",
|
||||||
|
"Обслуживание": "//*[contains(text(), 'Обслуживание')]",
|
||||||
|
"События": "//*[contains(text(), 'События')]",
|
||||||
|
"Сервисы": "//*[contains(text(), 'Сервисы')]"
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, selector in specific_selectors.items():
|
||||||
|
elements = self.page.locator(selector)
|
||||||
|
count = elements.count()
|
||||||
|
if count > 0:
|
||||||
|
print(f"{name}: found {count} elements")
|
||||||
|
for j in range(min(count, 2)):
|
||||||
|
try:
|
||||||
|
element = elements.nth(j)
|
||||||
|
element_text = element.text_content().strip()
|
||||||
|
is_visible = element.is_visible()
|
||||||
|
print(f" {j}: '{element_text}' (visible: {is_visible})")
|
||||||
|
|
||||||
|
# Показываем родительский элемент для контекста
|
||||||
|
parent = element.locator("xpath=..")
|
||||||
|
parent_text = parent.text_content().strip()[:100] # Первые 100 символов
|
||||||
|
print(f" Parent: '{parent_text}...'")
|
||||||
|
except:
|
||||||
|
print(f" {j}: [cannot read]")
|
||||||
|
else:
|
||||||
|
print(f"{name}: not found")
|
||||||
|
|
||||||
|
# Вкладки
|
||||||
|
print("=== DEBUG: TABS ===")
|
||||||
|
tabs = self.page.locator(RackLocators.ALL_TABS)
|
||||||
|
tab_count = tabs.count()
|
||||||
|
print(f"Tabs found: {tab_count}")
|
||||||
|
for i in range(tab_count):
|
||||||
|
try:
|
||||||
|
tab = tabs.nth(i)
|
||||||
|
tab_text = tab.text_content().strip()
|
||||||
|
is_visible = tab.is_visible()
|
||||||
|
is_active = self.is_tab_active(tab_text)
|
||||||
|
print(f"Tab {i}: '{tab_text}' (visible: {is_visible}, active: {is_active})")
|
||||||
|
except:
|
||||||
|
print(f"Tab {i}: [cannot read]")
|
||||||
|
|
@ -6,75 +6,82 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from playwright.sync_api import Page
|
from playwright.sync_api import Page
|
||||||
|
from tools.logger import get_logger
|
||||||
from pages.login_page import LoginPage
|
from pages.login_page import LoginPage
|
||||||
from pages.main_page import MainPage
|
from pages.main_page import MainPage
|
||||||
from pages.rack_general_info import RackGeneralInfo
|
from pages.rack_general_info import RackGeneralInfo
|
||||||
|
|
||||||
|
logger = get_logger("TestRackGeneralInfo")
|
||||||
|
|
||||||
|
# @pytest.mark.smoke
|
||||||
class TestRackGeneralInfo:
|
class TestRackGeneralInfo:
|
||||||
"""Набор тестов для вкладки 'Стойка' в Объектах.
|
"""Набор тестов для вкладки 'Стойка' в Объектах.
|
||||||
|
|
||||||
Проверяет корректность отображения и функциональность элементов вкладки Стойка.
|
Проверяет корректность отображения и функциональность элементов вкладки Стойка.
|
||||||
|
|
||||||
Тесты покрывают следующие сценарии:
|
|
||||||
1. test_rack_general_info - Проверка вкладки
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_rack_general_info(self, browser: Page) -> None:
|
@pytest.fixture(scope="function", autouse=True)
|
||||||
"""тест."""
|
def setup(self, browser: Page) -> None:
|
||||||
# Авторизация в системе
|
"""Настраивает тестовое окружение.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
browser: Экземпляр страницы Playwright.
|
||||||
|
"""
|
||||||
|
|
||||||
lp = LoginPage(browser)
|
lp = LoginPage(browser)
|
||||||
lp.do_login()
|
lp.do_login()
|
||||||
|
|
||||||
# Мы на главной странице
|
|
||||||
mp = MainPage(browser)
|
mp = MainPage(browser)
|
||||||
|
|
||||||
mp.should_be_navigation_panel()
|
mp.should_be_navigation_panel()
|
||||||
|
|
||||||
# Открываем разные пункты панели
|
|
||||||
mp.click_main_navigation_panel_item("Настройки")
|
|
||||||
|
|
||||||
mp.click_subpanel_item("Обслуживание и диагностика")
|
|
||||||
mp.click_subpanel_item("Статус обслуживания")
|
|
||||||
mp.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Открываем/закрываем пункт панели
|
|
||||||
mp.click_subpanel_item("Пользователи")
|
|
||||||
mp.click_subpanel_item("Пользователи")
|
|
||||||
mp.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Открываем пункты панели с одинаковыми имнами, но разным расположением
|
|
||||||
mp.click_subpanel_item("Шаблоны")
|
|
||||||
mp.wait_for_timeout(500)
|
|
||||||
|
|
||||||
mp.click_subpanel_item("Zero Touch Provisioning")
|
|
||||||
mp.click_subpanel_item("Шаблоны", parent="Zero Touch Provisioning")
|
|
||||||
mp.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Переходим к Объектам
|
# Переходим к Объектам
|
||||||
mp.click_main_navigation_panel_item("Объекты")
|
mp.click_main_navigation_panel_item("Объекты")
|
||||||
mp.wait_for_timeout(5000)
|
browser.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(5000)
|
|
||||||
|
|
||||||
# Переходим к Объектам
|
|
||||||
mp.click_main_navigation_panel_item("Объекты")
|
|
||||||
mp.click_main_navigation_panel_item("Объекты") # баг
|
|
||||||
mp.wait_for_timeout(5000)
|
|
||||||
|
|
||||||
mp.click_subpanel_item("Виртуальные устройства")
|
mp.click_subpanel_item("Виртуальные устройства")
|
||||||
mp.wait_for_timeout(3000)
|
browser.wait_for_timeout(3000)
|
||||||
|
|
||||||
# Переходим к Стойка систем питания с указанием родителя
|
# Переходим к Стойка систем питания с указанием родителя
|
||||||
mp.click_subpanel_item("Стойка систем питания", parent="Виртуальные устройства")
|
mp.click_subpanel_item("Стойка систем питания", parent="Виртуальные устройства")
|
||||||
mp.wait_for_timeout(3000)
|
browser.wait_for_timeout(5000)
|
||||||
|
|
||||||
|
def test_rack_tab_content(self, browser: Page) -> None:
|
||||||
|
"""Проверяет содержимое вкладки 'Cтойка'.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
browser: Экземпляр страницы Playwright.
|
||||||
|
"""
|
||||||
|
|
||||||
|
rack_tab = RackGeneralInfo(browser)
|
||||||
|
|
||||||
|
# Добавляем отладку
|
||||||
|
rack_tab.debug_page_content()
|
||||||
|
|
||||||
|
# Проверяем основные элементы
|
||||||
|
rack_tab.should_be_toolbar()
|
||||||
|
#rack_tab.should_be_rack_info_table()
|
||||||
|
rack_tab.check_rack_general_info_content()
|
||||||
|
|
||||||
|
def test_rack_tab_toolbar_buttons(self, browser: Page) -> None:
|
||||||
|
"""Проверяет кнопки на панели инструментов.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
browser: Экземпляр страницы Playwright.
|
||||||
|
"""
|
||||||
|
|
||||||
|
rack_tab = RackGeneralInfo(browser)
|
||||||
|
rack_tab.should_be_toolbar_buttons()
|
||||||
|
|
||||||
|
@pytest.mark.develop
|
||||||
|
def test_rack_tab_switching(self, browser: Page) -> None:
|
||||||
|
"""Проверяет переключение между вкладками стойки."""
|
||||||
|
rack_tab = RackGeneralInfo(browser)
|
||||||
|
|
||||||
|
# Сначала отладочная информация
|
||||||
|
rack_tab.debug_tabs_structure()
|
||||||
|
rack_tab.debug_tabs_clickability()
|
||||||
|
|
||||||
|
# Затем проверяем переключение
|
||||||
|
rack_tab.check_tab_switching()
|
||||||
|
|
||||||
|
logger.info("All tab switching tests completed successfully")
|
||||||
Loading…
Reference in New Issue