""" conftest.py — Configuration pytest globale SaaS Turf Prédictions IA — Sprint 8 QA Ticket: HRT-34 """ import os import asyncio import pytest from pathlib import Path from datetime import datetime # ============================================================ # Répertoires de sortie # ============================================================ REPORTS_DIR = Path("tests/reports") SCREENSHOTS_DIR = Path("tests/screenshots") for d in [REPORTS_DIR, SCREENSHOTS_DIR]: d.mkdir(parents=True, exist_ok=True) # ============================================================ # Variables d'environnement # ============================================================ BASE_URL = os.environ.get("APP_URL", "http://localhost:8792") # ============================================================ # Fixtures globales # ============================================================ @pytest.fixture(scope="session") def base_url(): return BASE_URL @pytest.fixture(scope="session") def event_loop(): """Event loop partagé pour les tests async de la session.""" policy = asyncio.get_event_loop_policy() loop = policy.new_event_loop() yield loop loop.close() @pytest.fixture(scope="session") def reports_dir(): return REPORTS_DIR @pytest.fixture(scope="session") def screenshots_dir(): return SCREENSHOTS_DIR # ============================================================ # Hook : screenshot automatique sur échec # ============================================================ @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): """Capture screenshot automatiquement sur tout test E2E en échec.""" outcome = yield report = outcome.get_result() if report.when == "call" and report.failed: # Récupérer la page Playwright si disponible dans les fixtures page = None for fixture_name in ("page", "context_page"): if fixture_name in item.funcargs: val = item.funcargs[fixture_name] if isinstance(val, tuple): page = val[0] # (page, browser_name) else: page = val break if page is not None: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") test_name = item.name.replace("/", "_").replace(":", "_") screenshot_path = SCREENSHOTS_DIR / f"FAIL_{test_name}_{timestamp}.png" try: # Playwright page.screenshot est synchrone dans les fixtures sync # Pour les fixtures async, on force la capture import asyncio as _asyncio if _asyncio.iscoroutinefunction(page.screenshot): loop = _asyncio.get_event_loop() loop.run_until_complete(page.screenshot(path=str(screenshot_path))) else: page.screenshot(path=str(screenshot_path)) report.sections.append( ("Screenshot", f"Sauvegardé : {screenshot_path}") ) except Exception as e: report.sections.append( ("Screenshot Error", f"Impossible de capturer : {e}") ) # ============================================================ # Marqueurs personnalisés # ============================================================ def pytest_configure(config): config.addinivalue_line("markers", "e2e: Tests End-to-End Playwright") config.addinivalue_line("markers", "load: Tests de charge Locust") config.addinivalue_line("markers", "security: Tests de sécurité") config.addinivalue_line( "markers", "smoke: Tests rapides de smoke (sans infra complète)" ) config.addinivalue_line("markers", "beta: Tests spécifiques beta fermée") config.addinivalue_line( "markers", "requires_billing: Nécessite HRT-31 (Billing Stripe)" ) config.addinivalue_line( "markers", "requires_infra: Nécessite HRT-33 (infra staging)" )