@vibe-validate/extractors 0.17.4 → 0.17.5-rc.1
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.
- package/dist/extractor-registry.d.ts.map +1 -1
- package/dist/extractor-registry.js +8 -8
- package/dist/extractor-registry.js.map +1 -1
- package/dist/extractors/ava/index.d.ts.map +1 -1
- package/dist/extractors/ava/index.js +15 -14
- package/dist/extractors/ava/index.js.map +1 -1
- package/dist/extractors/ava/index.test.js +30 -20
- package/dist/extractors/ava/index.test.js.map +1 -1
- package/dist/extractors/eslint/index.d.ts.map +1 -1
- package/dist/extractors/eslint/index.js +1 -1
- package/dist/extractors/eslint/index.js.map +1 -1
- package/dist/extractors/eslint/index.test.js.map +1 -1
- package/dist/extractors/generic/index.test.js.map +1 -1
- package/dist/extractors/jasmine/index.test.js.map +1 -1
- package/dist/extractors/jest/index.d.ts.map +1 -1
- package/dist/extractors/jest/index.js.map +1 -1
- package/dist/extractors/jest/index.test.js.map +1 -1
- package/dist/extractors/junit/index.d.ts.map +1 -1
- package/dist/extractors/junit/index.js +5 -5
- package/dist/extractors/junit/index.js.map +1 -1
- package/dist/extractors/junit/index.test.js.map +1 -1
- package/dist/extractors/maven-checkstyle/index.d.ts.map +1 -1
- package/dist/extractors/maven-checkstyle/index.js +1 -1
- package/dist/extractors/maven-checkstyle/index.js.map +1 -1
- package/dist/extractors/maven-checkstyle/index.test.js +2 -2
- package/dist/extractors/maven-checkstyle/index.test.js.map +1 -1
- package/dist/extractors/maven-compiler/index.d.ts.map +1 -1
- package/dist/extractors/maven-compiler/index.js +1 -1
- package/dist/extractors/maven-compiler/index.js.map +1 -1
- package/dist/extractors/maven-compiler/index.test.js +1 -1
- package/dist/extractors/maven-compiler/index.test.js.map +1 -1
- package/dist/extractors/maven-surefire/index.d.ts.map +1 -1
- package/dist/extractors/maven-surefire/index.js.map +1 -1
- package/dist/extractors/maven-surefire/index.test.js.map +1 -1
- package/dist/extractors/mocha/index.test.js.map +1 -1
- package/dist/extractors/playwright/index.test.js.map +1 -1
- package/dist/extractors/tap/index.test.js +2 -2
- package/dist/extractors/tap/index.test.js.map +1 -1
- package/dist/extractors/typescript/index.d.ts.map +1 -1
- package/dist/extractors/typescript/index.js +16 -19
- package/dist/extractors/typescript/index.js.map +1 -1
- package/dist/extractors/typescript/index.test.js.map +1 -1
- package/dist/extractors/vitest/index.d.ts.map +1 -1
- package/dist/extractors/vitest/index.js +16 -7
- package/dist/extractors/vitest/index.js.map +1 -1
- package/dist/extractors/vitest/index.test.js +38 -0
- package/dist/extractors/vitest/index.test.js.map +1 -1
- package/dist/plugin-loader.d.ts.map +1 -1
- package/dist/plugin-loader.js +1 -1
- package/dist/plugin-loader.js.map +1 -1
- package/dist/result-schema-export.d.ts.map +1 -1
- package/dist/result-schema-export.js.map +1 -1
- package/dist/result-schema.d.ts.map +1 -1
- package/dist/result-schema.js +1 -1
- package/dist/result-schema.js.map +1 -1
- package/dist/sandboxed-extractor.d.ts +1 -1
- package/dist/sandboxed-extractor.d.ts.map +1 -1
- package/dist/sandboxed-extractor.js.map +1 -1
- package/dist/scripts/generate-result-schema.js +1 -1
- package/dist/scripts/generate-result-schema.js.map +1 -1
- package/dist/smart-extractor.d.ts.map +1 -1
- package/dist/smart-extractor.js.map +1 -1
- package/dist/utils/guidance-generator.d.ts.map +1 -1
- package/dist/utils/guidance-generator.js +4 -7
- package/dist/utils/guidance-generator.js.map +1 -1
- package/dist/utils/test-framework-utils.d.ts.map +1 -1
- package/dist/utils/test-framework-utils.js.map +1 -1
- package/dist/utils.js +1 -1
- package/dist/utils.js.map +1 -1
- package/package.json +8 -8
- package/dist/ava-extractor.d.ts +0 -24
- package/dist/ava-extractor.d.ts.map +0 -1
- package/dist/ava-extractor.js +0 -343
- package/dist/ava-extractor.js.map +0 -1
- package/dist/eslint-extractor.d.ts +0 -25
- package/dist/eslint-extractor.d.ts.map +0 -1
- package/dist/eslint-extractor.js +0 -145
- package/dist/eslint-extractor.js.map +0 -1
- package/dist/generic-extractor.d.ts +0 -35
- package/dist/generic-extractor.d.ts.map +0 -1
- package/dist/generic-extractor.js +0 -128
- package/dist/generic-extractor.js.map +0 -1
- package/dist/jasmine-extractor.d.ts +0 -23
- package/dist/jasmine-extractor.d.ts.map +0 -1
- package/dist/jasmine-extractor.js +0 -151
- package/dist/jasmine-extractor.js.map +0 -1
- package/dist/jest-extractor.d.ts +0 -29
- package/dist/jest-extractor.d.ts.map +0 -1
- package/dist/jest-extractor.js +0 -174
- package/dist/jest-extractor.js.map +0 -1
- package/dist/junit-extractor.d.ts +0 -24
- package/dist/junit-extractor.d.ts.map +0 -1
- package/dist/junit-extractor.js +0 -193
- package/dist/junit-extractor.js.map +0 -1
- package/dist/maven-checkstyle-extractor.d.ts +0 -20
- package/dist/maven-checkstyle-extractor.d.ts.map +0 -1
- package/dist/maven-checkstyle-extractor.js +0 -208
- package/dist/maven-checkstyle-extractor.js.map +0 -1
- package/dist/maven-compiler-extractor.d.ts +0 -20
- package/dist/maven-compiler-extractor.d.ts.map +0 -1
- package/dist/maven-compiler-extractor.js +0 -218
- package/dist/maven-compiler-extractor.js.map +0 -1
- package/dist/maven-surefire-extractor.d.ts +0 -20
- package/dist/maven-surefire-extractor.d.ts.map +0 -1
- package/dist/maven-surefire-extractor.js +0 -228
- package/dist/maven-surefire-extractor.js.map +0 -1
- package/dist/mocha-extractor.d.ts +0 -23
- package/dist/mocha-extractor.d.ts.map +0 -1
- package/dist/mocha-extractor.js +0 -160
- package/dist/mocha-extractor.js.map +0 -1
- package/dist/playwright-extractor.d.ts +0 -38
- package/dist/playwright-extractor.d.ts.map +0 -1
- package/dist/playwright-extractor.js +0 -239
- package/dist/playwright-extractor.js.map +0 -1
- package/dist/sandbox.test.d.ts +0 -8
- package/dist/sandbox.test.d.ts.map +0 -1
- package/dist/sandbox.test.js +0 -395
- package/dist/sandbox.test.js.map +0 -1
- package/dist/sandboxed-extractor.test.d.ts +0 -5
- package/dist/sandboxed-extractor.test.d.ts.map +0 -1
- package/dist/sandboxed-extractor.test.js +0 -346
- package/dist/sandboxed-extractor.test.js.map +0 -1
- package/dist/tap-extractor.d.ts +0 -24
- package/dist/tap-extractor.d.ts.map +0 -1
- package/dist/tap-extractor.js +0 -217
- package/dist/tap-extractor.js.map +0 -1
- package/dist/typescript-extractor.d.ts +0 -25
- package/dist/typescript-extractor.d.ts.map +0 -1
- package/dist/typescript-extractor.js +0 -96
- package/dist/typescript-extractor.js.map +0 -1
- package/dist/vitest-extractor.d.ts +0 -38
- package/dist/vitest-extractor.d.ts.map +0 -1
- package/dist/vitest-extractor.js +0 -540
- package/dist/vitest-extractor.js.map +0 -1
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TypeScript Error Extractor
|
|
3
|
-
*
|
|
4
|
-
* Parses and formats TypeScript compiler (tsc) error output for LLM consumption.
|
|
5
|
-
*
|
|
6
|
-
* @package @vibe-validate/extractors
|
|
7
|
-
*/
|
|
8
|
-
import { MAX_ERRORS_IN_ARRAY } from './result-schema.js';
|
|
9
|
-
/**
|
|
10
|
-
* Format TypeScript compiler errors
|
|
11
|
-
*
|
|
12
|
-
* Parses tsc output format: `file(line,col): error TSxxxx: message`
|
|
13
|
-
*
|
|
14
|
-
* @param output - Raw tsc command output
|
|
15
|
-
* @returns Structured error information with TypeScript-specific guidance
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```typescript
|
|
19
|
-
* const result = extractTypeScriptErrors(tscOutput);
|
|
20
|
-
* console.log(result.summary); // "3 type error(s), 0 warning(s)"
|
|
21
|
-
* console.log(result.guidance); // "Type mismatch - check variable/parameter types"
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
export function extractTypeScriptErrors(output) {
|
|
25
|
-
const errors = [];
|
|
26
|
-
// TypeScript error patterns - support both old and new formats:
|
|
27
|
-
// Old: file(line,col): error TSxxxx: message
|
|
28
|
-
// New: file:line:col - error TSxxxx: message
|
|
29
|
-
// eslint-disable-next-line sonarjs/slow-regex -- Safe: only parses TypeScript compiler output (controlled tool output), not user input
|
|
30
|
-
const oldPattern = /^(.+?)\((\d+),(\d+)\):\s*(error|warning)\s+(TS\d+):\s+(.+)$/gm;
|
|
31
|
-
// eslint-disable-next-line sonarjs/slow-regex -- Safe: only parses TypeScript compiler output (controlled tool output), not user input
|
|
32
|
-
const newPattern = /^(.+?):(\d+):(\d+)\s+-\s*(error|warning)\s+(TS\d+):\s+(.+)$/gm;
|
|
33
|
-
// Try new format first (more common in modern tsc)
|
|
34
|
-
let match;
|
|
35
|
-
while ((match = newPattern.exec(output)) !== null) {
|
|
36
|
-
errors.push({
|
|
37
|
-
file: match[1].trim(),
|
|
38
|
-
line: Number.parseInt(match[2]),
|
|
39
|
-
column: Number.parseInt(match[3]),
|
|
40
|
-
severity: match[4],
|
|
41
|
-
code: match[5],
|
|
42
|
-
message: match[6].trim()
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
// Try old format if no matches yet
|
|
46
|
-
if (errors.length === 0) {
|
|
47
|
-
while ((match = oldPattern.exec(output)) !== null) {
|
|
48
|
-
errors.push({
|
|
49
|
-
file: match[1].trim(),
|
|
50
|
-
line: Number.parseInt(match[2]),
|
|
51
|
-
column: Number.parseInt(match[3]),
|
|
52
|
-
severity: match[4],
|
|
53
|
-
code: match[5],
|
|
54
|
-
message: match[6].trim()
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
const errorCount = errors.filter(e => e.severity === 'error').length;
|
|
59
|
-
const warningCount = errors.filter(e => e.severity === 'warning').length;
|
|
60
|
-
// Build error summary (limit to MAX_ERRORS_IN_ARRAY for token efficiency)
|
|
61
|
-
const errorSummary = errors
|
|
62
|
-
.slice(0, MAX_ERRORS_IN_ARRAY)
|
|
63
|
-
.map(e => `${e.file}:${e.line}:${e.column} - ${e.code}: ${e.message}`)
|
|
64
|
-
.join('\n');
|
|
65
|
-
return {
|
|
66
|
-
errors: errors.slice(0, MAX_ERRORS_IN_ARRAY),
|
|
67
|
-
summary: `${errorCount} type error(s), ${warningCount} warning(s)`,
|
|
68
|
-
totalErrors: errors.length,
|
|
69
|
-
guidance: getTypeScriptGuidance(errors),
|
|
70
|
-
errorSummary
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Generate TypeScript-specific guidance based on error codes
|
|
75
|
-
*
|
|
76
|
-
* @param errors - Parsed TypeScript errors
|
|
77
|
-
* @returns Actionable guidance string
|
|
78
|
-
*/
|
|
79
|
-
function getTypeScriptGuidance(errors) {
|
|
80
|
-
const errorCodes = new Set(errors.map(e => e.code));
|
|
81
|
-
const guidance = [];
|
|
82
|
-
if (errorCodes.has('TS2322')) {
|
|
83
|
-
guidance.push('Type mismatch - check variable/parameter types');
|
|
84
|
-
}
|
|
85
|
-
if (errorCodes.has('TS2304')) {
|
|
86
|
-
guidance.push('Cannot find name - check imports and type definitions');
|
|
87
|
-
}
|
|
88
|
-
if (errorCodes.has('TS2345')) {
|
|
89
|
-
guidance.push('Argument type mismatch - check function signatures');
|
|
90
|
-
}
|
|
91
|
-
if (guidance.length === 0) {
|
|
92
|
-
return 'Fix TypeScript type errors in listed files';
|
|
93
|
-
}
|
|
94
|
-
return guidance.join('. ');
|
|
95
|
-
}
|
|
96
|
-
//# sourceMappingURL=typescript-extractor.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"typescript-extractor.js","sourceRoot":"","sources":["../src/typescript-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vitest/Jest Error Extractor
|
|
3
|
-
*
|
|
4
|
-
* Parses and formats Vitest (and Jest) test failure output for LLM consumption.
|
|
5
|
-
*
|
|
6
|
-
* @package @vibe-validate/extractors
|
|
7
|
-
*/
|
|
8
|
-
import type { ErrorExtractorResult } from './types.js';
|
|
9
|
-
/**
|
|
10
|
-
* Options for error extraction
|
|
11
|
-
*/
|
|
12
|
-
export interface ExtractorOptions {
|
|
13
|
-
/** Include quality metadata and suggestions for improving extraction */
|
|
14
|
-
developerFeedback?: boolean;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Format Vitest test failures
|
|
18
|
-
*
|
|
19
|
-
* Extracts:
|
|
20
|
-
* - Test file and location (file:line:column)
|
|
21
|
-
* - Test hierarchy (describe blocks > test name)
|
|
22
|
-
* - Assertion error message
|
|
23
|
-
* - Source code line that failed
|
|
24
|
-
* - Expected vs actual values (when available)
|
|
25
|
-
*
|
|
26
|
-
* @param output - Raw Vitest/Jest command output
|
|
27
|
-
* @param options - Extraction options (e.g., developerFeedback for quality metadata)
|
|
28
|
-
* @returns Structured error information with test-specific guidance
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* ```typescript
|
|
32
|
-
* const result = extractVitestErrors(vitestOutput);
|
|
33
|
-
* console.log(result.summary); // "3 test failure(s)"
|
|
34
|
-
* console.log(result.guidance); // "Fix each failing test individually..."
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
|
-
export declare function extractVitestErrors(output: string, options?: ExtractorOptions): ErrorExtractorResult;
|
|
38
|
-
//# sourceMappingURL=vitest-extractor.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vitest-extractor.d.ts","sourceRoot":"","sources":["../src/vitest-extractor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAsB,MAAM,YAAY,CAAC;AAG3E;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAmZD;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,gBAAgB,GACzB,oBAAoB,CA8HtB"}
|
package/dist/vitest-extractor.js
DELETED
|
@@ -1,540 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vitest/Jest Error Extractor
|
|
3
|
-
*
|
|
4
|
-
* Parses and formats Vitest (and Jest) test failure output for LLM consumption.
|
|
5
|
-
*
|
|
6
|
-
* @package @vibe-validate/extractors
|
|
7
|
-
*/
|
|
8
|
-
import { MAX_ERRORS_IN_ARRAY } from './result-schema.js';
|
|
9
|
-
/**
|
|
10
|
-
* Parse failure line to determine format and extract initial data
|
|
11
|
-
*
|
|
12
|
-
* @param line - Current line being parsed
|
|
13
|
-
* @param currentFile - File path from Format 2 header
|
|
14
|
-
* @param hasFormat2 - Whether Format 2 has been detected
|
|
15
|
-
* @returns Partial failure object or null if no match
|
|
16
|
-
*
|
|
17
|
-
* @internal
|
|
18
|
-
*/
|
|
19
|
-
function parseFailureLine(line, currentFile, hasFormat2) {
|
|
20
|
-
// Match Format 1: FAIL file.test.ts > test hierarchy
|
|
21
|
-
// BUT: Skip if we've seen Format 2 headers (to avoid processing duplicate FAIL lines)
|
|
22
|
-
const format1Match = !hasFormat2 && /(?:FAIL|❌|×)\s+([^\s]+\.test\.ts)\s*>\s*(.+)/.exec(line);
|
|
23
|
-
if (format1Match) {
|
|
24
|
-
return {
|
|
25
|
-
file: format1Match[1],
|
|
26
|
-
testHierarchy: format1Match[2].trim(),
|
|
27
|
-
errorMessage: '',
|
|
28
|
-
sourceLine: '',
|
|
29
|
-
location: ''
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
// Match Format 2: × test hierarchy (without file path)
|
|
33
|
-
// eslint-disable-next-line sonarjs/slow-regex -- Safe: only parses Vitest test framework output (controlled output), limited line length
|
|
34
|
-
const format2Match = /(?:×)\s+(.+?)(?:\s+\d+ms)?$/.exec(line);
|
|
35
|
-
if (format2Match && currentFile) {
|
|
36
|
-
return {
|
|
37
|
-
file: currentFile,
|
|
38
|
-
testHierarchy: format2Match[1].trim(),
|
|
39
|
-
errorMessage: '',
|
|
40
|
-
sourceLine: '',
|
|
41
|
-
location: ''
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Extract initial error message from line
|
|
48
|
-
*
|
|
49
|
-
* @param line - Line to extract error from
|
|
50
|
-
* @returns Extracted error message
|
|
51
|
-
*
|
|
52
|
-
* @internal
|
|
53
|
-
*/
|
|
54
|
-
function extractErrorMessage(line) {
|
|
55
|
-
const errorMatch = /((?:AssertionError|Error):\s*.+)/.exec(line);
|
|
56
|
-
if (errorMatch) {
|
|
57
|
-
return errorMatch[1].trim();
|
|
58
|
-
}
|
|
59
|
-
const format2ErrorMatch = /→\s+(.+)/.exec(line);
|
|
60
|
-
if (format2ErrorMatch) {
|
|
61
|
-
return format2ErrorMatch[1].trim();
|
|
62
|
-
}
|
|
63
|
-
return line.trim();
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Check if line is a stop marker for error continuation
|
|
67
|
-
*
|
|
68
|
-
* @param line - Trimmed line to check
|
|
69
|
-
* @returns True if this line marks end of error message
|
|
70
|
-
*
|
|
71
|
-
* @internal
|
|
72
|
-
*/
|
|
73
|
-
function isStopMarker(line) {
|
|
74
|
-
return (line.startsWith('❯') ||
|
|
75
|
-
/^\d+\|/.test(line) ||
|
|
76
|
-
line.startsWith('FAIL') ||
|
|
77
|
-
line.startsWith('✓') ||
|
|
78
|
-
line.startsWith('❌') ||
|
|
79
|
-
line.startsWith('×') ||
|
|
80
|
-
line.startsWith('⎯') ||
|
|
81
|
-
line.startsWith('at '));
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Append continuation line to error message
|
|
85
|
-
*
|
|
86
|
-
* @param errorMessage - Current error message
|
|
87
|
-
* @param line - Raw line (with indentation)
|
|
88
|
-
* @param trimmedLine - Trimmed version of line
|
|
89
|
-
* @param isSnapshotError - Whether handling snapshot error
|
|
90
|
-
* @returns Updated error message
|
|
91
|
-
*
|
|
92
|
-
* @internal
|
|
93
|
-
*/
|
|
94
|
-
function appendContinuationLine(errorMessage, line, trimmedLine, isSnapshotError) {
|
|
95
|
-
if (isSnapshotError) {
|
|
96
|
-
return errorMessage + '\n' + line; // Preserve indentation for diffs
|
|
97
|
-
}
|
|
98
|
-
return errorMessage + ' ' + trimmedLine; // Compact for normal errors
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Handle truncation logic for non-snapshot errors
|
|
102
|
-
*
|
|
103
|
-
* @param errorMessage - Current error message
|
|
104
|
-
* @param nextLine - Next trimmed line
|
|
105
|
-
* @param isSnapshotError - Whether handling snapshot error
|
|
106
|
-
* @param linesConsumed - Number of lines consumed so far
|
|
107
|
-
* @param maxLines - Maximum continuation lines allowed
|
|
108
|
-
* @returns Object with updated message and whether to stop
|
|
109
|
-
*
|
|
110
|
-
* @internal
|
|
111
|
-
*/
|
|
112
|
-
function handleTruncation(errorMessage, nextLine, isSnapshotError, linesConsumed, maxLines) {
|
|
113
|
-
if (isSnapshotError || linesConsumed < maxLines) {
|
|
114
|
-
return { errorMessage, shouldStop: false };
|
|
115
|
-
}
|
|
116
|
-
const updatedMessage = nextLine ? errorMessage + ' ...(truncated)' : errorMessage;
|
|
117
|
-
return { errorMessage: updatedMessage, shouldStop: true };
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Parse error message with continuation line handling
|
|
121
|
-
*
|
|
122
|
-
* @param lines - All output lines
|
|
123
|
-
* @param startIndex - Index where error message starts
|
|
124
|
-
* @param isSnapshotError - Whether this is a snapshot error (different continuation rules)
|
|
125
|
-
* @returns Error message and index of last consumed line
|
|
126
|
-
*
|
|
127
|
-
* @internal
|
|
128
|
-
*/
|
|
129
|
-
function parseErrorMessage(lines, startIndex, isSnapshotError) {
|
|
130
|
-
let errorMessage = extractErrorMessage(lines[startIndex]);
|
|
131
|
-
const MAX_CONTINUATION_LINES = 5;
|
|
132
|
-
let j = startIndex + 1;
|
|
133
|
-
let linesConsumed = 0;
|
|
134
|
-
while (j < lines.length) {
|
|
135
|
-
const nextLine = lines[j].trim();
|
|
136
|
-
if (isStopMarker(nextLine)) {
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
// Check truncation limit for non-snapshot errors
|
|
140
|
-
const truncation = handleTruncation(errorMessage, nextLine, isSnapshotError, linesConsumed, MAX_CONTINUATION_LINES);
|
|
141
|
-
if (truncation.shouldStop) {
|
|
142
|
-
errorMessage = truncation.errorMessage;
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
145
|
-
// For non-snapshot errors, stop at blank lines
|
|
146
|
-
if (!nextLine && !isSnapshotError) {
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
// Add line to error message
|
|
150
|
-
if (nextLine || isSnapshotError) {
|
|
151
|
-
errorMessage = appendContinuationLine(errorMessage, lines[j], nextLine, isSnapshotError);
|
|
152
|
-
if (!isSnapshotError) {
|
|
153
|
-
linesConsumed++;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
j++;
|
|
157
|
-
}
|
|
158
|
-
return { errorMessage, lastIndex: j - 1 };
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Parse location from vitest marker or stack trace
|
|
162
|
-
*
|
|
163
|
-
* @param line - Line to parse
|
|
164
|
-
* @returns Location string (file:line:column) or null
|
|
165
|
-
*
|
|
166
|
-
* @internal
|
|
167
|
-
*/
|
|
168
|
-
function parseLocation(line) {
|
|
169
|
-
// Try vitest location marker first
|
|
170
|
-
// eslint-disable-next-line sonarjs/slow-regex -- Safe: only parses Vitest test framework location markers (controlled output), not user input
|
|
171
|
-
const vitestLocation = /❯\s*(.+\.test\.ts):(\d+):(\d+)/.exec(line);
|
|
172
|
-
if (vitestLocation) {
|
|
173
|
-
return `${vitestLocation[1]}:${vitestLocation[2]}:${vitestLocation[3]}`;
|
|
174
|
-
}
|
|
175
|
-
// Try stack trace pattern
|
|
176
|
-
// eslint-disable-next-line sonarjs/slow-regex -- Safe: only parses Vitest test framework stack traces (controlled output), not user input
|
|
177
|
-
const stackLocation = /at\s+.+\(([^\s]+\.test\.ts):(\d+):(\d+)\)/.exec(line);
|
|
178
|
-
if (stackLocation) {
|
|
179
|
-
return `${stackLocation[1]}:${stackLocation[2]}:${stackLocation[3]}`;
|
|
180
|
-
}
|
|
181
|
-
return null;
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Format failures into clean output
|
|
185
|
-
*
|
|
186
|
-
* @param failures - Extracted test failures
|
|
187
|
-
* @param expected - Expected value (if present)
|
|
188
|
-
* @param actual - Actual value (if present)
|
|
189
|
-
* @returns Formatted output string
|
|
190
|
-
*
|
|
191
|
-
* @internal
|
|
192
|
-
*/
|
|
193
|
-
function formatFailuresOutput(failures, expected, actual) {
|
|
194
|
-
return failures
|
|
195
|
-
.slice(0, MAX_ERRORS_IN_ARRAY)
|
|
196
|
-
.map((f, idx) => {
|
|
197
|
-
const parts = [
|
|
198
|
-
`[Test ${idx + 1}/${failures.length}] ${f.location ?? f.file}`,
|
|
199
|
-
'',
|
|
200
|
-
`Test: ${f.testHierarchy}`,
|
|
201
|
-
`Error: ${f.errorMessage}`,
|
|
202
|
-
];
|
|
203
|
-
if (expected && actual) {
|
|
204
|
-
parts.push(`Expected: ${expected}`, `Actual: ${actual}`);
|
|
205
|
-
}
|
|
206
|
-
if (f.sourceLine) {
|
|
207
|
-
parts.push('', f.sourceLine);
|
|
208
|
-
}
|
|
209
|
-
return parts.filter(p => p).join('\n');
|
|
210
|
-
})
|
|
211
|
-
.join('\n\n');
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* Generate LLM-friendly guidance based on failures
|
|
215
|
-
*
|
|
216
|
-
* @param failureCount - Number of failures
|
|
217
|
-
* @param expected - Expected value (if present)
|
|
218
|
-
* @param actual - Actual value (if present)
|
|
219
|
-
* @param hasTimeout - Whether any failures are due to timeout
|
|
220
|
-
* @returns Guidance text
|
|
221
|
-
*
|
|
222
|
-
* @internal
|
|
223
|
-
*/
|
|
224
|
-
function generateGuidanceText(failureCount, expected, actual, hasTimeout) {
|
|
225
|
-
let guidance = `${failureCount} test(s) failed. `;
|
|
226
|
-
// Timeout-specific guidance
|
|
227
|
-
if (hasTimeout) {
|
|
228
|
-
guidance += 'Test(s) timed out. ';
|
|
229
|
-
if (failureCount === 1) {
|
|
230
|
-
guidance += 'Options: 1) Increase timeout with test.timeout() or testTimeout config, ';
|
|
231
|
-
guidance += '2) Optimize test to run faster, ';
|
|
232
|
-
guidance += '3) Mock slow operations (API calls, file I/O, child processes). ';
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
guidance += 'Multiple tests timing out suggests resource constraints. ';
|
|
236
|
-
guidance += 'Try: 1) Run tests individually to identify slow tests, ';
|
|
237
|
-
guidance += '2) Increase testTimeout config, ';
|
|
238
|
-
guidance += '3) Reduce parallel test workers (--pool-workers=2). ';
|
|
239
|
-
}
|
|
240
|
-
guidance += 'Run: npm test -- <test-file> to verify the fix.';
|
|
241
|
-
return guidance;
|
|
242
|
-
}
|
|
243
|
-
// Standard assertion failure guidance
|
|
244
|
-
if (failureCount === 1) {
|
|
245
|
-
guidance += 'Fix the assertion in the test file at the location shown. ';
|
|
246
|
-
if (expected && actual) {
|
|
247
|
-
guidance += `The test expected "${expected}" but got "${actual}". `;
|
|
248
|
-
}
|
|
249
|
-
guidance += 'Run: npm test -- <test-file> to verify the fix.';
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
guidance += 'Fix each failing test individually. Run: npm test -- <test-file> to test each file.';
|
|
253
|
-
}
|
|
254
|
-
return guidance;
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* Extract runtime errors (Unhandled Rejection, ENOENT, etc.)
|
|
258
|
-
*
|
|
259
|
-
* @param output - Full test output
|
|
260
|
-
* @returns Test failure object if runtime error found
|
|
261
|
-
*/
|
|
262
|
-
function extractRuntimeError(output) {
|
|
263
|
-
// Look for "Unhandled Rejection" section
|
|
264
|
-
// eslint-disable-next-line sonarjs/slow-regex -- Safe: only parses Vitest test framework runtime errors (controlled output), not user input
|
|
265
|
-
const unhandledMatch = /⎯+\s*Unhandled Rejection\s*⎯+\s*\n\s*(Error:[^\n]+(?:\n\s*[^\n❯⎯]+)?)/.exec(output);
|
|
266
|
-
if (!unhandledMatch) {
|
|
267
|
-
return null;
|
|
268
|
-
}
|
|
269
|
-
// Error message may span multiple lines (e.g., path on next line)
|
|
270
|
-
const errorMessage = unhandledMatch[1].trim().replace(/\n\s+/g, ' ');
|
|
271
|
-
// Extract location from stack trace (❯ function file:line:col)
|
|
272
|
-
// File path may contain colons (e.g., node:internal/fs/promises), so match ❯ function filepath:number:number
|
|
273
|
-
const locationMatch = /❯\s+\S+\s+([\w:/.]+):(\d+):(\d+)/.exec(output);
|
|
274
|
-
let file = 'unknown';
|
|
275
|
-
let location = '';
|
|
276
|
-
if (locationMatch) {
|
|
277
|
-
file = locationMatch[1];
|
|
278
|
-
location = `${locationMatch[1]}:${locationMatch[2]}:${locationMatch[3]}`;
|
|
279
|
-
}
|
|
280
|
-
return {
|
|
281
|
-
file,
|
|
282
|
-
location,
|
|
283
|
-
testHierarchy: 'Runtime Error',
|
|
284
|
-
errorMessage,
|
|
285
|
-
sourceLine: ''
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Extract coverage threshold failures
|
|
290
|
-
*
|
|
291
|
-
* @param output - Full test output
|
|
292
|
-
* @returns Test failure object if coverage threshold error found
|
|
293
|
-
*/
|
|
294
|
-
function extractCoverageThresholdError(output) {
|
|
295
|
-
// Look for: "ERROR: Coverage for functions (86.47%) does not meet global threshold (87%)"
|
|
296
|
-
const coverageMatch = /ERROR:\s+Coverage for (\w+) \(([\d.]+)%\) does not meet (?:global )?threshold \(([\d.]+)%\)/.exec(output);
|
|
297
|
-
if (!coverageMatch) {
|
|
298
|
-
return null;
|
|
299
|
-
}
|
|
300
|
-
const [, metric, actual, expected] = coverageMatch;
|
|
301
|
-
return {
|
|
302
|
-
file: 'vitest.config.ts',
|
|
303
|
-
location: '',
|
|
304
|
-
testHierarchy: 'Coverage Threshold',
|
|
305
|
-
errorMessage: `Coverage for ${metric} (${actual}%) does not meet threshold (${expected}%)`,
|
|
306
|
-
sourceLine: ''
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
/**
|
|
310
|
-
* Extract Vitest worker timeout errors
|
|
311
|
-
*
|
|
312
|
-
* These occur when Vitest worker threads timeout during test execution,
|
|
313
|
-
* often due to system resource constraints or competing processes.
|
|
314
|
-
*
|
|
315
|
-
* @param output - Full test output
|
|
316
|
-
* @returns Test failure object if worker timeout error found
|
|
317
|
-
*/
|
|
318
|
-
function extractVitestWorkerTimeoutError(output) {
|
|
319
|
-
// Look for: "⎯⎯⎯⎯⎯⎯ Unhandled Error(s) ⎯⎯⎯⎯⎯⎯⎯\nError: [vitest-worker]: Timeout calling..."
|
|
320
|
-
// Handles both singular "Unhandled Error" and plural "Unhandled Errors"
|
|
321
|
-
// eslint-disable-next-line sonarjs/slow-regex -- Safe: only parses Vitest test framework error output (controlled output), not user input
|
|
322
|
-
const timeoutMatch = /⎯+\s*Unhandled Errors?\s*⎯+\s*\n\s*Error:\s*\[vitest-worker\]:\s*(Timeout[^\n]+)/.exec(output);
|
|
323
|
-
if (!timeoutMatch) {
|
|
324
|
-
return null;
|
|
325
|
-
}
|
|
326
|
-
const errorMessage = timeoutMatch[1].trim();
|
|
327
|
-
return {
|
|
328
|
-
file: 'vitest.config.ts',
|
|
329
|
-
location: '',
|
|
330
|
-
testHierarchy: 'Vitest Worker Timeout',
|
|
331
|
-
errorMessage: `${errorMessage}. This is usually caused by system resource constraints or competing processes. Try: 1) Kill background processes, 2) Reduce --pool-workers, 3) Increase --test-timeout`,
|
|
332
|
-
sourceLine: ''
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* Format Vitest test failures
|
|
337
|
-
*
|
|
338
|
-
* Extracts:
|
|
339
|
-
* - Test file and location (file:line:column)
|
|
340
|
-
* - Test hierarchy (describe blocks > test name)
|
|
341
|
-
* - Assertion error message
|
|
342
|
-
* - Source code line that failed
|
|
343
|
-
* - Expected vs actual values (when available)
|
|
344
|
-
*
|
|
345
|
-
* @param output - Raw Vitest/Jest command output
|
|
346
|
-
* @param options - Extraction options (e.g., developerFeedback for quality metadata)
|
|
347
|
-
* @returns Structured error information with test-specific guidance
|
|
348
|
-
*
|
|
349
|
-
* @example
|
|
350
|
-
* ```typescript
|
|
351
|
-
* const result = extractVitestErrors(vitestOutput);
|
|
352
|
-
* console.log(result.summary); // "3 test failure(s)"
|
|
353
|
-
* console.log(result.guidance); // "Fix each failing test individually..."
|
|
354
|
-
* ```
|
|
355
|
-
*/
|
|
356
|
-
// eslint-disable-next-line sonarjs/cognitive-complexity -- Complexity 29 acceptable for Vitest output parsing (down from 97) - main parsing loop coordinates multiple format detection and state tracking
|
|
357
|
-
export function extractVitestErrors(output, options) {
|
|
358
|
-
const lines = output.split('\n');
|
|
359
|
-
const failures = [];
|
|
360
|
-
let currentFailure = null;
|
|
361
|
-
let currentFile = ''; // Track file from ❯ lines for Format 2
|
|
362
|
-
let hasFormat2 = false; // Track if we found Format 2 file headers
|
|
363
|
-
// First, check for runtime errors (Unhandled Rejection, ENOENT, etc.)
|
|
364
|
-
const runtimeError = extractRuntimeError(output);
|
|
365
|
-
if (runtimeError) {
|
|
366
|
-
failures.push(runtimeError);
|
|
367
|
-
}
|
|
368
|
-
// Check for coverage threshold failures
|
|
369
|
-
const coverageError = extractCoverageThresholdError(output);
|
|
370
|
-
if (coverageError) {
|
|
371
|
-
failures.push(coverageError);
|
|
372
|
-
}
|
|
373
|
-
// Check for Vitest worker timeout errors
|
|
374
|
-
const timeoutError = extractVitestWorkerTimeoutError(output);
|
|
375
|
-
if (timeoutError) {
|
|
376
|
-
failures.push(timeoutError);
|
|
377
|
-
}
|
|
378
|
-
let i = -1;
|
|
379
|
-
while (i < lines.length - 1) {
|
|
380
|
-
i++; // Increment at start so 'continue' statements don't bypass it
|
|
381
|
-
const line = lines[i];
|
|
382
|
-
// Check for Format 2 file header
|
|
383
|
-
const fileHeaderMatch = /❯\s+([^\s]+\.test\.ts)\s+\(/.exec(line);
|
|
384
|
-
if (fileHeaderMatch) {
|
|
385
|
-
currentFile = fileHeaderMatch[1];
|
|
386
|
-
hasFormat2 = true;
|
|
387
|
-
continue;
|
|
388
|
-
}
|
|
389
|
-
// Try to parse as failure line (Format 1 or Format 2)
|
|
390
|
-
const parsedFailure = parseFailureLine(line, currentFile, hasFormat2);
|
|
391
|
-
if (parsedFailure) {
|
|
392
|
-
if (currentFailure?.file) {
|
|
393
|
-
failures.push(currentFailure);
|
|
394
|
-
}
|
|
395
|
-
currentFailure = parsedFailure;
|
|
396
|
-
continue;
|
|
397
|
-
}
|
|
398
|
-
// Parse error message if we have a current failure
|
|
399
|
-
if (currentFailure && !currentFailure.errorMessage) {
|
|
400
|
-
const snapshotMatch = /Snapshot\s+`([^`]+)`\s+mismatched/.exec(line);
|
|
401
|
-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- Need to check truthy regex matches, not just null/undefined
|
|
402
|
-
const hasError = /((?:AssertionError|Error):\s*.+)/.exec(line) || /→\s+(.+)/.exec(line) || snapshotMatch;
|
|
403
|
-
if (hasError) {
|
|
404
|
-
const isSnapshotError = !!snapshotMatch;
|
|
405
|
-
const { errorMessage, lastIndex } = parseErrorMessage(lines, i, isSnapshotError);
|
|
406
|
-
currentFailure.errorMessage = errorMessage;
|
|
407
|
-
i = lastIndex;
|
|
408
|
-
}
|
|
409
|
-
continue;
|
|
410
|
-
}
|
|
411
|
-
// Parse location if we have a current failure
|
|
412
|
-
if (currentFailure && !currentFailure.location) {
|
|
413
|
-
const location = parseLocation(line);
|
|
414
|
-
if (location) {
|
|
415
|
-
currentFailure.location = location;
|
|
416
|
-
continue;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
// Parse source line
|
|
420
|
-
if (currentFailure && /^\s*\d+\|\s+/.exec(line)) {
|
|
421
|
-
const sourceMatch = /^\s*(\d+)\|\s*(.+)/.exec(line);
|
|
422
|
-
if (sourceMatch) {
|
|
423
|
-
currentFailure.sourceLine = `${sourceMatch[1]}| ${sourceMatch[2].trim()}`;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
// Add last failure
|
|
428
|
-
if (currentFailure?.file) {
|
|
429
|
-
failures.push(currentFailure);
|
|
430
|
-
}
|
|
431
|
-
// Detect timeout failures
|
|
432
|
-
const hasTimeout = failures.some(f => f.errorMessage.includes('Test timed out'));
|
|
433
|
-
// Extract expected/actual values and format output
|
|
434
|
-
const { expected, actual } = extractExpectedActual(output);
|
|
435
|
-
const errorSummary = formatFailuresOutput(failures, expected, actual);
|
|
436
|
-
const guidance = generateGuidanceText(failures.length, expected, actual, hasTimeout);
|
|
437
|
-
const result = {
|
|
438
|
-
errors: failures.slice(0, MAX_ERRORS_IN_ARRAY).map(f => {
|
|
439
|
-
// Parse line:column from end of location string (file paths may contain colons)
|
|
440
|
-
let line;
|
|
441
|
-
let column;
|
|
442
|
-
if (f.location) {
|
|
443
|
-
const parts = f.location.split(':');
|
|
444
|
-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- Need to filter empty strings for parseInt, not just null/undefined
|
|
445
|
-
column = Number.parseInt(parts.pop() || '');
|
|
446
|
-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- Need to filter empty strings for parseInt, not just null/undefined
|
|
447
|
-
line = Number.parseInt(parts.pop() || '');
|
|
448
|
-
}
|
|
449
|
-
return {
|
|
450
|
-
file: f.file,
|
|
451
|
-
line: line !== undefined && !Number.isNaN(line) ? line : undefined,
|
|
452
|
-
column: column !== undefined && !Number.isNaN(column) ? column : undefined,
|
|
453
|
-
message: f.errorMessage
|
|
454
|
-
};
|
|
455
|
-
}),
|
|
456
|
-
summary: `${failures.length} test failure(s)`,
|
|
457
|
-
totalErrors: failures.length,
|
|
458
|
-
guidance,
|
|
459
|
-
errorSummary
|
|
460
|
-
};
|
|
461
|
-
// Add quality metadata if developer feedback enabled
|
|
462
|
-
if (options?.developerFeedback) {
|
|
463
|
-
result.metadata = calculateExtractionQuality(failures, options);
|
|
464
|
-
}
|
|
465
|
-
return result;
|
|
466
|
-
}
|
|
467
|
-
/**
|
|
468
|
-
* Calculate extraction quality metadata
|
|
469
|
-
*
|
|
470
|
-
* Reports what the extractor knows about its own extraction quality.
|
|
471
|
-
* Does NOT know expected count - test infrastructure does the comparison.
|
|
472
|
-
*
|
|
473
|
-
* @param failures - Extracted test failures
|
|
474
|
-
* @param options - Extractor options
|
|
475
|
-
* @returns Quality metadata
|
|
476
|
-
*/
|
|
477
|
-
function calculateExtractionQuality(failures, options) {
|
|
478
|
-
const withFile = failures.filter(f => f.file && f.file !== 'unknown').length;
|
|
479
|
-
const withLocation = failures.filter(f => f.location).length;
|
|
480
|
-
const withMessage = failures.filter(f => f.errorMessage).length;
|
|
481
|
-
// Completeness: percentage with file + location + message
|
|
482
|
-
const complete = failures.filter(f => f.file && f.file !== 'unknown' && f.location && f.errorMessage).length;
|
|
483
|
-
const completeness = failures.length > 0
|
|
484
|
-
? Math.round((complete / failures.length) * 100)
|
|
485
|
-
: 100;
|
|
486
|
-
// Confidence: based on how well patterns matched
|
|
487
|
-
// High confidence if most failures have complete data
|
|
488
|
-
let confidence;
|
|
489
|
-
if (completeness >= 80) {
|
|
490
|
-
confidence = 90;
|
|
491
|
-
}
|
|
492
|
-
else if (completeness >= 50) {
|
|
493
|
-
confidence = 70;
|
|
494
|
-
}
|
|
495
|
-
else {
|
|
496
|
-
confidence = 50;
|
|
497
|
-
}
|
|
498
|
-
// Issues encountered
|
|
499
|
-
const issues = [];
|
|
500
|
-
if (withFile < failures.length) {
|
|
501
|
-
issues.push(`${failures.length - withFile} failure(s) missing file path`);
|
|
502
|
-
}
|
|
503
|
-
if (withLocation < failures.length) {
|
|
504
|
-
issues.push(`${failures.length - withLocation} failure(s) missing line numbers`);
|
|
505
|
-
}
|
|
506
|
-
if (withMessage < failures.length) {
|
|
507
|
-
issues.push(`${failures.length - withMessage} failure(s) missing error messages`);
|
|
508
|
-
}
|
|
509
|
-
// Suggestions (only if developer feedback enabled)
|
|
510
|
-
const suggestions = [];
|
|
511
|
-
if (options.developerFeedback) {
|
|
512
|
-
if (completeness < 80) {
|
|
513
|
-
suggestions.push('Try running Vitest with --reporter=verbose for more complete output');
|
|
514
|
-
}
|
|
515
|
-
if (failures.length > 20) {
|
|
516
|
-
suggestions.push(`Extracted ${failures.length} failures - verify this matches actual test output`);
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
return {
|
|
520
|
-
confidence,
|
|
521
|
-
completeness,
|
|
522
|
-
issues,
|
|
523
|
-
...(suggestions.length > 0 && { suggestions })
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
/**
|
|
527
|
-
* Extract expected/actual values from test output
|
|
528
|
-
*
|
|
529
|
-
* @param output - Full test output
|
|
530
|
-
* @returns Expected and actual values (if found)
|
|
531
|
-
*/
|
|
532
|
-
function extractExpectedActual(fullOutput) {
|
|
533
|
-
const expectedMatch = /- Expected[^\n]*\n[^\n]*\n- (.+)/.exec(fullOutput);
|
|
534
|
-
const actualMatch = /\+ Received[^\n]*\n[^\n]*\n\+ (.+)/.exec(fullOutput);
|
|
535
|
-
return {
|
|
536
|
-
expected: expectedMatch ? expectedMatch[1].trim() : undefined,
|
|
537
|
-
actual: actualMatch ? actualMatch[1].trim() : undefined
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
//# sourceMappingURL=vitest-extractor.js.map
|