"""Frontend E2E tests — asset list, search, filter, detail.""" import pytest import requests def _login(page): """Helper: login as admin.""" page.locator("#loginUsername").fill("admin") page.locator("#loginPassword").fill("changeme") page.locator("button:has-text('Sign In')").click() page.wait_for_selector("#loginOverlay", state="hidden", timeout=5000) @pytest.mark.frontend def test_asset_list_shows_created_asset(page, live_server): """Assets created via API appear in the Assets tab.""" _login(page) # Create an asset via API resp = requests.post( f"{live_server}/api/assets", json={ "machine_id": "TEST-001", "name": "Test Espresso Machine", "category": "Appliances", "status": "active", }, ) assert resp.status_code == 201 # Navigate to Assets tab page.locator(".tab-btn[data-tab='tabAssets']").click() page.wait_for_selector("#tabAssets.active", timeout=3000) # Wait for the asset list to render page.wait_for_selector(".asset-item", timeout=5000) assert page.locator(".asset-item").count() >= 1 assert page.locator(".ai-name:has-text('Test Espresso Machine')").is_visible() @pytest.mark.frontend def test_asset_list_empty_state(page, live_server): """Assets tab shows empty state when no assets exist.""" _login(page) page.locator(".tab-btn[data-tab='tabAssets']").click() page.wait_for_selector("#tabAssets.active", timeout=3000) # Should show empty state (no assets seeded into fresh DB). # loadAssets() runs async — give it time to fetch and render. page.wait_for_timeout(2000) has_empty = page.locator(".empty-state").count() > 0 has_items = page.locator(".asset-item").count() > 0 assert not has_items, f"Fresh DB has {page.locator('.asset-item').count()} assets unexpectedly" assert has_empty, "Empty state should appear on fresh DB" @pytest.mark.frontend def test_asset_search_filters_by_name(page, live_server): """Search input filters assets by name.""" _login(page) # Create two assets via API for mid, name in [("SRCH-001", "Alpha Blender"), ("SRCH-002", "Beta Oven")]: requests.post( f"{live_server}/api/assets", json={"machine_id": mid, "name": name, "category": "Appliances"}, ) # Navigate to Assets page.locator(".tab-btn[data-tab='tabAssets']").click() page.wait_for_selector("#tabAssets.active", timeout=3000) page.wait_for_selector(".asset-item", timeout=5000) # Search for "Alpha" — use #assetSearch to avoid ambiguity with # customer and activity search inputs that share the .input-field class. page.locator("#assetSearch").fill("Alpha") page.wait_for_timeout(500) # debounce items = page.locator(".asset-item") assert items.count() == 1 assert page.locator(".ai-name:has-text('Alpha Blender')").is_visible() @pytest.mark.frontend def test_asset_category_filter(page, live_server): """Category filter pills filter assets.""" _login(page) # Create assets in different categories requests.post( f"{live_server}/api/assets", json={"machine_id": "FILT-001", "name": "Chair", "category": "Furniture"}, ) requests.post( f"{live_server}/api/assets", json={"machine_id": "FILT-002", "name": "Fridge", "category": "Appliances"}, ) # Navigate to Assets page.locator(".tab-btn[data-tab='tabAssets']").click() page.wait_for_selector("#tabAssets.active", timeout=3000) page.wait_for_selector(".asset-item", timeout=5000) # wait_for_selector returns on first match — give the list time to fully render page.wait_for_timeout(500) assert page.locator(".asset-item").count() == 2 # Click "Furniture" filter pill page.locator(".pill:has-text('Furniture')").click() page.wait_for_timeout(300) assert page.locator(".asset-item").count() == 1 assert page.locator(".ai-name:has-text('Chair')").is_visible() @pytest.mark.frontend def test_asset_detail_view(page, live_server): """Clicking an asset opens detail panel with correct info.""" _login(page) requests.post( f"{live_server}/api/assets", json={ "machine_id": "DETAIL-001", "name": "Detail Test Asset", "description": "A test asset for detail view", "category": "Equipment", "status": "active", }, ) page.locator(".tab-btn[data-tab='tabAssets']").click() page.wait_for_selector("#tabAssets.active", timeout=3000) page.wait_for_selector(".asset-item", timeout=5000) # Click the asset — viewAsset() calls showDetailView(), which # makes #assetsDetailView visible (not .scan-result — that's for # barcode scans). page.locator(".ai-name:has-text('Detail Test Asset')").click() page.wait_for_selector("#assetsDetailView", state="visible", timeout=5000) # Verify detail content assert page.locator("#detailName:has-text('Detail Test Asset')").is_visible()