import { test, expect } from "@playwright/test"; import { DashboardPage } from "../pages/DashboardPage"; const MOCK_TOKEN = "mock-jwt-token"; const MOCK_USER = { id: 1, username: "admin", role: "admin" }; const MOCK_SERVERS = [ { id: 1, name: "A3Master", game_type: "arma3", status: "running", port: 2302, max_players: 64, current_players: 32, restart_count: 2, auto_restart: true, created_at: "2026-01-01T00:00:00Z", }, { id: 2, name: "Arma3 Test Server", game_type: "arma3", status: "stopped", port: 2303, max_players: 32, current_players: 0, restart_count: 0, auto_restart: false, created_at: "2026-01-02T00:00:00Z", }, ]; /** * Set up auth + mock all API calls the dashboard needs. * IMPORTANT: Playwright checks routes in reverse registration order (last registered = first checked). * So we register the catch-all FIRST, then specific routes AFTER so they take priority. */ async function setupDashboardMocks(page: import("@playwright/test").Page, servers = MOCK_SERVERS) { // Set mock auth state in localStorage for both: // 1) The Zustand persist store (key: languard-auth) so ProtectedLayout sees isAuthenticated: true // 2) The raw token (key: languard_token) so the Axios interceptor adds the Bearer header await page.addInitScript(({ token, user }) => { localStorage.setItem("languard_token", token); localStorage.setItem( "languard-auth", JSON.stringify({ state: { token, user }, version: 0 }), ); }, { token: MOCK_TOKEN, user: MOCK_USER }); // Catch-all for unhandled API calls — register FIRST so it has lowest priority await page.route("**/api/**", (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, data: null, error: null }), }), ); // Specific routes — register AFTER catch-all so they take priority await page.route("**/api/auth/me", (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, data: MOCK_USER, error: null }), }), ); await page.route("**/api/servers**", (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, data: servers, error: null }), }), ); } test.describe("Dashboard", () => { let dashboardPage: DashboardPage; test.beforeEach(async ({ page }) => { await setupDashboardMocks(page); dashboardPage = new DashboardPage(page); await dashboardPage.goto(); }); test("should display dashboard header", async () => { await expect(dashboardPage.content).toBeVisible({ timeout: 10_000 }); await expect(dashboardPage.content.locator("h1")).toContainText("Dashboard"); }); test("should show server count", async () => { await expect(dashboardPage.content).toBeVisible({ timeout: 10_000 }); await expect(dashboardPage.content.locator("text=2 servers configured")).toBeVisible(); }); test("should render server cards", async () => { await expect(dashboardPage.serverCards.first()).toBeVisible({ timeout: 10_000 }); const count = await dashboardPage.getServerCount(); expect(count).toBe(2); }); test("should display server names in cards", async () => { await expect(dashboardPage.serverCards.first()).toBeVisible({ timeout: 10_000 }); const name = await dashboardPage.getServerCardName(0); expect(name).toContain("A3Master"); }); test("should show Add Server button", async () => { await expect(dashboardPage.content).toBeVisible({ timeout: 10_000 }); await expect(dashboardPage.addServerButton).toBeVisible(); await expect(dashboardPage.addServerButton).toContainText("Add Server"); }); test("should show sidebar with server list", async () => { await expect(dashboardPage.sidebar).toBeVisible({ timeout: 10_000 }); await expect(dashboardPage.sidebar.locator("text=Servers")).toBeVisible(); await expect(dashboardPage.sidebar.locator("text=A3Master")).toBeVisible(); }); test("should show Stop button for running server", async () => { await expect(dashboardPage.serverCards.first()).toBeVisible({ timeout: 10_000 }); const firstCard = dashboardPage.serverCards.nth(0); await expect(firstCard.locator('button[aria-label^="Stop"]')).toBeVisible(); }); test("should show Start button for stopped server", async () => { await expect(dashboardPage.serverCards.nth(1)).toBeVisible({ timeout: 10_000 }); const secondCard = dashboardPage.serverCards.nth(1); await expect(secondCard.locator('button[aria-label^="Start"]')).toBeVisible(); }); test("should display player count in server card", async () => { await expect(dashboardPage.serverCards.first()).toBeVisible({ timeout: 10_000 }); const firstCard = dashboardPage.serverCards.nth(0); await expect(firstCard.locator("text=32/64")).toBeVisible(); }); test("should navigate to server detail on card click", async ({ page }) => { await expect(dashboardPage.serverCards.first()).toBeVisible({ timeout: 10_000 }); // Click the server card link (not the sidebar link) — use .first() to avoid strict mode const serverLink = page.locator('[data-testid="dashboard-content"] a[href="/servers/1"]'); await serverLink.click(); await expect(page).toHaveURL(/\/servers\/1/, { timeout: 5_000 }); }); }); test.describe("Dashboard - Empty State", () => { test("should show empty state when no servers", async ({ page }) => { await page.addInitScript(({ token, user }) => { localStorage.setItem("languard_token", token); localStorage.setItem( "languard-auth", JSON.stringify({ state: { token, user }, version: 0 }), ); }, { token: MOCK_TOKEN, user: MOCK_USER }); // Catch-all first (lowest priority) await page.route("**/api/**", (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, data: null, error: null }), }), ); // Specific routes after (higher priority) await page.route("**/api/auth/me", (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, data: MOCK_USER, error: null }), }), ); await page.route("**/api/servers**", (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, data: [], error: null }), }), ); const dashboardPage = new DashboardPage(page); await dashboardPage.goto(); await expect(dashboardPage.emptyState).toBeVisible({ timeout: 10_000 }); await expect(dashboardPage.emptyState.locator("text=No servers configured yet")).toBeVisible(); }); }); test.describe("Dashboard - Error State", () => { test("should show error state when API fails", async ({ page }) => { await page.addInitScript(({ token, user }) => { localStorage.setItem("languard_token", token); localStorage.setItem( "languard-auth", JSON.stringify({ state: { token, user }, version: 0 }), ); }, { token: MOCK_TOKEN, user: MOCK_USER }); // Catch-all first (lowest priority) await page.route("**/api/**", (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, data: null, error: null }), }), ); // Specific routes after (higher priority) await page.route("**/api/auth/me", (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, data: MOCK_USER, error: null }), }), ); await page.route("**/api/servers**", (route) => route.fulfill({ status: 500, contentType: "application/json", body: JSON.stringify({ success: false, error: "Internal Server Error" }), }), ); const dashboardPage = new DashboardPage(page); await dashboardPage.goto(); await expect(dashboardPage.errorMessage).toBeVisible({ timeout: 15_000 }); }); });