MIRROR: javascript for 🐜's, a tiny runtime with big ambitions
1const GREEN = '\x1b[32m';
2const RED = '\x1b[31m';
3const RESET = '\x1b[0m';
4
5let passed = 0;
6let failed = 0;
7let failures = [];
8
9function specHook() {
10 return globalThis.__ANT_SPEC_HOOK__ || null;
11}
12
13function specState() {
14 const hook = specHook();
15 if (hook && hook.current) return hook.current;
16 return null;
17}
18
19function incPassed() {
20 const s = specState();
21 if (s) s.passed++;
22 else passed++;
23}
24
25function incFailed(name) {
26 const s = specState();
27 if (s) {
28 s.failed++;
29 if (name) s.failures.push(`${RED}✗${RESET} ${name}`);
30 } else {
31 failed++;
32 if (name) failures.push(`${RED}✗${RESET} ${name}`);
33 }
34}
35
36export function test(name, actual, expected) {
37 if (actual === expected) {
38 console.log(`${GREEN}✓${RESET} ${name}`);
39 incPassed();
40 } else {
41 console.log(`${RED}✗${RESET} ${name}`);
42 console.log(` Expected: ${expected}`);
43 console.log(` Actual: ${actual}`);
44 incFailed(name);
45 }
46}
47
48export function testApprox(name, actual, expected, epsilon = 1e-10) {
49 const pass =
50 (Number.isNaN(actual) && Number.isNaN(expected)) ||
51 (!Number.isFinite(actual) && !Number.isFinite(expected) && actual === expected) ||
52 Math.abs(actual - expected) < epsilon;
53 if (pass) {
54 console.log(`${GREEN}✓${RESET} ${name}`);
55 incPassed();
56 } else {
57 console.log(`${RED}✗${RESET} ${name}`);
58 console.log(` Expected: ${expected}`);
59 console.log(` Actual: ${actual}`);
60 incFailed(name);
61 }
62}
63
64export function testThrows(name, fn) {
65 try {
66 fn();
67 console.log(`${RED}✗${RESET} ${name} (expected to throw)`);
68 incFailed(`${name} (expected to throw)`);
69 } catch (e) {
70 console.log(`${GREEN}✓${RESET} ${name}`);
71 incPassed();
72 }
73}
74
75export function testDeep(name, actual, expected) {
76 const pass = JSON.stringify(actual) === JSON.stringify(expected);
77 if (pass) {
78 console.log(`${GREEN}✓${RESET} ${name}`);
79 incPassed();
80 } else {
81 console.log(`${RED}✗${RESET} ${name}`);
82 console.log(` Expected: ${JSON.stringify(expected)}`);
83 console.log(` Actual: ${JSON.stringify(actual)}`);
84 incFailed(name);
85 }
86}
87
88export function summary() {
89 const s = specState();
90 const p = s ? s.passed : passed;
91 const f = s ? s.failed : failed;
92
93 console.log(`\nPassed: ${p}`);
94 console.log(`Failed: ${f}`);
95
96 const hook = specHook();
97 if (hook && hook.current && typeof hook.onSummary === 'function') {
98 hook.onSummary({
99 passed: p,
100 failed: f,
101 failures: hook.current.failures.slice()
102 });
103 return;
104 }
105
106 if (f > 0) process.exit(1);
107}