""" Smoke tests for production — read-only, no clicks. Run against a live app: pytest api/tests/qa/test_qa_smoke_prod.py --base-url http://localhost:5003 """ import time import urllib.request import json import pytest from playwright.sync_api import sync_playwright pytestmark = pytest.mark.smoke PAGES = ["/", "/logs", "/mappings", "/missing-skus", "/settings"] def _app_is_reachable(base_url: str) -> bool: """Quick check if the app is reachable.""" try: urllib.request.urlopen(f"{base_url}/health", timeout=3) return True except Exception: return False @pytest.fixture(scope="module", autouse=True) def _require_app(base_url): """Skip all smoke tests if the app is not running.""" if not _app_is_reachable(base_url): pytest.skip(f"App not reachable at {base_url} — start the app first") PAGE_TITLES = { "/": "Panou de Comanda", "/logs": "Jurnale Import", "/mappings": "Mapari SKU", "/missing-skus": "SKU-uri Lipsa", "/settings": "Setari", } @pytest.fixture(scope="module") def browser(): with sync_playwright() as p: b = p.chromium.launch(headless=True) yield b b.close() # --------------------------------------------------------------------------- # test_page_loads # --------------------------------------------------------------------------- @pytest.mark.parametrize("path", PAGES) def test_page_loads(browser, base_url, screenshots_dir, path): """Each page returns HTTP 200 and loads without crashing.""" page = browser.new_page() try: response = page.goto(f"{base_url}{path}", wait_until="domcontentloaded", timeout=15_000) assert response is not None, f"No response for {path}" assert response.status == 200, f"Expected 200, got {response.status} for {path}" safe_name = path.strip("/").replace("/", "_") or "dashboard" screenshot_path = screenshots_dir / f"smoke_{safe_name}.png" page.screenshot(path=str(screenshot_path)) finally: page.close() # --------------------------------------------------------------------------- # test_page_titles # --------------------------------------------------------------------------- @pytest.mark.parametrize("path", PAGES) def test_page_titles(browser, base_url, path): """Each page has the correct h4 heading text.""" expected = PAGE_TITLES[path] page = browser.new_page() try: page.goto(f"{base_url}{path}", wait_until="domcontentloaded", timeout=15_000) h4 = page.locator("h4").first actual = h4.inner_text().strip() assert actual == expected, f"{path}: expected h4='{expected}', got '{actual}'" finally: page.close() # --------------------------------------------------------------------------- # test_no_console_errors # --------------------------------------------------------------------------- @pytest.mark.parametrize("path", PAGES) def test_no_console_errors(browser, base_url, path): """No console.error events on any page.""" errors = [] page = browser.new_page() try: page.on("console", lambda msg: errors.append(msg.text) if msg.type == "error" else None) page.goto(f"{base_url}{path}", wait_until="networkidle", timeout=15_000) finally: page.close() assert errors == [], f"Console errors on {path}: {errors}" # --------------------------------------------------------------------------- # test_api_health_json # --------------------------------------------------------------------------- def test_api_health_json(base_url): """GET /health returns valid JSON with 'oracle' key.""" with urllib.request.urlopen(f"{base_url}/health", timeout=10) as resp: data = json.loads(resp.read().decode()) assert "oracle" in data, f"/health JSON missing 'oracle' key: {data}" # --------------------------------------------------------------------------- # test_api_dashboard_orders_json # --------------------------------------------------------------------------- def test_api_dashboard_orders_json(base_url): """GET /api/dashboard/orders returns valid JSON with 'orders' key.""" with urllib.request.urlopen(f"{base_url}/api/dashboard/orders", timeout=10) as resp: data = json.loads(resp.read().decode()) assert "orders" in data, f"/api/dashboard/orders JSON missing 'orders' key: {data}" # --------------------------------------------------------------------------- # test_response_time # --------------------------------------------------------------------------- @pytest.mark.parametrize("path", PAGES) def test_response_time(browser, base_url, path): """Each page loads in under 10 seconds.""" page = browser.new_page() try: start = time.monotonic() page.goto(f"{base_url}{path}", wait_until="domcontentloaded", timeout=15_000) elapsed = time.monotonic() - start finally: page.close() assert elapsed < 10, f"{path} took {elapsed:.2f}s (limit: 10s)"