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,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Analyzes test errors to extract patterns and classify problems.
|
|
5
|
+
*/
|
|
6
|
+
import type { TestError, ErrorType } from '../types.js';
|
|
7
|
+
export interface ParsedError {
|
|
8
|
+
type: ErrorType;
|
|
9
|
+
message: string;
|
|
10
|
+
details: Record<string, unknown>;
|
|
11
|
+
fixable: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Analyze a test error message and classify it
|
|
15
|
+
*/
|
|
16
|
+
export declare function analyzeTestError(testFile: string, testName: string, errorMessage: string, stack?: string): TestError;
|
|
17
|
+
/**
|
|
18
|
+
* Classify multiple test errors
|
|
19
|
+
*/
|
|
20
|
+
export declare function classifyErrors(errors: string[]): Map<ErrorType, number>;
|
|
21
|
+
/**
|
|
22
|
+
* Check if error is auto-fixable
|
|
23
|
+
*/
|
|
24
|
+
export declare function isAutoFixable(error: TestError): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Get fix suggestions for an error type
|
|
27
|
+
*/
|
|
28
|
+
export declare function getCommonFixes(error: TestError): string[];
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Analyzes test errors to extract patterns and classify problems.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Analyze a test error message and classify it
|
|
8
|
+
*/
|
|
9
|
+
export function analyzeTestError(testFile, testName, errorMessage, stack) {
|
|
10
|
+
const parsed = parseErrorMessage(errorMessage, stack);
|
|
11
|
+
return {
|
|
12
|
+
testFile,
|
|
13
|
+
testName,
|
|
14
|
+
line: extractErrorLine(stack),
|
|
15
|
+
type: parsed.type,
|
|
16
|
+
message: parsed.message,
|
|
17
|
+
stack,
|
|
18
|
+
context: extractContext(parsed),
|
|
19
|
+
code: extractErrorCode(parsed)
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Parse error message to extract type and details
|
|
24
|
+
*/
|
|
25
|
+
function parseErrorMessage(message, stack) {
|
|
26
|
+
const lower = message.toLowerCase();
|
|
27
|
+
// AssertionError
|
|
28
|
+
if (lower.includes('expected') || lower.includes('assertion')) {
|
|
29
|
+
return {
|
|
30
|
+
type: 'assertion_error',
|
|
31
|
+
message,
|
|
32
|
+
details: {
|
|
33
|
+
expected: extractExpected(message),
|
|
34
|
+
received: extractReceived(message),
|
|
35
|
+
},
|
|
36
|
+
fixable: true
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// TimeoutError
|
|
40
|
+
if (lower.includes('timeout') || lower.includes('timed out')) {
|
|
41
|
+
return {
|
|
42
|
+
type: 'timeout_error',
|
|
43
|
+
message,
|
|
44
|
+
details: {},
|
|
45
|
+
fixable: true
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// ReferenceError
|
|
49
|
+
if (lower.includes('is not defined') || lower.includes('reference error')) {
|
|
50
|
+
return {
|
|
51
|
+
type: 'reference_error',
|
|
52
|
+
message,
|
|
53
|
+
details: {
|
|
54
|
+
missingVar: extractMissingVariable(message)
|
|
55
|
+
},
|
|
56
|
+
fixable: true
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// TypeError
|
|
60
|
+
if (lower.includes('is not a') || lower.includes('is not function') || lower.includes('type error')) {
|
|
61
|
+
return {
|
|
62
|
+
type: 'type_error',
|
|
63
|
+
message,
|
|
64
|
+
details: {},
|
|
65
|
+
fixable: true
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
// Import error
|
|
69
|
+
if (lower.includes("cannot find module") || lower.includes('unexpected token')) {
|
|
70
|
+
return {
|
|
71
|
+
type: 'import_error',
|
|
72
|
+
message,
|
|
73
|
+
details: {
|
|
74
|
+
missingModule: extractMissingModule(message)
|
|
75
|
+
},
|
|
76
|
+
fixable: true
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
// Async error
|
|
80
|
+
if (lower.includes('async') || lower.includes('promise')) {
|
|
81
|
+
return {
|
|
82
|
+
type: 'async_error',
|
|
83
|
+
message,
|
|
84
|
+
details: {},
|
|
85
|
+
fixable: true
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
type: 'unknown',
|
|
90
|
+
message,
|
|
91
|
+
details: {},
|
|
92
|
+
fixable: false
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Extract line number from stack trace
|
|
97
|
+
*/
|
|
98
|
+
function extractErrorLine(stack) {
|
|
99
|
+
if (!stack)
|
|
100
|
+
return 0;
|
|
101
|
+
const lines = stack.split('\n');
|
|
102
|
+
for (const line of lines) {
|
|
103
|
+
const match = line.match(/:(\d+):(\d+)/);
|
|
104
|
+
if (match) {
|
|
105
|
+
return parseInt(match[1], 10);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Extract expected value from error message
|
|
112
|
+
*/
|
|
113
|
+
function extractExpected(message) {
|
|
114
|
+
const patterns = [
|
|
115
|
+
/expected\s+["']([^"']+)["']/i,
|
|
116
|
+
/to be\s+["']?([^"')\s]+)["']?/i,
|
|
117
|
+
/expected:\s*(.+?)(?:,|but)/i
|
|
118
|
+
];
|
|
119
|
+
for (const pattern of patterns) {
|
|
120
|
+
const match = message.match(pattern);
|
|
121
|
+
if (match)
|
|
122
|
+
return match[1].trim();
|
|
123
|
+
}
|
|
124
|
+
return '';
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Extract received value from error message
|
|
128
|
+
*/
|
|
129
|
+
function extractReceived(message) {
|
|
130
|
+
const patterns = [
|
|
131
|
+
/received\s+["']([^"']+)["']/i,
|
|
132
|
+
/but got\s+["']?([^"')\s]+)["']?/i,
|
|
133
|
+
/received:\s*(.+?)(?:,|\)|$)/i
|
|
134
|
+
];
|
|
135
|
+
for (const pattern of patterns) {
|
|
136
|
+
const match = message.match(pattern);
|
|
137
|
+
if (match)
|
|
138
|
+
return match[1].trim();
|
|
139
|
+
}
|
|
140
|
+
return '';
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Extract missing variable from reference error
|
|
144
|
+
*/
|
|
145
|
+
function extractMissingVariable(message) {
|
|
146
|
+
const match = message.match(/(\w+)\s+is not defined/i);
|
|
147
|
+
if (match)
|
|
148
|
+
return match[1];
|
|
149
|
+
const refMatch = message.match(/ReferenceError:\s*(\w+)/i);
|
|
150
|
+
if (refMatch)
|
|
151
|
+
return refMatch[1];
|
|
152
|
+
return '';
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Extract missing module from import error
|
|
156
|
+
*/
|
|
157
|
+
function extractMissingModule(message) {
|
|
158
|
+
const match = message.match(/Cannot find module ['"]([^'"]+)['"]/i);
|
|
159
|
+
if (match)
|
|
160
|
+
return match[1];
|
|
161
|
+
const modMatch = message.match(/Cannot find module ['"](.+)['"]/);
|
|
162
|
+
if (modMatch)
|
|
163
|
+
return modMatch[1];
|
|
164
|
+
return '';
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Extract context from parsed error
|
|
168
|
+
*/
|
|
169
|
+
function extractContext(parsed) {
|
|
170
|
+
const contexts = {
|
|
171
|
+
assertion_error: 'Assertion failed - check expected vs actual values',
|
|
172
|
+
timeout_error: 'Test took too long - increase timeout or fix performance',
|
|
173
|
+
reference_error: 'Undefined variable - check imports and spelling',
|
|
174
|
+
type_error: 'Type mismatch - check data types and conversions',
|
|
175
|
+
import_error: 'Module not found - check import path and dependencies',
|
|
176
|
+
async_error: 'Async issue - check promises and async/await usage',
|
|
177
|
+
mock_error: 'Mock not configured - check test setup and mocks',
|
|
178
|
+
unknown: 'Unknown error type'
|
|
179
|
+
};
|
|
180
|
+
return contexts[parsed.type] || contexts.unknown;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Extract error code if present
|
|
184
|
+
*/
|
|
185
|
+
function extractErrorCode(parsed) {
|
|
186
|
+
return parsed.type.toUpperCase().replace(/_/g, '-');
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Classify multiple test errors
|
|
190
|
+
*/
|
|
191
|
+
export function classifyErrors(errors) {
|
|
192
|
+
const counts = new Map();
|
|
193
|
+
for (const error of errors) {
|
|
194
|
+
const parsed = parseErrorMessage(error);
|
|
195
|
+
counts.set(parsed.type, (counts.get(parsed.type) || 0) + 1);
|
|
196
|
+
}
|
|
197
|
+
return counts;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Check if error is auto-fixable
|
|
201
|
+
*/
|
|
202
|
+
export function isAutoFixable(error) {
|
|
203
|
+
// Most assertion and reference errors are auto-fixable
|
|
204
|
+
if (error.type === 'assertion_error' || error.type === 'reference_error') {
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
// Import errors and type errors are often fixable
|
|
208
|
+
if (error.type === 'import_error' || error.type === 'type_error') {
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
// Timeouts can sometimes be fixed by increasing timeout
|
|
212
|
+
if (error.type === 'timeout_error') {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Get fix suggestions for an error type
|
|
219
|
+
*/
|
|
220
|
+
export function getCommonFixes(error) {
|
|
221
|
+
const fixes = {
|
|
222
|
+
assertion_error: [
|
|
223
|
+
'Update expected value to match actual',
|
|
224
|
+
'Add/modify mock to return expected value',
|
|
225
|
+
'Fix test data setup'
|
|
226
|
+
],
|
|
227
|
+
timeout_error: [
|
|
228
|
+
'Increase test timeout',
|
|
229
|
+
'Optimize test execution',
|
|
230
|
+
'Remove unnecessary waits'
|
|
231
|
+
],
|
|
232
|
+
reference_error: [
|
|
233
|
+
'Import missing module/variable',
|
|
234
|
+
'Fix typo in variable name',
|
|
235
|
+
'Define missing variable'
|
|
236
|
+
],
|
|
237
|
+
type_error: [
|
|
238
|
+
'Add type conversion',
|
|
239
|
+
'Fix data type mismatch',
|
|
240
|
+
'Check API response structure'
|
|
241
|
+
],
|
|
242
|
+
import_error: [
|
|
243
|
+
'Install missing dependency',
|
|
244
|
+
'Fix import path',
|
|
245
|
+
'Add module to dependencies'
|
|
246
|
+
],
|
|
247
|
+
async_error: [
|
|
248
|
+
'Add await to async calls',
|
|
249
|
+
'Return promise from async function',
|
|
250
|
+
'Fix promise chain'
|
|
251
|
+
],
|
|
252
|
+
mock_error: [
|
|
253
|
+
'Configure mock properly',
|
|
254
|
+
'Add mock setup in test',
|
|
255
|
+
'Reset mocks between tests'
|
|
256
|
+
],
|
|
257
|
+
unknown: [
|
|
258
|
+
'Review test code',
|
|
259
|
+
'Check test setup',
|
|
260
|
+
'Verify test environment'
|
|
261
|
+
]
|
|
262
|
+
};
|
|
263
|
+
return fixes[error.type] || fixes.unknown;
|
|
264
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flakiness Integration for Smart Retry
|
|
3
|
+
*
|
|
4
|
+
* Integrates Smart Retry with the Flakiness Detection Engine
|
|
5
|
+
* to make intelligent retry decisions based on historical data.
|
|
6
|
+
*/
|
|
7
|
+
import type { FlakinessResult, TestResult as FlakinessTestResult } from '../flakiness/index.js';
|
|
8
|
+
import type { RetryConfig, RetryRecommendation } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Enhanced retry config with flakiness awareness
|
|
11
|
+
*/
|
|
12
|
+
export interface FlakinessAwareRetryConfig extends RetryConfig {
|
|
13
|
+
/** Use flakiness score to determine retry strategy */
|
|
14
|
+
useFlakinessScore?: boolean;
|
|
15
|
+
/** Minimum historical runs before using flakiness data */
|
|
16
|
+
minHistoricalRuns?: number;
|
|
17
|
+
/** Boost retry attempts for known flaky tests */
|
|
18
|
+
boostRetriesForFlaky?: boolean;
|
|
19
|
+
/** Reduce retries for known stable tests */
|
|
20
|
+
reduceRetriesForStable?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Flakiness-aware retry engine
|
|
24
|
+
*/
|
|
25
|
+
export declare class FlakinessAwareRetryEngine {
|
|
26
|
+
private flakinessHistory;
|
|
27
|
+
private testHistory;
|
|
28
|
+
/**
|
|
29
|
+
* Load flakiness data
|
|
30
|
+
*/
|
|
31
|
+
loadFlakinessData(results: FlakinessResult[]): void;
|
|
32
|
+
/**
|
|
33
|
+
* Add test result to history
|
|
34
|
+
*/
|
|
35
|
+
addTestResult(testId: string, result: FlakinessTestResult): void;
|
|
36
|
+
/**
|
|
37
|
+
* Get retry config for a specific test based on flakiness
|
|
38
|
+
*/
|
|
39
|
+
getRetryConfigForTest(testId: string, baseConfig: FlakinessAwareRetryConfig): RetryConfig;
|
|
40
|
+
/**
|
|
41
|
+
* Get comprehensive retry recommendation
|
|
42
|
+
*/
|
|
43
|
+
getRetryRecommendation(testId: string): RetryRecommendation;
|
|
44
|
+
/**
|
|
45
|
+
* Check if test should be skipped (too flaky)
|
|
46
|
+
*/
|
|
47
|
+
shouldSkipTest(testId: string): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Get flakiness history
|
|
50
|
+
*/
|
|
51
|
+
getFlakinessHistory(): Map<string, FlakinessResult>;
|
|
52
|
+
/**
|
|
53
|
+
* Clear all history
|
|
54
|
+
*/
|
|
55
|
+
clearHistory(): void;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Create a flakiness-aware retry engine
|
|
59
|
+
*/
|
|
60
|
+
export declare function createFlakinessAwareRetryEngine(): FlakinessAwareRetryEngine;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flakiness Integration for Smart Retry
|
|
3
|
+
*
|
|
4
|
+
* Integrates Smart Retry with the Flakiness Detection Engine
|
|
5
|
+
* to make intelligent retry decisions based on historical data.
|
|
6
|
+
*/
|
|
7
|
+
import { RetryStrategy } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Flakiness-aware retry engine
|
|
10
|
+
*/
|
|
11
|
+
export class FlakinessAwareRetryEngine {
|
|
12
|
+
flakinessHistory = new Map();
|
|
13
|
+
testHistory = new Map();
|
|
14
|
+
/**
|
|
15
|
+
* Load flakiness data
|
|
16
|
+
*/
|
|
17
|
+
loadFlakinessData(results) {
|
|
18
|
+
for (const result of results) {
|
|
19
|
+
this.flakinessHistory.set(result.testId, result);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Add test result to history
|
|
24
|
+
*/
|
|
25
|
+
addTestResult(testId, result) {
|
|
26
|
+
if (!this.testHistory.has(testId)) {
|
|
27
|
+
this.testHistory.set(testId, []);
|
|
28
|
+
}
|
|
29
|
+
this.testHistory.get(testId).push(result);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get retry config for a specific test based on flakiness
|
|
33
|
+
*/
|
|
34
|
+
getRetryConfigForTest(testId, baseConfig) {
|
|
35
|
+
const flakiness = this.flakinessHistory.get(testId);
|
|
36
|
+
const recentResults = this.testHistory.get(testId);
|
|
37
|
+
// If no flakiness data, return base config
|
|
38
|
+
const minRuns = baseConfig.minHistoricalRuns ?? 0;
|
|
39
|
+
if (!flakiness || !recentResults || recentResults.length < minRuns) {
|
|
40
|
+
return baseConfig;
|
|
41
|
+
}
|
|
42
|
+
const config = { ...baseConfig };
|
|
43
|
+
// Adjust based on flakiness score
|
|
44
|
+
if (baseConfig.useFlakinessScore) {
|
|
45
|
+
const score = flakiness.score;
|
|
46
|
+
if (score >= 90) {
|
|
47
|
+
// Legendary/Solid - reduce retries
|
|
48
|
+
if (baseConfig.reduceRetriesForStable) {
|
|
49
|
+
config.maxRetries = Math.max(0, baseConfig.maxRetries - 1);
|
|
50
|
+
config.strategy = RetryStrategy.FIXED;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else if (score >= 75) {
|
|
54
|
+
// Good - keep base config
|
|
55
|
+
config.strategy = RetryStrategy.FIXED;
|
|
56
|
+
}
|
|
57
|
+
else if (score >= 50) {
|
|
58
|
+
// Shaky - increase retries slightly
|
|
59
|
+
if (baseConfig.boostRetriesForFlaky) {
|
|
60
|
+
config.maxRetries = baseConfig.maxRetries + 1;
|
|
61
|
+
config.strategy = RetryStrategy.ADAPTIVE;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
// Unstable - aggressive retry
|
|
66
|
+
if (baseConfig.boostRetriesForFlaky) {
|
|
67
|
+
config.maxRetries = baseConfig.maxRetries + 2;
|
|
68
|
+
config.strategy = RetryStrategy.INTELLIGENT;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Adjust based on pattern type
|
|
73
|
+
if (flakiness.patternType) {
|
|
74
|
+
switch (flakiness.patternType) {
|
|
75
|
+
case 'timing':
|
|
76
|
+
config.initialDelayMs = Math.max(config.initialDelayMs, 2000);
|
|
77
|
+
config.retryOnTimeout = true;
|
|
78
|
+
break;
|
|
79
|
+
case 'external_dependency':
|
|
80
|
+
config.initialDelayMs = Math.max(config.initialDelayMs, 3000);
|
|
81
|
+
config.strategy = RetryStrategy.EXPONENTIAL;
|
|
82
|
+
break;
|
|
83
|
+
case 'race_condition':
|
|
84
|
+
config.strategy = RetryStrategy.EXPONENTIAL;
|
|
85
|
+
config.jitterFactor = Math.max(config.jitterFactor, 0.2);
|
|
86
|
+
break;
|
|
87
|
+
case 'selector_issue':
|
|
88
|
+
config.strategy = RetryStrategy.FIXED;
|
|
89
|
+
config.maxRetries = Math.min(config.maxRetries, 3);
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Adjust based on recent streak
|
|
94
|
+
if (flakiness.currentStreak && flakiness.currentStreak > 3) {
|
|
95
|
+
if (flakiness.streakType === 'fail') {
|
|
96
|
+
// Long fail streak - boost retries
|
|
97
|
+
config.maxRetries = config.maxRetries + 1;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// Long pass streak - reduce retries
|
|
101
|
+
config.maxRetries = Math.max(1, config.maxRetries - 1);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return config;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get comprehensive retry recommendation
|
|
108
|
+
*/
|
|
109
|
+
getRetryRecommendation(testId) {
|
|
110
|
+
const flakiness = this.flakinessHistory.get(testId);
|
|
111
|
+
const recentResults = this.testHistory.get(testId);
|
|
112
|
+
if (!flakiness || !recentResults || recentResults.length < 3) {
|
|
113
|
+
return {
|
|
114
|
+
shouldRetry: true,
|
|
115
|
+
strategy: RetryStrategy.ADAPTIVE,
|
|
116
|
+
maxRetries: 3,
|
|
117
|
+
initialDelayMs: 1000,
|
|
118
|
+
confidence: 0.5,
|
|
119
|
+
reason: 'Insufficient historical data, using default adaptive retry'
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// Analyze recent failures
|
|
123
|
+
const recentFailures = recentResults.slice(-10).filter(r => !r.success);
|
|
124
|
+
const failureRate = recentFailures.length / Math.min(recentResults.length, 10);
|
|
125
|
+
// Build recommendation
|
|
126
|
+
let strategy;
|
|
127
|
+
let maxRetries;
|
|
128
|
+
let initialDelayMs;
|
|
129
|
+
let confidence;
|
|
130
|
+
const reasons = [];
|
|
131
|
+
// Base recommendation on flakiness score
|
|
132
|
+
if (flakiness.score >= 90) {
|
|
133
|
+
strategy = RetryStrategy.NONE;
|
|
134
|
+
maxRetries = 0;
|
|
135
|
+
initialDelayMs = 0;
|
|
136
|
+
confidence = 0.9;
|
|
137
|
+
reasons.push('Test is highly reliable');
|
|
138
|
+
}
|
|
139
|
+
else if (flakiness.score >= 75) {
|
|
140
|
+
strategy = RetryStrategy.FIXED;
|
|
141
|
+
maxRetries = 2;
|
|
142
|
+
initialDelayMs = 1000;
|
|
143
|
+
confidence = 0.75;
|
|
144
|
+
reasons.push('Test is occasionally flaky');
|
|
145
|
+
}
|
|
146
|
+
else if (flakiness.score >= 50) {
|
|
147
|
+
strategy = RetryStrategy.ADAPTIVE;
|
|
148
|
+
maxRetries = 3;
|
|
149
|
+
initialDelayMs = 1000;
|
|
150
|
+
confidence = 0.85;
|
|
151
|
+
reasons.push('Test is often flaky');
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
strategy = RetryStrategy.INTELLIGENT;
|
|
155
|
+
maxRetries = 5;
|
|
156
|
+
initialDelayMs = 2000;
|
|
157
|
+
confidence = 0.9;
|
|
158
|
+
reasons.push('Test is very flaky, aggressive retry recommended');
|
|
159
|
+
}
|
|
160
|
+
// Adjust based on pattern
|
|
161
|
+
if (flakiness.patternType) {
|
|
162
|
+
reasons.push(`Pattern: ${flakiness.patternType}`);
|
|
163
|
+
switch (flakiness.patternType) {
|
|
164
|
+
case 'timing':
|
|
165
|
+
initialDelayMs = Math.max(initialDelayMs, 2000);
|
|
166
|
+
strategy = flakiness.score < 75 ? RetryStrategy.EXPONENTIAL : strategy;
|
|
167
|
+
break;
|
|
168
|
+
case 'external_dependency':
|
|
169
|
+
initialDelayMs = Math.max(initialDelayMs, 3000);
|
|
170
|
+
break;
|
|
171
|
+
case 'selector_issue':
|
|
172
|
+
strategy = RetryStrategy.FIXED;
|
|
173
|
+
maxRetries = Math.min(maxRetries, 3);
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Adjust based on recent failure rate
|
|
178
|
+
if (failureRate > 0.5) {
|
|
179
|
+
maxRetries = Math.min(maxRetries + 1, 5);
|
|
180
|
+
reasons.push(`High recent failure rate (${Math.round(failureRate * 100)}%)`);
|
|
181
|
+
}
|
|
182
|
+
else if (failureRate < 0.1 && flakiness.score >= 75) {
|
|
183
|
+
// For highly reliable tests (score >= 90), keep maxRetries at 0
|
|
184
|
+
if (flakiness.score < 90) {
|
|
185
|
+
maxRetries = Math.max(maxRetries - 1, 1);
|
|
186
|
+
}
|
|
187
|
+
reasons.push('Low recent failure rate');
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
shouldRetry: maxRetries > 0,
|
|
191
|
+
strategy,
|
|
192
|
+
maxRetries,
|
|
193
|
+
initialDelayMs,
|
|
194
|
+
confidence,
|
|
195
|
+
reason: reasons.join('; '),
|
|
196
|
+
pattern: flakiness.patternType
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Check if test should be skipped (too flaky)
|
|
201
|
+
*/
|
|
202
|
+
shouldSkipTest(testId) {
|
|
203
|
+
const flakiness = this.flakinessHistory.get(testId);
|
|
204
|
+
if (!flakiness)
|
|
205
|
+
return false;
|
|
206
|
+
// Skip if score is very low and has many runs
|
|
207
|
+
return flakiness.score < 30 && flakiness.totalRuns >= 10;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Get flakiness history
|
|
211
|
+
*/
|
|
212
|
+
getFlakinessHistory() {
|
|
213
|
+
return new Map(this.flakinessHistory);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Clear all history
|
|
217
|
+
*/
|
|
218
|
+
clearHistory() {
|
|
219
|
+
this.flakinessHistory.clear();
|
|
220
|
+
this.testHistory.clear();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Create a flakiness-aware retry engine
|
|
225
|
+
*/
|
|
226
|
+
export function createFlakinessAwareRetryEngine() {
|
|
227
|
+
return new FlakinessAwareRetryEngine();
|
|
228
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Smart Retry Module (F8)
|
|
3
|
+
*
|
|
4
|
+
* Intelligent retry strategies based on flakiness scoring and pattern detection.
|
|
5
|
+
* Completes the flakiness system: Detection → Scoring → Quarantine → Smart Retry
|
|
6
|
+
*
|
|
7
|
+
* @module retry
|
|
8
|
+
*/
|
|
9
|
+
export { RetryStrategy, DEFAULT_RETRY_CONFIG } from './types.js';
|
|
10
|
+
export type { RetryAttempt, RetryResult, RetryConfig, RetryRecommendation, RetryHistoryRecord, RetryStatistics } from './types.js';
|
|
11
|
+
export { SmartRetryEngine, createSmartRetryEngine, type TestExecutor } from './retry-engine.js';
|
|
12
|
+
export { FlakinessAwareRetryEngine, createFlakinessAwareRetryEngine, type FlakinessAwareRetryConfig } from './flakiness-integration.js';
|
|
13
|
+
export { RetryVault } from './vault.js';
|
|
14
|
+
export type { FlakinessResult, FlakinessCategory, TestResult as FlakinessTestResult } from '../flakiness/index.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Smart Retry Module (F8)
|
|
3
|
+
*
|
|
4
|
+
* Intelligent retry strategies based on flakiness scoring and pattern detection.
|
|
5
|
+
* Completes the flakiness system: Detection → Scoring → Quarantine → Smart Retry
|
|
6
|
+
*
|
|
7
|
+
* @module retry
|
|
8
|
+
*/
|
|
9
|
+
// Types
|
|
10
|
+
export { RetryStrategy, DEFAULT_RETRY_CONFIG } from './types.js';
|
|
11
|
+
// Core engine
|
|
12
|
+
export { SmartRetryEngine, createSmartRetryEngine } from './retry-engine.js';
|
|
13
|
+
// Flakiness integration
|
|
14
|
+
export { FlakinessAwareRetryEngine, createFlakinessAwareRetryEngine } from './flakiness-integration.js';
|
|
15
|
+
// Vault integration
|
|
16
|
+
export { RetryVault } from './vault.js';
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart Retry Engine
|
|
3
|
+
*
|
|
4
|
+
* Core retry logic with multiple strategies and adaptive behavior.
|
|
5
|
+
*/
|
|
6
|
+
import type { RetryConfig, RetryResult, RetryRecommendation } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Test execution function type
|
|
9
|
+
*/
|
|
10
|
+
export type TestExecutor<T = unknown> = () => Promise<T>;
|
|
11
|
+
/**
|
|
12
|
+
* Smart Retry Engine
|
|
13
|
+
*/
|
|
14
|
+
export declare class SmartRetryEngine {
|
|
15
|
+
private config;
|
|
16
|
+
constructor(config?: Partial<RetryConfig>);
|
|
17
|
+
/**
|
|
18
|
+
* Execute a test with retry logic
|
|
19
|
+
*/
|
|
20
|
+
executeWithRetry(testId: string, executor: TestExecutor, context?: {
|
|
21
|
+
testName?: string;
|
|
22
|
+
gate?: string;
|
|
23
|
+
flakinessScore?: number;
|
|
24
|
+
patternType?: string;
|
|
25
|
+
}): Promise<RetryResult>;
|
|
26
|
+
/**
|
|
27
|
+
* Get retry recommendation based on flakiness and pattern
|
|
28
|
+
*/
|
|
29
|
+
getRetryRecommendation(params: {
|
|
30
|
+
flakinessScore?: number;
|
|
31
|
+
patternType?: string;
|
|
32
|
+
errorType?: string;
|
|
33
|
+
errorCount?: number;
|
|
34
|
+
recentAttempts?: number;
|
|
35
|
+
}): RetryRecommendation;
|
|
36
|
+
/**
|
|
37
|
+
* Calculate delay before retry attempt
|
|
38
|
+
*/
|
|
39
|
+
private calculateDelay;
|
|
40
|
+
/**
|
|
41
|
+
* Get effective strategy based on context
|
|
42
|
+
*/
|
|
43
|
+
private getEffectiveStrategy;
|
|
44
|
+
/**
|
|
45
|
+
* Calculate intelligent delay based on common patterns
|
|
46
|
+
*/
|
|
47
|
+
private getIntelligentDelay;
|
|
48
|
+
/**
|
|
49
|
+
* Apply jitter to delay to avoid thundering herd
|
|
50
|
+
*/
|
|
51
|
+
private applyJitter;
|
|
52
|
+
/**
|
|
53
|
+
* Check if error type is non-retriable
|
|
54
|
+
*/
|
|
55
|
+
private isNonRetriableError;
|
|
56
|
+
/**
|
|
57
|
+
* Check if error is an assertion failure
|
|
58
|
+
*/
|
|
59
|
+
private isAssertionError;
|
|
60
|
+
/**
|
|
61
|
+
* Check if error is a timeout
|
|
62
|
+
*/
|
|
63
|
+
private isTimeoutError;
|
|
64
|
+
/**
|
|
65
|
+
* Delay for specified milliseconds
|
|
66
|
+
*/
|
|
67
|
+
private delay;
|
|
68
|
+
/**
|
|
69
|
+
* Update retry configuration
|
|
70
|
+
*/
|
|
71
|
+
updateConfig(config: Partial<RetryConfig>): void;
|
|
72
|
+
/**
|
|
73
|
+
* Get current configuration
|
|
74
|
+
*/
|
|
75
|
+
getConfig(): RetryConfig;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Create a smart retry engine
|
|
79
|
+
*/
|
|
80
|
+
export declare function createSmartRetryEngine(config?: Partial<RetryConfig>): SmartRetryEngine;
|