deflake 1.2.32 → 1.2.34

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 +30 -43
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -117,7 +117,7 @@ async function analyzeAndFix(artifacts, client, argv, capturedOutput = '') {
117
117
  }
118
118
 
119
119
  // Strip ANSI escape codes from captured output — they break regex matching
120
- const cleanOutput = capturedOutput.replace(/\x1b\[[0-9;]*m/g, '');
120
+ const cleanOutput = capturedOutput.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
121
121
 
122
122
  console.log(`${C.BRIGHT}🔍 Analyzing ${artifacts.length} failure(s)...${C.RESET}\n`);
123
123
  let count = 0;
@@ -261,13 +261,9 @@ async function applyFix(res, loc) {
261
261
  }
262
262
 
263
263
  function extractRelevantOutput(fullOutput, artifactName) {
264
- // SMART: Use Playwright's "Error Context:" line to find the EXACT error block for this artifact.
265
- // Each Playwright failure ends with: "Error Context: test-results/<artifact-name>/error-context.md"
266
- // We find that line, then walk backwards to capture the full error + stack trace.
267
-
264
+ // Find the Error Context line for this artifact, then grab 30 lines before it
268
265
  const lines = fullOutput.split('\n');
269
266
 
270
- // Find the Error Context line that matches this artifact
271
267
  let anchorIdx = -1;
272
268
  for (let i = 0; i < lines.length; i++) {
273
269
  if (lines[i].includes('Error Context:') && lines[i].includes(artifactName)) {
@@ -277,7 +273,7 @@ function extractRelevantOutput(fullOutput, artifactName) {
277
273
  }
278
274
 
279
275
  if (anchorIdx === -1) {
280
- // Fallback: try partial match on artifact name parts
276
+ // Fallback: partial match on artifact name parts
281
277
  const nameParts = artifactName.split('-').filter(p => p.length > 3);
282
278
  for (let i = 0; i < lines.length; i++) {
283
279
  if (lines[i].includes('Error Context:') && nameParts.some(part => lines[i].includes(part))) {
@@ -289,16 +285,8 @@ function extractRelevantOutput(fullOutput, artifactName) {
289
285
 
290
286
  if (anchorIdx === -1) return '';
291
287
 
292
- // Walk backwards from anchor to find the start of this error block
293
- // Error blocks in Playwright start with "N) [chromium] › tests/..."
294
- let startIdx = anchorIdx;
295
- for (let i = anchorIdx; i >= 0; i--) {
296
- if (lines[i].match(/^\s*\d+\)\s+\[/)) {
297
- startIdx = i;
298
- break;
299
- }
300
- }
301
-
288
+ // Grab 30 lines before the anchor — guaranteed to include the full stack trace
289
+ const startIdx = Math.max(0, anchorIdx - 30);
302
290
  return lines.slice(startIdx, anchorIdx + 1).join('\n');
303
291
  }
304
292
 
@@ -306,36 +294,35 @@ function extractLoc(text) {
306
294
  if (!text) return null;
307
295
 
308
296
  // === MULTI-FRAMEWORK LOCATION EXTRACTION ===
309
- // Supports: Playwright, Cypress, WebdriverIO, Jest, Mocha
310
-
311
- // Strategy 1: Absolute path inside parentheses (most precise)
312
- // Format: at FunctionName (/absolute/path/file.ts:75:35)
313
- let m = text.match(/\(([^()]*?[\\/][^()]+\.(?:ts|js|tsx|jsx|mjs|cjs)):(\d+)(?::(\d+))?\)/);
314
- if (m) return { path: path.resolve(m[1]), line: parseInt(m[2]) };
297
+ // Returns the LAST match (closest to the Error Context anchor)
298
+ // to avoid picking up stack traces from previous error blocks.
315
299
 
316
- // Strategy 2: Absolute path after "at" keyword (no parens)
317
- // Format: at /absolute/path/file.ts:75:35
318
- m = text.match(/at\s+(\/[^\s]+\.(?:ts|js|tsx|jsx)):(\d+)(?::(\d+))?/);
319
- if (m) return { path: path.resolve(m[1]), line: parseInt(m[2]) };
320
-
321
- // Strategy 3: Relative path after "at" keyword
322
- // Format: at ../pages/file.ts:75
323
- m = text.match(/at\s+(\.\.?\/[^\s]+\.(?:ts|js|tsx|jsx)):(\d+)(?::(\d+))?/);
324
- if (m) return { path: path.resolve(m[1]), line: parseInt(m[2]) };
300
+ const patterns = [
301
+ // Strategy 1: Absolute path in parens — at Func (/path/file.ts:75:35)
302
+ /\(([^()]*?[\\/][^()]+\.(?:ts|js|tsx|jsx|mjs|cjs)):(\d+)(?::(\d+))?\)/g,
303
+ // Strategy 2: Absolute path — at /path/file.ts:75:35
304
+ /at\s+(\/[^\s]+\.(?:ts|js|tsx|jsx)):(\d+)(?::(\d+))?/g,
305
+ // Strategy 3: Relative path at ../pages/file.ts:75
306
+ /at\s+(\.\.?\/[^\s]+\.(?:ts|js|tsx|jsx)):(\d+)(?::(\d+))?/g,
307
+ ];
325
308
 
326
- // Strategy 4: Playwright "at file:line" in stack (single line match)
327
- m = text.match(/^\s+at\s+(.*?\.(?:ts|js)):(\d+)$/m);
328
- if (m) return { path: path.resolve(m[1]), line: parseInt(m[2]) };
329
-
330
- // Strategy 5: Cypress at Context.<anonymous> (path)
331
- m = text.match(/Context\.<anonymous>\s*\(([^)]+\.(?:ts|js|cy\.ts|cy\.js)):(\d+):(\d+)\)/);
332
- if (m) return { path: path.resolve(m[1]), line: parseInt(m[2]) };
309
+ let lastMatch = null;
310
+ for (const pattern of patterns) {
311
+ let m;
312
+ while ((m = pattern.exec(text)) !== null) {
313
+ lastMatch = { path: path.resolve(m[1]), line: parseInt(m[2]) };
314
+ }
315
+ if (lastMatch) return lastMatch;
316
+ }
333
317
 
334
- // Strategy 6: Generic — any path-like string with /dir/file.ts:line
335
- m = text.match(/((?:[\w@.-]+\/)+[\w.-]+\.(?:ts|js|tsx|jsx)):(\d+)/);
336
- if (m) return { path: path.resolve(m[1]), line: parseInt(m[2]) };
318
+ // Strategy 4: Generic — any path-like /dir/file.ts:line
319
+ const generic = /((?:[\w@.-]+\/)+[\w.-]+\.(?:ts|js|tsx|jsx)):(\d+)/g;
320
+ let m;
321
+ while ((m = generic.exec(text)) !== null) {
322
+ lastMatch = { path: path.resolve(m[1]), line: parseInt(m[2]) };
323
+ }
337
324
 
338
- return null;
325
+ return lastMatch;
339
326
  }
340
327
 
341
328
  async function runDoctor() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deflake",
3
- "version": "1.2.32",
3
+ "version": "1.2.34",
4
4
  "description": "AI-powered self-healing tool for Playwright, Cypress, and WebdriverIO tests.",
5
5
  "main": "client.js",
6
6
  "bin": {