Реструктурированы тесты под иерархию Проект > Личный кабинет > БП > Шаги. Архитектура: - Бизнес-процесс = Python-класс, шаг = метод класса - Шаги именуются test_s01_*, test_s02_* и т.д. - БП именуются BP01_, BP02_ и т.д., каждый в своей папке Передача данных: - browser_page (scope=class) — один браузер на весь БП, авторизация 1 раз - bp_state (scope=class) — словарь для передачи данных между шагами одного БП - shared_state (scope=session) — словарь для передачи между разными БП Зависимости шагов (pytest-dependency): - Если шаг N упал, шаги N+1... получают статус SKIPPED автоматически - Имена зависимостей: BP01::s01, BP01::s02 и т.д. Изменения файлов: - tests/conftest.py: добавлены browser_page и bp_state, вынесены вспомогательные функции _get_role и _login, сохранена role_page (deprecated) для обратной совместимости - tests/01_vetdept/conftest.py: новый, содержит shared_state - tests/01_vetdept/BP01_navigation/: новая папка с тестом навигации - tests/01_vetdept/001_login_test.py: удалён (переписан в BP01) - requirements.txt: добавлен pytest-dependency - pytest.ini: добавлен маркер dependency
111 lines
4.6 KiB
Python
111 lines
4.6 KiB
Python
import os
|
||
# pyrefly: ignore [missing-import]
|
||
import pytest
|
||
# pyrefly: ignore [missing-import]
|
||
from dotenv import load_dotenv
|
||
# pyrefly: ignore [missing-import]
|
||
from playwright.sync_api import Page
|
||
|
||
# Загружаем переменные из .env (файл находится в корне проекта)
|
||
load_dotenv()
|
||
|
||
# Базовый URL портала — берётся из .env или дефолт
|
||
BASE_URL = os.getenv("BASE_URL", "https://vet.goznak.ru")
|
||
|
||
# Учётные данные для разных ролей — берутся из .env
|
||
CREDENTIALS = {
|
||
"vetdept": {
|
||
"login": os.getenv("VETDEPT_LOGIN"),
|
||
"password": os.getenv("VETDEPT_PASSWORD"),
|
||
},
|
||
"vetstation": {
|
||
"login": os.getenv("VETSTATION_LOGIN"),
|
||
"password": os.getenv("VETSTATION_PASSWORD"),
|
||
},
|
||
"prefecture": {
|
||
"login": os.getenv("PREFECTURE_LOGIN"),
|
||
"password": os.getenv("PREFECTURE_PASSWORD"),
|
||
},
|
||
}
|
||
|
||
|
||
def _get_role(request) -> str:
|
||
"""Определяет роль теста по pytest-маркеру."""
|
||
for marker in request.node.iter_markers():
|
||
if marker.name in CREDENTIALS:
|
||
return marker.name
|
||
pytest.fail("ОШИБКА: У теста нет маркера роли (например, @pytest.mark.vetdept)!")
|
||
|
||
|
||
def _login(page: Page, role: str) -> None:
|
||
"""Выполняет авторизацию на портале под указанной ролью."""
|
||
creds = CREDENTIALS[role]
|
||
if not creds["login"] or not creds["password"]:
|
||
pytest.fail(
|
||
f"ОШИБКА: Переменные окружения для роли '{role}' не заданы. "
|
||
f"Скопируй .env.example в .env и заполни данные."
|
||
)
|
||
print(f"\n[АВТОЛОГИН] Вход под ролью: {role}")
|
||
page.goto(BASE_URL)
|
||
page.get_by_role("textbox", name="Email").fill(creds["login"])
|
||
page.get_by_role("textbox", name="Введите пароль").fill(creds["password"])
|
||
page.get_by_role("button", name="Войти в аккаунт").click()
|
||
page.wait_for_selector("#app", timeout=10000)
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Фикстура для бизнес-процессов (scope=class)
|
||
# Один браузер на весь класс — авторизация выполняется один раз.
|
||
# Создаёт собственный context и page, чтобы избежать ScopeMismatch
|
||
# с function-scoped page фикстурой pytest-playwright.
|
||
# ---------------------------------------------------------------------------
|
||
|
||
@pytest.fixture(scope="class")
|
||
def browser_page(browser, request):
|
||
"""
|
||
Авторизованная страница с жизненным циклом на весь класс (бизнес-процесс).
|
||
Все шаги внутри класса работают в одной вкладке браузера.
|
||
"""
|
||
role = _get_role(request)
|
||
context = browser.new_context()
|
||
page = context.new_page()
|
||
_login(page, role)
|
||
yield page
|
||
context.close()
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Контейнер для передачи данных между шагами одного БП (scope=class)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
@pytest.fixture(scope="class")
|
||
def bp_state() -> dict:
|
||
"""
|
||
Словарь для передачи данных между шагами одного бизнес-процесса.
|
||
Создаётся при старте класса, уничтожается после его завершения.
|
||
|
||
Пример:
|
||
def test_s01_create(self, browser_page, bp_state):
|
||
bp_state["claim_id"] = "12345"
|
||
|
||
def test_s02_verify(self, browser_page, bp_state):
|
||
assert bp_state["claim_id"] == "12345"
|
||
"""
|
||
return {}
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Устаревшая фикстура — оставлена для обратной совместимости
|
||
# Используй browser_page для новых тестов
|
||
# ---------------------------------------------------------------------------
|
||
|
||
@pytest.fixture(scope="function")
|
||
def role_page(page: Page, request):
|
||
"""
|
||
[DEPRECATED] Авторизованная страница с жизненным циклом на один тест.
|
||
Для новых тестов используй browser_page (scope=class).
|
||
"""
|
||
role = _get_role(request)
|
||
_login(page, role)
|
||
yield page
|
||
page.context.clear_cookies() |