"""E2E test fixtures: live uvicorn server for Playwright.""" from __future__ import annotations import os import socket import threading import time import httpx import pytest import uvicorn def _free_port() -> int: with socket.socket() as s: s.bind(("127.0.0.1", 0)) return s.getsockname()[1] @pytest.fixture(scope="session") def live_server(tmp_path_factory): """Start the FastAPI app on a random port in a background thread.""" # Isolated data dir for E2E session data_dir = tmp_path_factory.mktemp("imptune_e2e_data") # Patch config module so the app uses the temp dir import imptune.config as cfg cfg.DATA_DIR = str(data_dir) cfg.DB_PATH = str(data_dir / "imptune.db") cfg.DRIVERS_DIR = str(data_dir / "drivers") cfg.ICONS_DIR = str(data_dir / "icons") os.makedirs(cfg.DRIVERS_DIR, exist_ok=True) os.makedirs(cfg.ICONS_DIR, exist_ok=True) # Plain HTTP loopback server — Secure-flagged cookies would be dropped # inconsistently across browser engines, so disable that flag for E2E. cfg.COOKIE_SECURE = False # Also set env var so lifespan handler picks up correct dirs os.environ["DATA_DIR"] = str(data_dir) # Initialize DB against the tmp dir from imptune.db.database import init_db init_db() from imptune.main import app port = _free_port() config = uvicorn.Config(app, host="127.0.0.1", port=port, log_level="error") server = uvicorn.Server(config) thread = threading.Thread(target=server.run, daemon=True) thread.start() base_url = f"http://127.0.0.1:{port}" # Readiness poll via /health (up to 5 s) deadline = time.time() + 5.0 while time.time() < deadline: try: r = httpx.get(f"{base_url}/health", timeout=0.5) if r.status_code == 200: break except Exception: pass time.sleep(0.1) else: raise RuntimeError("live_server did not become ready within 5 s") yield base_url server.should_exit = True thread.join(timeout=2.0) @pytest.fixture(scope="session") def _e2e_owner_key(live_server): """One Owner shared by every E2E browser context. E2E specs cover unrelated UI (theme, i18n, port autofill); without a pre-existing cookie each fresh Playwright context would be treated as a first-time visitor and blocked by the onboarding modal (a real that intercepts all pointer events until dismissed). """ from imptune.services.session import COOKIE_NAME resp = httpx.get(f"{live_server}/") return resp.cookies[COOKIE_NAME] @pytest.fixture(autouse=True) def _seed_owner_cookie(page, live_server, _e2e_owner_key): """Pre-seed the owner cookie so the first-visit modal never appears in specs unrelated to onboarding.""" from urllib.parse import urlparse from imptune.services.session import COOKIE_NAME hostname = urlparse(live_server).hostname page.context.add_cookies( [{"name": COOKIE_NAME, "value": _e2e_owner_key, "domain": hostname, "path": "/"}] )