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.
Files changed (209) hide show
  1. package/README.md +1 -1
  2. package/dist/commands/ai.d.ts +41 -0
  3. package/dist/commands/ai.js +499 -0
  4. package/dist/commands/ask.js +12 -12
  5. package/dist/commands/coverage.d.ts +8 -0
  6. package/dist/commands/coverage.js +252 -0
  7. package/dist/commands/explain.d.ts +27 -0
  8. package/dist/commands/explain.js +630 -0
  9. package/dist/commands/flakiness.d.ts +73 -0
  10. package/dist/commands/flakiness.js +435 -0
  11. package/dist/commands/generate.d.ts +66 -0
  12. package/dist/commands/generate.js +438 -0
  13. package/dist/commands/init.d.ts +56 -9
  14. package/dist/commands/init.js +217 -10
  15. package/dist/commands/monitor.d.ts +27 -0
  16. package/dist/commands/monitor.js +225 -0
  17. package/dist/commands/ollama.d.ts +40 -0
  18. package/dist/commands/ollama.js +301 -0
  19. package/dist/commands/pack.d.ts +37 -9
  20. package/dist/commands/pack.js +240 -141
  21. package/dist/commands/regression.d.ts +8 -0
  22. package/dist/commands/regression.js +340 -0
  23. package/dist/commands/repair.d.ts +26 -0
  24. package/dist/commands/repair.js +307 -0
  25. package/dist/commands/retry.d.ts +43 -0
  26. package/dist/commands/retry.js +275 -0
  27. package/dist/commands/run.d.ts +8 -3
  28. package/dist/commands/run.js +45 -31
  29. package/dist/commands/slo.d.ts +8 -0
  30. package/dist/commands/slo.js +327 -0
  31. package/dist/core/adapters/playwright-native-api.d.ts +183 -0
  32. package/dist/core/adapters/playwright-native-api.js +461 -0
  33. package/dist/core/adapters/playwright-ui.d.ts +7 -0
  34. package/dist/core/adapters/playwright-ui.js +29 -1
  35. package/dist/core/ai/anthropic-provider.d.ts +50 -0
  36. package/dist/core/ai/anthropic-provider.js +211 -0
  37. package/dist/core/ai/deepseek-provider.d.ts +81 -0
  38. package/dist/core/ai/deepseek-provider.js +254 -0
  39. package/dist/core/ai/index.d.ts +60 -0
  40. package/dist/core/ai/index.js +18 -0
  41. package/dist/core/ai/llm-client.d.ts +45 -0
  42. package/dist/core/ai/llm-client.js +7 -0
  43. package/dist/core/ai/mock-provider.d.ts +49 -0
  44. package/dist/core/ai/mock-provider.js +121 -0
  45. package/dist/core/ai/ollama-provider.d.ts +78 -0
  46. package/dist/core/ai/ollama-provider.js +192 -0
  47. package/dist/core/ai/openai-provider.d.ts +48 -0
  48. package/dist/core/ai/openai-provider.js +188 -0
  49. package/dist/core/ai/provider-factory.d.ts +160 -0
  50. package/dist/core/ai/provider-factory.js +269 -0
  51. package/dist/core/auth/api-key-provider.d.ts +16 -0
  52. package/dist/core/auth/api-key-provider.js +63 -0
  53. package/dist/core/auth/aws-iam-provider.d.ts +35 -0
  54. package/dist/core/auth/aws-iam-provider.js +177 -0
  55. package/dist/core/auth/azure-ad-provider.d.ts +15 -0
  56. package/dist/core/auth/azure-ad-provider.js +99 -0
  57. package/dist/core/auth/basic-auth-provider.d.ts +26 -0
  58. package/dist/core/auth/basic-auth-provider.js +111 -0
  59. package/dist/core/auth/gcp-adc-provider.d.ts +27 -0
  60. package/dist/core/auth/gcp-adc-provider.js +126 -0
  61. package/dist/core/auth/index.d.ts +238 -0
  62. package/dist/core/auth/index.js +82 -0
  63. package/dist/core/auth/jwt-provider.d.ts +19 -0
  64. package/dist/core/auth/jwt-provider.js +160 -0
  65. package/dist/core/auth/manager.d.ts +84 -0
  66. package/dist/core/auth/manager.js +230 -0
  67. package/dist/core/auth/oauth2-provider.d.ts +17 -0
  68. package/dist/core/auth/oauth2-provider.js +114 -0
  69. package/dist/core/auth/totp-provider.d.ts +31 -0
  70. package/dist/core/auth/totp-provider.js +134 -0
  71. package/dist/core/auth/ui-login-provider.d.ts +26 -0
  72. package/dist/core/auth/ui-login-provider.js +198 -0
  73. package/dist/core/cache/index.d.ts +7 -0
  74. package/dist/core/cache/index.js +6 -0
  75. package/dist/core/cache/lru-cache.d.ts +203 -0
  76. package/dist/core/cache/lru-cache.js +397 -0
  77. package/dist/core/coverage/analyzer.d.ts +101 -0
  78. package/dist/core/coverage/analyzer.js +415 -0
  79. package/dist/core/coverage/collector.d.ts +74 -0
  80. package/dist/core/coverage/collector.js +459 -0
  81. package/dist/core/coverage/config.d.ts +37 -0
  82. package/dist/core/coverage/config.js +156 -0
  83. package/dist/core/coverage/index.d.ts +11 -0
  84. package/dist/core/coverage/index.js +15 -0
  85. package/dist/core/coverage/types.d.ts +267 -0
  86. package/dist/core/coverage/types.js +6 -0
  87. package/dist/core/coverage/vault.d.ts +95 -0
  88. package/dist/core/coverage/vault.js +405 -0
  89. package/dist/core/dashboard/assets.d.ts +6 -0
  90. package/dist/core/dashboard/assets.js +690 -0
  91. package/dist/core/dashboard/index.d.ts +6 -0
  92. package/dist/core/dashboard/index.js +5 -0
  93. package/dist/core/dashboard/server.d.ts +72 -0
  94. package/dist/core/dashboard/server.js +354 -0
  95. package/dist/core/dashboard/types.d.ts +70 -0
  96. package/dist/core/dashboard/types.js +5 -0
  97. package/dist/core/discoverer/index.d.ts +115 -0
  98. package/dist/core/discoverer/index.js +250 -0
  99. package/dist/core/flakiness/index.d.ts +228 -0
  100. package/dist/core/flakiness/index.js +384 -0
  101. package/dist/core/generation/code-formatter.d.ts +111 -0
  102. package/dist/core/generation/code-formatter.js +307 -0
  103. package/dist/core/generation/code-generator.d.ts +144 -0
  104. package/dist/core/generation/code-generator.js +293 -0
  105. package/dist/core/generation/generator.d.ts +40 -0
  106. package/dist/core/generation/generator.js +76 -0
  107. package/dist/core/generation/index.d.ts +30 -0
  108. package/dist/core/generation/index.js +28 -0
  109. package/dist/core/generation/pack-generator.d.ts +107 -0
  110. package/dist/core/generation/pack-generator.js +416 -0
  111. package/dist/core/generation/prompt-builder.d.ts +132 -0
  112. package/dist/core/generation/prompt-builder.js +672 -0
  113. package/dist/core/generation/source-analyzer.d.ts +213 -0
  114. package/dist/core/generation/source-analyzer.js +657 -0
  115. package/dist/core/generation/test-optimizer.d.ts +117 -0
  116. package/dist/core/generation/test-optimizer.js +328 -0
  117. package/dist/core/generation/types.d.ts +214 -0
  118. package/dist/core/generation/types.js +4 -0
  119. package/dist/core/index.d.ts +23 -1
  120. package/dist/core/index.js +39 -0
  121. package/dist/core/pack/validator.js +31 -1
  122. package/dist/core/pack-v2/index.d.ts +9 -0
  123. package/dist/core/pack-v2/index.js +8 -0
  124. package/dist/core/pack-v2/loader.d.ts +62 -0
  125. package/dist/core/pack-v2/loader.js +231 -0
  126. package/dist/core/pack-v2/migrator.d.ts +56 -0
  127. package/dist/core/pack-v2/migrator.js +455 -0
  128. package/dist/core/pack-v2/validator.d.ts +61 -0
  129. package/dist/core/pack-v2/validator.js +577 -0
  130. package/dist/core/regression/detector.d.ts +107 -0
  131. package/dist/core/regression/detector.js +497 -0
  132. package/dist/core/regression/index.d.ts +9 -0
  133. package/dist/core/regression/index.js +11 -0
  134. package/dist/core/regression/trend-analyzer.d.ts +102 -0
  135. package/dist/core/regression/trend-analyzer.js +345 -0
  136. package/dist/core/regression/types.d.ts +222 -0
  137. package/dist/core/regression/types.js +7 -0
  138. package/dist/core/regression/vault.d.ts +87 -0
  139. package/dist/core/regression/vault.js +289 -0
  140. package/dist/core/repair/engine/fixer.d.ts +24 -0
  141. package/dist/core/repair/engine/fixer.js +226 -0
  142. package/dist/core/repair/engine/suggestion-engine.d.ts +18 -0
  143. package/dist/core/repair/engine/suggestion-engine.js +187 -0
  144. package/dist/core/repair/index.d.ts +10 -0
  145. package/dist/core/repair/index.js +13 -0
  146. package/dist/core/repair/repairer.d.ts +90 -0
  147. package/dist/core/repair/repairer.js +284 -0
  148. package/dist/core/repair/types.d.ts +91 -0
  149. package/dist/core/repair/types.js +6 -0
  150. package/dist/core/repair/utils/error-analyzer.d.ts +28 -0
  151. package/dist/core/repair/utils/error-analyzer.js +264 -0
  152. package/dist/core/retry/flakiness-integration.d.ts +60 -0
  153. package/dist/core/retry/flakiness-integration.js +228 -0
  154. package/dist/core/retry/index.d.ts +14 -0
  155. package/dist/core/retry/index.js +16 -0
  156. package/dist/core/retry/retry-engine.d.ts +80 -0
  157. package/dist/core/retry/retry-engine.js +296 -0
  158. package/dist/core/retry/types.d.ts +178 -0
  159. package/dist/core/retry/types.js +52 -0
  160. package/dist/core/retry/vault.d.ts +77 -0
  161. package/dist/core/retry/vault.js +304 -0
  162. package/dist/core/runner/e2e-helpers.d.ts +102 -0
  163. package/dist/core/runner/e2e-helpers.js +153 -0
  164. package/dist/core/runner/phase3-runner.d.ts +101 -2
  165. package/dist/core/runner/phase3-runner.js +559 -24
  166. package/dist/core/self-healing/assertion-healer.d.ts +97 -0
  167. package/dist/core/self-healing/assertion-healer.js +371 -0
  168. package/dist/core/self-healing/engine.d.ts +122 -0
  169. package/dist/core/self-healing/engine.js +538 -0
  170. package/dist/core/self-healing/index.d.ts +10 -0
  171. package/dist/core/self-healing/index.js +11 -0
  172. package/dist/core/self-healing/selector-healer.d.ts +103 -0
  173. package/dist/core/self-healing/selector-healer.js +372 -0
  174. package/dist/core/self-healing/types.d.ts +152 -0
  175. package/dist/core/self-healing/types.js +6 -0
  176. package/dist/core/slo/config.d.ts +107 -0
  177. package/dist/core/slo/config.js +360 -0
  178. package/dist/core/slo/index.d.ts +11 -0
  179. package/dist/core/slo/index.js +15 -0
  180. package/dist/core/slo/sli-calculator.d.ts +92 -0
  181. package/dist/core/slo/sli-calculator.js +364 -0
  182. package/dist/core/slo/slo-tracker.d.ts +148 -0
  183. package/dist/core/slo/slo-tracker.js +379 -0
  184. package/dist/core/slo/types.d.ts +281 -0
  185. package/dist/core/slo/types.js +7 -0
  186. package/dist/core/slo/vault.d.ts +102 -0
  187. package/dist/core/slo/vault.js +427 -0
  188. package/dist/core/tui/index.d.ts +7 -0
  189. package/dist/core/tui/index.js +6 -0
  190. package/dist/core/tui/monitor.d.ts +92 -0
  191. package/dist/core/tui/monitor.js +271 -0
  192. package/dist/core/tui/renderer.d.ts +33 -0
  193. package/dist/core/tui/renderer.js +218 -0
  194. package/dist/core/tui/types.d.ts +63 -0
  195. package/dist/core/tui/types.js +5 -0
  196. package/dist/core/types/pack-v2.d.ts +425 -0
  197. package/dist/core/types/pack-v2.js +8 -0
  198. package/dist/core/vault/index.d.ts +116 -0
  199. package/dist/core/vault/index.js +400 -5
  200. package/dist/core/watch/index.d.ts +7 -0
  201. package/dist/core/watch/index.js +6 -0
  202. package/dist/core/watch/watch-mode.d.ts +213 -0
  203. package/dist/core/watch/watch-mode.js +389 -0
  204. package/dist/index.js +68 -68
  205. package/dist/utils/config.d.ts +5 -0
  206. package/dist/utils/config.js +136 -0
  207. package/package.json +5 -1
  208. package/dist/core/adapters/playwright-api.d.ts +0 -82
  209. package/dist/core/adapters/playwright-api.js +0 -264
@@ -0,0 +1,538 @@
1
+ /**
2
+ * Self-Healing Engine
3
+ *
4
+ * Main orchestration for self-healing functionality.
5
+ * Coordinates selector healing, assertion healing, and AI assistance.
6
+ */
7
+ import { SelectorHealer } from './selector-healer.js';
8
+ import { AssertionHealer } from './assertion-healer.js';
9
+ import { createLLMProvider } from '../ai/index.js';
10
+ /**
11
+ * Self-Healing Engine class
12
+ */
13
+ export class SelfHealingEngine {
14
+ config;
15
+ selectorHealer;
16
+ assertionHealer;
17
+ currentSession;
18
+ constructor(config) {
19
+ this.config = config;
20
+ this.selectorHealer = new SelectorHealer(config);
21
+ this.assertionHealer = new AssertionHealer(config);
22
+ }
23
+ /**
24
+ * Start a new self-healing session
25
+ */
26
+ startSession() {
27
+ const sessionId = this.generateSessionId();
28
+ const startTime = Date.now();
29
+ this.currentSession = {
30
+ sessionId,
31
+ startTime,
32
+ endTime: 0,
33
+ duration: 0,
34
+ config: this.config,
35
+ selectorHeals: [],
36
+ assertionUpdates: [],
37
+ apiHeals: [],
38
+ success: false,
39
+ unhealed: 0,
40
+ summary: {
41
+ totalIssues: 0,
42
+ healed: 0,
43
+ failed: 0,
44
+ highConfidence: 0,
45
+ mediumConfidence: 0,
46
+ lowConfidence: 0,
47
+ byType: {
48
+ selector: 0,
49
+ assertion: 0,
50
+ api: 0,
51
+ timeout: 0
52
+ },
53
+ successRate: 0
54
+ }
55
+ };
56
+ return sessionId;
57
+ }
58
+ /**
59
+ * End current session and return results
60
+ */
61
+ endSession() {
62
+ if (!this.currentSession) {
63
+ return null;
64
+ }
65
+ this.currentSession.endTime = Date.now();
66
+ this.currentSession.duration = this.currentSession.endTime - this.currentSession.startTime;
67
+ this.currentSession.summary = this.calculateSummary();
68
+ const session = { ...this.currentSession };
69
+ this.currentSession = undefined;
70
+ return session;
71
+ }
72
+ /**
73
+ * Attempt to heal a test failure
74
+ */
75
+ async healFailure(context) {
76
+ if (!this.config.enabled || !this.currentSession) {
77
+ return {
78
+ success: false,
79
+ healed: false,
80
+ suggestion: null,
81
+ reason: 'Self-healing is disabled or no active session'
82
+ };
83
+ }
84
+ this.currentSession.summary.totalIssues++;
85
+ // Determine failure type and attempt healing
86
+ switch (context.testType) {
87
+ case 'ui':
88
+ return await this.healUiFailure(context);
89
+ case 'api':
90
+ return await this.healApiFailure(context);
91
+ default:
92
+ return await this.healGenericFailure(context);
93
+ }
94
+ }
95
+ /**
96
+ * Heal a UI test failure (selector issues)
97
+ */
98
+ async healUiFailure(context) {
99
+ // Extract selector from error message
100
+ const selector = this.extractSelector(context.errorMessage);
101
+ if (!selector) {
102
+ return {
103
+ success: false,
104
+ healed: false,
105
+ suggestion: null,
106
+ reason: 'Could not extract selector from error message'
107
+ };
108
+ }
109
+ const errorType = this.inferSelectorErrorType(context.errorMessage);
110
+ const result = await this.selectorHealer.healSelector({
111
+ originalSelector: selector,
112
+ pageUrl: context.pageUrl,
113
+ domSnapshot: context.domSnapshot,
114
+ errorType,
115
+ expectedText: this.extractExpectedText(context.errorMessage),
116
+ confidenceThreshold: this.config.confidenceThreshold
117
+ });
118
+ this.currentSession.selectorHeals.push(result);
119
+ this.currentSession.summary.byType.selector++;
120
+ if (result.confidence >= this.config.confidenceThreshold && result.healedSelector !== result.originalSelector) {
121
+ this.currentSession.summary.healed++;
122
+ this.currentSession.summary.highConfidence++;
123
+ return {
124
+ success: true,
125
+ healed: true,
126
+ suggestion: {
127
+ code: this.formatSelectorHeal(result),
128
+ explanation: `Replace selector "${result.originalSelector}" with "${result.healedSelector}" using ${result.strategy} strategy (confidence: ${(result.confidence * 100).toFixed(0)}%)`,
129
+ filePath: context.testSource,
130
+ lineNumber: this.extractLineNumber(context.stackTrace),
131
+ confidence: result.confidence,
132
+ effort: 'low',
133
+ healingType: 'selector'
134
+ },
135
+ reason: 'Selector healed successfully'
136
+ };
137
+ }
138
+ this.currentSession.summary.failed++;
139
+ this.currentSession.unhealed++;
140
+ // Try AI-assisted healing if enabled
141
+ if (this.config.features.aiAssisted) {
142
+ const aiSuggestion = await this.tryAiHealing(context);
143
+ if (aiSuggestion) {
144
+ return {
145
+ success: true,
146
+ healed: false,
147
+ suggestion: aiSuggestion,
148
+ reason: 'AI suggestion available'
149
+ };
150
+ }
151
+ }
152
+ return {
153
+ success: false,
154
+ healed: false,
155
+ suggestion: null,
156
+ reason: result.failureReason
157
+ };
158
+ }
159
+ /**
160
+ * Heal an API test failure
161
+ */
162
+ async healApiFailure(context) {
163
+ if (!context.requestDetails) {
164
+ return {
165
+ success: false,
166
+ healed: false,
167
+ suggestion: null,
168
+ reason: 'No request details available'
169
+ };
170
+ }
171
+ const apiHeal = this.analyzeApiFailure(context);
172
+ this.currentSession.apiHeals.push(apiHeal);
173
+ this.currentSession.summary.byType.api++;
174
+ if (apiHeal.confidence >= this.config.confidenceThreshold) {
175
+ this.currentSession.summary.healed++;
176
+ return {
177
+ success: true,
178
+ healed: true,
179
+ suggestion: {
180
+ code: apiHeal.suggestion,
181
+ explanation: `API change detected: ${apiHeal.changeType}. Suggested fix: ${apiHeal.suggestion}`,
182
+ filePath: context.testSource,
183
+ lineNumber: this.extractLineNumber(context.stackTrace),
184
+ confidence: apiHeal.confidence,
185
+ effort: 'medium',
186
+ healingType: 'api'
187
+ },
188
+ reason: 'API failure analyzed'
189
+ };
190
+ }
191
+ this.currentSession.summary.failed++;
192
+ this.currentSession.unhealed++;
193
+ return {
194
+ success: false,
195
+ healed: false,
196
+ suggestion: null,
197
+ reason: 'API heal confidence below threshold'
198
+ };
199
+ }
200
+ /**
201
+ * Heal a generic test failure
202
+ */
203
+ async healGenericFailure(context) {
204
+ // Try AI-assisted healing for generic failures
205
+ if (this.config.features.aiAssisted) {
206
+ const aiSuggestion = await this.tryAiHealing(context);
207
+ if (aiSuggestion) {
208
+ this.currentSession.summary.healed++;
209
+ return {
210
+ success: true,
211
+ healed: false,
212
+ suggestion: aiSuggestion,
213
+ reason: 'AI suggestion available'
214
+ };
215
+ }
216
+ }
217
+ this.currentSession.summary.failed++;
218
+ this.currentSession.unhealed++;
219
+ return {
220
+ success: false,
221
+ healed: false,
222
+ suggestion: null,
223
+ reason: 'No healing strategy available for this failure type'
224
+ };
225
+ }
226
+ /**
227
+ * Try AI-assisted healing
228
+ */
229
+ async tryAiHealing(context) {
230
+ try {
231
+ const aiClient = await createLLMProvider({ preferred: 'deepseek' });
232
+ const prompt = this.buildAiPrompt(context);
233
+ const response = await aiClient.generate({
234
+ prompt,
235
+ systemPrompt: 'You are an expert test debugging assistant. Analyze test failures and provide specific code fixes.',
236
+ maxTokens: 1000,
237
+ temperature: 0.3
238
+ });
239
+ if (!response.content) {
240
+ return null;
241
+ }
242
+ return {
243
+ code: this.extractCodeFromAiResponse(response.content),
244
+ explanation: this.extractExplanationFromAiResponse(response.content),
245
+ filePath: context.testSource,
246
+ lineNumber: this.extractLineNumber(context.stackTrace),
247
+ confidence: 0.7, // AI suggestions have medium confidence by default
248
+ effort: 'medium',
249
+ healingType: 'other',
250
+ applied: false,
251
+ verified: false
252
+ };
253
+ }
254
+ catch (error) {
255
+ // Silently fail on AI errors
256
+ return null;
257
+ }
258
+ }
259
+ /**
260
+ * Build AI prompt from failure context
261
+ */
262
+ buildAiPrompt(context) {
263
+ let prompt = `Test Failure Analysis Request\n\n`;
264
+ prompt += `Test Type: ${context.testType}\n`;
265
+ prompt += `Gate: ${context.gate}\n`;
266
+ prompt += `Test Source: ${context.testSource}\n\n`;
267
+ prompt += `Error Message:\n${context.errorMessage}\n\n`;
268
+ if (context.stackTrace) {
269
+ prompt += `Stack Trace:\n${context.stackTrace}\n\n`;
270
+ }
271
+ if (context.pageUrl) {
272
+ prompt += `Page URL: ${context.pageUrl}\n\n`;
273
+ }
274
+ if (context.requestDetails) {
275
+ prompt += `Request Details:\n`;
276
+ prompt += `- URL: ${context.requestDetails.url}\n`;
277
+ prompt += `- Method: ${context.requestDetails.method}\n`;
278
+ if (context.requestDetails.status) {
279
+ prompt += `- Status: ${context.requestDetails.status}\n`;
280
+ }
281
+ prompt += `\n`;
282
+ }
283
+ prompt += `Please provide:\n`;
284
+ prompt += `1. A specific code fix for this failure\n`;
285
+ prompt += `2. A brief explanation of why this fix works\n`;
286
+ prompt += `3. Estimated effort level (low/medium/high)\n\n`;
287
+ prompt += `Format your response with:\n`;
288
+ prompt += `- CODE: [your fix]\n`;
289
+ prompt += `- EXPLANATION: [your explanation]\n`;
290
+ prompt += `- EFFORT: [low/medium/high]\n`;
291
+ return prompt;
292
+ }
293
+ /**
294
+ * Extract code from AI response
295
+ */
296
+ extractCodeFromAiResponse(response) {
297
+ const codeMatch = response.match(/CODE:\s*([\s\S]*?)(?=EXPLANATION:|$)/i);
298
+ return codeMatch ? codeMatch[1].trim() : response;
299
+ }
300
+ /**
301
+ * Extract explanation from AI response
302
+ */
303
+ extractExplanationFromAiResponse(response) {
304
+ const expMatch = response.match(/EXPLANATION:\s*([\s\S]*?)(?=EFFORT:|$)/i);
305
+ return expMatch ? expMatch[1].trim() : 'AI-generated fix';
306
+ }
307
+ /**
308
+ * Analyze API failure
309
+ */
310
+ analyzeApiFailure(context) {
311
+ const details = context.requestDetails;
312
+ const changeType = this.detectApiChangeType(context);
313
+ const confidences = {
314
+ 'path_changed': 0.7,
315
+ 'method_changed': 0.6,
316
+ 'status_changed': 0.8,
317
+ 'response_structure': 0.65,
318
+ 'auth_required': 0.75,
319
+ 'deprecated': 0.9,
320
+ 'moved_permanently': 0.85
321
+ };
322
+ let suggestion = '';
323
+ switch (changeType) {
324
+ case 'status_changed':
325
+ suggestion = `Update expected status from ${context.metadata?.expectedStatus} to ${details.status}`;
326
+ break;
327
+ case 'auth_required':
328
+ suggestion = 'Add authentication headers to the request';
329
+ break;
330
+ case 'path_changed':
331
+ suggestion = 'Update the API endpoint path';
332
+ break;
333
+ case 'method_changed':
334
+ suggestion = `Change request method to ${details.method}`;
335
+ break;
336
+ default:
337
+ suggestion = 'Review API specification for changes';
338
+ }
339
+ return {
340
+ originalSpec: `${details.method} ${details.url}`,
341
+ healedSpec: `${details.method} ${details.url}`,
342
+ changeType,
343
+ suggestion,
344
+ confidence: confidences[changeType] || 0.5
345
+ };
346
+ }
347
+ /**
348
+ * Detect API change type from context
349
+ */
350
+ detectApiChangeType(context) {
351
+ const msg = context.errorMessage.toLowerCase();
352
+ const details = context.requestDetails;
353
+ if (msg.includes('401') || msg.includes('403') || msg.includes('unauthorized') || msg.includes('forbidden')) {
354
+ return 'auth_required';
355
+ }
356
+ if (msg.includes('404') || msg.includes('not found')) {
357
+ return 'path_changed';
358
+ }
359
+ if (msg.includes('405') || msg.includes('method not allowed')) {
360
+ return 'method_changed';
361
+ }
362
+ if (msg.includes('410') || msg.includes('gone')) {
363
+ return 'deprecated';
364
+ }
365
+ if (msg.includes('status') || msg.includes('expected 200')) {
366
+ return 'status_changed';
367
+ }
368
+ if (msg.includes('response') || msg.includes('cannot read') || msg.includes('undefined')) {
369
+ return 'response_structure';
370
+ }
371
+ if (msg.includes('301') || msg.includes('302') || msg.includes('moved')) {
372
+ return 'moved_permanently';
373
+ }
374
+ return 'response_structure';
375
+ }
376
+ /**
377
+ * Extract selector from error message
378
+ */
379
+ extractSelector(errorMessage) {
380
+ // Common patterns for selector errors
381
+ const patterns = [
382
+ /selector['"]\s*:\s*['"]([^'"]+)['"]/i,
383
+ /['"]([^'"]+\s*selector[^'"]*)['"]/i,
384
+ /found\s+['"]([^'"]+)['"]/i,
385
+ /waiting\s+for\s+['"]([^'"]+)['"]/i
386
+ ];
387
+ for (const pattern of patterns) {
388
+ const match = errorMessage.match(pattern);
389
+ if (match && match[1]) {
390
+ return match[1];
391
+ }
392
+ }
393
+ return null;
394
+ }
395
+ /**
396
+ * Extract expected text from error message
397
+ */
398
+ extractExpectedText(errorMessage) {
399
+ const textMatch = errorMessage.match(/text\s+['"]([^'"]+)['"]/i);
400
+ return textMatch ? textMatch[1] : undefined;
401
+ }
402
+ /**
403
+ * Infer selector error type from message
404
+ */
405
+ inferSelectorErrorType(errorMessage) {
406
+ const msg = errorMessage.toLowerCase();
407
+ if (msg.includes('timeout') || msg.includes('timed out')) {
408
+ return 'timeout';
409
+ }
410
+ if (msg.includes('not found') || msg.includes('found 0')) {
411
+ return 'not_found';
412
+ }
413
+ if (msg.includes('found multiple') || msg.includes('ambiguous')) {
414
+ return 'ambiguous';
415
+ }
416
+ if (msg.includes('detached') || msg.includes('removed from dom')) {
417
+ return 'detached';
418
+ }
419
+ if (msg.includes('not visible') || msg.includes('hidden')) {
420
+ return 'invisible';
421
+ }
422
+ if (msg.includes('stale') || msg.includes('element reference')) {
423
+ return 'stale';
424
+ }
425
+ return 'not_found';
426
+ }
427
+ /**
428
+ * Extract line number from stack trace
429
+ */
430
+ extractLineNumber(stackTrace) {
431
+ if (!stackTrace)
432
+ return 0;
433
+ const lineMatch = stackTrace.match(/:(\d+)(?::\d+)?/);
434
+ return lineMatch ? parseInt(lineMatch[1], 10) : 0;
435
+ }
436
+ /**
437
+ * Format selector heal result
438
+ */
439
+ formatSelectorHeal(result) {
440
+ return `// Replace: ${result.originalSelector}\n// With: ${result.healedSelector}\n// Strategy: ${result.strategy} (confidence: ${(result.confidence * 100).toFixed(0)}%)`;
441
+ }
442
+ /**
443
+ * Calculate session summary
444
+ */
445
+ calculateSummary() {
446
+ if (!this.currentSession) {
447
+ return {
448
+ totalIssues: 0,
449
+ healed: 0,
450
+ failed: 0,
451
+ highConfidence: 0,
452
+ mediumConfidence: 0,
453
+ lowConfidence: 0,
454
+ byType: { selector: 0, assertion: 0, api: 0, timeout: 0 },
455
+ successRate: 0
456
+ };
457
+ }
458
+ const summary = this.currentSession.summary;
459
+ const allHeals = [
460
+ ...this.currentSession.selectorHeals,
461
+ ...this.currentSession.assertionUpdates,
462
+ ...this.currentSession.apiHeals
463
+ ];
464
+ for (const heal of allHeals) {
465
+ if (heal.confidence >= 0.8) {
466
+ summary.highConfidence++;
467
+ }
468
+ else if (heal.confidence >= 0.5) {
469
+ summary.mediumConfidence++;
470
+ }
471
+ else {
472
+ summary.lowConfidence++;
473
+ }
474
+ }
475
+ summary.successRate = summary.totalIssues > 0
476
+ ? summary.healed / summary.totalIssues
477
+ : 0;
478
+ return summary;
479
+ }
480
+ /**
481
+ * Generate unique session ID
482
+ */
483
+ generateSessionId() {
484
+ return `sh_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
485
+ }
486
+ /**
487
+ * Get current session
488
+ */
489
+ getSession() {
490
+ return this.currentSession;
491
+ }
492
+ /**
493
+ * Update configuration
494
+ */
495
+ updateConfig(updates) {
496
+ this.config = { ...this.config, ...updates };
497
+ this.selectorHealer = new SelectorHealer(this.config);
498
+ this.assertionHealer = new AssertionHealer(this.config);
499
+ }
500
+ /**
501
+ * Check if self-healing is enabled
502
+ */
503
+ isEnabled() {
504
+ return this.config.enabled;
505
+ }
506
+ /**
507
+ * Get confidence threshold
508
+ */
509
+ getConfidenceThreshold() {
510
+ return this.config.confidenceThreshold;
511
+ }
512
+ }
513
+ /**
514
+ * Create a self-healing engine with default config
515
+ */
516
+ export function createSelfHealingEngine(config) {
517
+ const defaultConfig = {
518
+ enabled: true,
519
+ maxAttempts: 3,
520
+ confidenceThreshold: 0.7,
521
+ autoApply: false,
522
+ backup: true,
523
+ features: {
524
+ selectorHealing: true,
525
+ assertionUpdates: true,
526
+ apiHealing: true,
527
+ timeoutAdjustment: true,
528
+ aiAssisted: true
529
+ },
530
+ selectorStrategies: {
531
+ useDataTestIds: true,
532
+ useAriaLabels: true,
533
+ useTextSelectors: true,
534
+ fuzzyMatching: true
535
+ }
536
+ };
537
+ return new SelfHealingEngine({ ...defaultConfig, ...config });
538
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * QA360 Self-Healing Module
3
+ *
4
+ * Exports all self-healing functionality.
5
+ */
6
+ export type { SelfHealingConfig, SelectorErrorType, SelectorHealingStrategy, SelectorHealingResult, AssertionType, AssertionUpdateResult, ApiChangeType, ApiHealingResult, SelfHealingSession, SelfHealingSummary, TestFailureContext, AiHealingSuggestion } from './types.js';
7
+ export { SelectorHealer, createDefaultSelfHealingConfig } from './selector-healer.js';
8
+ export { AssertionHealer } from './assertion-healer.js';
9
+ export { SelfHealingEngine, createSelfHealingEngine, type SelfHealingResult } from './engine.js';
10
+ export { createDefaultSelfHealingConfig as createSelfHealingConfig } from './selector-healer.js';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * QA360 Self-Healing Module
3
+ *
4
+ * Exports all self-healing functionality.
5
+ */
6
+ // Classes
7
+ export { SelectorHealer, createDefaultSelfHealingConfig } from './selector-healer.js';
8
+ export { AssertionHealer } from './assertion-healer.js';
9
+ export { SelfHealingEngine, createSelfHealingEngine } from './engine.js';
10
+ // Convenience re-exports
11
+ export { createDefaultSelfHealingConfig as createSelfHealingConfig } from './selector-healer.js';
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Selector Healer
3
+ *
4
+ * Intelligently heals broken CSS selectors in UI/E2E tests.
5
+ * Uses multiple strategies to find replacement selectors.
6
+ */
7
+ import type { SelectorHealingResult, SelectorHealingStrategy, SelectorErrorType, SelfHealingConfig } from './types.js';
8
+ /**
9
+ * Configuration for selector healing
10
+ */
11
+ interface SelectorHealOptions {
12
+ /** Page URL where selector failed */
13
+ pageUrl?: string;
14
+ /** DOM snapshot at time of failure */
15
+ domSnapshot?: {
16
+ title: string;
17
+ url: string;
18
+ html?: string;
19
+ elements: Record<string, number>;
20
+ };
21
+ /** Error type that occurred */
22
+ errorType?: SelectorErrorType;
23
+ /** Original selector that failed */
24
+ originalSelector: string;
25
+ /** Expected element type (button, input, etc.) */
26
+ expectedType?: string;
27
+ /** Text content the element should have */
28
+ expectedText?: string;
29
+ /** Confidence threshold for healing */
30
+ confidenceThreshold?: number;
31
+ }
32
+ /**
33
+ * Candidate replacement selector
34
+ */
35
+ interface SelectorCandidate {
36
+ selector: string;
37
+ strategy: SelectorHealingStrategy;
38
+ confidence: number;
39
+ reason: string;
40
+ }
41
+ /**
42
+ * Selector Healer class
43
+ */
44
+ export declare class SelectorHealer {
45
+ private config;
46
+ constructor(config: SelfHealingConfig);
47
+ /**
48
+ * Attempt to heal a broken selector
49
+ */
50
+ healSelector(options: SelectorHealOptions): Promise<SelectorHealingResult>;
51
+ /**
52
+ * Try data-testid attribute strategies
53
+ */
54
+ private tryDataTestIdStrategies;
55
+ /**
56
+ * Try ARIA label strategies
57
+ */
58
+ private tryAriaLabelStrategies;
59
+ /**
60
+ * Try text content strategies
61
+ */
62
+ private tryTextContentStrategies;
63
+ /**
64
+ * Try fuzzy matching strategies
65
+ */
66
+ private tryFuzzyMatchStrategies;
67
+ /**
68
+ * Infer failure reason from error type
69
+ */
70
+ private inferFailureReason;
71
+ /**
72
+ * Escape string for CSS selectors
73
+ */
74
+ private escapeCssString;
75
+ /**
76
+ * Escape string for XPath
77
+ */
78
+ private escapeXPathString;
79
+ /**
80
+ * Parse selector to understand its structure
81
+ */
82
+ parseSelector(selector: string): {
83
+ tag?: string;
84
+ id?: string;
85
+ classes: string[];
86
+ attributes: Record<string, string>;
87
+ pseudoClasses: string[];
88
+ combinators: string[];
89
+ };
90
+ /**
91
+ * Generate multiple healing candidates for a selector
92
+ */
93
+ generateHealingCandidates(selector: string, context?: Pick<SelectorHealOptions, 'expectedText' | 'errorType'>): Promise<SelectorCandidate[]>;
94
+ /**
95
+ * Remove duplicate candidates
96
+ */
97
+ private deduplicateCandidates;
98
+ }
99
+ /**
100
+ * Create a default self-healing config
101
+ */
102
+ export declare function createDefaultSelfHealingConfig(): SelfHealingConfig;
103
+ export {};