- Replace 'any' types with proper TypeScript interfaces in auth setup/teardown - Remove conflicting custom Playwright type declarations that were overriding official types - Fix ES module compatibility by replacing require() with proper import paths - Add proper generic typing to Playwright test fixtures - Fix test discovery in auth debug configuration - Add comprehensive auth debug setup documentation Fixes: - 3 lint warnings about explicit 'any' usage - 45+ TypeScript compilation errors from type conflicts - ES module import errors in auth configuration - Test fixture typing issues All e2e tests now pass lint and type checking with zero warnings.
355 lines
9.3 KiB
TypeScript
355 lines
9.3 KiB
TypeScript
import { chromium } from 'playwright';
|
|
import { logger } from '../../services/logging';
|
|
|
|
interface GlobalSetupConfig {
|
|
projects?: Array<{
|
|
name: string;
|
|
use?: Record<string, unknown>;
|
|
}>;
|
|
webServer?: Array<{
|
|
command: string;
|
|
port: number;
|
|
timeout: number;
|
|
reuseExistingServer: boolean;
|
|
}>;
|
|
use?: {
|
|
baseURL?: string;
|
|
trace?: string;
|
|
screenshot?: string;
|
|
};
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
/**
|
|
* Global setup for authentication debug tests
|
|
* Ensures services are running and database is properly initialized
|
|
*/
|
|
async function globalSetup(_config: GlobalSetupConfig) {
|
|
logger.info(
|
|
'🔧 Setting up authentication debug test environment...',
|
|
'SETUP'
|
|
);
|
|
|
|
const startTime = Date.now();
|
|
|
|
try {
|
|
// Wait for services to be ready
|
|
await waitForServices();
|
|
|
|
// Initialize test data if needed
|
|
await initializeTestData();
|
|
|
|
// Verify admin user exists
|
|
await verifyAdminUser();
|
|
|
|
const duration = Date.now() - startTime;
|
|
logger.info(`✅ Auth debug setup completed in ${duration}ms`, 'SETUP');
|
|
} catch (error) {
|
|
logger.error('❌ Auth debug setup failed:', 'SETUP', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wait for required services to be available
|
|
*/
|
|
async function waitForServices(): Promise<void> {
|
|
logger.info('⏳ Waiting for services to be ready...', 'SETUP');
|
|
|
|
// Wait for frontend
|
|
await waitForService('http://localhost:8080', 'Frontend');
|
|
|
|
// Wait for CouchDB
|
|
await waitForService('http://localhost:5984', 'CouchDB');
|
|
|
|
logger.info('✅ All services are ready', 'SETUP');
|
|
}
|
|
|
|
/**
|
|
* Wait for a specific service to be available
|
|
*/
|
|
async function waitForService(url: string, serviceName: string): Promise<void> {
|
|
const maxAttempts = 30;
|
|
const delay = 2000; // 2 seconds
|
|
|
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
try {
|
|
const response = await fetch(url, {
|
|
method: 'GET',
|
|
headers: {
|
|
Authorization:
|
|
'Basic ' + Buffer.from('admin:password').toString('base64'),
|
|
},
|
|
signal: AbortSignal.timeout(5000), // 5 second timeout per request
|
|
});
|
|
|
|
if (response.ok) {
|
|
logger.info(`✅ ${serviceName} is ready at ${url}`, 'SETUP');
|
|
return;
|
|
}
|
|
} catch (error) {
|
|
// Log specific error types for better debugging
|
|
if (error instanceof Error) {
|
|
logger.debug(
|
|
`⏳ ${serviceName} connection failed: ${error.message}`,
|
|
'SETUP'
|
|
);
|
|
}
|
|
}
|
|
|
|
if (attempt === maxAttempts) {
|
|
throw new Error(
|
|
`${serviceName} not available at ${url} after ${maxAttempts} attempts`
|
|
);
|
|
}
|
|
|
|
logger.debug(
|
|
`⏳ ${serviceName} not ready, attempt ${attempt}/${maxAttempts}...`,
|
|
'SETUP'
|
|
);
|
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize test data and ensure databases exist
|
|
*/
|
|
async function initializeTestData(): Promise<void> {
|
|
logger.info('🔧 Initializing test data...', 'SETUP');
|
|
|
|
try {
|
|
// Check if databases exist, create if needed
|
|
const databases = [
|
|
'users',
|
|
'medications',
|
|
'settings',
|
|
'taken_doses',
|
|
'reminders',
|
|
];
|
|
|
|
for (const dbName of databases) {
|
|
await ensureDatabaseExists(dbName);
|
|
}
|
|
|
|
logger.info('✅ Test data initialization completed', 'SETUP');
|
|
} catch (error) {
|
|
logger.warn('⚠️ Test data initialization had issues:', 'SETUP', error);
|
|
// Don't fail setup for database issues as the app should handle missing DBs
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ensure a database exists
|
|
*/
|
|
async function ensureDatabaseExists(dbName: string): Promise<void> {
|
|
const url = `http://localhost:5984/${dbName}`;
|
|
|
|
try {
|
|
const response = await fetch(url, {
|
|
method: 'HEAD',
|
|
headers: {
|
|
Authorization:
|
|
'Basic ' + Buffer.from('admin:password').toString('base64'),
|
|
},
|
|
});
|
|
|
|
if (response.status === 404) {
|
|
// Database doesn't exist, create it
|
|
await fetch(url, {
|
|
method: 'PUT',
|
|
headers: {
|
|
Authorization:
|
|
'Basic ' + Buffer.from('admin:password').toString('base64'),
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
logger.info(`📊 Created database: ${dbName}`, 'SETUP');
|
|
} else if (response.ok) {
|
|
logger.info(`✅ Database exists: ${dbName}`, 'SETUP');
|
|
}
|
|
} catch (error) {
|
|
logger.warn(
|
|
`⚠️ Could not verify/create database ${dbName}:`,
|
|
'SETUP',
|
|
error
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verify admin user exists for testing
|
|
*/
|
|
async function verifyAdminUser(): Promise<void> {
|
|
logger.info('🔧 Verifying admin user exists...', 'SETUP');
|
|
|
|
try {
|
|
const browser = await chromium.launch();
|
|
const context = await browser.newContext();
|
|
const page = await context.newPage();
|
|
|
|
// Navigate to the app
|
|
await page.goto('http://localhost:8080');
|
|
|
|
// Wait for login form
|
|
await page.waitForSelector('input[type="email"]', { timeout: 10000 });
|
|
|
|
// Try to login with admin credentials
|
|
await page.fill('input[type="email"]', 'admin@localhost');
|
|
await page.fill('input[type="password"]', 'admin123!');
|
|
await page.click('button[type="submit"]');
|
|
|
|
// Check if login was successful
|
|
try {
|
|
await page.waitForSelector('h1:has-text("Medication Reminder")', {
|
|
timeout: 15000,
|
|
});
|
|
logger.info('✅ Admin user verified and functional', 'SETUP');
|
|
} catch (error) {
|
|
logger.warn(
|
|
'⚠️ Admin user may not exist or credentials may be incorrect',
|
|
'SETUP'
|
|
);
|
|
logger.warn(
|
|
' Tests will attempt to create/verify admin user during execution',
|
|
'SETUP'
|
|
);
|
|
if (error instanceof Error) {
|
|
logger.debug(`Login verification error: ${error.message}`, 'SETUP');
|
|
}
|
|
}
|
|
|
|
await browser.close();
|
|
} catch (error) {
|
|
logger.warn('⚠️ Could not verify admin user', 'SETUP');
|
|
if (error instanceof Error) {
|
|
logger.debug(`Admin verification error: ${error.message}`, 'SETUP');
|
|
}
|
|
// Don't fail setup, let individual tests handle this
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log environment information
|
|
*/
|
|
function logEnvironmentInfo(): void {
|
|
logger.info('🌍 Environment Information:', 'SETUP');
|
|
logger.info(` Node.js: ${process.version}`, 'SETUP');
|
|
logger.info(` Platform: ${process.platform}`, 'SETUP');
|
|
logger.info(` NODE_ENV: ${process.env.NODE_ENV || 'not set'}`, 'SETUP');
|
|
logger.info(` CI: ${process.env.CI || 'false'}`, 'SETUP');
|
|
logger.info(` Frontend URL: http://localhost:8080`, 'SETUP');
|
|
logger.info(` CouchDB URL: http://localhost:5984`, 'SETUP');
|
|
}
|
|
|
|
/**
|
|
* Test database connectivity and return detailed status
|
|
*/
|
|
async function testDatabaseConnectivity(): Promise<{
|
|
connected: boolean;
|
|
databases: string[];
|
|
errors: string[];
|
|
}> {
|
|
const result = {
|
|
connected: false,
|
|
databases: [] as string[],
|
|
errors: [] as string[],
|
|
};
|
|
|
|
try {
|
|
// Test basic connectivity
|
|
const response = await fetch('http://localhost:5984/', {
|
|
headers: {
|
|
Authorization:
|
|
'Basic ' + Buffer.from('admin:password').toString('base64'),
|
|
},
|
|
signal: AbortSignal.timeout(5000),
|
|
});
|
|
|
|
if (response.ok) {
|
|
result.connected = true;
|
|
const info = await response.json();
|
|
logger.debug(`CouchDB version: ${info.version}`, 'SETUP');
|
|
|
|
// Get list of databases
|
|
const dbResponse = await fetch('http://localhost:5984/_all_dbs', {
|
|
headers: {
|
|
Authorization:
|
|
'Basic ' + Buffer.from('admin:password').toString('base64'),
|
|
},
|
|
});
|
|
|
|
if (dbResponse.ok) {
|
|
result.databases = await dbResponse.json();
|
|
logger.debug(
|
|
`Found databases: ${result.databases.join(', ')}`,
|
|
'SETUP'
|
|
);
|
|
}
|
|
} else {
|
|
result.errors.push(`HTTP ${response.status}: ${response.statusText}`);
|
|
}
|
|
} catch (error) {
|
|
if (error instanceof Error) {
|
|
result.errors.push(error.message);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Perform comprehensive service diagnostics
|
|
*/
|
|
async function runServiceDiagnostics(): Promise<void> {
|
|
logger.info('🔍 Running comprehensive service diagnostics...', 'SETUP');
|
|
|
|
// Test frontend
|
|
try {
|
|
const frontendResponse = await fetch('http://localhost:8080', {
|
|
signal: AbortSignal.timeout(5000),
|
|
});
|
|
logger.info(
|
|
`Frontend status: ${frontendResponse.status} ${frontendResponse.statusText}`,
|
|
'SETUP'
|
|
);
|
|
} catch (error) {
|
|
logger.warn(
|
|
`Frontend not accessible: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
'SETUP'
|
|
);
|
|
}
|
|
|
|
// Test database with detailed info
|
|
const dbStatus = await testDatabaseConnectivity();
|
|
if (dbStatus.connected) {
|
|
logger.info(
|
|
`Database connected with ${dbStatus.databases.length} databases`,
|
|
'SETUP'
|
|
);
|
|
} else {
|
|
logger.warn(
|
|
`Database connection failed: ${dbStatus.errors.join(', ')}`,
|
|
'SETUP'
|
|
);
|
|
}
|
|
|
|
// Test network connectivity
|
|
try {
|
|
const response = await fetch('http://localhost:8080/health', {
|
|
signal: AbortSignal.timeout(3000),
|
|
});
|
|
if (response.ok) {
|
|
logger.info('Health endpoint accessible', 'SETUP');
|
|
}
|
|
} catch {
|
|
logger.debug('Health endpoint not available (this is optional)', 'SETUP');
|
|
}
|
|
}
|
|
|
|
// Execute setup
|
|
export default async function (config: GlobalSetupConfig) {
|
|
logEnvironmentInfo();
|
|
await runServiceDiagnostics();
|
|
await globalSetup(config);
|
|
}
|