Files
euchre_camp/e2e/global.setup.ts

190 lines
6.1 KiB
TypeScript

/**
* Global setup for Playwright tests
* Creates test users and saves authentication state
*/
import { chromium, type FullConfig } from '@playwright/test';
import { PrismaClient } from '@prisma/client';
import { PrismaPg } from '@prisma/adapter-pg';
import path from 'path';
import fs from 'fs';
const authFile = 'playwright/.auth/user.json';
const adminAuthFile = 'playwright/.auth/admin.json';
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');
}
if (isProductionDatabase()) {
console.error('');
console.error('='.repeat(80));
console.error('CRITICAL ERROR: 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 development (euchre_camp_dev) or CI (euchre_camp_ci)');
console.error('');
console.error('Aborting test execution to prevent data corruption.');
console.error('');
process.exit(1);
}
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...');
const { execSync } = await import('child_process');
execSync('bunx prisma migrate deploy', { stdio: 'inherit' });
}
async function createTestUsers(config: FullConfig) {
const baseURL = config.projects[0]?.use?.baseURL || 'http://localhost:3000';
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
page.on('response', response => {
if (response.url().includes('/api/auth')) {
console.log('API Response:', response.status(), response.url());
}
});
const timestamp = Date.now();
const testEmail = `setup-user-${timestamp}@example.com`;
const testPassword = 'TestPassword1234!';
const testName = 'Setup User';
try {
console.log('Navigating to registration page...');
await page.goto(`${baseURL}/auth/register`);
await page.fill('input[name="name"]', testName);
await page.fill('input[name="email"]', testEmail);
await page.fill('input[name="password"]', testPassword);
console.log('Submitting registration form...');
await page.click('button[type="submit"]');
try {
await page.waitForResponse(response =>
response.url().includes('/api/auth/sign-up/email') && response.status() === 200,
{ timeout: 10000 }
);
console.log('Sign-up API call successful');
} catch {
console.log('Sign-up API call failed or timed out');
}
await page.waitForTimeout(2000);
await context.storageState({ path: authFile });
console.log(`Created and authenticated test user: ${testEmail}`);
// Clear session so admin registration doesn't get redirected
await context.clearCookies();
const adminTimestamp = timestamp + 1;
const adminEmail = `setup-admin-${adminTimestamp}@example.com`;
const adminPassword = 'AdminPassword123!';
const adminName = 'Setup Admin';
console.log('Navigating to registration page for admin...');
await page.goto(`${baseURL}/auth/register`);
await page.fill('input[name="name"]', adminName);
await page.fill('input[name="email"]', adminEmail);
await page.fill('input[name="password"]', adminPassword);
console.log('Submitting admin registration form...');
await page.click('button[type="submit"]');
try {
await page.waitForResponse(response =>
response.url().includes('/api/auth/sign-up/email') && response.status() === 200,
{ timeout: 10000 }
);
console.log('Admin sign-up API call successful');
} catch {
console.log('Admin sign-up API call failed or timed out');
}
await page.waitForTimeout(2000);
const prisma = createPrismaClient();
const user = await prisma.user.findUnique({ where: { email: adminEmail } });
if (user) {
await prisma.user.update({
where: { id: user.id },
data: { role: 'club_admin' }
});
console.log('Updated user role to club_admin');
}
await prisma.$disconnect();
console.log('Navigating to admin page for admin user...');
await page.goto(`${baseURL}/admin`);
await page.waitForLoadState('domcontentloaded');
console.log('Admin page loaded:', page.url());
await page.waitForTimeout(2000);
await page.reload();
await page.waitForLoadState('domcontentloaded');
console.log('Page reloaded');
await context.storageState({ path: adminAuthFile });
console.log(`Created and authenticated admin user: ${adminEmail}`);
} finally {
await browser.close();
}
}
export default async function globalSetup(config: FullConfig) {
console.log('=== Global Setup ===');
console.log('DATABASE_URL:', process.env.DATABASE_URL);
if (isCIDatabase()) {
console.log('CI environment detected - will reset database schema');
const prisma = createPrismaClient();
await resetDatabaseSchema(prisma);
await prisma.$disconnect();
} else if (!isProductionDatabase()) {
console.log('Development environment - preserving existing data');
}
await createTestUsers(config);
console.log('=== Global Setup Complete ===\n');
}