@vibe-validate/extractors 0.16.0 → 0.17.0-rc.10

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 (167) hide show
  1. package/dist/extractor-registry.d.ts +103 -0
  2. package/dist/extractor-registry.d.ts.map +1 -0
  3. package/dist/extractor-registry.js +278 -0
  4. package/dist/extractor-registry.js.map +1 -0
  5. package/dist/extractors/ava/index.d.ts +23 -0
  6. package/dist/extractors/ava/index.d.ts.map +1 -0
  7. package/dist/extractors/ava/index.js +507 -0
  8. package/dist/extractors/ava/index.js.map +1 -0
  9. package/dist/extractors/ava/index.test.d.ts +7 -0
  10. package/dist/extractors/ava/index.test.d.ts.map +1 -0
  11. package/dist/extractors/ava/index.test.js +408 -0
  12. package/dist/extractors/ava/index.test.js.map +1 -0
  13. package/dist/extractors/eslint/index.d.ts +18 -0
  14. package/dist/extractors/eslint/index.d.ts.map +1 -0
  15. package/dist/extractors/eslint/index.js +206 -0
  16. package/dist/extractors/eslint/index.js.map +1 -0
  17. package/dist/extractors/eslint/index.test.d.ts +9 -0
  18. package/dist/extractors/eslint/index.test.d.ts.map +1 -0
  19. package/dist/extractors/eslint/index.test.js +191 -0
  20. package/dist/extractors/eslint/index.test.js.map +1 -0
  21. package/dist/extractors/generic/index.d.ts +30 -0
  22. package/dist/extractors/generic/index.d.ts.map +1 -0
  23. package/dist/extractors/generic/index.js +140 -0
  24. package/dist/extractors/generic/index.js.map +1 -0
  25. package/dist/extractors/generic/index.test.d.ts +7 -0
  26. package/dist/extractors/generic/index.test.d.ts.map +1 -0
  27. package/dist/extractors/generic/index.test.js +61 -0
  28. package/dist/extractors/generic/index.test.js.map +1 -0
  29. package/dist/extractors/jasmine/index.d.ts +17 -0
  30. package/dist/extractors/jasmine/index.d.ts.map +1 -0
  31. package/dist/extractors/jasmine/index.js +254 -0
  32. package/dist/extractors/jasmine/index.js.map +1 -0
  33. package/dist/extractors/jasmine/index.test.d.ts +7 -0
  34. package/dist/extractors/jasmine/index.test.d.ts.map +1 -0
  35. package/dist/extractors/jasmine/index.test.js +345 -0
  36. package/dist/extractors/jasmine/index.test.js.map +1 -0
  37. package/dist/extractors/jest/index.d.ts +17 -0
  38. package/dist/extractors/jest/index.d.ts.map +1 -0
  39. package/dist/extractors/jest/index.js +278 -0
  40. package/dist/extractors/jest/index.js.map +1 -0
  41. package/dist/extractors/jest/index.test.d.ts +9 -0
  42. package/dist/extractors/jest/index.test.d.ts.map +1 -0
  43. package/dist/extractors/jest/index.test.js +353 -0
  44. package/dist/extractors/jest/index.test.js.map +1 -0
  45. package/dist/extractors/junit/index.d.ts +18 -0
  46. package/dist/extractors/junit/index.d.ts.map +1 -0
  47. package/dist/extractors/junit/index.js +259 -0
  48. package/dist/extractors/junit/index.js.map +1 -0
  49. package/dist/extractors/junit/index.test.d.ts +7 -0
  50. package/dist/extractors/junit/index.test.d.ts.map +1 -0
  51. package/dist/extractors/junit/index.test.js +341 -0
  52. package/dist/extractors/junit/index.test.js.map +1 -0
  53. package/dist/extractors/maven-checkstyle/index.d.ts +23 -0
  54. package/dist/extractors/maven-checkstyle/index.d.ts.map +1 -0
  55. package/dist/extractors/maven-checkstyle/index.js +263 -0
  56. package/dist/extractors/maven-checkstyle/index.js.map +1 -0
  57. package/dist/extractors/maven-checkstyle/index.test.d.ts +2 -0
  58. package/dist/extractors/maven-checkstyle/index.test.d.ts.map +1 -0
  59. package/dist/extractors/maven-checkstyle/index.test.js +197 -0
  60. package/dist/extractors/maven-checkstyle/index.test.js.map +1 -0
  61. package/dist/extractors/maven-compiler/index.d.ts +23 -0
  62. package/dist/extractors/maven-compiler/index.d.ts.map +1 -0
  63. package/dist/extractors/maven-compiler/index.js +271 -0
  64. package/dist/extractors/maven-compiler/index.js.map +1 -0
  65. package/dist/extractors/maven-compiler/index.test.d.ts +2 -0
  66. package/dist/extractors/maven-compiler/index.test.d.ts.map +1 -0
  67. package/dist/extractors/maven-compiler/index.test.js +189 -0
  68. package/dist/extractors/maven-compiler/index.test.js.map +1 -0
  69. package/dist/extractors/maven-surefire/index.d.ts +23 -0
  70. package/dist/extractors/maven-surefire/index.d.ts.map +1 -0
  71. package/dist/extractors/maven-surefire/index.js +292 -0
  72. package/dist/extractors/maven-surefire/index.js.map +1 -0
  73. package/dist/extractors/maven-surefire/index.test.d.ts +2 -0
  74. package/dist/extractors/maven-surefire/index.test.d.ts.map +1 -0
  75. package/dist/extractors/maven-surefire/index.test.js +169 -0
  76. package/dist/extractors/maven-surefire/index.test.js.map +1 -0
  77. package/dist/extractors/mocha/index.d.ts +17 -0
  78. package/dist/extractors/mocha/index.d.ts.map +1 -0
  79. package/dist/extractors/mocha/index.js +241 -0
  80. package/dist/extractors/mocha/index.js.map +1 -0
  81. package/dist/extractors/mocha/index.test.d.ts +7 -0
  82. package/dist/extractors/mocha/index.test.d.ts.map +1 -0
  83. package/dist/extractors/mocha/index.test.js +300 -0
  84. package/dist/extractors/mocha/index.test.js.map +1 -0
  85. package/dist/extractors/playwright/index.d.ts +17 -0
  86. package/dist/extractors/playwright/index.d.ts.map +1 -0
  87. package/dist/extractors/playwright/index.js +320 -0
  88. package/dist/extractors/playwright/index.js.map +1 -0
  89. package/dist/extractors/playwright/index.test.d.ts +7 -0
  90. package/dist/extractors/playwright/index.test.d.ts.map +1 -0
  91. package/dist/extractors/playwright/index.test.js +274 -0
  92. package/dist/extractors/playwright/index.test.js.map +1 -0
  93. package/dist/extractors/tap/index.d.ts +23 -0
  94. package/dist/extractors/tap/index.d.ts.map +1 -0
  95. package/dist/extractors/tap/index.js +352 -0
  96. package/dist/extractors/tap/index.js.map +1 -0
  97. package/dist/extractors/tap/index.test.d.ts +7 -0
  98. package/dist/extractors/tap/index.test.d.ts.map +1 -0
  99. package/dist/extractors/tap/index.test.js +100 -0
  100. package/dist/extractors/tap/index.test.js.map +1 -0
  101. package/dist/extractors/typescript/index.d.ts +17 -0
  102. package/dist/extractors/typescript/index.d.ts.map +1 -0
  103. package/dist/extractors/typescript/index.js +150 -0
  104. package/dist/extractors/typescript/index.js.map +1 -0
  105. package/dist/extractors/typescript/index.test.d.ts +9 -0
  106. package/dist/extractors/typescript/index.test.d.ts.map +1 -0
  107. package/dist/extractors/typescript/index.test.js +177 -0
  108. package/dist/extractors/typescript/index.test.js.map +1 -0
  109. package/dist/extractors/vitest/index.d.ts +17 -0
  110. package/dist/extractors/vitest/index.d.ts.map +1 -0
  111. package/dist/extractors/vitest/index.js +564 -0
  112. package/dist/extractors/vitest/index.js.map +1 -0
  113. package/dist/extractors/vitest/index.test.d.ts +9 -0
  114. package/dist/extractors/vitest/index.test.d.ts.map +1 -0
  115. package/dist/extractors/vitest/index.test.js +373 -0
  116. package/dist/extractors/vitest/index.test.js.map +1 -0
  117. package/dist/index.d.ts +27 -11
  118. package/dist/index.d.ts.map +1 -1
  119. package/dist/index.js +27 -11
  120. package/dist/index.js.map +1 -1
  121. package/dist/maven-checkstyle-extractor.d.ts +20 -0
  122. package/dist/maven-checkstyle-extractor.d.ts.map +1 -0
  123. package/dist/maven-checkstyle-extractor.js +208 -0
  124. package/dist/maven-checkstyle-extractor.js.map +1 -0
  125. package/dist/maven-compiler-extractor.d.ts +20 -0
  126. package/dist/maven-compiler-extractor.d.ts.map +1 -0
  127. package/dist/maven-compiler-extractor.js +218 -0
  128. package/dist/maven-compiler-extractor.js.map +1 -0
  129. package/dist/maven-surefire-extractor.d.ts +20 -0
  130. package/dist/maven-surefire-extractor.d.ts.map +1 -0
  131. package/dist/maven-surefire-extractor.js +228 -0
  132. package/dist/maven-surefire-extractor.js.map +1 -0
  133. package/dist/maven-utils.d.ts +24 -0
  134. package/dist/maven-utils.d.ts.map +1 -0
  135. package/dist/maven-utils.js +36 -0
  136. package/dist/maven-utils.js.map +1 -0
  137. package/dist/plugin-loader.d.ts +82 -0
  138. package/dist/plugin-loader.d.ts.map +1 -0
  139. package/dist/plugin-loader.js +201 -0
  140. package/dist/plugin-loader.js.map +1 -0
  141. package/dist/result-schema.d.ts +59 -9
  142. package/dist/result-schema.d.ts.map +1 -1
  143. package/dist/result-schema.js +3 -19
  144. package/dist/result-schema.js.map +1 -1
  145. package/dist/sandbox.d.ts +161 -0
  146. package/dist/sandbox.d.ts.map +1 -0
  147. package/dist/sandbox.js +254 -0
  148. package/dist/sandbox.js.map +1 -0
  149. package/dist/sandbox.test.d.ts +8 -0
  150. package/dist/sandbox.test.d.ts.map +1 -0
  151. package/dist/sandbox.test.js +395 -0
  152. package/dist/sandbox.test.js.map +1 -0
  153. package/dist/sandboxed-extractor.d.ts +46 -0
  154. package/dist/sandboxed-extractor.d.ts.map +1 -0
  155. package/dist/sandboxed-extractor.js +172 -0
  156. package/dist/sandboxed-extractor.js.map +1 -0
  157. package/dist/sandboxed-extractor.test.d.ts +5 -0
  158. package/dist/sandboxed-extractor.test.d.ts.map +1 -0
  159. package/dist/sandboxed-extractor.test.js +346 -0
  160. package/dist/sandboxed-extractor.test.js.map +1 -0
  161. package/dist/smart-extractor.d.ts +22 -10
  162. package/dist/smart-extractor.d.ts.map +1 -1
  163. package/dist/smart-extractor.js +116 -163
  164. package/dist/smart-extractor.js.map +1 -1
  165. package/dist/types.d.ts +94 -0
  166. package/dist/types.d.ts.map +1 -1
  167. 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,9 @@
1
+ /**
2
+ * Jest Extractor Plugin Tests
3
+ *
4
+ * Tests Jest test framework error parsing and formatting.
5
+ *
6
+ * @package @vibe-validate/extractors
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=index.test.d.ts.map
@@ -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