deflake 1.2.8 → 1.2.12

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 +55 -23
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -715,7 +715,6 @@ async function runDoctor(argv) {
715
715
  }
716
716
 
717
717
  async function applySelfHealing(result) {
718
-
719
718
  if (!result.location || !result.location.fullRootPath || !result.fix) return;
720
719
 
721
720
  try {
@@ -727,28 +726,55 @@ async function applySelfHealing(result) {
727
726
  return;
728
727
  }
729
728
 
730
- let fixCode = result.fix;
729
+ let patches = [];
731
730
  try {
732
731
  const parsed = JSON.parse(result.fix);
733
- if (parsed.code) fixCode = parsed.code;
734
- } catch (e) { }
732
+ if (parsed.patches && Array.isArray(parsed.patches)) {
733
+ patches = parsed.patches;
734
+ } else if (parsed.code) {
735
+ patches = [{
736
+ file: filePath,
737
+ line: targetLine,
738
+ action: 'REPLACE',
739
+ new_line: parsed.code
740
+ }];
741
+ }
742
+ } catch (e) {
743
+ // Fallback for raw string fixes
744
+ patches = [{
745
+ file: filePath,
746
+ line: targetLine,
747
+ action: 'REPLACE',
748
+ new_line: result.fix
749
+ }];
750
+ }
735
751
 
736
- const lines = fs.readFileSync(filePath, 'utf8').split('\n');
737
- const originalLineIndex = targetLine - 1;
752
+ for (const patch of patches) {
753
+ const pFile = patch.file || filePath;
754
+ const pLine = parseInt(patch.line || targetLine);
755
+ const pAction = patch.action || 'REPLACE';
756
+ let pNew = patch.new_line;
738
757
 
739
- if (originalLineIndex < 0 || originalLineIndex >= lines.length) {
740
- console.error(` ❌ [Self-Healing] Line ${targetLine} out of bounds in ${filePath}`);
741
- return;
742
- }
758
+ if (!fs.existsSync(pFile)) continue;
759
+
760
+ const lines = fs.readFileSync(pFile, 'utf8').split('\n');
761
+ const originalLineIndex = pLine - 1;
743
762
 
744
- // Apply fix: Replace the entire line or the specific locator
745
- // For robustness, we replace the whole line but keep indentation
746
- const originalLine = lines[originalLineIndex];
747
- const indentation = originalLine.match(/^\s*/)[0];
748
- lines[originalLineIndex] = indentation + fixCode.trim();
763
+ if (originalLineIndex < 0 || originalLineIndex >= lines.length) continue;
749
764
 
750
- fs.writeFileSync(filePath, lines.join('\n'));
751
- console.log(` ✅ ${C.GREEN}[Self-Healing] Successfully patched:${C.RESET} ${path.basename(filePath)}:${targetLine}`);
765
+ const originalLine = lines[originalLineIndex];
766
+ const indentation = originalLine.match(/^\s*/)[0];
767
+
768
+ if (pAction === 'INSERT_AFTER') {
769
+ lines.splice(pLine, 0, indentation + pNew.trim());
770
+ fs.writeFileSync(pFile, lines.join('\n'));
771
+ console.log(` ✅ ${C.GREEN}[Self-Healing] Successfully inserted at:${C.RESET} ${path.basename(pFile)}:${pLine}`);
772
+ } else {
773
+ lines[originalLineIndex] = indentation + pNew.trim();
774
+ fs.writeFileSync(pFile, lines.join('\n'));
775
+ console.log(` ✅ ${C.GREEN}[Self-Healing] Successfully patched:${C.RESET} ${path.basename(pFile)}:${pLine}`);
776
+ }
777
+ }
752
778
  } catch (error) {
753
779
  console.error(` ❌ ${C.RED}[Self-Healing] Error patching file:${C.RESET} ${error.message}`);
754
780
  }
@@ -956,10 +982,6 @@ async function analyzeFailures(artifacts, fullLog, client) {
956
982
  console.log(`\n${C.BRIGHT}💡 Tip: Use ${C.CYAN}--fix${C.RESET}${C.BRIGHT} to automatically apply these suggested fixes next time.${C.RESET}`);
957
983
  }
958
984
 
959
- // TRIGGER REPORT
960
- if (results.length > 0 && argv.fix && argv.report) {
961
- showFrameworkReport();
962
- }
963
985
 
964
986
  return results.length;
965
987
  }
@@ -985,6 +1007,7 @@ async function runCommand(cmd, args) {
985
1007
 
986
1008
  async function main() {
987
1009
  const command = argv._ || [];
1010
+ let finalExitCode = 0;
988
1011
 
989
1012
  // If 'doctor' or 'migrate' was called, don't proceed to wrapper logic
990
1013
  if (command.includes('doctor') || command.includes('migrate')) return;
@@ -1005,26 +1028,35 @@ async function main() {
1005
1028
  const artifacts = detectAllArtifacts(null, argv.html);
1006
1029
  const fixesApplied = await analyzeFailures(artifacts, output, client);
1007
1030
 
1031
+ let outcomeCode = code;
1008
1032
  // AUTO-VERIFICATION
1009
1033
  if (fixesApplied > 0 && argv.fix) {
1010
1034
  console.log(`\n${C.BRIGHT}💉 Fixes applied. Re-running tests to verify...${C.RESET}`);
1011
1035
  const secondRun = await runCommand(cmd, args);
1036
+ outcomeCode = secondRun.code;
1012
1037
  if (secondRun.code === 0) {
1013
1038
  console.log(`\n${C.GREEN}${C.BRIGHT}✅ All tests passed after DeFlake healing!${C.RESET}`);
1014
1039
  } else {
1015
1040
  console.log(`\n${C.YELLOW}⚠️ Some tests still failing after fixes. Check the report for details.${C.RESET}`);
1016
1041
  }
1017
1042
  }
1018
- process.exit(code);
1043
+ finalExitCode = outcomeCode;
1019
1044
  } else {
1020
1045
  console.log("\n🟢 Command passed successfully.");
1021
- process.exit(0);
1046
+ finalExitCode = 0;
1022
1047
  }
1023
1048
  } else {
1024
1049
  const artifacts = detectAllArtifacts(argv.log, argv.html);
1025
1050
  const fullLog = argv.log && fs.existsSync(argv.log) ? fs.readFileSync(argv.log, 'utf8') : null;
1026
1051
  await analyzeFailures(artifacts, fullLog, client);
1027
1052
  }
1053
+
1054
+ // FINAL REPORT TRIGGER
1055
+ if (argv.report) {
1056
+ showFrameworkReport();
1057
+ }
1058
+
1059
+ process.exit(finalExitCode);
1028
1060
  }
1029
1061
 
1030
1062
  // main() call is now handled by the check above
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deflake",
3
- "version": "1.2.8",
3
+ "version": "1.2.12",
4
4
  "description": "AI-powered self-healing tool for Playwright, Cypress, and WebdriverIO tests.",
5
5
  "main": "client.js",
6
6
  "bin": {