import { test, expect } from '@playwright/test'; test.describe('AuthGuard - Route Protection', () => { test.beforeEach(async ({ page, context }) => { // Clear storage before each test to ensure clean state await context.clearCookies(); await page.goto('/'); await page.evaluate(() => { localStorage.clear(); sessionStorage.clear(); }); }); test('should redirect to login when accessing protected route without auth', async ({ page, }) => { // Try to access a protected route (if you have one) // For now, we'll test the root if it's protected // Adjust the route based on your actual protected routes await page.goto('/'); // If root is protected, should redirect to login // This depends on your AuthGuard implementation await page.waitForURL(/\/(login)?/, { timeout: 5000 }); // Should show login form await expect(page.locator('h2')).toContainText('Sign in to your account').or( expect(page.locator('h2')).toContainText('Create your account') ); }); test('should allow access to public routes without auth', async ({ page }) => { // Test login page await page.goto('/login'); await expect(page).toHaveURL('/login'); await expect(page.locator('h2')).toContainText('Sign in to your account'); // Test register page await page.goto('/register'); await expect(page).toHaveURL('/register'); await expect(page.locator('h2')).toContainText('Create your account'); // Test password reset page await page.goto('/password-reset'); await expect(page).toHaveURL('/password-reset'); await expect(page.locator('h2')).toContainText('Reset your password'); }); test('should persist authentication across page reloads', async ({ page }) => { // First, login with valid credentials await page.goto('/login'); // Fill and submit login form await page.locator('input[name="email"]').fill('test@example.com'); await page.locator('input[name="password"]').fill('TestPassword123!'); await page.locator('input[type="checkbox"]').check(); // Remember me await page.locator('button[type="submit"]').click(); // Wait for potential redirect await page.waitForTimeout(2000); // Manually set a mock token in localStorage for testing // In real scenario, this would come from successful login await page.evaluate(() => { const mockToken = { access_token: 'mock-access-token', refresh_token: 'mock-refresh-token', user: { id: 1, email: 'test@example.com', username: 'testuser', is_active: true, }, }; localStorage.setItem('auth_token', JSON.stringify(mockToken)); }); // Reload the page await page.reload(); // Should still have the token const hasToken = await page.evaluate(() => { return localStorage.getItem('auth_token') !== null; }); expect(hasToken).toBe(true); }); test('should clear authentication on logout', async ({ page }) => { // Set up authenticated state await page.goto('/'); await page.evaluate(() => { const mockToken = { access_token: 'mock-access-token', refresh_token: 'mock-refresh-token', user: { id: 1, email: 'test@example.com', username: 'testuser', is_active: true, }, }; localStorage.setItem('auth_token', JSON.stringify(mockToken)); }); // Reload to apply token await page.reload(); // Simulate logout by clearing storage await page.evaluate(() => { localStorage.clear(); sessionStorage.clear(); }); // Reload page await page.reload(); // Should redirect to login await page.waitForURL(/login/, { timeout: 5000 }).catch(() => { // If already on login, that's fine }); // Storage should be clear const hasToken = await page.evaluate(() => { return localStorage.getItem('auth_token') === null; }); expect(hasToken).toBe(true); }); test('should not allow access to auth pages when already logged in', async ({ page }) => { // Set up authenticated state await page.goto('/'); await page.evaluate(() => { const mockToken = { access_token: 'mock-access-token', refresh_token: 'mock-refresh-token', user: { id: 1, email: 'test@example.com', username: 'testuser', is_active: true, }, }; localStorage.setItem('auth_token', JSON.stringify(mockToken)); }); // Try to access login page await page.goto('/login'); // Depending on your implementation: // - Should redirect away from login // - Or show a message that user is already logged in // Adjust this assertion based on your actual behavior // Wait a bit for potential redirect await page.waitForTimeout(2000); // Check if we got redirected or if login page shows "already logged in" const currentUrl = page.url(); const isOnLoginPage = currentUrl.includes('/login'); if (!isOnLoginPage) { // Good - redirected away from login expect(currentUrl).not.toContain('/login'); } else { // Might show "already logged in" message or redirect on interaction // This is implementation-dependent } }); test('should handle expired tokens gracefully', async ({ page }) => { // Set up authenticated state with expired token await page.goto('/'); await page.evaluate(() => { const expiredToken = { access_token: 'expired-access-token', refresh_token: 'expired-refresh-token', user: { id: 1, email: 'test@example.com', username: 'testuser', is_active: true, }, }; localStorage.setItem('auth_token', JSON.stringify(expiredToken)); }); // Try to access a protected route // Backend should return 401, triggering logout await page.reload(); // Wait for potential redirect to login await page.waitForTimeout(3000); // Should eventually redirect to login or clear token // This depends on your token refresh logic }); test('should preserve intended destination after login', async ({ page }) => { // Try to access a protected route await page.goto('/dashboard'); // Adjust to your actual protected route // Should redirect to login await page.waitForURL(/login/, { timeout: 5000 }).catch(() => {}); // Login await page.evaluate(() => { const mockToken = { access_token: 'mock-access-token', refresh_token: 'mock-refresh-token', user: { id: 1, email: 'test@example.com', username: 'testuser', is_active: true, }, }; localStorage.setItem('auth_token', JSON.stringify(mockToken)); }); // Reload or navigate await page.reload(); // Depending on your implementation, should redirect to intended route // This is a nice-to-have feature }); });