import { test, expect } from '@playwright/test'; test.describe('API Routing Tests', () => { test.beforeEach(async ({ page }) => { // Clear cookies and localStorage before each test await page.context().clearCookies(); await page.evaluate(() => { localStorage.clear(); }); }); test('Trip creation uses correct /juntete/api endpoint', async ({ page }) => { // Enable request interception const apiRequests: { url: string; method: string; status?: number }[] = []; page.on('request', request => { const url = request.url(); if (url.includes('/api/trips')) { apiRequests.push({ url: url, method: request.method(), }); } }); page.on('response', response => { const url = response.url(); if (url.includes('/api/trips')) { const requestIndex = apiRequests.findIndex(req => req.url === url); if (requestIndex !== -1) { apiRequests[requestIndex].status = response.status(); } } }); // Navigate to create page await page.goto('http://localhost:3002?create=true'); // Fill out the trip creation form await page.fill('input#trip-name', 'API Routing Test Trip'); await page.fill('input#creator-name', 'Test User'); await page.selectOption('select#currency', 'USD'); // Submit the form await page.click('button:has-text("Create Trip")'); // Wait for success state or timeout try { await expect(page.locator('text=Trip Created!')).toBeVisible({ timeout: 10000 }); } catch (e) { // Even if trip creation fails, we can still check the API routing console.log('Trip creation may have failed, but checking API routing...'); } // Analyze API requests console.log('API Requests captured:', apiRequests); // Should have at least one API request expect(apiRequests.length).toBeGreaterThan(0); // Check if any requests go to incorrect endpoint const incorrectRequests = apiRequests.filter(req => req.url.includes('/api/trips') && !req.url.includes('/juntete/api/trips') ); // Check if requests go to correct endpoint const correctRequests = apiRequests.filter(req => req.url.includes('/juntete/api/trips') ); console.log('Correct API requests:', correctRequests.length); console.log('Incorrect API requests:', incorrectRequests.length); // Assert that all API requests use the correct endpoint expect(incorrectRequests.length, `Found requests to incorrect endpoint: ${incorrectRequests.map(r => r.url).join(', ')}`).toBe(0); expect(correctRequests.length, 'Should have at least one request to correct endpoint').toBeGreaterThan(0); }); test('Join trip uses correct /juntete/api endpoint', async ({ page }) => { // First create a trip to get a share code await page.goto('http://localhost:3002?create=true'); await page.fill('input#trip-name', 'Join API Test Trip'); await page.fill('input#creator-name', 'Creator'); await page.click('button:has-text("Create Trip")'); try { await expect(page.locator('text=Trip Created!')).toBeVisible({ timeout: 10000 }); } catch (e) { console.log('Trip creation failed, but continuing test...'); } // Extract share code if possible let shareCode = 'ABC123'; // Fallback for testing try { const shareCodeElement = page.locator('.bg-gray-100 .text-2xl, [class*="bg-gray"] .text-2xl').first(); if (await shareCodeElement.isVisible({ timeout: 3000 })) { const extractedCode = await shareCodeElement.textContent(); if (extractedCode) { shareCode = extractedCode.trim(); } } } catch (e) { console.log('Could not extract share code, using fallback'); } // Clear localStorage and set up request monitoring await page.evaluate(() => localStorage.clear()); const apiRequests: { url: string; method: string; status?: number }[] = []; page.on('request', request => { const url = request.url(); if (url.includes('/api/trips') || url.includes('/api/auth')) { apiRequests.push({ url: url, method: request.method(), }); } }); // Navigate to join page with share code await page.goto(`http://localhost:3002/join?code=${shareCode}`); // Wait a bit to see if any API requests are made await page.waitForTimeout(3000); // Analyze API requests console.log('Join API Requests captured:', apiRequests); // Check if any requests go to incorrect endpoint const incorrectRequests = apiRequests.filter(req => (req.url.includes('/api/trips') || req.url.includes('/api/auth')) && !req.url.includes('/juntete/api/') ); // Check if requests go to correct endpoint const correctRequests = apiRequests.filter(req => req.url.includes('/juntete/api/') ); console.log('Correct join API requests:', correctRequests.length); console.log('Incorrect join API requests:', incorrectRequests.length); // Assert that all API requests use the correct endpoint expect(incorrectRequests.length, `Found incorrect join requests: ${incorrectRequests.map(r => r.url).join(', ')}`).toBe(0); }); test('Trip page loading uses correct /juntete/api endpoint', async ({ page }) => { // First create a trip and navigate to it await page.goto('http://localhost:3002?create=true'); await page.fill('input#trip-name', 'Trip Page API Test'); await page.fill('input#creator-name', 'Test User'); await page.click('button:has-text("Create Trip")'); try { await expect(page.locator('text=Trip Created!')).toBeVisible({ timeout: 10000 }); } catch (e) { console.log('Trip creation failed, but continuing...'); } // Set up request monitoring const apiRequests: { url: string; method: string; status?: number }[] = []; page.on('request', request => { const url = request.url(); if (url.includes('/api/') && !url.includes('/api/auth/identify')) { apiRequests.push({ url: url, method: request.method(), }); } }); // Try to go to trip dashboard (this might work even if creation failed) await page.click('button:has-text("Go to Trip Dashboard")'); // Wait for navigation or error await page.waitForTimeout(5000); // Analyze API requests console.log('Trip page API Requests captured:', apiRequests); // Check for incorrect endpoints const incorrectRequests = apiRequests.filter(req => req.url.includes('/api/') && !req.url.includes('/juntete/api/') ); // Check for correct endpoints const correctRequests = apiRequests.filter(req => req.url.includes('/juntete/api/') ); console.log('Correct trip page API requests:', correctRequests.length); console.log('Incorrect trip page API requests:', incorrectRequests.length); // Assert routing is correct expect(incorrectRequests.length, `Found incorrect trip page requests: ${incorrectRequests.map(r => r.url).join(', ')}`).toBe(0); }); test('JavaScript bundle analysis for API configuration', async ({ page }) => { // Navigate to the page await page.goto('http://localhost:3002?create=true'); // Get all JavaScript bundles loaded const jsBundles = await page.evaluate(() => { const scripts = Array.from(document.querySelectorAll('script[src]')); return scripts.map(script => script.getAttribute('src')).filter(Boolean); }); console.log('JavaScript bundles found:', jsBundles.length); // Check if any bundles contain hardcoded API calls const hardcodedApiCalls: { bundle: string; calls: string[] }[] = []; for (const bundle of jsBundles.slice(0, 3)) { // Check first 3 bundles if (!bundle) continue; try { const bundleContent = await page.evaluate((url) => { return fetch(url).then(response => response.text()); }, bundle); // Look for patterns that indicate hardcoded API calls const directFetchCalls = (bundleContent.match(/fetch\s*\(\s*['"`]\/api\//g) || []).length; const correctApiCalls = (bundleContent.match(/fetch\s*\(\s*['"`]\/juntete\/api\//g) || []).length; const axiosBaseUrls = (bundleContent.match(/baseURL:\s*['"`]\/juntete['"`]/g) || []).length; if (directFetchCalls > 0 || correctApiCalls > 0 || axiosBaseUrls > 0) { hardcodedApiCalls.push({ bundle: bundle, calls: [ `Direct /api fetch calls: ${directFetchCalls}`, `Correct /juntete/api fetch calls: ${correctApiCalls}`, `Axios baseURL configs: ${axiosBaseUrls}` ] }); } } catch (error) { console.log(`Could not analyze bundle ${bundle}:`, error); } } console.log('JavaScript bundle analysis results:'); hardcodedApiCalls.forEach(result => { console.log(`Bundle: ${result.bundle}`); result.calls.forEach(call => console.log(` - ${call}`)); }); // Verify no hardcoded /api calls without /juntete const hasHardcodedIncorrectCalls = hardcodedApiCalls.some(result => result.calls.some(call => call.includes('Direct /api fetch calls:') && !call.includes('Direct /api fetch calls: 0')) ); expect(hasHardcodedIncorrectCalls, 'Found JavaScript bundles with hardcoded incorrect API calls').toBe(false); }); test('Network request inspection for all API calls', async ({ page }) => { // Enable comprehensive request monitoring const allRequests: { url: string; method: string; resourceType: string }[] = []; page.on('request', request => { allRequests.push({ url: request.url(), method: request.method(), resourceType: request.resourceType(), }); }); // Navigate and interact with the app await page.goto('http://localhost:3002?create=true'); // Try to fill form and submit await page.fill('input#trip-name', 'Network Test Trip'); await page.fill('input#creator-name', 'Test User'); await page.click('button:has-text("Create Trip")'); // Wait for completion or timeout try { await expect(page.locator('text=Trip Created!')).toBeVisible({ timeout: 8000 }); // Try to navigate to trip page await page.click('button:has-text("Go to Trip Dashboard")'); await page.waitForTimeout(3000); } catch (e) { console.log('Trip flow failed, but analyzing network requests...'); } // Filter API-related requests const apiRequests = allRequests.filter(req => req.url.includes('/api/') && (req.resourceType === 'xhr' || req.resourceType === 'fetch') ); console.log('All API requests captured:'); apiRequests.forEach(req => { console.log(` ${req.method} ${req.url}`); }); // Check for incorrect routing const incorrectApiRequests = apiRequests.filter(req => req.url.includes('/api/') && !req.url.includes('/juntete/api/') ); const correctApiRequests = apiRequests.filter(req => req.url.includes('/juntete/api/') ); console.log(`Total API requests: ${apiRequests.length}`); console.log(`Correct API requests: ${correctApiRequests.length}`); console.log(`Incorrect API requests: ${incorrectApiRequests.length}`); // Final assertions if (apiRequests.length > 0) { expect(incorrectApiRequests.length, `Found ${incorrectApiRequests.length} incorrectly routed API calls`).toBe(0); if (correctApiRequests.length === 0 && apiRequests.length > 0) { console.warn('Warning: API requests found but none are correctly routed to /juntete/api'); } } else { console.log('No API requests were captured during the test'); } }); }); test.describe('API Routing Regression Tests', () => { test('Verify no hardcoded fetch calls exist in source code', async ({ page }) => { // This test checks the actual page content for signs of hardcoded API calls await page.goto('http://localhost:3002?create=true'); // Check page source for problematic patterns const pageSource = await page.content(); // Look for potential hardcoded API calls in scripts const hardcodedPatterns = [ /fetch\s*\(\s*['"`]\/api\//g, /['"`]\/api\/trips['"`]/g, /['"`]\/api\/participants['"`]/g, /['"`]\/api\/expenses['"`]/g, ]; let foundHardcodedCalls = false; const foundPatterns: string[] = []; hardcodedPatterns.forEach((pattern, index) => { const matches = pageSource.match(pattern); if (matches && matches.length > 0) { foundHardcodedCalls = true; foundPatterns.push(`Pattern ${index + 1}: ${matches.length} matches`); } }); if (foundHardcodedCalls) { console.log('Potential hardcoded API calls found:', foundPatterns); } expect(foundHardcodedCalls, 'Found potential hardcoded API calls in page source').toBe(false); }); });