clean routes
This commit is contained in:
343
tests/integration/website/auth-guard.test.ts
Normal file
343
tests/integration/website/auth-guard.test.ts
Normal file
@@ -0,0 +1,343 @@
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user