201 lines
6.3 KiB
JavaScript
Executable File
201 lines
6.3 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
/**
|
|
* Test runner for standalone integration tests
|
|
* Spawns separate Node.js processes for each test file to handle process.exit() calls
|
|
*
|
|
* Usage:
|
|
* node tests/run_all_tests.js # Run all tests
|
|
* node tests/run_all_tests.js --pattern 'promo/test_*.js' # Run specific pattern
|
|
* node tests/run_all_tests.js --verbose # Show detailed output
|
|
* node tests/run_all_tests.js --bail # Stop on first failure
|
|
*/
|
|
|
|
const path = require('path');
|
|
const glob = require('glob');
|
|
const { spawn } = require('child_process');
|
|
|
|
// Parse arguments
|
|
const args = process.argv.slice(2);
|
|
let envFile = './environment.env';
|
|
let pattern = '**/test_*.js'; // Default: all tests in subdirectories
|
|
let verbose = false;
|
|
let bail = false;
|
|
|
|
for (let i = 0; i < args.length; i++) {
|
|
if (args[i] === '--env' && args[i + 1]) {
|
|
envFile = args[i + 1];
|
|
i++;
|
|
} else if (args[i] === '--pattern' && args[i + 1]) {
|
|
pattern = args[i + 1];
|
|
i++;
|
|
} else if (args[i] === '--verbose' || args[i] === '-v') {
|
|
verbose = true;
|
|
} else if (args[i] === '--bail' || args[i] === '--stop-on-failure') {
|
|
bail = true;
|
|
}
|
|
}
|
|
|
|
// Load environment BEFORE finding files (in case env affects file discovery)
|
|
const envPath = path.resolve(process.cwd(), envFile);
|
|
require('dotenv').config({ path: envPath });
|
|
|
|
console.log('═══════════════════════════════════════════════════════');
|
|
console.log('🧪 AgMission Test Runner');
|
|
console.log('═══════════════════════════════════════════════════════');
|
|
console.log(`📁 Environment: ${envFile}`);
|
|
console.log(`🔍 Pattern: ${pattern}`);
|
|
console.log(`📢 Verbose: ${verbose}`);
|
|
console.log(`🛑 Stop on failure: ${bail}`);
|
|
console.log('═══════════════════════════════════════════════════════\n');
|
|
|
|
// Find all test files
|
|
const testPattern = `tests/${pattern}`;
|
|
const files = glob.sync(testPattern, {
|
|
ignore: [
|
|
'**/node_modules/**',
|
|
'**/organize_tests.js',
|
|
'**/fix_paths.js',
|
|
'**/run_all_tests.js',
|
|
'**/setup.js',
|
|
'**/*.spec.js', // Skip Mocha tests
|
|
'**/manual_*.js' // Skip manual scripts
|
|
]
|
|
});
|
|
|
|
if (files.length === 0) {
|
|
console.log(`❌ No test files found matching: ${testPattern}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(`📋 Found ${files.length} test files:\n`);
|
|
files.forEach((f, i) => {
|
|
console.log(` ${i + 1}. ${f}`);
|
|
});
|
|
console.log();
|
|
|
|
// Run each test file in separate process
|
|
let passed = 0;
|
|
let failed = 0;
|
|
const failures = [];
|
|
const startTime = Date.now();
|
|
|
|
/**
|
|
* Run a single test file in a separate process
|
|
*/
|
|
function runTest(file) {
|
|
return new Promise((resolve) => {
|
|
const fileName = path.basename(file);
|
|
const testStartTime = Date.now();
|
|
|
|
console.log('─'.repeat(60));
|
|
console.log(`🧪 Running: ${file}`);
|
|
console.log('─'.repeat(60));
|
|
|
|
const child = spawn('node', [file], {
|
|
cwd: process.cwd(),
|
|
stdio: verbose ? 'inherit' : 'pipe',
|
|
env: process.env
|
|
});
|
|
|
|
let output = '';
|
|
let errorOutput = '';
|
|
|
|
if (!verbose) {
|
|
child.stdout?.on('data', (data) => {
|
|
output += data.toString();
|
|
});
|
|
|
|
child.stderr?.on('data', (data) => {
|
|
errorOutput += data.toString();
|
|
});
|
|
}
|
|
|
|
child.on('close', (code) => {
|
|
const duration = Date.now() - testStartTime;
|
|
|
|
if (code === 0) {
|
|
console.log(`✅ PASSED: ${fileName} (${duration}ms)\n`);
|
|
resolve({ passed: true, file: fileName, duration });
|
|
} else {
|
|
console.log(`❌ FAILED: ${fileName} (${duration}ms)`);
|
|
console.log(` Exit code: ${code}`);
|
|
|
|
// Show output only on failure (unless verbose)
|
|
if (!verbose && (output || errorOutput)) {
|
|
console.log(` Output preview:`);
|
|
const preview = (errorOutput || output).split('\n').slice(-5).join('\n');
|
|
console.log(preview.split('\n').map(l => ' ' + l).join('\n'));
|
|
}
|
|
console.log();
|
|
|
|
resolve({
|
|
passed: false,
|
|
file: fileName,
|
|
duration,
|
|
error: `Exit code: ${code}`,
|
|
output: errorOutput || output
|
|
});
|
|
}
|
|
});
|
|
|
|
child.on('error', (error) => {
|
|
const duration = Date.now() - testStartTime;
|
|
console.log(`❌ FAILED: ${fileName} (${duration}ms)`);
|
|
console.error(` Error: ${error.message}\n`);
|
|
|
|
resolve({
|
|
passed: false,
|
|
file: fileName,
|
|
duration,
|
|
error: error.message
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
async function runTests() {
|
|
for (let i = 0; i < files.length; i++) {
|
|
const result = await runTest(files[i]);
|
|
|
|
if (result.passed) {
|
|
passed++;
|
|
} else {
|
|
failed++;
|
|
failures.push(result);
|
|
|
|
// Stop on first failure if --bail flag is set
|
|
if (bail) {
|
|
console.log('🛑 Stopping due to test failure (--bail flag)\n');
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Summary
|
|
const totalDuration = Date.now() - startTime;
|
|
const totalTests = passed + failed;
|
|
|
|
console.log('═══════════════════════════════════════════════════════');
|
|
console.log('📊 TEST SUMMARY');
|
|
console.log('═══════════════════════════════════════════════════════');
|
|
console.log(`✅ Passed: ${passed}/${totalTests}`);
|
|
console.log(`❌ Failed: ${failed}/${totalTests}`);
|
|
console.log(`⏱️ Total Duration: ${(totalDuration / 1000).toFixed(2)}s`);
|
|
|
|
if (failures.length > 0) {
|
|
console.log('\n❌ FAILED TESTS:');
|
|
failures.forEach((f, i) => {
|
|
console.log(` ${i + 1}. ${f.file} - ${f.error}`);
|
|
});
|
|
}
|
|
|
|
console.log('═══════════════════════════════════════════════════════\n');
|
|
|
|
process.exit(failed > 0 ? 1 : 0);
|
|
}
|
|
|
|
runTests().catch(err => {
|
|
console.error('Fatal error running tests:', err);
|
|
process.exit(1);
|
|
});
|