Best landing page yet, lost logged in links to lists of bets

This commit is contained in:
2026-01-06 00:23:17 -06:00
parent f50eb2ba3b
commit eac0d6e970
67 changed files with 3932 additions and 99 deletions

130
frontend/tests/app.spec.ts Normal file
View File

@ -0,0 +1,130 @@
import { test, expect } from '@playwright/test';
test.describe('H2H Application', () => {
test('should load the homepage', async ({ page }) => {
// Listen for page errors only (ignore 401 for public API)
const pageErrors: Error[] = [];
page.on('pageerror', error => {
pageErrors.push(error);
});
await page.goto('/');
// Wait for the page to load
await page.waitForLoadState('networkidle');
if (pageErrors.length > 0) {
console.log('Page Errors:', pageErrors.map(e => e.message));
}
// Take a screenshot
await page.screenshot({ path: 'tests/screenshots/homepage.png', fullPage: true });
// Basic assertions
await expect(page).toHaveTitle(/H2H/);
// Should show the header with H2H logo
await expect(page.locator('h1:has-text("H2H")')).toBeVisible();
});
test('should navigate to login page', async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle');
// Look for login button in header (first one)
const loginButton = page.getByRole('link', { name: /log in/i }).first();
await loginButton.click();
await page.waitForLoadState('networkidle');
await page.screenshot({ path: 'tests/screenshots/login.png', fullPage: true });
await expect(page).toHaveURL(/login/);
});
test('should login as admin and see events on home page', async ({ page }) => {
const pageErrors: Error[] = [];
page.on('pageerror', error => {
pageErrors.push(error);
});
// Go to login page
await page.goto('/login');
await page.waitForLoadState('networkidle');
// Fill in login form
await page.fill('input[type="email"]', 'admin@h2h.com');
await page.fill('input[type="password"]', 'admin123');
// Submit form
await page.click('button[type="submit"]');
// Wait for navigation after login - now redirects to home page with events
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
await page.screenshot({ path: 'tests/screenshots/after-login.png', fullPage: true });
// Home page should now show events heading
await expect(page.getByRole('heading', { name: 'Upcoming Events' })).toBeVisible({ timeout: 5000 });
if (pageErrors.length > 0) {
console.log('Page Errors during flow:', pageErrors.map(e => e.message));
}
expect(pageErrors.length).toBe(0);
});
test('should complete full spread betting flow', async ({ page }) => {
const pageErrors: Error[] = [];
page.on('pageerror', error => {
pageErrors.push(error);
});
// Login as alice
await page.goto('/login');
await page.waitForLoadState('networkidle');
await page.fill('input[type="email"]', 'alice@example.com');
await page.fill('input[type="password"]', 'password123');
await page.click('button[type="submit"]');
// Wait for home page to load with events
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
// Events are now shown on the home page
await expect(page.getByRole('heading', { name: 'Upcoming Events' })).toBeVisible({ timeout: 5000 });
await page.screenshot({ path: 'tests/screenshots/events-list.png', fullPage: true });
// Click on first event in the table
const firstEventRow = page.locator('.divide-y button').first();
if (await firstEventRow.isVisible()) {
await firstEventRow.click();
await page.waitForLoadState('networkidle');
await page.screenshot({ path: 'tests/screenshots/spread-grid.png', fullPage: true });
// Check if spread grid is visible
const spreadButtons = page.locator('button').filter({ hasText: /^[+-]?\d+\.?\d*$/ });
const count = await spreadButtons.count();
console.log(`Found ${count} spread buttons`);
// Try to create a bet by clicking an empty spread
if (count > 0) {
await spreadButtons.first().click({ timeout: 5000 }).catch(() => {
console.log('Could not click spread button - might be occupied');
});
await page.waitForTimeout(1000);
await page.screenshot({ path: 'tests/screenshots/create-bet-modal.png', fullPage: true });
}
}
if (pageErrors.length > 0) {
console.log('Page Errors during spread betting flow:', pageErrors.map(e => e.message));
}
expect(pageErrors.length).toBe(0);
});
});

View File

@ -0,0 +1,65 @@
import { test } from '@playwright/test';
test('Capture actual browser errors', async ({ page }) => {
console.log('\n=== CAPTURING ALL BROWSER CONSOLE OUTPUT ===\n');
const allMessages: any[] = [];
page.on('console', msg => {
const msgData = {
type: msg.type(),
text: msg.text(),
location: msg.location()
};
allMessages.push(msgData);
const prefix = msg.type() === 'error' ? '❌ ERROR' :
msg.type() === 'warning' ? '⚠️ WARNING' :
msg.type() === 'log' ? '📝 LOG' :
` ${msg.type().toUpperCase()}`;
console.log(`${prefix}: ${msg.text()}`);
if (msg.location().url) {
console.log(` Location: ${msg.location().url}:${msg.location().lineNumber}`);
}
});
page.on('pageerror', error => {
console.log(`\n💥 PAGE ERROR: ${error.message}`);
console.log(` Stack: ${error.stack}\n`);
});
page.on('requestfailed', request => {
console.log(`\n🔴 REQUEST FAILED: ${request.url()}`);
console.log(` Failure: ${request.failure()?.errorText}\n`);
});
console.log('\nLoading: /\n');
try {
await page.goto('/', { waitUntil: 'networkidle', timeout: 30000 });
} catch (e) {
console.log(`\n❌ Failed to load page: ${e}\n`);
}
// Wait a bit more to capture any delayed errors
await page.waitForTimeout(3000);
console.log('\n=== SUMMARY ===');
console.log(`Total console messages: ${allMessages.length}`);
console.log(`Errors: ${allMessages.filter(m => m.type === 'error').length}`);
console.log(`Warnings: ${allMessages.filter(m => m.type === 'warning').length}`);
const errors = allMessages.filter(m => m.type === 'error');
if (errors.length > 0) {
console.log('\n=== ALL ERRORS ===');
errors.forEach((err, i) => {
console.log(`\n${i + 1}. ${err.text}`);
if (err.location.url) {
console.log(` ${err.location.url}:${err.location.lineNumber}`);
}
});
}
await page.screenshot({ path: 'tests/screenshots/browser-state.png', fullPage: true });
});

View File

@ -0,0 +1,88 @@
import { test, expect } from '@playwright/test';
test('Debug application errors', async ({ page }) => {
// Collect all console messages
const consoleMessages: Array<{ type: string; text: string }> = [];
page.on('console', msg => {
consoleMessages.push({
type: msg.type(),
text: msg.text()
});
});
// Collect page errors
const pageErrors: Error[] = [];
page.on('pageerror', error => {
pageErrors.push(error);
});
// Collect network errors
const networkErrors: Array<{ url: string; status: number }> = [];
page.on('response', response => {
if (response.status() >= 400) {
networkErrors.push({
url: response.url(),
status: response.status()
});
}
});
console.log('\n=== Loading Homepage ===');
await page.goto('/');
// Wait a bit for any errors to show up
await page.waitForTimeout(3000);
// Take screenshot
await page.screenshot({ path: 'tests/screenshots/debug-homepage.png', fullPage: true });
// Print all collected information
console.log('\n=== Console Messages ===');
consoleMessages.forEach(msg => {
console.log(`[${msg.type.toUpperCase()}] ${msg.text}`);
});
console.log('\n=== Page Errors ===');
if (pageErrors.length > 0) {
pageErrors.forEach(error => {
console.log(`ERROR: ${error.message}`);
console.log(`Stack: ${error.stack}`);
});
} else {
console.log('No page errors!');
}
console.log('\n=== Network Errors ===');
if (networkErrors.length > 0) {
networkErrors.forEach(error => {
console.log(`${error.status} - ${error.url}`);
});
} else {
console.log('No network errors!');
}
// Check if the page has rendered properly
console.log('\n=== Page Content Check ===');
const bodyText = await page.textContent('body');
console.log(`Page has content: ${bodyText ? 'YES' : 'NO'}`);
console.log(`Body text length: ${bodyText?.length || 0} characters`);
// Try to find the H2H title
const h2hTitle = await page.locator('h1:has-text("H2H")').count();
console.log(`Found H2H title: ${h2hTitle > 0 ? 'YES' : 'NO'}`);
// Check for error messages in the page
const errorText = bodyText?.toLowerCase() || '';
if (errorText.includes('error') || errorText.includes('failed')) {
console.log(`\nWARNING: Page contains error text!`);
console.log('First 500 chars of body:', bodyText?.substring(0, 500));
}
// Verify no critical errors
const criticalErrors = pageErrors.filter(e =>
!e.message.includes('Warning') &&
!e.message.includes('DevTools')
);
expect(criticalErrors.length).toBe(0);
});

View File

@ -0,0 +1,97 @@
import { test, expect } from '@playwright/test';
test.describe('End-to-End Spread Betting Flow', () => {
test('should allow admin to create event and user to place bet', async ({ page, context }) => {
// Track errors
const pageErrors: string[] = [];
page.on('pageerror', error => {
pageErrors.push(error.message);
});
console.log('\n=== Step 1: Login as Admin ===');
await page.goto('/login');
await page.fill('input[type="email"]', 'admin@h2h.com');
await page.fill('input[type="password"]', 'admin123');
await page.click('button[type="submit"]');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
await page.screenshot({ path: 'tests/screenshots/e2e-01-admin-login.png', fullPage: true });
console.log('✓ Admin logged in successfully');
console.log('\n=== Step 2: Navigate to Admin Panel ===');
const adminLink = page.getByRole('link', { name: /admin/i });
if (await adminLink.isVisible()) {
await adminLink.click();
await page.waitForLoadState('networkidle');
await page.screenshot({ path: 'tests/screenshots/e2e-02-admin-panel.png', fullPage: true });
console.log('✓ Admin panel loaded');
} else {
console.log('! Admin link not visible - user might not have admin privileges');
}
console.log('\n=== Step 3: View Sport Events on Home Page ===');
await page.goto('/');
await page.waitForLoadState('networkidle');
await page.screenshot({ path: 'tests/screenshots/e2e-03-sport-events.png', fullPage: true });
console.log('✓ Sport events page loaded');
// Count available events in the table
const eventRows = page.locator('.divide-y button');
const eventCount = await eventRows.count();
console.log(`✓ Found ${eventCount} sport events`);
if (eventCount > 0) {
console.log('\n=== Step 4: View Event Spread Grid ===');
await eventRows.first().click();
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
await page.screenshot({ path: 'tests/screenshots/e2e-04-spread-grid.png', fullPage: true });
console.log('✓ Spread grid displayed');
// Check for spread grid
const gridExists = await page.locator('.grid').count() > 0;
console.log(`Grid container found: ${gridExists}`);
// Log page content for debugging
const pageContent = await page.textContent('body');
if (pageContent?.includes('Wake Forest') || pageContent?.includes('Lakers') || pageContent?.includes('Chiefs')) {
console.log('✓ Event details are visible on page');
}
}
console.log('\n=== Step 5: Logout and Login as Regular User ===');
await page.goto('/');
await page.waitForLoadState('networkidle');
await page.getByRole('button', { name: /logout/i }).click();
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
await page.screenshot({ path: 'tests/screenshots/e2e-05-logged-out.png', fullPage: true });
console.log('✓ Logged out successfully');
// Login as Alice
await page.goto('/login');
await page.fill('input[type="email"]', 'alice@example.com');
await page.fill('input[type="password"]', 'password123');
await page.click('button[type="submit"]');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
await page.screenshot({ path: 'tests/screenshots/e2e-06-alice-login.png', fullPage: true });
console.log('✓ Alice logged in successfully');
console.log('\n=== Step 6: Alice Views Sport Events on Home ===');
// Events are now on the home page
await expect(page.getByRole('heading', { name: 'Upcoming Events' })).toBeVisible({ timeout: 5000 });
await page.screenshot({ path: 'tests/screenshots/e2e-07-alice-events.png', fullPage: true });
console.log('✓ Alice can view sport events');
console.log('\n=== Error Summary ===');
if (pageErrors.length > 0) {
console.log('Page Errors:', pageErrors);
} else {
console.log('✓ No page errors!');
}
// Verify no critical errors
expect(pageErrors.length).toBe(0);
});
});

View File

@ -0,0 +1,46 @@
import { test } from '@playwright/test';
test('Open browser and wait for manual inspection', async ({ page }) => {
console.log('\n🌐 Opening browser');
console.log('📋 Watching console for errors...\n');
const errors: string[] = [];
const warnings: string[] = [];
page.on('console', msg => {
if (msg.type() === 'error') {
const errorMsg = msg.text();
errors.push(errorMsg);
console.log(`❌ ERROR: ${errorMsg}`);
} else if (msg.type() === 'warning') {
warnings.push(msg.text());
}
});
page.on('pageerror', error => {
const errorMsg = `PAGE ERROR: ${error.message}`;
errors.push(errorMsg);
console.log(`\n💥 ${errorMsg}`);
console.log(`Stack: ${error.stack}\n`);
});
await page.goto('/');
console.log('\n✅ Page loaded');
console.log('⏳ Waiting 10 seconds to capture any async errors...\n');
await page.waitForTimeout(10000);
console.log('\n📊 FINAL REPORT:');
console.log(` Errors: ${errors.length}`);
console.log(` Warnings: ${warnings.length}`);
if (errors.length > 0) {
console.log('\n🔴 ERRORS FOUND:');
errors.forEach((err, i) => console.log(` ${i + 1}. ${err}`));
} else {
console.log('\n✅ NO ERRORS FOUND!');
}
await page.screenshot({ path: 'tests/screenshots/final-browser-state.png', fullPage: true });
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,108 @@
import { test, expect } from '@playwright/test';
test('Complete application flow verification', async ({ page }) => {
const errors: string[] = [];
page.on('pageerror', error => errors.push(`PAGE ERROR: ${error.message}`));
console.log('\n====================================');
console.log(' TESTING H2H APPLICATION');
console.log('====================================\n');
// Test 1: Homepage loads
console.log('TEST 1: Loading homepage...');
await page.goto('/');
await page.waitForLoadState('networkidle');
const title = await page.title();
console.log(`✓ Homepage loaded: "${title}"`);
await page.screenshot({ path: 'tests/screenshots/flow-01-homepage.png' });
// Test 2: Can navigate to login (button in header now)
console.log('\nTEST 2: Navigating to login...');
await page.getByRole('link', { name: /log in/i }).first().click();
await page.waitForURL('**/login');
console.log('✓ Login page loaded');
await page.screenshot({ path: 'tests/screenshots/flow-02-login.png' });
// Test 3: Can login as admin
console.log('\nTEST 3: Logging in as admin...');
await page.fill('input[type="email"]', 'admin@h2h.com');
await page.fill('input[type="password"]', 'admin123');
await page.click('button[type="submit"]');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
const currentUrl = page.url();
console.log(`✓ Logged in successfully, redirected to: ${currentUrl}`);
await page.screenshot({ path: 'tests/screenshots/flow-03-logged-in.png' });
// Test 4: Check navigation links (events are now on home page)
console.log('\nTEST 4: Checking available navigation links...');
const links = await page.locator('nav a').allTextContents();
console.log('Available links:', links);
const hasAdmin = links.some(l => l.toLowerCase().includes('admin'));
const hasMyBets = links.some(l => l.toLowerCase().includes('my bets'));
const hasWallet = links.some(l => l.toLowerCase().includes('wallet'));
console.log(` - Admin link: ${hasAdmin ? '✓ Found' : '✗ Not found'}`);
console.log(` - My Bets link: ${hasMyBets ? '✓ Found' : '✗ Not found'}`);
console.log(` - Wallet link: ${hasWallet ? '✓ Found' : '✗ Not found'}`);
// Test 5: Home page shows events for authenticated users
console.log('\nTEST 5: Checking events on home page...');
await expect(page.getByRole('heading', { name: 'Upcoming Events' })).toBeVisible({ timeout: 5000 });
console.log('✓ Upcoming Events section visible');
// Check for events in the table
const eventRows = page.locator('.divide-y button');
const eventCount = await eventRows.count();
console.log(`✓ Found ${eventCount} sport events`);
await page.screenshot({ path: 'tests/screenshots/flow-04-events-home.png' });
if (eventCount > 0) {
console.log('\nTEST 6: Viewing event spread grid...');
await eventRows.first().click();
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
console.log('✓ Event details loaded');
await page.screenshot({ path: 'tests/screenshots/flow-05-spread-grid.png' });
// Check if spread grid is visible
const bodyText = await page.textContent('body');
const hasEventName = bodyText?.includes('Wake Forest') || bodyText?.includes('Lakers') || bodyText?.includes('Chiefs');
console.log(` - Event details visible: ${hasEventName ? '✓ Yes' : '✗ No'}`);
// Go back to home
await page.click('button:has-text("Back to Events")');
await page.waitForLoadState('networkidle');
}
// Test 7: Can login as regular user
console.log('\nTEST 7: Testing regular user login...');
await page.getByRole('button', { name: /logout/i }).click();
await page.waitForTimeout(1000);
await page.goto('/login');
await page.fill('input[type="email"]', 'alice@example.com');
await page.fill('input[type="password"]', 'password123');
await page.click('button[type="submit"]');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
console.log('✓ Alice logged in successfully');
await page.screenshot({ path: 'tests/screenshots/flow-06-alice-login.png' });
// Check alice's navigation - should NOT have admin link
const aliceLinks = await page.locator('nav a').allTextContents();
const aliceHasAdmin = aliceLinks.some(l => l.toLowerCase().includes('admin'));
console.log(` - Admin link for Alice: ${aliceHasAdmin ? '✗ SHOULD NOT BE VISIBLE' : '✓ Correctly hidden'}`);
console.log('\n====================================');
console.log(' ERROR SUMMARY');
console.log('====================================');
if (errors.length > 0) {
console.log('\nErrors found:');
errors.forEach(e => console.log(`${e}`));
} else {
console.log('\n✓ NO ERRORS - Application is working correctly!');
}
console.log('\n====================================\n');
// Final assertion
expect(errors.length).toBe(0);
});