import { test, expect, Page } from '@playwright/test'; /** * Focused Tests for Take Bet Flow * Investigating why taking a bet doesn't work */ const TEST_USERS = { alice: { email: 'alice@example.com', password: 'password123' }, bob: { email: 'bob@example.com', password: 'password123' }, }; interface TestErrors { jsErrors: string[]; networkErrors: { url: string; status: number; method: string; body?: any }[]; consoleErrors: string[]; } function setupErrorTracking(page: Page): TestErrors { const errors: TestErrors = { jsErrors: [], networkErrors: [], consoleErrors: [], }; page.on('pageerror', (error) => { errors.jsErrors.push(`${error.name}: ${error.message}`); }); page.on('response', async (response) => { if (response.status() >= 400) { let body = null; try { body = await response.json(); } catch {} errors.networkErrors.push({ url: response.url(), status: response.status(), method: response.request().method(), body, }); } }); page.on('console', (msg) => { if (msg.type() === 'error') { errors.consoleErrors.push(msg.text()); } }); return errors; } 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'); await page.waitForTimeout(1500); } test.describe('Take Bet Flow Investigation', () => { test('TAKE-001: Bob takes bet via TradingPanel', async ({ page }) => { const errors = setupErrorTracking(page); console.log('\n=== TAKE-001: Take Bet via TradingPanel ==='); // Login as Bob await login(page, TEST_USERS.bob); console.log('Logged in as Bob'); // Go to home page and click on an event await page.goto('/'); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); // Click on first event name to go to event detail page const eventLinks = page.locator('text=/\\w+ vs \\w+/i'); const linkCount = await eventLinks.count(); console.log(`Found ${linkCount} event links`); if (linkCount > 0) { await eventLinks.first().click(); await page.waitForTimeout(2000); await page.screenshot({ path: 'tests/screenshots/take001-event-page.png', fullPage: true }); // Look for "Take Existing Bet" section in TradingPanel const takeExistingSection = page.locator('text=Take Existing Bet'); const hasTakeSection = await takeExistingSection.isVisible().catch(() => false); console.log(`"Take Existing Bet" section visible: ${hasTakeSection}`); // Look for Take buttons const takeButtons = page.locator('button').filter({ hasText: /^take$/i }); const takeButtonCount = await takeButtons.count(); console.log(`Found ${takeButtonCount} Take buttons`); if (takeButtonCount > 0) { // Set up API listener let apiCalled = false; let apiUrl = ''; let apiResponse: any = null; page.on('request', req => { if (req.url().includes('/take') && req.method() === 'POST') { apiCalled = true; apiUrl = req.url(); console.log('Take API Request:', req.url()); } }); page.on('response', async resp => { if (resp.url().includes('/take')) { apiResponse = { status: resp.status(), body: await resp.json().catch(() => null) }; } }); // Click first Take button await takeButtons.first().click(); console.log('Clicked Take button'); await page.waitForTimeout(3000); await page.screenshot({ path: 'tests/screenshots/take001-after-take.png', fullPage: true }); console.log(`API was called: ${apiCalled}`); if (apiCalled) { console.log(`API URL: ${apiUrl}`); } if (apiResponse) { console.log('API Response:', apiResponse); } // Check for toast messages const successToast = await page.locator('text=Bet taken successfully').isVisible().catch(() => false); const errorToast = await page.locator('[role="status"]').textContent().catch(() => ''); console.log(`Success toast visible: ${successToast}`); console.log(`Toast content: ${errorToast}`); } else { console.log('No Take buttons found in TradingPanel - checking if there are open bets'); // Check order book for open bets const openIndicators = await page.locator('text=/\\d+ open/i').count(); console.log(`Open bet indicators found: ${openIndicators}`); } } console.log('\n--- Errors ---'); console.log('JS Errors:', errors.jsErrors); console.log('Network Errors:', errors.networkErrors); }); test('TAKE-002: Bob takes bet via SpreadGrid modal', async ({ page }) => { const errors = setupErrorTracking(page); console.log('\n=== TAKE-002: Take Bet via SpreadGrid Modal ==='); await login(page, TEST_USERS.bob); console.log('Logged in as Bob'); await page.goto('/sport-events'); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); // Select first event const eventButtons = page.locator('button').filter({ hasText: /vs/i }); if (await eventButtons.count() > 0) { await eventButtons.first().click(); await page.waitForTimeout(1500); await page.screenshot({ path: 'tests/screenshots/take002-spread-grid.png', fullPage: true }); // Find a spread with open bets (green background, shows "X open") const spreadsWithOpen = page.locator('button').filter({ hasText: /open/i }); const openSpreadCount = await spreadsWithOpen.count(); console.log(`Found ${openSpreadCount} spreads with open bets`); if (openSpreadCount > 0) { // Click on spread with open bets await spreadsWithOpen.first().click(); await page.waitForTimeout(1000); await page.screenshot({ path: 'tests/screenshots/take002-spread-modal.png', fullPage: true }); // Look for Take Bet button in modal const takeBetButtons = page.locator('button').filter({ hasText: /take bet/i }); const takeBtnCount = await takeBetButtons.count(); console.log(`Found ${takeBtnCount} "Take Bet" buttons in modal`); if (takeBtnCount > 0) { // Log modal content before clicking const modalContent = await page.locator('.fixed.inset-0').textContent().catch(() => ''); console.log('Modal content preview:', modalContent?.substring(0, 300)); // Set up API listener const apiPromise = page.waitForResponse( resp => resp.url().includes('/take') && resp.request().method() === 'POST', { timeout: 10000 } ).catch(() => null); // Click Take Bet button await takeBetButtons.first().click(); console.log('Clicked "Take Bet" button'); await page.waitForTimeout(1500); await page.screenshot({ path: 'tests/screenshots/take002-take-modal.png', fullPage: true }); // Check if TakeBetModal opened const takeModalTitle = await page.locator('h2, h3').filter({ hasText: /take bet/i }).isVisible().catch(() => false); console.log(`TakeBetModal title visible: ${takeModalTitle}`); // Check for data issues in modal (the bug: betInfo is array used as object) const takeModalContent = await page.locator('.fixed.inset-0').last().textContent().catch(() => ''); console.log('TakeBetModal content:', takeModalContent?.substring(0, 500)); const hasUndefined = takeModalContent?.includes('undefined'); const hasNaN = takeModalContent?.includes('NaN'); console.log(`Modal shows undefined: ${hasUndefined}`); console.log(`Modal shows NaN: ${hasNaN}`); if (hasUndefined || hasNaN) { console.log('DEFECT CONFIRMED: TakeBetModal has data display issue'); } // Look for confirm button in TakeBetModal const confirmTakeBtn = page.locator('button').filter({ hasText: /take bet.*\$/i }); if (await confirmTakeBtn.isVisible().catch(() => false)) { console.log('Clicking confirm button in TakeBetModal'); await confirmTakeBtn.click(); await page.waitForTimeout(3000); await page.screenshot({ path: 'tests/screenshots/take002-after-confirm.png', fullPage: true }); const response = await apiPromise; if (response) { console.log(`Take API status: ${response.status()}`); const body = await response.json().catch(() => null); console.log('Take API response:', body); } else { console.log('No API call made'); } } } } else { console.log('No spreads with open bets found'); } } console.log('\n--- Errors ---'); console.log('JS Errors:', errors.jsErrors); console.log('Network Errors:', errors.networkErrors); }); test('TAKE-003: Verify TakeBetModal receives correct data', async ({ page }) => { const errors = setupErrorTracking(page); console.log('\n=== TAKE-003: TakeBetModal Data Verification ==='); await login(page, TEST_USERS.bob); await page.goto('/sport-events'); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); // Select first event const eventButtons = page.locator('button').filter({ hasText: /vs/i }); if (await eventButtons.count() > 0) { await eventButtons.first().click(); await page.waitForTimeout(1500); // Find spread with high number of open bets const openTexts = await page.locator('text=/\\d+ open/').all(); console.log(`Found ${openTexts.length} open bet indicators`); // Find the parent button of an open bet indicator for (const openText of openTexts.slice(0, 3)) { const parentButton = openText.locator('xpath=ancestor::button'); if (await parentButton.count() > 0) { const spreadText = await parentButton.textContent(); console.log(`Clicking spread: ${spreadText?.trim()}`); await parentButton.click(); await page.waitForTimeout(800); // Check modal for available bets const availableBets = page.locator('.fixed.inset-0').locator('text=Available to Take'); const hasAvailable = await availableBets.isVisible().catch(() => false); console.log(`"Available to Take" section visible: ${hasAvailable}`); if (hasAvailable) { // Get bet details from modal const betDetails = await page.locator('.bg-green-50, .border-green-200').first().textContent().catch(() => ''); console.log(`First available bet details: ${betDetails}`); // Click Take Bet const takeBetBtn = page.locator('button').filter({ hasText: /^take bet$/i }); if (await takeBetBtn.count() > 0) { await takeBetBtn.first().click(); await page.waitForTimeout(1000); await page.screenshot({ path: 'tests/screenshots/take003-take-modal.png', fullPage: true }); // Analyze TakeBetModal content const modalText = await page.locator('.fixed.inset-0').last().textContent().catch(() => ''); // Check for proper values (should have $ amounts, usernames, etc.) const has$Amount = /\$\d+/.test(modalText || ''); const hasCreatorInfo = /by \w+/.test(modalText || ''); const hasPotInfo = /pot/i.test(modalText || ''); console.log('\nTakeBetModal Content Analysis:'); console.log(` Has $ amount: ${has$Amount}`); console.log(` Has creator info: ${hasCreatorInfo}`); console.log(` Has pot info: ${hasPotInfo}`); console.log(` Modal text sample: ${modalText?.substring(0, 400)}`); // Close modal and continue const closeBtn = page.locator('button').filter({ hasText: /cancel|×|close/i }); if (await closeBtn.count() > 0) { await closeBtn.first().click(); } } } // Close modal await page.keyboard.press('Escape'); await page.waitForTimeout(300); } } } console.log('\n--- Error Summary ---'); console.log('JS Errors:', errors.jsErrors); if (errors.jsErrors.length > 0) { console.log('DEFECT: JavaScript errors occurred'); } }); test('TAKE-004: Direct API test - take existing bet', async ({ page, request }) => { console.log('\n=== TAKE-004: Direct API Take Bet Test ==='); // First, login as Alice and create a bet await login(page, TEST_USERS.alice); // Get token via evaluating the auth store const aliceAuth = await page.evaluate(() => { const authStr = localStorage.getItem('auth-storage'); return authStr ? JSON.parse(authStr) : null; }); console.log('Alice auth storage:', aliceAuth ? 'Found' : 'Not found'); let aliceToken = null; if (aliceAuth?.state?.token) { aliceToken = aliceAuth.state.token; } if (!aliceToken) { // Try alternate storage location aliceToken = await page.evaluate(() => localStorage.getItem('token')); } console.log(`Alice token: ${aliceToken ? 'Retrieved' : 'Not found'}`); if (!aliceToken) { // Try to get token from cookies or session storage const cookies = await page.context().cookies(); console.log('Cookies:', cookies.map(c => c.name)); const sessionData = await page.evaluate(() => sessionStorage.getItem('token')); console.log(`Session token: ${sessionData ? 'Found' : 'Not found'}`); } // Get events via the page's network context await page.goto('/sport-events'); await page.waitForLoadState('networkidle'); // Create bet via UI since we can't reliably get token const eventButtons = page.locator('button').filter({ hasText: /vs/i }); if (await eventButtons.count() > 0) { await eventButtons.first().click(); await page.waitForTimeout(1500); // Click on a spread const spreadCells = page.locator('button').filter({ hasText: /^[+-]?\d+\.?\d*$/ }); await spreadCells.first().click(); await page.waitForTimeout(500); // Create bet const createBtn = page.locator('button').filter({ hasText: /create new bet/i }); if (await createBtn.isVisible()) { await createBtn.click(); await page.waitForTimeout(500); // Fill stake await page.locator('input[type="number"]').fill('25'); await page.locator('button[type="submit"]').click(); await page.waitForTimeout(2000); console.log('Created bet as Alice'); } } // Logout Alice await page.goto('/'); await page.waitForTimeout(500); const logoutBtn = page.locator('button').filter({ hasText: /logout/i }); if (await logoutBtn.isVisible()) { await logoutBtn.click(); await page.waitForTimeout(1000); } // Login as Bob await login(page, TEST_USERS.bob); console.log('Logged in as Bob'); // Navigate and take bet await page.goto('/sport-events'); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); const bobEventBtns = page.locator('button').filter({ hasText: /vs/i }); if (await bobEventBtns.count() > 0) { await bobEventBtns.first().click(); await page.waitForTimeout(1500); await page.screenshot({ path: 'tests/screenshots/take004-bob-grid.png', fullPage: true }); // Find and click spread with open bets const openSpread = page.locator('button').filter({ hasText: /open/i }).first(); if (await openSpread.isVisible()) { await openSpread.click(); await page.waitForTimeout(800); await page.screenshot({ path: 'tests/screenshots/take004-detail-modal.png', fullPage: true }); // Click Take Bet const takeBtn = page.locator('button').filter({ hasText: /take bet/i }).first(); if (await takeBtn.isVisible()) { // Monitor API call const apiPromise = page.waitForResponse( resp => resp.url().includes('/take'), { timeout: 10000 } ).catch(() => null); await takeBtn.click(); await page.waitForTimeout(1000); await page.screenshot({ path: 'tests/screenshots/take004-take-modal.png', fullPage: true }); // Click confirm in TakeBetModal const confirmBtn = page.locator('button').filter({ hasText: /take bet.*\$|confirm/i }).last(); if (await confirmBtn.isVisible()) { await confirmBtn.click(); await page.waitForTimeout(3000); const response = await apiPromise; if (response) { console.log(`API Response: ${response.status()}`); const body = await response.json().catch(() => null); console.log('Response body:', body); if (response.status() >= 200 && response.status() < 300) { console.log('SUCCESS: Bet taken via UI'); } else { console.log('FAILURE: Bet taking failed'); } } else { console.log('WARNING: No API call detected'); } } } } } }); });