239 lines
6.1 KiB
JavaScript
239 lines
6.1 KiB
JavaScript
#!/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; |