✨ Increase comprehensive testing capabilities
This commit is contained in:
239
tests/integration.test.js
Normal file
239
tests/integration.test.js
Normal file
@@ -0,0 +1,239 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Integration Tests for Pake CLI
|
||||
*
|
||||
* These tests verify that different components work together correctly.
|
||||
* They may take longer to run as they test actual build processes.
|
||||
*/
|
||||
|
||||
import { spawn, execSync } from 'child_process';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import config, { TIMEOUTS, TEST_URLS } from './test.config.js';
|
||||
import ora from 'ora';
|
||||
|
||||
class IntegrationTestRunner {
|
||||
constructor() {
|
||||
this.tests = [];
|
||||
this.results = [];
|
||||
this.tempFiles = [];
|
||||
}
|
||||
|
||||
addTest(name, testFn, timeout = TIMEOUTS.MEDIUM) {
|
||||
this.tests.push({ name, testFn, timeout });
|
||||
}
|
||||
|
||||
async runAll() {
|
||||
console.log('🔧 Integration Tests');
|
||||
console.log('====================\n');
|
||||
|
||||
for (const [index, test] of this.tests.entries()) {
|
||||
const spinner = ora(`Running ${test.name}...`).start();
|
||||
|
||||
try {
|
||||
const result = await Promise.race([
|
||||
test.testFn(),
|
||||
new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Timeout')), test.timeout)
|
||||
)
|
||||
]);
|
||||
|
||||
if (result) {
|
||||
spinner.succeed(`${index + 1}. ${test.name}: PASS`);
|
||||
this.results.push({ name: test.name, passed: true });
|
||||
} else {
|
||||
spinner.fail(`${index + 1}. ${test.name}: FAIL`);
|
||||
this.results.push({ name: test.name, passed: false });
|
||||
}
|
||||
} catch (error) {
|
||||
spinner.fail(`${index + 1}. ${test.name}: ERROR - ${error.message.slice(0, 50)}...`);
|
||||
this.results.push({ name: test.name, passed: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
this.cleanup();
|
||||
this.displayResults();
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
// Clean up any temporary files created during tests
|
||||
this.tempFiles.forEach(file => {
|
||||
try {
|
||||
if (fs.existsSync(file)) {
|
||||
fs.unlinkSync(file);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Warning: Could not clean up ${file}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
displayResults() {
|
||||
const passed = this.results.filter(r => r.passed).length;
|
||||
const total = this.results.length;
|
||||
|
||||
console.log(`\n📊 Integration Test Results: ${passed}/${total} passed\n`);
|
||||
|
||||
if (passed === total) {
|
||||
console.log('🎉 All integration tests passed!');
|
||||
} else {
|
||||
console.log('❌ Some integration tests failed');
|
||||
this.results.filter(r => !r.passed).forEach(result => {
|
||||
console.log(` - ${result.name}${result.error ? `: ${result.error}` : ''}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
trackTempFile(filepath) {
|
||||
this.tempFiles.push(filepath);
|
||||
}
|
||||
}
|
||||
|
||||
const runner = new IntegrationTestRunner();
|
||||
|
||||
// Integration Tests
|
||||
runner.addTest(
|
||||
'CLI Process Spawning',
|
||||
() => {
|
||||
return new Promise((resolve) => {
|
||||
const child = spawn('node', [config.CLI_PATH, '--version'], {
|
||||
stdio: ['pipe', 'pipe', 'pipe']
|
||||
});
|
||||
|
||||
let output = '';
|
||||
child.stdout.on('data', (data) => {
|
||||
output += data.toString();
|
||||
});
|
||||
|
||||
child.on('close', (code) => {
|
||||
resolve(code === 0 && /\d+\.\d+\.\d+/.test(output));
|
||||
});
|
||||
|
||||
// Kill after 3 seconds if still running
|
||||
setTimeout(() => {
|
||||
child.kill();
|
||||
resolve(false);
|
||||
}, 3000);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
runner.addTest(
|
||||
'Interactive Mode Simulation',
|
||||
() => {
|
||||
return new Promise((resolve) => {
|
||||
const child = spawn('node', [config.CLI_PATH, TEST_URLS.WEEKLY], {
|
||||
stdio: ['pipe', 'pipe', 'pipe']
|
||||
});
|
||||
|
||||
let output = '';
|
||||
let prompted = false;
|
||||
|
||||
child.stdout.on('data', (data) => {
|
||||
output += data.toString();
|
||||
// If we see a prompt for application name, provide input
|
||||
if (output.includes('Enter your application name') && !prompted) {
|
||||
prompted = true;
|
||||
child.stdin.write('TestApp\n');
|
||||
setTimeout(() => {
|
||||
child.kill();
|
||||
resolve(true);
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
child.on('close', () => {
|
||||
resolve(prompted);
|
||||
});
|
||||
|
||||
// Timeout after 10 seconds
|
||||
setTimeout(() => {
|
||||
child.kill();
|
||||
resolve(false);
|
||||
}, 10000);
|
||||
});
|
||||
},
|
||||
TIMEOUTS.MEDIUM
|
||||
);
|
||||
|
||||
runner.addTest(
|
||||
'Command Line Argument Parsing',
|
||||
() => {
|
||||
try {
|
||||
// Test argument validation by running CLI with --help to verify args are parsed
|
||||
const helpOutput = execSync(`node "${config.CLI_PATH}" --help`, {
|
||||
encoding: 'utf8',
|
||||
timeout: 3000
|
||||
});
|
||||
|
||||
// Verify that our command structure is valid by checking help includes our options
|
||||
const validOptions = ['--width', '--height', '--debug', '--name'].every(opt =>
|
||||
helpOutput.includes(opt)
|
||||
);
|
||||
|
||||
return validOptions;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
TIMEOUTS.QUICK
|
||||
);
|
||||
|
||||
runner.addTest(
|
||||
'File System Permissions',
|
||||
() => {
|
||||
try {
|
||||
// Test that we can write to current directory
|
||||
const testFile = 'test-write-permission.tmp';
|
||||
fs.writeFileSync(testFile, 'test');
|
||||
runner.trackTempFile(testFile);
|
||||
|
||||
// Test that we can read from CLI directory
|
||||
const cliStats = fs.statSync(config.CLI_PATH);
|
||||
return cliStats.isFile();
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
runner.addTest(
|
||||
'Dependency Resolution',
|
||||
() => {
|
||||
try {
|
||||
// Verify that essential runtime dependencies are available
|
||||
const packageJsonPath = path.join(config.PROJECT_ROOT, 'package.json');
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
|
||||
const essentialDeps = [
|
||||
'commander',
|
||||
'chalk',
|
||||
'fs-extra',
|
||||
'execa',
|
||||
'prompts'
|
||||
];
|
||||
|
||||
return essentialDeps.every(dep => {
|
||||
try {
|
||||
// Try to resolve the dependency
|
||||
import.meta.resolve ?
|
||||
import.meta.resolve(dep) :
|
||||
require.resolve(dep, { paths: [config.PROJECT_ROOT] });
|
||||
return true;
|
||||
} catch {
|
||||
return packageJson.dependencies && packageJson.dependencies[dep];
|
||||
}
|
||||
});
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Run integration tests if this file is executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
runner.runAll().catch(console.error);
|
||||
}
|
||||
|
||||
export default runner;
|
||||
Reference in New Issue
Block a user