/** * Epic 1: Authentication & User Management * Acceptance Test: User Logout * * User Story: As a logged-in user, I want to log out so that I can secure my account * * Acceptance Criteria: * - Logout button in navigation * - Session cleared on logout * - Redirect to home page */ import { test, expect } from '@playwright/test'; import { prisma } from '@/lib/prisma'; const BASE_URL = process.env.CI ? 'https://euchre-ci.notsosm.art' : 'http://localhost:3000'; function getTestCredentials() { const timestamp = Date.now(); return { email: `logout-test-${timestamp}@example.com`, password: 'TestPassword1234!', name: `Logout Test User ${timestamp}` }; } test.describe.serial('Epic 1: User Logout', () => { let testEmail: string; let testPassword: string; let testName: string; test.beforeAll(async () => { const credentials = getTestCredentials(); testEmail = credentials.email; testPassword = credentials.password; testName = credentials.name; // Create test user via API with proper origin header const response = await fetch(`${BASE_URL}/api/auth/sign-up/email`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Origin': BASE_URL, 'X-Requested-With': 'XMLHttpRequest' }, body: JSON.stringify({ email: testEmail, password: testPassword, name: testName }) }); if (!response.ok) { const errorText = await response.text(); console.error('Failed to create user:', response.status, errorText); throw new Error(`Failed to create user: ${response.status}`); } const data = await response.json(); console.log('User created via API:', data.user?.email); // Wait for the database hook to complete (creating Player record) await new Promise(resolve => setTimeout(resolve, 2000)); // Verify the user was created in the database const user = await prisma.user.findUnique({ where: { email: testEmail } }); if (!user) { throw new Error(`User ${testEmail} was not created in database`); } if (!user.playerId) { throw new Error(`User ${testEmail} does not have a playerId (database hook may not have run)`); } console.log(`User ${testEmail} created with playerId: ${user.playerId}`); }); test.afterAll(async () => { try { const user = await prisma.user.findUnique({ where: { email: testEmail } }); if (user) { await prisma.user.delete({ where: { id: user.id } }); } } catch (error) { console.error('Cleanup error:', error); } }); test('Logout button appears in navigation when logged in', async ({ page }) => { // Login first await page.goto('/auth/login'); // Wait for page to load await page.waitForLoadState('domcontentloaded'); await page.waitForSelector('form'); await page.waitForSelector('button[type="submit"]:not([disabled])'); await page.fill('input[name="email"]', testEmail); await page.fill('input[name="password"]', testPassword); // Wait for redirect to admin or player profile after clicking submit const [response] = await Promise.all([ page.waitForResponse(response => response.url().includes('/api/auth/sign-in/email') && response.status() === 200, { timeout: 10000 } ), page.click('button[type="submit"]') ]); console.log('Login response:', response.status()); // Wait for redirect await page.waitForURL(/\/(admin|players)/, { timeout: 10000 }); console.log('After login URL:', page.url()); // Check cookies const cookies = await page.context().cookies(); console.log('Cookies after login:', cookies.map(c => `${c.name}=${c.value.substring(0, 20)}...`).join(', ')); // Check if better-auth.session_token exists const sessionCookie = cookies.find(c => c.name.includes('session')); console.log('Session cookie:', sessionCookie ? `${sessionCookie.name}=${sessionCookie.value.substring(0, 20)}...` : 'NOT FOUND'); // Navigate to home page to check navigation (session should persist) // Use reload to ensure session is read from cookies await page.reload(); await page.waitForLoadState('domcontentloaded'); // Wait a moment for the navigation component to update await page.waitForTimeout(1000); // Debug: Check what's on the page const pageContent = await page.content(); console.log('Page content contains Sign out:', pageContent.includes('Sign out')); console.log('Page content contains Sign in:', pageContent.includes('Sign in')); console.log('Page content length:', pageContent.length); console.log('Current URL:', page.url()); console.log('Page HTML snippet:', pageContent.substring(0, 500)); // Wait for the Navigation component to load and render await page.waitForSelector('nav', { timeout: 5000 }); // Wait for the session to be loaded (check for logout button directly) await page.waitForSelector('text=Sign out', { timeout: 10000 }); // Check for logout button const logoutButton = await page.locator('text=Sign out').count(); console.log('Logout button count:', logoutButton); expect(logoutButton).toBeGreaterThan(0); }); test('Logout clears session and redirects to home', async ({ page }) => { // Login first await page.goto('/auth/login'); await page.fill('input[name="email"]', testEmail); await page.fill('input[name="password"]', testPassword); await page.click('button[type="submit"]'); // Wait for redirect to admin or player profile (indicates successful login) await page.waitForURL(/\/(admin|players)/, { timeout: 10000 }); // Navigate to home using reload to ensure session is loaded await page.reload(); await page.waitForLoadState('domcontentloaded'); // Wait for logout button to appear await page.waitForSelector('text=Sign out', { timeout: 10000 }); // Click logout await page.click('text=Sign out'); // Wait for redirect to login page await page.waitForURL('**/auth/login**', { timeout: 10000 }); }); test('After logout, protected pages redirect to login', async ({ page }) => { // Login first await page.goto('/auth/login'); await page.fill('input[name="email"]', testEmail); await page.fill('input[name="password"]', testPassword); await page.click('button[type="submit"]'); // Wait for redirect to admin or player profile (indicates successful login) await page.waitForURL(/\/(admin|players)/, { timeout: 10000 }); // Navigate to home using reload to ensure session is loaded await page.reload(); await page.waitForLoadState('domcontentloaded'); // Wait for logout button to appear await page.waitForSelector('text=Sign out', { timeout: 10000 }); // Logout await page.click('text=Sign out'); await page.waitForURL('**/auth/login**', { timeout: 10000 }); // Try to access admin page await page.goto('/admin'); // Should redirect to login await expect(page).toHaveURL(/.*auth\/login.*/); }); });