import { test, expect } from "@playwright/test"; import { ServerDetailPage } from "../pages/ServerDetailPage"; const MOCK_TOKEN = "mock-jwt-token"; const MOCK_USER = { id: 1, username: "admin", role: "admin" }; const SERVER_ID = 1; const MOCK_SERVER = { id: SERVER_ID, name: "A3Master", game_type: "arma3", status: "running", port: 2302, max_players: 64, current_players: 3, restart_count: 0, auto_restart: true, created_at: "2026-01-01T00:00:00Z", exe_path: "/servers/A3Master/arma3server", config_path: "/servers/A3Master/server.cfg", }; const MOCK_CONFIG = { hostname: "A3Master Tactical", password: "", password_admin: "secret", max_players: 64, battleye: true, motd: ["Welcome to A3Master"], disable_von: false, voteMissionPlayers: 1, }; const MOCK_CONFIG_SCHEMA = { hostname: { widget: "text", label: "Hostname" }, password: { widget: "text", label: "Password" }, max_players: { widget: "number", label: "Max Players" }, battleye: { widget: "toggle", label: "BattlEye" }, motd: { widget: "tag-list", label: "MOTD Lines" }, }; const MOCK_MISSIONS = { server_id: SERVER_ID, total: 2, missions: [ { name: "co10_example", filename: "co10_example.Altis.pbo", size_bytes: 102400, terrain: "Altis" }, { name: "tvt06_test", filename: "tvt06_test.Stratis.pbo", size_bytes: 51200, terrain: "Stratis" }, ], }; const MOCK_ROTATION = { missions: [{ name: "co10_example.Altis", difficulty: "Regular" }], config_version: 1 }; const MOCK_MODS = { server_id: SERVER_ID, enabled_count: 2, mods: [ { name: "@ace", path: "/mods/@ace", size_bytes: 5000000, enabled: true, display_name: "ACE3", workshop_id: "463939057" }, { name: "@cba_a3", path: "/mods/@cba_a3", size_bytes: 1000000, enabled: true, display_name: "CBA_A3", workshop_id: "450814997" }, { name: "@task_force_radio", path: "/mods/@task_force_radio", size_bytes: 2000000, enabled: false, display_name: "Task Force Radio", workshop_id: null }, ], }; const MOCK_PLAYERS = { server_id: SERVER_ID, player_count: 2, players: [ { id: 1, slot_id: "0", name: "PlayerOne", guid: "abc123", ip: "192.168.1.1", ping: 45 }, { id: 2, slot_id: "1", name: "PlayerTwo", guid: "def456", ip: "192.168.1.2", ping: 88 }, ], }; const MOCK_LOGFILES = [ { filename: "arma3server_2026-04-17_12-00-00.rpt", size_bytes: 20480, modified_at: 1745092800 }, { filename: "arma3server_2026-04-16_08-00-00.rpt", size_bytes: 40960, modified_at: 1745006400 }, ]; async function setupMocks(page: import("@playwright/test").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 (lowest priority — register first) await page.route("**/api/**", (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, data: null, error: null }) }), ); 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/${SERVER_ID}`, (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, data: MOCK_SERVER, error: null }) }), ); await page.route(`**/api/servers/${SERVER_ID}/config/schema`, (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, data: MOCK_CONFIG_SCHEMA, error: null }) }), ); await page.route(`**/api/servers/${SERVER_ID}/config`, (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, data: MOCK_CONFIG, error: null }) }), ); await page.route(`**/api/servers/${SERVER_ID}/missions`, (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, data: MOCK_MISSIONS, error: null }) }), ); await page.route(`**/api/servers/${SERVER_ID}/missions/rotation`, (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, data: MOCK_ROTATION, error: null }) }), ); await page.route(`**/api/servers/${SERVER_ID}/mods`, (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, data: MOCK_MODS, error: null }) }), ); await page.route(`**/api/servers/${SERVER_ID}/players`, (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, data: MOCK_PLAYERS, error: null }) }), ); await page.route(`**/api/servers/${SERVER_ID}/logfiles`, (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, data: MOCK_LOGFILES, error: null }) }), ); } test.describe("Server Detail — Overview Tab", () => { let detailPage: ServerDetailPage; test.beforeEach(async ({ page }) => { await setupMocks(page); detailPage = new ServerDetailPage(page); await detailPage.goto(SERVER_ID); }); test("should display server name and status", async ({ page }) => { await expect(page.locator("text=A3Master").first()).toBeVisible({ timeout: 10_000 }); await expect(page.locator("text=running").first()).toBeVisible(); }); test("should show tab list with all tabs", async () => { await expect(detailPage.tabBar).toBeVisible({ timeout: 10_000 }); for (const tab of ["Overview", "Config", "Players", "Missions", "Mods", "Logs"]) { await expect(detailPage.getTab(tab)).toBeVisible(); } }); }); test.describe("Server Detail — Config Tab (Phase 1)", () => { let detailPage: ServerDetailPage; test.beforeEach(async ({ page }) => { await setupMocks(page); detailPage = new ServerDetailPage(page); await detailPage.goto(SERVER_ID); await detailPage.clickTab("Config"); }); test("should show config fields", async ({ page }) => { await expect(page.locator("text=Hostname").first()).toBeVisible({ timeout: 10_000 }); }); test("should render BattlEye field label", async ({ page }) => { // Config fields render their labels regardless of edit mode // formatLabel("battleye") → "Battleye" or via schema label "BattlEye" await expect(page.locator("text=Battleye").or(page.locator("text=BattlEye")).first()).toBeVisible({ timeout: 10_000 }); }); }); test.describe("Server Detail — Missions Tab (Phase 2)", () => { let detailPage: ServerDetailPage; test.beforeEach(async ({ page }) => { await setupMocks(page); detailPage = new ServerDetailPage(page); await detailPage.goto(SERVER_ID); await detailPage.clickTab("Missions"); }); test("should list mission files", async ({ page }) => { // MissionList shows mission.name (not filename) in the table await expect(page.getByText("co10_example", { exact: true })).toBeVisible({ timeout: 10_000 }); await expect(page.getByText("tvt06_test", { exact: true })).toBeVisible(); }); test("should show terrain names", async ({ page }) => { await expect(page.locator("text=Altis").first()).toBeVisible({ timeout: 10_000 }); await expect(page.locator("text=Stratis").first()).toBeVisible(); }); test("should show upload button", async ({ page }) => { // Upload is a