Добавлены новые тесты для вкладки Сессия

pull/1/head
nsubbot 2025-08-15 14:39:46 +03:00
parent 88e7e27c4a
commit a7c3e953f7
5 changed files with 346 additions and 78 deletions

View File

@ -148,9 +148,7 @@ class BaseComponent:
loc = self.get_locator(locator)
width = loc.evaluate("el => el.scrollWidth")
loc.scroll_into_view_if_needed()
self.page.mouse.wheel(-width, 0)
loc.evaluate("el => el.scrollBy(-el.scrollWidth, 0)")
loc.wait_for(timeout=2000)
@ -170,9 +168,7 @@ class BaseComponent:
loc = self.get_locator(locator)
width = loc.evaluate("el => el.scrollWidth")
loc.scroll_into_view_if_needed()
self.page.mouse.wheel(width, 0)
loc.evaluate("el => el.scrollBy(el.scrollWidth, 0)")
loc.wait_for(timeout=2000)

View File

@ -59,6 +59,16 @@ class ConfirmComponent(BaseComponent):
self.close_button.click()
def scroll_window_left(self) -> None:
"""Прокручивает содержимое окна влево."""
self.scroll_left(ConfirmLocators.CONFIRM)
def scroll_window_right(self) -> None:
"""Прокручивает содержимое окна вправо."""
self.scroll_right(ConfirmLocators.CONFIRM)
# Проверки:
def check_title(self, title: str, msg: str) -> None:
"""Проверяет текст заголовка окна подтверждения.
@ -79,3 +89,9 @@ class ConfirmComponent(BaseComponent):
"""
self.text.check_have_text(text, msg)
def check_window_horizontal_scrolling(self) -> bool:
"""Проверяет возможность горизонтальной прокрутки окна."""
return self.is_scrollable_horizontally(ConfirmLocators.CONFIRM)

View File

@ -1,14 +1,12 @@
"""Модуль base_page содержит базовый класс для работы со страницами.
"""Базовый класс страницы для работы с Playwright.
Класс BasePage предоставляет общие методы для взаимодействия
со страницами через Playwright и выполнения API-запросов.
Содержит общие методы для взаимодействия со страницей и API.
"""
import json
from typing import Any, Dict, List, Optional
from playwright.sync_api import Page, Response, APIRequestContext, expect
from tools.logger import get_logger
from data.environment import host
from tools.logger import get_logger
import json
logger = get_logger("BASE_PAGE")
@ -16,54 +14,67 @@ logger = get_logger("BASE_PAGE")
class BasePage:
"""Базовый класс для работы со страницами через Playwright.
Содержит общие методы для:
- Навигации по страницам
- Выполнения API-запросов
- Проверок состояния страницы
Атрибуты:
page (Page): Экземпляр страницы Playwright.
"""
def __init__(self, page: Page) -> None:
def __init__(self, page: Page):
"""Инициализирует базовую страницу.
Args:
page: Экземпляр страницы Playwright
page (Page): Экземпляр страницы Playwright.
"""
self.page = page
# Действия:
def current_url(self) -> str:
"""Возвращает текущий URL страницы."""
"""Возвращает текущий URL страницы.
Returns:
str: Текущий URL страницы.
"""
return self.page.url
def open(self, uri: str) -> Optional[Response]:
"""Открывает указанный URI на базовом URL."""
def open(self, uri) -> Response | None:
"""Открывает указанный URI в браузере.
Args:
uri (str): URI для открытия (без базового URL).
Returns:
Response | None: Ответ сервера или None в случае ошибки.
"""
return self.page.goto(f"{host.get_base_url()}{uri}", wait_until='domcontentloaded')
def page_reload(self) -> None:
"""Перезагружает текущую страницу."""
self.page.reload()
def wait_for_timeout(self, timeout: int) -> None:
"""Ожидает указанное количество миллисекунд."""
def wait_for_timeout(self, timeout):
"""Ожидает указанное количество миллисекунд.
Args:
timeout (int): Время ожидания в миллисекундах.
"""
self.page.wait_for_timeout(timeout)
def get_api_request_context(self) -> APIRequestContext:
"""Возвращает контекст для выполнения API-запросов."""
"""Возвращает контекст API-запросов.
Returns:
APIRequestContext: Контекст для выполнения API-запросов.
"""
return self.page.context.request
def send_get_api_request(self, uri: str) -> Response:
def send_get_api_request(self, uri) -> Response:
"""Отправляет GET-запрос к API.
Args:
uri: URI для запроса
"""
uri (str): URI API-эндпоинта (без базового URL).
Returns:
Response: Ответ сервера.
"""
api_request_context = self.get_api_request_context()
token = host.get_access_token()
headers = {"Accept": "application/json", "Authorization": f"Bearer {token}"}
@ -73,14 +84,16 @@ class BasePage:
)
return response
def send_post_api_request(self, uri: str, payload: Dict[str, Any]) -> Response:
def send_post_api_request(self, uri, payload) -> Response:
"""Отправляет POST-запрос к API.
Args:
uri: URI для запроса
payload: Тело запроса
"""
uri (str): URI API-эндпоинта (без базового URL).
payload: Данные для отправки в теле запроса.
Returns:
Response: Ответ сервера.
"""
api_request_context = self.get_api_request_context()
token = host.get_access_token()
headers = {"Accept": "application/json", "Authorization": f"Bearer {token}"}
@ -91,13 +104,15 @@ class BasePage:
)
return response
def get_response_body(self, response: Response) -> Optional[Dict[str, Any]]:
"""Возвращает тело ответа в формате JSON.
def get_response_body(self, response) -> dict | None:
"""Извлекает тело ответа в формате JSON.
Args:
response: Объект ответа
"""
response (Response): Ответ сервера.
Returns:
dict | None: Распарсенное тело ответа или None в случае ошибки.
"""
try:
response_body = response.json()
except json.JSONDecodeError:
@ -107,39 +122,54 @@ class BasePage:
# Проверки:
def check_URL(self, uri: str, msg: str) -> None:
"""Проверяет соответствие текущего URL ожидаемому.
"""Проверяет, что текущий URL соответствует ожидаемому.
Args:
uri: Ожидаемый URI
msg: Сообщение об ошибке
"""
uri (str): Ожидаемый URI (без базового URL).
msg (str): Сообщение об ошибке при несоответствии.
Raises:
AssertionError: Если URL не соответствует ожидаемому.
"""
expect(self.page).to_have_url(
f"{host.get_base_url()}{uri}",
timeout=60000
), msg
def check_equals(self, actual: Any, expected: Any, msg: str) -> None:
def check_equals(self, actual, expected, msg: str) -> None:
"""Проверяет равенство фактического и ожидаемого значений.
Args:
actual: Фактическое значение
expected: Ожидаемое значение
msg: Сообщение об ошибке
"""
actual: Фактическое значение.
expected: Ожидаемое значение.
msg (str): Сообщение об ошибке при несоответствии.
Raises:
AssertionError: Если значения не равны.
"""
assert actual == expected, msg
def check_lists_equals(self, actual: List[Any], expected: List[Any], msg: str) -> None:
def check_lists_equals(self, actual: list, expected: list, msg: str) -> None:
"""Рекурсивно проверяет равенство двух списков.
Args:
actual: Фактический список
expected: Ожидаемый список
msg: Сообщение об ошибке
"""
actual (list): Фактический список.
expected (list): Ожидаемый список.
msg (str): Сообщение об ошибке при несоответствии.
def compare_lists(list1: List[Any], list2: List[Any]) -> bool:
Raises:
AssertionError: Если списки не равны.
"""
def compare_lists(list1: list, list2: list) -> bool:
"""Вспомогательная функция для рекурсивного сравнения списков.
Args:
list1 (list): Первый список для сравнения.
list2 (list): Второй список для сравнения.
Returns:
bool: True если списки идентичны, иначе False.
"""
if len(list1) != len(list2):
return False
for item1, item2 in zip(list1, list2):

View File

@ -9,8 +9,10 @@ from locators.table_locators import TableLocators
from locators.button_locators import ButtonLocators
from elements.tooltip_button_element import TooltipButton
from data.roles_dict import roles_dict
from data.environment import host
from components.toolbar_component import ToolbarComponent
from components.table_component import TableComponent
from components.confirm_component import ConfirmComponent
from pages.base_page import BasePage
class SessionsTab(BasePage):
@ -31,6 +33,9 @@ class SessionsTab(BasePage):
self.toolbar = ToolbarComponent(page, "Сессия")
self.sessions_table = TableComponent(page)
self.delete_session_confirm = ConfirmComponent(page, " Отмена ", " Удалить ")
# Действия:
def get_rows_count(self) -> int:
"""Возвращает количество строк в таблице (без заголовка).
@ -71,6 +76,42 @@ class SessionsTab(BasePage):
button_locator = row_locator.locator(ButtonLocators.BUTTON_DELETE_SESSION)
return TooltipButton(self.page, button_locator, "delete_session_button")
def get_session_token(self) -> str:
"""Возвращает токен текущего пользователя.
Args:
Returns:
str: Токен текущего пользователя
Raises:
"""
return host.get_access_token()
def find_session_in_table(self, token: str) -> int:
"""Ищет сессию пользователя в таблице по выданному ему токену.
Args:
token (str): Токен пользователя
Returns:
int: Индекс строки или -1 если не найден
Raises:
AssertionError: Если таблица пуста.
"""
table_content = self.sessions_table.read(TableLocators.TABLE_WORK_AREA)
if len(table_content) == 0:
assert False, "The contents of the table are missing"
del table_content[0] # Удаляем заголовок
for row_index, session_info in enumerate(table_content):
if token in session_info:
return row_index
return -1
def scroll_sessions_table_up(self) -> None:
"""Прокручивает таблицу сессий вверх."""
@ -81,6 +122,46 @@ class SessionsTab(BasePage):
self.sessions_table.scroll_down(TableLocators.TABLE_SCROLL_CONTAINER)
# Проверки:
def check_delete_session_confirm_window(self):
""" Проверяет контент и возможность горизонтального скроллинга окна подтверждения удаления сессии. """
# Поиск в таблице сессий сроки для текущего пользователя
session_token = self.get_session_token()
row_index = self.find_session_in_table(session_token)
if row_index == -1:
assert False, "Session for this token has not been found"
# Найти кнопку удаления сессии и нажать на нее
delete_session_button = self.get_delete_session_button_from_row(row_index)
delete_session_button.click()
# Проверка открытия окна подтверждения с заголовком "Удаление"
title = "Удаление"
self.delete_session_confirm.check_title(
title,
f"Confirmation dialog window text '{title}' is missing"
)
# Проверка текста в окне подтверждения
confirm_message = f"Удалить сессию {session_token}?"
self.delete_session_confirm.check_text(
confirm_message,
"Confirmation dialog window text does not match what is expected"
)
# Проверка горизонтального скроллинга
is_scrollable_horizontally = self.delete_session_confirm.check_window_horizontal_scrolling()
assert is_scrollable_horizontally, "Should be horizontal scrolling"
self.delete_session_confirm.scroll_window_right()
self.page.wait_for_timeout(3000)
self.delete_session_confirm.scroll_window_left()
self.page.wait_for_timeout(2000)
# Нажать кнопку "Отмена"
self.delete_session_confirm.click_cancel_button()
def check_sessions_table_content(self, verify: bool = False) -> None:
"""Проверяет содержимое таблицы сессий.
@ -208,6 +289,33 @@ class SessionsTab(BasePage):
)
delete_button.check_tooltip_with_text(ButtonLocators.TOOLTIP, tooltip)
def should_be_session_in_table(self, token: str) -> None:
"""Проверяет наличие сессии пользователя в таблице.
Args:
token (str): Токен пользователя
Raises:
AssertionError: Если сессия не найдена.
"""
found = self.find_session_in_table(token)
if found == -1:
assert False, "Session for this token has not been found"
def should_not_be_session_in_table(self, token: str) -> None:
"""Проверяет отсутствие сессии пользователя в таблице.
Args:
token (str): Токен пользователя
Raises:
AssertionError: Если сессия найдена.
"""
found = self.find_session_in_table(token)
if found != -1:
assert False, "Session for this token has been found"
def verify_sessions_table_content(self, sessions_table: list) -> None:
"""Сверяет данные таблицы с данными из БД.

View File

@ -1,27 +1,25 @@
"""Модуль тестов вкладки 'Сеансы'.
Содержит тесты для проверки отображения и функциональности
элементов вкладки сеансов пользователей.
"""
from pages.login_page import LoginPage
from pages.main_page import MainPage
from pages.session_tab import SessionsTab
from pages.users_tab import UsersTab
import pytest
from pages.session_tab import SessionsTab
from pages.main_page import MainPage
from pages.login_page import LoginPage
from playwright.sync_api import Page
class TestSessionsTab:
"""Класс тестов для проверки вкладки 'Сеансы'."""
"""Набор тестов для вкладки 'Сеансы'.
Проверяет корректность отображения и функциональность элементов вкладки сеансов.
"""
@pytest.fixture(scope="function", autouse=True)
def setup(self, browser: Page):
"""Подготавливает тестовое окружение.
def setup(self, browser):
"""Фикстура для подготовки тестового окружения.
Args:
browser: Экземпляр страницы Playwright.
Выполняет:
1. Авторизацию в системе
2. Переход на вкладку 'Сеансы' через панель навигации
"""
# Авторизация в системе
login_page = LoginPage(browser)
login_page.do_login()
@ -35,14 +33,14 @@ class TestSessionsTab:
main_page.click_configuration_navigation_panel_item("Обслуживание и диагностика")
main_page.click_maintenance_navigation_panel_item("Сеансы")
def test_sessions_tab_content(self, browser):
"""Тест содержимого вкладки 'Сеансы'.
def test_sessions_tab_content(self, browser: Page):
"""Проверяет содержимое вкладки 'Сеансы'.
Args:
browser: Экземпляр страницы Playwright.
Проверяет:
1. Наличие и корректность тулбара
2. Наличие таблицы сеансов
3. Соответствие содержимого таблицы данным из БД
"""
# Инициализация страницы сеансов
sessions_tab = SessionsTab(browser)
@ -52,3 +50,123 @@ class TestSessionsTab:
# Проверка содержимого таблицы с верификацией данных из БД
sessions_tab.check_sessions_table_content(verify=True)
def test_sessions_table_row_highlighting(self, browser):
"""Тест содержимого вкладки 'Сеансы'.
Проверяет:
1. Наличие таблицы сеансов
2. Проверка подсветки строки при наведении на нее курсором
"""
# Инициализация страницы сеансов
sessions_tab = SessionsTab(browser)
# Проверка элементов интерфейса
sessions_tab.should_be_sessions_table()
# Получение количества строк в таблице без учета заголовка
rows_count = sessions_tab.get_rows_count()
# Проверка подсветки первой строки
sessions_tab.check_sessions_table_row_highlighting(0)
# Проверка подсветки последней строки строки (если в таблице более одной строки)
if rows_count > 1:
sessions_tab.check_sessions_table_row_highlighting(rows_count - 1)
def test_delete_session_confirm_window(self, browser):
"""Тест содержимого вкладки 'Сеансы'.
Проверяет:
1. Наличие таблицы сеансов
2. Проверка контента и возможности горизонтального скроллинга окна подтверждения удаления сессии
"""
# Инициализация страницы сеансов
sessions_tab = SessionsTab(browser)
# Проверка элементов интерфейса
sessions_tab.should_be_sessions_table()
# Проверка контента и скроллинга окна подтверждения удаления сессии
sessions_tab.check_delete_session_confirm_window()
#@pytest.mark.develop
def test_session_for_new_user(self, browser):
"""Тест содержимого вкладки 'Сеансы'.
Проверяет:
1. Создание нового пользователя
2. Вход нового пользователя в систему
3. Проверка наличия сессии нового пользователя
4. Выход нового пользователя из системы (logout)
5. Вход в систему первоначального пользователя
6. Проверка отсутствия сессии нового пользователя
7. Удаление нового пользователя
"""
user_data = {"name": "NewUser", "role": "Администратор", "password": "qwerty"}
mp = MainPage(browser)
ut = UsersTab(browser)
# Создание нового пользователя
mp.click_configuration_navigation_panel_item("Пользователи")
ut.open_add_user_window()
ut.add_new_user(user_data)
# Обновление списка пользователей (двойной клик - возможно баг?)
mp.click_configuration_navigation_panel_item("Пользователи")
mp.click_configuration_navigation_panel_item("Пользователи")
# Проверка наличия пользователя в таблице
ut.should_be_user_in_table(user_data["name"], user_data["role"])
# Вход в систему для нового пользователя
new_lp = LoginPage(browser)
new_lp.do_login(username=user_data["name"], password=user_data["password"])
# Инициализация главной страницы
new_mp = MainPage(browser)
# Открыть вкладку Сессии
new_mp.should_be_navigation_panel()
new_mp.click_main_navigation_panel_item("Настройки")
new_mp.click_configuration_navigation_panel_item("Обслуживание и диагностика")
new_mp.click_maintenance_navigation_panel_item("Сеансы")
# Инициализация страницы сеансов
st = SessionsTab(browser)
# Проверка элементов интерфейса
st.should_be_sessions_table()
# Проверка наличия записи о сессии текущего пользователя
session_token = st.get_session_token()
st.should_be_session_in_table(session_token)
# logout
new_mp.do_logout()
# Авторизация в системе предыдущего пользователя
prev_lp = LoginPage(browser)
prev_lp.do_login()
# Инициализация главной страницы
prev_mp = MainPage(browser)
# Открыть вкладку Сессии
prev_mp.should_be_navigation_panel()
prev_mp.click_main_navigation_panel_item("Настройки")
prev_mp.click_configuration_navigation_panel_item("Обслуживание и диагностика")
prev_mp.click_maintenance_navigation_panel_item("Сеансы")
# Проверка элементов интерфейса
st.should_be_sessions_table()
# Проверка отсутствия записи о сессии созданного пользователя после выхода из системы
st.should_not_be_session_in_table(session_token)
# Удаление созданного пользователя
prev_ut = UsersTab(browser)
prev_ut.open_edit_user_page_by_user(user_data["name"], user_data["role"])
prev_ut.delete_user(user_data["name"])