import { test, expect } from '@playwright/test'; import { setWebsiteAuthContext } from './websiteAuth'; /** * Website Middleware Route Protection Tests * * These tests specifically verify the Next.js middleware behavior: * - Public routes are always accessible * - Protected routes require authentication * - Auth routes redirect authenticated users * - Role-based access control */ 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 Middleware - Public Route Protection', () => { test('root route is publicly accessible', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); const response = await page.goto(`${getWebsiteBaseUrl()}/`, { waitUntil: 'domcontentloaded' }); expect(response?.status()).toBe(200); await expect(page.locator('body')).toBeVisible(); }); test('league routes are publicly accessible', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); const routes = ['/leagues', '/leagues/league-1', '/leagues/league-1/standings']; for (const route of routes) { const response = await page.goto(`${getWebsiteBaseUrl()}${route}`, { waitUntil: 'domcontentloaded' }); expect(response?.status()).toBe(200); await expect(page.locator('body')).toBeVisible(); } }); test('driver routes are publicly accessible', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); const response = await page.goto(`${getWebsiteBaseUrl()}/drivers/driver-1`, { waitUntil: 'domcontentloaded' }); expect(response?.status()).toBe(200); await expect(page.locator('body')).toBeVisible(); }); test('team routes are publicly accessible', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); const response = await page.goto(`${getWebsiteBaseUrl()}/teams/team-1`, { waitUntil: 'domcontentloaded' }); expect(response?.status()).toBe(200); await expect(page.locator('body')).toBeVisible(); }); test('race routes are publicly accessible', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); const response = await page.goto(`${getWebsiteBaseUrl()}/races/race-1`, { waitUntil: 'domcontentloaded' }); expect(response?.status()).toBe(200); await expect(page.locator('body')).toBeVisible(); }); test('leaderboard routes are publicly accessible', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); const response = await page.goto(`${getWebsiteBaseUrl()}/leaderboards`, { waitUntil: 'domcontentloaded' }); expect(response?.status()).toBe(200); await expect(page.locator('body')).toBeVisible(); }); test('sponsor signup is publicly accessible', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); const response = await page.goto(`${getWebsiteBaseUrl()}/sponsor/signup`, { waitUntil: 'domcontentloaded' }); expect(response?.status()).toBe(200); await expect(page.locator('body')).toBeVisible(); }); test('auth routes are publicly accessible', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); const authRoutes = [ '/auth/login', '/auth/signup', '/auth/forgot-password', '/auth/reset-password', '/auth/iracing', ]; for (const route of authRoutes) { const response = await page.goto(`${getWebsiteBaseUrl()}${route}`, { waitUntil: 'domcontentloaded' }); expect(response?.status()).toBe(200); await expect(page.locator('body')).toBeVisible(); } }); }); test.describe('Website Middleware - Protected Route Protection', () => { test('dashboard redirects unauthenticated users to login', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); expect(currentUrl.searchParams.get('returnTo')).toBe('/dashboard'); }); test('profile routes redirect unauthenticated users to login', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); const profileRoutes = ['/profile', '/profile/settings', '/profile/leagues']; for (const route of profileRoutes) { 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('admin routes redirect unauthenticated users to login', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); 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'); expect(currentUrl.searchParams.get('returnTo')).toBe(route); } }); test('sponsor routes redirect unauthenticated users to login', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); 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'); expect(currentUrl.searchParams.get('returnTo')).toBe(route); } }); test('league admin routes redirect unauthenticated users to login', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); const leagueAdminRoutes = [ '/leagues/league-1/roster/admin', '/leagues/league-1/schedule/admin', '/leagues/league-1/settings', '/leagues/league-1/stewarding', '/leagues/league-1/wallet', ]; for (const route of leagueAdminRoutes) { 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('onboarding redirects unauthenticated users to login', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); await page.goto(`${getWebsiteBaseUrl()}/onboarding`, { waitUntil: 'domcontentloaded' }); const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); expect(currentUrl.searchParams.get('returnTo')).toBe('/onboarding'); }); }); test.describe('Website Middleware - Authenticated Access', () => { test('dashboard is accessible when authenticated', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); const response = await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); expect(response?.status()).toBe(200); expect(page.url()).toContain('/dashboard'); await expect(page.locator('body')).toBeVisible(); }); test('profile routes are accessible when authenticated', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); const profileRoutes = ['/profile', '/profile/settings', '/profile/leagues']; for (const route of profileRoutes) { const response = await page.goto(`${getWebsiteBaseUrl()}${route}`, { waitUntil: 'domcontentloaded' }); expect(response?.status()).toBe(200); expect(page.url()).toContain(route); } }); test('onboarding is accessible when authenticated', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); const response = await page.goto(`${getWebsiteBaseUrl()}/onboarding`, { waitUntil: 'domcontentloaded' }); expect(response?.status()).toBe(200); expect(page.url()).toContain('/onboarding'); }); }); test.describe('Website Middleware - Role-Based Access', () => { 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' }); let 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' }); currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/admin'); }); 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' }); let 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' }); currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/sponsor/dashboard'); }); test('league admin routes require admin role', async ({ page, context }) => { // Test as regular driver (should be denied) 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 (should be allowed) 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('race stewarding routes require admin role', async ({ page, context }) => { // Test as regular driver (should be denied) await setWebsiteAuthContext(context, 'auth'); await page.goto(`${getWebsiteBaseUrl()}/races/race-1/stewarding`, { waitUntil: 'domcontentloaded' }); let 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()}/races/race-1/stewarding`, { waitUntil: 'domcontentloaded' }); currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/races/race-1/stewarding'); }); }); test.describe('Website Middleware - Auth Route Behavior', () => { test('auth routes redirect authenticated users away', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); const authRoutes = [ '/auth/login', '/auth/signup', '/auth/iracing', ]; for (const route of authRoutes) { await page.goto(`${getWebsiteBaseUrl()}${route}`, { waitUntil: 'domcontentloaded' }); 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'); const authRoutes = [ '/auth/login', '/auth/signup', '/auth/iracing', ]; for (const route of authRoutes) { await page.goto(`${getWebsiteBaseUrl()}${route}`, { waitUntil: 'domcontentloaded' }); const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/sponsor/dashboard'); } }); test('admin auth routes redirect to admin dashboard', async ({ page, context }) => { await setWebsiteAuthContext(context, 'admin'); const authRoutes = [ '/auth/login', '/auth/signup', '/auth/iracing', ]; for (const route of authRoutes) { await page.goto(`${getWebsiteBaseUrl()}${route}`, { waitUntil: 'domcontentloaded' }); const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/admin'); } }); }); test.describe('Website Middleware - Edge Cases', () => { test('handles trailing slashes correctly', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); const routes = [ { path: '/leagues', expected: '/leagues' }, { path: '/leagues/', expected: '/leagues' }, { path: '/drivers/driver-1', expected: '/drivers/driver-1' }, { path: '/drivers/driver-1/', expected: '/drivers/driver-1' }, ]; for (const { path, expected } of routes) { await page.goto(`${getWebsiteBaseUrl()}${path}`, { waitUntil: 'domcontentloaded' }); expect(page.url()).toContain(expected); } }); test('handles invalid routes gracefully', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); const invalidRoutes = [ '/invalid-route', '/leagues/invalid-id', '/drivers/invalid-id', ]; for (const route of invalidRoutes) { const response = await page.goto(`${getWebsiteBaseUrl()}${route}`, { waitUntil: 'domcontentloaded' }); // Should either show 404 or redirect to a valid page const status = response?.status(); const url = page.url(); expect([200, 404].includes(status ?? 0) || url.includes('/auth/login')).toBe(true); } }); test('preserves query parameters during redirects', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); const targetRoute = '/dashboard?tab=settings&filter=active'; await page.goto(`${getWebsiteBaseUrl()}${targetRoute}`, { waitUntil: 'domcontentloaded' }); const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); expect(currentUrl.searchParams.get('returnTo')).toBe('/dashboard?tab=settings&filter=active'); }); test('handles deeply nested routes', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); const deepRoute = '/leagues/league-1/stewarding/protests/protest-1'; await page.goto(`${getWebsiteBaseUrl()}${deepRoute}`, { waitUntil: 'domcontentloaded' }); const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); expect(currentUrl.searchParams.get('returnTo')).toBe(deepRoute); }); }); test.describe('Website Middleware - Performance', () => { test('middleware adds minimal overhead', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); const startTime = Date.now(); await page.goto(`${getWebsiteBaseUrl()}/leagues`, { waitUntil: 'domcontentloaded' }); const endTime = Date.now(); // Should complete quickly (under 3 seconds) expect(endTime - startTime).toBeLessThan(3000); }); test('no redirect loops', async ({ page, context }) => { await setWebsiteAuthContext(context, 'public'); // Try to access a protected route multiple times for (let i = 0; i < 3; i++) { await page.goto(`${getWebsiteBaseUrl()}/dashboard`, { waitUntil: 'domcontentloaded' }); const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); } }); test('handles rapid navigation', async ({ page, context }) => { await setWebsiteAuthContext(context, 'auth'); // Navigate between multiple protected 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); } }); });