import { test, expect, Page } from "@playwright/test"; // Admin credentials from seed data const ADMIN_EMAIL = "admin@example.com"; const ADMIN_PASSWORD = "admin123"; // Regular user from seed data const REGULAR_USER_EMAIL = "user@example.com"; async function loginAsAdmin(page: Page) { await page.goto("/login"); await page.fill('input[type="email"]', ADMIN_EMAIL); await page.fill('input[type="password"]', ADMIN_PASSWORD); await page.click('button[type="submit"]'); await expect(page).toHaveURL("/audit"); } test.describe("Admin Invites Page", () => { test.beforeEach(async ({ page }) => { await page.context().clearCookies(); await loginAsAdmin(page); }); test("admin can access invites page", async ({ page }) => { await page.goto("/admin/invites"); await expect(page.getByRole("heading", { name: "Create Invite" })).toBeVisible(); await expect(page.getByRole("heading", { name: "All Invites" })).toBeVisible(); }); test("godfather selection is a dropdown with users, not a number input", async ({ page }) => { await page.goto("/admin/invites"); // Wait for users to load await page.waitForSelector("select"); // The godfather selector should be a const selectElement = page.locator("select").first(); await expect(selectElement).toBeVisible(); // Verify it has user options (at least the seeded users) const options = selectElement.locator("option"); const optionCount = await options.count(); // Should have at least 2 options: placeholder + at least one user expect(optionCount).toBeGreaterThanOrEqual(2); // Verify the regular user appears as an option await expect(selectElement).toContainText(REGULAR_USER_EMAIL); // There should NOT be a number input for godfather ID const numberInput = page.locator('input[type="number"]'); await expect(numberInput).toHaveCount(0); }); test("can create invite by selecting user from dropdown", async ({ page }) => { await page.goto("/admin/invites"); // Wait for page to load await page.waitForSelector("select"); // Select the regular user as godfather const godfatherSelect = page.locator("select").first(); await godfatherSelect.selectOption({ label: REGULAR_USER_EMAIL }); // Click create invite await page.click('button:has-text("Create Invite")'); // Wait for the invite to appear in the table await expect(page.locator("table")).toContainText(REGULAR_USER_EMAIL); // Verify an invite code appears (format: word-word-NN) const inviteCodeCell = page.locator("td").first(); await expect(inviteCodeCell).toHaveText(/^[a-z]+-[a-z]+-\d{2}$/); }); test("create button is disabled when no user selected", async ({ page }) => { await page.goto("/admin/invites"); // Wait for page to load await page.waitForSelector("select"); // The create button should be disabled initially (no user selected) const createButton = page.locator('button:has-text("Create Invite")'); await expect(createButton).toBeDisabled(); // Select a user const godfatherSelect = page.locator("select").first(); await godfatherSelect.selectOption({ label: REGULAR_USER_EMAIL }); // Now the button should be enabled await expect(createButton).toBeEnabled(); }); test("can revoke a ready invite", async ({ page }) => { await page.goto("/admin/invites"); await page.waitForSelector("select"); // Create an invite first const godfatherSelect = page.locator("select").first(); await godfatherSelect.selectOption({ label: REGULAR_USER_EMAIL }); await page.click('button:has-text("Create Invite")'); // Wait for the invite to appear await expect(page.locator("table")).toContainText("ready"); // Click revoke on the first ready invite const revokeButton = page.locator('button:has-text("Revoke")').first(); await revokeButton.click(); // Verify the status changed to revoked await expect(page.locator("table")).toContainText("revoked"); }); test("status filter works", async ({ page }) => { await page.goto("/admin/invites"); await page.waitForSelector("select"); // Create an invite const godfatherSelect = page.locator("select").first(); await godfatherSelect.selectOption({ label: REGULAR_USER_EMAIL }); await page.click('button:has-text("Create Invite")'); await expect(page.locator("table")).toContainText("ready"); // Filter by "revoked" status - should show no ready invites const statusFilter = page.locator("select").nth(1); // Second select is the status filter await statusFilter.selectOption("revoked"); // Wait for the filter to apply await page.waitForResponse((resp) => resp.url().includes("status=revoked")); // Filter by "ready" status - should show our invite await statusFilter.selectOption("ready"); await page.waitForResponse((resp) => resp.url().includes("status=ready")); await expect(page.locator("table")).toContainText("ready"); }); }); test.describe("Admin Invites Access Control", () => { test("regular user cannot access admin invites page", async ({ page }) => { // Login as regular user await page.goto("/login"); await page.fill('input[type="email"]', REGULAR_USER_EMAIL); await page.fill('input[type="password"]', "user123"); await page.click('button[type="submit"]'); await expect(page).toHaveURL("/"); // Try to access admin invites page await page.goto("/admin/invites"); // Should be redirected away (to home page based on fallbackRedirect) await expect(page).not.toHaveURL("/admin/invites"); }); test("unauthenticated user cannot access admin invites page", async ({ page }) => { await page.context().clearCookies(); await page.goto("/admin/invites"); // Should be redirected to login await expect(page).toHaveURL("/login"); }); });