Files
Pake/tests/integration.test.js
2025-08-14 11:20:15 +08:00

241 lines
6.0 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;