""" E2E verification: Dashboard page against the live app (localhost:5003). Run with: python -m pytest api/tests/e2e/test_dashboard_live.py -v --headed This tests the LIVE app, not a test instance. Requires the app to be running. """ import pytest from playwright.sync_api import sync_playwright, Page, expect BASE_URL = "http://localhost:5003" @pytest.fixture(scope="module") def browser_page(): """Launch browser and yield a page connected to the live app.""" with sync_playwright() as p: browser = p.chromium.launch(headless=True) context = browser.new_context(viewport={"width": 1280, "height": 900}) page = context.new_page() yield page browser.close() class TestDashboardPageLoad: """Verify dashboard page loads and shows expected structure.""" def test_dashboard_loads(self, browser_page: Page): browser_page.goto(f"{BASE_URL}/") browser_page.wait_for_load_state("networkidle") expect(browser_page.locator("h4")).to_contain_text("Panou de Comanda") def test_sync_control_visible(self, browser_page: Page): expect(browser_page.locator("#btnStartSync")).to_be_visible() expect(browser_page.locator("#syncStatusBadge")).to_be_visible() def test_last_sync_card_populated(self, browser_page: Page): """The lastSyncBody should show data from previous runs.""" last_sync_date = browser_page.locator("#lastSyncDate") expect(last_sync_date).to_be_visible() text = last_sync_date.text_content() assert text and text != "-", f"Expected last sync date to be populated, got: '{text}'" def test_last_sync_imported_count(self, browser_page: Page): imported_el = browser_page.locator("#lastSyncImported") text = imported_el.text_content() count = int(text) if text and text.isdigit() else 0 assert count >= 0, f"Expected imported count >= 0, got: {text}" def test_last_sync_status_badge(self, browser_page: Page): status_el = browser_page.locator("#lastSyncStatus .badge") expect(status_el).to_be_visible() text = status_el.text_content() assert text in ("completed", "running", "failed"), f"Unexpected status: {text}" class TestDashboardOrdersTable: """Verify orders table displays data from SQLite.""" def test_orders_table_has_rows(self, browser_page: Page): """Dashboard should show orders from previous sync runs.""" browser_page.goto(f"{BASE_URL}/") browser_page.wait_for_load_state("networkidle") # Wait for the orders to load (async fetch) browser_page.wait_for_timeout(2000) rows = browser_page.locator("#dashOrdersBody tr") count = rows.count() assert count > 0, "Expected at least 1 order row in dashboard table" def test_orders_count_badges(self, browser_page: Page): """Filter badges should show counts.""" all_count = browser_page.locator("#dashCountAll").text_content() assert all_count and int(all_count) > 0, f"Expected total count > 0, got: {all_count}" def test_first_order_has_columns(self, browser_page: Page): """First row should have order number, date, customer, etc.""" first_row = browser_page.locator("#dashOrdersBody tr").first cells = first_row.locator("td") assert cells.count() >= 6, f"Expected at least 6 columns, got: {cells.count()}" # Order number should be a code element order_code = first_row.locator("td code").first expect(order_code).to_be_visible() def test_filter_imported(self, browser_page: Page): """Click 'Importate' filter and verify table updates.""" browser_page.locator("#dashFilterBtns button", has_text="Importate").click() browser_page.wait_for_timeout(1000) imported_count = browser_page.locator("#dashCountImported").text_content() if imported_count and int(imported_count) > 0: rows = browser_page.locator("#dashOrdersBody tr") assert rows.count() > 0, "Expected imported orders to show" # All visible rows should have 'Importat' badge badges = browser_page.locator("#dashOrdersBody .badge.bg-success") assert badges.count() > 0, "Expected green 'Importat' badges" def test_filter_all_reset(self, browser_page: Page): """Click 'Toate' to reset filter.""" browser_page.locator("#dashFilterBtns button", has_text="Toate").click() browser_page.wait_for_timeout(1000) rows = browser_page.locator("#dashOrdersBody tr") assert rows.count() > 0, "Expected orders after resetting filter" class TestDashboardOrderDetail: """Verify order detail modal opens and shows data.""" def test_click_order_opens_modal(self, browser_page: Page): browser_page.goto(f"{BASE_URL}/") browser_page.wait_for_load_state("networkidle") browser_page.wait_for_timeout(2000) # Click the first order row first_row = browser_page.locator("#dashOrdersBody tr").first first_row.click() browser_page.wait_for_timeout(1500) # Modal should be visible modal = browser_page.locator("#orderDetailModal") expect(modal).to_be_visible() # Order number should be populated order_num = browser_page.locator("#detailOrderNumber").text_content() assert order_num and order_num != "#", f"Expected order number in modal, got: {order_num}" def test_modal_shows_customer(self, browser_page: Page): customer = browser_page.locator("#detailCustomer").text_content() assert customer and customer not in ("...", "-"), f"Expected customer name, got: {customer}" def test_modal_shows_items(self, browser_page: Page): items_rows = browser_page.locator("#detailItemsBody tr") assert items_rows.count() > 0, "Expected at least 1 item in order detail" def test_close_modal(self, browser_page: Page): browser_page.locator("#orderDetailModal .btn-close").click() browser_page.wait_for_timeout(500) class TestDashboardAPIEndpoints: """Verify API endpoints return expected data.""" def test_api_dashboard_orders(self, browser_page: Page): response = browser_page.request.get(f"{BASE_URL}/api/dashboard/orders") assert response.ok, f"API returned {response.status}" data = response.json() assert "orders" in data, "Expected 'orders' key in response" assert "counts" in data, "Expected 'counts' key in response" assert len(data["orders"]) > 0, "Expected at least 1 order" def test_api_sync_status(self, browser_page: Page): response = browser_page.request.get(f"{BASE_URL}/api/sync/status") assert response.ok data = response.json() assert "status" in data assert "stats" in data def test_api_sync_history(self, browser_page: Page): response = browser_page.request.get(f"{BASE_URL}/api/sync/history?per_page=5") assert response.ok data = response.json() assert "runs" in data assert len(data["runs"]) > 0, "Expected at least 1 sync run" def test_api_missing_skus(self, browser_page: Page): response = browser_page.request.get(f"{BASE_URL}/api/validate/missing-skus") assert response.ok data = response.json() assert "missing_skus" in data