CI / test (pull_request) Canceled after 0s
- viewer.js: dynamic hls.js loading, HLS-aware panel creation, error handling - schema.json: hlsManifest and mediaType fields (optional, backward compatible) - types.ts: VideoEntry interface with MediaType type - isHls() null guard fix - tests: 39 tests (schema, viewer units, data flow, integration) - CI: GitHub/Gitea Actions workflow (npm test on push/PR) - deps: ajv, ajv-formats, jsdom for testing
81 lines
2.3 KiB
JavaScript
81 lines
2.3 KiB
JavaScript
/**
|
|
* 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 };
|
|
}
|