/** * Global teardown for Playwright tests * Handles cleanup based on environment: * - CI: Full schema reset (database can be destroyed and recreated) * - Dev: Selective cleanup of test records (preserve real data) * - Prod: Selective cleanup of test records (preserve real data) */ import { type FullConfig } from '@playwright/test'; import { PrismaClient } from '@prisma/client'; import { PrismaPg } from '@prisma/adapter-pg'; import { execSync } from 'child_process'; const TEST_PATTERNS = { players: [ '%Test%', '%Setup%', '%Home Test%', '%Home Match Player%', '%Admin User%', '%NinePart%', '%Nine Part%', '%Test Player%', '%TestUser%', '%Cucumber%', '%Config Admin%', ], events: [ '%Test%', '%Setup%', '%Recent%', '%Test Tournament%', '%Cucumber%', ], users: [ '%test%', '%setup%', '%cucumber%', '%TestUser%', ] }; function isDatabase(url: string, name: string): boolean { return url.includes(name); } function isProductionDatabase(): boolean { const dbUrl = process.env.DATABASE_URL || ''; return isDatabase(dbUrl, 'euchre_camp') && !isDatabase(dbUrl, '_dev') && !isDatabase(dbUrl, '_ci'); } function isCIDatabase(): boolean { const dbUrl = process.env.DATABASE_URL || ''; return isDatabase(dbUrl, '_ci'); } function buildLikeClause(patterns: string[]): string { return patterns.map(p => `name LIKE '${p}'`).join(' OR '); } function buildEmailLikeClause(patterns: string[]): string { return patterns.map(p => `email LIKE '${p}'`).join(' OR '); } function createPrismaClient() { const databaseUrl = process.env.DATABASE_URL; if (!databaseUrl) throw new Error('DATABASE_URL is required'); const adapter = new PrismaPg({ connectionString: databaseUrl }); return new PrismaClient({ adapter }); } async function resetDatabaseSchema(prisma: PrismaClient) { console.log('Resetting database schema...'); const tables = await prisma.$queryRaw<{ tablename: string }[]>` SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND tablename NOT LIKE '_prisma_migrations' `; if (tables.length > 0) { await prisma.$executeRawUnsafe(` DROP SCHEMA public CASCADE; CREATE SCHEMA public; `); console.log(`Dropped ${tables.length} tables`); } console.log('Running migrations...'); execSync('bunx prisma migrate deploy', { stdio: 'inherit' }); } async function cleanupTestRecords(prisma: PrismaClient) { console.log('Cleaning up test records...'); const playerWhere = buildLikeClause(TEST_PATTERNS.players); const eventWhere = buildLikeClause(TEST_PATTERNS.events); const userWhere = buildEmailLikeClause(TEST_PATTERNS.users); await prisma.$executeRawUnsafe(`DELETE FROM events WHERE (${eventWhere});`); console.log('Deleted test events'); await prisma.$executeRawUnsafe(`DELETE FROM players WHERE (${playerWhere});`); console.log('Deleted test players'); await prisma.$executeRawUnsafe(`DELETE FROM users WHERE (${userWhere}) AND "playerId" IS NULL;`); console.log('Deleted test users (without player associations)'); await prisma.$disconnect(); } export default async function globalTeardown(config: FullConfig) { console.log('\n=== Global Teardown ==='); const dbUrl = process.env.DATABASE_URL || ''; if (isCIDatabase()) { console.log('CI environment - resetting database schema'); const prisma = createPrismaClient(); await resetDatabaseSchema(prisma); await prisma.$disconnect(); } else if (isProductionDatabase()) { console.log('Production environment - selective cleanup of test records'); const prisma = createPrismaClient(); await cleanupTestRecords(prisma); } else { console.log('Development environment - selective cleanup of test records'); const prisma = createPrismaClient(); await cleanupTestRecords(prisma); } console.log('=== Global Teardown Complete ===\n'); }