import { test, expect, Page } from '@playwright/test'; /** * Comprehensive E2E Tests for Bet Creation and Taking Flows * * Test Plan: * 1. Test bet creation flow with Alice * 2. Test bet taking flow with Bob * 3. Verify JavaScript errors and network failures * 4. Document all issues found */ // Test data const TEST_USERS = { alice: { email: 'alice@example.com', password: 'password123' }, bob: { email: 'bob@example.com', password: 'password123' }, }; // Helper function to login async function login(page: Page, user: { email: string; password: string }) { await page.goto('/login'); await page.waitForLoadState('networkidle'); await page.fill('input[type="email"]', user.email); await page.fill('input[type="password"]', user.password); await page.click('button[type="submit"]'); await page.waitForLoadState('networkidle'); // Wait for redirect after login await page.waitForTimeout(1000); } // Helper function to logout async function logout(page: Page) { const logoutButton = page.getByRole('button', { name: /logout/i }); if (await logoutButton.isVisible()) { await logoutButton.click(); await page.waitForLoadState('networkidle'); } } // Helper to collect errors interface TestErrors { jsErrors: string[]; networkErrors: { url: string; status: number; statusText: string }[]; consoleErrors: string[]; } function setupErrorTracking(page: Page): TestErrors { const errors: TestErrors = { jsErrors: [], networkErrors: [], consoleErrors: [], }; page.on('pageerror', (error) => { errors.jsErrors.push(error.message); }); page.on('response', (response) => { if (response.status() >= 400) { errors.networkErrors.push({ url: response.url(), status: response.status(), statusText: response.statusText(), }); } }); page.on('console', (msg) => { if (msg.type() === 'error') { errors.consoleErrors.push(msg.text()); } }); return errors; } test.describe('Bet Creation and Taking - E2E Tests', () => { test.describe.configure({ mode: 'serial' }); test('TC-001: Verify home page loads with sport events', async ({ page }) => { const errors = setupErrorTracking(page); console.log('\n=== TC-001: Home Page Event Loading ==='); await page.goto('/'); await page.waitForLoadState('networkidle'); await page.screenshot({ path: 'tests/screenshots/tc001-home-page.png', fullPage: true }); // Check if events heading is visible const headingVisible = await page.getByRole('heading', { name: /events/i }).isVisible({ timeout: 5000 }).catch(() => false); console.log(`Events heading visible: ${headingVisible}`); // Check for event cards const eventCards = page.locator('button').filter({ hasText: /vs/i }); const eventCount = await eventCards.count(); console.log(`Found ${eventCount} event cards`); // Log any errors if (errors.jsErrors.length > 0) { console.log('JavaScript Errors:', errors.jsErrors); } if (errors.networkErrors.length > 0) { console.log('Network Errors:', errors.networkErrors); } expect(eventCount).toBeGreaterThan(0); }); test('TC-002: Alice logs in and navigates to Sport Events', async ({ page }) => { const errors = setupErrorTracking(page); console.log('\n=== TC-002: Alice Login and Navigate to Sport Events ==='); await login(page, TEST_USERS.alice); await page.screenshot({ path: 'tests/screenshots/tc002-alice-logged-in.png', fullPage: true }); // Navigate to Sport Events page const sportEventsLink = page.getByRole('link', { name: /sport.*events/i }); if (await sportEventsLink.isVisible()) { await sportEventsLink.click(); await page.waitForLoadState('networkidle'); console.log('Navigated to Sport Events via nav link'); } else { // Try direct navigation await page.goto('/sport-events'); await page.waitForLoadState('networkidle'); console.log('Navigated to Sport Events via direct URL'); } await page.screenshot({ path: 'tests/screenshots/tc002-sport-events-page.png', fullPage: true }); // Check for Sport Events heading const pageHeading = await page.getByRole('heading', { name: /sport events/i }).isVisible({ timeout: 5000 }).catch(() => false); console.log(`Sport Events heading visible: ${pageHeading}`); // Log any errors if (errors.jsErrors.length > 0) { console.log('JavaScript Errors:', errors.jsErrors); } expect(pageHeading).toBe(true); }); test('TC-003: Alice selects an event and views the spread grid', async ({ page }) => { const errors = setupErrorTracking(page); console.log('\n=== TC-003: Select Event and View Spread Grid ==='); await login(page, TEST_USERS.alice); await page.goto('/sport-events'); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); // Find and click on an event const eventButtons = page.locator('.grid button, button.bg-white').filter({ hasText: /vs/i }); const eventCount = await eventButtons.count(); console.log(`Found ${eventCount} events to select`); if (eventCount > 0) { await eventButtons.first().click(); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); await page.screenshot({ path: 'tests/screenshots/tc003-event-selected.png', fullPage: true }); // Check if spread grid is visible const spreadGridVisible = await page.locator('.grid').first().isVisible(); console.log(`Spread grid visible: ${spreadGridVisible}`); // Check for spread buttons (they show spreads like +3.5, -2.5, etc.) const spreadButtons = page.locator('button').filter({ hasText: /^[+-]?\d+\.?\d*$/ }); const spreadCount = await spreadButtons.count(); console.log(`Found ${spreadCount} spread buttons`); // Look for TradingPanel elements const tradingPanelVisible = await page.locator('text=Place Bet').isVisible({ timeout: 5000 }).catch(() => false); console.log(`TradingPanel "Place Bet" visible: ${tradingPanelVisible}`); } // Log any errors if (errors.jsErrors.length > 0) { console.log('JavaScript Errors:', errors.jsErrors); } if (errors.networkErrors.length > 0) { console.log('Network Errors:', errors.networkErrors); } }); test('TC-004: Alice attempts to create a bet via TradingPanel', async ({ page }) => { const errors = setupErrorTracking(page); console.log('\n=== TC-004: Alice Creates Bet via TradingPanel ==='); await login(page, TEST_USERS.alice); // Go to home page where events are shown with TradingPanel await page.goto('/'); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); // Check for event cards const eventCards = page.locator('button').filter({ hasText: /vs/i }); const eventCount = await eventCards.count(); console.log(`Found ${eventCount} events on home page`); if (eventCount > 0) { // Click on the first event await eventCards.first().click(); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); await page.screenshot({ path: 'tests/screenshots/tc004-event-details.png', fullPage: true }); // Look for the TradingPanel's stake input const stakeInput = page.locator('input[type="number"]'); const stakeInputVisible = await stakeInput.isVisible({ timeout: 5000 }).catch(() => false); console.log(`Stake input visible: ${stakeInputVisible}`); if (stakeInputVisible) { // Enter stake amount await stakeInput.fill('50'); console.log('Entered stake amount: 50'); await page.screenshot({ path: 'tests/screenshots/tc004-stake-entered.png', fullPage: true }); // Find the create bet button const createBetButton = page.locator('button').filter({ hasText: /create.*bet/i }); const createButtonVisible = await createBetButton.isVisible({ timeout: 3000 }).catch(() => false); console.log(`Create Bet button visible: ${createButtonVisible}`); if (createButtonVisible) { // Set up network request listener before clicking const createBetPromise = page.waitForResponse( (response) => response.url().includes('/api/v1/spread-bets') && response.request().method() === 'POST', { timeout: 10000 } ).catch(() => null); // Click create bet button await createBetButton.click(); console.log('Clicked Create Bet button'); await page.waitForTimeout(2000); await page.screenshot({ path: 'tests/screenshots/tc004-after-create-click.png', fullPage: true }); // Check for the API response const response = await createBetPromise; if (response) { console.log(`Create bet API response status: ${response.status()}`); if (response.status() >= 400) { const responseBody = await response.json().catch(() => ({})); console.log('API Error Response:', responseBody); } else { console.log('Bet created successfully via API'); } } else { console.log('No API call detected for bet creation'); } // Check for success/error toast const successToast = await page.locator('text=Bet created successfully').isVisible({ timeout: 3000 }).catch(() => false); const errorToast = await page.locator('[role="alert"], .toast, text=Failed').isVisible({ timeout: 1000 }).catch(() => false); console.log(`Success toast visible: ${successToast}`); console.log(`Error toast visible: ${errorToast}`); } } else { console.log('Stake input not found - checking alternative UI...'); // Try SpreadGrid approach - click on a spread cell const spreadCells = page.locator('button').filter({ hasText: /^[+-]?\d+\.?\d*$/ }); const spreadCellCount = await spreadCells.count(); console.log(`Found ${spreadCellCount} spread cells`); if (spreadCellCount > 0) { await spreadCells.first().click(); await page.waitForTimeout(1000); await page.screenshot({ path: 'tests/screenshots/tc004-spread-cell-clicked.png', fullPage: true }); // Check for modal const modalVisible = await page.locator('[role="dialog"], .modal, .fixed.inset-0').isVisible({ timeout: 3000 }).catch(() => false); console.log(`Modal visible after clicking spread: ${modalVisible}`); } } } // Log all collected errors console.log('\n--- Error Summary ---'); console.log('JavaScript Errors:', errors.jsErrors); console.log('Network Errors:', errors.networkErrors); console.log('Console Errors:', errors.consoleErrors); }); test('TC-005: Alice creates bet via SpreadGrid modal flow', async ({ page }) => { const errors = setupErrorTracking(page); console.log('\n=== TC-005: Alice Creates Bet via SpreadGrid Modal ==='); await login(page, TEST_USERS.alice); await page.goto('/sport-events'); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); // Select an event const eventButtons = page.locator('button').filter({ hasText: /vs/i }); if (await eventButtons.count() > 0) { await eventButtons.first().click(); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); await page.screenshot({ path: 'tests/screenshots/tc005-event-view.png', fullPage: true }); // Click on a spread cell to open modal const spreadCells = page.locator('button').filter({ hasText: /^[+-]?\d+\.?\d*$/ }); const spreadCount = await spreadCells.count(); console.log(`Found ${spreadCount} spread cells`); if (spreadCount > 0) { await spreadCells.nth(Math.floor(spreadCount / 2)).click(); // Click middle spread await page.waitForTimeout(1000); await page.screenshot({ path: 'tests/screenshots/tc005-spread-modal-opened.png', fullPage: true }); // Look for "Create New Bet" button in modal const createNewBetButton = page.locator('button').filter({ hasText: /create new bet|create bet/i }); const createButtonVisible = await createNewBetButton.isVisible({ timeout: 3000 }).catch(() => false); console.log(`"Create New Bet" button visible: ${createButtonVisible}`); if (createButtonVisible) { await createNewBetButton.click(); await page.waitForTimeout(1000); await page.screenshot({ path: 'tests/screenshots/tc005-create-bet-modal.png', fullPage: true }); // Fill in the stake amount const stakeInput = page.locator('input[type="number"]'); if (await stakeInput.isVisible()) { await stakeInput.fill('100'); console.log('Entered stake amount: 100'); await page.screenshot({ path: 'tests/screenshots/tc005-stake-filled.png', fullPage: true }); // Click submit button const submitButton = page.locator('button[type="submit"], button').filter({ hasText: /create bet/i }).first(); // Set up API listener const apiPromise = page.waitForResponse( (response) => response.url().includes('/api/v1/spread-bets') && response.request().method() === 'POST', { timeout: 10000 } ).catch(() => null); await submitButton.click(); console.log('Clicked submit button'); await page.waitForTimeout(2000); await page.screenshot({ path: 'tests/screenshots/tc005-after-submit.png', fullPage: true }); const response = await apiPromise; if (response) { console.log(`API Response Status: ${response.status()}`); const body = await response.json().catch(() => ({})); console.log('API Response Body:', JSON.stringify(body, null, 2)); if (response.status() >= 400) { console.log('DEFECT: Bet creation API returned error'); } } else { console.log('WARNING: No API call made for bet creation'); } // Check for success toast const successVisible = await page.locator('text=Bet created successfully').isVisible({ timeout: 3000 }).catch(() => false); console.log(`Success message visible: ${successVisible}`); } } } } // Log errors console.log('\n--- Error Summary ---'); console.log('JavaScript Errors:', errors.jsErrors); console.log('Network Errors:', errors.networkErrors); }); test('TC-006: Bob logs in and attempts to take a bet', async ({ page }) => { const errors = setupErrorTracking(page); console.log('\n=== TC-006: Bob Takes a Bet ==='); await login(page, TEST_USERS.bob); await page.goto('/sport-events'); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); // Select an event const eventButtons = page.locator('button').filter({ hasText: /vs/i }); if (await eventButtons.count() > 0) { await eventButtons.first().click(); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); await page.screenshot({ path: 'tests/screenshots/tc006-bob-event-view.png', fullPage: true }); // Look for spread cells with open bets (green background typically) const openBetCells = page.locator('button.bg-green-50, button').filter({ hasText: /open/i }); const openBetCount = await openBetCells.count(); console.log(`Found ${openBetCount} cells with open bets indicator`); // Click on any spread cell const spreadCells = page.locator('button').filter({ hasText: /^[+-]?\d+\.?\d*$/ }); if (await spreadCells.count() > 0) { await spreadCells.first().click(); await page.waitForTimeout(1000); await page.screenshot({ path: 'tests/screenshots/tc006-spread-detail-modal.png', fullPage: true }); // Look for "Take Bet" button const takeBetButton = page.locator('button').filter({ hasText: /take.*bet/i }); const takeBetVisible = await takeBetButton.isVisible({ timeout: 3000 }).catch(() => false); console.log(`Take Bet button visible: ${takeBetVisible}`); if (takeBetVisible) { await takeBetButton.first().click(); await page.waitForTimeout(1000); await page.screenshot({ path: 'tests/screenshots/tc006-take-bet-modal.png', fullPage: true }); // Check if TakeBetModal opened properly const modalTitle = await page.locator('text=Take Bet').isVisible({ timeout: 2000 }).catch(() => false); console.log(`TakeBetModal title visible: ${modalTitle}`); // Look for confirm button in TakeBetModal const confirmButton = page.locator('button').filter({ hasText: /take bet|confirm/i }).last(); if (await confirmButton.isVisible()) { // Set up API listener const apiPromise = page.waitForResponse( (response) => response.url().includes('/take') && response.request().method() === 'POST', { timeout: 10000 } ).catch(() => null); await confirmButton.click(); console.log('Clicked confirm/take button'); await page.waitForTimeout(2000); await page.screenshot({ path: 'tests/screenshots/tc006-after-take.png', fullPage: true }); const response = await apiPromise; if (response) { console.log(`Take Bet API Response Status: ${response.status()}`); const body = await response.json().catch(() => ({})); console.log('API Response Body:', JSON.stringify(body, null, 2)); } else { console.log('WARNING: No API call made for taking bet'); } } } else { console.log('No "Take Bet" button found - checking if there are available bets'); // Check modal content const modalContent = await page.locator('.fixed.inset-0').textContent().catch(() => ''); console.log('Modal content:', modalContent?.substring(0, 500)); } } } // Log errors console.log('\n--- Error Summary ---'); console.log('JavaScript Errors:', errors.jsErrors); console.log('Network Errors:', errors.networkErrors); }); test('TC-007: Test TradingPanel Create Bet Button Functionality', async ({ page }) => { const errors = setupErrorTracking(page); console.log('\n=== TC-007: TradingPanel Create Bet Button Test ==='); await login(page, TEST_USERS.alice); await page.goto('/'); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); // Click on an event to see TradingPanel const eventCards = page.locator('button').filter({ hasText: /vs/i }); if (await eventCards.count() > 0) { await eventCards.first().click(); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1500); // Take screenshot of the full event view await page.screenshot({ path: 'tests/screenshots/tc007-full-event-view.png', fullPage: true }); // Check if we're seeing TradingPanel or SpreadGrid const hasPlaceBetSection = await page.locator('text=Place Bet').isVisible().catch(() => false); const hasStakeInput = await page.locator('input[type="number"]').isVisible().catch(() => false); const hasTeamButtons = await page.locator('button').filter({ hasText: /HOME|AWAY/i }).count() > 0; console.log(`"Place Bet" section visible: ${hasPlaceBetSection}`); console.log(`Stake input visible: ${hasStakeInput}`); console.log(`Team selection buttons visible: ${hasTeamButtons}`); if (hasPlaceBetSection && hasStakeInput) { console.log('TradingPanel detected - testing create bet flow'); // Enter stake await page.locator('input[type="number"]').fill('75'); // Find and click Create Bet button const createButton = page.locator('button').filter({ hasText: /create.*bet/i }); if (await createButton.isVisible()) { // Monitor network const responsePromise = page.waitForResponse( (resp) => resp.url().includes('/api/v1/spread-bets') && resp.request().method() === 'POST', { timeout: 10000 } ).catch(() => null); await createButton.click(); console.log('Create Bet button clicked'); await page.waitForTimeout(2000); await page.screenshot({ path: 'tests/screenshots/tc007-after-create-click.png', fullPage: true }); const response = await responsePromise; if (response) { const status = response.status(); console.log(`API Response Status: ${status}`); if (status >= 200 && status < 300) { console.log('SUCCESS: Bet created via TradingPanel'); } else { const body = await response.json().catch(() => ({})); console.log('FAILURE: API returned error:', body); } } else { console.log('ISSUE: No API request was made when clicking Create Bet'); } } else { console.log('Create Bet button not found'); } } } console.log('\n--- Error Summary ---'); console.log('JavaScript Errors:', errors.jsErrors); console.log('Network Errors:', errors.networkErrors); }); test('TC-008: Comprehensive Error Detection on Event Page', async ({ page }) => { const errors = setupErrorTracking(page); console.log('\n=== TC-008: Comprehensive Error Detection ==='); // Test as authenticated user await login(page, TEST_USERS.alice); // Navigate to sport events await page.goto('/sport-events'); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); // Select first event const events = page.locator('button').filter({ hasText: /vs/i }); if (await events.count() > 0) { await events.first().click(); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1500); await page.screenshot({ path: 'tests/screenshots/tc008-event-loaded.png', fullPage: true }); // Interact with various elements and collect errors // 1. Click spread cells const spreadCells = page.locator('button').filter({ hasText: /^[+-]?\d+\.?\d*$/ }); const spreadCount = await spreadCells.count(); console.log(`Testing ${Math.min(spreadCount, 3)} spread cells for errors`); for (let i = 0; i < Math.min(spreadCount, 3); i++) { await spreadCells.nth(i).click(); await page.waitForTimeout(500); // Close any modal const closeButton = page.locator('button').filter({ hasText: /[x×]/ }).first(); if (await closeButton.isVisible().catch(() => false)) { await closeButton.click(); await page.waitForTimeout(300); } } // 2. Try quick stake buttons if visible const quickStakes = page.locator('button').filter({ hasText: /^\$\d+$/ }); if (await quickStakes.count() > 0) { await quickStakes.first().click(); await page.waitForTimeout(300); console.log('Quick stake button clicked'); } // 3. Try team selection if visible const teamButtons = page.locator('button').filter({ hasText: /^(home|away)$/i }); if (await teamButtons.count() > 0) { await teamButtons.first().click(); await page.waitForTimeout(300); console.log('Team selection button clicked'); } } await page.screenshot({ path: 'tests/screenshots/tc008-final-state.png', fullPage: true }); // Report all collected errors console.log('\n========== ERROR REPORT =========='); console.log(`JavaScript Errors (${errors.jsErrors.length}):`); errors.jsErrors.forEach((e, i) => console.log(` ${i + 1}. ${e}`)); console.log(`\nNetwork Errors (${errors.networkErrors.length}):`); errors.networkErrors.forEach((e, i) => console.log(` ${i + 1}. ${e.status} ${e.url}`)); console.log(`\nConsole Errors (${errors.consoleErrors.length}):`); errors.consoleErrors.forEach((e, i) => console.log(` ${i + 1}. ${e}`)); console.log('==================================='); // This test documents errors but doesn't fail - it's for diagnostic purposes if (errors.jsErrors.length > 0) { console.log('\nWARNING: JavaScript errors detected during testing'); } }); test('TC-009: Test TakeBetModal Data Bug', async ({ page }) => { const errors = setupErrorTracking(page); console.log('\n=== TC-009: TakeBetModal Data Bug Investigation ==='); console.log('Known Issue: TakeBetModal uses array as object (betInfo)'); // First create a bet as Alice await login(page, TEST_USERS.alice); await page.goto('/sport-events'); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); const events = page.locator('button').filter({ hasText: /vs/i }); if (await events.count() > 0) { await events.first().click(); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); // Click spread cell const spreadCells = page.locator('button').filter({ hasText: /^[+-]?\d+\.?\d*$/ }); if (await spreadCells.count() > 0) { await spreadCells.first().click(); await page.waitForTimeout(500); // Try to create a bet const createButton = page.locator('button').filter({ hasText: /create new bet/i }); if (await createButton.isVisible().catch(() => false)) { await createButton.click(); await page.waitForTimeout(500); const stakeInput = page.locator('input[type="number"]'); if (await stakeInput.isVisible()) { await stakeInput.fill('50'); const submitButton = page.locator('button[type="submit"]').filter({ hasText: /create/i }); await submitButton.click(); await page.waitForTimeout(2000); } } } } // Logout and login as Bob await logout(page); await login(page, TEST_USERS.bob); await page.goto('/sport-events'); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); // Select same event const bobEvents = page.locator('button').filter({ hasText: /vs/i }); if (await bobEvents.count() > 0) { await bobEvents.first().click(); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); await page.screenshot({ path: 'tests/screenshots/tc009-bob-event-view.png', fullPage: true }); // Find spread with open bets const openBetIndicator = page.locator('text=/\\d+ open/'); const hasOpenBets = await openBetIndicator.count() > 0; console.log(`Found open bets indicator: ${hasOpenBets}`); if (hasOpenBets) { // Click on that spread const spreadWithOpenBet = openBetIndicator.locator('..').locator('..'); await spreadWithOpenBet.first().click(); await page.waitForTimeout(500); await page.screenshot({ path: 'tests/screenshots/tc009-spread-with-open-bet.png', fullPage: true }); // Find and click Take Bet const takeBetButton = page.locator('button').filter({ hasText: /take bet/i }); if (await takeBetButton.isVisible().catch(() => false)) { console.log('Take Bet button found - clicking...'); await takeBetButton.first().click(); await page.waitForTimeout(1000); await page.screenshot({ path: 'tests/screenshots/tc009-take-bet-modal.png', fullPage: true }); // Check for errors in the modal - the bug would cause issues here const modalContent = await page.locator('[role="dialog"], .fixed.inset-0.bg-black').textContent().catch(() => ''); console.log('TakeBetModal content sample:', modalContent?.substring(0, 300)); // Check if modal shows proper data or undefined const showsUndefined = modalContent?.includes('undefined') || modalContent?.includes('NaN'); console.log(`Modal shows undefined/NaN data: ${showsUndefined}`); if (showsUndefined) { console.log('DEFECT CONFIRMED: TakeBetModal displays undefined/NaN due to incorrect data access'); } } } } console.log('\n--- Error Summary ---'); console.log('JavaScript Errors:', errors.jsErrors); if (errors.jsErrors.some(e => e.includes('Cannot read') || e.includes('undefined'))) { console.log('DEFECT: Type error detected - likely the betInfo array/object bug'); } }); test('TC-010: API Endpoint Verification', async ({ page }) => { console.log('\n=== TC-010: Direct API Endpoint Verification ==='); await login(page, TEST_USERS.alice); // Get auth token from storage const token = await page.evaluate(() => localStorage.getItem('token')); console.log(`Auth token present: ${!!token}`); // Test sport-events endpoint const eventsResponse = await page.request.get('http://localhost:8000/api/v1/sport-events', { headers: { Authorization: `Bearer ${token}` } }); console.log(`GET /sport-events status: ${eventsResponse.status()}`); if (eventsResponse.ok()) { const events = await eventsResponse.json(); console.log(`Found ${events.length} events`); if (events.length > 0) { const eventId = events[0].id; // Test event with grid endpoint const gridResponse = await page.request.get(`http://localhost:8000/api/v1/sport-events/${eventId}`, { headers: { Authorization: `Bearer ${token}` } }); console.log(`GET /sport-events/${eventId} status: ${gridResponse.status()}`); if (gridResponse.ok()) { const eventWithGrid = await gridResponse.json(); console.log(`Event: ${eventWithGrid.home_team} vs ${eventWithGrid.away_team}`); console.log(`Spread grid keys: ${Object.keys(eventWithGrid.spread_grid || {}).join(', ')}`); } // Test create bet endpoint const createBetResponse = await page.request.post('http://localhost:8000/api/v1/spread-bets', { headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, data: { event_id: eventId, spread: events[0].official_spread, team: 'home', stake_amount: 100 } }); console.log(`POST /spread-bets status: ${createBetResponse.status()}`); if (!createBetResponse.ok()) { const errorBody = await createBetResponse.json().catch(() => ({})); console.log('Create bet error:', errorBody); } else { const createdBet = await createBetResponse.json(); console.log(`Created bet ID: ${createdBet.id}`); } } } }); }); test.describe('TradingPanel Specific Tests', () => { test('TC-011: TradingPanel UI Elements Presence', async ({ page }) => { const errors = setupErrorTracking(page); console.log('\n=== TC-011: TradingPanel UI Elements Check ==='); await login(page, TEST_USERS.alice); await page.goto('/'); await page.waitForLoadState('networkidle'); // Click on event const events = page.locator('button').filter({ hasText: /vs/i }); if (await events.count() > 0) { await events.first().click(); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1500); // Check for all expected TradingPanel elements const elements = { 'Place Bet heading': await page.locator('text=Place Bet').isVisible().catch(() => false), 'Team buttons': await page.locator('button').filter({ hasText: /HOME|AWAY/i }).count() > 0, 'Spread selector': await page.locator('button').filter({ hasText: /[+-]/ }).count() > 0, 'Stake input': await page.locator('input[type="number"]').isVisible().catch(() => false), 'Quick stakes': await page.locator('button').filter({ hasText: /^\$\d+$/ }).count() > 0, 'Create button': await page.locator('button').filter({ hasText: /create.*bet/i }).isVisible().catch(() => false), 'Order book': await page.locator('text=Order Book').isVisible().catch(() => false), }; console.log('TradingPanel Elements:'); Object.entries(elements).forEach(([name, present]) => { console.log(` ${present ? '[X]' : '[ ]'} ${name}`); }); await page.screenshot({ path: 'tests/screenshots/tc011-trading-panel-elements.png', fullPage: true }); } console.log('\nJS Errors:', errors.jsErrors); }); });