import { test, expect, type CDPSession, type Page } from "./fixtures/test"; import { setupVirtualAuthenticator, removeVirtualAuthenticator, registerFreshUser } from "./helpers/auth"; test.describe("Account Settings", () => { test("unauthenticated user is redirected to login", async ({ page }) => { await page.goto("/settings"); await expect(page).toHaveURL(/\/auth\/login/, { timeout: 5000 }); }); test("settings page loads for authenticated user", async ({ page }) => { const cdp = await page.context().newCDPSession(page); const authenticatorId = await setupVirtualAuthenticator(cdp); await registerFreshUser(page, "settings"); await page.goto("/settings"); await expect(page.getByRole("heading", { name: "Settings" })).toBeVisible(); await expect(page.getByText("Profile")).toBeVisible(); await expect(page.getByText("Security")).toBeVisible(); await expect(page.getByText("Account")).toBeVisible(); await removeVirtualAuthenticator(cdp, authenticatorId); }); test("update display name and bio", async ({ page }) => { const cdp = await page.context().newCDPSession(page); const authenticatorId = await setupVirtualAuthenticator(cdp); const { username } = await registerFreshUser(page, "settings"); await page.goto("/settings"); await page.getByLabel("Display Name").fill("Test Display Name"); await page.getByLabel("Bio").fill("I love hiking!"); await page.getByRole("button", { name: "Save" }).click(); await expect(page.getByText("Profile saved")).toBeVisible({ timeout: 5000 }); // Reload and verify persistence await page.reload(); await expect(page.getByLabel("Display Name")).toHaveValue("Test Display Name"); await expect(page.getByLabel("Bio")).toHaveValue("I love hiking!"); // Verify public profile reflects changes await page.goto(`/users/${username}`); await expect(page.getByText("Test Display Name")).toBeVisible(); await removeVirtualAuthenticator(cdp, authenticatorId); }); test("passkey list shows registered passkey", async ({ page }) => { const cdp = await page.context().newCDPSession(page); const authenticatorId = await setupVirtualAuthenticator(cdp); await registerFreshUser(page, "settings"); await page.goto("/settings"); // Should show the passkey registered during registration await expect(page.getByText("This device")).toBeVisible(); await expect(page.getByText(/Added/)).toBeVisible(); await removeVirtualAuthenticator(cdp, authenticatorId); }); test("add and delete passkey from settings", async ({ page }) => { const cdp = await page.context().newCDPSession(page); const authenticatorId = await setupVirtualAuthenticator(cdp); await registerFreshUser(page, "settings"); await page.goto("/settings"); // Should have 1 passkey from registration const deleteButtons = page.getByRole("button", { name: "Delete" }); await expect(deleteButtons).toHaveCount(1); // Add another passkey await page.getByRole("button", { name: "Add Passkey" }).click(); await page.waitForTimeout(1000); // After reload, should have 2 passkeys await expect(deleteButtons).toHaveCount(2, { timeout: 5000 }); // Delete one page.on("dialog", (dialog) => dialog.accept()); await deleteButtons.first().click(); await expect(page).toHaveURL(/\/settings/, { timeout: 5000 }); await expect(page.getByRole("button", { name: "Delete" })).toHaveCount(1); await removeVirtualAuthenticator(cdp, authenticatorId); }); test("delete last passkey shows warning", async ({ page }) => { const cdp = await page.context().newCDPSession(page); const authenticatorId = await setupVirtualAuthenticator(cdp); await registerFreshUser(page, "settings"); await page.goto("/settings"); // Intercept the confirm dialog to check it contains the warning let dialogMessage = ""; page.on("dialog", async (dialog) => { dialogMessage = dialog.message(); await dialog.accept(); }); await page.getByRole("button", { name: "Delete" }).click(); expect(dialogMessage).toContain("only passkey"); // After deleting, should show "No passkeys registered" await expect(page.getByText(/No passkeys registered/)).toBeVisible({ timeout: 5000 }); await removeVirtualAuthenticator(cdp, authenticatorId); }); test("settings link reachable from nav when logged in", async ({ page }) => { const cdp = await page.context().newCDPSession(page); const authenticatorId = await setupVirtualAuthenticator(cdp); const { username } = await registerFreshUser(page, "settings"); // Settings now lives inside the avatar dropdown; opening the // dropdown reveals the menuitem. await page.getByRole("navigation").getByRole("button", { name: username }).click(); await expect(page.getByRole("menuitem", { name: "Settings" })).toBeVisible(); await removeVirtualAuthenticator(cdp, authenticatorId); }); test("settings link not visible when logged out", async ({ page }) => { await page.goto("/"); // Anonymous navbar has neither the avatar nor the Settings link. await expect(page.getByRole("navigation").getByRole("link", { name: "Settings" })).not.toBeVisible(); }); test("account deletion requires correct username", async ({ page }) => { const cdp = await page.context().newCDPSession(page); const authenticatorId = await setupVirtualAuthenticator(cdp); const { username } = await registerFreshUser(page, "settings"); await page.goto("/settings"); // Click delete account await page.getByRole("button", { name: "Delete Account" }).click(); // Type wrong username — button should be disabled await page.getByPlaceholder(username).fill("wrongname"); await expect(page.getByRole("button", { name: "Permanently Delete" })).toBeDisabled(); // Type correct username — button should be enabled await page.getByPlaceholder(username).fill(username); await expect(page.getByRole("button", { name: "Permanently Delete" })).toBeEnabled(); // Submit deletion await page.getByRole("button", { name: "Permanently Delete" }).click(); await expect(page).toHaveURL("/", { timeout: 10000 }); // Session should be destroyed — nav should show login await expect(page.getByRole("navigation").getByRole("link", { name: "Sign In" })).toBeVisible({ timeout: 5000 }); await removeVirtualAuthenticator(cdp, authenticatorId); }); });