agmission/Development/server/tests/convert_to_mocha.js

236 lines
7.4 KiB
JavaScript

#!/usr/bin/env node
/**
* Automated Test Conversion Script
* Converts standalone Node.js test scripts to Mocha format
*
* Usage:
* node tests/convert_to_mocha.js [--dry-run] [--pattern 'glob']
*/
const fs = require('fs');
const path = require('path');
const glob = require('glob');
// Parse arguments
const args = process.argv.slice(2);
let dryRun = false;
let pattern = 'tests/**/test_*.js';
for (let i = 0; i < args.length; i++) {
if (args[i] === '--dry-run') {
dryRun = true;
} else if (args[i] === '--pattern' && args[i + 1]) {
pattern = args[i + 1];
i++;
}
}
console.log('╔════════════════════════════════════════════════════════════╗');
console.log('║ Mocha Test Conversion Script ║');
console.log('╚════════════════════════════════════════════════════════════╝\n');
console.log(`Pattern: ${pattern}`);
console.log(`Dry Run: ${dryRun}\n`);
/**
* Convert a standalone test file to Mocha format
*/
function convertToMocha(filePath, content) {
let converted = content;
// Step 1: Remove shebang and convert header
converted = converted.replace(/^#!\/usr\/bin\/env node\n/, '');
// Step 2: Remove environment loading boilerplate (replaced by tests/setup.js)
const envLoadingPattern = /\/\/ Parse.*?const envPath.*?require\('dotenv'\)\.config\(\{ path: envPath \}\);?\n+/s;
converted = converted.replace(envLoadingPattern, '');
// Step 3: Add Mocha/Chai imports if not present
if (!converted.includes('require(\'chai\')')) {
const firstRequire = converted.search(/^const |^require\(/m);
if (firstRequire !== -1) {
converted = converted.slice(0, firstRequire) +
`const { expect } = require('chai');\n` +
converted.slice(firstRequire);
} else {
converted = `const { expect } = require('chai');\n\n` + converted;
}
}
// Step 4: Extract test name from file path
const testName = path.basename(filePath, '.js')
.replace(/^test_/, '')
.replace(/_/g, ' ')
.replace(/\b\w/g, l => l.toUpperCase());
// Step 5: Detect main async function pattern
const mainFunctionPattern = /async function (main|test\w+)\(\) \{[\s\S]*?\}\s*(?:main\(\)|test\w+\(\)).*?\.catch/;
const hasMainFunction = mainFunctionPattern.test(converted);
if (hasMainFunction) {
// Extract main function body
const mainMatch = converted.match(/async function (main|test\w+)\(\) \{\s*([\s\S]*?)\s*\}\s*(?:main\(\)|test\w+\(\))/);
if (mainMatch) {
const functionBody = mainMatch[2];
// Wrap in describe block
converted = converted.replace(
mainFunctionPattern,
`describe('${testName}', function() {
this.timeout(60000);
it('should complete successfully', async function() {
${functionBody.split('\n').map(line => ' ' + line).join('\n')}
});
});`
);
}
} else {
// For simple scripts without main function
// Try to detect test sections and wrap them
const lines = converted.split('\n');
let inTestSection = false;
let testSections = [];
let currentSection = [];
for (const line of lines) {
if (line.includes('console.log') && (line.includes('Test ') || line.includes('===') || line.includes('───'))) {
if (currentSection.length > 0) {
testSections.push(currentSection.join('\n'));
currentSection = [];
}
inTestSection = true;
}
if (inTestSection) {
currentSection.push(line);
}
}
if (currentSection.length > 0) {
testSections.push(currentSection.join('\n'));
}
}
// Step 6: Convert console.log assertions to expect()
// This is a simple heuristic - manual review still needed
converted = converted.replace(
/if \((.*?) === (.*?)\) \{\s*console\.log\('✓.*?'\);?\s*\} else \{\s*console\.log\('✗.*?'\);?\s*(?:process\.exit|return)\(1\);?\s*\}/g,
'expect($1).to.equal($2);'
);
converted = converted.replace(
/if \((.*?) !== (.*?)\) \{\s*console\.log\('✓.*?'\);?\s*\} else \{\s*console\.log\('✗.*?'\);?\s*(?:process\.exit|return)\(1\);?\s*\}/g,
'expect($1).to.not.equal($2);'
);
// Step 7: Remove process.exit calls
converted = converted.replace(/process\.exit\(\d+\);?/g, '');
converted = converted.replace(/\.then\(exitCode => process\.exit\(exitCode\)\)/g, '');
converted = converted.replace(/\.catch\(error => \{\s*console\.error.*?process\.exit\(1\);?\s*\}\);?/gs, '');
// Step 8: Handle try-catch blocks that return exit codes
converted = converted.replace(
/try \{([\s\S]*?)\s+return 0;\s*\} catch \(error\) \{[\s\S]*?return 1;\s*\}/g,
'try {$1\n } catch (error) {\n throw error;\n }'
);
return converted;
}
/**
* Find all test files matching pattern
*/
function findTestFiles() {
const exclude = [
'**/node_modules/**',
'**/run_all_tests.js',
'**/organize_tests.js',
'**/fix_paths.js',
'**/setup.js',
'**/convert_to_mocha.js',
'**/*.spec.js',
'**/manual_*.js'
];
return glob.sync(pattern, {
ignore: exclude,
absolute: true
});
}
/**
* Main execution
*/
async function main() {
const files = findTestFiles();
if (files.length === 0) {
console.log('No test files found matching pattern.');
return;
}
console.log(`Found ${files.length} test files to convert:\n`);
let converted = 0;
let skipped = 0;
let errors = 0;
for (const filePath of files) {
const relativePath = path.relative(process.cwd(), filePath);
try {
// Skip files that are already converted (contain describe/it)
const content = fs.readFileSync(filePath, 'utf8');
if (content.includes('describe(') && content.includes('it(')) {
console.log(`${relativePath} - Already Mocha format`);
skipped++;
continue;
}
const convertedContent = convertToMocha(filePath, content);
if (dryRun) {
console.log(`${relativePath} - Would convert`);
} else {
// Backup original
const backupPath = filePath + '.pre-mocha-backup';
fs.writeFileSync(backupPath, content, 'utf8');
// Write converted
fs.writeFileSync(filePath, convertedContent, 'utf8');
console.log(`${relativePath} - Converted (backup: ${path.basename(backupPath)})`);
}
converted++;
} catch (error) {
console.error(`${relativePath} - Error: ${error.message}`);
errors++;
}
}
console.log('\n' + '═'.repeat(64));
console.log('Summary');
console.log('═'.repeat(64));
console.log(`Converted: ${converted}`);
console.log(`Skipped: ${skipped} (already Mocha format)`);
console.log(`Errors: ${errors}`);
console.log(`Total: ${files.length}`);
if (dryRun) {
console.log('\n⚠ Dry run mode - no files were modified');
console.log('Run without --dry-run to apply changes');
} else {
console.log('\n✓ Conversion complete');
console.log('⚠ IMPORTANT: Review and test converted files manually!');
console.log(' Automated conversion handles common patterns but may need adjustments.');
console.log(` Backups saved with .pre-mocha-backup extension`);
}
}
main().catch(error => {
console.error('Fatal error:', error);
process.exit(1);
});