Files
h2h-prototype/frontend/tests/bet-creation-taking.spec.ts
2026-01-11 00:46:49 -06:00

827 lines
32 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
});
});