Initial commit: Canteen Asset Geolocation Tool v2
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
"""Fixtures for Playwright frontend E2E tests.
|
||||
|
||||
Architecture:
|
||||
- Each test gets an isolated temp SQLite DB.
|
||||
- A FastAPI uvicorn server runs on a random port in a background thread.
|
||||
- CANTEEN_SKIP_AUTH=1 skips auth middleware so Playwright doesn't need real tokens.
|
||||
- Playwright launches system Google Chrome (Ubuntu 26.04 can't install bundled browsers,
|
||||
and Chrome 148 SIGTRAPs with certain --disable-features flags; ignore_default_args
|
||||
workaround applied).
|
||||
- Viewport: iPhone 14 (390x844), Geolocation: Orlando, FL.
|
||||
"""
|
||||
|
||||
# Chrome 148 on Ubuntu 26.04 (kernel 7.0) SIGTRAPs when Playwright's default
|
||||
# --disable-features and related flags are passed. Ignoring these defaults
|
||||
# allows Chrome to launch cleanly with DevTools protocol.
|
||||
CHROME_IGNORE_DEFAULTS = [
|
||||
'--disable-field-trial-config',
|
||||
'--disable-background-networking',
|
||||
'--disable-background-timer-throttling',
|
||||
'--disable-breakpad',
|
||||
'--disable-client-side-phishing-detection',
|
||||
'--disable-default-apps',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-extensions',
|
||||
'--disable-hang-monitor',
|
||||
'--disable-ipc-flooding-protection',
|
||||
'--disable-popup-blocking',
|
||||
'--disable-prompt-on-repost',
|
||||
'--disable-renderer-backgrounding',
|
||||
'--disable-sync',
|
||||
'--enable-automation',
|
||||
]
|
||||
|
||||
import importlib
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import uvicorn
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
# Ensure project root is on path
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
# Global env: skip auth for all tests
|
||||
os.environ["CANTEEN_SKIP_AUTH"] = "1"
|
||||
|
||||
# ── helpers ────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def _find_free_port() -> int:
|
||||
"""Find an available TCP port."""
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.bind(("", 0))
|
||||
return s.getsockname()[1]
|
||||
|
||||
|
||||
# ── fixtures ────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def browser():
|
||||
"""Launch system Chromium once per test session."""
|
||||
pw = sync_playwright().start()
|
||||
browser = pw.chromium.launch(
|
||||
executable_path="/usr/bin/google-chrome-stable",
|
||||
headless=True,
|
||||
args=["--no-sandbox", "--disable-gpu"],
|
||||
ignore_default_args=CHROME_IGNORE_DEFAULTS,
|
||||
)
|
||||
yield browser
|
||||
browser.close()
|
||||
pw.stop()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_db_path():
|
||||
"""Create an isolated temp SQLite DB for each test."""
|
||||
fd, path = tempfile.mkstemp(suffix=".db", prefix="canteen_frontend_test_")
|
||||
os.close(fd)
|
||||
os.environ["CANTEEN_DB_PATH"] = path
|
||||
yield path
|
||||
# Cleanup DB and WAL/SHM/journal files
|
||||
for suffix in ("", "-shm", "-wal", "-journal"):
|
||||
p = Path(path + suffix)
|
||||
if p.exists():
|
||||
p.unlink()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def live_server(test_db_path):
|
||||
"""Start FastAPI + uvicorn on a random port in a background thread.
|
||||
|
||||
Returns the base URL (e.g. 'http://127.0.0.1:12345').
|
||||
"""
|
||||
port = _find_free_port()
|
||||
os.environ["CANTEEN_PORT"] = str(port)
|
||||
|
||||
# Reload the server module so DB_PATH picks up the current
|
||||
# CANTEEN_DB_PATH (module-level constant read at import time).
|
||||
import server
|
||||
importlib.reload(server)
|
||||
app = server.app
|
||||
|
||||
t = threading.Thread(
|
||||
target=uvicorn.run,
|
||||
kwargs={
|
||||
"app": "server:app",
|
||||
"host": "127.0.0.1",
|
||||
"port": port,
|
||||
"log_level": "error",
|
||||
},
|
||||
daemon=True,
|
||||
)
|
||||
t.start()
|
||||
|
||||
base_url = f"http://127.0.0.1:{port}"
|
||||
|
||||
# Wait for server to be ready
|
||||
deadline = time.time() + 10
|
||||
import urllib.request
|
||||
|
||||
while time.time() < deadline:
|
||||
try:
|
||||
urllib.request.urlopen(f"{base_url}/", timeout=1)
|
||||
break
|
||||
except Exception:
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
raise RuntimeError(f"Server did not start on {base_url} within 10s")
|
||||
|
||||
yield base_url
|
||||
# Thread is daemon, will exit when test process ends
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def page(browser, live_server):
|
||||
"""Create a Playwright page pointed at the live server.
|
||||
|
||||
iPhone 14 viewport, Orlando FL geolocation, geolocation permission granted.
|
||||
"""
|
||||
context = browser.new_context(
|
||||
viewport={"width": 390, "height": 844},
|
||||
geolocation={"latitude": 28.3852, "longitude": -81.5639},
|
||||
permissions=["geolocation"],
|
||||
)
|
||||
page = context.new_page()
|
||||
page.goto(live_server)
|
||||
yield page
|
||||
context.close()
|
||||
Reference in New Issue
Block a user