import { expect, test } from "@playwright/test"; import { mockApi, seedAuthenticatedState } from "./support/network.js"; function json(body, status = 200) { return { status, contentType: "application/json", body: JSON.stringify(body), }; } async function primeSuperuserSession(page) { const token = "superuser-system-status-token"; await seedAuthenticatedState(page, token); await page.goto("/login"); await page.evaluate(async (sessionToken) => { const sessionModule = await import("/src/components/session/token/SessionUser.vue"); window.localStorage.setItem("token", sessionToken); sessionModule.SessionUser.token.value = sessionToken; sessionModule.SessionUser.authenticated.value = true; sessionModule.SessionUser.permissions.value = ["superuser", "user"]; sessionModule.SessionUser.initiated.value = true; }, token); } async function installSystemStatusMock(page, snapshot) { await page.route(/\/superuser\/system\/status(?:\?.*)?$/, async (route) => { await route.fulfill( json({ success: true, data: snapshot, meta: {}, includes: {}, }) ); }); } const degradedSnapshot = { overall_status: "degraded", generated_at: "2026-04-08T08:45:00.000Z", refresh_after_seconds: 30, runtime: { cpu: { status: "ok", usage_percent: 34.5, source: "proc_stat", checked_at: "2026-04-08T08:45:00.000Z" }, memory: { status: "degraded", usage_percent: 91.3, used_bytes: 6442450944, total_bytes: 8589934592, source: "cgroup_v2", checked_at: "2026-04-08T08:45:00.000Z", }, disk: { status: "ok", usage_percent: 56.2, used_bytes: 53687091200, total_bytes: 107374182400, path: "/var/www/html", checked_at: "2026-04-08T08:45:00.000Z", }, }, dependencies: { database: { status: "ok", latency_ms: 4.2, database: "truckwash", server_version: "8.0.36", checked_at: "2026-04-08T08:45:00.000Z", }, redis: { status: "down", latency_ms: 12.8, database: 0, checked_at: "2026-04-08T08:45:00.000Z", error: "Connection refused", }, minio: { status: "degraded", latency_ms: 7.1, endpoint: "http://minio:9000", checked_at: "2026-04-08T08:45:00.000Z", buckets: [ { name: "attachments", status: "ok" }, { name: "backups", status: "down", error: "Missing bucket" }, ], }, }, modules: [ { key: "openai", enabled: true, configured: true, probe_supported: true, status: "ok", status_reason: "OpenAI API connectivity confirmed.", status_reason_key: "http_ok", status_reason_params: { label: "OpenAI API" }, checked_at: "2026-04-08T08:45:00.000Z", }, { key: "bird", enabled: true, configured: true, probe_supported: true, status: "degraded", status_reason: "Bird API returned HTTP 503.", status_reason_key: "http_status", status_reason_params: { label: "Bird API", httpStatus: 503 }, checked_at: "2026-04-08T08:45:00.000Z", }, ], sessions: { active_window_minutes: 15, active_users: 3, active_sessions: 4, recent_sessions: [ { session_kind: "user", principal_id: 101, display_name: "Acme Logistics", context_label: "Customer 1001", context_label_key: "customer_number", context_label_params: { number: 1001 }, customer_number_context: 1001, device_type: "desktop", user_agent: "Mozilla/5.0", last_route: "/superuser/vehicles", first_seen_at: "2026-04-08T08:20:00.000Z", last_seen_at: "2026-04-08T08:44:00.000Z", active: true, }, ], }, warnings: ["Redis is unavailable; module probe caching is bypassed."], warning_entries: [ { key: "redis_cache_bypass", params: {}, message: "Redis is unavailable; module probe caching is bypassed.", }, ], }; const databaseCompatibilitySnapshot = { ...degradedSnapshot, overall_status: "ok", runtime: { ...degradedSnapshot.runtime, memory: { status: "ok", usage_percent: 48.2, used_bytes: 4294967296, total_bytes: 8589934592, source: "cgroup_v2", checked_at: "2026-04-08T08:45:00.000Z", }, }, dependencies: { ...degradedSnapshot.dependencies, redis: { status: "ok", latency_ms: 1.9, database: 0, checked_at: "2026-04-08T08:45:00.000Z", }, minio: { status: "ok", latency_ms: 5.4, endpoint: "http://minio:9000", checked_at: "2026-04-08T08:45:00.000Z", buckets: [ { name: "attachments", status: "ok" }, { name: "backups", status: "ok" }, ], }, }, warnings: [], }; const longReasonSnapshot = { ...degradedSnapshot, modules: [ { key: "shelly", enabled: true, configured: true, probe_supported: true, status: "configured", status_reason: "Shelly cloud credentials are configured, but no known device id is available for a safe read-only probe.", status_reason_key: "shelly_no_device_id", status_reason_params: {}, checked_at: "2026-04-08T08:45:00.000Z", }, ], }; const longTitleSnapshot = { ...degradedSnapshot, modules: [ { key: "licenseplaterecognizer", enabled: true, configured: true, probe_supported: true, status: "ok", status_reason: "License Plate Recognizer connectivity confirmed.", status_reason_key: "http_ok", status_reason_params: { label: "License Plate Recognizer" }, checked_at: "2026-04-08T08:45:00.000Z", }, { key: "virkdata", enabled: true, configured: true, probe_supported: false, status: "configured", status_reason: "Configuration is present, but no safe read-only probe is available.", status_reason_key: "safe_probe_unavailable", status_reason_params: {}, checked_at: "2026-04-08T08:45:00.000Z", }, ], }; test.describe("Superuser system status smoke", () => { test.beforeEach(async ({ page }) => { await mockApi(page, { authenticated: true, permissions: ["superuser", "user"], }); await primeSuperuserSession(page); }); test("@smoke superuser status dashboard renders Danish translations for degraded infrastructure and recent sessions", async ({ page, }) => { await installSystemStatusMock(page, degradedSnapshot); await page.goto("/superuser"); await expect(page).toHaveURL(/\/superuser$/); await expect(page.getByTestId("system-status-dashboard")).toBeVisible(); await expect(page.getByTestId("status-card-database")).toContainText("truckwash"); await expect(page.locator("body")).toContainText("Acme Logistics"); await expect(page.locator("body")).toContainText("Kunde 1001"); await expect(page.locator("body")).toContainText("Bruger"); await expect(page.locator("body")).toContainText("Computer"); await expect(page.locator("body")).toContainText("/superuser/vehicles"); await expect(page.locator("body")).toContainText("Forbindelse til OpenAI API bekræftet."); await expect(page.locator("body")).toContainText("Redis er utilgængelig; cache til modulprober omgås."); await expect(page.locator("body")).not.toContainText("OpenAI API connectivity confirmed."); await expect(page.locator("body")).not.toContainText("Redis is unavailable; module probe caching is bypassed."); await expect(page.locator("body")).not.toContainText("Customer 1001"); await expect(page.locator('a[href="/superuser/configuration/openai"]').last()).toBeVisible(); }); test("@smoke superuser database compatibility page renders shared database status snapshot", async ({ page }) => { await installSystemStatusMock(page, databaseCompatibilitySnapshot); await page.goto("/superuser/system/database"); await expect(page).toHaveURL(/\/superuser\/system\/database$/); await expect(page.getByTestId("database-status-card")).toBeVisible(); await expect(page.getByTestId("database-status-card")).toContainText("truckwash"); await expect(page.getByTestId("database-status-card")).toContainText("8.0.36"); }); test("@smoke superuser module cards keep long Danish status reasons below the header row", async ({ page }) => { await installSystemStatusMock(page, longReasonSnapshot); await page.goto("/superuser"); const card = page.getByTestId("module-card-shelly"); const title = card.locator(".module-card__title"); const pill = card.locator(".status-pill"); const reason = page.getByTestId("module-reason-shelly"); await expect(card).toBeVisible(); await expect(reason).toContainText("der findes ikke et kendt device-id"); const [titleBox, pillBox, reasonBox] = await Promise.all([ title.boundingBox(), pill.boundingBox(), reason.boundingBox(), ]); expect(titleBox).not.toBeNull(); expect(pillBox).not.toBeNull(); expect(reasonBox).not.toBeNull(); const headerBottom = Math.max(titleBox.y + titleBox.height, pillBox.y + pillBox.height); expect(reasonBox.y).toBeGreaterThanOrEqual(headerBottom - 1); }); test("@smoke superuser module headers keep long titles and status pills inside their own cards", async ({ page }) => { await installSystemStatusMock(page, longTitleSnapshot); await page.goto("/superuser"); const card = page.getByTestId("module-card-licenseplaterecognizer"); const title = card.locator(".module-card__title"); const pill = card.locator(".status-pill"); await expect(card).toBeVisible(); await expect(title).toContainText("License Plate Recognizer"); const [cardBox, titleBox, pillBox] = await Promise.all([ card.boundingBox(), title.boundingBox(), pill.boundingBox(), ]); expect(cardBox).not.toBeNull(); expect(titleBox).not.toBeNull(); expect(pillBox).not.toBeNull(); expect(titleBox.x).toBeGreaterThanOrEqual(cardBox.x - 1); expect(titleBox.x + titleBox.width).toBeLessThanOrEqual(cardBox.x + cardBox.width + 1); expect(pillBox.x).toBeGreaterThanOrEqual(cardBox.x - 1); expect(pillBox.x + pillBox.width).toBeLessThanOrEqual(cardBox.x + cardBox.width + 1); const overlapWidth = Math.max( 0, Math.min(titleBox.x + titleBox.width, pillBox.x + pillBox.width) - Math.max(titleBox.x, pillBox.x) ); const overlapHeight = Math.max( 0, Math.min(titleBox.y + titleBox.height, pillBox.y + pillBox.height) - Math.max(titleBox.y, pillBox.y) ); expect(overlapWidth * overlapHeight).toBeLessThanOrEqual(1); }); });