Files
gridpilot.gg/tests/integration/website/middleware.test.ts
2026-01-03 02:42:47 +01:00

438 lines
16 KiB
TypeScript

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);
}
});
});