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,372 @@
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
+ /**
8
+ * Selector Healer class
9
+ */
10
+ export class SelectorHealer {
11
+ config;
12
+ constructor(config) {
13
+ this.config = config;
14
+ }
15
+ /**
16
+ * Attempt to heal a broken selector
17
+ */
18
+ async healSelector(options) {
19
+ const candidates = [];
20
+ // Try each enabled strategy
21
+ if (this.config.features.selectorHealing) {
22
+ if (this.config.selectorStrategies.useDataTestIds) {
23
+ candidates.push(...this.tryDataTestIdStrategies(options));
24
+ }
25
+ if (this.config.selectorStrategies.useAriaLabels) {
26
+ candidates.push(...this.tryAriaLabelStrategies(options));
27
+ }
28
+ if (this.config.selectorStrategies.useTextSelectors) {
29
+ candidates.push(...this.tryTextContentStrategies(options));
30
+ }
31
+ if (this.config.selectorStrategies.fuzzyMatching) {
32
+ candidates.push(...this.tryFuzzyMatchStrategies(options));
33
+ }
34
+ }
35
+ // Sort by confidence and return best candidate
36
+ candidates.sort((a, b) => b.confidence - a.confidence);
37
+ const best = candidates[0];
38
+ if (best && best.confidence >= (this.config.confidenceThreshold || 0.7)) {
39
+ return {
40
+ originalSelector: options.originalSelector,
41
+ healedSelector: best.selector,
42
+ strategy: best.strategy,
43
+ confidence: best.confidence,
44
+ elementCount: 1,
45
+ failureReason: this.inferFailureReason(options.errorType)
46
+ };
47
+ }
48
+ // No good heal found
49
+ return {
50
+ originalSelector: options.originalSelector,
51
+ healedSelector: options.originalSelector,
52
+ strategy: 'no_heal',
53
+ confidence: 0,
54
+ elementCount: 0,
55
+ failureReason: this.inferFailureReason(options.errorType)
56
+ };
57
+ }
58
+ /**
59
+ * Try data-testid attribute strategies
60
+ */
61
+ tryDataTestIdStrategies(options) {
62
+ const candidates = [];
63
+ const { originalSelector, expectedText } = options;
64
+ // Extract potential test ID from original selector
65
+ const idMatch = originalSelector.match(/#(\w+)/);
66
+ const classMatch = originalSelector.match(/\.([\w-]+)/);
67
+ const tagMatch = originalSelector.match(/^(\w+)/);
68
+ // Strategy 1: Convert ID to data-testid
69
+ if (idMatch) {
70
+ const testId = idMatch[1];
71
+ candidates.push({
72
+ selector: `[data-testid="${testId}"]`,
73
+ strategy: 'data_testid',
74
+ confidence: 0.85,
75
+ reason: 'Converted ID to data-testid attribute'
76
+ });
77
+ }
78
+ // Strategy 2: Convert class to data-testid
79
+ if (classMatch) {
80
+ const testId = classMatch[1].replace(/-/g, '_');
81
+ candidates.push({
82
+ selector: `[data-testid="${testId}"]`,
83
+ strategy: 'data_testid',
84
+ confidence: 0.75,
85
+ reason: 'Converted class name to data-testid attribute'
86
+ });
87
+ }
88
+ // Strategy 3: Generate data-testid from expected text
89
+ if (expectedText) {
90
+ const testId = expectedText
91
+ .toLowerCase()
92
+ .replace(/[^a-z0-9]+/g, '_')
93
+ .replace(/^_+|_+$/g, '');
94
+ candidates.push({
95
+ selector: `[data-testid="${testId}"]`,
96
+ strategy: 'data_testid',
97
+ confidence: 0.70,
98
+ reason: 'Generated data-testid from expected text'
99
+ });
100
+ }
101
+ // Strategy 4: Tag-based data-testid
102
+ if (tagMatch) {
103
+ const tag = tagMatch[1];
104
+ candidates.push({
105
+ selector: `${tag}[data-testid]`,
106
+ strategy: 'data_testid',
107
+ confidence: 0.60,
108
+ reason: 'Tag with any data-testid attribute'
109
+ });
110
+ }
111
+ return candidates;
112
+ }
113
+ /**
114
+ * Try ARIA label strategies
115
+ */
116
+ tryAriaLabelStrategies(options) {
117
+ const candidates = [];
118
+ const { originalSelector, expectedText } = options;
119
+ // Extract tag if available
120
+ const tagMatch = originalSelector.match(/^(\w+)/);
121
+ const tag = tagMatch ? `${tagMatch[1]}[` : '[';
122
+ // Strategy 1: Use expected text as aria-label
123
+ if (expectedText) {
124
+ const escapedText = this.escapeCssString(expectedText);
125
+ candidates.push({
126
+ selector: `${tag}aria-label="${escapedText}"]`,
127
+ strategy: 'aria_label',
128
+ confidence: 0.80,
129
+ reason: 'Match by aria-label attribute'
130
+ });
131
+ }
132
+ // Strategy 2: Role-based selectors
133
+ const roleMap = {
134
+ 'button': 'button',
135
+ 'input': 'textbox',
136
+ 'select': 'combobox',
137
+ 'a': 'link',
138
+ };
139
+ if (tagMatch) {
140
+ const tagName = tagMatch[1];
141
+ const role = roleMap[tagName];
142
+ if (role) {
143
+ candidates.push({
144
+ selector: `[role="${role}"]`,
145
+ strategy: 'aria_label',
146
+ confidence: 0.60,
147
+ reason: `Match by ARIA role for <${tagName}> element`
148
+ });
149
+ }
150
+ }
151
+ return candidates;
152
+ }
153
+ /**
154
+ * Try text content strategies
155
+ */
156
+ tryTextContentStrategies(options) {
157
+ const candidates = [];
158
+ const { originalSelector, expectedText } = options;
159
+ if (!expectedText) {
160
+ return candidates;
161
+ }
162
+ // Strategy 1: Playwright text selector
163
+ candidates.push({
164
+ selector: `text="${expectedText}"`,
165
+ strategy: 'text_content',
166
+ confidence: 0.75,
167
+ reason: 'Exact text match'
168
+ });
169
+ // Strategy 2: XPath text selector
170
+ const xpathText = this.escapeXPathString(expectedText);
171
+ candidates.push({
172
+ selector: `xpath=.//*[text()="${xpathText}"]`,
173
+ strategy: 'text_content',
174
+ confidence: 0.72,
175
+ reason: 'XPath text match'
176
+ });
177
+ // Strategy 3: Combine with tag if available
178
+ const tagMatch = originalSelector.match(/^(\w+)/);
179
+ if (tagMatch) {
180
+ const tag = tagMatch[1];
181
+ candidates.push({
182
+ selector: `${tag}:has-text("${expectedText}")`,
183
+ strategy: 'text_content',
184
+ confidence: 0.70,
185
+ reason: `Tag with text content: ${tag}`
186
+ });
187
+ }
188
+ return candidates;
189
+ }
190
+ /**
191
+ * Try fuzzy matching strategies
192
+ */
193
+ tryFuzzyMatchStrategies(options) {
194
+ const candidates = [];
195
+ const { originalSelector } = options;
196
+ // Strategy 1: Remove pseudo-classes
197
+ const withoutPseudo = originalSelector.replace(/::?[a-z-]+(\([^)]*\))?/gi, '');
198
+ if (withoutPseudo !== originalSelector) {
199
+ candidates.push({
200
+ selector: withoutPseudo,
201
+ strategy: 'fuzzy_match',
202
+ confidence: 0.65,
203
+ reason: 'Removed pseudo-classes'
204
+ });
205
+ }
206
+ // Strategy 2: Try nth-child variants
207
+ const nthMatch = originalSelector.match(/:nth-child\((\d+)\)/);
208
+ if (nthMatch) {
209
+ const index = parseInt(nthMatch[1], 10);
210
+ const baseSelector = originalSelector.replace(/:nth-child\(\d+\)/, '');
211
+ // Try nth-of-type
212
+ candidates.push({
213
+ selector: `${baseSelector}:nth-of-type(${index})`,
214
+ strategy: 'fuzzy_match',
215
+ confidence: 0.55,
216
+ reason: 'Changed nth-child to nth-of-type'
217
+ });
218
+ // Try first/last
219
+ if (index === 1) {
220
+ candidates.push({
221
+ selector: `${baseSelector}:first-child`,
222
+ strategy: 'fuzzy_match',
223
+ confidence: 0.60,
224
+ reason: 'Changed nth-child(1) to first-child'
225
+ });
226
+ }
227
+ }
228
+ // Strategy 3: Try removing one selector component at a time
229
+ const parts = originalSelector.split(/\s+/);
230
+ if (parts.length > 1) {
231
+ // Try without the last part
232
+ candidates.push({
233
+ selector: parts.slice(0, -1).join(' '),
234
+ strategy: 'fuzzy_match',
235
+ confidence: 0.45,
236
+ reason: 'Removed last selector component'
237
+ });
238
+ }
239
+ return candidates;
240
+ }
241
+ /**
242
+ * Infer failure reason from error type
243
+ */
244
+ inferFailureReason(errorType) {
245
+ const reasons = {
246
+ timeout: 'Element not found within timeout period',
247
+ not_found: 'No matching elements found in DOM',
248
+ ambiguous: 'Multiple elements matched - selector not specific enough',
249
+ detached: 'Element was detached from DOM',
250
+ invisible: 'Element exists but not visible or interactable',
251
+ stale: 'Element reference became stale'
252
+ };
253
+ return errorType ? reasons[errorType] : 'Unknown selector failure';
254
+ }
255
+ /**
256
+ * Escape string for CSS selectors
257
+ */
258
+ escapeCssString(str) {
259
+ return str.replace(/(["\\])/g, '\\$1');
260
+ }
261
+ /**
262
+ * Escape string for XPath
263
+ */
264
+ escapeXPathString(str) {
265
+ if (str.includes('"')) {
266
+ if (str.includes("'")) {
267
+ // Use concat for strings with both quotes
268
+ return str
269
+ .split(/(")/)
270
+ .map(part => {
271
+ if (part === '"')
272
+ return '\'"\''; // '"'"' concat hack
273
+ if (part === "'")
274
+ return "'";
275
+ return `"${part}"`;
276
+ })
277
+ .join(', ');
278
+ }
279
+ return `'${str}'`;
280
+ }
281
+ return `"${str}"`;
282
+ }
283
+ /**
284
+ * Parse selector to understand its structure
285
+ */
286
+ parseSelector(selector) {
287
+ const result = {
288
+ classes: [],
289
+ attributes: {},
290
+ pseudoClasses: [],
291
+ combinators: []
292
+ };
293
+ // Extract tag
294
+ const tagMatch = selector.match(/^([\w-]+)/);
295
+ if (tagMatch) {
296
+ result.tag = tagMatch[1];
297
+ }
298
+ // Extract ID
299
+ const idMatch = selector.match(/#([\w-]+)/);
300
+ if (idMatch) {
301
+ result.id = idMatch[1];
302
+ }
303
+ // Extract classes
304
+ const classMatches = selector.matchAll(/\.([\w-]+)/g);
305
+ for (const match of classMatches) {
306
+ result.classes.push(match[1]);
307
+ }
308
+ // Extract attributes
309
+ const attrMatches = selector.matchAll(/\[([\w-]+)(=["']([^"']*)["'])?\]/g);
310
+ for (const match of attrMatches) {
311
+ result.attributes[match[1]] = match[3] || '';
312
+ }
313
+ return result;
314
+ }
315
+ /**
316
+ * Generate multiple healing candidates for a selector
317
+ */
318
+ async generateHealingCandidates(selector, context) {
319
+ const allCandidates = [];
320
+ const options = {
321
+ originalSelector: selector,
322
+ expectedText: context?.expectedText,
323
+ errorType: context?.errorType
324
+ };
325
+ // Generate candidates from all strategies
326
+ allCandidates.push(...this.tryDataTestIdStrategies(options));
327
+ allCandidates.push(...this.tryAriaLabelStrategies(options));
328
+ allCandidates.push(...this.tryTextContentStrategies(options));
329
+ allCandidates.push(...this.tryFuzzyMatchStrategies(options));
330
+ // Remove duplicates and sort by confidence
331
+ const unique = this.deduplicateCandidates(allCandidates);
332
+ unique.sort((a, b) => b.confidence - a.confidence);
333
+ return unique;
334
+ }
335
+ /**
336
+ * Remove duplicate candidates
337
+ */
338
+ deduplicateCandidates(candidates) {
339
+ const seen = new Set();
340
+ return candidates.filter(c => {
341
+ if (seen.has(c.selector))
342
+ return false;
343
+ seen.add(c.selector);
344
+ return true;
345
+ });
346
+ }
347
+ }
348
+ /**
349
+ * Create a default self-healing config
350
+ */
351
+ export function createDefaultSelfHealingConfig() {
352
+ return {
353
+ enabled: true,
354
+ maxAttempts: 3,
355
+ confidenceThreshold: 0.7,
356
+ autoApply: false,
357
+ backup: true,
358
+ features: {
359
+ selectorHealing: true,
360
+ assertionUpdates: true,
361
+ apiHealing: true,
362
+ timeoutAdjustment: true,
363
+ aiAssisted: true
364
+ },
365
+ selectorStrategies: {
366
+ useDataTestIds: true,
367
+ useAriaLabels: true,
368
+ useTextSelectors: true,
369
+ fuzzyMatching: true
370
+ }
371
+ };
372
+ }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * QA360 Self-Healing Module
3
+ *
4
+ * Types for self-healing functionality.
5
+ */
6
+ /**
7
+ * Self-healing configuration
8
+ */
9
+ export interface SelfHealingConfig {
10
+ enabled: boolean;
11
+ maxAttempts: number;
12
+ confidenceThreshold: number;
13
+ autoApply: boolean;
14
+ backup: boolean;
15
+ features: {
16
+ selectorHealing: boolean;
17
+ assertionUpdates: boolean;
18
+ apiHealing: boolean;
19
+ timeoutAdjustment: boolean;
20
+ aiAssisted: boolean;
21
+ };
22
+ selectorStrategies: {
23
+ useDataTestIds: boolean;
24
+ useAriaLabels: boolean;
25
+ useTextSelectors: boolean;
26
+ fuzzyMatching: boolean;
27
+ };
28
+ }
29
+ /**
30
+ * Selector error types
31
+ */
32
+ export type SelectorErrorType = 'timeout' | 'not_found' | 'ambiguous' | 'detached' | 'invisible' | 'stale';
33
+ /**
34
+ * Selector healing strategy types
35
+ */
36
+ export type SelectorHealingStrategy = 'data_testid' | 'aria_label' | 'text_content' | 'fuzzy_match' | 'sibling_traversal' | 'css_variant' | 'ai_suggested' | 'no_heal';
37
+ /**
38
+ * Selector healing result
39
+ */
40
+ export interface SelectorHealingResult {
41
+ originalSelector: string;
42
+ healedSelector: string;
43
+ strategy: SelectorHealingStrategy;
44
+ confidence: number;
45
+ elementCount: number;
46
+ failureReason: string;
47
+ }
48
+ /**
49
+ * Assertion types
50
+ */
51
+ export type AssertionType = 'equality' | 'containment' | 'truthiness' | 'count' | 'status_code' | 'response_body' | 'ui_element_exists' | 'ui_text_matches';
52
+ /**
53
+ * Assertion update result
54
+ */
55
+ export interface AssertionUpdateResult {
56
+ testFile: string;
57
+ lineNumber: number;
58
+ originalAssertion: string;
59
+ updatedAssertion: string;
60
+ expected: string;
61
+ actual: string;
62
+ confidence: number;
63
+ assertionType: AssertionType;
64
+ }
65
+ /**
66
+ * API change types
67
+ */
68
+ export type ApiChangeType = 'path_changed' | 'method_changed' | 'status_changed' | 'response_structure' | 'auth_required' | 'deprecated' | 'moved_permanently';
69
+ /**
70
+ * API healing result
71
+ */
72
+ export interface ApiHealingResult {
73
+ originalSpec: string;
74
+ healedSpec: string;
75
+ changeType: ApiChangeType;
76
+ suggestion: string;
77
+ confidence: number;
78
+ }
79
+ /**
80
+ * Self-healing session result
81
+ */
82
+ export interface SelfHealingSession {
83
+ sessionId: string;
84
+ startTime: number;
85
+ endTime: number;
86
+ duration: number;
87
+ config: SelfHealingConfig;
88
+ selectorHeals: SelectorHealingResult[];
89
+ assertionUpdates: AssertionUpdateResult[];
90
+ apiHeals: ApiHealingResult[];
91
+ success: boolean;
92
+ unhealed: number;
93
+ summary: SelfHealingSummary;
94
+ }
95
+ /**
96
+ * Self-healing summary statistics
97
+ */
98
+ export interface SelfHealingSummary {
99
+ totalIssues: number;
100
+ healed: number;
101
+ failed: number;
102
+ highConfidence: number;
103
+ mediumConfidence: number;
104
+ lowConfidence: number;
105
+ byType: {
106
+ selector: number;
107
+ assertion: number;
108
+ api: number;
109
+ timeout: number;
110
+ };
111
+ successRate: number;
112
+ }
113
+ /**
114
+ * Test failure context for healing
115
+ */
116
+ export interface TestFailureContext {
117
+ testType: 'api' | 'ui' | 'a11y' | 'perf' | 'unknown';
118
+ gate: string;
119
+ testSource: string;
120
+ errorMessage: string;
121
+ stackTrace?: string;
122
+ screenshot?: string;
123
+ pageUrl?: string;
124
+ domSnapshot?: {
125
+ title: string;
126
+ url: string;
127
+ html?: string;
128
+ elements: Record<string, number>;
129
+ };
130
+ requestDetails?: {
131
+ url: string;
132
+ method: string;
133
+ status?: number;
134
+ responseTime?: number;
135
+ responseBody?: unknown;
136
+ };
137
+ metadata?: Record<string, unknown>;
138
+ }
139
+ /**
140
+ * AI healing suggestion
141
+ */
142
+ export interface AiHealingSuggestion {
143
+ code: string;
144
+ explanation: string;
145
+ filePath: string;
146
+ lineNumber: number;
147
+ confidence: number;
148
+ effort: 'low' | 'medium' | 'high';
149
+ healingType: 'selector' | 'assertion' | 'api' | 'timeout' | 'other';
150
+ applied?: boolean;
151
+ verified?: boolean;
152
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * QA360 Self-Healing Module
3
+ *
4
+ * Types for self-healing functionality.
5
+ */
6
+ export {};
@@ -0,0 +1,107 @@
1
+ /**
2
+ * SLO/SLI Default Configuration
3
+ *
4
+ * Predefined SLOs and SLIs for common quality metrics.
5
+ * Factory functions for creating standard configurations.
6
+ */
7
+ import type { SLO, SLI, SLOConfig } from './types.js';
8
+ /**
9
+ * Default time windows in milliseconds
10
+ */
11
+ export declare const TimeWindows: {
12
+ HOUR: number;
13
+ DAY: number;
14
+ WEEK: number;
15
+ MONTH: number;
16
+ QUARTER: number;
17
+ };
18
+ /**
19
+ * Create default SLO configuration
20
+ */
21
+ export declare function createDefaultSLOConfig(): SLOConfig;
22
+ /**
23
+ * Create strict SLO configuration (enterprise/production)
24
+ */
25
+ export declare function createStrictSLOConfig(): SLOConfig;
26
+ /**
27
+ * Create relaxed SLO configuration (development)
28
+ */
29
+ export declare function createRelaxedSLOConfig(): SLOConfig;
30
+ /**
31
+ * Default: Test Quality SLO (95% pass rate over 30 days)
32
+ */
33
+ export declare function createDefaultTestQualitySLO(): SLO;
34
+ /**
35
+ * Default: API Success Rate SLO (99.9% over 30 days)
36
+ */
37
+ export declare function createDefaultAPISuccessSLO(): SLO;
38
+ /**
39
+ * Default: Performance SLO (p95 latency < 500ms)
40
+ */
41
+ export declare function createDefaultPerformanceSLO(): SLO;
42
+ /**
43
+ * Strict: Test Quality SLO (98% pass rate)
44
+ */
45
+ export declare function createStrictTestQualitySLO(): SLO;
46
+ /**
47
+ * Strict: API Success Rate SLO (99.95%)
48
+ */
49
+ export declare function createStrictAPISuccessSLO(): SLO;
50
+ /**
51
+ * Strict: Performance SLO (p95 latency < 200ms)
52
+ */
53
+ export declare function createStrictPerformanceSLO(): SLO;
54
+ /**
55
+ * Relaxed: Test Quality SLO (90% pass rate)
56
+ */
57
+ export declare function createRelaxedTestQualitySLO(): SLO;
58
+ /**
59
+ * Relaxed: API Success Rate SLO (99%)
60
+ */
61
+ export declare function createRelaxedAPISuccessSLO(): SLO;
62
+ /**
63
+ * Relaxed: Performance SLO (p95 latency < 1000ms)
64
+ */
65
+ export declare function createRelaxedPerformanceSLO(): SLO;
66
+ /**
67
+ * SLI: Test Pass Rate
68
+ */
69
+ export declare function createTestPassRateSLI(): SLI;
70
+ /**
71
+ * SLI: API Success Rate
72
+ */
73
+ export declare function createAPISuccessRateSLI(): SLI;
74
+ /**
75
+ * SLI: Latency (p95)
76
+ */
77
+ export declare function createLatencySLI(): SLI;
78
+ /**
79
+ * Create custom SLO from parameters
80
+ */
81
+ export declare function createCustomSLO(params: {
82
+ name: string;
83
+ description?: string;
84
+ target: number;
85
+ windowMs?: number;
86
+ sliIds: string[];
87
+ tags?: string[];
88
+ owner?: string;
89
+ }): SLO;
90
+ /**
91
+ * Create custom SLI from parameters
92
+ */
93
+ export declare function createCustomSLI(params: {
94
+ name: string;
95
+ description?: string;
96
+ type: SLI['type'];
97
+ source: SLI['source'];
98
+ queryString: string;
99
+ aggregation: SLI['aggregation'];
100
+ unit: SLI['unit'];
101
+ threshold: {
102
+ operator: SLI['threshold']['operator'];
103
+ value: number;
104
+ };
105
+ category?: SLI['category'];
106
+ tags?: string[];
107
+ }): SLI;