e-nms_qa_automation/pages/rack_general_info.py

534 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.

"""Модуль вкладки 'Стойка'."""
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]")