9353ab1edc
Progress on issue #7 - tournament schedule e2e tests: - Created ScheduleDisplay component with clickthrough to matches - Updated ScheduleGenerator to use router.refresh() instead of window.location.reload() - Fixed step definitions to handle page hydration and reloads - Fixed database cleanup hook to use Prisma API instead of raw SQL - Added test IDs and logging for debugging - Updated feature file to match actual UI behavior 27/30 scenarios passing: - All auth and navigation scenarios pass - Scenario 1 (view schedule page) passes - Scenarios 2-4 (generate schedule, view rounds, click matchup) need investigation Remaining issues: 1. Page refresh not picking up newly generated schedule data 2. Round data not visible after 'Generated' message 3. Matchup elements not found after refresh 4. Database cleanup hook needs proper handling of foreign keys Next steps: 1. Investigate why router.refresh() and explicit reload don't show new data 2. Check if there's a caching issue in the production build 3. Verify database transactions are committing correctly 4. Add more logging to trace data flow
232 lines
6.3 KiB
TypeScript
232 lines
6.3 KiB
TypeScript
/**
|
|
* Cucumber hooks for EuchreCamp E2E tests
|
|
*/
|
|
|
|
import { Before, After, BeforeAll, AfterAll, setDefaultTimeout } from '@cucumber/cucumber';
|
|
import { chromium, Browser } from '@playwright/test';
|
|
import { world } from './world';
|
|
import path from 'path';
|
|
import fs from 'fs';
|
|
|
|
// Set default timeout for Cucumber steps (30 seconds)
|
|
setDefaultTimeout(30000);
|
|
|
|
// Global browser instance
|
|
let browser: Browser;
|
|
|
|
// Load environment files
|
|
const envPath = path.resolve(process.cwd(), '.env');
|
|
const envDevPath = path.resolve(process.cwd(), '.env.development');
|
|
|
|
if (fs.existsSync(envPath)) {
|
|
require('dotenv').config({ path: envPath });
|
|
}
|
|
|
|
if (fs.existsSync(envDevPath)) {
|
|
require('dotenv').config({ path: envDevPath, override: true });
|
|
}
|
|
|
|
// Database safety check - prevent tests from running against production
|
|
function isProductionDatabase(): boolean {
|
|
const dbUrl = process.env.DATABASE_URL || '';
|
|
return dbUrl.includes('euchre_camp') && !dbUrl.includes('_dev') && !dbUrl.includes('test');
|
|
}
|
|
|
|
if (isProductionDatabase()) {
|
|
console.error('');
|
|
console.error('='.repeat(80));
|
|
console.error('CRITICAL ERROR: Cucumber tests are attempting to run against PRODUCTION database!');
|
|
console.error('='.repeat(80));
|
|
console.error('');
|
|
console.error('Current DATABASE_URL:', process.env.DATABASE_URL);
|
|
console.error('');
|
|
console.error('Tests MUST run against a development/test database.');
|
|
console.error('');
|
|
console.error('To fix this:');
|
|
console.error(' 1. Set DATABASE_URL to a dev/test database');
|
|
console.error(' 2. Or run: npm run test:acceptance (which uses Playwright tests)');
|
|
console.error('');
|
|
console.error('Aborting test execution to prevent data corruption.');
|
|
console.error('');
|
|
process.exit(1);
|
|
}
|
|
|
|
/**
|
|
* Before all scenarios: Launch browser
|
|
*/
|
|
BeforeAll(async function () {
|
|
console.log('🌍 Launching browser for Cucumber tests...');
|
|
browser = await chromium.launch();
|
|
});
|
|
|
|
/**
|
|
* After all scenarios: Close browser and cleanup
|
|
*/
|
|
AfterAll(async function () {
|
|
console.log('🌍 Closing browser...');
|
|
await browser.close();
|
|
|
|
console.log('🌍 Disconnecting from database...');
|
|
if (world.prisma) {
|
|
await world.prisma.$disconnect();
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Before each scenario: Create new page context
|
|
*/
|
|
Before(async function () {
|
|
try {
|
|
// Create new context and page
|
|
world.context = await browser.newContext();
|
|
world.page = await world.context.newPage();
|
|
} catch (error) {
|
|
console.error('❌ Failed to create new browser context:', error);
|
|
console.log('🌍 Attempting to relaunch browser...');
|
|
|
|
// Try to relaunch browser
|
|
try {
|
|
await browser.close();
|
|
} catch (e) {
|
|
// Ignore close errors
|
|
}
|
|
|
|
// Relaunch browser
|
|
browser = await chromium.launch({
|
|
headless: process.env.CI ? true : false,
|
|
});
|
|
|
|
// Retry creating context
|
|
world.context = await browser.newContext();
|
|
world.page = await world.context.newPage();
|
|
console.log('✅ Browser relaunched successfully');
|
|
}
|
|
|
|
// Log console messages for debugging
|
|
world.page.on('console', msg => {
|
|
console.log(`🌍 BROWSER ${msg.type()}:`, msg.text());
|
|
});
|
|
|
|
// Log network errors
|
|
world.page.on('requestfailed', request => {
|
|
console.log('❌ REQUEST FAILED:', request.url());
|
|
});
|
|
|
|
console.log('🌍 Created new page context');
|
|
});
|
|
|
|
/**
|
|
* After each scenario: Close page and clean up test data
|
|
*/
|
|
After(async function () {
|
|
console.log('🌍 Cleaning up after scenario...');
|
|
|
|
// Clean up test data from dev database
|
|
try {
|
|
const prisma = await world.getPrisma();
|
|
const dbUrl = process.env.DATABASE_URL || '';
|
|
|
|
// Safety check: only clean up dev/test databases
|
|
if (dbUrl.includes('_dev') || dbUrl.includes('test') || dbUrl.includes('ci')) {
|
|
// Use Prisma API for cleanup instead of raw SQL to avoid column name issues
|
|
|
|
// Find test tournaments first
|
|
const testTournaments = await prisma.event.findMany({
|
|
where: {
|
|
OR: [
|
|
{ name: { startsWith: 'Test Tournament' } },
|
|
{ name: { startsWith: 'Test Schedule Tournament' } }
|
|
]
|
|
},
|
|
select: { id: true }
|
|
});
|
|
|
|
const tournamentIds = testTournaments.map(t => t.id);
|
|
|
|
if (tournamentIds.length > 0) {
|
|
// Delete bracket matchups via Prisma
|
|
await prisma.bracketMatchup.deleteMany({
|
|
where: {
|
|
round: {
|
|
eventId: { in: tournamentIds }
|
|
}
|
|
}
|
|
});
|
|
|
|
// Delete rounds
|
|
await prisma.tournamentRound.deleteMany({
|
|
where: { eventId: { in: tournamentIds } }
|
|
});
|
|
|
|
// Delete event participants
|
|
await prisma.eventParticipant.deleteMany({
|
|
where: { eventId: { in: tournamentIds } }
|
|
});
|
|
|
|
// Delete tournaments
|
|
await prisma.event.deleteMany({
|
|
where: { id: { in: tournamentIds } }
|
|
});
|
|
}
|
|
|
|
// Delete test players
|
|
await prisma.player.deleteMany({
|
|
where: {
|
|
OR: [
|
|
{ name: { startsWith: 'Tournament Player' } },
|
|
{ name: { startsWith: 'Schedule Player' } },
|
|
{ name: { startsWith: 'Test Player' } },
|
|
{ name: { startsWith: 'Test Activity Player' } }
|
|
]
|
|
}
|
|
});
|
|
|
|
// Delete test users
|
|
await prisma.user.deleteMany({
|
|
where: {
|
|
email: { startsWith: 'cucumber-' }
|
|
}
|
|
});
|
|
|
|
console.log('🌍 Test data cleaned up from dev database');
|
|
} else {
|
|
console.log('🌍 Skipping database cleanup (not a dev/test database)');
|
|
}
|
|
} catch (error) {
|
|
console.log('🌍 Database cleanup error (non-critical):', error);
|
|
}
|
|
|
|
// Close page and context
|
|
if (world.page) {
|
|
await world.page.close();
|
|
}
|
|
if (world.context) {
|
|
await world.context.close();
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Custom hook for navigating to a page
|
|
*/
|
|
Before({ tags: '@navigation' }, async function () {
|
|
console.log('🌍 Navigation hook triggered');
|
|
});
|
|
|
|
/**
|
|
* Custom hook for authenticated scenarios
|
|
*/
|
|
Before({ tags: '@authenticated' }, async function () {
|
|
console.log('🌍 Authenticated hook triggered');
|
|
});
|
|
|
|
/**
|
|
* Tag hooks for specific scenarios
|
|
*/
|
|
Before({ tags: '@slow' }, async function () {
|
|
console.log('🌍 Slow test - extending timeout');
|
|
});
|
|
|
|
Before({ tags: '@flaky' }, async function () {
|
|
console.log('🌍 Flaky test - retry enabled');
|
|
});
|