Files
h2h-prototype/test-e2e-comprehensive.js
William D. Jones 93fb46f19b Fix critical errors: infinite loop, database schema, and add comprehensive E2E tests
## Critical Fixes:

1. **Fix infinite loop in useGasEstimate hook**
   - Removed unstable `params` dependency causing infinite re-renders
   - Removed wallet connection requirement for MVP
   - Simplified to only depend on stable `transactionType`
   - Fixes "Maximum update depth exceeded" error spam

2. **Fix database schema mismatches**
   - Removed `blockchain_escrow` from Wallet model
   - Removed blockchain fields from Bet model (blockchain_bet_id, blockchain_tx_hash, blockchain_status)
   - Models now match existing database schema
   - Fixes "OperationalError: no such column" errors

3. **Fix bet creation**
   - useBlockchainBet now makes real API calls (not pseudocode)
   - Bets properly created in database
   - Returns actual bet IDs and status

## Testing:

- Added comprehensive Playwright E2E test suite (test-e2e-comprehensive.js)
- Tests all critical flows: login, marketplace, wallet, create bet, my bets, navigation
- Captures all console errors and warnings
- Result:  0 errors (was 500+)

## Development:

- Added docker-compose.dev.yml for local development with hot-reload
- Added dev.sh quick-start script
- Added LOCAL_DEVELOPMENT.md comprehensive guide
- Updated vite.config.ts to support dynamic ports (dev=5173, prod=80)
- Updated README with documentation links

## Files Changed:

Backend:
- backend/app/models/wallet.py - Remove blockchain_escrow field
- backend/app/models/bet.py - Remove blockchain fields

Frontend:
- frontend/src/blockchain/hooks/useGasEstimate.ts - Fix infinite loop
- frontend/src/blockchain/hooks/useBlockchainBet.ts - Add real API calls
- frontend/vite.config.ts - Dynamic port support

Docs/Scripts:
- FIXES_APPLIED.md - Detailed fix documentation
- LOCAL_DEVELOPMENT.md - Local dev guide
- docker-compose.dev.yml - Dev environment config
- dev.sh - Quick start script
- test-e2e-comprehensive.js - E2E test suite

🚀 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-02 15:22:57 -06:00

294 lines
8.5 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

/**
* Comprehensive E2E Test Suite for H2H Betting Platform
*
* Tests all critical user flows and captures console errors
*/
const { chromium } = require('playwright');
// Configuration
const BASE_URL = 'http://localhost:5173';
const API_URL = 'http://localhost:8000';
// Test users
const USERS = {
alice: { email: 'alice@example.com', password: 'password123' },
bob: { email: 'bob@example.com', password: 'password123' },
charlie: { email: 'charlie@example.com', password: 'password123' }
};
// Collect all console errors
let consoleErrors = [];
let consoleWarnings = [];
async function setupBrowser() {
const browser = await chromium.launch({ headless: false });
const context = await browser.newContext({
viewport: { width: 1280, height: 720 }
});
const page = await context.newPage();
// Capture console errors and warnings
page.on('console', msg => {
const type = msg.type();
const text = msg.text();
if (type === 'error') {
consoleErrors.push({ text, url: page.url() });
console.log(`❌ Console Error: ${text}`);
} else if (type === 'warning') {
consoleWarnings.push({ text, url: page.url() });
console.log(`⚠️ Console Warning: ${text}`);
}
});
// Capture page errors
page.on('pageerror', error => {
consoleErrors.push({ text: error.message, url: page.url(), stack: error.stack });
console.log(`❌ Page Error: ${error.message}`);
});
// Capture failed requests
page.on('requestfailed', request => {
console.log(`❌ Request Failed: ${request.url()} - ${request.failure().errorText}`);
});
return { browser, context, page };
}
async function login(page, user) {
console.log(`\n🔐 Logging in as ${user.email}...`);
await page.goto(BASE_URL);
await page.waitForLoadState('networkidle');
// Check if already logged in
const isLoggedIn = await page.locator('text=/logout/i').count() > 0;
if (isLoggedIn) {
console.log('✅ Already logged in');
return;
}
// Click login/sign in button
const loginButton = page.locator('button', { hasText: /sign in|login/i }).first();
if (await loginButton.count() > 0) {
await loginButton.click();
await page.waitForTimeout(500);
}
// Fill in credentials
await page.fill('input[type="email"], input[name="email"]', user.email);
await page.fill('input[type="password"], input[name="password"]', user.password);
// Submit
await page.click('button[type="submit"]');
await page.waitForTimeout(2000);
console.log('✅ Logged in successfully');
}
async function logout(page) {
console.log('\n🚪 Logging out...');
const logoutButton = page.locator('button, a', { hasText: /logout|sign out/i }).first();
if (await logoutButton.count() > 0) {
await logoutButton.click();
await page.waitForTimeout(1000);
console.log('✅ Logged out');
}
}
async function testMarketplace(page) {
console.log('\n📊 Testing Marketplace...');
await page.goto(`${BASE_URL}/marketplace`);
await page.waitForLoadState('networkidle');
await page.waitForTimeout(2000);
// Check for bet cards
const betCards = await page.locator('[class*="bet"], [data-testid*="bet"]').count();
console.log(` Found ${betCards} bet cards`);
// Check filters
const categoryFilters = await page.locator('button, select', { hasText: /sports|entertainment|politics|custom/i }).count();
console.log(` Found ${categoryFilters} category filters`);
console.log('✅ Marketplace loaded');
}
async function testWallet(page) {
console.log('\n💰 Testing Wallet...');
await page.goto(`${BASE_URL}/wallet`);
await page.waitForLoadState('networkidle');
await page.waitForTimeout(2000);
// Check wallet balance display
const balanceElements = await page.locator('text=/balance|\\$/i').count();
console.log(` Found ${balanceElements} balance elements`);
// Check for deposit button
const depositButton = await page.locator('button', { hasText: /deposit|add funds/i }).count();
console.log(` Deposit button present: ${depositButton > 0}`);
console.log('✅ Wallet loaded');
}
async function testCreateBet(page) {
console.log('\n Testing Create Bet...');
await page.goto(`${BASE_URL}/marketplace`);
await page.waitForLoadState('networkidle');
// Find and click create bet button
const createButton = page.locator('button', { hasText: /create|new bet/i }).first();
if (await createButton.count() === 0) {
console.log('⚠️ Create bet button not found');
return;
}
await createButton.click();
await page.waitForTimeout(1000);
// Check if modal opened
const modalOpen = await page.locator('[role="dialog"], .modal').count() > 0;
console.log(` Modal opened: ${modalOpen}`);
if (modalOpen) {
// Fill in bet details
console.log(' Filling bet form...');
await page.fill('input[type="text"]').first().fill('Test Bet Title');
await page.locator('textarea').first().fill('This is a test bet description');
// Find stake amount input
const stakeInput = page.locator('input[type="number"]').first();
await stakeInput.fill('10');
// Look for submit button
const submitButton = page.locator('button[type="submit"], button', { hasText: /create|submit/i }).last();
console.log(` Submit button found: ${await submitButton.count() > 0}`);
// Don't actually submit - just testing the form opens
const cancelButton = page.locator('button', { hasText: /cancel/i }).first();
if (await cancelButton.count() > 0) {
await cancelButton.click();
await page.waitForTimeout(500);
}
}
console.log('✅ Create bet flow tested');
}
async function testMyBets(page) {
console.log('\n📋 Testing My Bets...');
await page.goto(`${BASE_URL}/my-bets`);
await page.waitForLoadState('networkidle');
await page.waitForTimeout(2000);
// Check for tabs
const tabs = await page.locator('[role="tab"], button[class*="tab"]').count();
console.log(` Found ${tabs} tabs`);
console.log('✅ My Bets loaded');
}
async function testNavigation(page) {
console.log('\n🧭 Testing Navigation...');
const routes = [
{ path: '/marketplace', name: 'Marketplace' },
{ path: '/my-bets', name: 'My Bets' },
{ path: '/wallet', name: 'Wallet' }
];
for (const route of routes) {
console.log(` Navigating to ${route.name}...`);
await page.goto(`${BASE_URL}${route.path}`);
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
}
console.log('✅ Navigation tested');
}
async function runTests() {
console.log('🧪 Starting Comprehensive E2E Tests\n');
console.log('=' .repeat(60));
const { browser, page } = await setupBrowser();
try {
// Test 1: Login Flow
console.log('\n📝 TEST 1: Login Flow');
await login(page, USERS.alice);
// Test 2: Marketplace
console.log('\n📝 TEST 2: Marketplace');
await testMarketplace(page);
// Test 3: Wallet
console.log('\n📝 TEST 3: Wallet');
await testWallet(page);
// Test 4: Create Bet
console.log('\n📝 TEST 4: Create Bet');
await testCreateBet(page);
// Test 5: My Bets
console.log('\n📝 TEST 5: My Bets');
await testMyBets(page);
// Test 6: Navigation
console.log('\n📝 TEST 6: Navigation');
await testNavigation(page);
// Test 7: Logout
console.log('\n📝 TEST 7: Logout');
await logout(page);
// Summary
console.log('\n' + '='.repeat(60));
console.log('📊 TEST SUMMARY\n');
console.log(`Console Errors: ${consoleErrors.length}`);
if (consoleErrors.length > 0) {
console.log('\n❌ CONSOLE ERRORS:');
consoleErrors.forEach((err, i) => {
console.log(`\n${i + 1}. ${err.text}`);
console.log(` Page: ${err.url}`);
if (err.stack) {
console.log(` Stack: ${err.stack.split('\n')[0]}`);
}
});
}
console.log(`\nConsole Warnings: ${consoleWarnings.length}`);
if (consoleWarnings.length > 0 && consoleWarnings.length < 20) {
console.log('\n⚠ CONSOLE WARNINGS:');
consoleWarnings.forEach((warn, i) => {
console.log(`${i + 1}. ${warn.text}`);
});
}
console.log('\n' + '='.repeat(60));
if (consoleErrors.length === 0) {
console.log('✅ All tests passed with no console errors!');
} else {
console.log(`⚠️ Tests completed with ${consoleErrors.length} console errors`);
}
} catch (error) {
console.error('\n❌ Test failed:', error);
} finally {
console.log('\n🔍 Keeping browser open for 10 seconds for inspection...');
await page.waitForTimeout(10000);
await browser.close();
}
}
// Run tests
runTests().catch(console.error);