deflake 1.0.6 → 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 (2) hide show
  1. package/cli.js +89 -32
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -307,7 +307,7 @@ function extractFailureLocation(logText) {
307
307
  return null;
308
308
  }
309
309
 
310
- function printDetailedFix(fixText, location, sourceCode = null) {
310
+ function printDetailedFix(fixText, location, sourceCode = null, isApplied = false) {
311
311
  const C = {
312
312
  RESET: "\x1b[0m",
313
313
  BRIGHT: "\x1b[1m",
@@ -332,32 +332,85 @@ function printDetailedFix(fixText, location, sourceCode = null) {
332
332
  } catch (e) { }
333
333
 
334
334
  console.log("\n" + C.GRAY + "─".repeat(50) + C.RESET);
335
- if (location) {
336
- // Label is default color, Value is colored
337
- if (location.rootFile && location.rootLine) {
338
- console.log(`💥 Runtime Error: ${C.RED}${location.rootFile}:${location.rootLine}${C.RESET}`);
335
+
336
+ if (isApplied) {
337
+ // --- APPLIED VIEW ---
338
+ console.log(`${C.GREEN}${C.BRIGHT}✅ FIX APPLIED:${C.RESET}`);
339
+ if (location) {
340
+ // Label is default color, Value is colored
341
+ const fileLabel = location.specFile || location.rootFile;
342
+ let lineLabel = location.testLine || location.rootLine;
343
+
344
+ if (fileLabel) {
345
+ console.log(`${C.BRIGHT}📄 File:${C.RESET} ${fileLabel}:${lineLabel}`);
346
+ }
339
347
  }
340
- if (location.specFile) {
341
- // Calculate a nice label for the target
342
- let targetLabel = location.specFile;
343
- if (location.testLine) targetLabel += `:${location.testLine}`;
344
- console.log(`🎯 Fix Target: ${C.CYAN}${targetLabel} (Definition)${C.RESET}`);
348
+
349
+ if (explanation) { // Highlight the reason as requested by user
350
+ console.log(`\n${C.BRIGHT}ℹ️ Reason:${C.RESET} ${explanation}`);
351
+ }
352
+
353
+ console.log(C.GRAY + "─".repeat(20) + C.RESET);
354
+
355
+ // --- OLD CODE (Context) ---
356
+ if (sourceCode && location && location.rootLine) {
357
+ console.log(`${C.RED}🔴 OLD:${C.RESET}`);
358
+ try {
359
+ const lines = sourceCode.split('\n');
360
+ const centerIdx = parseInt(location.rootLine) - 1;
361
+ // Show a small window around the error
362
+ const start = Math.max(0, centerIdx - 2);
363
+ const end = Math.min(lines.length - 1, centerIdx + 2);
364
+
365
+ for (let i = start; i <= end; i++) {
366
+ let prefix = (i === centerIdx) ? "> " : " ";
367
+ let line = lines[i];
368
+ if (i === centerIdx) line = `${C.RED}${line}${C.RESET}`;
369
+ else line = `${C.GRAY}${line}${C.RESET}`;
370
+ console.log(prefix + line);
371
+ }
372
+ } catch (e) { }
373
+ }
374
+
375
+ // --- NEW CODE ---
376
+ console.log(`\n${C.GREEN}🟢 NEW:${C.RESET}`);
377
+ fixCode.split('\n').forEach(line => {
378
+ let colored = line
379
+ .replace(/(\/\/.*)/g, `${C.GRAY}$1${C.RESET}`)
380
+ .replace(/\b(const|let|var|await|async|function|return)\b/g, `${C.YELLOW}$1${C.RESET}`)
381
+ .replace(/('.*?')|(".*?")|(`.*?`)/g, `${C.GREEN}$1${C.RESET}`)
382
+ .replace(/(\.click|\.fill|\.locator)/g, `${C.CYAN}$1${C.RESET}`);
383
+ console.log(" " + colored);
384
+ });
385
+
386
+ } else {
387
+ // --- SUGGESTION VIEW (Default) ---
388
+ if (location) {
389
+ if (location.rootFile && location.rootLine) {
390
+ console.log(`💥 Runtime Error: ${C.RED}${location.rootFile}:${location.rootLine}${C.RESET}`);
391
+ }
392
+ if (location.specFile) {
393
+ let targetLabel = location.specFile;
394
+ if (location.testLine) targetLabel += `:${location.testLine}`;
395
+ console.log(`🎯 Fix Target: ${C.CYAN}${targetLabel} (Definition)${C.RESET}`);
396
+ }
345
397
  }
398
+ console.log(C.GRAY + "─".repeat(50) + C.RESET);
399
+
400
+ console.log(`${C.GREEN}${C.BRIGHT}✨ DEFLAKE SUGGESTION:${C.RESET}`);
401
+ if (explanation) console.log(`${C.GRAY}// ${explanation}${C.RESET}`);
402
+
403
+ // Print code with simple coloring
404
+ fixCode.split('\n').forEach(line => {
405
+ let colored = line
406
+ .replace(/(\/\/.*)/g, `${C.GRAY}$1${C.RESET}`)
407
+ .replace(/\b(const|let|var|await|async|function|return)\b/g, `${C.YELLOW}$1${C.RESET}`)
408
+ .replace(/('.*?')|(".*?")|(`.*?`)/g, `${C.GREEN}$1${C.RESET}`)
409
+ .replace(/(\.click|\.fill|\.locator)/g, `${C.CYAN}$1${C.RESET}`);
410
+ console.log(" " + colored);
411
+ });
346
412
  }
347
- console.log(C.GRAY + "─".repeat(50) + C.RESET);
348
413
 
349
- console.log(`${C.GREEN}${C.BRIGHT}✨ DEFLAKE SUGGESTION:${C.RESET}`);
350
- if (explanation) console.log(`${C.GRAY}// ${explanation}${C.RESET}`);
351
-
352
- // Print code with simple coloring
353
- fixCode.split('\n').forEach(line => {
354
- let colored = line
355
- .replace(/(\/\/.*)/g, `${C.GRAY}$1${C.RESET}`)
356
- .replace(/\b(const|let|var|await|async|function|return)\b/g, `${C.YELLOW}$1${C.RESET}`)
357
- .replace(/('.*?')|(".*?")|(`.*?`)/g, `${C.GREEN}$1${C.RESET}`)
358
- .replace(/(\.click|\.fill|\.locator)/g, `${C.CYAN}$1${C.RESET}`);
359
- console.log(" " + colored);
360
- });
361
414
  console.log(C.GRAY + "─".repeat(50) + C.RESET);
362
415
  }
363
416
 
@@ -658,11 +711,6 @@ async function analyzeFailures(artifacts, fullLog, client) {
658
711
  process.stdout.write(`\r⏳ Analyzing ${art.name}... `);
659
712
  const result = await runHealer(specificLog, art.htmlPath, argv.apiUrl, art.name);
660
713
 
661
- if (result && result.status === 'error' && result.detail === 'QUOTA_EXCEEDED') {
662
- console.log(`\n${C.RED}🛑 Quota exceeded! Stopping.${C.RESET}`);
663
- break;
664
- }
665
-
666
714
  if (result && result.status === 'success') {
667
715
  results.push(result);
668
716
 
@@ -670,15 +718,18 @@ async function analyzeFailures(artifacts, fullLog, client) {
670
718
  if (argv.fix) {
671
719
  await applySelfHealing(result);
672
720
  }
721
+ } else {
722
+ console.log(`\n ${C.RED}❌ Analysis failed for ${art.name}:${C.RESET} ${result?.detail || 'Check server status'}`);
673
723
  }
674
724
  }
675
- console.log("\r✅ Analysis complete. \n");
725
+ process.stdout.write("\r✅ Analysis complete. \n");
676
726
 
677
727
  // GROUPING & PRINTING
678
728
  const groups = {};
679
729
  for (const res of results) {
680
- if (!res.location) continue;
681
- const key = `${res.location.rootFile}:${res.location.rootLine}|${JSON.stringify(res.fix)}`;
730
+ // Use a unique key for grouping. If location is missing, use testName to ensure it shows up.
731
+ const locId = res.location ? `${res.location.rootFile}:${res.location.rootLine}` : `unknown-${res.testName}`;
732
+ const key = `${locId}|${JSON.stringify(res.fix)}`;
682
733
  if (!groups[key]) {
683
734
  groups[key] = { location: res.location, sourceCode: res.sourceCode, fix: res.fix, tests: [] };
684
735
  }
@@ -692,7 +743,13 @@ async function analyzeFailures(artifacts, fullLog, client) {
692
743
  });
693
744
 
694
745
  for (const key of sortedKeys) {
695
- printDetailedFix(groups[key].fix, groups[key].location, groups[key].sourceCode);
746
+ printDetailedFix(groups[key].fix, groups[key].location, groups[key].sourceCode, argv.fix);
747
+ }
748
+
749
+ if (results.length > 0 && sortedKeys.length === 0) {
750
+ console.log(`${C.YELLOW}⚠️ Note: Some suggestions were found but could not be grouped for display.${C.RESET}`);
751
+ } else if (results.length === 0) {
752
+ console.log(`${C.GRAY}ℹ️ DeFlake analyzed the logs but couldn't find a confident fix for these errors.${C.RESET}`);
696
753
  }
697
754
  }
698
755
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deflake",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "AI-powered self-healing tool for Playwright, Cypress, and WebdriverIO tests.",
5
5
  "main": "client.js",
6
6
  "bin": {