MIRROR: javascript for 🐜's, a tiny runtime with big ambitions
1import fs from 'ant:fs';
2import path from 'ant:path';
3
4const GREEN = '\x1b[32m';
5const RED = '\x1b[31m';
6const CYAN = '\x1b[36m';
7const BOLD = '\x1b[1m';
8const DIM = '\x1b[2m';
9const RESET = '\x1b[0m';
10
11const specDir = path.dirname(import.meta.url.replace('file://', ''));
12const nonSpecFiles = new Set(['run.js', 'helpers.js', 'import_abs_target.js']);
13
14const allFiles = fs
15 .readdirSync(specDir)
16 .filter(f => f.endsWith('.js') && !nonSpecFiles.has(f))
17 .sort();
18
19const cliArgs = process.argv.slice(2);
20const runAll = cliArgs.includes('--all');
21const requestedSpecs = cliArgs.filter(arg => arg !== '--all');
22
23function normalizeSpecName(arg) {
24 const base = path.basename(arg);
25 return base.endsWith('.js') ? base.slice(0, -3) : base;
26}
27
28const fileByName = new Map(allFiles.map(file => [path.basename(file, '.js'), file]));
29const files = (() => {
30 if (!runAll && requestedSpecs.length === 0) {
31 console.log(`${RED}No specs selected.${RESET} Use ${BOLD}--all${RESET} or pass one or more spec names.`);
32 process.exit(1);
33 }
34 if (runAll) return allFiles;
35
36 const selected = [];
37 const seen = new Set();
38 const missing = [];
39
40 for (const raw of requestedSpecs) {
41 const name = normalizeSpecName(raw);
42 const file = fileByName.get(name);
43
44 if (!file) {
45 missing.push(raw);
46 continue;
47 }
48
49 if (seen.has(file)) continue;
50 seen.add(file);
51 selected.push(file);
52 }
53
54 if (missing.length > 0) console.log(`${RED}Unknown spec file(s):${RESET} ${missing.join(', ')}`);
55 if (selected.length === 0) process.exit(1);
56
57 return selected;
58})();
59
60let totalPassed = 0;
61let totalFailed = 0;
62
63let filesPassed = 0;
64let filesFailed = 0;
65
66const fileTimes = [];
67const failedTests = [];
68
69console.log(`\n${BOLD}${CYAN}Running ${files.length} spec files...${RESET}\n`);
70const totalStart = performance.now();
71
72async function runInProcessMode() {
73 const hook = {
74 current: null,
75 onSummary: null
76 };
77 globalThis.__ANT_SPEC_HOOK__ = hook;
78
79 for (let i = 0; i < files.length; i++) {
80 const file = files[i];
81 const name = path.basename(file, '.js');
82 const fileUrl = `./${file}`;
83 const start = performance.now();
84
85 hook.current = { passed: 0, failed: 0, failures: [] };
86 let summaryResolve;
87 const summaryP = new Promise(resolve => {
88 summaryResolve = resolve;
89 });
90 hook.onSummary = summaryResolve;
91
92 try {
93 await import(fileUrl);
94 const result = await summaryP;
95 const elapsed = performance.now() - start;
96
97 totalPassed += result.passed;
98 totalFailed += result.failed;
99 if (result.failures.length > 0) {
100 failedTests.push({ name, failures: result.failures });
101 }
102
103 if (result.failed === 0) {
104 console.log(`${GREEN}✓${RESET} ${name} ${DIM}(${elapsed.toFixed(0)}ms)${RESET}\n`);
105 filesPassed++;
106 } else {
107 console.log(`${RED}✗${RESET} ${name} ${DIM}(${elapsed.toFixed(0)}ms)${RESET}\n`);
108 filesFailed++;
109 }
110 fileTimes.push({ name, elapsed });
111 } catch (e) {
112 const elapsed = performance.now() - start;
113 console.log(`${RED}✗${RESET} ${name} ${DIM}(error, ${elapsed.toFixed(0)}ms)${RESET}\n`);
114 if (e && e.stack) console.log(String(e.stack));
115 else console.log(String(e));
116 filesFailed++;
117 totalFailed++;
118 failedTests.push({ name, failures: [`${RED}✗${RESET} import/exec error`] });
119 fileTimes.push({ name, elapsed });
120 } finally {
121 hook.onSummary = null;
122 hook.current = null;
123 }
124 }
125}
126
127await runInProcessMode();
128const totalElapsed = performance.now() - totalStart;
129
130console.log(`\n${BOLD}Results:${RESET}`);
131console.log(` ${GREEN}${totalPassed} tests passed${RESET}`);
132console.log(` ${RED}${totalFailed} tests failed${RESET}`);
133console.log(` ${GREEN}${filesPassed} files passed${RESET}`);
134console.log(` ${RED}${filesFailed} files failed${RESET}`);
135
136console.log(`\n${BOLD}Timing:${RESET}`);
137for (const { name, elapsed } of fileTimes) console.log(` ${DIM}${name.padEnd(30)}${RESET} ${elapsed.toFixed(0)}ms`);
138console.log(`\n ${BOLD}Total${RESET}${' '.padEnd(26)}${totalElapsed.toFixed(0)}ms\n`);
139
140if (failedTests.length > 0) {
141 console.log(`${BOLD}${RED}Failed Tests:${RESET}`);
142 for (const { name, failures } of failedTests) {
143 console.log(`\n ${BOLD}${name}${RESET}`);
144 for (const line of failures) console.log(` ${line.trim()}`);
145 }
146 console.log();
147}
148
149process.exit(totalFailed > 0 ? 1 : 0);