import { test, expect } from '@playwright/test'; import { WebsiteRouteManager } from '../../shared/website/WebsiteRouteManager'; import { WebsiteAuthManager } from '../../shared/website/WebsiteAuthManager'; import { ConsoleErrorCapture } from '../../shared/website/ConsoleErrorCapture'; const WEBSITE_BASE_URL = process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:3000'; test.describe('Website Pages - TypeORM Integration', () => { let routeManager: WebsiteRouteManager; test.beforeEach(() => { routeManager = new WebsiteRouteManager(); }); test('website loads and connects to API', async ({ page }) => { // Test that the website loads const response = await page.goto(WEBSITE_BASE_URL); expect(response?.ok()).toBe(true); // Check that the page renders (body is visible) await expect(page.locator('body')).toBeVisible(); }); test('all routes from RouteConfig are discoverable', async () => { expect(() => routeManager.getWebsiteRouteInventory()).not.toThrow(); }); test('public routes are accessible without authentication', async ({ page }) => { const routes = routeManager.getWebsiteRouteInventory(); const publicRoutes = routes.filter(r => r.access === 'public').slice(0, 5); for (const route of publicRoutes) { const path = routeManager.resolvePathTemplate(route.pathTemplate, route.params); const response = await page.goto(`${WEBSITE_BASE_URL}${path}`); const status = response?.status(); const finalUrl = page.url(); console.log(`[TEST DEBUG] Public route - Path: ${path}, Status: ${status}, Final URL: ${finalUrl}`); if (status === 500) { console.log(`[TEST DEBUG] 500 error on ${path} - Page title: ${await page.title()}`); } // The /500 error page intentionally returns 500 status // All other routes should load successfully or show 404 if (path === '/500') { expect(response?.status()).toBe(500); } else { expect(response?.ok() || response?.status() === 404).toBeTruthy(); } } }); test('protected routes redirect unauthenticated users to login', async ({ page }) => { const routes = routeManager.getWebsiteRouteInventory(); const protectedRoutes = routes.filter(r => r.access !== 'public').slice(0, 3); for (const route of protectedRoutes) { const path = routeManager.resolvePathTemplate(route.pathTemplate, route.params); await page.goto(`${WEBSITE_BASE_URL}${path}`); const currentUrl = new URL(page.url()); expect(currentUrl.pathname).toBe('/auth/login'); expect(currentUrl.searchParams.get('returnTo')).toBe(path); } }); test('admin routes require admin role', async ({ browser, request }) => { const routes = routeManager.getWebsiteRouteInventory(); const adminRoutes = routes.filter(r => r.access === 'admin').slice(0, 2); for (const route of adminRoutes) { const path = routeManager.resolvePathTemplate(route.pathTemplate, route.params); // Regular auth user should be redirected to their home page (dashboard) { const auth = await WebsiteAuthManager.createAuthContext(browser, request, 'auth'); const response = await auth.page.goto(`${WEBSITE_BASE_URL}${path}`); const finalUrl = auth.page.url(); console.log(`[TEST DEBUG] Admin route test - Path: ${path}`); console.log(`[TEST DEBUG] Response status: ${response?.status()}`); console.log(`[TEST DEBUG] Final URL: ${finalUrl}`); console.log(`[TEST DEBUG] Page title: ${await auth.page.title()}`); expect(auth.page.url().includes('dashboard')).toBeTruthy(); await auth.context.close(); } // Admin user should have access { const admin = await WebsiteAuthManager.createAuthContext(browser, request, 'admin'); await admin.page.goto(`${WEBSITE_BASE_URL}${path}`); expect(admin.page.url().includes(path)).toBeTruthy(); await admin.context.close(); } } }); test('sponsor routes require sponsor role', async ({ browser, request }) => { const routes = routeManager.getWebsiteRouteInventory(); const sponsorRoutes = routes.filter(r => r.access === 'sponsor').slice(0, 2); for (const route of sponsorRoutes) { const path = routeManager.resolvePathTemplate(route.pathTemplate, route.params); // Regular auth user should be redirected to their home page (dashboard) { const auth = await WebsiteAuthManager.createAuthContext(browser, request, 'auth'); await auth.page.goto(`${WEBSITE_BASE_URL}${path}`); const finalUrl = auth.page.url(); console.log(`[DEBUG] Final URL: ${finalUrl}`); console.log(`[DEBUG] Includes 'dashboard': ${finalUrl.includes('dashboard')}`); expect(finalUrl.includes('dashboard')).toBeTruthy(); await auth.context.close(); } // Sponsor user should have access { const sponsor = await WebsiteAuthManager.createAuthContext(browser, request, 'sponsor'); await sponsor.page.goto(`${WEBSITE_BASE_URL}${path}`); expect(sponsor.page.url().includes(path)).toBeTruthy(); await sponsor.context.close(); } } }); test('auth routes redirect authenticated users away', async ({ browser, request }) => { const routes = routeManager.getWebsiteRouteInventory(); const authRoutes = routes.filter(r => r.access === 'auth').slice(0, 2); for (const route of authRoutes) { const path = routeManager.resolvePathTemplate(route.pathTemplate, route.params); const auth = await WebsiteAuthManager.createAuthContext(browser, request, 'auth'); await auth.page.goto(`${WEBSITE_BASE_URL}${path}`); // Should redirect to dashboard or stay on the page const currentUrl = auth.page.url(); expect(currentUrl.includes('dashboard') || currentUrl.includes(path)).toBeTruthy(); await auth.context.close(); } }); test('parameterized routes handle edge cases', async ({ page }) => { const edgeCases = routeManager.getParamEdgeCases(); for (const route of edgeCases) { const path = routeManager.resolvePathTemplate(route.pathTemplate, route.params); const response = await page.goto(`${WEBSITE_BASE_URL}${path}`); // Client-side pages return 200 even when data doesn't exist // They show error messages in the UI instead of HTTP 404 // This is expected behavior for CSR pages in Next.js if (route.allowNotFound) { const status = response?.status(); expect([200, 404, 500].includes(status ?? 0)).toBeTruthy(); // If it's 200, verify error message is shown in the UI if (status === 200) { const bodyText = await page.textContent('body'); const hasErrorMessage = bodyText?.includes('not found') || bodyText?.includes('doesn\'t exist') || bodyText?.includes('Error'); expect(hasErrorMessage).toBeTruthy(); } } } }); test('no console or page errors on critical routes', async ({ page }) => { const faultRoutes = routeManager.getFaultInjectionRoutes(); for (const route of faultRoutes) { const capture = new ConsoleErrorCapture(page); const path = routeManager.resolvePathTemplate(route.pathTemplate, route.params); await page.goto(`${WEBSITE_BASE_URL}${path}`); await page.waitForTimeout(500); const errors = capture.getErrors(); // Filter out known/expected errors const unexpectedErrors = errors.filter(error => { const msg = error.message.toLowerCase(); // Filter out hydration warnings and other expected Next.js warnings return !msg.includes('hydration') && !msg.includes('text content does not match') && !msg.includes('warning:') && !msg.includes('download the react devtools'); }); if (unexpectedErrors.length > 0) { console.log(`[TEST DEBUG] Unexpected errors on ${path}:`, unexpectedErrors); } expect(unexpectedErrors.length).toBe(0); } }); test('TypeORM session persistence across routes', async ({ page }) => { const routes = routeManager.getWebsiteRouteInventory(); const testRoutes = routes.filter(r => r.access === 'public').slice(0, 5); for (const route of testRoutes) { const path = routeManager.resolvePathTemplate(route.pathTemplate, route.params); const response = await page.goto(`${WEBSITE_BASE_URL}${path}`); // The /500 error page intentionally returns 500 status if (path === '/500') { expect(response?.status()).toBe(500); } else { expect(response?.ok() || response?.status() === 404).toBeTruthy(); } } }); test('auth drift scenarios', async ({ page }) => { const driftRoutes = routeManager.getAuthDriftRoutes(); for (const route of driftRoutes) { const path = routeManager.resolvePathTemplate(route.pathTemplate, route.params); // Try accessing protected route without auth await page.goto(`${WEBSITE_BASE_URL}${path}`); const currentUrl = page.url(); expect(currentUrl.includes('login') || currentUrl.includes('auth')).toBeTruthy(); } }); test('handles invalid routes gracefully', async ({ page }) => { const invalidRoutes = [ '/invalid-route', '/leagues/invalid-id', '/drivers/invalid-id', ]; for (const route of invalidRoutes) { const response = await page.goto(`${WEBSITE_BASE_URL}${route}`); const status = response?.status(); const url = page.url(); expect([200, 404].includes(status ?? 0) || url.includes('/auth/login')).toBe(true); } }); });