deflake 1.2.47 → 1.2.49

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 +25 -3
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -375,6 +375,7 @@ async function analyzeAndFix(artifacts, client, argv, capturedOutput = '', backu
375
375
 
376
376
  console.log(`${C.BRIGHT}🔍 Analyzing ${artifacts.length} failure(s)...${C.RESET}\n`);
377
377
  let count = 0;
378
+ const fixedFiles = new Set(); // Dedup: track files already fixed to avoid cascading mutations
378
379
  for (let i = 0; i < artifacts.length; i++) {
379
380
  const art = artifacts[i];
380
381
  console.log(`${C.CYAN}━━━ [${i+1}/${artifacts.length}] ${art.name} ━━━${C.RESET}`);
@@ -404,6 +405,14 @@ async function analyzeAndFix(artifacts, client, argv, capturedOutput = '', backu
404
405
  // Step 1: Location extraction
405
406
  if (loc) {
406
407
  console.log(` ${C.GRAY}📍 Location:${C.RESET} ${path.basename(loc.path)}:${loc.line} ${C.GRAY}(from ${locSource})${C.RESET}`);
408
+
409
+ // Dedup: skip if this EXACT file:line was already fixed by a previous artifact
410
+ const fileKey = `${loc.path}:${loc.line}`;
411
+ if (fixedFiles.has(fileKey)) {
412
+ console.log(` ${C.GREEN}⏭️ Already fixed by a previous artifact — skipping duplicate${C.RESET}`);
413
+ console.log('');
414
+ continue;
415
+ }
407
416
  } else {
408
417
  console.log(` ${C.YELLOW}📍 Location: Could not extract from any source${C.RESET}`);
409
418
  }
@@ -455,9 +464,16 @@ async function analyzeAndFix(artifacts, client, argv, capturedOutput = '', backu
455
464
  if (p.new_line && p.action === 'REPLACE') {
456
465
  const validation = validateSelectorAgainstAxTree(p.new_line, content);
457
466
  if (!validation.valid) {
458
- allValid = false;
459
- rejections.push(`Line ${p.line}: "${p.new_line.trim()}" REJECTED: ${validation.reason}`);
460
- console.log(` ${C.YELLOW}🔍 Validation FAILED for line ${p.line}: ${validation.reason}${C.RESET}`);
467
+ // On last retry: allow getByRole/getByText through (dynamic elements not in initial AX tree)
468
+ const isLastRetry = retryIdx === MAX_VALIDATION_RETRIES - 1;
469
+ const isAccessibilitySelector = /getBy(Role|Text|Label|Placeholder)/.test(p.new_line);
470
+ if (isLastRetry && isAccessibilitySelector) {
471
+ console.log(` ${C.YELLOW}🔍 Validation SOFT-PASS for line ${p.line}: ${validation.reason} (last retry, allowing accessibility selector)${C.RESET}`);
472
+ } else {
473
+ allValid = false;
474
+ rejections.push(`Line ${p.line}: "${p.new_line.trim()}" → REJECTED: ${validation.reason}`);
475
+ console.log(` ${C.YELLOW}🔍 Validation FAILED for line ${p.line}: ${validation.reason}${C.RESET}`);
476
+ }
461
477
  } else {
462
478
  console.log(` ${C.GREEN}🔍 Validation OK for line ${p.line}: ${validation.reason}${C.RESET}`);
463
479
  }
@@ -520,6 +536,7 @@ async function analyzeAndFix(artifacts, client, argv, capturedOutput = '', backu
520
536
  console.log(` ${C.BRIGHT}💉 Applying patches...${C.RESET}`);
521
537
  if (await applyFix(res, fixLoc, backups)) {
522
538
  count++;
539
+ fixedFiles.add(`${fixLoc.path}:${fixLoc.line}`); // Track: don't fix this exact location again
523
540
  console.log(` ${C.GREEN}${C.BRIGHT}✅ Fix applied to ${path.basename(fixLoc.path)}:${fixLoc.line}${C.RESET}`);
524
541
  } else {
525
542
  console.log(` ${C.YELLOW}⚠️ Patches could not be applied (see details above)${C.RESET}`);
@@ -601,6 +618,11 @@ async function applyFix(res, loc, backups = new Map()) {
601
618
  console.log(` ${C.GRAY}⏭️ Skipped (already present): ${p.new_line.trim().substring(0, 60)}...${C.RESET}`);
602
619
  continue;
603
620
  }
621
+ // Safety: block constructor patterns (this.xxx =) from being inserted into spec/test files
622
+ if (/\.(spec|test)\.(ts|js)$/.test(filePath) && /^\s*this\.\w+\s*=/.test(p.new_line)) {
623
+ console.log(` ${C.YELLOW}⏭️ Blocked: constructor assignment in spec file (${p.new_line.trim().substring(0, 50)})${C.RESET}`);
624
+ continue;
625
+ }
604
626
  if (p.action === 'INSERT_AFTER') {
605
627
  // SAFETY: Detect if we'd insert at class scope (outside any method)
606
628
  // Pattern: line is '}' (end of constructor/method) and next non-blank line starts a new method
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deflake",
3
- "version": "1.2.47",
3
+ "version": "1.2.49",
4
4
  "description": "AI-powered self-healing tool for Playwright, Cypress, and WebdriverIO tests.",
5
5
  "main": "client.js",
6
6
  "bin": {