@vibe-validate/extractors 0.16.1 → 0.17.0-rc.11
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 +103 -0
- package/dist/extractor-registry.d.ts.map +1 -0
- package/dist/extractor-registry.js +278 -0
- package/dist/extractor-registry.js.map +1 -0
- package/dist/extractors/ava/index.d.ts +23 -0
- package/dist/extractors/ava/index.d.ts.map +1 -0
- package/dist/extractors/ava/index.js +507 -0
- package/dist/extractors/ava/index.js.map +1 -0
- package/dist/extractors/ava/index.test.d.ts +7 -0
- package/dist/extractors/ava/index.test.d.ts.map +1 -0
- package/dist/extractors/ava/index.test.js +408 -0
- package/dist/extractors/ava/index.test.js.map +1 -0
- package/dist/extractors/eslint/index.d.ts +18 -0
- package/dist/extractors/eslint/index.d.ts.map +1 -0
- package/dist/extractors/eslint/index.js +206 -0
- package/dist/extractors/eslint/index.js.map +1 -0
- package/dist/extractors/eslint/index.test.d.ts +9 -0
- package/dist/extractors/eslint/index.test.d.ts.map +1 -0
- package/dist/extractors/eslint/index.test.js +191 -0
- package/dist/extractors/eslint/index.test.js.map +1 -0
- package/dist/extractors/generic/index.d.ts +30 -0
- package/dist/extractors/generic/index.d.ts.map +1 -0
- package/dist/extractors/generic/index.js +140 -0
- package/dist/extractors/generic/index.js.map +1 -0
- package/dist/extractors/generic/index.test.d.ts +7 -0
- package/dist/extractors/generic/index.test.d.ts.map +1 -0
- package/dist/extractors/generic/index.test.js +61 -0
- package/dist/extractors/generic/index.test.js.map +1 -0
- package/dist/extractors/jasmine/index.d.ts +17 -0
- package/dist/extractors/jasmine/index.d.ts.map +1 -0
- package/dist/extractors/jasmine/index.js +254 -0
- package/dist/extractors/jasmine/index.js.map +1 -0
- package/dist/extractors/jasmine/index.test.d.ts +7 -0
- package/dist/extractors/jasmine/index.test.d.ts.map +1 -0
- package/dist/extractors/jasmine/index.test.js +345 -0
- package/dist/extractors/jasmine/index.test.js.map +1 -0
- package/dist/extractors/jest/index.d.ts +17 -0
- package/dist/extractors/jest/index.d.ts.map +1 -0
- package/dist/extractors/jest/index.js +278 -0
- package/dist/extractors/jest/index.js.map +1 -0
- package/dist/extractors/jest/index.test.d.ts +9 -0
- package/dist/extractors/jest/index.test.d.ts.map +1 -0
- package/dist/extractors/jest/index.test.js +353 -0
- package/dist/extractors/jest/index.test.js.map +1 -0
- package/dist/extractors/junit/index.d.ts +18 -0
- package/dist/extractors/junit/index.d.ts.map +1 -0
- package/dist/extractors/junit/index.js +259 -0
- package/dist/extractors/junit/index.js.map +1 -0
- package/dist/extractors/junit/index.test.d.ts +7 -0
- package/dist/extractors/junit/index.test.d.ts.map +1 -0
- package/dist/extractors/junit/index.test.js +341 -0
- package/dist/extractors/junit/index.test.js.map +1 -0
- package/dist/extractors/maven-checkstyle/index.d.ts +23 -0
- package/dist/extractors/maven-checkstyle/index.d.ts.map +1 -0
- package/dist/extractors/maven-checkstyle/index.js +263 -0
- package/dist/extractors/maven-checkstyle/index.js.map +1 -0
- package/dist/extractors/maven-checkstyle/index.test.d.ts +2 -0
- package/dist/extractors/maven-checkstyle/index.test.d.ts.map +1 -0
- package/dist/extractors/maven-checkstyle/index.test.js +197 -0
- package/dist/extractors/maven-checkstyle/index.test.js.map +1 -0
- package/dist/extractors/maven-compiler/index.d.ts +23 -0
- package/dist/extractors/maven-compiler/index.d.ts.map +1 -0
- package/dist/extractors/maven-compiler/index.js +271 -0
- package/dist/extractors/maven-compiler/index.js.map +1 -0
- package/dist/extractors/maven-compiler/index.test.d.ts +2 -0
- package/dist/extractors/maven-compiler/index.test.d.ts.map +1 -0
- package/dist/extractors/maven-compiler/index.test.js +189 -0
- package/dist/extractors/maven-compiler/index.test.js.map +1 -0
- package/dist/extractors/maven-surefire/index.d.ts +23 -0
- package/dist/extractors/maven-surefire/index.d.ts.map +1 -0
- package/dist/extractors/maven-surefire/index.js +292 -0
- package/dist/extractors/maven-surefire/index.js.map +1 -0
- package/dist/extractors/maven-surefire/index.test.d.ts +2 -0
- package/dist/extractors/maven-surefire/index.test.d.ts.map +1 -0
- package/dist/extractors/maven-surefire/index.test.js +169 -0
- package/dist/extractors/maven-surefire/index.test.js.map +1 -0
- package/dist/extractors/mocha/index.d.ts +17 -0
- package/dist/extractors/mocha/index.d.ts.map +1 -0
- package/dist/extractors/mocha/index.js +241 -0
- package/dist/extractors/mocha/index.js.map +1 -0
- package/dist/extractors/mocha/index.test.d.ts +7 -0
- package/dist/extractors/mocha/index.test.d.ts.map +1 -0
- package/dist/extractors/mocha/index.test.js +300 -0
- package/dist/extractors/mocha/index.test.js.map +1 -0
- package/dist/extractors/playwright/index.d.ts +17 -0
- package/dist/extractors/playwright/index.d.ts.map +1 -0
- package/dist/extractors/playwright/index.js +320 -0
- package/dist/extractors/playwright/index.js.map +1 -0
- package/dist/extractors/playwright/index.test.d.ts +7 -0
- package/dist/extractors/playwright/index.test.d.ts.map +1 -0
- package/dist/extractors/playwright/index.test.js +274 -0
- package/dist/extractors/playwright/index.test.js.map +1 -0
- package/dist/extractors/tap/index.d.ts +23 -0
- package/dist/extractors/tap/index.d.ts.map +1 -0
- package/dist/extractors/tap/index.js +352 -0
- package/dist/extractors/tap/index.js.map +1 -0
- package/dist/extractors/tap/index.test.d.ts +7 -0
- package/dist/extractors/tap/index.test.d.ts.map +1 -0
- package/dist/extractors/tap/index.test.js +100 -0
- package/dist/extractors/tap/index.test.js.map +1 -0
- package/dist/extractors/typescript/index.d.ts +17 -0
- package/dist/extractors/typescript/index.d.ts.map +1 -0
- package/dist/extractors/typescript/index.js +150 -0
- package/dist/extractors/typescript/index.js.map +1 -0
- package/dist/extractors/typescript/index.test.d.ts +9 -0
- package/dist/extractors/typescript/index.test.d.ts.map +1 -0
- package/dist/extractors/typescript/index.test.js +177 -0
- package/dist/extractors/typescript/index.test.js.map +1 -0
- package/dist/extractors/vitest/index.d.ts +17 -0
- package/dist/extractors/vitest/index.d.ts.map +1 -0
- package/dist/extractors/vitest/index.js +564 -0
- package/dist/extractors/vitest/index.js.map +1 -0
- package/dist/extractors/vitest/index.test.d.ts +9 -0
- package/dist/extractors/vitest/index.test.d.ts.map +1 -0
- package/dist/extractors/vitest/index.test.js +373 -0
- package/dist/extractors/vitest/index.test.js.map +1 -0
- package/dist/index.d.ts +27 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -11
- package/dist/index.js.map +1 -1
- package/dist/maven-checkstyle-extractor.d.ts +20 -0
- package/dist/maven-checkstyle-extractor.d.ts.map +1 -0
- package/dist/maven-checkstyle-extractor.js +208 -0
- package/dist/maven-checkstyle-extractor.js.map +1 -0
- package/dist/maven-compiler-extractor.d.ts +20 -0
- package/dist/maven-compiler-extractor.d.ts.map +1 -0
- package/dist/maven-compiler-extractor.js +218 -0
- package/dist/maven-compiler-extractor.js.map +1 -0
- package/dist/maven-surefire-extractor.d.ts +20 -0
- package/dist/maven-surefire-extractor.d.ts.map +1 -0
- package/dist/maven-surefire-extractor.js +228 -0
- package/dist/maven-surefire-extractor.js.map +1 -0
- package/dist/maven-utils.d.ts +24 -0
- package/dist/maven-utils.d.ts.map +1 -0
- package/dist/maven-utils.js +36 -0
- package/dist/maven-utils.js.map +1 -0
- package/dist/plugin-loader.d.ts +82 -0
- package/dist/plugin-loader.d.ts.map +1 -0
- package/dist/plugin-loader.js +201 -0
- package/dist/plugin-loader.js.map +1 -0
- package/dist/result-schema.d.ts +59 -9
- package/dist/result-schema.d.ts.map +1 -1
- package/dist/result-schema.js +3 -19
- package/dist/result-schema.js.map +1 -1
- package/dist/sandbox.d.ts +161 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +254 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/sandbox.test.d.ts +8 -0
- package/dist/sandbox.test.d.ts.map +1 -0
- package/dist/sandbox.test.js +395 -0
- package/dist/sandbox.test.js.map +1 -0
- package/dist/sandboxed-extractor.d.ts +46 -0
- package/dist/sandboxed-extractor.d.ts.map +1 -0
- package/dist/sandboxed-extractor.js +172 -0
- package/dist/sandboxed-extractor.js.map +1 -0
- package/dist/sandboxed-extractor.test.d.ts +5 -0
- package/dist/sandboxed-extractor.test.d.ts.map +1 -0
- package/dist/sandboxed-extractor.test.js +346 -0
- package/dist/sandboxed-extractor.test.js.map +1 -0
- package/dist/smart-extractor.d.ts +22 -10
- package/dist/smart-extractor.d.ts.map +1 -1
- package/dist/smart-extractor.js +116 -163
- package/dist/smart-extractor.js.map +1 -1
- package/dist/types.d.ts +94 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jest Error Extractor Plugin
|
|
3
|
+
*
|
|
4
|
+
* Parses and formats 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
|
+
* Match FAIL line and extract file path
|
|
11
|
+
*/
|
|
12
|
+
function matchFailLine(line) {
|
|
13
|
+
const failMatch = /^\s*FAIL\s+(?:[\w-]+\s+)?([\w/-]+\.test\.\w+)/.exec(line);
|
|
14
|
+
return failMatch ? failMatch[1] : null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Match inline failure (✕) and extract test name
|
|
18
|
+
*/
|
|
19
|
+
function matchInlineFailure(line) {
|
|
20
|
+
// eslint-disable-next-line sonarjs/slow-regex -- Safe: only parses Jest test framework output (controlled output), not user input
|
|
21
|
+
const failureMatch = /^\s+✕\s+(.+?)(?:\s+\(\d+\s*ms\))?$/.exec(line);
|
|
22
|
+
return failureMatch ? failureMatch[1].trim() : null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Match detailed test format (●) and extract hierarchy
|
|
26
|
+
*/
|
|
27
|
+
function matchDetailedTest(line) {
|
|
28
|
+
// eslint-disable-next-line sonarjs/slow-regex -- Safe: only parses Jest test framework output (controlled output), not user input
|
|
29
|
+
const detailedTestMatch = /^\s*●\s+(.+)$/.exec(line);
|
|
30
|
+
return detailedTestMatch ? detailedTestMatch[1].trim() : null;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Match test suite line and extract suite info
|
|
34
|
+
*/
|
|
35
|
+
function matchSuiteLine(line) {
|
|
36
|
+
const suiteMatch = /^\s+([A-Z][\w\s›-]+)$/.exec(line);
|
|
37
|
+
if (!suiteMatch || line.includes('✕') || line.includes('✓') || line.includes('ms)')) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const indentMatch = /^(\s*)/.exec(line);
|
|
41
|
+
const indent = indentMatch ? indentMatch[1].length : 0;
|
|
42
|
+
return { name: suiteMatch[1].trim(), indent };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Adjust hierarchy stack based on indentation level
|
|
46
|
+
* Pops from stack when indent indicates we're at same or higher level
|
|
47
|
+
*/
|
|
48
|
+
function adjustHierarchyForIndent(hierarchyStack, indent) {
|
|
49
|
+
// Calculate expected indent for next level: 2 spaces per level (starting at 2)
|
|
50
|
+
// Level 0 (empty stack) → expect indent 2
|
|
51
|
+
// Level 1 (1 item) → expect indent 4
|
|
52
|
+
// Level 2 (2 items) → expect indent 6
|
|
53
|
+
const expectedIndent = (hierarchyStack.length + 1) * 2;
|
|
54
|
+
// Pop while we're not at the right level
|
|
55
|
+
// If indent < expectedIndent, we've moved to a higher level (less indented)
|
|
56
|
+
while (hierarchyStack.length > 0 && indent < expectedIndent) {
|
|
57
|
+
hierarchyStack.pop();
|
|
58
|
+
// Recalculate for next iteration
|
|
59
|
+
const newExpectedIndent = (hierarchyStack.length + 1) * 2;
|
|
60
|
+
if (indent === newExpectedIndent) {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Process a single line of Jest output
|
|
67
|
+
*/
|
|
68
|
+
function processLine(line, currentFile, hierarchyStack) {
|
|
69
|
+
const result = {};
|
|
70
|
+
// Check for FAIL line
|
|
71
|
+
const newFile = matchFailLine(line);
|
|
72
|
+
if (newFile) {
|
|
73
|
+
result.newFile = newFile;
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
// Skip lines before we have a file
|
|
77
|
+
if (!currentFile) {
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
// Check for inline failure (✕)
|
|
81
|
+
const inlineTest = matchInlineFailure(line);
|
|
82
|
+
if (inlineTest) {
|
|
83
|
+
// Adjust hierarchy based on test indentation
|
|
84
|
+
const indentMatch = /^(\s*)/.exec(line);
|
|
85
|
+
const indent = indentMatch ? indentMatch[1].length : 0;
|
|
86
|
+
adjustHierarchyForIndent(hierarchyStack, indent);
|
|
87
|
+
const fullHierarchy = hierarchyStack.length > 0
|
|
88
|
+
? [...hierarchyStack, inlineTest].join(' › ')
|
|
89
|
+
: inlineTest;
|
|
90
|
+
result.failure = {
|
|
91
|
+
file: currentFile,
|
|
92
|
+
location: currentFile,
|
|
93
|
+
testHierarchy: fullHierarchy,
|
|
94
|
+
errorMessage: 'Test failed'
|
|
95
|
+
};
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
// Check for detailed test format (●)
|
|
99
|
+
const detailedHierarchy = matchDetailedTest(line);
|
|
100
|
+
if (detailedHierarchy) {
|
|
101
|
+
result.failure = {
|
|
102
|
+
file: currentFile,
|
|
103
|
+
location: currentFile,
|
|
104
|
+
testHierarchy: detailedHierarchy,
|
|
105
|
+
errorMessage: 'Test failed'
|
|
106
|
+
};
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
// Check for suite line
|
|
110
|
+
const suiteInfo = matchSuiteLine(line);
|
|
111
|
+
if (suiteInfo) {
|
|
112
|
+
result.newSuite = suiteInfo;
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Format failures into clean output string
|
|
118
|
+
*/
|
|
119
|
+
function formatJestFailures(failures) {
|
|
120
|
+
const errorSummaryLines = [];
|
|
121
|
+
for (const failure of failures) {
|
|
122
|
+
errorSummaryLines.push(`● ${failure.testHierarchy}`);
|
|
123
|
+
errorSummaryLines.push(` ${failure.errorMessage}`);
|
|
124
|
+
errorSummaryLines.push(` Location: ${failure.location}`);
|
|
125
|
+
errorSummaryLines.push('');
|
|
126
|
+
}
|
|
127
|
+
return errorSummaryLines.join('\n');
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Extract Jest test failures from output
|
|
131
|
+
*
|
|
132
|
+
* @param output - Raw Jest command output
|
|
133
|
+
* @returns Structured error information with test-specific guidance
|
|
134
|
+
*/
|
|
135
|
+
function extract(output) {
|
|
136
|
+
// Note: ANSI codes are stripped centrally in smart-extractor.ts
|
|
137
|
+
const lines = output.split('\n');
|
|
138
|
+
const failures = [];
|
|
139
|
+
let currentFile = '';
|
|
140
|
+
const hierarchyStack = [];
|
|
141
|
+
for (const line of lines) {
|
|
142
|
+
const result = processLine(line, currentFile, hierarchyStack);
|
|
143
|
+
if (result.newFile) {
|
|
144
|
+
currentFile = result.newFile;
|
|
145
|
+
hierarchyStack.length = 0;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (result.failure) {
|
|
149
|
+
failures.push(result.failure);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (result.newSuite) {
|
|
153
|
+
adjustHierarchyForIndent(hierarchyStack, result.newSuite.indent);
|
|
154
|
+
hierarchyStack.push(result.newSuite.name);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Build formatted errors (limit to MAX_ERRORS_IN_ARRAY)
|
|
158
|
+
const errors = failures.slice(0, MAX_ERRORS_IN_ARRAY).map(f => {
|
|
159
|
+
const locationParts = f.location.split(':');
|
|
160
|
+
const lineNum = Number.parseInt(locationParts[1] || '');
|
|
161
|
+
const colNum = Number.parseInt(locationParts[2] || '');
|
|
162
|
+
return {
|
|
163
|
+
file: f.file,
|
|
164
|
+
line: lineNum > 0 ? lineNum : undefined,
|
|
165
|
+
column: colNum > 0 ? colNum : undefined,
|
|
166
|
+
message: `${f.testHierarchy}: ${f.errorMessage}`,
|
|
167
|
+
severity: 'error'
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
const summary = failures.length > 0
|
|
171
|
+
? `${failures.length} test failure(s)`
|
|
172
|
+
: 'No test failures detected';
|
|
173
|
+
const guidance = failures.length > 0
|
|
174
|
+
? 'Fix each failing test individually. Check test setup, mocks, and assertions.'
|
|
175
|
+
: '';
|
|
176
|
+
return {
|
|
177
|
+
errors,
|
|
178
|
+
summary,
|
|
179
|
+
totalErrors: failures.length,
|
|
180
|
+
guidance,
|
|
181
|
+
errorSummary: formatJestFailures(failures.slice(0, MAX_ERRORS_IN_ARRAY))
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Detect if output is from Jest test runner
|
|
186
|
+
*
|
|
187
|
+
* @param output - Command output to analyze
|
|
188
|
+
* @returns Detection result with confidence and patterns
|
|
189
|
+
*/
|
|
190
|
+
function detect(output) {
|
|
191
|
+
// Check for Jest-specific patterns (multiline mode)
|
|
192
|
+
// Optimized: Use [ \t]* instead of \s* to avoid backtracking on newlines
|
|
193
|
+
const hasFailMarker = /^[ \t]*FAIL[ \t]+/m.test(output);
|
|
194
|
+
const hasTestMarkers = /[✕✓]/.test(output);
|
|
195
|
+
// Optimized: Use [ \t]* instead of \s* to avoid backtracking on newlines
|
|
196
|
+
const hasDetailedMarker = /^[ \t]*●[ \t]+/m.test(output);
|
|
197
|
+
if (hasFailMarker || (hasTestMarkers && hasDetailedMarker)) {
|
|
198
|
+
return {
|
|
199
|
+
confidence: 90,
|
|
200
|
+
patterns: ['FAIL marker', 'test markers (✕/✓)', '● detailed format'],
|
|
201
|
+
reason: 'Jest test framework output detected',
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
// Lower confidence if only partial markers
|
|
205
|
+
if (hasTestMarkers) {
|
|
206
|
+
return {
|
|
207
|
+
confidence: 50,
|
|
208
|
+
patterns: ['test markers (✕/✓)'],
|
|
209
|
+
reason: 'Possible Jest output (partial markers)',
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return { confidence: 0, patterns: [], reason: '' };
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Sample test cases for Jest extractor
|
|
216
|
+
*/
|
|
217
|
+
const samples = [
|
|
218
|
+
{
|
|
219
|
+
name: 'single-test-failure',
|
|
220
|
+
description: 'Single Jest test failure with inline marker',
|
|
221
|
+
input: `
|
|
222
|
+
FAIL test/example.test.ts
|
|
223
|
+
Example Suite
|
|
224
|
+
✕ should pass (15 ms)
|
|
225
|
+
`.trim(),
|
|
226
|
+
expectedErrors: 1,
|
|
227
|
+
expectedPatterns: ['FAIL', 'Example Suite › should pass'],
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
name: 'multiple-failures-with-hierarchy',
|
|
231
|
+
description: 'Multiple test failures with nested describe blocks',
|
|
232
|
+
input: `
|
|
233
|
+
FAIL test/example.test.ts
|
|
234
|
+
Example Suite
|
|
235
|
+
Nested Suite
|
|
236
|
+
✕ test one (10 ms)
|
|
237
|
+
✕ test two (12 ms)
|
|
238
|
+
`.trim(),
|
|
239
|
+
expectedErrors: 2,
|
|
240
|
+
expectedPatterns: ['Example Suite › Nested Suite › test one', 'Example Suite › Nested Suite › test two'],
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
name: 'detailed-format',
|
|
244
|
+
description: 'Jest output with detailed error format (●)',
|
|
245
|
+
input: `
|
|
246
|
+
FAIL test/example.test.ts
|
|
247
|
+
● Example Suite › should handle errors
|
|
248
|
+
`.trim(),
|
|
249
|
+
expectedErrors: 1,
|
|
250
|
+
expectedPatterns: ['Example Suite › should handle errors'],
|
|
251
|
+
},
|
|
252
|
+
];
|
|
253
|
+
/**
|
|
254
|
+
* Jest Error Extractor Plugin
|
|
255
|
+
*
|
|
256
|
+
* Extracts Jest test failures with high confidence (90%).
|
|
257
|
+
* Supports inline (✕) and detailed (●) failure formats.
|
|
258
|
+
*/
|
|
259
|
+
const jestPlugin = {
|
|
260
|
+
metadata: {
|
|
261
|
+
name: 'jest',
|
|
262
|
+
version: '1.0.0',
|
|
263
|
+
author: 'vibe-validate',
|
|
264
|
+
description: 'Extracts Jest test framework errors',
|
|
265
|
+
repository: 'https://github.com/jdutton/vibe-validate',
|
|
266
|
+
tags: ['jest', 'testing', 'test-runner'],
|
|
267
|
+
},
|
|
268
|
+
hints: {
|
|
269
|
+
required: [],
|
|
270
|
+
anyOf: ['FAIL', '✕', '●'],
|
|
271
|
+
},
|
|
272
|
+
priority: 90,
|
|
273
|
+
detect,
|
|
274
|
+
extract,
|
|
275
|
+
samples,
|
|
276
|
+
};
|
|
277
|
+
export default jestPlugin;
|
|
278
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extractors/jest/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../src/extractors/jest/index.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jest Extractor Plugin Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests Jest test framework error parsing and formatting.
|
|
5
|
+
*
|
|
6
|
+
* @package @vibe-validate/extractors
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import jestPlugin from './index.js';
|
|
10
|
+
const { extract, detect } = jestPlugin;
|
|
11
|
+
describe('Jest Extractor Plugin', () => {
|
|
12
|
+
describe('detect', () => {
|
|
13
|
+
it('should detect Jest output with FAIL marker', () => {
|
|
14
|
+
const output = ` FAIL test/example.test.ts
|
|
15
|
+
Example Suite
|
|
16
|
+
✕ should pass`;
|
|
17
|
+
const result = detect(output);
|
|
18
|
+
expect(result.confidence).toBe(90);
|
|
19
|
+
expect(result.patterns).toContain('FAIL marker');
|
|
20
|
+
expect(result.reason).toContain('Jest test framework');
|
|
21
|
+
});
|
|
22
|
+
it('should detect Jest output with detailed marker and test markers', () => {
|
|
23
|
+
const output = ` ● Example Suite › should handle errors
|
|
24
|
+
✕ test failed`;
|
|
25
|
+
const result = detect(output);
|
|
26
|
+
expect(result.confidence).toBe(90);
|
|
27
|
+
expect(result.patterns).toContain('● detailed format');
|
|
28
|
+
expect(result.patterns).toContain('test markers (✕/✓)');
|
|
29
|
+
});
|
|
30
|
+
it('should detect Jest output with test markers only (lower confidence)', () => {
|
|
31
|
+
const output = ` ✕ some test
|
|
32
|
+
✓ passing test`;
|
|
33
|
+
const result = detect(output);
|
|
34
|
+
expect(result.confidence).toBe(50);
|
|
35
|
+
expect(result.patterns).toContain('test markers (✕/✓)');
|
|
36
|
+
});
|
|
37
|
+
it('should not detect non-Jest output', () => {
|
|
38
|
+
const output = `Some random text without Jest markers`;
|
|
39
|
+
const result = detect(output);
|
|
40
|
+
expect(result.confidence).toBe(0);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe('extract', () => {
|
|
44
|
+
it('should parse single inline test failure', () => {
|
|
45
|
+
const output = `
|
|
46
|
+
FAIL test/example.test.ts
|
|
47
|
+
Example Suite
|
|
48
|
+
✕ should pass (15 ms)
|
|
49
|
+
`.trim();
|
|
50
|
+
const result = extract(output);
|
|
51
|
+
expect(result.errors).toHaveLength(1);
|
|
52
|
+
expect(result.errors[0]).toMatchObject({
|
|
53
|
+
file: 'test/example.test.ts',
|
|
54
|
+
message: 'Example Suite › should pass: Test failed',
|
|
55
|
+
severity: 'error',
|
|
56
|
+
});
|
|
57
|
+
expect(result.summary).toBe('1 test failure(s)');
|
|
58
|
+
expect(result.totalErrors).toBe(1);
|
|
59
|
+
expect(result.guidance).toContain('Fix each failing test individually');
|
|
60
|
+
});
|
|
61
|
+
it('should parse multiple test failures with hierarchy', () => {
|
|
62
|
+
const output = `
|
|
63
|
+
FAIL test/example.test.ts
|
|
64
|
+
Example Suite
|
|
65
|
+
Nested Suite
|
|
66
|
+
✕ test one (10 ms)
|
|
67
|
+
✕ test two (12 ms)
|
|
68
|
+
`.trim();
|
|
69
|
+
const result = extract(output);
|
|
70
|
+
expect(result.errors).toHaveLength(2);
|
|
71
|
+
expect(result.errors[0].message).toContain('Example Suite › Nested Suite › test one');
|
|
72
|
+
expect(result.errors[1].message).toContain('Example Suite › Nested Suite › test two');
|
|
73
|
+
expect(result.summary).toBe('2 test failure(s)');
|
|
74
|
+
expect(result.totalErrors).toBe(2);
|
|
75
|
+
});
|
|
76
|
+
it('should parse detailed test format with ● marker', () => {
|
|
77
|
+
const output = `
|
|
78
|
+
FAIL test/example.test.ts
|
|
79
|
+
● Example Suite › should handle errors
|
|
80
|
+
|
|
81
|
+
Test failed with assertion error
|
|
82
|
+
`.trim();
|
|
83
|
+
const result = extract(output);
|
|
84
|
+
expect(result.errors).toHaveLength(1);
|
|
85
|
+
expect(result.errors[0].message).toContain('Example Suite › should handle errors');
|
|
86
|
+
});
|
|
87
|
+
it('should handle multiple test files', () => {
|
|
88
|
+
const output = `
|
|
89
|
+
FAIL test/first.test.ts
|
|
90
|
+
Suite One
|
|
91
|
+
✕ test A (5 ms)
|
|
92
|
+
|
|
93
|
+
FAIL test/second.test.ts
|
|
94
|
+
Suite Two
|
|
95
|
+
✕ test B (8 ms)
|
|
96
|
+
`.trim();
|
|
97
|
+
const result = extract(output);
|
|
98
|
+
expect(result.errors).toHaveLength(2);
|
|
99
|
+
expect(result.errors[0].file).toBe('test/first.test.ts');
|
|
100
|
+
expect(result.errors[1].file).toBe('test/second.test.ts');
|
|
101
|
+
expect(result.summary).toBe('2 test failure(s)');
|
|
102
|
+
});
|
|
103
|
+
it('should handle test without suite hierarchy', () => {
|
|
104
|
+
const output = `
|
|
105
|
+
FAIL test/example.test.ts
|
|
106
|
+
✕ standalone test (10 ms)
|
|
107
|
+
`.trim();
|
|
108
|
+
const result = extract(output);
|
|
109
|
+
expect(result.errors).toHaveLength(1);
|
|
110
|
+
expect(result.errors[0].message).toBe('standalone test: Test failed');
|
|
111
|
+
});
|
|
112
|
+
it('should reset hierarchy when switching files', () => {
|
|
113
|
+
const output = `
|
|
114
|
+
FAIL test/first.test.ts
|
|
115
|
+
First Suite
|
|
116
|
+
✕ test one (5 ms)
|
|
117
|
+
|
|
118
|
+
FAIL test/second.test.ts
|
|
119
|
+
Second Suite
|
|
120
|
+
✕ test two (8 ms)
|
|
121
|
+
`.trim();
|
|
122
|
+
const result = extract(output);
|
|
123
|
+
expect(result.errors).toHaveLength(2);
|
|
124
|
+
expect(result.errors[0].message).toContain('First Suite › test one');
|
|
125
|
+
expect(result.errors[1].message).toContain('Second Suite › test two');
|
|
126
|
+
});
|
|
127
|
+
it('should handle nested suite hierarchy changes', () => {
|
|
128
|
+
const output = `
|
|
129
|
+
FAIL test/example.test.ts
|
|
130
|
+
Outer Suite
|
|
131
|
+
Inner Suite
|
|
132
|
+
✕ nested test (5 ms)
|
|
133
|
+
✕ outer test (8 ms)
|
|
134
|
+
`.trim();
|
|
135
|
+
const result = extract(output);
|
|
136
|
+
expect(result.errors).toHaveLength(2);
|
|
137
|
+
expect(result.errors[0].message).toContain('Outer Suite › Inner Suite › nested test');
|
|
138
|
+
expect(result.errors[1].message).toContain('Outer Suite › outer test');
|
|
139
|
+
});
|
|
140
|
+
it('should ignore lines without file context', () => {
|
|
141
|
+
const output = `
|
|
142
|
+
Some random output
|
|
143
|
+
✕ test without file
|
|
144
|
+
FAIL test/example.test.ts
|
|
145
|
+
✕ real test (10 ms)
|
|
146
|
+
`.trim();
|
|
147
|
+
const result = extract(output);
|
|
148
|
+
expect(result.errors).toHaveLength(1);
|
|
149
|
+
expect(result.errors[0].file).toBe('test/example.test.ts');
|
|
150
|
+
});
|
|
151
|
+
it('should handle mixed pass/fail output', () => {
|
|
152
|
+
const output = `
|
|
153
|
+
FAIL test/example.test.ts
|
|
154
|
+
Example Suite
|
|
155
|
+
✓ passing test (5 ms)
|
|
156
|
+
✕ failing test (10 ms)
|
|
157
|
+
✓ another pass (3 ms)
|
|
158
|
+
`.trim();
|
|
159
|
+
const result = extract(output);
|
|
160
|
+
expect(result.errors).toHaveLength(1);
|
|
161
|
+
expect(result.errors[0].message).toContain('failing test');
|
|
162
|
+
});
|
|
163
|
+
it('should limit output to MAX_ERRORS_IN_ARRAY', async () => {
|
|
164
|
+
const { MAX_ERRORS_IN_ARRAY } = await import('../../result-schema.js');
|
|
165
|
+
// Generate 15 test failures (more than MAX_ERRORS_IN_ARRAY = 10)
|
|
166
|
+
const failures = Array.from({ length: 15 }, (_, i) => ` ✕ test ${i + 1} (${i + 1} ms)`).join('\n');
|
|
167
|
+
const output = `
|
|
168
|
+
FAIL test/example.test.ts
|
|
169
|
+
Test Suite
|
|
170
|
+
${failures}
|
|
171
|
+
`.trim();
|
|
172
|
+
const result = extract(output);
|
|
173
|
+
// totalErrors should be 15 (full count)
|
|
174
|
+
expect(result.totalErrors).toBe(15);
|
|
175
|
+
// errors array should be truncated to MAX_ERRORS_IN_ARRAY (10)
|
|
176
|
+
expect(result.errors).toHaveLength(MAX_ERRORS_IN_ARRAY);
|
|
177
|
+
expect(result.errors).toHaveLength(10);
|
|
178
|
+
// Verify we got the first 10 errors
|
|
179
|
+
expect(result.errors[0].message).toContain('test 1');
|
|
180
|
+
expect(result.errors[9].message).toContain('test 10');
|
|
181
|
+
// Summary should show full count (15)
|
|
182
|
+
expect(result.summary).toBe('15 test failure(s)');
|
|
183
|
+
// Error summary should also be truncated
|
|
184
|
+
const summaryLines = result.errorSummary.split('\n').filter(line => line.startsWith('●'));
|
|
185
|
+
expect(summaryLines.length).toBeLessThanOrEqual(MAX_ERRORS_IN_ARRAY);
|
|
186
|
+
});
|
|
187
|
+
it('should handle empty output', () => {
|
|
188
|
+
const result = extract('');
|
|
189
|
+
expect(result.errors).toHaveLength(0);
|
|
190
|
+
expect(result.summary).toBe('No test failures detected');
|
|
191
|
+
expect(result.totalErrors).toBe(0);
|
|
192
|
+
expect(result.guidance).toBe('');
|
|
193
|
+
});
|
|
194
|
+
it('should handle output with no failures', () => {
|
|
195
|
+
const output = `
|
|
196
|
+
PASS test/example.test.ts
|
|
197
|
+
Example Suite
|
|
198
|
+
✓ should pass (5 ms)
|
|
199
|
+
`.trim();
|
|
200
|
+
const result = extract(output);
|
|
201
|
+
expect(result.errors).toHaveLength(0);
|
|
202
|
+
expect(result.summary).toBe('No test failures detected');
|
|
203
|
+
expect(result.totalErrors).toBe(0);
|
|
204
|
+
});
|
|
205
|
+
it('should generate clean errorSummary output', () => {
|
|
206
|
+
const output = `
|
|
207
|
+
FAIL test/example.test.ts
|
|
208
|
+
Example Suite
|
|
209
|
+
✕ first test (10 ms)
|
|
210
|
+
✕ second test (15 ms)
|
|
211
|
+
`.trim();
|
|
212
|
+
const result = extract(output);
|
|
213
|
+
expect(result.errorSummary).toContain('● Example Suite › first test');
|
|
214
|
+
expect(result.errorSummary).toContain('● Example Suite › second test');
|
|
215
|
+
expect(result.errorSummary).toContain('Location: test/example.test.ts');
|
|
216
|
+
expect(result.errorSummary).toContain('Test failed');
|
|
217
|
+
});
|
|
218
|
+
it('should handle test names with special characters', () => {
|
|
219
|
+
const output = `
|
|
220
|
+
FAIL test/example.test.ts
|
|
221
|
+
Example Suite
|
|
222
|
+
✕ should handle "quotes" and 'apostrophes' (10 ms)
|
|
223
|
+
`.trim();
|
|
224
|
+
const result = extract(output);
|
|
225
|
+
expect(result.errors).toHaveLength(1);
|
|
226
|
+
expect(result.errors[0].message).toContain('should handle "quotes" and \'apostrophes\'');
|
|
227
|
+
});
|
|
228
|
+
it('should handle suite names with special characters', () => {
|
|
229
|
+
const output = `
|
|
230
|
+
FAIL test/example.test.ts
|
|
231
|
+
Suite-With-Dashes
|
|
232
|
+
✕ test one (10 ms)
|
|
233
|
+
Suite›With›Chevrons
|
|
234
|
+
✕ test two (15 ms)
|
|
235
|
+
`.trim();
|
|
236
|
+
const result = extract(output);
|
|
237
|
+
expect(result.errors).toHaveLength(2);
|
|
238
|
+
expect(result.errors[0].message).toContain('Suite-With-Dashes › test one');
|
|
239
|
+
expect(result.errors[1].message).toContain('Suite›With›Chevrons › test two');
|
|
240
|
+
});
|
|
241
|
+
it('should handle deeply nested suite hierarchies', () => {
|
|
242
|
+
const output = `
|
|
243
|
+
FAIL test/example.test.ts
|
|
244
|
+
Level 1
|
|
245
|
+
Level 2
|
|
246
|
+
Level 3
|
|
247
|
+
Level 4
|
|
248
|
+
✕ deeply nested test (10 ms)
|
|
249
|
+
`.trim();
|
|
250
|
+
const result = extract(output);
|
|
251
|
+
expect(result.errors).toHaveLength(1);
|
|
252
|
+
expect(result.errors[0].message).toContain('Level 1 › Level 2 › Level 3 › Level 4 › deeply nested test');
|
|
253
|
+
});
|
|
254
|
+
it('should handle file paths with directories', () => {
|
|
255
|
+
const output = `
|
|
256
|
+
FAIL packages/core/test/validator.test.ts
|
|
257
|
+
Validator Suite
|
|
258
|
+
✕ should validate (10 ms)
|
|
259
|
+
`.trim();
|
|
260
|
+
const result = extract(output);
|
|
261
|
+
expect(result.errors).toHaveLength(1);
|
|
262
|
+
expect(result.errors[0].file).toBe('packages/core/test/validator.test.ts');
|
|
263
|
+
});
|
|
264
|
+
it('should handle test timing variations', () => {
|
|
265
|
+
const output = `
|
|
266
|
+
FAIL test/example.test.ts
|
|
267
|
+
Example Suite
|
|
268
|
+
✕ fast test (1 ms)
|
|
269
|
+
✕ slow test (1234 ms)
|
|
270
|
+
✕ no timing
|
|
271
|
+
`.trim();
|
|
272
|
+
const result = extract(output);
|
|
273
|
+
expect(result.errors).toHaveLength(3);
|
|
274
|
+
expect(result.errors[0].message).toContain('fast test');
|
|
275
|
+
expect(result.errors[1].message).toContain('slow test');
|
|
276
|
+
expect(result.errors[2].message).toContain('no timing');
|
|
277
|
+
});
|
|
278
|
+
it('should set line/column to undefined when location info not available (regression test for GH-57)', () => {
|
|
279
|
+
// Regression test: Jest output without line:column info should produce
|
|
280
|
+
// undefined values (not 0) to comply with schema validation
|
|
281
|
+
const output = `
|
|
282
|
+
FAIL test/example.test.ts
|
|
283
|
+
Example Suite
|
|
284
|
+
✕ test without location (10 ms)
|
|
285
|
+
`.trim();
|
|
286
|
+
const result = extract(output);
|
|
287
|
+
expect(result.errors).toHaveLength(1);
|
|
288
|
+
expect(result.errors[0].file).toBe('test/example.test.ts');
|
|
289
|
+
expect(result.errors[0].line).toBeUndefined(); // Not 0!
|
|
290
|
+
expect(result.errors[0].column).toBeUndefined(); // Not 0!
|
|
291
|
+
expect(result.errors[0].message).toContain('Example Suite › test without location');
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
describe('plugin metadata', () => {
|
|
295
|
+
it('should have correct metadata', () => {
|
|
296
|
+
expect(jestPlugin.metadata.name).toBe('jest');
|
|
297
|
+
expect(jestPlugin.metadata.description).toContain('Jest');
|
|
298
|
+
expect(jestPlugin.priority).toBe(90);
|
|
299
|
+
expect(jestPlugin.hints.anyOf).toContain('FAIL');
|
|
300
|
+
expect(jestPlugin.hints.anyOf).toContain('✕');
|
|
301
|
+
expect(jestPlugin.hints.anyOf).toContain('●');
|
|
302
|
+
});
|
|
303
|
+
it('should include sample test cases', () => {
|
|
304
|
+
expect(jestPlugin.samples).toBeDefined();
|
|
305
|
+
expect(jestPlugin.samples.length).toBeGreaterThan(0);
|
|
306
|
+
const singleFailure = jestPlugin.samples.find(s => s.name === 'single-test-failure');
|
|
307
|
+
expect(singleFailure).toBeDefined();
|
|
308
|
+
expect(singleFailure?.expectedErrors).toBe(1);
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
describe('edge cases', () => {
|
|
312
|
+
it('should handle output with only FAIL line (no test details)', () => {
|
|
313
|
+
const output = ' FAIL test/example.test.ts';
|
|
314
|
+
const result = extract(output);
|
|
315
|
+
expect(result.errors).toHaveLength(0);
|
|
316
|
+
expect(result.summary).toBe('No test failures detected');
|
|
317
|
+
});
|
|
318
|
+
it('should not extract suite names as failures', () => {
|
|
319
|
+
const output = `
|
|
320
|
+
FAIL test/example.test.ts
|
|
321
|
+
Example Suite
|
|
322
|
+
Another Suite
|
|
323
|
+
✕ actual test (10 ms)
|
|
324
|
+
`.trim();
|
|
325
|
+
const result = extract(output);
|
|
326
|
+
expect(result.errors).toHaveLength(1);
|
|
327
|
+
expect(result.errors[0].message).toContain('actual test');
|
|
328
|
+
});
|
|
329
|
+
it('should handle malformed test markers', () => {
|
|
330
|
+
const output = `
|
|
331
|
+
FAIL test/example.test.ts
|
|
332
|
+
Example Suite
|
|
333
|
+
✕
|
|
334
|
+
✕ valid test (10 ms)
|
|
335
|
+
`.trim();
|
|
336
|
+
const result = extract(output);
|
|
337
|
+
// Should only extract the valid test
|
|
338
|
+
expect(result.errors).toHaveLength(1);
|
|
339
|
+
expect(result.errors[0].message).toContain('valid test');
|
|
340
|
+
});
|
|
341
|
+
it('should handle FAIL with module name prefix', () => {
|
|
342
|
+
const output = `
|
|
343
|
+
FAIL node_modules test/example.test.ts
|
|
344
|
+
Example Suite
|
|
345
|
+
✕ should pass (10 ms)
|
|
346
|
+
`.trim();
|
|
347
|
+
const result = extract(output);
|
|
348
|
+
expect(result.errors).toHaveLength(1);
|
|
349
|
+
expect(result.errors[0].file).toBe('test/example.test.ts');
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
//# sourceMappingURL=index.test.js.map
|