import { test, expect, Page } from "@playwright/test"; // Helper to generate unique email for each test function uniqueEmail(): string { return `test-${Date.now()}-${Math.random().toString(36).substring(7)}@example.com`; } // Helper to clear auth cookies async function clearAuth(page: Page) { await page.context().clearCookies(); } test.describe("Authentication Flow", () => { test.beforeEach(async ({ page }) => { await clearAuth(page); }); test("redirects to login when not authenticated", async ({ page }) => { await page.goto("/"); await expect(page).toHaveURL("/login"); }); test("login page has correct form elements", async ({ page }) => { await page.goto("/login"); await expect(page.locator("h1")).toHaveText("Welcome back"); await expect(page.locator('input[type="email"]')).toBeVisible(); await expect(page.locator('input[type="password"]')).toBeVisible(); await expect(page.locator('button[type="submit"]')).toHaveText("Sign in"); await expect(page.locator('a[href="/signup"]')).toBeVisible(); }); test("signup page has correct form elements", async ({ page }) => { await page.goto("/signup"); await expect(page.locator("h1")).toHaveText("Create account"); await expect(page.locator('input[type="email"]')).toBeVisible(); await expect(page.locator('input[type="password"]').first()).toBeVisible(); await expect(page.locator('input[type="password"]').nth(1)).toBeVisible(); await expect(page.locator('button[type="submit"]')).toHaveText("Create account"); await expect(page.locator('a[href="/login"]')).toBeVisible(); }); test("can navigate from login to signup", async ({ page }) => { await page.goto("/login"); await page.click('a[href="/signup"]'); await expect(page).toHaveURL("/signup"); }); test("can navigate from signup to login", async ({ page }) => { await page.goto("/signup"); await page.click('a[href="/login"]'); await expect(page).toHaveURL("/login"); }); }); test.describe("Signup", () => { test.beforeEach(async ({ page }) => { await clearAuth(page); }); test("can create a new account", async ({ page }) => { const email = uniqueEmail(); await page.goto("/signup"); await page.fill('input[type="email"]', email); await page.fill('input[type="password"]', "password123"); await page.locator('input[type="password"]').nth(1).fill("password123"); await page.click('button[type="submit"]'); // Should redirect to home after signup await expect(page).toHaveURL("/"); // Should show user email await expect(page.getByText(email)).toBeVisible(); }); test("shows error for duplicate email", async ({ page }) => { const email = uniqueEmail(); // First registration await page.goto("/signup"); await page.fill('input[type="email"]', email); await page.fill('input[type="password"]', "password123"); await page.locator('input[type="password"]').nth(1).fill("password123"); await page.click('button[type="submit"]'); await expect(page).toHaveURL("/"); // Clear cookies and try again with same email await clearAuth(page); await page.goto("/signup"); await page.fill('input[type="email"]', email); await page.fill('input[type="password"]', "password123"); await page.locator('input[type="password"]').nth(1).fill("password123"); await page.click('button[type="submit"]'); // Should show error await expect(page.getByText("Email already registered")).toBeVisible(); }); test("shows error for password mismatch", async ({ page }) => { await page.goto("/signup"); await page.fill('input[type="email"]', uniqueEmail()); await page.fill('input[type="password"]', "password123"); await page.locator('input[type="password"]').nth(1).fill("differentpassword"); await page.click('button[type="submit"]'); await expect(page.getByText("Passwords do not match")).toBeVisible(); }); test("shows error for short password", async ({ page }) => { await page.goto("/signup"); await page.fill('input[type="email"]', uniqueEmail()); await page.fill('input[type="password"]', "short"); await page.locator('input[type="password"]').nth(1).fill("short"); await page.click('button[type="submit"]'); await expect(page.getByText("Password must be at least 6 characters")).toBeVisible(); }); test("shows loading state while submitting", async ({ page }) => { await page.goto("/signup"); await page.fill('input[type="email"]', uniqueEmail()); await page.fill('input[type="password"]', "password123"); await page.locator('input[type="password"]').nth(1).fill("password123"); // Start submission and check for loading state const submitPromise = page.click('button[type="submit"]'); await expect(page.locator('button[type="submit"]')).toHaveText("Creating account..."); await submitPromise; }); }); test.describe("Login", () => { const testEmail = `login-test-${Date.now()}@example.com`; const testPassword = "testpassword123"; test.beforeAll(async ({ browser }) => { // Create a test user const page = await browser.newPage(); await page.goto("/signup"); await page.fill('input[type="email"]', testEmail); await page.fill('input[type="password"]', testPassword); await page.locator('input[type="password"]').nth(1).fill(testPassword); await page.click('button[type="submit"]'); await expect(page).toHaveURL("/"); await page.close(); }); test.beforeEach(async ({ page }) => { await clearAuth(page); }); test("can login with valid credentials", async ({ page }) => { await page.goto("/login"); await page.fill('input[type="email"]', testEmail); await page.fill('input[type="password"]', testPassword); await page.click('button[type="submit"]'); await expect(page).toHaveURL("/"); await expect(page.getByText(testEmail)).toBeVisible(); }); test("shows error for wrong password", async ({ page }) => { await page.goto("/login"); await page.fill('input[type="email"]', testEmail); await page.fill('input[type="password"]', "wrongpassword"); await page.click('button[type="submit"]'); await expect(page.getByText("Incorrect email or password")).toBeVisible(); }); test("shows error for non-existent user", async ({ page }) => { await page.goto("/login"); await page.fill('input[type="email"]', "nonexistent@example.com"); await page.fill('input[type="password"]', "password123"); await page.click('button[type="submit"]'); await expect(page.getByText("Incorrect email or password")).toBeVisible(); }); test("shows loading state while submitting", async ({ page }) => { await page.goto("/login"); await page.fill('input[type="email"]', testEmail); await page.fill('input[type="password"]', testPassword); const submitPromise = page.click('button[type="submit"]'); await expect(page.locator('button[type="submit"]')).toHaveText("Signing in..."); await submitPromise; }); }); test.describe("Logout", () => { test("can logout", async ({ page }) => { const email = uniqueEmail(); // Sign up first await page.goto("/signup"); await page.fill('input[type="email"]', email); await page.fill('input[type="password"]', "password123"); await page.locator('input[type="password"]').nth(1).fill("password123"); await page.click('button[type="submit"]'); await expect(page).toHaveURL("/"); // Click logout await page.click("text=Sign out"); // Should redirect to login await expect(page).toHaveURL("/login"); }); test("cannot access home after logout", async ({ page }) => { const email = uniqueEmail(); // Sign up await page.goto("/signup"); await page.fill('input[type="email"]', email); await page.fill('input[type="password"]', "password123"); await page.locator('input[type="password"]').nth(1).fill("password123"); await page.click('button[type="submit"]'); await expect(page).toHaveURL("/"); // Logout await page.click("text=Sign out"); await expect(page).toHaveURL("/login"); // Try to access home await page.goto("/"); await expect(page).toHaveURL("/login"); }); }); test.describe("Session Persistence", () => { test("session persists after page reload", async ({ page }) => { const email = uniqueEmail(); // Sign up await page.goto("/signup"); await page.fill('input[type="email"]', email); await page.fill('input[type="password"]', "password123"); await page.locator('input[type="password"]').nth(1).fill("password123"); await page.click('button[type="submit"]'); await expect(page).toHaveURL("/"); await expect(page.getByText(email)).toBeVisible(); // Reload page await page.reload(); // Should still be logged in await expect(page).toHaveURL("/"); await expect(page.getByText(email)).toBeVisible(); }); test("auth cookie is set after login", async ({ page }) => { const email = uniqueEmail(); await page.goto("/signup"); await page.fill('input[type="email"]', email); await page.fill('input[type="password"]', "password123"); await page.locator('input[type="password"]').nth(1).fill("password123"); await page.click('button[type="submit"]'); await expect(page).toHaveURL("/"); // Check cookies const cookies = await page.context().cookies(); const authCookie = cookies.find((c) => c.name === "auth_token"); expect(authCookie).toBeTruthy(); expect(authCookie!.httpOnly).toBe(true); }); test("auth cookie is cleared on logout", async ({ page }) => { const email = uniqueEmail(); await page.goto("/signup"); await page.fill('input[type="email"]', email); await page.fill('input[type="password"]', "password123"); await page.locator('input[type="password"]').nth(1).fill("password123"); await page.click('button[type="submit"]'); await expect(page).toHaveURL("/"); await page.click("text=Sign out"); // Wait for navigation to complete - ensures the logout request finished // and the Set-Cookie header was processed by the browser await expect(page).toHaveURL("/login"); const cookies = await page.context().cookies(); const authCookie = cookies.find((c) => c.name === "auth_token"); // Cookie should be deleted or have empty value expect(!authCookie || authCookie.value === "").toBe(true); }); });