import { expect, openSettings, test } from "./helpers"; const API = process.env.API_URL || "http://localhost:13490"; /** Auth + JSON content-type (POST, PUT). */ function authJson(token: string): Record { return { Authorization: `Bearer ${token}`, "Content-Type": "application/json" }; } /** Auth header only (GET, DELETE). */ function authOnly(token: string): Record { return { Authorization: `Bearer ${token}` }; } async function getAdminToken(): Promise { const res = await fetch(`${API}/api/auth/login`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username: "admin", password: "admin" }), }); // Fail here, not three calls later. Without this an admin login that was // refused returns undefined and every later request sends "Bearer undefined", // which surfaces as a 401 on whatever the test happened to do next. if (!res.ok) { throw new Error( `Admin login failed with ${res.status}; the admin password may not have been restored by an earlier spec`, ); } const data = (await res.json()) as { token?: string }; if (!data.token) throw new Error("Admin login returned no token"); return data.token; } /** Delete all non-admin test users by prefix. */ async function cleanupUsersByPrefix(adminToken: string, prefix: string): Promise { const listRes = await fetch(`${API}/api/auth/users`, { headers: authOnly(adminToken), }); if (!listRes.ok) return; const { users } = await listRes.json(); for (const u of users) { if (u.username.startsWith(prefix)) { await fetch(`${API}/api/auth/users/${u.id}`, { method: "DELETE", headers: authOnly(adminToken), }); } } } /** Delete test teams by prefix. */ async function cleanupTeamsByPrefix(adminToken: string, prefix: string): Promise { const listRes = await fetch(`${API}/api/v1/teams`, { headers: authOnly(adminToken), }); if (!listRes.ok) return; const { teams } = await listRes.json(); for (const t of teams) { if (t.name.startsWith(prefix)) { await fetch(`${API}/api/v1/teams/${t.id}`, { method: "DELETE", headers: authOnly(adminToken), }); } } } // --------------------------------------------------------------------------- // Settings Dialog -- People tab (user CRUD), Teams, and Roles // --------------------------------------------------------------------------- const UID = Date.now().toString(36); test.describe("GUI Settings - People Tab", () => { test("displays user count and user table", async ({ loggedInPage: page }) => { await openSettings(page); await page.getByRole("button", { name: /people/i }).click(); // User count await expect(page.getByText(/\d+ users?/)).toBeVisible({ timeout: 5_000 }); // Table headers await expect(page.getByText("User").first()).toBeVisible(); await expect(page.getByText("Role").first()).toBeVisible(); await expect(page.getByText("Team").first()).toBeVisible(); // Admin user row await expect(page.getByText("admin").first()).toBeVisible(); }); test("search filters users and shows empty state", async ({ loggedInPage: page }) => { await openSettings(page); await page.getByRole("button", { name: /people/i }).click(); await page.waitForTimeout(500); const searchInput = page.getByPlaceholder("Search members..."); await searchInput.fill("zzzznonexistent"); await expect(page.getByText("No members match your search.")).toBeVisible(); // Clear search restores admin await searchInput.fill(""); await expect(page.getByText("admin").first()).toBeVisible(); }); test("Add Members opens form with username, password, role, team fields", async ({ loggedInPage: page, }) => { await openSettings(page); await page.getByRole("button", { name: /people/i }).click(); await page.waitForTimeout(500); const addBtn = page.getByRole("button", { name: /add members/i }); await expect(addBtn).toBeVisible(); const isDisabled = await addBtn.isDisabled(); if (!isDisabled) { await addBtn.click(); await expect(page.getByPlaceholder("Username")).toBeVisible(); await expect(page.getByPlaceholder("Password")).toBeVisible(); // Role and team dropdowns (two