agentic-qe 3.7.6 → 3.7.7

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 (63) hide show
  1. package/.claude/skills/skills-manifest.json +1 -1
  2. package/CHANGELOG.md +20 -0
  3. package/dist/cli/bundle.js +1023 -40
  4. package/dist/domains/test-execution/services/e2e/adaptive-locator-service.d.ts +71 -0
  5. package/dist/domains/test-execution/services/e2e/adaptive-locator-service.d.ts.map +1 -0
  6. package/dist/domains/test-execution/services/e2e/adaptive-locator-service.js +456 -0
  7. package/dist/domains/test-execution/services/e2e/adaptive-locator-service.js.map +1 -0
  8. package/dist/domains/test-execution/services/e2e/adaptive-locator-types.d.ts +81 -0
  9. package/dist/domains/test-execution/services/e2e/adaptive-locator-types.d.ts.map +1 -0
  10. package/dist/domains/test-execution/services/e2e/adaptive-locator-types.js +20 -0
  11. package/dist/domains/test-execution/services/e2e/adaptive-locator-types.js.map +1 -0
  12. package/dist/domains/test-execution/services/e2e/browser-orchestrator.d.ts +19 -0
  13. package/dist/domains/test-execution/services/e2e/browser-orchestrator.d.ts.map +1 -1
  14. package/dist/domains/test-execution/services/e2e/browser-orchestrator.js +82 -0
  15. package/dist/domains/test-execution/services/e2e/browser-orchestrator.js.map +1 -1
  16. package/dist/domains/test-execution/services/e2e/index.d.ts +2 -0
  17. package/dist/domains/test-execution/services/e2e/index.d.ts.map +1 -1
  18. package/dist/domains/test-execution/services/e2e/index.js +5 -0
  19. package/dist/domains/test-execution/services/e2e/index.js.map +1 -1
  20. package/dist/domains/test-execution/services/e2e/step-executors.d.ts +6 -0
  21. package/dist/domains/test-execution/services/e2e/step-executors.d.ts.map +1 -1
  22. package/dist/domains/test-execution/services/e2e/step-executors.js +17 -2
  23. package/dist/domains/test-execution/services/e2e/step-executors.js.map +1 -1
  24. package/dist/domains/test-execution/services/e2e/types.d.ts +18 -1
  25. package/dist/domains/test-execution/services/e2e/types.d.ts.map +1 -1
  26. package/dist/domains/test-execution/services/e2e/types.js.map +1 -1
  27. package/dist/integrations/browser/client-factory.d.ts +6 -1
  28. package/dist/integrations/browser/client-factory.d.ts.map +1 -1
  29. package/dist/integrations/browser/client-factory.js +37 -2
  30. package/dist/integrations/browser/client-factory.js.map +1 -1
  31. package/dist/integrations/browser/index.d.ts +5 -1
  32. package/dist/integrations/browser/index.d.ts.map +1 -1
  33. package/dist/integrations/browser/index.js +8 -1
  34. package/dist/integrations/browser/index.js.map +1 -1
  35. package/dist/integrations/browser/page-pool-types.d.ts +70 -0
  36. package/dist/integrations/browser/page-pool-types.d.ts.map +1 -0
  37. package/dist/integrations/browser/page-pool-types.js +19 -0
  38. package/dist/integrations/browser/page-pool-types.js.map +1 -0
  39. package/dist/integrations/browser/page-pool.d.ts +79 -0
  40. package/dist/integrations/browser/page-pool.d.ts.map +1 -0
  41. package/dist/integrations/browser/page-pool.js +288 -0
  42. package/dist/integrations/browser/page-pool.js.map +1 -0
  43. package/dist/integrations/browser/resource-blocking.d.ts +47 -0
  44. package/dist/integrations/browser/resource-blocking.d.ts.map +1 -0
  45. package/dist/integrations/browser/resource-blocking.js +195 -0
  46. package/dist/integrations/browser/resource-blocking.js.map +1 -0
  47. package/dist/integrations/browser/stealth/index.d.ts +8 -0
  48. package/dist/integrations/browser/stealth/index.d.ts.map +1 -0
  49. package/dist/integrations/browser/stealth/index.js +7 -0
  50. package/dist/integrations/browser/stealth/index.js.map +1 -0
  51. package/dist/integrations/browser/stealth/stealth-client.d.ts +51 -0
  52. package/dist/integrations/browser/stealth/stealth-client.d.ts.map +1 -0
  53. package/dist/integrations/browser/stealth/stealth-client.js +359 -0
  54. package/dist/integrations/browser/stealth/stealth-client.js.map +1 -0
  55. package/dist/integrations/browser/stealth/stealth-types.d.ts +35 -0
  56. package/dist/integrations/browser/stealth/stealth-types.d.ts.map +1 -0
  57. package/dist/integrations/browser/stealth/stealth-types.js +17 -0
  58. package/dist/integrations/browser/stealth/stealth-types.js.map +1 -0
  59. package/dist/integrations/browser/types.d.ts +13 -10
  60. package/dist/integrations/browser/types.d.ts.map +1 -1
  61. package/dist/integrations/browser/types.js.map +1 -1
  62. package/dist/mcp/bundle.js +1114 -70
  63. package/package.json +1 -1
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Adaptive Locator Service
3
+ *
4
+ * Inspired by Scrapling's similarity-based element matching.
5
+ * When a primary selector fails, falls back through text, ARIA, and
6
+ * fingerprint similarity matching to find the intended element.
7
+ *
8
+ * @module test-execution/services/e2e/adaptive-locator-service
9
+ */
10
+ import type { IBrowserClient } from '@integrations/browser';
11
+ import type { ElementFingerprint, AdaptiveLocatorConfig, LocatorResolutionResult } from './adaptive-locator-types';
12
+ /**
13
+ * Compute weighted similarity between a stored fingerprint and a candidate element
14
+ */
15
+ export declare function computeSimilarity(stored: ElementFingerprint, candidate: Omit<ElementFingerprint, 'matchCount' | 'lastMatchedAt'>): number;
16
+ /**
17
+ * Simple in-memory fingerprint store keyed by `pageUrl::selector`
18
+ */
19
+ export declare class FingerprintStore {
20
+ private readonly store;
21
+ private readonly maxPerPage;
22
+ constructor(maxPerPage?: number);
23
+ private key;
24
+ get(pageUrl: string, selector: string): ElementFingerprint | undefined;
25
+ set(pageUrl: string, selector: string, fp: ElementFingerprint): void;
26
+ size(): number;
27
+ clear(): void;
28
+ }
29
+ /**
30
+ * Adaptive Locator Service
31
+ *
32
+ * Provides fallback element resolution when primary selectors fail.
33
+ * Maintains a fingerprint database of previously-matched elements
34
+ * and uses weighted similarity to find the best match.
35
+ */
36
+ export declare class AdaptiveLocatorService {
37
+ private readonly config;
38
+ private readonly fingerprintStore;
39
+ constructor(config?: Partial<AdaptiveLocatorConfig>);
40
+ /**
41
+ * Capture and store a fingerprint for a successfully-interacted element
42
+ */
43
+ captureFingerprint(selector: string, client: IBrowserClient, pageUrl: string): Promise<ElementFingerprint | null>;
44
+ /**
45
+ * Resolve an element target with adaptive fallback chain.
46
+ *
47
+ * 1. Try primary selector
48
+ * 2. Text-based match
49
+ * 3. ARIA-based match
50
+ * 4. Fingerprint similarity matching
51
+ */
52
+ resolveWithFallback(selector: string, client: IBrowserClient, pageUrl: string): Promise<LocatorResolutionResult | null>;
53
+ /**
54
+ * Get the fingerprint store size (for diagnostics)
55
+ */
56
+ getStoreSize(): number;
57
+ /**
58
+ * Clear all stored fingerprints
59
+ */
60
+ clearFingerprints(): void;
61
+ private tryFallbackMethod;
62
+ private tryTextMatch;
63
+ private tryAriaMatch;
64
+ private tryFingerprintMatch;
65
+ /**
66
+ * Convert a selector string to a CSS selector suitable for capture.
67
+ * Returns null for non-CSS selectors that can't be used with querySelector.
68
+ */
69
+ private toCssForCapture;
70
+ }
71
+ //# sourceMappingURL=adaptive-locator-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adaptive-locator-service.d.ts","sourceRoot":"","sources":["../../../../../src/domains/test-execution/services/e2e/adaptive-locator-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EACV,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EAExB,MAAM,0BAA0B,CAAC;AA4JlC;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,kBAAkB,EAC1B,SAAS,EAAE,IAAI,CAAC,kBAAkB,EAAE,YAAY,GAAG,eAAe,CAAC,GAClE,MAAM,CAuCR;AA8BD;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyC;IAC/D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAExB,UAAU,SAAM;IAI5B,OAAO,CAAC,GAAG;IAIX,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAItE,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,kBAAkB,GAAG,IAAI;IAepE,IAAI,IAAI,MAAM;IAId,KAAK,IAAI,IAAI;CAGd;AAMD;;;;;;GAMG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAC/C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;gBAExC,MAAM,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC;IAKnD;;OAEG;IACG,kBAAkB,CACtB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAwBrC;;;;;;;OAOG;IACG,mBAAmB,CACvB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC;IAc1C;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;OAEG;IACH,iBAAiB,IAAI,IAAI;YAQX,iBAAiB;YAiBjB,YAAY;YAwCZ,YAAY;YAkCZ,mBAAmB;IAoCjC;;;OAGG;IACH,OAAO,CAAC,eAAe;CAMxB"}
@@ -0,0 +1,456 @@
1
+ /**
2
+ * Adaptive Locator Service
3
+ *
4
+ * Inspired by Scrapling's similarity-based element matching.
5
+ * When a primary selector fails, falls back through text, ARIA, and
6
+ * fingerprint similarity matching to find the intended element.
7
+ *
8
+ * @module test-execution/services/e2e/adaptive-locator-service
9
+ */
10
+ import { DEFAULT_ADAPTIVE_LOCATOR_CONFIG } from './adaptive-locator-types';
11
+ // ============================================================================
12
+ // Fingerprint Capture Script (runs in browser context)
13
+ // ============================================================================
14
+ /**
15
+ * JavaScript to evaluate in the browser to capture an element fingerprint.
16
+ * Returns a serialisable object matching ElementFingerprint.
17
+ */
18
+ function buildCaptureScript(cssSelector) {
19
+ return `
20
+ (() => {
21
+ const el = document.querySelector(${JSON.stringify(cssSelector)});
22
+ if (!el) return null;
23
+ const parent = el.parentElement;
24
+ const siblings = parent ? parent.children : [];
25
+ let childIndex = 0;
26
+ for (let i = 0; i < siblings.length; i++) {
27
+ if (siblings[i] === el) { childIndex = i; break; }
28
+ }
29
+ return {
30
+ tagName: el.tagName.toLowerCase(),
31
+ classes: Array.from(el.classList),
32
+ ariaRole: el.getAttribute('role') || undefined,
33
+ ariaLabel: el.getAttribute('aria-label') || undefined,
34
+ textContent: (el.textContent || '').trim().slice(0, 200) || undefined,
35
+ attributes: {
36
+ id: el.id || undefined,
37
+ name: el.getAttribute('name') || undefined,
38
+ 'data-testid': el.getAttribute('data-testid') || undefined,
39
+ type: el.getAttribute('type') || undefined,
40
+ placeholder: el.getAttribute('placeholder') || undefined,
41
+ href: el.getAttribute('href') || undefined,
42
+ },
43
+ positionHints: {
44
+ parentTag: parent ? parent.tagName.toLowerCase() : undefined,
45
+ childIndex,
46
+ siblingCount: siblings.length,
47
+ },
48
+ matchCount: 0,
49
+ lastMatchedAt: new Date().toISOString(),
50
+ };
51
+ })()
52
+ `;
53
+ }
54
+ /**
55
+ * JavaScript to find candidate elements on the page and return their fingerprints.
56
+ */
57
+ function buildCandidatesScript() {
58
+ return `
59
+ (() => {
60
+ const interactiveTags = 'a,button,input,select,textarea,[role],[data-testid],[aria-label]';
61
+ const elements = document.querySelectorAll(interactiveTags);
62
+ const results = [];
63
+ for (let i = 0; i < Math.min(elements.length, 300); i++) {
64
+ const el = elements[i];
65
+ const parent = el.parentElement;
66
+ const siblings = parent ? parent.children : [];
67
+ let childIndex = 0;
68
+ for (let j = 0; j < siblings.length; j++) {
69
+ if (siblings[j] === el) { childIndex = j; break; }
70
+ }
71
+ results.push({
72
+ tagName: el.tagName.toLowerCase(),
73
+ classes: Array.from(el.classList),
74
+ ariaRole: el.getAttribute('role') || undefined,
75
+ ariaLabel: el.getAttribute('aria-label') || undefined,
76
+ textContent: (el.textContent || '').trim().slice(0, 200) || undefined,
77
+ attributes: {
78
+ id: el.id || undefined,
79
+ name: el.getAttribute('name') || undefined,
80
+ 'data-testid': el.getAttribute('data-testid') || undefined,
81
+ type: el.getAttribute('type') || undefined,
82
+ placeholder: el.getAttribute('placeholder') || undefined,
83
+ href: el.getAttribute('href') || undefined,
84
+ },
85
+ positionHints: {
86
+ parentTag: parent ? parent.tagName.toLowerCase() : undefined,
87
+ childIndex,
88
+ siblingCount: siblings.length,
89
+ },
90
+ // Build a unique-enough CSS selector for this element
91
+ selector: buildSelector(el),
92
+ });
93
+ }
94
+
95
+ function buildSelector(el) {
96
+ if (el.id) return '#' + CSS.escape(el.id);
97
+ if (el.getAttribute('data-testid')) return '[data-testid="' + el.getAttribute('data-testid') + '"]';
98
+ if (el.getAttribute('name')) return el.tagName.toLowerCase() + '[name="' + el.getAttribute('name') + '"]';
99
+ // Fallback: tag + nth-child
100
+ const parent = el.parentElement;
101
+ if (!parent) return el.tagName.toLowerCase();
102
+ const siblings = Array.from(parent.children).filter(s => s.tagName === el.tagName);
103
+ const idx = siblings.indexOf(el) + 1;
104
+ return el.tagName.toLowerCase() + ':nth-of-type(' + idx + ')';
105
+ }
106
+
107
+ return results;
108
+ })()
109
+ `;
110
+ }
111
+ // ============================================================================
112
+ // Similarity Computation
113
+ // ============================================================================
114
+ /** Weights for similarity scoring */
115
+ const WEIGHTS = {
116
+ tagName: 0.20,
117
+ ariaRole: 0.15,
118
+ classes: 0.15,
119
+ textContent: 0.20,
120
+ attributes: 0.15,
121
+ positionHints: 0.15,
122
+ };
123
+ /**
124
+ * Compute Jaccard similarity between two string arrays
125
+ */
126
+ function jaccardSimilarity(a, b) {
127
+ if (a.length === 0 && b.length === 0)
128
+ return 1;
129
+ const setA = new Set(a);
130
+ const setB = new Set(b);
131
+ let intersection = 0;
132
+ for (const item of setA) {
133
+ if (setB.has(item))
134
+ intersection++;
135
+ }
136
+ const union = new Set([...setA, ...setB]).size;
137
+ return union === 0 ? 0 : intersection / union;
138
+ }
139
+ /**
140
+ * Normalised string similarity (simple containment-based)
141
+ */
142
+ function textSimilarity(a, b) {
143
+ if (!a && !b)
144
+ return 1;
145
+ if (!a || !b)
146
+ return 0;
147
+ const la = a.toLowerCase();
148
+ const lb = b.toLowerCase();
149
+ if (la === lb)
150
+ return 1;
151
+ if (la.includes(lb) || lb.includes(la))
152
+ return 0.8;
153
+ // Character overlap ratio
154
+ const setA = new Set(la);
155
+ const setB = new Set(lb);
156
+ let common = 0;
157
+ for (const c of setA) {
158
+ if (setB.has(c))
159
+ common++;
160
+ }
161
+ const total = new Set([...setA, ...setB]).size;
162
+ return total === 0 ? 0 : common / total * 0.6;
163
+ }
164
+ /**
165
+ * Compute weighted similarity between a stored fingerprint and a candidate element
166
+ */
167
+ export function computeSimilarity(stored, candidate) {
168
+ let score = 0;
169
+ // Tag name (exact match)
170
+ score += (stored.tagName === candidate.tagName ? 1 : 0) * WEIGHTS.tagName;
171
+ // ARIA role
172
+ if (stored.ariaRole || candidate.ariaRole) {
173
+ score += (stored.ariaRole === candidate.ariaRole ? 1 : 0) * WEIGHTS.ariaRole;
174
+ }
175
+ else {
176
+ score += WEIGHTS.ariaRole; // both undefined = match
177
+ }
178
+ // Classes (Jaccard)
179
+ score += jaccardSimilarity(stored.classes, candidate.classes) * WEIGHTS.classes;
180
+ // Text content
181
+ score += textSimilarity(stored.textContent, candidate.textContent) * WEIGHTS.textContent;
182
+ // Attributes overlap
183
+ const storedAttrs = Object.entries(stored.attributes).filter(([, v]) => v);
184
+ const candidateAttrs = Object.entries(candidate.attributes).filter(([, v]) => v);
185
+ if (storedAttrs.length === 0 && candidateAttrs.length === 0) {
186
+ score += WEIGHTS.attributes;
187
+ }
188
+ else {
189
+ let matches = 0;
190
+ for (const [key, val] of storedAttrs) {
191
+ const candidateVal = candidate.attributes[key];
192
+ if (candidateVal === val)
193
+ matches++;
194
+ }
195
+ const total = Math.max(storedAttrs.length, candidateAttrs.length, 1);
196
+ score += (matches / total) * WEIGHTS.attributes;
197
+ }
198
+ // Position hints
199
+ const posScore = computePositionScore(stored.positionHints, candidate.positionHints);
200
+ score += posScore * WEIGHTS.positionHints;
201
+ return Math.min(1, Math.max(0, score));
202
+ }
203
+ function computePositionScore(a, b) {
204
+ let total = 0;
205
+ let matches = 0;
206
+ // Parent tag
207
+ total++;
208
+ if (a.parentTag === b.parentTag)
209
+ matches++;
210
+ // Child index proximity
211
+ total++;
212
+ const indexDiff = Math.abs(a.childIndex - b.childIndex);
213
+ matches += Math.max(0, 1 - indexDiff / 10);
214
+ // Sibling count proximity
215
+ total++;
216
+ const sibDiff = Math.abs(a.siblingCount - b.siblingCount);
217
+ matches += Math.max(0, 1 - sibDiff / 10);
218
+ return matches / total;
219
+ }
220
+ // ============================================================================
221
+ // In-Memory Fingerprint Store (lightweight, no DB dependency)
222
+ // ============================================================================
223
+ /**
224
+ * Simple in-memory fingerprint store keyed by `pageUrl::selector`
225
+ */
226
+ export class FingerprintStore {
227
+ store = new Map();
228
+ maxPerPage;
229
+ constructor(maxPerPage = 200) {
230
+ this.maxPerPage = maxPerPage;
231
+ }
232
+ key(pageUrl, selector) {
233
+ return `${pageUrl}::${selector}`;
234
+ }
235
+ get(pageUrl, selector) {
236
+ return this.store.get(this.key(pageUrl, selector));
237
+ }
238
+ set(pageUrl, selector, fp) {
239
+ const k = this.key(pageUrl, selector);
240
+ this.store.set(k, fp);
241
+ // Enforce per-page limit by evicting oldest entries
242
+ const prefix = `${pageUrl}::`;
243
+ const pageKeys = [...this.store.keys()].filter((key) => key.startsWith(prefix));
244
+ if (pageKeys.length > this.maxPerPage) {
245
+ const toRemove = pageKeys.length - this.maxPerPage;
246
+ for (let i = 0; i < toRemove; i++) {
247
+ this.store.delete(pageKeys[i]);
248
+ }
249
+ }
250
+ }
251
+ size() {
252
+ return this.store.size;
253
+ }
254
+ clear() {
255
+ this.store.clear();
256
+ }
257
+ }
258
+ // ============================================================================
259
+ // Adaptive Locator Service
260
+ // ============================================================================
261
+ /**
262
+ * Adaptive Locator Service
263
+ *
264
+ * Provides fallback element resolution when primary selectors fail.
265
+ * Maintains a fingerprint database of previously-matched elements
266
+ * and uses weighted similarity to find the best match.
267
+ */
268
+ export class AdaptiveLocatorService {
269
+ config;
270
+ fingerprintStore;
271
+ constructor(config) {
272
+ this.config = { ...DEFAULT_ADAPTIVE_LOCATOR_CONFIG, ...config };
273
+ this.fingerprintStore = new FingerprintStore(this.config.maxFingerprintsPerPage);
274
+ }
275
+ /**
276
+ * Capture and store a fingerprint for a successfully-interacted element
277
+ */
278
+ async captureFingerprint(selector, client, pageUrl) {
279
+ if (!this.config.enabled)
280
+ return null;
281
+ try {
282
+ const cssSelector = this.toCssForCapture(selector);
283
+ if (!cssSelector)
284
+ return null;
285
+ const result = await client.evaluate(buildCaptureScript(cssSelector));
286
+ if (!result.success || !result.value)
287
+ return null;
288
+ const fingerprint = result.value;
289
+ fingerprint.matchCount = (this.fingerprintStore.get(pageUrl, selector)?.matchCount ?? 0) + 1;
290
+ fingerprint.lastMatchedAt = new Date().toISOString();
291
+ this.fingerprintStore.set(pageUrl, selector, fingerprint);
292
+ return fingerprint;
293
+ }
294
+ catch {
295
+ return null;
296
+ }
297
+ }
298
+ /**
299
+ * Resolve an element target with adaptive fallback chain.
300
+ *
301
+ * 1. Try primary selector
302
+ * 2. Text-based match
303
+ * 3. ARIA-based match
304
+ * 4. Fingerprint similarity matching
305
+ */
306
+ async resolveWithFallback(selector, client, pageUrl) {
307
+ if (!this.config.enabled)
308
+ return null;
309
+ const storedFingerprint = this.fingerprintStore.get(pageUrl, selector);
310
+ if (!storedFingerprint)
311
+ return null;
312
+ for (const method of this.config.fallbackChain) {
313
+ const result = await this.tryFallbackMethod(method, storedFingerprint, client);
314
+ if (result)
315
+ return result;
316
+ }
317
+ return null;
318
+ }
319
+ /**
320
+ * Get the fingerprint store size (for diagnostics)
321
+ */
322
+ getStoreSize() {
323
+ return this.fingerprintStore.size();
324
+ }
325
+ /**
326
+ * Clear all stored fingerprints
327
+ */
328
+ clearFingerprints() {
329
+ this.fingerprintStore.clear();
330
+ }
331
+ // ==========================================================================
332
+ // Private Helpers
333
+ // ==========================================================================
334
+ async tryFallbackMethod(method, stored, client) {
335
+ switch (method) {
336
+ case 'text':
337
+ return this.tryTextMatch(stored, client);
338
+ case 'aria':
339
+ return this.tryAriaMatch(stored, client);
340
+ case 'fingerprint':
341
+ return this.tryFingerprintMatch(stored, client);
342
+ default:
343
+ return null;
344
+ }
345
+ }
346
+ async tryTextMatch(stored, client) {
347
+ if (!stored.textContent)
348
+ return null;
349
+ const safeText = JSON.stringify(stored.textContent);
350
+ const script = `
351
+ (() => {
352
+ const target = ${safeText};
353
+ const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT);
354
+ while (walker.nextNode()) {
355
+ const el = walker.currentNode;
356
+ const text = (el.textContent || '').trim().slice(0, 200);
357
+ if (text === target) {
358
+ if (el.id) return '#' + CSS.escape(el.id);
359
+ if (el.getAttribute('data-testid')) return '[data-testid="' + el.getAttribute('data-testid') + '"]';
360
+ return null;
361
+ }
362
+ }
363
+ return null;
364
+ })()
365
+ `;
366
+ try {
367
+ const result = await client.evaluate(script);
368
+ if (result.success && result.value) {
369
+ return {
370
+ resolvedSelector: result.value,
371
+ method: 'text',
372
+ similarityScore: 0.9,
373
+ fingerprintUpdated: false,
374
+ };
375
+ }
376
+ }
377
+ catch {
378
+ // continue to next method
379
+ }
380
+ return null;
381
+ }
382
+ async tryAriaMatch(stored, client) {
383
+ if (!stored.ariaRole && !stored.ariaLabel)
384
+ return null;
385
+ let ariaSelector = '';
386
+ if (stored.ariaRole && stored.ariaLabel) {
387
+ ariaSelector = `[role="${stored.ariaRole}"][aria-label="${stored.ariaLabel}"]`;
388
+ }
389
+ else if (stored.ariaLabel) {
390
+ ariaSelector = `[aria-label="${stored.ariaLabel}"]`;
391
+ }
392
+ else if (stored.ariaRole) {
393
+ ariaSelector = `[role="${stored.ariaRole}"]`;
394
+ }
395
+ if (!ariaSelector)
396
+ return null;
397
+ try {
398
+ const checkScript = `!!document.querySelector(${JSON.stringify(ariaSelector)})`;
399
+ const result = await client.evaluate(checkScript);
400
+ if (result.success && result.value) {
401
+ return {
402
+ resolvedSelector: ariaSelector,
403
+ method: 'aria',
404
+ similarityScore: 0.85,
405
+ fingerprintUpdated: false,
406
+ };
407
+ }
408
+ }
409
+ catch {
410
+ // continue to next method
411
+ }
412
+ return null;
413
+ }
414
+ async tryFingerprintMatch(stored, client) {
415
+ try {
416
+ const result = await client.evaluate(buildCandidatesScript());
417
+ if (!result.success || !result.value?.length)
418
+ return null;
419
+ let bestScore = 0;
420
+ let bestSelector = '';
421
+ for (const candidate of result.value) {
422
+ const score = computeSimilarity(stored, candidate);
423
+ if (score > bestScore) {
424
+ bestScore = score;
425
+ bestSelector = candidate.selector;
426
+ }
427
+ }
428
+ if (bestScore >= this.config.similarityThreshold && bestSelector) {
429
+ return {
430
+ resolvedSelector: bestSelector,
431
+ method: 'fingerprint',
432
+ similarityScore: bestScore,
433
+ fingerprintUpdated: false,
434
+ };
435
+ }
436
+ }
437
+ catch {
438
+ // fingerprint match failed
439
+ }
440
+ return null;
441
+ }
442
+ /**
443
+ * Convert a selector string to a CSS selector suitable for capture.
444
+ * Returns null for non-CSS selectors that can't be used with querySelector.
445
+ */
446
+ toCssForCapture(selector) {
447
+ if (selector.startsWith('//') || selector.startsWith('xpath='))
448
+ return null;
449
+ if (/^@?e\d+$/.test(selector))
450
+ return null;
451
+ if (selector.startsWith('text='))
452
+ return null;
453
+ return selector;
454
+ }
455
+ }
456
+ //# sourceMappingURL=adaptive-locator-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adaptive-locator-service.js","sourceRoot":"","sources":["../../../../../src/domains/test-execution/services/e2e/adaptive-locator-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Adaptive Locator Types
3
+ *
4
+ * Inspired by Scrapling's similarity-based element matching.
5
+ * When a CSS/XPath selector fails, falls back to fingerprint-based
6
+ * element matching to reduce E2E flakiness from UI changes.
7
+ *
8
+ * @module test-execution/services/e2e/adaptive-locator-types
9
+ */
10
+ /**
11
+ * Captured fingerprint of a DOM element for similarity matching.
12
+ * Stored after successful interactions to enable fallback matching.
13
+ */
14
+ export interface ElementFingerprint {
15
+ /** HTML tag name (e.g., 'button', 'input', 'a') */
16
+ tagName: string;
17
+ /** CSS classes on the element */
18
+ classes: string[];
19
+ /** WAI-ARIA role */
20
+ ariaRole?: string;
21
+ /** WAI-ARIA label */
22
+ ariaLabel?: string;
23
+ /** Visible text content (trimmed, max 200 chars) */
24
+ textContent?: string;
25
+ /** Key attributes for matching */
26
+ attributes: {
27
+ id?: string;
28
+ name?: string;
29
+ 'data-testid'?: string;
30
+ type?: string;
31
+ placeholder?: string;
32
+ href?: string;
33
+ };
34
+ /** Structural position hints */
35
+ positionHints: {
36
+ parentTag?: string;
37
+ childIndex: number;
38
+ siblingCount: number;
39
+ };
40
+ /** Number of times this fingerprint matched */
41
+ matchCount: number;
42
+ /** Last time this fingerprint was matched (ISO string) */
43
+ lastMatchedAt: string;
44
+ }
45
+ /**
46
+ * Configuration for the adaptive locator service
47
+ */
48
+ export interface AdaptiveLocatorConfig {
49
+ /** Enable adaptive locator fallback */
50
+ enabled: boolean;
51
+ /** Minimum similarity score (0-1) to accept a fingerprint match */
52
+ similarityThreshold: number;
53
+ /** Maximum fingerprints to store per page */
54
+ maxFingerprintsPerPage: number;
55
+ /** Memory namespace for fingerprint storage */
56
+ namespace: string;
57
+ /** Fallback chain order */
58
+ fallbackChain: Array<'text' | 'aria' | 'fingerprint'>;
59
+ }
60
+ /**
61
+ * Default adaptive locator configuration
62
+ */
63
+ export declare const DEFAULT_ADAPTIVE_LOCATOR_CONFIG: AdaptiveLocatorConfig;
64
+ /**
65
+ * Method used to resolve an element target
66
+ */
67
+ export type LocatorResolutionMethod = 'primary' | 'text' | 'aria' | 'fingerprint';
68
+ /**
69
+ * Result of adaptive locator resolution
70
+ */
71
+ export interface LocatorResolutionResult {
72
+ /** The resolved element target selector */
73
+ resolvedSelector: string;
74
+ /** How the element was found */
75
+ method: LocatorResolutionMethod;
76
+ /** Similarity score (1.0 for primary, 0-1 for fallbacks) */
77
+ similarityScore: number;
78
+ /** Whether the fingerprint database was updated */
79
+ fingerprintUpdated: boolean;
80
+ }
81
+ //# sourceMappingURL=adaptive-locator-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adaptive-locator-types.d.ts","sourceRoot":"","sources":["../../../../../src/domains/test-execution/services/e2e/adaptive-locator-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,oBAAoB;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,UAAU,EAAE;QACV,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,gCAAgC;IAChC,aAAa,EAAE;QACb,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,aAAa,EAAE,MAAM,CAAC;CACvB;AAMD;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,mBAAmB,EAAE,MAAM,CAAC;IAC5B,6CAA6C;IAC7C,sBAAsB,EAAE,MAAM,CAAC;IAC/B,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,aAAa,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,GAAG,aAAa,CAAC,CAAC;CACvD;AAED;;GAEG;AACH,eAAO,MAAM,+BAA+B,EAAE,qBAM7C,CAAC;AAMF;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,aAAa,CAAC;AAElF;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,2CAA2C;IAC3C,gBAAgB,EAAE,MAAM,CAAC;IACzB,gCAAgC;IAChC,MAAM,EAAE,uBAAuB,CAAC;IAChC,4DAA4D;IAC5D,eAAe,EAAE,MAAM,CAAC;IACxB,mDAAmD;IACnD,kBAAkB,EAAE,OAAO,CAAC;CAC7B"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Adaptive Locator Types
3
+ *
4
+ * Inspired by Scrapling's similarity-based element matching.
5
+ * When a CSS/XPath selector fails, falls back to fingerprint-based
6
+ * element matching to reduce E2E flakiness from UI changes.
7
+ *
8
+ * @module test-execution/services/e2e/adaptive-locator-types
9
+ */
10
+ /**
11
+ * Default adaptive locator configuration
12
+ */
13
+ export const DEFAULT_ADAPTIVE_LOCATOR_CONFIG = {
14
+ enabled: true,
15
+ similarityThreshold: 0.6,
16
+ maxFingerprintsPerPage: 200,
17
+ namespace: 'aqe/adaptive-locators',
18
+ fallbackChain: ['text', 'aria', 'fingerprint'],
19
+ };
20
+ //# sourceMappingURL=adaptive-locator-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adaptive-locator-types.js","sourceRoot":"","sources":["../../../../../src/domains/test-execution/services/e2e/adaptive-locator-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA8DH;;GAEG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAA0B;IACpE,OAAO,EAAE,IAAI;IACb,mBAAmB,EAAE,GAAG;IACxB,sBAAsB,EAAE,GAAG;IAC3B,SAAS,EAAE,uBAAuB;IAClC,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC;CAC/C,CAAC"}