bun-ui-tests 1.0.7 → 1.0.8

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.
Files changed (3) hide show
  1. package/cli.ts +40 -58
  2. package/package.json +1 -1
  3. package/ui-runner.ts +23 -79
package/cli.ts CHANGED
@@ -45,7 +45,18 @@ function getPackageRoot() {
45
45
  return process.cwd();
46
46
  }
47
47
 
48
- let packageRoot = getPackageRoot();
48
+ const packageRoot = getPackageRoot();
49
+
50
+ // Cores ANSI suaves
51
+ const colors = {
52
+ reset: "\x1b[0m",
53
+ red: "\x1b[31m",
54
+ green: "\x1b[32m",
55
+ yellow: "\x1b[33m",
56
+ blue: "\x1b[34m",
57
+ cyan: "\x1b[36m",
58
+ gray: "\x1b[90m"
59
+ };
49
60
 
50
61
  const COMMANDS = {
51
62
  run: "Run the test UI (production mode)",
@@ -55,7 +66,7 @@ const COMMANDS = {
55
66
 
56
67
  async function findVerifiedRoot(): Promise<string> {
57
68
  const possibleRoots = [
58
- packageRoot,
69
+ getPackageRoot(),
59
70
  process.cwd(),
60
71
  dirname(fileURLToPath(import.meta.url)),
61
72
  ];
@@ -70,9 +81,9 @@ async function findVerifiedRoot(): Promise<string> {
70
81
  }
71
82
  }
72
83
 
73
- // Se não achou, tenta procurar subindo diretórios (útil se estiver em node_modules/.bin)
84
+ // Se não achou, tenta procurar subindo diretórios
74
85
  try {
75
- let current = packageRoot;
86
+ let current = getPackageRoot();
76
87
  for (let i = 0; i < 3; i++) {
77
88
  const runnerPath = join(current, "ui-runner.ts");
78
89
  if (await Bun.file(runnerPath).exists()) return current;
@@ -80,12 +91,12 @@ async function findVerifiedRoot(): Promise<string> {
80
91
  }
81
92
  } catch (e) {}
82
93
 
83
- return packageRoot;
94
+ return getPackageRoot();
84
95
  }
85
96
 
86
97
  async function showHelp() {
87
98
  console.log(`
88
- 🧪 Bun Test UI - A beautiful UI for running Bun tests
99
+ ${colors.cyan}Bun Test UI${colors.reset} - A beautiful UI for running Bun tests
89
100
 
90
101
  Usage:
91
102
  bunx bun-ui-tests <command>
@@ -104,56 +115,32 @@ Examples:
104
115
  async function checkBuildExists(root: string): Promise<boolean> {
105
116
  const distPath = join(root, "app", "dist", "index.html");
106
117
 
107
- console.log(`🔍 Debug Info:`);
108
- console.log(` - process.argv[1]: ${process.argv[1]}`);
109
- console.log(` - Resolved packageRoot: ${root}`);
110
- console.log(` - Looking for: ${distPath}`);
111
-
112
118
  try {
113
- const exists = await Bun.file(distPath).exists();
114
- if (!exists) {
115
- console.log(` - File exists: NO ❌`);
116
- } else {
117
- console.log(` - File exists: YES ✓`);
118
- }
119
- return exists;
119
+ return await Bun.file(distPath).exists();
120
120
  } catch (err) {
121
- console.error(`❌ Error checking path:`, err);
122
121
  return false;
123
122
  }
124
123
  }
125
124
 
126
125
  async function runTestUI() {
127
- // Encontra o root real onde estão os arquivos
128
- packageRoot = await findVerifiedRoot();
129
-
130
- // Verifica se o build do frontend existe
131
- const buildExists = await checkBuildExists(packageRoot);
126
+ const root = await findVerifiedRoot();
127
+ const buildExists = await checkBuildExists(root);
132
128
 
133
129
  if (!buildExists) {
134
- console.log("\n⚠️ Frontend assets not found.\n");
130
+ console.log(`\n${colors.yellow}!${colors.reset} Frontend assets not found.\n`);
135
131
  console.log("This usually means one of:");
136
- console.log(" 1. The package wasn't built before publishing (contact maintainer)");
132
+ console.log(" 1. The package wasn't built before publishing");
137
133
  console.log(" 2. You're running from source (run: bun run build first)");
138
- console.log(" 3. Installation issue (try: npm cache clean --force)\n");
139
-
140
- console.log("💡 Temporary workaround:");
141
- console.log(" git clone https://github.com/KillDarkness/Bun-UI-Test.git");
142
- console.log(" cd Bun-UI-Test");
143
- console.log(" bun install");
144
- console.log(" cd app && bun install && bun run build && cd ..");
145
- console.log(" bun run ui-runner.ts\n");
134
+ console.log(" 3. Installation issue\n");
146
135
 
147
136
  process.exit(1);
148
137
  }
149
138
 
150
- console.log("🚀 Starting Bun Test UI (Production Mode)...\n");
151
- console.log("📡 WebSocket server: ws://localhost:5050/ws");
152
- console.log("🌐 Frontend: http://localhost:5050\n");
153
- console.log("Press Ctrl+C to stop\n");
139
+ console.log(`${colors.green}›${colors.reset} Starting Bun Test UI...`);
140
+ console.log(`${colors.gray}→ WebSocket: ws://localhost:5050/ws`);
141
+ console.log(`→ Frontend: http://localhost:5050${colors.reset}\n`);
154
142
 
155
- // Roda o script do backend diretamente com Bun
156
- const runnerScript = join(packageRoot, "ui-runner.ts");
143
+ const runnerScript = join(root, "ui-runner.ts");
157
144
 
158
145
  const proc = spawn("bun", ["run", runnerScript], {
159
146
  cwd: process.cwd(),
@@ -164,34 +151,31 @@ async function runTestUI() {
164
151
 
165
152
  proc.on("close", (code) => {
166
153
  if (code !== 0) {
167
- console.error(`\n Process exited with code ${code}`);
154
+ console.error(`\n${colors.red}✗${colors.reset} Process exited with code ${code}`);
168
155
  process.exit(code || 1);
169
156
  }
170
157
  });
171
158
 
172
159
  proc.on("error", (err) => {
173
- console.error("❌ Error starting test UI:", err);
160
+ console.error(`${colors.red}✗${colors.reset} Error starting test UI:`, err);
174
161
  process.exit(1);
175
162
  });
176
163
 
177
- // Handle Ctrl+C
178
164
  process.on("SIGINT", () => {
179
- console.log("\n\n👋 Stopping Bun Test UI...");
165
+ console.log(`\n\n${colors.gray}Stopping Bun Test UI...${colors.reset}`);
180
166
  proc.kill("SIGINT");
181
167
  process.exit(0);
182
168
  });
183
169
  }
184
170
 
185
171
  async function runDevMode() {
186
- // Encontra o root real onde estão os arquivos
187
- packageRoot = await findVerifiedRoot();
172
+ const root = await findVerifiedRoot();
188
173
 
189
- console.log("🚀 Starting Bun Test UI (Development Mode)...\n");
190
- console.log("📡 WebSocket server: ws://localhost:5060");
191
- console.log("🌐 Frontend: http://localhost:5050 (with hot reload)\n");
192
- console.log("Press Ctrl+C to stop\n");
174
+ console.log(`${colors.cyan}›${colors.reset} Starting Bun Test UI (Dev Mode)...`);
175
+ console.log(`${colors.gray}→ WebSocket: ws://localhost:5060`);
176
+ console.log(`→ Frontend: http://localhost:5050${colors.reset}\n`);
193
177
 
194
- const backendPath = join(packageRoot, "ui-runner.ts");
178
+ const backendPath = join(root, "ui-runner.ts");
195
179
  const backendProc = spawn("bun", ["run", backendPath], {
196
180
  cwd: process.cwd(),
197
181
  stdio: "inherit",
@@ -201,7 +185,7 @@ async function runDevMode() {
201
185
 
202
186
  await new Promise(resolve => setTimeout(resolve, 1000));
203
187
 
204
- const appDir = join(packageRoot, "app");
188
+ const appDir = join(root, "app");
205
189
  const frontendProc = spawn("bun", ["run", "dev"], {
206
190
  cwd: appDir,
207
191
  stdio: "inherit",
@@ -209,20 +193,18 @@ async function runDevMode() {
209
193
  });
210
194
 
211
195
  process.on("SIGINT", () => {
212
- console.log("\n\n👋 Stopping Bun Test UI...");
196
+ console.log(`\n\n${colors.gray}Stopping Bun Test UI...${colors.reset}`);
213
197
  backendProc.kill("SIGINT");
214
198
  frontendProc.kill("SIGINT");
215
199
  process.exit(0);
216
200
  });
217
201
 
218
202
  backendProc.on("close", (code) => {
219
- console.log("\n❌ Backend stopped");
220
203
  frontendProc.kill("SIGINT");
221
204
  process.exit(code || 1);
222
205
  });
223
206
 
224
207
  frontendProc.on("close", (code) => {
225
- console.log("\n❌ Frontend stopped");
226
208
  backendProc.kill("SIGINT");
227
209
  process.exit(code || 1);
228
210
  });
@@ -235,7 +217,7 @@ switch (command) {
235
217
  case "run":
236
218
  runTestUI()
237
219
  .catch((err) => {
238
- console.error("❌ Failed to start:", err);
220
+ console.error(`${colors.red}✗${colors.reset} Failed to start:`, err);
239
221
  process.exit(1);
240
222
  });
241
223
  break;
@@ -243,7 +225,7 @@ switch (command) {
243
225
  case "dev":
244
226
  runDevMode()
245
227
  .catch((err) => {
246
- console.error("❌ Failed to start dev mode:", err);
228
+ console.error(`${colors.red}✗${colors.reset} Failed to start dev mode:`, err);
247
229
  process.exit(1);
248
230
  });
249
231
  break;
@@ -255,7 +237,7 @@ switch (command) {
255
237
  break;
256
238
 
257
239
  default:
258
- console.error(`❌ Unknown command: ${command}\n`);
240
+ console.error(`${colors.red}✗${colors.reset} Unknown command: ${command}\n`);
259
241
  showHelp();
260
242
  process.exit(1);
261
243
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bun-ui-tests",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "A beautiful UI for running Bun tests",
5
5
  "type": "module",
6
6
  "bin": {
package/ui-runner.ts CHANGED
@@ -45,22 +45,29 @@ const getBaseDir = () => {
45
45
  return process.cwd();
46
46
  };
47
47
 
48
+ const colors = {
49
+ reset: "\x1b[0m",
50
+ red: "\x1b[31m",
51
+ green: "\x1b[32m",
52
+ yellow: "\x1b[33m",
53
+ blue: "\x1b[34m",
54
+ cyan: "\x1b[36m",
55
+ gray: "\x1b[90m"
56
+ };
57
+
48
58
  const baseDir = getBaseDir();
49
59
  const distPath = join(baseDir, "app", "dist");
50
- console.log(`Debug: ui-runner argv[1] is ${process.argv[1]}`);
51
- console.log(`Debug: distPath is ${distPath}`);
52
60
  const isDevMode = process.env.BUN_TEST_UI_DEV === "true";
53
61
 
54
62
  // WebSocket Handler (lógica compartilhada)
55
63
  const websocketHandler = {
56
64
  async open(ws: any) {
57
- console.log("✓ UI connected");
65
+ console.log(`${colors.green}✓${colors.reset} UI connected`);
58
66
 
59
67
  // Escaneia arquivos de teste
60
68
  const testFiles = await scanTestFiles();
61
69
 
62
70
  // Escaneia todos os testes de cada arquivo
63
- console.log("📖 Reading test files to extract test names...");
64
71
  const testsMap = await scanAllTests();
65
72
 
66
73
  // Envia evento de conexão com lista de arquivos e testes
@@ -73,65 +80,48 @@ const websocketHandler = {
73
80
  }
74
81
  }));
75
82
 
76
- console.log(`✓ Found ${testFiles.length} test files with ${Object.values(testsMap).flat().length} tests total`);
83
+ console.log(`${colors.gray}› Found ${testFiles.length} test files with ${Object.values(testsMap).flat().length} tests total${colors.reset}`);
77
84
  },
78
85
  message(ws: any, message: any) {
79
86
  try {
80
87
  const data = JSON.parse(message.toString());
81
- console.log('📨 [WEBSOCKET] Mensagem recebida:', data.type, data.payload);
82
88
 
83
89
  // Processa comandos da UI
84
90
  if (data.type === "run:request") {
85
91
  const file = data.payload?.file;
86
92
  const testName = data.payload?.testName;
87
93
 
88
- console.log('▶️ [RUN REQUEST] file:', file, 'testName:', testName);
89
-
90
94
  if (testName) {
91
- console.log(`Running specific test: ${testName} in ${file}`);
95
+ console.log(`${colors.cyan}▶${colors.reset} Running test: ${colors.gray}${testName}${colors.reset} in ${file}`);
92
96
  } else if (file) {
93
- console.log(`Running file: ${file}`);
97
+ console.log(`${colors.cyan}▶${colors.reset} Running file: ${file}`);
94
98
  } else {
95
- console.log("Running all tests");
99
+ console.log(`${colors.cyan}▶${colors.reset} Running all tests`);
96
100
  }
97
101
 
98
102
  runTests(ws, file, testName);
99
- } else {
100
- console.log('⚠️ [WEBSOCKET] Tipo desconhecido:', data.type);
101
103
  }
102
104
  } catch (err) {
103
105
  console.error("Error processing message:", err);
104
106
  }
105
107
  },
106
108
  close(ws: any) {
107
- console.log("✗ UI disconnected");
109
+ console.log(`${colors.red}✗${colors.reset} UI disconnected`);
108
110
  },
109
111
  };
110
112
 
111
113
  if (isDevMode) {
112
114
  // === MODO DESENVOLVIMENTO ===
113
- // Frontend roda via Vite na porta 5050
114
- // Backend roda separadamente na porta 5060 (apenas WS)
115
-
116
115
  Bun.serve({
117
116
  port: 5060,
118
117
  fetch(req, server) {
119
- // Aceita upgrade em qualquer path ou especificamente /ws
120
- if (server.upgrade(req)) {
121
- return;
122
- }
118
+ if (server.upgrade(req)) return;
123
119
  return new Response("Bun Test UI Backend (Dev Mode)", { status: 200 });
124
120
  },
125
121
  websocket: websocketHandler
126
122
  });
127
-
128
- console.log("📡 WebSocket server running on ws://localhost:5060");
129
-
130
123
  } else {
131
124
  // === MODO PRODUÇÃO ===
132
- // Servidor ÚNICO na porta 5050
133
- // Serve arquivos estáticos do frontend E WebSocket no mesmo endpoint
134
-
135
125
  const PORT = 5050;
136
126
 
137
127
  Bun.serve({
@@ -141,24 +131,18 @@ if (isDevMode) {
141
131
 
142
132
  // 1. WebSocket Upgrade (/ws)
143
133
  if (url.pathname === "/ws") {
144
- if (server.upgrade(req)) {
145
- return;
146
- }
134
+ if (server.upgrade(req)) return;
147
135
  return new Response("WebSocket upgrade failed", { status: 400 });
148
136
  }
149
137
 
150
138
  // 2. Arquivos Estáticos (Frontend)
151
139
  const file = Bun.file(join(distPath, url.pathname === "/" ? "/index.html" : url.pathname));
152
- if (await file.exists()) {
153
- return new Response(file);
154
- }
140
+ if (await file.exists()) return new Response(file);
155
141
 
156
142
  // SPA fallback
157
143
  if (!url.pathname.includes(".")) {
158
144
  const indexFile = Bun.file(join(distPath, "index.html"));
159
- if (await indexFile.exists()) {
160
- return new Response(indexFile);
161
- }
145
+ if (await indexFile.exists()) return new Response(indexFile);
162
146
  }
163
147
 
164
148
  return new Response("Frontend build not found.", { status: 404 });
@@ -166,8 +150,7 @@ if (isDevMode) {
166
150
  websocket: websocketHandler
167
151
  });
168
152
 
169
- console.log(`🚀 Server running on http://localhost:${PORT}`);
170
- console.log(`📡 WebSocket endpoint available at ws://localhost:${PORT}/ws`);
153
+ console.log(`${colors.green}✓${colors.reset} Server running on http://localhost:${PORT}`);
171
154
  }
172
155
 
173
156
 
@@ -292,17 +275,11 @@ function runTests(ws: any, file?: string, testName?: string) {
292
275
  args.push(file);
293
276
  }
294
277
 
295
- // Bun suporta --test-name-pattern para filtrar testes
296
- // Escapa caracteres especiais de regex e usa o nome exato
297
278
  if (testName) {
298
- // Escapa caracteres especiais de regex
299
279
  const escapedName = testName.replace(/[.*+?^${}()|[\\]/g, '\\$&');
300
280
  args.push("--test-name-pattern", escapedName);
301
281
  }
302
282
 
303
- console.log('🚀 [RUN] Comando:', `bun ${args.join(" ")}`);
304
- console.log(`Starting bun test ${args.slice(1).join(" ") || "(all)"}...`);
305
-
306
283
  // Emite evento de início
307
284
  ws.send(JSON.stringify({
308
285
  type: "run:start",
@@ -313,11 +290,9 @@ function runTests(ws: any, file?: string, testName?: string) {
313
290
  }
314
291
  }));
315
292
 
316
- // Spawna o processo bun test
317
- // IMPORTANTE: Não usamos nenhuma API interna do bun:test
318
293
  const bunTest = spawn("bun", args, {
319
294
  cwd: process.cwd(),
320
- env: { ...process.env, FORCE_COLOR: "0" }, // Desabilita cores para facilitar parsing
295
+ env: { ...process.env, FORCE_COLOR: "0" },
321
296
  });
322
297
 
323
298
  let currentTestFile = "";
@@ -359,11 +334,6 @@ function runTests(ws: any, file?: string, testName?: string) {
359
334
  // Função para enviar bloco de erro
360
335
  const flushErrorBlock = () => {
361
336
  if (errorBlock.length > 0) {
362
- console.log('📦 [ERROR BLOCK] Enviando bloco com', errorBlock.length, 'linhas:');
363
- console.log('---START---');
364
- console.log(errorBlock.join('\n'));
365
- console.log('---END---');
366
-
367
337
  ws.send(JSON.stringify({
368
338
  type: "log",
369
339
  payload: { message: errorBlock.join('\n'), stream: "stdout" }
@@ -376,11 +346,6 @@ function runTests(ws: any, file?: string, testName?: string) {
376
346
  // Função para enviar bloco de resumo
377
347
  const flushSummaryBlock = () => {
378
348
  if (summaryBlock.length > 0) {
379
- console.log('📊 [SUMMARY BLOCK] Enviando bloco com', summaryBlock.length, 'linhas:');
380
- console.log('---START---');
381
- console.log(summaryBlock.join('\n'));
382
- console.log('---END---');
383
-
384
349
  ws.send(JSON.stringify({
385
350
  type: "log",
386
351
  payload: { message: summaryBlock.join('\n'), stream: "stdout" }
@@ -395,52 +360,36 @@ function runTests(ws: any, file?: string, testName?: string) {
395
360
  const text = data.toString();
396
361
  buffer += text;
397
362
 
398
- // Processa linhas completas
399
363
  const lines = buffer.split("\n");
400
- buffer = lines.pop() || ""; // Guarda última linha incompleta
364
+ buffer = lines.pop() || "";
401
365
 
402
366
  for (const line of lines) {
403
367
  currentTestFile = processLine(line, ws, currentTestFile);
404
-
405
- // Linhas vazias podem fazer parte de blocos
406
368
  const trimmed = line.trim();
407
369
 
408
- // Verifica se é linha de erro
409
370
  if (isErrorLine(line)) {
410
- console.log('🔴 [ERROR LINE] Detectado:', line.substring(0, 50));
411
- // Se estava no resumo, envia o resumo primeiro
412
371
  flushSummaryBlock();
413
-
414
372
  inErrorBlock = true;
415
373
  errorBlock.push(line);
416
374
  continue;
417
375
  }
418
376
 
419
- // Linha vazia dentro de bloco de erro - mantém no bloco
420
377
  if (inErrorBlock && !trimmed) {
421
- console.log('🔴 [ERROR EMPTY] Linha vazia no bloco de erro');
422
378
  errorBlock.push(line);
423
379
  continue;
424
380
  }
425
381
 
426
- // Verifica se é linha de resumo
427
382
  if (isSummaryLine(line)) {
428
- console.log('📊 [SUMMARY LINE] Detectado:', line.substring(0, 50));
429
- // Se estava no erro, envia o erro primeiro
430
383
  flushErrorBlock();
431
-
432
384
  inSummaryBlock = true;
433
385
  summaryBlock.push(line);
434
386
  continue;
435
387
  }
436
388
 
437
- // Linha normal - envia blocos pendentes e depois a linha
438
389
  flushErrorBlock();
439
390
  flushSummaryBlock();
440
391
 
441
- // Envia linhas normais separadamente (se não vazia)
442
392
  if (trimmed) {
443
- console.log('📝 [NORMAL LINE] Enviando:', line.substring(0, 50));
444
393
  ws.send(JSON.stringify({
445
394
  type: "log",
446
395
  payload: { message: line, stream: "stdout" }
@@ -463,11 +412,8 @@ function runTests(ws: any, file?: string, testName?: string) {
463
412
  });
464
413
 
465
414
  bunTest.on("close", (code) => {
466
- console.log(`bun test exited with code ${code}`);
467
-
468
415
  // Processa buffer pendente antes de finalizar
469
416
  if (buffer.trim()) {
470
- console.log('⚠️ [CLOSE] Buffer pendente:', buffer.substring(0, 100));
471
417
  const lines = buffer.split("\n");
472
418
  for (const line of lines) {
473
419
  if (line.trim()) {
@@ -500,8 +446,6 @@ function runTests(ws: any, file?: string, testName?: string) {
500
446
  flushErrorBlock();
501
447
  flushSummaryBlock();
502
448
 
503
- console.log('✅ [CLOSE] Processo finalizado, todos os logs enviados');
504
-
505
449
  ws.send(JSON.stringify({
506
450
  type: "run:complete",
507
451
  payload: {