/** * Minimal test runner — describe/it with timing and summary. * No external dependencies. Import this from test files. */ const suites = []; let currentSuite = null; let totalTests = 0; let totalPassed = 0; let totalFailed = 0; const failures = []; export function describe(name, fn) { currentSuite = { name, tests: [], passed: 0, failed: 0, duration: 0 }; suites.push(currentSuite); fn(); currentSuite = null; } export function it(name, fn) { if (!currentSuite) { throw new Error(`it() called outside describe(). Test: "${name}"`); } currentSuite.tests.push({ name, fn, async: typeof fn === 'function' }); } // Run all suites and print results export async function run() { let globalStart = Date.now(); for (const suite of suites) { const suiteStart = Date.now(); process.stdout.write(`\n${suite.name}\n${'─'.repeat(suite.name.length + 2)}\n`); for (const test of suite.tests) { totalTests++; const testStart = Date.now(); try { const result = test.fn(); if (result instanceof Promise) { await result; } const ms = Date.now() - testStart; totalPassed++; suite.passed++; process.stdout.write(` ✓ ${test.name} (${ms}ms)\n`); } catch (err) { const ms = Date.now() - testStart; totalFailed++; suite.failed++; failures.push({ suite: suite.name, test: test.name, error: err.message, stack: err.stack }); process.stdout.write(` ✗ ${test.name} (${ms}ms)\n`); process.stdout.write(` → ${err.message}\n`); } } suite.duration = Date.now() - suiteStart; } const totalMs = Date.now() - globalStart; process.stdout.write(`\n${'='.repeat(60)}\n`); process.stdout.write(`Results: ${totalPassed}/${totalTests} passed`); if (totalFailed > 0) { process.stdout.write(` | ${totalFailed} FAILED`); } process.stdout.write(` | ${totalMs}ms\n`); process.stdout.write(`${'='.repeat(60)}\n`); if (failures.length > 0) { process.stdout.write(`\nFailures:\n`); for (const f of failures) { process.stdout.write(` [${f.suite}] ${f.test}\n`); process.stdout.write(` Error: ${f.error}\n`); process.stdout.write(` ${f.stack.split('\n')[1].trim()}\n`); } } return { totalTests, totalPassed, totalFailed, totalMs, failures }; }