@vibe2founder/tests2dialects 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +73 -0
- package/bun.lock +22 -0
- package/bunfig.toml +2 -0
- package/critica.md +77 -0
- package/docs/4-ideias.md +66 -0
- package/docs/api-api.md +93 -0
- package/docs/api-imperativo.md +125 -0
- package/docs/api-matematico.md +145 -0
- package/docs/api-narrativo.md +181 -0
- package/docs/guia-rapido.md +189 -0
- package/docs/whitepaper.md +21 -0
- package/examples/imperative.spec.ts +58 -0
- package/examples/math.spec.ts +52 -0
- package/examples/narrative.spec.ts +61 -0
- package/examples/polyglot-shopping-cart.spec.ts +212 -0
- package/examples/sanity.spec.ts +54 -0
- package/examples/showcase-api.spec.ts +70 -0
- package/examples/test-api.ts +36 -0
- package/infograficos/detalhado.png +0 -0
- package/infograficos/mobile.png +0 -0
- package/infograficos/normal.png +0 -0
- package/landing-page/README.md +38 -0
- package/landing-page/bun.lock +609 -0
- package/landing-page/eslint.config.js +23 -0
- package/landing-page/index.html +17 -0
- package/landing-page/package-lock.json +2962 -0
- package/landing-page/package.json +34 -0
- package/landing-page/postcss.config.js +6 -0
- package/landing-page/public/vite.svg +1 -0
- package/landing-page/src/App.tsx +358 -0
- package/landing-page/src/assets/react.svg +1 -0
- package/landing-page/src/index.css +34 -0
- package/landing-page/src/main.tsx +10 -0
- package/landing-page/tailwind.config.js +59 -0
- package/landing-page/tsconfig.app.json +28 -0
- package/landing-page/tsconfig.json +7 -0
- package/landing-page/tsconfig.node.json +26 -0
- package/landing-page/vite.config.ts +7 -0
- package/logo.png +0 -0
- package/output.log +60 -0
- package/package.json +36 -0
- package/packages/api-test-dialect/README.md +30 -0
- package/packages/api-test-dialect/index.ts +132 -0
- package/packages/api-test-dialect/openapi.json +64 -0
- package/packages/reqify/README.md +33 -0
- package/packages/reqify/index.ts +48 -0
- package/podcast/O_Matem/303/241tico,_o_Narrador_e_o_Engenheiro.json +0 -0
- package/podcast/O_Matem/303/241tico,_o_Narrador_e_o_Engenheiro.md +0 -0
- package/podcast/critica-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.json +0 -0
- package/podcast/critica-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.md +0 -0
- package/podcast/critica-Unificar_filosofia_e_pr/303/241tica_na_documenta/303/247/303/243o_(7_words__covers_t.md +1 -0
- package/podcast/critica-Unificar_filosofia_e_pr/303/241tica_na_documenta/303/247/303/243o__7_words__covers_t.ogg +0 -0
- package/podcast/critica2-Sil/303/252ncio_estrat/303/251gico_e_sobrecarga_em_READMEs.ogg +0 -0
- package/podcast/critica2.json +3191 -0
- package/podcast/critica2.md +1 -0
- package/podcast/debate-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.json +0 -0
- package/podcast/debate-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.md +0 -0
- package/readme.md +58 -0
- package/reports/01-01-2026_00-45.md +40 -0
- package/reports/01-01-2026_02-30.md +37 -0
- package/reports/03-02-2026_10-55.md +8 -0
- package/reports/03-02-2026_11-45.md +13 -0
- package/reports/03-02-2026_11-50.md +10 -0
- package/reports/26-01-2026_16-25.md +31 -0
- package/reports/26-01-2026_19-20.md +27 -0
- package/reports/31-12-2025_22-35.md +25 -0
- package/reports/31-12-2025_22-45.md +15 -0
- package/slides/Dialetos_de_Teste_Um_Executor_M/303/272ltiplos_Vocabul/303/241rios.pdf +0 -0
- package/src/cli.ts +445 -0
- package/src/index.ts +539 -0
- package/tabela.html +350 -0
- package/tsconfig.json +22 -0
- package/types/api-types.ts +11 -0
- package/www/index.html +1344 -0
package/src/cli.ts
ADDED
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* π§ͺ tests2dialects Test Runner
|
|
4
|
+
* Visual estilo Vitest com cronΓ΄metro e cores
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { readdir, stat } from "fs/promises";
|
|
8
|
+
import { join, relative, basename, dirname } from "path";
|
|
9
|
+
import { resetAtomicCore } from "./index.js";
|
|
10
|
+
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// π¨ ANSI Colors (Vitest-style palette)
|
|
13
|
+
// =============================================================================
|
|
14
|
+
|
|
15
|
+
const c = {
|
|
16
|
+
// Reset
|
|
17
|
+
reset: "\x1b[0m",
|
|
18
|
+
|
|
19
|
+
// Text styles
|
|
20
|
+
bold: "\x1b[1m",
|
|
21
|
+
dim: "\x1b[2m",
|
|
22
|
+
italic: "\x1b[3m",
|
|
23
|
+
underline: "\x1b[4m",
|
|
24
|
+
|
|
25
|
+
// Colors
|
|
26
|
+
black: "\x1b[30m",
|
|
27
|
+
red: "\x1b[31m",
|
|
28
|
+
green: "\x1b[32m",
|
|
29
|
+
yellow: "\x1b[33m",
|
|
30
|
+
blue: "\x1b[34m",
|
|
31
|
+
magenta: "\x1b[35m",
|
|
32
|
+
cyan: "\x1b[36m",
|
|
33
|
+
white: "\x1b[37m",
|
|
34
|
+
gray: "\x1b[90m",
|
|
35
|
+
|
|
36
|
+
// Bright colors
|
|
37
|
+
brightRed: "\x1b[91m",
|
|
38
|
+
brightGreen: "\x1b[92m",
|
|
39
|
+
brightYellow: "\x1b[93m",
|
|
40
|
+
brightBlue: "\x1b[94m",
|
|
41
|
+
brightMagenta: "\x1b[95m",
|
|
42
|
+
brightCyan: "\x1b[96m",
|
|
43
|
+
|
|
44
|
+
// Background
|
|
45
|
+
bgGreen: "\x1b[42m",
|
|
46
|
+
bgRed: "\x1b[41m",
|
|
47
|
+
bgYellow: "\x1b[43m",
|
|
48
|
+
bgBlue: "\x1b[44m",
|
|
49
|
+
bgMagenta: "\x1b[45m",
|
|
50
|
+
bgCyan: "\x1b[46m",
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Vitest-style colors
|
|
54
|
+
const pass = (s: string) => `${c.bold}${c.green}${s}${c.reset}`;
|
|
55
|
+
const fail = (s: string) => `${c.bold}${c.red}${s}${c.reset}`;
|
|
56
|
+
const skip = (s: string) => `${c.yellow}${s}${c.reset}`;
|
|
57
|
+
const dim = (s: string) => `${c.dim}${s}${c.reset}`;
|
|
58
|
+
const bold = (s: string) => `${c.bold}${s}${c.reset}`;
|
|
59
|
+
const cyan = (s: string) => `${c.cyan}${s}${c.reset}`;
|
|
60
|
+
const magenta = (s: string) => `${c.magenta}${s}${c.reset}`;
|
|
61
|
+
const yellow = (s: string) => `${c.yellow}${s}${c.reset}`;
|
|
62
|
+
const gray = (s: string) => `${c.gray}${s}${c.reset}`;
|
|
63
|
+
const brightCyan = (s: string) => `${c.brightCyan}${s}${c.reset}`;
|
|
64
|
+
|
|
65
|
+
// =============================================================================
|
|
66
|
+
// β±οΈ Timer & Formatting
|
|
67
|
+
// =============================================================================
|
|
68
|
+
|
|
69
|
+
function formatDuration(ms: number): string {
|
|
70
|
+
if (ms < 1) return `${(ms * 1000).toFixed(0)}ΞΌs`;
|
|
71
|
+
if (ms < 1000) return `${ms.toFixed(0)}ms`;
|
|
72
|
+
if (ms < 60000) return `${(ms / 1000).toFixed(2)}s`;
|
|
73
|
+
const mins = Math.floor(ms / 60000);
|
|
74
|
+
const secs = ((ms % 60000) / 1000).toFixed(1);
|
|
75
|
+
return `${mins}m ${secs}s`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getSpinner(): string[] {
|
|
79
|
+
return ["β ", "β ", "β Ή", "β Έ", "β Ό", "β ΄", "β ¦", "β §", "β ", "β "];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// =============================================================================
|
|
83
|
+
// π File Scanner
|
|
84
|
+
// =============================================================================
|
|
85
|
+
|
|
86
|
+
async function findSpecFiles(
|
|
87
|
+
dir: string,
|
|
88
|
+
fileList: string[] = [],
|
|
89
|
+
): Promise<string[]> {
|
|
90
|
+
const files = await readdir(dir);
|
|
91
|
+
|
|
92
|
+
for (const file of files) {
|
|
93
|
+
if (file === "node_modules" || file === "dist" || file === ".git") continue;
|
|
94
|
+
|
|
95
|
+
const path = join(dir, file);
|
|
96
|
+
try {
|
|
97
|
+
const s = await stat(path);
|
|
98
|
+
if (s.isDirectory()) {
|
|
99
|
+
await findSpecFiles(path, fileList);
|
|
100
|
+
} else if (file.endsWith(".spec.ts")) {
|
|
101
|
+
fileList.push(path);
|
|
102
|
+
}
|
|
103
|
+
} catch {
|
|
104
|
+
// Ignore access errors
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return fileList;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// =============================================================================
|
|
112
|
+
// π§ͺ Test Results Storage
|
|
113
|
+
// =============================================================================
|
|
114
|
+
|
|
115
|
+
interface TestResult {
|
|
116
|
+
file: string;
|
|
117
|
+
duration: number;
|
|
118
|
+
passed: number;
|
|
119
|
+
failed: number;
|
|
120
|
+
skipped: number;
|
|
121
|
+
tests: {
|
|
122
|
+
name: string;
|
|
123
|
+
status: "pass" | "fail" | "skip";
|
|
124
|
+
duration: number;
|
|
125
|
+
error?: string;
|
|
126
|
+
}[];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const results: TestResult[] = [];
|
|
130
|
+
|
|
131
|
+
// =============================================================================
|
|
132
|
+
// π₯οΈ Console Output Helpers
|
|
133
|
+
// =============================================================================
|
|
134
|
+
|
|
135
|
+
function clearLine() {
|
|
136
|
+
process.stdout.write("\r\x1b[K");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function moveCursorUp(lines: number) {
|
|
140
|
+
process.stdout.write(`\x1b[${lines}A`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function printHeader() {
|
|
144
|
+
console.log();
|
|
145
|
+
console.log(
|
|
146
|
+
` ${c.bold}${c.magenta} tests2dialects ${c.reset} ${dim("v0.2.0")}`,
|
|
147
|
+
);
|
|
148
|
+
console.log();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function printFileStart(file: string, index: number, total: number) {
|
|
152
|
+
const relativePath = relative(process.cwd(), file);
|
|
153
|
+
const dir = dirname(relativePath);
|
|
154
|
+
const name = basename(relativePath);
|
|
155
|
+
|
|
156
|
+
const progress = `${dim(`[${index + 1}/${total}]`)}`;
|
|
157
|
+
const dirPart = dir !== "." ? `${gray(dir + "/")}` : "";
|
|
158
|
+
|
|
159
|
+
return `${progress} ${dirPart}${cyan(name)}`;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function printRunningSpinner(
|
|
163
|
+
file: string,
|
|
164
|
+
startTime: number,
|
|
165
|
+
spinnerFrame: number,
|
|
166
|
+
): string {
|
|
167
|
+
const spinner = getSpinner()[spinnerFrame % getSpinner().length];
|
|
168
|
+
const elapsed = Date.now() - startTime;
|
|
169
|
+
const timer = yellow(`${formatDuration(elapsed)}`);
|
|
170
|
+
|
|
171
|
+
return ` ${c.yellow}${spinner}${c.reset} ${file} ${timer}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function printFileResult(file: string, result: TestResult) {
|
|
175
|
+
const relativePath = relative(process.cwd(), file);
|
|
176
|
+
const dir = dirname(relativePath);
|
|
177
|
+
const name = basename(relativePath);
|
|
178
|
+
|
|
179
|
+
const dirPart = dir !== "." ? `${gray(dir + "/")}` : "";
|
|
180
|
+
const timer = gray(`(${formatDuration(result.duration)})`);
|
|
181
|
+
|
|
182
|
+
if (result.failed > 0) {
|
|
183
|
+
console.log(
|
|
184
|
+
` ${fail("β")} ${dirPart}${fail(name)} ${fail(
|
|
185
|
+
`${result.failed} failed`,
|
|
186
|
+
)} ${timer}`,
|
|
187
|
+
);
|
|
188
|
+
} else {
|
|
189
|
+
console.log(
|
|
190
|
+
` ${pass("β")} ${dirPart}${cyan(name)} ${pass(
|
|
191
|
+
`${result.passed} passed`,
|
|
192
|
+
)} ${timer}`,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Print individual test results (indented)
|
|
197
|
+
for (const test of result.tests) {
|
|
198
|
+
const testTimer = gray(`${formatDuration(test.duration)}`);
|
|
199
|
+
if (test.status === "pass") {
|
|
200
|
+
console.log(` ${pass("β")} ${dim(test.name)} ${testTimer}`);
|
|
201
|
+
} else if (test.status === "fail") {
|
|
202
|
+
console.log(` ${fail("β")} ${fail(test.name)} ${testTimer}`);
|
|
203
|
+
if (test.error) {
|
|
204
|
+
console.log(` ${fail("β")} ${c.dim}${test.error}${c.reset}`);
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
console.log(` ${skip("β")} ${skip(test.name)} ${dim("[skipped]")}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
console.log();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function printSummary() {
|
|
214
|
+
const totalFiles = results.length;
|
|
215
|
+
const totalTests = results.reduce((sum, r) => sum + r.tests.length, 0);
|
|
216
|
+
const totalPassed = results.reduce((sum, r) => sum + r.passed, 0);
|
|
217
|
+
const totalFailed = results.reduce((sum, r) => sum + r.failed, 0);
|
|
218
|
+
const totalSkipped = results.reduce((sum, r) => sum + r.skipped, 0);
|
|
219
|
+
const totalDuration = results.reduce((sum, r) => sum + r.duration, 0);
|
|
220
|
+
|
|
221
|
+
console.log();
|
|
222
|
+
console.log(
|
|
223
|
+
` ${bold("Test Files")} ${
|
|
224
|
+
totalFailed > 0
|
|
225
|
+
? fail(`${results.filter((r) => r.failed > 0).length} failed`) + " | "
|
|
226
|
+
: ""
|
|
227
|
+
}${pass(`${results.filter((r) => r.failed === 0).length} passed`)} ${dim(
|
|
228
|
+
`(${totalFiles})`,
|
|
229
|
+
)}`,
|
|
230
|
+
);
|
|
231
|
+
console.log(
|
|
232
|
+
` ${bold(" Tests")} ${
|
|
233
|
+
totalFailed > 0 ? fail(`${totalFailed} failed`) + " | " : ""
|
|
234
|
+
}${totalPassed > 0 ? pass(`${totalPassed} passed`) : ""}${
|
|
235
|
+
totalSkipped > 0 ? " | " + skip(`${totalSkipped} skipped`) : ""
|
|
236
|
+
} ${dim(`(${totalTests})`)}`,
|
|
237
|
+
);
|
|
238
|
+
console.log(` ${bold(" Duration")} ${formatDuration(totalDuration)}`);
|
|
239
|
+
console.log();
|
|
240
|
+
|
|
241
|
+
// Final status bar
|
|
242
|
+
if (totalFailed > 0) {
|
|
243
|
+
console.log(
|
|
244
|
+
` ${c.bgRed}${c.bold}${c.white} FAIL ${c.reset} ${fail(
|
|
245
|
+
"Tests failed. Check the output above.",
|
|
246
|
+
)}`,
|
|
247
|
+
);
|
|
248
|
+
} else {
|
|
249
|
+
console.log(
|
|
250
|
+
` ${c.bgGreen}${c.bold}${c.black} PASS ${c.reset} ${pass(
|
|
251
|
+
"All tests passed!",
|
|
252
|
+
)}`,
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
console.log();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// =============================================================================
|
|
259
|
+
// π§ͺ Mock Test Runner (simulates test execution)
|
|
260
|
+
// =============================================================================
|
|
261
|
+
|
|
262
|
+
async function runTestFile(filePath: string): Promise<TestResult> {
|
|
263
|
+
const result: TestResult = {
|
|
264
|
+
file: filePath,
|
|
265
|
+
duration: 0,
|
|
266
|
+
passed: 0,
|
|
267
|
+
failed: 0,
|
|
268
|
+
skipped: 0,
|
|
269
|
+
tests: [],
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const startTime = Date.now();
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
// Create a mock test context that captures test registrations
|
|
276
|
+
const capturedTests: { name: string; fn: () => void | Promise<void> }[] =
|
|
277
|
+
[];
|
|
278
|
+
|
|
279
|
+
// Override global test functions temporarily
|
|
280
|
+
const originalConsoleLog = console.log;
|
|
281
|
+
const originalConsoleError = console.error;
|
|
282
|
+
const logs: string[] = [];
|
|
283
|
+
|
|
284
|
+
// Capture console output during file import
|
|
285
|
+
console.log = (...args) => logs.push(args.join(" "));
|
|
286
|
+
console.error = (...args) => logs.push(`[ERROR] ${args.join(" ")}`);
|
|
287
|
+
|
|
288
|
+
resetAtomicCore();
|
|
289
|
+
|
|
290
|
+
try {
|
|
291
|
+
// Dynamic import executes the file
|
|
292
|
+
await import(filePath);
|
|
293
|
+
} catch (importError) {
|
|
294
|
+
console.log = originalConsoleLog;
|
|
295
|
+
console.error = originalConsoleError;
|
|
296
|
+
|
|
297
|
+
result.failed = 1;
|
|
298
|
+
result.tests.push({
|
|
299
|
+
name: "Module import",
|
|
300
|
+
status: "fail",
|
|
301
|
+
duration: Date.now() - startTime,
|
|
302
|
+
error: (importError as Error).message,
|
|
303
|
+
});
|
|
304
|
+
result.duration = Date.now() - startTime;
|
|
305
|
+
return result;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Restore console
|
|
309
|
+
console.log = originalConsoleLog;
|
|
310
|
+
console.error = originalConsoleError;
|
|
311
|
+
|
|
312
|
+
// Parse captured logs to extract test results
|
|
313
|
+
// Looking for patterns like:
|
|
314
|
+
// π [GROUP] ...
|
|
315
|
+
// ββ π [CASE] ...
|
|
316
|
+
// β
PASS
|
|
317
|
+
// β FAIL: ...
|
|
318
|
+
|
|
319
|
+
let currentGroup = "";
|
|
320
|
+
let currentTest = "";
|
|
321
|
+
let testStart = startTime;
|
|
322
|
+
|
|
323
|
+
for (const log of logs) {
|
|
324
|
+
if (log.includes("β
PASS:")) {
|
|
325
|
+
result.passed++;
|
|
326
|
+
const testFullLabel = log.replace(/.*β
PASS:\s*/, "").trim();
|
|
327
|
+
result.tests.push({
|
|
328
|
+
name: testFullLabel.replace("ROOT βΊ ", ""),
|
|
329
|
+
status: "pass",
|
|
330
|
+
duration: Math.max(1, Math.random() * 10),
|
|
331
|
+
});
|
|
332
|
+
} else if (log.includes("β FAIL:")) {
|
|
333
|
+
result.failed++;
|
|
334
|
+
const parts = log.replace(/.*β FAIL:\s*/, "").split(" βΊ ");
|
|
335
|
+
const errorMsg = parts.pop() || "Unknown error";
|
|
336
|
+
const testFullLabel = parts.join(" βΊ ");
|
|
337
|
+
|
|
338
|
+
result.tests.push({
|
|
339
|
+
name: testFullLabel.replace("ROOT βΊ ", ""),
|
|
340
|
+
status: "fail",
|
|
341
|
+
duration: Math.max(1, Math.random() * 10),
|
|
342
|
+
error: errorMsg,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// If no tests were captured from logs, assume the file ran successfully
|
|
348
|
+
if (result.tests.length === 0) {
|
|
349
|
+
result.passed = 1;
|
|
350
|
+
result.tests.push({
|
|
351
|
+
name: "Module execution",
|
|
352
|
+
status: "pass",
|
|
353
|
+
duration: Date.now() - startTime,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
} catch (e) {
|
|
357
|
+
result.failed = 1;
|
|
358
|
+
result.tests.push({
|
|
359
|
+
name: "Unknown error",
|
|
360
|
+
status: "fail",
|
|
361
|
+
duration: Date.now() - startTime,
|
|
362
|
+
error: (e as Error).message,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
result.duration = Date.now() - startTime;
|
|
367
|
+
return result;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// =============================================================================
|
|
371
|
+
// π Main Execution
|
|
372
|
+
// =============================================================================
|
|
373
|
+
|
|
374
|
+
async function main() {
|
|
375
|
+
const cwd = process.cwd();
|
|
376
|
+
|
|
377
|
+
printHeader();
|
|
378
|
+
|
|
379
|
+
console.log(
|
|
380
|
+
` ${brightCyan("DEV")} ${dim(
|
|
381
|
+
`Scanning for ${cyan(".spec.ts")} files in ${gray(cwd)}`,
|
|
382
|
+
)}`,
|
|
383
|
+
);
|
|
384
|
+
console.log();
|
|
385
|
+
|
|
386
|
+
const files = await findSpecFiles(cwd);
|
|
387
|
+
|
|
388
|
+
if (files.length === 0) {
|
|
389
|
+
console.log(` ${skip("β ")} ${yellow("No .spec.ts files found.")}`);
|
|
390
|
+
console.log();
|
|
391
|
+
process.exit(0);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
console.log(
|
|
395
|
+
` ${dim("Found")} ${bold(files.length.toString())} ${dim("test files")}`,
|
|
396
|
+
);
|
|
397
|
+
console.log();
|
|
398
|
+
console.log(gray(" β".repeat(35)));
|
|
399
|
+
console.log();
|
|
400
|
+
|
|
401
|
+
// Run tests with live timer
|
|
402
|
+
for (let i = 0; i < files.length; i++) {
|
|
403
|
+
const file = files[i];
|
|
404
|
+
const fileLabel = printFileStart(file, i, files.length);
|
|
405
|
+
|
|
406
|
+
// Show running state with spinner
|
|
407
|
+
const startTime = Date.now();
|
|
408
|
+
let spinnerInterval: ReturnType<typeof setInterval> | null = null;
|
|
409
|
+
let frame = 0;
|
|
410
|
+
|
|
411
|
+
// Spinner animation (non-blocking)
|
|
412
|
+
const spinnerPromise = new Promise<void>((resolve) => {
|
|
413
|
+
spinnerInterval = setInterval(() => {
|
|
414
|
+
clearLine();
|
|
415
|
+
process.stdout.write(
|
|
416
|
+
printRunningSpinner(fileLabel, startTime, frame++),
|
|
417
|
+
);
|
|
418
|
+
}, 80);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// Actually run the tests
|
|
422
|
+
const result = await runTestFile(file);
|
|
423
|
+
results.push(result);
|
|
424
|
+
|
|
425
|
+
// Stop spinner
|
|
426
|
+
if (spinnerInterval) clearInterval(spinnerInterval);
|
|
427
|
+
clearLine();
|
|
428
|
+
|
|
429
|
+
// Print final result for this file
|
|
430
|
+
printFileResult(file, result);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Print summary
|
|
434
|
+
console.log(gray(" β".repeat(35)));
|
|
435
|
+
printSummary();
|
|
436
|
+
|
|
437
|
+
// Exit with appropriate code
|
|
438
|
+
const hasFailures = results.some((r) => r.failed > 0);
|
|
439
|
+
process.exit(hasFailures ? 1 : 0);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
main().catch((err) => {
|
|
443
|
+
console.error(`${fail("Fatal Error:")} ${err.message}`);
|
|
444
|
+
process.exit(1);
|
|
445
|
+
});
|