Добавлен компонент Панель Событий

pull/1/head
nsubbot 2025-09-11 18:04:01 +03:00
parent c218531d34
commit 2f6bd950e7
12 changed files with 441 additions and 41 deletions

View File

@ -0,0 +1,151 @@
"""Модуль компонента панели событий. Содержит класс для работы с элементами панели."""
from playwright.sync_api import Page
from tools.logger import get_logger
from locators.event_panel_locators import EventPanelLocators
from elements.tooltip_button_element import TooltipButton
from elements.tab_button_element import TabButton
from elements.button_element import Button
from components.card_component import CardComponent
from components.base_component import BaseComponent
logger = get_logger("EVENT_PANEL")
class EventPanelComponent(BaseComponent):
"""Компонент панели событий. Предоставляет методы для взаимодействия с ней."""
def __init__(self, page: Page):
"""Инициализирует компонент панели событий.
Args:
page: Экземпляр страницы Playwright.
"""
super().__init__(page)
self.states_tab = TabButton(page, self.page.locator(EventPanelLocators.TAB_STATES), "states_tab")
self.actions_tab = TabButton(page, self.page.locator(EventPanelLocators.TAB_ACTIONS), "actions_tab")
self.events_tab = TabButton(page, self.page.locator(EventPanelLocators.TAB_EVENTS), "events_tab")
self.maintenance_tab = TabButton(page, self.page.locator(EventPanelLocators.TAB_MAINTENANCE), "maintenance_tab")
self.system_log_tab = TabButton(page, self.page.locator(EventPanelLocators.TAB_SYSTEM_LOG), "system_log_tab")
self.unknown_reason_button = TooltipButton(page, self.page.locator(EventPanelLocators.BUTTONS_EVENT).nth(0), "unknown_reason_button")
self.warning_button = TooltipButton(page, self.page.locator(EventPanelLocators.BUTTONS_EVENT).nth(1), "warning_button")
self.damage_button = TooltipButton(page, self.page.locator(EventPanelLocators.BUTTONS_EVENT).nth(2), "damage_button")
self.failure_button = TooltipButton(page, self.page.locator(EventPanelLocators.BUTTONS_EVENT).nth(3), "failure_button")
buttons_service_locators = self.page.locator(EventPanelLocators.BUTTONS_SERVICE).get_by_role("button").all()
self.search_button = Button(page, buttons_service_locators[0], "search_button")
self.user_button = Button(page, buttons_service_locators[1], "user_button")
self.user_card = CardComponent(page)
# Действия:
def click_expand_less_button(self) -> None:
"""Выполняет нажатие кнопки галочка вверх."""
button_locator = self.page.locator(EventPanelLocators.TAB_EXPAND_BUTTONS).\
get_by_role("button").filter(has_text='expand_less')
button_locator.click()
def click_expand_more_button(self) -> None:
"""Выполняет нажатие кнопки галочка вниз."""
button_locator = self.page.locator(EventPanelLocators.TAB_EXPAND_BUTTONS).\
get_by_role("button").filter(has_text='expand_more')
button_locator.click()
def do_logout(self) -> None:
"""Выполняет выход из системы."""
self.should_be_user_button()
self.user_button.click()
self.user_card.click_logout_button()
def get_event_tooltip_texts(self) -> []:
"""Возвращает список текстов всплывающих подсказок кнопок счетчиков событий."""
tooltip_texts = []
tooltip_texts.append(self.unknown_reason_button.get_tooltip_text())
tooltip_texts.append(self.warning_button.get_tooltip_text())
tooltip_texts.append(self.damage_button.get_tooltip_text())
tooltip_texts.append(self.failure_button.get_tooltip_text())
return tooltip_texts
def get_event_button_values(self) -> {}:
"""Возвращает набор значений кнопок счетчиков событий."""
event_buttons_texts = {}
event_buttons_texts['Неизвестно'] = self.unknown_reason_button.get_text(0)
event_buttons_texts['Предупреждение'] = self.warning_button.get_text(0)
event_buttons_texts['Повреждение'] = self.damage_button.get_text(0)
event_buttons_texts['Авария'] = self.failure_button.get_text(0)
return event_buttons_texts
def get_panel_position(self) -> str:
"""Возвращает текущее положение панели событий относительно страницы: "top", "center","bottom"."""
style_attr = self.page.locator(EventPanelLocators.AREA_EVENTS).get_attribute("style")
position = "bottom"
if style_attr.find("display: none;") == -1:
height = style_attr.replace("height: ","").replace(";", "")
if height == "100%":
position = "top"
else:
position = "center"
return position
# Проверки:
def check_expand_less_button(self) -> bool:
"""Проверяет наличие кнопки галочка вверх."""
try:
_ = self.page.locator(EventPanelLocators.TAB_EXPAND_BUTTONS).\
get_by_role("button").filter(has_text='expand_less')
except TimeoutError:
return False
return True
def check_expand_more_button(self) -> bool:
"""Проверяет наличие кнопки галочка вниз"""
try:
_ = self.page.locator(EventPanelLocators.TAB_EXPAND_BUTTONS).\
get_by_role("button").filter(has_text='expand_more')
except TimeoutError:
return False
return True
def should_be_user_button(self) -> None:
"""Проверяет наличие кнопки пользователя."""
self.user_button.check_visibility("User button is missing on event panel")
def should_be_search_button(self) -> None:
"""Проверяет наличие кнопки поиска."""
self.search_button.check_visibility("Search button is missing on event panel")
def should_be_tab_buttons(self) -> None:
"""Проверяет наличие блока tab-кнопок Состояния, Действия, События, Обслуживание, Системный журнал."""
self.states_tab.check_have_text('Состояния', "Tab button with text Состояния is missing on event panel")
self.actions_tab.check_have_text('Действия',"Tab button with text Действия is missing on event panel")
self.events_tab.check_have_text('События', "Tab button with text События is missing on event panel")
self.maintenance_tab.check_have_text('Обслуживание', "Tab button with text Обслуживание is missing on event panel")
self.system_log_tab.check_have_text('Системный журнал', "Tab button with text Системный журнал is missing on event panel")
def should_be_event_buttons(self) -> None:
"""Проверяет наличие блока кнопок-счетчиков событий."""
self.unknown_reason_button.check_visibility("Unknown reason event button is missing on event panel")
self.warning_button.check_visibility("Warning event button is missing on event panel")
self.damage_button.check_visibility("Damage event button is missing on event panel")
self.failure_button.check_visibility("Failure event button is missing on event panel")

View File

@ -157,4 +157,4 @@ class ToolbarComponent(BaseComponent):
button = self.get_button_by_name(name)
if button is None:
raise AssertionError(f"Unsupported button name {name}")
button.check_tooltip_with_text(ToolbarLocators.TOOLTIP, tooltip)
button.check_tooltip_with_text(tooltip)

View File

@ -29,8 +29,7 @@ class Environment:
try:
self.env: str = os.getenv('ENV', self.TEST)
self.access_token: str = ""
self.token: str = ""
self.current_user_credential = {}
except KeyError:
self.env: str = self.TEST
@ -66,14 +65,14 @@ class Environment:
return self.URLS[self.env]
raise Exception(f"Unknown value of ENV variable {self.env}")
def set_access_token(self, token: str) -> None:
"""Устанавливает токен доступа.
def set_current_user_credential(self, credential: {}) -> None:
"""Сохраняет учетные данные текущего пользователя.
Аргументы:
token (str): Токен для установки.
credential ({}): Учетные данные текущего пользователя.
"""
self.token = token
self.current_user_credential = credential
def get_access_token(self) -> str:
"""Возвращает текущий токен доступа.
@ -82,7 +81,34 @@ class Environment:
str: Текущий токен доступа.
"""
return self.token
return self.current_user_credential.get("access_token")
def get_current_user_name(self) -> str:
"""Возвращает имя текущего пользователя.
Возвращает:
str: Имя текущего пользователя.
"""
return self.current_user_credential.get("login")
def get_current_user_role(self) -> str:
"""Возвращает роль текущего пользователя.
Возвращает:
str: Роль текущего пользователя.
"""
return self.current_user_credential.get("role")
def get_current_user_id(self) -> str:
"""Возвращает id текущего пользователя.
Возвращает:
str: id текущего пользователя.
"""
return self.current_user_credential.get("$id")
host: Environment = Environment()

View File

@ -0,0 +1,34 @@
"""Модуль tab_button_element содержит класс для работы с кнопками типа v-tabs на странице.
Класс TabButton наследует базовый функционал BaseElement и предоставляет
специфичные методы для работы с элементами.
"""
from tools.logger import get_logger
from elements.base_element import BaseElement
logger = get_logger("BUTTON_TAB")
class TabButton(BaseElement):
"""Класс для работы с кнопками типа v-tabs на странице.
Наследует функциональность BaseElement и добавляет специфичные
для кнопок методы и проверки.
"""
@property
def type_of(self) -> str:
"""Возвращает тип элемента ('кнопка-tab').
Returns:
str: Тип элемента - 'кнопка-tab'.
"""
return "button_tab"
# Действия:
# (Методы действий будут добавлены по мере необходимости)
# Проверки:
# (Методы проверок будут добавлены по мере необходимости)

View File

@ -5,6 +5,7 @@
"""
from tools.logger import get_logger
from locators.button_locators import ButtonLocators
from elements.base_element import BaseElement
logger = get_logger("TOOLTIP_BUTTON")
@ -26,7 +27,28 @@ class TooltipButton(BaseElement):
return "tooltip_button"
def check_tooltip_with_text(self, tooltip_locator: str, expected_text: str) -> None:
# Действия:
def get_tooltip_text(self) -> str:
"""Возвращает текст всплывающей подсказки.
Args:
tooltip_locator (str): Локатор элемента подсказки.
"""
# Наведение на элемент для отображения подсказки
self.locator.hover()
# Получение элемента подсказки
tooltip = self.page.locator(ButtonLocators.TOOLTIP)
tooltip.wait_for(state="visible", timeout=5000)
self.page.wait_for_timeout(300)
return tooltip.text_content().strip()
# Проверки:
def check_tooltip_with_text(self, expected_text: str) -> None:
"""Проверяет соответствие текста всплывающей подсказки.
Args:
@ -41,7 +63,7 @@ class TooltipButton(BaseElement):
self.locator.hover()
# Получение элемента подсказки
tooltip = self.page.locator(tooltip_locator)
tooltip = self.page.locator(ButtonLocators.TOOLTIP)
tooltip.wait_for(state="visible", timeout=5000)

View File

@ -30,7 +30,7 @@ def pytest_addoption(parser: Parser):
help="Choose browser: chrome, remote_chrome or firefox")
parser.addoption('--h', action='store', default=False,
help='Choose headless: True or False')
parser.addoption('--s', action='store', default="{'width': 1920, 'height': 400}",
parser.addoption('--s', action='store', default="{'width': 1600, 'height': 900}",
help='Size window: width,height')
# Закомментированные альтернативные размеры окон
# parser.addoption('--s', action='store', default="{'width': 1920, 'height': 1080}",

View File

@ -7,9 +7,29 @@
class EventPanelLocators:
"""Локаторы элементов панели событий.
Содержит XPath локаторы для:
BUTTONS_BLOCK (str): блока кнопок в панели инструментов
TABS_BLOCK (str): блока кнопок в панели событий.
TAB_EXPAND_BUTTONS (str): блока кнопок расширения панели событий.
TAB_STATES (str): кнопки Состояния.
TAB_ACTIONS (str): кнопки Действия.
TAB_EVENTS (str): кнопки События.
TAB_MAINTENANCE (str): кнопки Обслуживания.
TAB_SYSTEM_LOG (str): кнопки Системный журнал.
BUTTONS_EVENT (str): блока кнопок-счетчиков событий.
BUTTONS_SERVICE (str): блока кнопок, содержащего кнопки Поиска и Текущего пользователя.
AREA_EVENTS (str): рабочей области страницы.
"""
BUTTONS_BLOCK = "//nav/div[@class='v-toolbar__content']/div[@class='v-toolbar__items'][2]"
TABS_BLOCK = "//nav/div[@class='v-toolbar__content']/div[@class='v-toolbar__items'][1]"
TAB_EXPAND_BUTTONS = f"{TABS_BLOCK}/div[1]/div[1]/div"
TAB_STATES = f"{TABS_BLOCK}//div[@class='v-tabs']//div[@class='v-tabs__container']/div[2]"
TAB_ACTIONS = f"{TABS_BLOCK}//div[@class='v-tabs']//div[@class='v-tabs__container']/div[3]"
TAB_EVENTS = f"{TABS_BLOCK}//div[@class='v-tabs']//div[@class='v-tabs__container']/div[4]"
TAB_MAINTENANCE = f"{TABS_BLOCK}//div[@class='v-tabs']//div[@class='v-tabs__container']/div[5]"
TAB_SYSTEM_LOG = f"{TABS_BLOCK}//div[@class='v-tabs']//div[@class='v-tabs__container']/div[6]"
BUTTONS_EVENT = "//nav/div[@class='v-toolbar__content']/div[@class='v-toolbar__items'][2]//span[contains(@class, 'v-tooltip')]"
BUTTONS_SERVICE = "//nav/div[@class='v-toolbar__content']/div[@class='v-toolbar__items'][2]"
AREA_EVENTS = "#app > div.application--wrap > div > div:nth-child(3)"

View File

@ -195,7 +195,7 @@ class EditUserModalWindow(ModalWindowComponent):
menu_locator = self.page.locator(ModalWindowLocators.MENU_INPUT_FORM_USER_DATA)
self.check_by_window_title()
self.check_toolbar_button__visibility("close")
self.check_toolbar_button_visibility("close")
self.check_toolbar_button_tooltip("close", "Закрыть")
for name in self.content_items.keys():

View File

@ -57,8 +57,7 @@ class LoginPage(BasePage):
if "login" in response.url:
response_body = self.get_response_body(response)
if response_body:
token = response_body.get("access_token")
host.set_access_token(token)
host.set_current_user_credential(response_body)
self.page.on("response", handle_response)

View File

@ -6,10 +6,8 @@
from playwright.sync_api import Page
from locators.navigation_panel_locators import NavigationPanelLocators
from locators.event_panel_locators import EventPanelLocators
from elements.button_element import Button
from components.navbar_component import NavigationPanelComponent
from components.card_component import CardComponent
from components.eventbar_component import EventPanelComponent
from pages.base_page import BasePage
class MainPage(BasePage):
@ -32,12 +30,7 @@ class MainPage(BasePage):
super().__init__(page)
self.navigation_panel = NavigationPanelComponent(page)
locators = self.page.locator(EventPanelLocators.BUTTONS_BLOCK).get_by_role("button").all()
self.user_button = Button(page, locators[0], "search_button")
self.user_button = Button(page, locators[1], "user_button")
self.user_card = CardComponent(page)
self.event_panel = EventPanelComponent(page)
# Действия:
def click_main_navigation_panel_item(self, item_name: str) -> None:
@ -58,17 +51,20 @@ class MainPage(BasePage):
# Рекурсивный поиск в дереве v-treeview заданного элемента и клик по нему
self.navigation_panel.click_sub_item(node_locator, item_name)
def click_user_button(self) -> None:
"""Кликает по кнопке пользователя."""
def click_events_panel_expand_less_button(self) -> None:
"""Выполняет нажатие кнопки галочка вверх."""
self.user_button.click()
self.event_panel.click_expand_less_button()
def click_events_panel_expand_more_button(self) -> None:
"""Выполняет нажатие кнопки галочка вниз."""
self.event_panel.click_expand_more_button()
def do_logout(self) -> None:
"""Выполняет выход из системы."""
self.should_be_user_button()
self.click_user_button()
self.user_card.click_logout_button()
self.event_panel.do_logout()
def expand_navigation_subpanel(self):
"""Выполняет полное открытие активной главной навигационной подпанели."""
@ -79,6 +75,29 @@ class MainPage(BasePage):
# Рекурсивный обход дерева v-treeview выбранной подпанели и вывод информации об элементах в режиме отладки (debug=True)
self.navigation_panel.traverse_panel_tree(node_locator, debug=False)
def get_event_counters_by_tooltips(self) -> {}:
"""Возвращает набор текстов всплывающих подсказок кнопок счетчиков событий."""
events = self.event_panel.get_event_tooltip_texts()
event_counters = {}
for event in events:
if len(event) > 0:
event_name, count = event.split(" ")
event_counters[event_name] = count
return event_counters
def get_event_counters_by_buttons(self) -> {}:
"""Возвращает набор значений кнопок счетчиков событий."""
return self.event_panel.get_event_button_values()
def get_events_panel_position(self) -> str:
"""Возвращает текущее положение панели событий относительно страницы: "top", "center","bottom"."""
return self.event_panel.get_panel_position()
def scroll_navigation_panel_up(self) -> None:
"""Прокручивает панель навигации вверх."""
@ -98,10 +117,24 @@ class MainPage(BasePage):
"Navigation panel is missing"
)
def should_be_user_button(self) -> None:
"""Проверяет наличие кнопки пользователя."""
def should_be_event_panel(self) -> None:
"""Проверяет наличие элементов панели событий."""
self.user_button.check_visibility("User button is missing on event panel")
## to-do: кнопки галочки???
self.event_panel.should_be_tab_buttons()
self.event_panel.should_be_event_buttons()
self.event_panel.should_be_search_button()
self.event_panel.should_be_user_button()
def check_expand_less_button(self) -> bool:
"""Проверяет наличие кнопки галочка вверх."""
return self.event_panel.check_expand_less_button()
def check_expand_more_button(self) -> bool:
"""Проверяет наличие кнопки галочка вниз."""
return self.event_panel.check_expand_more_button()
def check_navigation_panel_verticall_scrolling(self) -> bool:
"""Проверяет возможность вертикальной прокрутки панели.

View File

@ -339,7 +339,7 @@ class SessionsTab(BasePage):
delete_button.check_visibility(
f"Delete session button is missing on {row_index} row"
)
delete_button.check_tooltip_with_text(ButtonLocators.TOOLTIP, tooltip)
delete_button.check_tooltip_with_text(tooltip)
# Ожидаем исчезновения подсказки после проверки
self.wait_for_tooltip_to_disappear()

View File

@ -0,0 +1,115 @@
"""Модуль тестов панели событий.
Содержит тесты для проверки функциональности
панели событий в приложении.
"""
import pytest
from playwright.sync_api import Page
from pages.main_page import MainPage
from pages.login_page import LoginPage
# @pytest.mark.smoke
class TestEventPanel:
"""Класс тестов для проверки панели событий.
Атрибуты:
browser: Фикстура для работы с браузером.
"""
def test_event_panel_content(self, browser: Page) -> None:
"""Проверяет содержимое панели событий.
Args:
browser: Экземпляр страницы Playwright.
"""
lp = LoginPage(browser)
lp.do_login()
mp = MainPage(browser)
mp.should_be_event_panel()
# Проверяем соответствие тултипов информации на кнопках
tooltip_event_counters = mp.get_event_counters_by_tooltips()
button_event_counters = mp.get_event_counters_by_tooltips()
for event, counter in tooltip_event_counters.items():
button_counter = button_event_counters.get(event)
if button_counter is None:
assert False, f"Found unexpected tooltip {event} for event button"
if button_counter != counter:
assert False, f"Expected tooltip value {counter} is not equal button value {button_counter} for event button {event}"
# @pytest.mark.develop
def test_event_panel_expand_buttons(self, browser: Page) -> None:
"""Проверяет состояние и количество кнопок расширения рабочей области панели событий.
Args:
browser: Экземпляр страницы Playwright.
"""
lp = LoginPage(browser)
lp.do_login()
mp = MainPage(browser)
# Проверяем начальное состояние - панель событий внизу, видна одна кнопка expand less
current_position = mp.get_events_panel_position()
assert current_position == "bottom", \
"Events panel should be located on main page bottom"
assert mp.check_expand_less_button(), \
"Expand less button should be present"
assert mp.check_expand_more_button(), \
"Expand more button should be absent"
mp.click_events_panel_expand_less_button()
mp.wait_for_timeout(500)
# Проверяем, что панель событий переместилась в середину экрана,
# видна обе кнопки expand less и expand more
current_position = mp.get_events_panel_position()
assert current_position == "center", \
"Events panel should be located on main page center"
assert mp.check_expand_less_button(), \
"Expand less button should be present"
assert mp.check_expand_more_button(), \
"Expand more button should be present"
mp.click_events_panel_expand_less_button()
mp.wait_for_timeout(500)
# Проверяем, что панель событий находится вверху экрана,
# видна кнопки expand more и отсутствует expand less
current_position = mp.get_events_panel_position()
assert current_position == "top", \
"Events panel should be located on main page top"
assert mp.check_expand_less_button(), \
"Expand less button should be absent"
assert mp.check_expand_more_button(), \
"Expand more button should be present"
# перемещение в отратном напрвлении сверху вниз
mp.click_events_panel_expand_more_button()
mp.wait_for_timeout(500)
current_position = mp.get_events_panel_position()
assert current_position == "center", \
"Events panel should be located on main page center"
assert mp.check_expand_less_button(), \
"Expand less button should be present"
assert mp.check_expand_more_button(), \
"Expand more button should be present"
mp.click_events_panel_expand_more_button()
mp.wait_for_timeout(500)
current_position = mp.get_events_panel_position()
assert current_position == "bottom", \
"Events panel should be located on main page bottom"
assert mp.check_expand_less_button(), \
"Expand less button should be present"
assert mp.check_expand_more_button(), \
"Expand more button should be absent"