import { test, expect } from '@playwright/test'; import { setWebsiteAuthContext, } from './websiteAuth'; import { getWebsiteRouteInventory, resolvePathTemplate, } from './websiteRouteInventory'; /** * Website Authentication Flow Integration Tests * * These tests verify the complete authentication flow including: * - Middleware route protection * - AuthGuard component functionality * - Session management and loading states * - Role-based access control * - Auth state transitions * - API integration */ function getWebsiteBaseUrl(): string { const configured = process.env.WEBSITE_BASE_URL ?? process.env.PLAYWRIGHT_BASE_URL; if (configured && configured.trim()) { return configured.trim().replace(/\/$/, ''); } return 'http://localhost:3100'; } test.describe('Website Auth Flow - Middleware Protection', () => { const routes = getWebsiteRouteInventory(); // Test public routes are accessible without auth test('public routes are accessible without authentication', async ({ page, context }) => { const publicRoutes = routes.filter(r => r.access === 'public'); expect(publicRoutes.length).toBeGreaterThan(0); for (const route of publicRoutes.slice(0, 5)) { // Test first 5 to keep test fast const resolvedPath = resolvePathTemplate(route.pathTemplate, route.params); await setWebsiteAuthContext(context, 'public'); const response = await page.goto(`${getWebsiteBaseUrl()}${resolvedPath}`, { waitUntil: 'domcontentloaded' }); expect(response?.status()).toBe(200); await expect(page.locator('body')).toBeVisible(); } }); // Test protected routes redirect unauthenticated users test('protected routes redirect unauthenticated users to login', async ({ page, context }) => { const protectedRoutes = routes.filter(r => r.access !== 'public'); expect(protectedRoutes.length).toBeGreaterThan(0); for (const route of protectedRoutes.slice(0, 3)) { // Test first 3 const resolvedPath = resolvePathTemplate(route.pathTemplate, route.params); await setWebsiteAuthContext(context, 'public'); await page.goto(`${getWebsiteBaseUrl()}${resolvedPath}`, { waitUntil: 'domcontentloaded' }); const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); expect(currentUrl.searchParams.get('returnTo')).toBe(resolvedPath); } }); // Test authenticated users can access protected routes test('authenticated users can access protected routes', async ({ page, context }) => { const authRoutes = routes.filter(r => r.access === 'auth'); expect(authRoutes.length).toBeGreaterThan(0); for (const route of authRoutes.slice(0, 3)) { const resolvedPath = resolvePathTemplate(route.pathTemplate, route.params); await setWebsiteAuthContext(context, 'auth'); const response = await page.goto(`${getWebsiteBaseUrl()}${resolvedPath}`, { waitUntil: 'domcontentloaded' }); expect(response?.status()).toBe(200); await expect(page.locator('body')).toBeVisible(); } }); }); test.describe('Website Auth Flow - AuthGuard Component', () => { test('dashboard route shows loading state then content', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); const navigationPromise = page.waitForNavigation({ waitUntil: 'domcontentloaded' }); await page.goto(`${getWebsiteBaseUrl()}/dashboard`); await navigationPromise; // Should show loading briefly then render dashboard await expect(page.locator('body')).toBeVisible(); expect(page.url()).toContain('/dashboard'); }); test('dashboard redirects unauthenticated users', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); // Should redirect to login with returnTo parameter const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); expect(currentUrl.searchParams.get('returnTo')).toBe('/dashboard'); }); test('admin routes require admin role', async ({ page, context }) => { // Test as regular driver (should be denied) await setWebsiteAuthContext(context, 'auth'); await page.goto(`${getWebsiteBaseUrl()}/admin`, { waitUntil: 'domcontentloaded' }); // Should redirect to login (no admin role) const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); // Test as admin (should be allowed) await setWebsiteAuthContext(context, 'admin'); await page.goto(`${getWebsiteBaseUrl()}/admin`, { waitUntil: 'domcontentloaded' }); expect(page.url()).toContain('/admin'); await expect(page.locator('body')).toBeVisible(); }); test('sponsor routes require sponsor role', async ({ page, context }) => { // Test as driver (should be denied) await setWebsiteAuthContext(context, 'auth'); await page.goto(`${getWebsiteBaseUrl()}/sponsor/dashboard`, { waitUntil: 'domcontentloaded' }); const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); // Test as sponsor (should be allowed) await setWebsiteAuthContext(context, 'sponsor'); await page.goto(`${getWebsiteBaseUrl()}/sponsor/dashboard`, { waitUntil: 'domcontentloaded' }); expect(page.url()).toContain('/sponsor/dashboard'); await expect(page.locator('body')).toBeVisible(); }); }); test.describe('Website Auth Flow - Session Management', () => { test('session is properly loaded on page visit', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); // Visit dashboard await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); // Verify session is available by checking for user-specific content // (This would depend on your actual UI, but we can verify no errors) await expect(page.locator('body')).toBeVisible(); }); test('logout clears session and redirects', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); // Go to dashboard await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); await expect(page.locator('body')).toBeVisible(); // Find and click logout (assuming it exists) // This test would need to be adapted based on actual logout implementation // For now, we'll test that clearing cookies works await context.clearCookies(); await page.reload({ waitUntil: 'domcontentloaded' }); // Should redirect to login const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); }); test('auth state transitions work correctly', async ({ page, context }) => { // Start unauthenticated await setWebsiteAuthContext(context, 'public'); await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); expect(new URL(page.url()).pathname).toBe('/auth/login'); // Simulate login by setting auth context await setWebsiteAuthContext(context, 'auth'); await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); expect(new URL(page.url()).pathname).toBe('/dashboard'); // Simulate logout await setWebsiteAuthContext(context, 'public'); await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); expect(new URL(page.url()).pathname).toBe('/auth/login'); }); }); test.describe('Website Auth Flow - API Integration', () => { test('session endpoint returns correct data', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); // Direct API call to verify session endpoint const response = await page.request.get(`${getWebsiteBaseUrl()}/api/auth/session`); expect(response.ok()).toBe(true); const session = await response.json(); expect(session).toBeDefined(); }); test('normal login flow works', async ({ page, context }) => { // Clear any existing cookies await context.clearCookies(); // Navigate to login page await page.goto(`${getWebsiteBaseUrl()}/auth/login`, { waitUntil: 'domcontentloaded' }); // Verify login page loads await expect(page.locator('body')).toBeVisible(); // Note: Actual login form interaction would go here // For now, we'll test the API endpoint directly const response = await page.request.post(`${getWebsiteBaseUrl()}/api/auth/login`, { data: { email: 'demo.driver@example.com', password: 'Demo1234!' } }); expect(response.ok()).toBe(true); // Verify cookies were set const cookies = await context.cookies(); const gpSession = cookies.find(c => c.name === 'gp_session'); expect(gpSession).toBeDefined(); }); test('auth API handles login with seeded credentials', async ({ page }) => { // Test normal login with seeded demo user credentials const response = await page.request.post(`${getWebsiteBaseUrl()}/api/auth/login`, { data: { email: 'demo.driver@example.com', password: 'Demo1234!' } }); expect(response.ok()).toBe(true); const session = await response.json(); expect(session.user).toBeDefined(); expect(session.user.email).toBe('demo.driver@example.com'); }); }); test.describe('Website Auth Flow - Edge Cases', () => { test('handles auth state drift gracefully', async ({ page, context }) => { // Set sponsor context but with missing sponsor ID await setWebsiteAuthContext(context, 'sponsor', { sessionDrift: 'missing-sponsor-id' }); await page.goto(`${getWebsiteBaseUrl()}/sponsor/dashboard`, { waitUntil: 'domcontentloaded' }); // Should redirect to login due to invalid session const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); }); test('handles expired session', async ({ page, context }) => { await setWebsiteAuthContext(context, 'sponsor', { sessionDrift: 'expired' }); await page.goto(`${getWebsiteBaseUrl()}/sponsor/dashboard`, { waitUntil: 'domcontentloaded' }); // Should redirect to login const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); }); test('handles invalid session cookie', async ({ page, context }) => { await setWebsiteAuthContext(context, 'sponsor', { sessionDrift: 'invalid-cookie' }); await page.goto(`${getWebsiteBaseUrl()}/sponsor/dashboard`, { waitUntil: 'domcontentloaded' }); // Should redirect to login const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); }); test('public routes accessible even with invalid auth cookies', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public', { sessionDrift: 'invalid-cookie' }); await page.goto(`${getWebsiteBaseUrl()}/leagues`, { waitUntil: 'domcontentloaded' }); // Should still work expect(page.url()).toContain('/leagues'); await expect(page.locator('body')).toBeVisible(); }); }); test.describe('Website Auth Flow - Redirect Scenarios', () => { test('auth routes redirect authenticated users away', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); // Try to access login page while authenticated await page.goto(`${getWebsiteBaseUrl()}/auth/login`, { waitUntil: 'domcontentloaded' }); // Should redirect to dashboard const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/dashboard'); }); test('sponsor auth routes redirect to sponsor dashboard', async ({ page, context }) => { await setWebsiteAuthContext(context, 'sponsor'); await page.goto(`${getWebsiteBaseUrl()}/auth/login`, { waitUntil: 'domcontentloaded' }); // Should redirect to sponsor dashboard const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/sponsor/dashboard'); }); test('returnTo parameter works correctly', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); const targetRoute = '/leagues/league-1/settings'; await page.goto(`${getWebsiteBaseUrl()}${targetRoute}`, { waitUntil: 'domcontentloaded' }); // Should redirect to login with returnTo const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); expect(currentUrl.searchParams.get('returnTo')).toBe(targetRoute); // After login, should return to target await setWebsiteAuthContext(context, 'admin'); await page.goto(`${getWebsiteBaseUrl()}${targetRoute}`, { waitUntil: 'domcontentloaded' }); expect(page.url()).toContain(targetRoute); }); }); test.describe('Website Auth Flow - Performance', () => { test('auth verification completes quickly', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); const startTime = Date.now(); await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); const endTime = Date.now(); // Should complete within reasonable time (under 5 seconds) expect(endTime - startTime).toBeLessThan(5000); // Should show content await expect(page.locator('body')).toBeVisible(); }); test('no infinite loading states', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); // Monitor for loading indicators let loadingCount = 0; page.on('request', (req) => { if (req.url().includes('/auth/session')) loadingCount++; }); await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'networkidle' }); // Should not make excessive session calls expect(loadingCount).toBeLessThan(3); // Should eventually show content await expect(page.locator('body')).toBeVisible(); }); });