qa360 1.4.5 → 2.0.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/README.md +1 -1
- package/dist/commands/ai.d.ts +41 -0
- package/dist/commands/ai.js +499 -0
- package/dist/commands/ask.js +12 -12
- package/dist/commands/coverage.d.ts +8 -0
- package/dist/commands/coverage.js +252 -0
- package/dist/commands/explain.d.ts +27 -0
- package/dist/commands/explain.js +630 -0
- package/dist/commands/flakiness.d.ts +73 -0
- package/dist/commands/flakiness.js +435 -0
- package/dist/commands/generate.d.ts +66 -0
- package/dist/commands/generate.js +438 -0
- package/dist/commands/init.d.ts +56 -9
- package/dist/commands/init.js +217 -10
- package/dist/commands/monitor.d.ts +27 -0
- package/dist/commands/monitor.js +225 -0
- package/dist/commands/ollama.d.ts +40 -0
- package/dist/commands/ollama.js +301 -0
- package/dist/commands/pack.d.ts +37 -9
- package/dist/commands/pack.js +240 -141
- package/dist/commands/regression.d.ts +8 -0
- package/dist/commands/regression.js +340 -0
- package/dist/commands/repair.d.ts +26 -0
- package/dist/commands/repair.js +307 -0
- package/dist/commands/retry.d.ts +43 -0
- package/dist/commands/retry.js +275 -0
- package/dist/commands/run.d.ts +8 -3
- package/dist/commands/run.js +45 -31
- package/dist/commands/slo.d.ts +8 -0
- package/dist/commands/slo.js +327 -0
- package/dist/core/adapters/playwright-native-api.d.ts +183 -0
- package/dist/core/adapters/playwright-native-api.js +461 -0
- package/dist/core/adapters/playwright-ui.d.ts +7 -0
- package/dist/core/adapters/playwright-ui.js +29 -1
- package/dist/core/ai/anthropic-provider.d.ts +50 -0
- package/dist/core/ai/anthropic-provider.js +211 -0
- package/dist/core/ai/deepseek-provider.d.ts +81 -0
- package/dist/core/ai/deepseek-provider.js +254 -0
- package/dist/core/ai/index.d.ts +60 -0
- package/dist/core/ai/index.js +18 -0
- package/dist/core/ai/llm-client.d.ts +45 -0
- package/dist/core/ai/llm-client.js +7 -0
- package/dist/core/ai/mock-provider.d.ts +49 -0
- package/dist/core/ai/mock-provider.js +121 -0
- package/dist/core/ai/ollama-provider.d.ts +78 -0
- package/dist/core/ai/ollama-provider.js +192 -0
- package/dist/core/ai/openai-provider.d.ts +48 -0
- package/dist/core/ai/openai-provider.js +188 -0
- package/dist/core/ai/provider-factory.d.ts +160 -0
- package/dist/core/ai/provider-factory.js +269 -0
- package/dist/core/auth/api-key-provider.d.ts +16 -0
- package/dist/core/auth/api-key-provider.js +63 -0
- package/dist/core/auth/aws-iam-provider.d.ts +35 -0
- package/dist/core/auth/aws-iam-provider.js +177 -0
- package/dist/core/auth/azure-ad-provider.d.ts +15 -0
- package/dist/core/auth/azure-ad-provider.js +99 -0
- package/dist/core/auth/basic-auth-provider.d.ts +26 -0
- package/dist/core/auth/basic-auth-provider.js +111 -0
- package/dist/core/auth/gcp-adc-provider.d.ts +27 -0
- package/dist/core/auth/gcp-adc-provider.js +126 -0
- package/dist/core/auth/index.d.ts +238 -0
- package/dist/core/auth/index.js +82 -0
- package/dist/core/auth/jwt-provider.d.ts +19 -0
- package/dist/core/auth/jwt-provider.js +160 -0
- package/dist/core/auth/manager.d.ts +84 -0
- package/dist/core/auth/manager.js +230 -0
- package/dist/core/auth/oauth2-provider.d.ts +17 -0
- package/dist/core/auth/oauth2-provider.js +114 -0
- package/dist/core/auth/totp-provider.d.ts +31 -0
- package/dist/core/auth/totp-provider.js +134 -0
- package/dist/core/auth/ui-login-provider.d.ts +26 -0
- package/dist/core/auth/ui-login-provider.js +198 -0
- package/dist/core/cache/index.d.ts +7 -0
- package/dist/core/cache/index.js +6 -0
- package/dist/core/cache/lru-cache.d.ts +203 -0
- package/dist/core/cache/lru-cache.js +397 -0
- package/dist/core/coverage/analyzer.d.ts +101 -0
- package/dist/core/coverage/analyzer.js +415 -0
- package/dist/core/coverage/collector.d.ts +74 -0
- package/dist/core/coverage/collector.js +459 -0
- package/dist/core/coverage/config.d.ts +37 -0
- package/dist/core/coverage/config.js +156 -0
- package/dist/core/coverage/index.d.ts +11 -0
- package/dist/core/coverage/index.js +15 -0
- package/dist/core/coverage/types.d.ts +267 -0
- package/dist/core/coverage/types.js +6 -0
- package/dist/core/coverage/vault.d.ts +95 -0
- package/dist/core/coverage/vault.js +405 -0
- package/dist/core/dashboard/assets.d.ts +6 -0
- package/dist/core/dashboard/assets.js +690 -0
- package/dist/core/dashboard/index.d.ts +6 -0
- package/dist/core/dashboard/index.js +5 -0
- package/dist/core/dashboard/server.d.ts +72 -0
- package/dist/core/dashboard/server.js +354 -0
- package/dist/core/dashboard/types.d.ts +70 -0
- package/dist/core/dashboard/types.js +5 -0
- package/dist/core/discoverer/index.d.ts +115 -0
- package/dist/core/discoverer/index.js +250 -0
- package/dist/core/flakiness/index.d.ts +228 -0
- package/dist/core/flakiness/index.js +384 -0
- package/dist/core/generation/code-formatter.d.ts +111 -0
- package/dist/core/generation/code-formatter.js +307 -0
- package/dist/core/generation/code-generator.d.ts +144 -0
- package/dist/core/generation/code-generator.js +293 -0
- package/dist/core/generation/generator.d.ts +40 -0
- package/dist/core/generation/generator.js +76 -0
- package/dist/core/generation/index.d.ts +30 -0
- package/dist/core/generation/index.js +28 -0
- package/dist/core/generation/pack-generator.d.ts +107 -0
- package/dist/core/generation/pack-generator.js +416 -0
- package/dist/core/generation/prompt-builder.d.ts +132 -0
- package/dist/core/generation/prompt-builder.js +672 -0
- package/dist/core/generation/source-analyzer.d.ts +213 -0
- package/dist/core/generation/source-analyzer.js +657 -0
- package/dist/core/generation/test-optimizer.d.ts +117 -0
- package/dist/core/generation/test-optimizer.js +328 -0
- package/dist/core/generation/types.d.ts +214 -0
- package/dist/core/generation/types.js +4 -0
- package/dist/core/index.d.ts +23 -1
- package/dist/core/index.js +39 -0
- package/dist/core/pack/validator.js +31 -1
- package/dist/core/pack-v2/index.d.ts +9 -0
- package/dist/core/pack-v2/index.js +8 -0
- package/dist/core/pack-v2/loader.d.ts +62 -0
- package/dist/core/pack-v2/loader.js +231 -0
- package/dist/core/pack-v2/migrator.d.ts +56 -0
- package/dist/core/pack-v2/migrator.js +455 -0
- package/dist/core/pack-v2/validator.d.ts +61 -0
- package/dist/core/pack-v2/validator.js +577 -0
- package/dist/core/regression/detector.d.ts +107 -0
- package/dist/core/regression/detector.js +497 -0
- package/dist/core/regression/index.d.ts +9 -0
- package/dist/core/regression/index.js +11 -0
- package/dist/core/regression/trend-analyzer.d.ts +102 -0
- package/dist/core/regression/trend-analyzer.js +345 -0
- package/dist/core/regression/types.d.ts +222 -0
- package/dist/core/regression/types.js +7 -0
- package/dist/core/regression/vault.d.ts +87 -0
- package/dist/core/regression/vault.js +289 -0
- package/dist/core/repair/engine/fixer.d.ts +24 -0
- package/dist/core/repair/engine/fixer.js +226 -0
- package/dist/core/repair/engine/suggestion-engine.d.ts +18 -0
- package/dist/core/repair/engine/suggestion-engine.js +187 -0
- package/dist/core/repair/index.d.ts +10 -0
- package/dist/core/repair/index.js +13 -0
- package/dist/core/repair/repairer.d.ts +90 -0
- package/dist/core/repair/repairer.js +284 -0
- package/dist/core/repair/types.d.ts +91 -0
- package/dist/core/repair/types.js +6 -0
- package/dist/core/repair/utils/error-analyzer.d.ts +28 -0
- package/dist/core/repair/utils/error-analyzer.js +264 -0
- package/dist/core/retry/flakiness-integration.d.ts +60 -0
- package/dist/core/retry/flakiness-integration.js +228 -0
- package/dist/core/retry/index.d.ts +14 -0
- package/dist/core/retry/index.js +16 -0
- package/dist/core/retry/retry-engine.d.ts +80 -0
- package/dist/core/retry/retry-engine.js +296 -0
- package/dist/core/retry/types.d.ts +178 -0
- package/dist/core/retry/types.js +52 -0
- package/dist/core/retry/vault.d.ts +77 -0
- package/dist/core/retry/vault.js +304 -0
- package/dist/core/runner/e2e-helpers.d.ts +102 -0
- package/dist/core/runner/e2e-helpers.js +153 -0
- package/dist/core/runner/phase3-runner.d.ts +101 -2
- package/dist/core/runner/phase3-runner.js +559 -24
- package/dist/core/self-healing/assertion-healer.d.ts +97 -0
- package/dist/core/self-healing/assertion-healer.js +371 -0
- package/dist/core/self-healing/engine.d.ts +122 -0
- package/dist/core/self-healing/engine.js +538 -0
- package/dist/core/self-healing/index.d.ts +10 -0
- package/dist/core/self-healing/index.js +11 -0
- package/dist/core/self-healing/selector-healer.d.ts +103 -0
- package/dist/core/self-healing/selector-healer.js +372 -0
- package/dist/core/self-healing/types.d.ts +152 -0
- package/dist/core/self-healing/types.js +6 -0
- package/dist/core/slo/config.d.ts +107 -0
- package/dist/core/slo/config.js +360 -0
- package/dist/core/slo/index.d.ts +11 -0
- package/dist/core/slo/index.js +15 -0
- package/dist/core/slo/sli-calculator.d.ts +92 -0
- package/dist/core/slo/sli-calculator.js +364 -0
- package/dist/core/slo/slo-tracker.d.ts +148 -0
- package/dist/core/slo/slo-tracker.js +379 -0
- package/dist/core/slo/types.d.ts +281 -0
- package/dist/core/slo/types.js +7 -0
- package/dist/core/slo/vault.d.ts +102 -0
- package/dist/core/slo/vault.js +427 -0
- package/dist/core/tui/index.d.ts +7 -0
- package/dist/core/tui/index.js +6 -0
- package/dist/core/tui/monitor.d.ts +92 -0
- package/dist/core/tui/monitor.js +271 -0
- package/dist/core/tui/renderer.d.ts +33 -0
- package/dist/core/tui/renderer.js +218 -0
- package/dist/core/tui/types.d.ts +63 -0
- package/dist/core/tui/types.js +5 -0
- package/dist/core/types/pack-v2.d.ts +425 -0
- package/dist/core/types/pack-v2.js +8 -0
- package/dist/core/vault/index.d.ts +116 -0
- package/dist/core/vault/index.js +400 -5
- package/dist/core/watch/index.d.ts +7 -0
- package/dist/core/watch/index.js +6 -0
- package/dist/core/watch/watch-mode.d.ts +213 -0
- package/dist/core/watch/watch-mode.js +389 -0
- package/dist/index.js +68 -68
- package/dist/utils/config.d.ts +5 -0
- package/dist/utils/config.js +136 -0
- package/package.json +5 -1
- package/dist/core/adapters/playwright-api.d.ts +0 -82
- package/dist/core/adapters/playwright-api.js +0 -264
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fix Suggestion Engine
|
|
3
|
+
*
|
|
4
|
+
* Generates fix suggestions using LLM.
|
|
5
|
+
*/
|
|
6
|
+
import { createLLMProvider } from '../../ai/index.js';
|
|
7
|
+
/**
|
|
8
|
+
* Generate fix suggestions for test errors
|
|
9
|
+
*/
|
|
10
|
+
export async function generateFixSuggestions(errors, testCode, sourceCode, options) {
|
|
11
|
+
const suggestions = [];
|
|
12
|
+
for (const error of errors) {
|
|
13
|
+
const errorSuggestions = await generateSuggestionsForError(error, testCode, sourceCode, options);
|
|
14
|
+
suggestions.push(...errorSuggestions);
|
|
15
|
+
}
|
|
16
|
+
// Sort by confidence and limit
|
|
17
|
+
const max = options?.maxSuggestions ?? 10;
|
|
18
|
+
suggestions.sort((a, b) => b.confidence - a.confidence);
|
|
19
|
+
return suggestions.slice(0, max);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Generate suggestions for a single error
|
|
23
|
+
*/
|
|
24
|
+
async function generateSuggestionsForError(error, testCode, sourceCode, options) {
|
|
25
|
+
const suggestions = [];
|
|
26
|
+
// Use LLM to generate suggestions
|
|
27
|
+
const llmResponse = await callLLMForFix(error, testCode, sourceCode, options);
|
|
28
|
+
// Parse LLM response into suggestions
|
|
29
|
+
const parsed = parseLLMResponse(llmResponse, error);
|
|
30
|
+
return parsed.length > 0 ? parsed : generateFallbackSuggestions(error);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Call LLM to generate fix
|
|
34
|
+
*/
|
|
35
|
+
async function callLLMForFix(error, testCode, sourceCode, options) {
|
|
36
|
+
const providerType = options?.llmProvider || 'ollama';
|
|
37
|
+
const prompt = buildRepairPrompt(error, testCode, sourceCode);
|
|
38
|
+
try {
|
|
39
|
+
const provider = await createLLMProvider({ preferred: providerType });
|
|
40
|
+
const result = await provider.generate({
|
|
41
|
+
prompt,
|
|
42
|
+
maxTokens: 500,
|
|
43
|
+
temperature: 0.3
|
|
44
|
+
});
|
|
45
|
+
return result.content || '';
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Fallback to empty response
|
|
49
|
+
return '';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build prompt for repair
|
|
54
|
+
*/
|
|
55
|
+
function buildRepairPrompt(error, testCode, sourceCode) {
|
|
56
|
+
return `You are a test repair expert. Analyze this test error and suggest fixes.
|
|
57
|
+
|
|
58
|
+
Test file: ${error.testFile}
|
|
59
|
+
Test: ${error.testName}
|
|
60
|
+
Error type: ${error.type}
|
|
61
|
+
Error message: ${error.message}
|
|
62
|
+
|
|
63
|
+
Test code:
|
|
64
|
+
\`\`\`typescript
|
|
65
|
+
${testCode}
|
|
66
|
+
\`\`\`
|
|
67
|
+
|
|
68
|
+
${sourceCode ? `Source code:\n\`\`\`typescript\n${sourceCode}\n\`\`\`\n` : ''}
|
|
69
|
+
|
|
70
|
+
Provide a JSON response with this format:
|
|
71
|
+
{
|
|
72
|
+
"fixes": [
|
|
73
|
+
{
|
|
74
|
+
"type": "fix_type",
|
|
75
|
+
"description": "Brief description",
|
|
76
|
+
"code": "code to apply",
|
|
77
|
+
"line": ${error.line},
|
|
78
|
+
"confidence": 0.8,
|
|
79
|
+
"reason": "Why this fix works"
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
Only respond with valid JSON, no explanations.`;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Parse LLM response into suggestions
|
|
88
|
+
*/
|
|
89
|
+
function parseLLMResponse(response, error) {
|
|
90
|
+
try {
|
|
91
|
+
// Try to extract JSON from response
|
|
92
|
+
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
93
|
+
if (!jsonMatch)
|
|
94
|
+
return [];
|
|
95
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
96
|
+
if (Array.isArray(parsed.fixes)) {
|
|
97
|
+
return parsed.fixes.map((fix) => ({
|
|
98
|
+
type: fix.type || 'update_expectation',
|
|
99
|
+
description: fix.description || 'Fix test',
|
|
100
|
+
code: fix.code || '',
|
|
101
|
+
filePath: error.testFile,
|
|
102
|
+
line: fix.line || error.line,
|
|
103
|
+
confidence: fix.confidence || 0.5,
|
|
104
|
+
reason: fix.reason || 'Generated fix',
|
|
105
|
+
estimatedEffort: fix.estimatedEffort || 'medium'
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Failed to parse, return empty
|
|
111
|
+
}
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Generate fallback suggestions when LLM fails
|
|
116
|
+
*/
|
|
117
|
+
function generateFallbackSuggestions(error) {
|
|
118
|
+
const suggestions = [];
|
|
119
|
+
switch (error.type) {
|
|
120
|
+
case 'assertion_error':
|
|
121
|
+
suggestions.push({
|
|
122
|
+
type: 'update_expectation',
|
|
123
|
+
description: 'Update expected value to match actual',
|
|
124
|
+
code: `// Update expectation based on error: ${error.message}`,
|
|
125
|
+
filePath: error.testFile,
|
|
126
|
+
line: error.line,
|
|
127
|
+
confidence: 0.6,
|
|
128
|
+
reason: 'Assertion failed - check expected vs actual',
|
|
129
|
+
estimatedEffort: 'low'
|
|
130
|
+
});
|
|
131
|
+
break;
|
|
132
|
+
case 'reference_error':
|
|
133
|
+
suggestions.push({
|
|
134
|
+
type: 'fix_import',
|
|
135
|
+
description: 'Import missing variable or module',
|
|
136
|
+
code: `// Import missing: ${error.message.match(/(\w+) is not defined/)?.[1] || 'unknown'}`,
|
|
137
|
+
filePath: error.testFile,
|
|
138
|
+
line: 1,
|
|
139
|
+
confidence: 0.7,
|
|
140
|
+
reason: 'Reference error - missing import or definition',
|
|
141
|
+
estimatedEffort: 'low'
|
|
142
|
+
});
|
|
143
|
+
break;
|
|
144
|
+
case 'timeout_error':
|
|
145
|
+
suggestions.push({
|
|
146
|
+
type: 'fix_timeout',
|
|
147
|
+
description: 'Increase test timeout',
|
|
148
|
+
code: `test.setTimeout(10000); // Increase timeout`,
|
|
149
|
+
filePath: error.testFile,
|
|
150
|
+
line: error.line,
|
|
151
|
+
confidence: 0.8,
|
|
152
|
+
reason: 'Test timed out - increase timeout or optimize',
|
|
153
|
+
estimatedEffort: 'low'
|
|
154
|
+
});
|
|
155
|
+
break;
|
|
156
|
+
case 'import_error':
|
|
157
|
+
suggestions.push({
|
|
158
|
+
type: 'add_dependency',
|
|
159
|
+
description: 'Install missing dependency',
|
|
160
|
+
code: `// Run: npm install ${error.message.match(/Cannot find module ['"]([^'"]+)['"]/)?.[1] || 'missing-package'}`,
|
|
161
|
+
filePath: error.testFile,
|
|
162
|
+
line: 1,
|
|
163
|
+
confidence: 0.7,
|
|
164
|
+
reason: 'Module not found - install or fix import path',
|
|
165
|
+
estimatedEffort: 'low'
|
|
166
|
+
});
|
|
167
|
+
break;
|
|
168
|
+
default:
|
|
169
|
+
suggestions.push({
|
|
170
|
+
type: 'manual_review',
|
|
171
|
+
description: 'Manual review required',
|
|
172
|
+
code: `// Review error: ${error.message}`,
|
|
173
|
+
filePath: error.testFile,
|
|
174
|
+
line: error.line,
|
|
175
|
+
confidence: 0.3,
|
|
176
|
+
reason: 'Unknown error type - manual intervention needed',
|
|
177
|
+
estimatedEffort: 'high'
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return suggestions;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get quick fix suggestions without LLM
|
|
184
|
+
*/
|
|
185
|
+
export function getQuickFixes(error) {
|
|
186
|
+
return generateFallbackSuggestions(error);
|
|
187
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-Repair Module
|
|
3
|
+
*
|
|
4
|
+
* Automatic test failure repair using AI.
|
|
5
|
+
*/
|
|
6
|
+
export type { TestError, ErrorType, FixSuggestion, FixType, RepairAnalysis, FixResult, FileChange, RepairOptions, TestRepairResult } from './types.js';
|
|
7
|
+
export { analyzeTestError, classifyErrors, isAutoFixable, getCommonFixes, type ParsedError } from './utils/error-analyzer.js';
|
|
8
|
+
export { generateFixSuggestions, getQuickFixes, type SuggestionOptions } from './engine/suggestion-engine.js';
|
|
9
|
+
export { applyFixes, generateDiff, revertBackup, type FixerOptions } from './engine/fixer.js';
|
|
10
|
+
export { TestRepairer, analyzeTestErrors, createRepairJSONReport, type RepairerOptions, type RepairProgress, type RepairReport, type TestRunResult } from './repairer.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-Repair Module
|
|
3
|
+
*
|
|
4
|
+
* Automatic test failure repair using AI.
|
|
5
|
+
*/
|
|
6
|
+
// Error Analyzer
|
|
7
|
+
export { analyzeTestError, classifyErrors, isAutoFixable, getCommonFixes } from './utils/error-analyzer.js';
|
|
8
|
+
// Suggestion Engine
|
|
9
|
+
export { generateFixSuggestions, getQuickFixes } from './engine/suggestion-engine.js';
|
|
10
|
+
// Fixer
|
|
11
|
+
export { applyFixes, generateDiff, revertBackup } from './engine/fixer.js';
|
|
12
|
+
// Repairer
|
|
13
|
+
export { TestRepairer, analyzeTestErrors, createRepairJSONReport } from './repairer.js';
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Repairer
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the automatic test repair process.
|
|
5
|
+
*/
|
|
6
|
+
import type { TestError, FixSuggestion, RepairAnalysis, TestRepairResult, RepairOptions } from './types.js';
|
|
7
|
+
import { type FixerOptions } from './engine/fixer.js';
|
|
8
|
+
export interface RepairerOptions extends RepairOptions {
|
|
9
|
+
fixer?: FixerOptions;
|
|
10
|
+
maxIterations?: number;
|
|
11
|
+
onProgress?: (progress: RepairProgress) => void;
|
|
12
|
+
}
|
|
13
|
+
export interface RepairProgress {
|
|
14
|
+
stage: 'analyzing' | 'suggesting' | 'applying' | 'verifying' | 'complete';
|
|
15
|
+
message: string;
|
|
16
|
+
iteration?: number;
|
|
17
|
+
fixesApplied?: number;
|
|
18
|
+
errorsRemaining?: number;
|
|
19
|
+
}
|
|
20
|
+
export interface RepairReport {
|
|
21
|
+
analysis: RepairAnalysis;
|
|
22
|
+
result: TestRepairResult;
|
|
23
|
+
duration: number;
|
|
24
|
+
success: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Main test repairer orchestrator
|
|
28
|
+
*/
|
|
29
|
+
export declare class TestRepairer {
|
|
30
|
+
private options;
|
|
31
|
+
private iteration;
|
|
32
|
+
constructor(options?: RepairerOptions);
|
|
33
|
+
/**
|
|
34
|
+
* Analyze test failures and generate repair suggestions
|
|
35
|
+
*/
|
|
36
|
+
analyze(testFile: string, testResults?: TestRunResult): Promise<RepairAnalysis>;
|
|
37
|
+
/**
|
|
38
|
+
* Repair tests automatically
|
|
39
|
+
*/
|
|
40
|
+
repair(testFile: string, testResults: TestRunResult): Promise<RepairReport>;
|
|
41
|
+
/**
|
|
42
|
+
* Get quick fixes without LLM
|
|
43
|
+
*/
|
|
44
|
+
getQuickFixes(testFile: string, error: TestError): Promise<FixSuggestion[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Generate repair report
|
|
47
|
+
*/
|
|
48
|
+
generateReport(report: RepairReport): string;
|
|
49
|
+
/**
|
|
50
|
+
* Read test code from file
|
|
51
|
+
*/
|
|
52
|
+
private readTestCode;
|
|
53
|
+
/**
|
|
54
|
+
* Infer source file path from test file path
|
|
55
|
+
*/
|
|
56
|
+
private inferSourcePath;
|
|
57
|
+
/**
|
|
58
|
+
* Extract errors from test file (basic implementation)
|
|
59
|
+
*/
|
|
60
|
+
private extractErrorsFromTestFile;
|
|
61
|
+
/**
|
|
62
|
+
* Simulate test rerun (for demonstration)
|
|
63
|
+
*/
|
|
64
|
+
private simulateRerun;
|
|
65
|
+
/**
|
|
66
|
+
* Calculate repair priority
|
|
67
|
+
*/
|
|
68
|
+
private calculatePriority;
|
|
69
|
+
/**
|
|
70
|
+
* Emit progress callback
|
|
71
|
+
*/
|
|
72
|
+
private emitProgress;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Test run result
|
|
76
|
+
*/
|
|
77
|
+
export interface TestRunResult {
|
|
78
|
+
passed: number;
|
|
79
|
+
failed: number;
|
|
80
|
+
errors: TestError[];
|
|
81
|
+
duration: number;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Analyze test errors from raw output
|
|
85
|
+
*/
|
|
86
|
+
export declare function analyzeTestErrors(testFile: string, errorOutput: string): Promise<TestError[]>;
|
|
87
|
+
/**
|
|
88
|
+
* Create repair report in JSON format
|
|
89
|
+
*/
|
|
90
|
+
export declare function createRepairJSONReport(report: RepairReport): string;
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Repairer
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the automatic test repair process.
|
|
5
|
+
*/
|
|
6
|
+
import { readFile } from 'fs/promises';
|
|
7
|
+
import { existsSync } from 'fs';
|
|
8
|
+
import { analyzeTestError, isAutoFixable } from './utils/error-analyzer.js';
|
|
9
|
+
import { generateFixSuggestions, getQuickFixes } from './engine/suggestion-engine.js';
|
|
10
|
+
import { applyFixes } from './engine/fixer.js';
|
|
11
|
+
/**
|
|
12
|
+
* Main test repairer orchestrator
|
|
13
|
+
*/
|
|
14
|
+
export class TestRepairer {
|
|
15
|
+
options;
|
|
16
|
+
iteration = 0;
|
|
17
|
+
constructor(options = {}) {
|
|
18
|
+
this.options = {
|
|
19
|
+
maxIterations: options.maxIterations ?? 3,
|
|
20
|
+
backup: options.backup ?? true,
|
|
21
|
+
autoApply: options.autoApply ?? false,
|
|
22
|
+
llmProvider: options.llmProvider ?? 'ollama',
|
|
23
|
+
verbose: options.verbose ?? false,
|
|
24
|
+
...options
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Analyze test failures and generate repair suggestions
|
|
29
|
+
*/
|
|
30
|
+
async analyze(testFile, testResults) {
|
|
31
|
+
this.emitProgress('analyzing', `Analyzing test file: ${testFile}`);
|
|
32
|
+
const errors = testResults?.errors || this.extractErrorsFromTestFile(testFile);
|
|
33
|
+
const suggestions = [];
|
|
34
|
+
for (const error of errors) {
|
|
35
|
+
// Read test code
|
|
36
|
+
const testCode = await this.readTestCode(error.testFile);
|
|
37
|
+
let sourceCode;
|
|
38
|
+
// Try to find and read source file
|
|
39
|
+
const sourcePath = this.inferSourcePath(error.testFile);
|
|
40
|
+
if (sourcePath && existsSync(sourcePath)) {
|
|
41
|
+
sourceCode = await readFile(sourcePath, 'utf-8');
|
|
42
|
+
}
|
|
43
|
+
// Generate suggestions
|
|
44
|
+
const errorSuggestions = await generateFixSuggestions([error], testCode, sourceCode, {
|
|
45
|
+
llmProvider: this.options.llmProvider,
|
|
46
|
+
maxSuggestions: 3,
|
|
47
|
+
includeCode: true
|
|
48
|
+
});
|
|
49
|
+
suggestions.push(...errorSuggestions);
|
|
50
|
+
}
|
|
51
|
+
const autoFixable = errors.filter(isAutoFixable).length;
|
|
52
|
+
const estimatedTime = suggestions.length * 5; // 5 seconds per suggestion
|
|
53
|
+
const priority = this.calculatePriority(errors, autoFixable);
|
|
54
|
+
return {
|
|
55
|
+
errors,
|
|
56
|
+
autoFixable,
|
|
57
|
+
suggestions: suggestions.slice(0, 10),
|
|
58
|
+
estimatedTime,
|
|
59
|
+
priority
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Repair tests automatically
|
|
64
|
+
*/
|
|
65
|
+
async repair(testFile, testResults) {
|
|
66
|
+
const startTime = Date.now();
|
|
67
|
+
// Analyze failures
|
|
68
|
+
const analysis = await this.analyze(testFile, testResults);
|
|
69
|
+
this.emitProgress('suggesting', `Generated ${analysis.suggestions.length} fix suggestions`);
|
|
70
|
+
if (analysis.suggestions.length === 0) {
|
|
71
|
+
return {
|
|
72
|
+
analysis,
|
|
73
|
+
result: {
|
|
74
|
+
originalErrors: analysis.errors,
|
|
75
|
+
remainingErrors: analysis.errors,
|
|
76
|
+
fixes: [],
|
|
77
|
+
success: false,
|
|
78
|
+
iterations: 0
|
|
79
|
+
},
|
|
80
|
+
duration: Date.now() - startTime,
|
|
81
|
+
success: false
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
let currentErrors = [...analysis.errors];
|
|
85
|
+
const appliedFixes = [];
|
|
86
|
+
// Iterative repair cycle
|
|
87
|
+
for (this.iteration = 0; this.iteration < this.options.maxIterations; this.iteration++) {
|
|
88
|
+
this.emitProgress('applying', `Iteration ${this.iteration + 1}/${this.options.maxIterations}`);
|
|
89
|
+
// Apply fixes
|
|
90
|
+
if (this.options.autoApply) {
|
|
91
|
+
const fixResult = await applyFixes(analysis.suggestions, {
|
|
92
|
+
backup: this.options.backup,
|
|
93
|
+
dryRun: false
|
|
94
|
+
});
|
|
95
|
+
appliedFixes.push(...analysis.suggestions.slice(0, fixResult.applied));
|
|
96
|
+
this.emitProgress('applying', `Applied ${fixResult.applied} fixes`);
|
|
97
|
+
// Re-run tests (simulated here - in real scenario would run actual tests)
|
|
98
|
+
const newErrors = this.simulateRerun(testFile);
|
|
99
|
+
if (newErrors.length === 0) {
|
|
100
|
+
this.emitProgress('complete', 'All tests passing!');
|
|
101
|
+
return {
|
|
102
|
+
analysis,
|
|
103
|
+
result: {
|
|
104
|
+
originalErrors: currentErrors,
|
|
105
|
+
remainingErrors: [],
|
|
106
|
+
fixes: appliedFixes,
|
|
107
|
+
success: true,
|
|
108
|
+
iterations: this.iteration + 1
|
|
109
|
+
},
|
|
110
|
+
duration: Date.now() - startTime,
|
|
111
|
+
success: true
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
currentErrors = newErrors;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
// Dry run - just report what would be done
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const success = currentErrors.length < analysis.errors.length;
|
|
122
|
+
return {
|
|
123
|
+
analysis,
|
|
124
|
+
result: {
|
|
125
|
+
originalErrors: analysis.errors,
|
|
126
|
+
remainingErrors: currentErrors,
|
|
127
|
+
fixes: appliedFixes,
|
|
128
|
+
success,
|
|
129
|
+
iterations: this.iteration
|
|
130
|
+
},
|
|
131
|
+
duration: Date.now() - startTime,
|
|
132
|
+
success
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Get quick fixes without LLM
|
|
137
|
+
*/
|
|
138
|
+
async getQuickFixes(testFile, error) {
|
|
139
|
+
return getQuickFixes(error);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Generate repair report
|
|
143
|
+
*/
|
|
144
|
+
generateReport(report) {
|
|
145
|
+
const lines = [];
|
|
146
|
+
lines.push('=== Test Repair Report ===');
|
|
147
|
+
lines.push(`Duration: ${report.duration}ms`);
|
|
148
|
+
lines.push(`Iterations: ${report.result.iterations}`);
|
|
149
|
+
lines.push(`Original Errors: ${report.result.originalErrors.length}`);
|
|
150
|
+
lines.push(`Remaining Errors: ${report.result.remainingErrors.length}`);
|
|
151
|
+
lines.push(`Fixes Applied: ${report.result.fixes.length}`);
|
|
152
|
+
lines.push(`Success: ${report.success ? 'Yes' : 'No'}`);
|
|
153
|
+
lines.push('');
|
|
154
|
+
if (report.analysis.suggestions.length > 0) {
|
|
155
|
+
lines.push('Suggestions:');
|
|
156
|
+
for (const suggestion of report.analysis.suggestions) {
|
|
157
|
+
lines.push(` - ${suggestion.description}`);
|
|
158
|
+
lines.push(` Type: ${suggestion.type}`);
|
|
159
|
+
lines.push(` Confidence: ${(suggestion.confidence * 100).toFixed(0)}%`);
|
|
160
|
+
lines.push(` Effort: ${suggestion.estimatedEffort}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return lines.join('\n');
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Read test code from file
|
|
167
|
+
*/
|
|
168
|
+
async readTestCode(filePath) {
|
|
169
|
+
try {
|
|
170
|
+
return await readFile(filePath, 'utf-8');
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return '';
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Infer source file path from test file path
|
|
178
|
+
*/
|
|
179
|
+
inferSourcePath(testPath) {
|
|
180
|
+
// Common patterns: src.test.ts -> src.ts, __tests__/foo.test.ts -> foo.ts
|
|
181
|
+
const replacements = [
|
|
182
|
+
{ from: '.test.ts', to: '.ts' },
|
|
183
|
+
{ from: '.spec.ts', to: '.ts' },
|
|
184
|
+
{ from: '.test.js', to: '.js' },
|
|
185
|
+
{ from: '.spec.js', to: '.js' },
|
|
186
|
+
{ from: '__tests__/', to: '../src/' },
|
|
187
|
+
{ from: '/test/', to: '/src/' }
|
|
188
|
+
];
|
|
189
|
+
for (const { from, to } of replacements) {
|
|
190
|
+
if (testPath.includes(from)) {
|
|
191
|
+
return testPath.replace(from, to);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Extract errors from test file (basic implementation)
|
|
198
|
+
*/
|
|
199
|
+
extractErrorsFromTestFile(testFile) {
|
|
200
|
+
// In real implementation, would parse test output
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Simulate test rerun (for demonstration)
|
|
205
|
+
*/
|
|
206
|
+
simulateRerun(testFile) {
|
|
207
|
+
// In real implementation, would actually run tests
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Calculate repair priority
|
|
212
|
+
*/
|
|
213
|
+
calculatePriority(errors, autoFixable) {
|
|
214
|
+
const fixableRatio = errors.length > 0 ? autoFixable / errors.length : 0;
|
|
215
|
+
if (fixableRatio >= 0.7 && errors.length > 0) {
|
|
216
|
+
return 'high';
|
|
217
|
+
}
|
|
218
|
+
else if (fixableRatio >= 0.4) {
|
|
219
|
+
return 'medium';
|
|
220
|
+
}
|
|
221
|
+
return 'low';
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Emit progress callback
|
|
225
|
+
*/
|
|
226
|
+
emitProgress(stage, message) {
|
|
227
|
+
this.options.onProgress?.({
|
|
228
|
+
stage,
|
|
229
|
+
message,
|
|
230
|
+
iteration: this.iteration,
|
|
231
|
+
errorsRemaining: 0
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Analyze test errors from raw output
|
|
237
|
+
*/
|
|
238
|
+
export async function analyzeTestErrors(testFile, errorOutput) {
|
|
239
|
+
const errors = [];
|
|
240
|
+
// Parse error output (basic implementation)
|
|
241
|
+
const errorBlocks = errorOutput.split(/\n\s*●/g).filter(Boolean);
|
|
242
|
+
for (const block of errorBlocks) {
|
|
243
|
+
const lines = block.split('\n');
|
|
244
|
+
const firstLine = lines[0] || '';
|
|
245
|
+
// Extract test name
|
|
246
|
+
const testNameMatch = firstLine.match(/^(.+?)\s*$/);
|
|
247
|
+
const testName = testNameMatch?.[1] || 'unknown';
|
|
248
|
+
// Extract error message
|
|
249
|
+
const errorMessage = lines.slice(1).join('\n').trim();
|
|
250
|
+
if (errorMessage) {
|
|
251
|
+
errors.push(analyzeTestError(testFile, testName, errorMessage));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return errors;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Create repair report in JSON format
|
|
258
|
+
*/
|
|
259
|
+
export function createRepairJSONReport(report) {
|
|
260
|
+
return JSON.stringify({
|
|
261
|
+
duration: report.duration,
|
|
262
|
+
success: report.success,
|
|
263
|
+
result: {
|
|
264
|
+
originalErrors: report.result.originalErrors.length,
|
|
265
|
+
remainingErrors: report.result.remainingErrors.length,
|
|
266
|
+
fixesApplied: report.result.fixes.length,
|
|
267
|
+
iterations: report.result.iterations,
|
|
268
|
+
success: report.result.success
|
|
269
|
+
},
|
|
270
|
+
analysis: {
|
|
271
|
+
totalErrors: report.analysis.errors.length,
|
|
272
|
+
autoFixable: report.analysis.autoFixable,
|
|
273
|
+
suggestions: report.analysis.suggestions.length,
|
|
274
|
+
priority: report.analysis.priority,
|
|
275
|
+
estimatedTime: report.analysis.estimatedTime
|
|
276
|
+
},
|
|
277
|
+
suggestions: report.analysis.suggestions.map(s => ({
|
|
278
|
+
type: s.type,
|
|
279
|
+
description: s.description,
|
|
280
|
+
confidence: s.confidence,
|
|
281
|
+
estimatedEffort: s.estimatedEffort
|
|
282
|
+
}))
|
|
283
|
+
}, null, 2);
|
|
284
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-Repair Module - Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Automatic test repair using AI.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Test error information
|
|
8
|
+
*/
|
|
9
|
+
export interface TestError {
|
|
10
|
+
testFile: string;
|
|
11
|
+
testName: string;
|
|
12
|
+
line: number;
|
|
13
|
+
type: ErrorType;
|
|
14
|
+
message: string;
|
|
15
|
+
stack?: string;
|
|
16
|
+
context?: string;
|
|
17
|
+
code?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Error types
|
|
21
|
+
*/
|
|
22
|
+
export type ErrorType = 'assertion_error' | 'timeout_error' | 'reference_error' | 'type_error' | 'import_error' | 'async_error' | 'mock_error' | 'unknown';
|
|
23
|
+
/**
|
|
24
|
+
* Fix suggestion
|
|
25
|
+
*/
|
|
26
|
+
export interface FixSuggestion {
|
|
27
|
+
type: FixType;
|
|
28
|
+
description: string;
|
|
29
|
+
code: string;
|
|
30
|
+
filePath: string;
|
|
31
|
+
line: number;
|
|
32
|
+
confidence: number;
|
|
33
|
+
reason: string;
|
|
34
|
+
estimatedEffort: 'low' | 'medium' | 'high';
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Fix types
|
|
38
|
+
*/
|
|
39
|
+
export type FixType = 'add_mock' | 'add_assertion' | 'fix_import' | 'fix_async' | 'fix_timeout' | 'fix_typo' | 'update_expectation' | 'add_test' | 'refactor' | 'add_dependency' | 'manual_review';
|
|
40
|
+
/**
|
|
41
|
+
* Repair analysis result
|
|
42
|
+
*/
|
|
43
|
+
export interface RepairAnalysis {
|
|
44
|
+
errors: TestError[];
|
|
45
|
+
autoFixable: number;
|
|
46
|
+
suggestions: FixSuggestion[];
|
|
47
|
+
estimatedTime: number;
|
|
48
|
+
priority: 'high' | 'medium' | 'low';
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Fix application result
|
|
52
|
+
*/
|
|
53
|
+
export interface FixResult {
|
|
54
|
+
success: boolean;
|
|
55
|
+
suggestions: FixSuggestion[];
|
|
56
|
+
applied: number;
|
|
57
|
+
failed: number;
|
|
58
|
+
testsPassed: number;
|
|
59
|
+
testsFailed: number;
|
|
60
|
+
changes: FileChange[];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* File change
|
|
64
|
+
*/
|
|
65
|
+
export interface FileChange {
|
|
66
|
+
path: string;
|
|
67
|
+
original: string;
|
|
68
|
+
modified: string;
|
|
69
|
+
backup: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Repair options
|
|
73
|
+
*/
|
|
74
|
+
export interface RepairOptions {
|
|
75
|
+
testPattern?: string;
|
|
76
|
+
maxIterations?: number;
|
|
77
|
+
autoApply?: boolean;
|
|
78
|
+
backup?: boolean;
|
|
79
|
+
llmProvider?: 'ollama' | 'openai' | 'anthropic' | 'mock';
|
|
80
|
+
verbose?: boolean;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Test repair result
|
|
84
|
+
*/
|
|
85
|
+
export interface TestRepairResult {
|
|
86
|
+
originalErrors: TestError[];
|
|
87
|
+
remainingErrors: TestError[];
|
|
88
|
+
fixes: FixSuggestion[];
|
|
89
|
+
success: boolean;
|
|
90
|
+
iterations: number;
|
|
91
|
+
}
|