#!/usr/bin/env node /** * Script principal pour lancer tous les tests * Usage: node tests/run-tests.js [options] */ import { spawn } from 'child_process'; import { readdir } from 'fs/promises'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Configuration const config = { timeout: 30000, // 30 secondes par test verbose: process.argv.includes('--verbose'), watch: process.argv.includes('--watch'), coverage: process.argv.includes('--coverage'), pattern: process.argv.find(arg => arg.startsWith('--pattern='))?.split('=')[1] || '*', bail: process.argv.includes('--bail'), unitOnly: process.argv.includes('--unit-only'), integrationOnly: process.argv.includes('--integration-only') }; // Couleurs pour les logs const colors = { green: '\x1b[32m', red: '\x1b[31m', yellow: '\x1b[33m', blue: '\x1b[34m', reset: '\x1b[0m', bold: '\x1b[1m' }; function log(message, color = 'reset') { console.log(`${colors[color]}${message}${colors.reset}`); } function logSection(title) { log(`\n${'='.repeat(60)}`, 'blue'); log(`${title}`, 'bold'); log(`${'='.repeat(60)}`, 'blue'); } async function findTestFiles(directory, pattern = '*.test.js') { try { const files = await readdir(directory); return files .filter(file => file.endsWith('.test.js')) .filter(file => pattern === '*' || file.includes(pattern)) .map(file => path.join(directory, file)); } catch (error) { log(`⚠️ Impossible de lire ${directory}: ${error.message}`, 'yellow'); return []; } } async function runTests(testFiles, description) { if (testFiles.length === 0) { log(`📁 Aucun test trouvé pour ${description}`, 'yellow'); return { success: true, total: 0, passed: 0, failed: 0 }; } logSection(`🧪 ${description} (${testFiles.length} fichiers)`); const args = ['--test']; if (config.coverage) { args.push('--experimental-test-coverage'); } args.push(...testFiles); return new Promise((resolve) => { const nodeProcess = spawn('node', args, { stdio: 'pipe', cwd: path.resolve(__dirname, '..') }); let output = ''; let errorOutput = ''; nodeProcess.stdout.on('data', (data) => { const text = data.toString(); output += text; if (config.verbose) { process.stdout.write(text); } }); nodeProcess.stderr.on('data', (data) => { const text = data.toString(); errorOutput += text; if (config.verbose) { process.stderr.write(text); } }); nodeProcess.on('close', (code) => { const success = code === 0; // Parser les résultats basiques const lines = output.split('\n'); const summary = lines.find(line => line.includes('tests') && line.includes('passed')); let stats = { total: 0, passed: 0, failed: 0 }; if (summary) { const match = summary.match(/(\\d+) passed.*?(\\d+) failed/); if (match) { stats.passed = parseInt(match[1]); stats.failed = parseInt(match[2]); stats.total = stats.passed + stats.failed; } } if (success) { log(`✅ ${description} - Tous les tests sont passés!`, 'green'); } else { log(`❌ ${description} - Des tests ont échoué`, 'red'); if (!config.verbose && errorOutput) { log('Erreurs:', 'red'); console.error(errorOutput); } } resolve({ success, ...stats, output, errorOutput }); }); // Timeout setTimeout(() => { nodeProcess.kill(); log(`⏰ Timeout atteint pour ${description}`, 'yellow'); resolve({ success: false, total: 0, passed: 0, failed: 0, timeout: true }); }, config.timeout); }); } async function checkDependencies() { log('🔍 Vérification des dépendances...', 'blue'); // Vérifier que Node.js supporte --test const nodeVersion = process.version; const majorVersion = parseInt(nodeVersion.split('.')[0].slice(1)); if (majorVersion < 18) { log(`❌ Node.js ${nodeVersion} ne supporte pas --test. Version minimale: 18.0.0`, 'red'); return false; } log(`✅ Node.js ${nodeVersion} - Support des tests natif OK`, 'green'); return true; } async function setupEnvironment() { log('🔧 Configuration de l\'environnement de test...', 'blue'); // Variables d'environnement pour les tests process.env.NODE_ENV = 'test'; process.env.TEST_VERBOSE = config.verbose ? '1' : '0'; // Désactiver les logs en mode non-verbose if (!config.verbose) { process.env.SILENT_TESTS = '1'; } log('✅ Environnement configuré', 'green'); } function printSummary(results) { logSection('📊 Résumé des Tests'); let totalTests = 0, totalPassed = 0, totalFailed = 0; let allSuccess = true; results.forEach(({ description, success, total, passed, failed }) => { const status = success ? '✅' : '❌'; const stats = total > 0 ? ` (${passed}/${total})` : ''; log(`${status} ${description}${stats}`); totalTests += total; totalPassed += passed; totalFailed += failed; allSuccess = allSuccess && success; }); log('\\n' + '─'.repeat(40)); log(`📈 Total: ${totalTests} tests`, 'bold'); log(`✅ Réussis: ${totalPassed}`, 'green'); if (totalFailed > 0) { log(`❌ Échoués: ${totalFailed}`, 'red'); } const successRate = totalTests > 0 ? Math.round((totalPassed / totalTests) * 100) : 100; log(`📊 Taux de réussite: ${successRate}%`, successRate === 100 ? 'green' : 'yellow'); if (allSuccess && totalTests > 0) { log('\\n🎉 Tous les tests sont passés!', 'green'); } else if (totalFailed > 0) { log('\\n💥 Certains tests ont échoué', 'red'); } return allSuccess; } function printUsage() { log('📚 Usage: node tests/run-tests.js [options]\\n', 'bold'); log('Options disponibles:'); log(' --verbose Affichage détaillé des tests'); log(' --watch Mode surveillance (redémarre les tests si changement)'); log(' --coverage Rapport de couverture de code'); log(' --pattern=PATTERN Ne lancer que les tests contenant PATTERN'); log(' --bail Arrêter au premier échec'); log(' --unit-only Lancer seulement les tests unitaires'); log(' --integration-only Lancer seulement les tests d\'intégration'); log(' --help Afficher cette aide'); } async function main() { if (process.argv.includes('--help')) { printUsage(); return; } logSection('🚀 Class Generator - Suite de Tests'); // Vérifications préliminaires if (!(await checkDependencies())) { process.exit(1); } await setupEnvironment(); const results = []; try { // Tests unitaires if (!config.integrationOnly) { const unitTestDir = path.join(__dirname, 'unit'); const unitTests = await findTestFiles(unitTestDir, config.pattern); const unitResult = await runTests(unitTests, 'Tests Unitaires'); results.push({ description: 'Tests Unitaires', ...unitResult }); if (config.bail && !unitResult.success) { log('🛑 Arrêt après échec des tests unitaires (--bail)', 'red'); process.exit(1); } } // Tests d'intégration if (!config.unitOnly) { const integrationTestDir = path.join(__dirname, 'integration'); const integrationTests = await findTestFiles(integrationTestDir, config.pattern); const integrationResult = await runTests(integrationTests, 'Tests d\'Intégration'); results.push({ description: 'Tests d\'Intégration', ...integrationResult }); if (config.bail && !integrationResult.success) { log('🛑 Arrêt après échec des tests d\'intégration (--bail)', 'red'); process.exit(1); } } // Résumé final const allSuccess = printSummary(results); // Code de sortie process.exit(allSuccess ? 0 : 1); } catch (error) { log(`💥 Erreur lors de l'exécution des tests: ${error.message}`, 'red'); console.error(error); process.exit(1); } } // Mode watch if (config.watch) { log('👀 Mode surveillance activé - Ctrl+C pour arrêter', 'yellow'); // TODO: Implémenter la surveillance des fichiers log('⚠️ Mode watch non encore implémenté', 'yellow'); } // Lancement main().catch(error => { console.error('Erreur fatale:', error); process.exit(1); });