Files
e2e_Pets/tests/conftest.py
VolandSZ f511102289 feat: customize HTML report Environment block and title
conftest.py (корневой, новый):
  - pytest_sessionstart — заменяет блок Environment:
    Проект / Портал / Окружение / Браузер / Тестировщик
  - pytest_html_report_title — заголовок 'Отчёт E2E тестирования'
  - Использует config.stash[metadata_key] (правильный API
    для pytest-metadata 3.x + pytest-html 4.x)
  - Fallback на config._metadata для старых версий

tests/conftest.py:
  - Убраны дублирующие хуки (перенесены в корневой conftest)

Переменные окружения для кастомизации:
  TEST_ENV     — название среды (по умолчанию 'Production')
  TESTER_NAME  — имя тестировщика (по умолчанию 'QA Team')
2026-09-02 10:42:51 +03:00

135 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()
# ---------------------------------------------------------------------------
# Хук для отображения читаемых имён шагов в pytest-html отчёте
# ---------------------------------------------------------------------------
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
"""
Заменяет технический путь к тесту в колонке Test отчёта pytest-html
на docstring метода (например, 'Шаг 1: Открыть главную страницу').
Если docstring отсутствует — оставляет оригинальный nodeid.
"""
outcome = yield
report = outcome.get_result()
# Берём docstring тестовой функции
doc = getattr(item.function, "__doc__", None)
if doc:
description = doc.strip().splitlines()[0] # только первая строка
report.nodeid = description