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

313 lines
8.5 KiB
JavaScript

#!/usr/bin/env node
/**
* Builder-specific Tests for Pake CLI
*
* These tests verify platform-specific builder logic and file naming patterns
* Based on analysis of bin/builders/ implementation
*/
import { execSync } from "child_process";
import path from "path";
import config, { TEST_URLS, TEST_NAMES } from "./test.config.js";
import ora from "ora";
class BuilderTestRunner {
constructor() {
this.tests = [];
this.results = [];
}
addTest(name, testFn, description = "") {
this.tests.push({ name, testFn, description });
}
async runAll() {
console.log("🏗️ Builder-specific Tests");
console.log("==========================\n");
for (const [index, test] of this.tests.entries()) {
const spinner = ora(`Running ${test.name}...`).start();
try {
const result = await test.testFn();
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.displayResults();
}
displayResults() {
const passed = this.results.filter((r) => r.passed).length;
const total = this.results.length;
console.log(`\n📊 Builder Test Results: ${passed}/${total} passed\n`);
if (passed === total) {
console.log("🎉 All builder tests passed!");
} else {
console.log("❌ Some builder tests failed");
this.results
.filter((r) => !r.passed)
.forEach((result) => {
console.log(
` - ${result.name}${result.error ? `: ${result.error}` : ""}`,
);
});
}
}
}
const runner = new BuilderTestRunner();
// Platform-specific file naming tests
runner.addTest(
"Mac Builder File Naming Pattern",
() => {
try {
// Test macOS file naming: name_version_arch.dmg
const mockName = "TestApp";
const mockVersion = "1.0.0";
const arch = process.arch === "arm64" ? "aarch64" : process.arch;
// Expected pattern: TestApp_1.0.0_aarch64.dmg (for M1) or TestApp_1.0.0_x64.dmg (for Intel)
const expectedPattern = `${mockName}_${mockVersion}_${arch}`;
const universalPattern = `${mockName}_${mockVersion}_universal`;
// Test that naming pattern is consistent
return (
expectedPattern.includes(mockName) &&
expectedPattern.includes(mockVersion) &&
(expectedPattern.includes(arch) ||
universalPattern.includes("universal"))
);
} catch (error) {
return false;
}
},
"Should generate correct macOS file naming pattern",
);
runner.addTest(
"Windows Builder File Naming Pattern",
() => {
try {
// Test Windows file naming: name_version_arch_language.msi
const mockName = "TestApp";
const mockVersion = "1.0.0";
const arch = process.arch;
const language = "en-US"; // default language
// Expected pattern: TestApp_1.0.0_x64_en-US.msi
const expectedPattern = `${mockName}_${mockVersion}_${arch}_${language}`;
return (
expectedPattern.includes(mockName) &&
expectedPattern.includes(mockVersion) &&
expectedPattern.includes(arch) &&
expectedPattern.includes(language)
);
} catch (error) {
return false;
}
},
"Should generate correct Windows file naming pattern",
);
runner.addTest(
"Linux Builder File Naming Pattern",
() => {
try {
// Test Linux file naming variations
const mockName = "testapp";
const mockVersion = "1.0.0";
let arch = process.arch === "x64" ? "amd64" : process.arch;
// Test different target formats
const debPattern = `${mockName}_${mockVersion}_${arch}`; // .deb
const rpmPattern = `${mockName}-${mockVersion}-1.${arch === "arm64" ? "aarch64" : arch}`; // .rpm
const appImagePattern = `${mockName}_${mockVersion}_${arch === "arm64" ? "aarch64" : arch}`; // .AppImage
return (
debPattern.includes(mockName) &&
rpmPattern.includes(mockName) &&
appImagePattern.includes(mockName)
);
} catch (error) {
return false;
}
},
"Should generate correct Linux file naming patterns for different targets",
);
runner.addTest(
"Architecture Detection Logic",
() => {
try {
// Test architecture mapping logic
const currentArch = process.arch;
// Mac: arm64 -> aarch64, others keep same
const macArch = currentArch === "arm64" ? "aarch64" : currentArch;
// Linux: x64 -> amd64 for deb, arm64 -> aarch64 for rpm/appimage
const linuxArch = currentArch === "x64" ? "amd64" : currentArch;
// Windows: keeps process.arch as-is
const winArch = currentArch;
return (
typeof macArch === "string" &&
typeof linuxArch === "string" &&
typeof winArch === "string"
);
} catch (error) {
return false;
}
},
"Should correctly detect and map system architecture",
);
runner.addTest(
"Multi-arch Build Detection",
() => {
try {
// Test universal binary logic for macOS
const platform = process.platform;
if (platform === "darwin") {
// macOS should support multi-arch with --multi-arch flag
const supportsMultiArch = true;
const universalSuffix = "universal";
return supportsMultiArch && universalSuffix === "universal";
} else {
// Other platforms don't support multi-arch
return true;
}
} catch (error) {
return false;
}
},
"Should handle multi-architecture builds correctly",
);
runner.addTest(
"Target Format Validation",
() => {
try {
// Test valid target formats per platform
const platform = process.platform;
const validTargets = {
darwin: ["dmg"],
win32: ["msi"],
linux: ["deb", "appimage", "rpm"],
};
const platformTargets = validTargets[platform];
return Array.isArray(platformTargets) && platformTargets.length > 0;
} catch (error) {
return false;
}
},
"Should validate target formats per platform",
);
runner.addTest(
"Build Path Generation",
() => {
try {
// Test build path logic: debug vs release
const debugPath = "src-tauri/target/debug/bundle/";
const releasePath = "src-tauri/target/release/bundle/";
const universalPath =
"src-tauri/target/universal-apple-darwin/release/bundle";
// Paths should be consistent
return (
debugPath.includes("debug") &&
releasePath.includes("release") &&
universalPath.includes("universal")
);
} catch (error) {
return false;
}
},
"Should generate correct build paths for different modes",
);
runner.addTest(
"File Extension Mapping",
() => {
try {
// Test file extension mapping logic
const platform = process.platform;
const extensionMap = {
darwin: "dmg",
win32: "msi",
linux: "deb", // default, can be appimage or rpm
};
const expectedExt = extensionMap[platform];
// Special case for Linux AppImage (capital A)
const appImageExt = "AppImage";
return (
typeof expectedExt === "string" &&
(expectedExt.length > 0 || appImageExt === "AppImage")
);
} catch (error) {
return false;
}
},
"Should map file extensions correctly per platform",
);
runner.addTest(
"Name Sanitization Logic",
() => {
try {
// Test name sanitization for file systems
const testNames = [
"Simple App", // Should handle spaces
"App-With_Symbols", // Should handle hyphens and underscores
"CamelCaseApp", // Should handle case variations
"App123", // Should handle numbers
];
// Test that names can be processed (basic validation)
return testNames.every((name) => {
const processed = name.toLowerCase().replace(/\s+/g, "");
return processed.length > 0;
});
} catch (error) {
return false;
}
},
"Should sanitize app names for filesystem compatibility",
);
// Run builder tests if this file is executed directly
if (import.meta.url === `file://${process.argv[1]}`) {
runner.runAll().catch(console.error);
}
export default runner;