From 4e9bd9b1ab8e09d54b05acb01c015e3a67aa175d Mon Sep 17 00:00:00 2001 From: Kawa Date: Mon, 13 Apr 2026 10:55:49 +0200 Subject: [PATCH] chore(09-02): add Playwright dev deps and e2e package scaffolding - Add pytest-playwright and playwright to requirements-dev.txt - Create tests/e2e/__init__.py package marker - Create tests/e2e/conftest.py with session-scoped live_server fixture --- requirements-dev.txt | 2 ++ tests/e2e/__init__.py | 0 tests/e2e/conftest.py | 70 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 tests/e2e/__init__.py create mode 100644 tests/e2e/conftest.py diff --git a/requirements-dev.txt b/requirements-dev.txt index 719f860..29a0eb1 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,2 +1,4 @@ pytest>=8.0 httpx>=0.27 +pytest-playwright +playwright diff --git a/tests/e2e/__init__.py b/tests/e2e/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py new file mode 100644 index 0000000..c3ac40e --- /dev/null +++ b/tests/e2e/conftest.py @@ -0,0 +1,70 @@ +"""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) + + # 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)