304 lines
7.7 KiB
JavaScript
304 lines
7.7 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Comprehensive test suite for @soroban/templates package
|
|
* Tests Node.js/TypeScript interface and file integrity
|
|
*/
|
|
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const {
|
|
FLASHCARDS_TEMPLATE,
|
|
SINGLE_CARD_TEMPLATE,
|
|
getTemplatePath,
|
|
} = require("./index.js");
|
|
|
|
// Test configuration
|
|
const TESTS = [];
|
|
let testCount = 0;
|
|
let passedTests = 0;
|
|
|
|
function test(name, fn) {
|
|
TESTS.push({ name, fn });
|
|
}
|
|
|
|
function assert(condition, message) {
|
|
testCount++;
|
|
if (condition) {
|
|
passedTests++;
|
|
console.log(" ✅", message);
|
|
} else {
|
|
console.log(" ❌", message);
|
|
throw new Error(`Assertion failed: ${message}`);
|
|
}
|
|
}
|
|
|
|
function assertEqual(actual, expected, message) {
|
|
assert(
|
|
actual === expected,
|
|
`${message} (expected: ${expected}, got: ${actual})`,
|
|
);
|
|
}
|
|
|
|
function assertContains(content, substring, message) {
|
|
assert(
|
|
content.includes(substring),
|
|
`${message} (should contain: ${substring})`,
|
|
);
|
|
}
|
|
|
|
function runTests() {
|
|
console.log("🧮 @soroban/templates - Node.js Test Suite");
|
|
console.log("==========================================\n");
|
|
|
|
for (const { name, fn } of TESTS) {
|
|
console.log(`🔍 ${name}`);
|
|
try {
|
|
fn();
|
|
console.log("");
|
|
} catch (error) {
|
|
console.log(` 💥 Test failed: ${error.message}\n`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
console.log(`🎉 All tests passed! (${passedTests}/${testCount})`);
|
|
}
|
|
|
|
// Test: Package Structure
|
|
test("Package Structure Validation", () => {
|
|
assert(
|
|
typeof FLASHCARDS_TEMPLATE === "string",
|
|
"FLASHCARDS_TEMPLATE should be a string",
|
|
);
|
|
assert(
|
|
typeof SINGLE_CARD_TEMPLATE === "string",
|
|
"SINGLE_CARD_TEMPLATE should be a string",
|
|
);
|
|
assert(
|
|
typeof getTemplatePath === "function",
|
|
"getTemplatePath should be a function",
|
|
);
|
|
|
|
assert(
|
|
FLASHCARDS_TEMPLATE.endsWith("flashcards.typ"),
|
|
"FLASHCARDS_TEMPLATE should end with flashcards.typ",
|
|
);
|
|
assert(
|
|
SINGLE_CARD_TEMPLATE.endsWith("single-card.typ"),
|
|
"SINGLE_CARD_TEMPLATE should end with single-card.typ",
|
|
);
|
|
});
|
|
|
|
// Test: File Existence
|
|
test("Template File Existence", () => {
|
|
assert(
|
|
fs.existsSync(FLASHCARDS_TEMPLATE),
|
|
"flashcards.typ file should exist",
|
|
);
|
|
assert(
|
|
fs.existsSync(SINGLE_CARD_TEMPLATE),
|
|
"single-card.typ file should exist",
|
|
);
|
|
|
|
const flashcardsStats = fs.statSync(FLASHCARDS_TEMPLATE);
|
|
const singleCardStats = fs.statSync(SINGLE_CARD_TEMPLATE);
|
|
|
|
assert(flashcardsStats.isFile(), "flashcards.typ should be a file");
|
|
assert(singleCardStats.isFile(), "single-card.typ should be a file");
|
|
assert(
|
|
flashcardsStats.size > 1000,
|
|
"flashcards.typ should be substantial (>1KB)",
|
|
);
|
|
assert(
|
|
singleCardStats.size > 1000,
|
|
"single-card.typ should be substantial (>1KB)",
|
|
);
|
|
});
|
|
|
|
// Test: Template Content Validation
|
|
test("Template Content Validation", () => {
|
|
const flashcardsContent = fs.readFileSync(FLASHCARDS_TEMPLATE, "utf-8");
|
|
const singleCardContent = fs.readFileSync(SINGLE_CARD_TEMPLATE, "utf-8");
|
|
|
|
assertContains(
|
|
flashcardsContent,
|
|
"draw-soroban",
|
|
"flashcards.typ should contain draw-soroban function",
|
|
);
|
|
assertContains(
|
|
singleCardContent,
|
|
"generate-single-card",
|
|
"single-card.typ should contain generate-single-card function",
|
|
);
|
|
|
|
// Check for common Typst syntax
|
|
assertContains(
|
|
flashcardsContent,
|
|
"#let",
|
|
"flashcards.typ should use Typst function syntax",
|
|
);
|
|
assertContains(
|
|
singleCardContent,
|
|
"#let",
|
|
"single-card.typ should use Typst function syntax",
|
|
);
|
|
|
|
// Check for soroban-specific content
|
|
assertContains(
|
|
flashcardsContent,
|
|
"bead",
|
|
"flashcards.typ should reference beads",
|
|
);
|
|
assertContains(
|
|
flashcardsContent,
|
|
"column",
|
|
"flashcards.typ should reference columns",
|
|
);
|
|
});
|
|
|
|
// Test: Dynamic Path Resolution
|
|
test("Dynamic Path Resolution", () => {
|
|
const dynamicFlashcards = getTemplatePath("flashcards.typ");
|
|
const dynamicSingleCard = getTemplatePath("single-card.typ");
|
|
|
|
assertEqual(
|
|
dynamicFlashcards,
|
|
FLASHCARDS_TEMPLATE,
|
|
"getTemplatePath should resolve flashcards.typ correctly",
|
|
);
|
|
assertEqual(
|
|
dynamicSingleCard,
|
|
SINGLE_CARD_TEMPLATE,
|
|
"getTemplatePath should resolve single-card.typ correctly",
|
|
);
|
|
|
|
// Test error handling
|
|
try {
|
|
getTemplatePath("nonexistent.typ");
|
|
assert(false, "getTemplatePath should throw for nonexistent files");
|
|
} catch (error) {
|
|
assert(
|
|
error.message.includes("not found"),
|
|
"getTemplatePath should provide meaningful error messages",
|
|
);
|
|
}
|
|
});
|
|
|
|
// Test: Path Properties
|
|
test("Path Properties and Security", () => {
|
|
// Ensure paths are absolute
|
|
assert(
|
|
path.isAbsolute(FLASHCARDS_TEMPLATE),
|
|
"FLASHCARDS_TEMPLATE should be absolute path",
|
|
);
|
|
assert(
|
|
path.isAbsolute(SINGLE_CARD_TEMPLATE),
|
|
"SINGLE_CARD_TEMPLATE should be absolute path",
|
|
);
|
|
|
|
// Ensure paths point to correct directory
|
|
const templatesDir = path.dirname(FLASHCARDS_TEMPLATE);
|
|
const expectedTemplatesDir = path.dirname(SINGLE_CARD_TEMPLATE);
|
|
assertEqual(
|
|
templatesDir,
|
|
expectedTemplatesDir,
|
|
"Both templates should be in same directory",
|
|
);
|
|
assert(
|
|
templatesDir.includes("templates"),
|
|
"Templates should be in templates directory",
|
|
);
|
|
});
|
|
|
|
// Test: Multiple Import Styles
|
|
test("Import Compatibility", () => {
|
|
// Test CommonJS require
|
|
const cjsImport = require("./index.js");
|
|
assert(
|
|
typeof cjsImport.FLASHCARDS_TEMPLATE === "string",
|
|
"CommonJS import should work",
|
|
);
|
|
|
|
// Test destructuring
|
|
const { FLASHCARDS_TEMPLATE: destructured } = require("./index.js");
|
|
assertEqual(
|
|
destructured,
|
|
FLASHCARDS_TEMPLATE,
|
|
"Destructuring import should work",
|
|
);
|
|
});
|
|
|
|
// Test: File Permissions and Readability
|
|
test("File Access and Permissions", () => {
|
|
try {
|
|
fs.accessSync(FLASHCARDS_TEMPLATE, fs.constants.R_OK);
|
|
assert(true, "flashcards.typ should be readable");
|
|
} catch (error) {
|
|
assert(false, "flashcards.typ should have read permissions");
|
|
}
|
|
|
|
try {
|
|
fs.accessSync(SINGLE_CARD_TEMPLATE, fs.constants.R_OK);
|
|
assert(true, "single-card.typ should be readable");
|
|
} catch (error) {
|
|
assert(false, "single-card.typ should have read permissions");
|
|
}
|
|
});
|
|
|
|
// Test: Template Encoding
|
|
test("Template Encoding and Character Set", () => {
|
|
const flashcardsContent = fs.readFileSync(FLASHCARDS_TEMPLATE, "utf-8");
|
|
const singleCardContent = fs.readFileSync(SINGLE_CARD_TEMPLATE, "utf-8");
|
|
|
|
// Test for valid UTF-8 encoding
|
|
assert(
|
|
typeof flashcardsContent === "string",
|
|
"flashcards.typ should decode as valid UTF-8",
|
|
);
|
|
assert(
|
|
typeof singleCardContent === "string",
|
|
"single-card.typ should decode as valid UTF-8",
|
|
);
|
|
|
|
// Test for reasonable line count
|
|
const flashcardsLines = flashcardsContent.split("\n").length;
|
|
const singleCardLines = singleCardContent.split("\n").length;
|
|
|
|
assert(
|
|
flashcardsLines > 10,
|
|
"flashcards.typ should have substantial content (>10 lines)",
|
|
);
|
|
assert(
|
|
singleCardLines > 10,
|
|
"single-card.typ should have substantial content (>10 lines)",
|
|
);
|
|
});
|
|
|
|
// Test: Package Metadata Consistency
|
|
test("Package Metadata Consistency", () => {
|
|
const packageJson = require("./package.json");
|
|
|
|
assertEqual(
|
|
packageJson.name,
|
|
"@soroban/templates",
|
|
"Package name should be correct",
|
|
);
|
|
assertEqual(packageJson.main, "index.js", "Main entry should be index.js");
|
|
assertEqual(
|
|
packageJson.types,
|
|
"index.d.ts",
|
|
"Types entry should be index.d.ts",
|
|
);
|
|
|
|
// Check that declared files exist
|
|
const declaredFiles = packageJson.files || [];
|
|
const templateFiles = declaredFiles.filter(
|
|
(f) => f.includes(".typ") || f === "*.typ",
|
|
);
|
|
assert(templateFiles.length > 0, "Package should declare template files");
|
|
});
|
|
|
|
// Run all tests
|
|
runTests();
|