caefb0dcc0
- Replace window.location.reload() with router.refresh() in ScheduleGenerator, MatchEditor, RecalculateEloButton for proper Next.js cache invalidation - Add revalidatePath() call after schedule generation in POST handler - Add ownerId to tournament creation in cucumber tests for proper permission checks - Assign tournament_admin role via Prisma after user creation in cucumber tests - Fix TypeScript type annotations in hooks.ts (tournament id map) - Update page reload to use networkidle in common-steps.ts - Clear .next/ cache before cucumber tests in justfile - Add .turbo to clean target - Add comprehensive debug logging to schedule API route - Document findings in docs/TROUBLESHOOTING_SCHEDULE_GENERATION.md
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: { id: number }) => 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');
|
|
});
|