soroban-abacus-flashcards/packages/templates/test.js

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();