import { test, expect } from '@playwright/test'; import { setWebsiteAuthContext } from './websiteAuth'; /** * Website AuthGuard Component Tests * * These tests verify the AuthGuard component behavior: * - Loading states during session verification * - Redirect behavior for unauthorized access * - Role-based access control * - Component rendering with different auth states */ 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('AuthGuard Component - Loading States', () => { test('shows loading state during session verification', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); // Monitor for loading indicators page.on('request', async (req) => { if (req.url().includes('/auth/session')) { // Check if loading indicator is visible during session fetch await page.locator('text=/Verifying authentication|Loading/').isVisible().catch(() => false); } }); await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); // Should eventually show dashboard content await expect(page.locator('body')).toBeVisible(); expect(page.url()).toContain('/dashboard'); }); test('handles rapid auth state changes', 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'); // Quickly switch to authenticated await setWebsiteAuthContext(context, 'auth'); await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); expect(new URL(page.url()).pathname).toBe('/dashboard'); // Quickly switch back await setWebsiteAuthContext(context, 'public'); await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); expect(new URL(page.url()).pathname).toBe('/auth/login'); }); test('handles session fetch failures gracefully', async ({ page, context }) => { // Clear cookies to simulate session fetch returning null await context.clearCookies(); await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); // Should redirect to login const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); }); }); test.describe('AuthGuard Component - Redirect Behavior', () => { test('redirects to login with returnTo parameter', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); const protectedRoutes = [ '/dashboard', '/profile', '/leagues/league-1/settings', '/sponsor/dashboard', ]; for (const route of protectedRoutes) { await page.goto(`${getWebsiteBaseUrl()}${route}`, { waitUntil: 'domcontentloaded' }); const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); expect(currentUrl.searchParams.get('returnTo')).toBe(route); } }); test('redirects back to protected route after login', async ({ page, context }) => { const targetRoute = '/leagues/league-1/settings'; // Start unauthenticated, try to access protected route await setWebsiteAuthContext(context, 'public'); await page.goto(`${getWebsiteBaseUrl()}${targetRoute}`, { waitUntil: 'domcontentloaded' }); // Verify redirect to login let currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); expect(currentUrl.searchParams.get('returnTo')).toBe(targetRoute); // Simulate login by switching auth context await setWebsiteAuthContext(context, 'admin'); await page.goto(`${getWebsiteBaseUrl()}${targetRoute}`, { waitUntil: 'domcontentloaded' }); // Should be on target route currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe(targetRoute); }); test('handles auth routes when authenticated', 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' }); const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/sponsor/dashboard'); }); }); test.describe('AuthGuard Component - Role-Based Access', () => { test('admin routes allow admin users', async ({ page, context }) => { await setWebsiteAuthContext(context, 'admin'); const adminRoutes = ['/admin', '/admin/users']; for (const route of adminRoutes) { const response = await page.goto(`${getWebsiteBaseUrl()}${route}`, { waitUntil: 'domcontentloaded' }); expect(response?.status()).toBe(200); expect(page.url()).toContain(route); } }); test('admin routes block non-admin users', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); const adminRoutes = ['/admin', '/admin/users']; for (const route of adminRoutes) { await page.goto(`${getWebsiteBaseUrl()}${route}`, { waitUntil: 'domcontentloaded' }); const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); } }); test('sponsor routes allow sponsor users', async ({ page, context }) => { await setWebsiteAuthContext(context, 'sponsor'); const sponsorRoutes = ['/sponsor', '/sponsor/dashboard', '/sponsor/settings']; for (const route of sponsorRoutes) { const response = await page.goto(`${getWebsiteBaseUrl()}${route}`, { waitUntil: 'domcontentloaded' }); expect(response?.status()).toBe(200); expect(page.url()).toContain(route); } }); test('sponsor routes block non-sponsor users', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); const sponsorRoutes = ['/sponsor', '/sponsor/dashboard', '/sponsor/settings']; for (const route of sponsorRoutes) { await page.goto(`${getWebsiteBaseUrl()}${route}`, { waitUntil: 'domcontentloaded' }); const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); } }); test('league admin routes require league admin role', async ({ page, context }) => { // Test as regular driver await setWebsiteAuthContext(context, 'auth'); await page.goto(`${getWebsiteBaseUrl()}/leagues/league-1/settings`, { waitUntil: 'domcontentloaded' }); let currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); // Test as admin (has access to league admin routes) await setWebsiteAuthContext(context, 'admin'); await page.goto(`${getWebsiteBaseUrl()}/leagues/league-1/settings`, { waitUntil: 'domcontentloaded' }); currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/leagues/league-1/settings'); }); test('authenticated users can access auth-required routes', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); const authRoutes = ['/dashboard', '/profile', '/onboarding']; for (const route of authRoutes) { const response = await page.goto(`${getWebsiteBaseUrl()}${route}`, { waitUntil: 'domcontentloaded' }); expect(response?.status()).toBe(200); expect(page.url()).toContain(route); } }); }); test.describe('AuthGuard Component - Component Rendering', () => { test('renders protected content when access granted', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); // Should render the dashboard content await expect(page.locator('body')).toBeVisible(); // Should not show loading or redirect messages const loadingText = await page.locator('text=/Verifying authentication|Redirecting/').count(); expect(loadingText).toBe(0); }); test('shows redirect message briefly before redirect', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); // This is hard to catch, but we can verify the final state await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); // Should end up at login const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); }); test('handles multiple AuthGuard instances on same page', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); // Visit a page that might have nested AuthGuards await page.goto(`${getWebsiteBaseUrl()}/leagues/league-1`, { waitUntil: 'domcontentloaded' }); // Should render correctly await expect(page.locator('body')).toBeVisible(); expect(page.url()).toContain('/leagues/league-1'); }); test('preserves child component state during auth checks', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); // Visit dashboard await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); // Should maintain component state (no full page reload) // This is verified by the fact that the page loads without errors await expect(page.locator('body')).toBeVisible(); }); }); test.describe('AuthGuard Component - Error Handling', () => { test('handles network errors during session check', async ({ page, context }) => { // Clear cookies to simulate failed session check await context.clearCookies(); await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); // Should redirect to login const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); }); test('handles invalid session data', async ({ page, context }) => { // Set invalid session cookie 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('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 missing required role data', async ({ page, context }) => { await setWebsiteAuthContext(context, 'sponsor', { sessionDrift: 'missing-sponsor-id' }); 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.describe('AuthGuard Component - Performance', () => { test('auth check completes within reasonable time', 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 5 seconds expect(endTime - startTime).toBeLessThan(5000); }); test('no excessive session checks', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); let sessionCheckCount = 0; page.on('request', (req) => { if (req.url().includes('/auth/session')) { sessionCheckCount++; } }); await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'networkidle' }); // Should check session once or twice (initial + maybe one refresh) expect(sessionCheckCount).toBeLessThan(3); }); test('handles concurrent route access', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); // Navigate to multiple routes rapidly const routes = ['/dashboard', '/profile', '/leagues', '/dashboard']; for (const route of routes) { await page.goto(`${getWebsiteBaseUrl()}${route}`, { waitUntil: 'domcontentloaded' }); expect(page.url()).toContain(route); } }); });