paperhawk / tests /e2e_screenshot /conftest.py
Nándorfi Vince
Initial paperhawk push to HF Space (LFS for binaries)
7ff7119
raw
history blame
4.72 kB
"""E2E Playwright fixtures: Streamlit dev-server + Chromium browser.
A `prototype-agentic/docs/prototype-agentic-tesztek/` 72 manuális screenshot-os
tesztet automatizáljuk. Per teszt-eset:
- 5 tab full-page screenshot (görgetett tartalom)
- chat-szekvencia 4-5 kérdés-válasz JSON-be mentve
- DOCX letöltés + text-extract
- AI-validáció külön Claude-hívással (lásd `ai_validator.py`)
A Streamlit szervert egy session-fixture indítja a 8520-as porton (LLM_PROFILE
default=claude, ha .env-ben be van állítva — egyébként dummy fallback).
"""
from __future__ import annotations
import os
import socket
import subprocess
import sys
import time
from pathlib import Path
import pytest
import requests
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
SNAPSHOTS_DIR = Path(__file__).resolve().parent / "snapshots"
STREAMLIT_PORT = 8520
STREAMLIT_URL = f"http://localhost:{STREAMLIT_PORT}"
# A `.env`-ből betöltjük az ANTHROPIC_API_KEY-t és a többi env-változót
# úgy, hogy a pytest folyamatban is rendelkezésre álljanak (az AI-validátor
# Claude vision-API-t hív, ami az env-kulcsot használja). A python-dotenv
# import-ja optional — ha nincs telepítve, manuális parsing.
def _load_env_file() -> None:
env_path = PROJECT_ROOT / ".env"
if not env_path.exists():
return
try:
from dotenv import load_dotenv
load_dotenv(env_path)
return
except ImportError:
pass
# Manuális fallback
for line in env_path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, _, value = line.partition("=")
key = key.strip()
value = value.strip().strip('"').strip("'")
if key and key not in os.environ:
os.environ[key] = value
_load_env_file()
def _port_in_use(port: int) -> bool:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
return s.connect_ex(("127.0.0.1", port)) == 0
def _wait_for_health(url: str, timeout: float = 30.0) -> None:
deadline = time.time() + timeout
while time.time() < deadline:
try:
r = requests.get(f"{url}/_stcore/health", timeout=2)
if r.ok:
return
except Exception:
pass
time.sleep(0.5)
raise TimeoutError(f"Streamlit nem indult el {timeout}s alatt: {url}")
@pytest.fixture(scope="session")
def streamlit_server():
"""Streamlit dev-server session-fixture (port 8520).
Ha a port már használatban van (pl. user `make dev` futtat), akkor a
fixture nem indít újat — a futó server-en teszteljük.
"""
SNAPSHOTS_DIR.mkdir(exist_ok=True)
if _port_in_use(STREAMLIT_PORT):
print(f"[streamlit_server] port {STREAMLIT_PORT} már használatban, skip indítás")
_wait_for_health(STREAMLIT_URL, timeout=5)
yield STREAMLIT_URL
return
env = os.environ.copy()
# Ha az LLM_PROFILE nincs beállítva, a .env-ből veszi (claude default)
proc = subprocess.Popen(
[
sys.executable, "-m", "streamlit", "run",
str(PROJECT_ROOT / "app" / "main.py"),
"--server.headless=true",
f"--server.port={STREAMLIT_PORT}",
"--browser.gatherUsageStats=false",
],
cwd=str(PROJECT_ROOT),
env=env,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
try:
_wait_for_health(STREAMLIT_URL, timeout=30)
print(f"[streamlit_server] elindult: {STREAMLIT_URL}")
yield STREAMLIT_URL
finally:
proc.terminate()
try:
proc.wait(timeout=5)
except subprocess.TimeoutExpired:
proc.kill()
@pytest.fixture(scope="session")
def browser():
"""Chromium session-fixture (headless=True, viewport=1600x1000).
A `full_page=True` screenshot-tal a teljes scrollable tartalom rögzítve,
nem csak a viewport — paritás a `prototype-agentic-tesztek/` manuális
screenshotokkal (ahol a user maga görgette le a UI-t).
"""
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context(viewport={"width": 1600, "height": 1000})
yield context
context.close()
browser.close()
@pytest.fixture
def page(browser, streamlit_server):
"""Per-teszt page fixture: új tab, alapértelmezett URL navigálás."""
page = browser.new_page()
page.goto(streamlit_server)
page.wait_for_load_state("networkidle", timeout=30000)
yield page
page.close()