deflake 1.2.37 → 1.2.38

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 +62 -10
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -46,15 +46,63 @@ async function main() {
46
46
  console.log(`šŸ” Detected Framework: ${C.GREEN}${fw.toUpperCase()}${C.RESET}`);
47
47
 
48
48
  if (commandToRun) {
49
- const { code, output } = await runNative(commandToRun);
50
- if (code !== 0) {
49
+ const MAX_ATTEMPTS = 5;
50
+ let { code, output } = await runNative(commandToRun);
51
+
52
+ if (code !== 0 && argv.fix) {
51
53
  console.log(`\nšŸ”“ Command failed with code ${code}. Activating DeFlake...`);
52
- const artifacts = detectFailures();
53
- const applied = await analyzeAndFix(artifacts, client, argv, output);
54
- if (applied > 0 && argv.fix) {
55
- console.log(`\n${C.BRIGHT}šŸ’‰ Fixes applied. Re-running tests to verify...${C.RESET}`);
56
- await runNative(commandToRun);
54
+
55
+ // Save backups of all source files before any modifications
56
+ const backups = new Map(); // path -> original content
57
+ let prevFailCount = Infinity;
58
+
59
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
60
+ console.log(`\n${C.BRIGHT}${C.CYAN}šŸ”„ Fix attempt ${attempt}/${MAX_ATTEMPTS}${C.RESET}`);
61
+
62
+ const artifacts = detectFailures();
63
+ const currentFailCount = artifacts.length;
64
+
65
+ if (currentFailCount === 0) {
66
+ console.log(`${C.GREEN}šŸŽ‰ All tests passing! No failures detected.${C.RESET}`);
67
+ break;
68
+ }
69
+
70
+ // If failures INCREASED since last attempt, rollback all changes
71
+ if (attempt > 1 && currentFailCount > prevFailCount) {
72
+ console.log(`${C.RED}āš ļø Failures increased (${prevFailCount} → ${currentFailCount}). Rolling back all changes...${C.RESET}`);
73
+ for (const [filePath, originalContent] of backups) {
74
+ fs.writeFileSync(filePath, originalContent);
75
+ console.log(` ${C.YELLOW}ā†©ļø Restored: ${path.basename(filePath)}${C.RESET}`);
76
+ }
77
+ break;
78
+ }
79
+ prevFailCount = currentFailCount;
80
+
81
+ const applied = await analyzeAndFix(artifacts, client, argv, output, backups);
82
+
83
+ if (applied === 0) {
84
+ console.log(`${C.YELLOW}āš ļø No fixes could be applied. Stopping retry loop.${C.RESET}`);
85
+ break;
86
+ }
87
+
88
+
89
+ console.log(`\n${C.BRIGHT}šŸ’‰ ${applied} fix(es) applied. Re-running tests to verify (attempt ${attempt})...${C.RESET}`);
90
+ const rerun = await runNative(commandToRun);
91
+ output = rerun.output; // Use new output for next iteration's AI context
92
+
93
+ if (rerun.code === 0) {
94
+ console.log(`\n${C.GREEN}${C.BRIGHT}šŸŽ‰ All tests passing after attempt ${attempt}!${C.RESET}`);
95
+ break;
96
+ }
97
+
98
+ if (attempt === MAX_ATTEMPTS) {
99
+ console.log(`\n${C.YELLOW}āš ļø Max attempts (${MAX_ATTEMPTS}) reached. Some failures may persist.${C.RESET}`);
100
+ }
57
101
  }
102
+ } else if (code !== 0) {
103
+ console.log(`\nšŸ”“ Command failed with code ${code}. Activating DeFlake...`);
104
+ const artifacts = detectFailures();
105
+ await analyzeAndFix(artifacts, client, argv, output, new Map());
58
106
  }
59
107
  }
60
108
  if (argv.report) showReport();
@@ -110,7 +158,7 @@ function detectFailures() {
110
158
  return results;
111
159
  }
112
160
 
113
- async function analyzeAndFix(artifacts, client, argv, capturedOutput = '') {
161
+ async function analyzeAndFix(artifacts, client, argv, capturedOutput = '', backups = new Map()) {
114
162
  if (artifacts.length === 0) {
115
163
  console.log(` ${C.YELLOW}āš ļø No failure artifacts detected. Run 'npx deflake doctor' to check permissions.${C.RESET}`);
116
164
  return 0;
@@ -186,7 +234,7 @@ async function analyzeAndFix(artifacts, client, argv, capturedOutput = '') {
186
234
  }
187
235
  if (fixLoc) {
188
236
  console.log(` ${C.BRIGHT}šŸ’‰ Applying patches...${C.RESET}`);
189
- if (await applyFix(res, fixLoc)) {
237
+ if (await applyFix(res, fixLoc, backups)) {
190
238
  count++;
191
239
  console.log(` ${C.GREEN}${C.BRIGHT}āœ… Fix applied to ${path.basename(fixLoc.path)}:${fixLoc.line}${C.RESET}`);
192
240
  } else {
@@ -239,7 +287,7 @@ function checkBalance(code) {
239
287
  return { balanced: true, detail: 'ok' };
240
288
  }
241
289
 
242
- async function applyFix(res, loc) {
290
+ async function applyFix(res, loc, backups = new Map()) {
243
291
  if (!loc?.path) {
244
292
  console.log(` ${C.YELLOW}āš ļø Cannot apply fix: failure location not detected.${C.RESET}`);
245
293
  return false;
@@ -251,6 +299,10 @@ async function applyFix(res, loc) {
251
299
  return false;
252
300
  }
253
301
  const original = fs.readFileSync(loc.path, 'utf8');
302
+ // Save backup before first modification
303
+ if (!backups.has(loc.path)) {
304
+ backups.set(loc.path, original);
305
+ }
254
306
  let content = original;
255
307
  let appliedCount = 0;
256
308
  for (const p of patches) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deflake",
3
- "version": "1.2.37",
3
+ "version": "1.2.38",
4
4
  "description": "AI-powered self-healing tool for Playwright, Cypress, and WebdriverIO tests.",
5
5
  "main": "client.js",
6
6
  "bin": {