synapse-hub 0.0.0-stage → 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 (122) hide show
  1. package/.agents/agents/failure-analyst.md +47 -0
  2. package/.agents/agents/live-explorer.md +41 -0
  3. package/.agents/agents/orchestrator.md +43 -0
  4. package/.agents/agents/pom-crafter.md +81 -0
  5. package/.agents/agents/quality-gatekeeper.md +47 -0
  6. package/.agents/agents/test-runner.md +43 -0
  7. package/.agents/rules/context-engineering.md +88 -0
  8. package/.agents/rules/git-push-protocol.md +101 -0
  9. package/.agents/rules/graphify.md +14 -0
  10. package/.agents/rules/lessons-learned.md +23 -0
  11. package/.agents/rules/mandatory-playwright-live.md +29 -0
  12. package/.agents/rules/skill-precedence.md +42 -0
  13. package/.agents/rules/systematic-debugging.md +99 -0
  14. package/.agents/skills/agent-orchestration-multi-agent-optimize/SKILL.md +239 -0
  15. package/.agents/skills/antigravity-test-sentinel/SKILL.md +64 -0
  16. package/.agents/skills/api-network-mocking-interception/SKILL.md +142 -0
  17. package/.agents/skills/api-spec-to-e2e-matrix/SKILL.md +70 -0
  18. package/.agents/skills/architecture-decision-records/SKILL.md +441 -0
  19. package/.agents/skills/auth-session-and-cookie-manager/SKILL.md +72 -0
  20. package/.agents/skills/backend-load-and-stress-patterns/SKILL.md +214 -0
  21. package/.agents/skills/bats-testing-patterns/SKILL.md +100 -0
  22. package/.agents/skills/bats-testing-patterns/resources/implementation-playbook.md +614 -0
  23. package/.agents/skills/clean-pom-crafter/SKILL.md +75 -0
  24. package/.agents/skills/clean-pom-crafter/examples/standard_test_case.groovy +28 -0
  25. package/.agents/skills/clean-pom-crafter/references/pom_patterns.groovy +138 -0
  26. package/.agents/skills/code-refactoring-refactor-clean/SKILL.md +64 -0
  27. package/.agents/skills/code-refactoring-refactor-clean/resources/implementation-playbook.md +879 -0
  28. package/.agents/skills/db-state-verifier-and-seeder/SKILL.md +123 -0
  29. package/.agents/skills/e2e-testing-patterns/SKILL.md +103 -0
  30. package/.agents/skills/e2e-testing-patterns/resources/implementation-playbook.md +522 -0
  31. package/.agents/skills/failure-autopsy-and-healing/SKILL.md +54 -0
  32. package/.agents/skills/failure-autopsy-and-healing/references/healing_matrix.md +64 -0
  33. package/.agents/skills/failure-autopsy-and-healing/references/qa_investigation_protocol.md +56 -0
  34. package/.agents/skills/failure-autopsy-and-healing/scripts/triage_failures.sh +98 -0
  35. package/.agents/skills/file-and-report-validator/SKILL.md +90 -0
  36. package/.agents/skills/flaky-test-sentinel-and-quarantine/SKILL.md +114 -0
  37. package/.agents/skills/github-actions-templates/SKILL.md +334 -0
  38. package/.agents/skills/graphify-impact-analyzer/SKILL.md +78 -0
  39. package/.agents/skills/katalon-cli-runner/SKILL.md +76 -0
  40. package/.agents/skills/katalon-cli-runner/references/groovy-patterns.md +209 -0
  41. package/.agents/skills/katalon-cli-runner/references/katalon-api.md +155 -0
  42. package/.agents/skills/katalon-cli-runner/references/runner_commands.md +67 -0
  43. package/.agents/skills/lam-script-generator/SKILL.md +83 -0
  44. package/.agents/skills/playwright-live-interaction/SKILL.md +86 -0
  45. package/.agents/skills/playwright-live-interaction/references/advanced_superpowers.md +160 -0
  46. package/.agents/skills/playwright-live-interaction/references/compact_accessibility_snapshots.md +75 -0
  47. package/.agents/skills/playwright-live-interaction/references/recipes.md +388 -0
  48. package/.agents/skills/playwright-live-interaction/references/swarm_orchestration.md +134 -0
  49. package/.agents/skills/security-dast-sentinel/SKILL.md +176 -0
  50. package/.agents/skills/self-healing-locator/SKILL.md +73 -0
  51. package/.agents/skills/synthetic-test-data-generator/SKILL.md +135 -0
  52. package/.agents/skills/token-and-context-optimizer/SKILL.md +70 -0
  53. package/.agents/skills/visual-regression-sentinel/SKILL.md +62 -0
  54. package/.agents/skills/wcag-audit-patterns/SKILL.md +77 -0
  55. package/.agents/skills/wcag-audit-patterns/resources/implementation-playbook.md +541 -0
  56. package/.agents/skills/web-vitals-and-performance-profiler/SKILL.md +65 -0
  57. package/.agents/templates/completion_report.md +57 -0
  58. package/.agents/workflows/graphify.md +10 -0
  59. package/.agents/workflows/test-impact.md +15 -0
  60. package/.agents/workflows/verify.md +34 -0
  61. package/AGENTS.md +115 -0
  62. package/README.md +537 -2
  63. package/bin/agent-qa.js +90 -0
  64. package/package.json +59 -4
  65. package/skills_manifest.json +262 -0
  66. package/templates/PROJECT_AI_GUIDE_TEMPLATE.md +97 -0
  67. package/templates/PROJECT_STATE_TEMPLATE.md +20 -0
  68. package/tools/TOOLS_RUNBOOK.md +298 -0
  69. package/tools/agent_canary.py +501 -0
  70. package/tools/analyze_project.py +242 -0
  71. package/tools/api_drift_sentinel.py +208 -0
  72. package/tools/auto_heal.py +189 -0
  73. package/tools/browser_helpers.js +593 -0
  74. package/tools/bug_reporter.py +224 -0
  75. package/tools/cache_resolver.py +846 -0
  76. package/tools/check_env.py +288 -0
  77. package/tools/ci_healing_hook.py +128 -0
  78. package/tools/cli.py +402 -0
  79. package/tools/config_decoupler.py +253 -0
  80. package/tools/db_sandbox.py +341 -0
  81. package/tools/distill_log.py +152 -0
  82. package/tools/e2e_flow_prober.ps1 +65 -0
  83. package/tools/flaky_detector.py +222 -0
  84. package/tools/fuzz_payloads.py +197 -0
  85. package/tools/generate_allure_report.ps1 +30 -0
  86. package/tools/generate_api_matrix.py +193 -0
  87. package/tools/generate_tc_from_story.py +182 -0
  88. package/tools/generate_test_reports.ps1 +135 -0
  89. package/tools/git_pre_commit_guard.py +203 -0
  90. package/tools/heal_pr_automator.py +195 -0
  91. package/tools/heuristic_locator_matcher.py +202 -0
  92. package/tools/infer_linter.py +238 -0
  93. package/tools/lint_ai_docs.py +254 -0
  94. package/tools/lookup_tc.py +165 -0
  95. package/tools/lookup_xpath.py +167 -0
  96. package/tools/matrix_sharder.py +146 -0
  97. package/tools/mcp_excel_server.ps1 +251 -0
  98. package/tools/mcp_server.py +490 -0
  99. package/tools/micro_mutator.py +309 -0
  100. package/tools/network_mock_shield.py +170 -0
  101. package/tools/omni_emitter.py +500 -0
  102. package/tools/omni_parser.py +435 -0
  103. package/tools/omni_transpiler.py +168 -0
  104. package/tools/pinpoint_failure.py +88 -0
  105. package/tools/project_state.py +169 -0
  106. package/tools/repo_map.json +12 -0
  107. package/tools/repo_map.py +277 -0
  108. package/tools/repo_map.txt +7 -0
  109. package/tools/safety_firewall.py +245 -0
  110. package/tools/setup_project.py +265 -0
  111. package/tools/smart_visual_masker.py +134 -0
  112. package/tools/test_impact.py +321 -0
  113. package/tools/tms_sync_bridge.py +342 -0
  114. package/tools/trace_data_flow.py +240 -0
  115. package/tools/traffic_session_converter.py +256 -0
  116. package/tools/universal_ir.py +480 -0
  117. package/tools/validate_groovy_scripts.py +173 -0
  118. package/tools/verify_all.py +496 -0
  119. package/tools/verify_all.sh +4 -0
  120. package/tools/visual_diff_canvas.py +142 -0
  121. package/tools/visual_som_helper.py +425 -0
  122. package/tools/wcag_audit.py +214 -0
@@ -0,0 +1,593 @@
1
+ /**
2
+ * Enterprise Browser Runtime Helper (window.__qa)
3
+ * -------------------------------------------------------------
4
+ * Provides advanced AI testing capabilities directly inside the live browser DOM:
5
+ * 1. Smart Action Batching (qa.batch) - Atomic execution with pause & state preservation.
6
+ * 2. Instant Form Audit (qa.audit) - Detects Angular/React/Vue invalid fields in 1-shot.
7
+ * 3. Self-Healing Locator Engine (qa.heal) - Suggests resilient selectors when locators break.
8
+ * 4. Safe Click & Overlay Bypass (qa.clickSafe) - Clears CDK/PrimeNG/Modal overlays before clicking.
9
+ * 5. Session State Exporter/Importer (qa.exportSession / qa.importSession).
10
+ * 6. Deterministic Sync Engine (qa.isStable / qa.waitForStable / qa.waitForNetworkIdle).
11
+ */
12
+
13
+ (function () {
14
+ 'use strict';
15
+
16
+ if (window.__qa && window.__qa.__version === '2.0.1') {
17
+ return window.__qa;
18
+ }
19
+
20
+ // Active in-flight network request tracker
21
+ let activeNetworkRequests = 0;
22
+ if (!window.__qa_net_hooked) {
23
+ window.__qa_net_hooked = true;
24
+ if (window.fetch) {
25
+ const origFetch = window.fetch;
26
+ window.fetch = function () {
27
+ activeNetworkRequests++;
28
+ return origFetch.apply(window, arguments).finally(() => {
29
+ activeNetworkRequests = Math.max(0, activeNetworkRequests - 1);
30
+ });
31
+ };
32
+ }
33
+ if (window.XMLHttpRequest) {
34
+ const origOpen = XMLHttpRequest.prototype.open;
35
+ const origSend = XMLHttpRequest.prototype.send;
36
+ XMLHttpRequest.prototype.open = function () {
37
+ this.addEventListener('loadend', () => {
38
+ activeNetworkRequests = Math.max(0, activeNetworkRequests - 1);
39
+ });
40
+ return origOpen.apply(this, arguments);
41
+ };
42
+ XMLHttpRequest.prototype.send = function () {
43
+ activeNetworkRequests++;
44
+ return origSend.apply(this, arguments);
45
+ };
46
+ }
47
+ }
48
+
49
+ const qa = {
50
+ __version: '2.0.1',
51
+
52
+ /**
53
+ * 1. Smart Action Batching
54
+ * Runs a sequence of step functions atomically.
55
+ * Halts immediately upon failure without resetting previously filled inputs.
56
+ * @param {Array<{name: string, fn: Function}>} steps
57
+ * @returns {Object} { status: 'SUCCESS' | 'FAILED', failedStep?: number, name?: string, error?: string }
58
+ */
59
+ batch: function (steps) {
60
+ if (!Array.isArray(steps)) {
61
+ return { status: 'FAILED', error: 'Steps must be an array of {name, fn}' };
62
+ }
63
+ for (let i = 0; i < steps.length; i++) {
64
+ const step = steps[i];
65
+ try {
66
+ if (typeof step.fn === 'function') {
67
+ step.fn();
68
+ } else if (typeof step.action === 'function') {
69
+ step.action();
70
+ } else {
71
+ throw new Error(`Step #${i + 1} (${step.name}) has no runnable function.`);
72
+ }
73
+ } catch (err) {
74
+ return {
75
+ status: 'FAILED',
76
+ failedStep: i + 1,
77
+ stepName: step.name || `Step #${i + 1}`,
78
+ error: err.message,
79
+ preservedStateUrl: window.location.href
80
+ };
81
+ }
82
+ }
83
+ return { status: 'SUCCESS', totalSteps: steps.length };
84
+ },
85
+
86
+ /**
87
+ * 2. Instant Form Audit
88
+ * Finds all invalid fields in active forms (diagnosing disabled Save buttons).
89
+ * Covers Angular Material, PrimeNG, Ant Design, Bootstrap, and HTML5 validation.
90
+ * @returns {Array<Object>} List of invalid controls with metadata
91
+ */
92
+ audit: function () {
93
+ const invalidNodes = Array.from(
94
+ document.querySelectorAll(
95
+ 'form .ng-invalid, input.ng-invalid, mat-form-field.ng-invalid, mat-select.ng-invalid, textarea.ng-invalid, p-dropdown.ng-invalid, :invalid'
96
+ )
97
+ );
98
+ return invalidNodes.map(el => ({
99
+ tag: el.tagName.toLowerCase(),
100
+ control: el.getAttribute('formcontrolname') || el.getAttribute('name') || el.id || 'unnamed',
101
+ placeholder: el.getAttribute('placeholder') || '',
102
+ classes: el.className,
103
+ value: el.value || el.innerText.trim()
104
+ }));
105
+ },
106
+
107
+ /**
108
+ * 3. Self-Healing Locator Engine
109
+ * When a selector returns 0 elements, scans DOM for matching text, label, or placeholder.
110
+ * @param {string} brokenSelector
111
+ * @param {string} [searchText]
112
+ * @returns {Object} Suggested robust selector
113
+ */
114
+ heal: function (brokenSelector, searchText) {
115
+ const query = (searchText || brokenSelector || '').replace(/['"\\\[\]]/g, '').trim().toLowerCase();
116
+ const candidates = [];
117
+
118
+ // Scan buttons
119
+ document.querySelectorAll('button, a.p-button, span.p-button-text, .mat-mdc-button, [role="button"]').forEach(btn => {
120
+ const txt = btn.innerText.trim().toLowerCase();
121
+ if (query && txt.includes(query)) {
122
+ candidates.push({
123
+ type: 'button',
124
+ text: btn.innerText.trim(),
125
+ suggestedXPath: `//button[contains(normalize-space(.), '${btn.innerText.trim()}')]`,
126
+ score: 0.95
127
+ });
128
+ }
129
+ });
130
+
131
+ // Scan inputs & form controls
132
+ document.querySelectorAll('input, textarea, mat-select, p-dropdown, select').forEach(inp => {
133
+ const fcn = inp.getAttribute('formcontrolname');
134
+ const ph = inp.getAttribute('placeholder') || '';
135
+ const name = inp.getAttribute('name');
136
+ const testid = inp.getAttribute('data-testid');
137
+ if (testid && testid.toLowerCase().includes(query)) {
138
+ candidates.push({
139
+ type: 'input',
140
+ dataTestId: testid,
141
+ suggestedCss: `[data-testid='${testid}']`,
142
+ score: 0.99
143
+ });
144
+ } else if ((fcn || name) && ((fcn || name).toLowerCase().includes(query) || (query && ph.toLowerCase().includes(query)))) {
145
+ const ctrl = fcn || name;
146
+ candidates.push({
147
+ type: 'input',
148
+ formControlName: fcn,
149
+ name: name,
150
+ placeholder: ph,
151
+ suggestedXPath: `//*[@formcontrolname='${ctrl}']`,
152
+ suggestedCss: `[formcontrolname='${ctrl}']`,
153
+ score: 0.98
154
+ });
155
+ }
156
+ });
157
+
158
+ return {
159
+ originalSelector: brokenSelector,
160
+ candidatesFound: candidates.length,
161
+ bestMatch: candidates[0] || null
162
+ };
163
+ },
164
+
165
+ /**
166
+ * 4. Safe Click & Overlay Bypass
167
+ * Dispatches click event directly, bypassing blocking overlays
168
+ * (.cdk-overlay-backdrop, .p-component-overlay, .modal-backdrop).
169
+ * @param {string | HTMLElement} target
170
+ * @returns {boolean}
171
+ */
172
+ clickSafe: function (target) {
173
+ const el = typeof target === 'string' ? document.querySelector(target) : target;
174
+ if (!el) return false;
175
+
176
+ // Temporarily neutralize blocking overlays if present
177
+ document.querySelectorAll('.cdk-overlay-backdrop, .p-component-overlay, .modal-backdrop').forEach(overlay => {
178
+ overlay.style.pointerEvents = 'none';
179
+ });
180
+
181
+ el.scrollIntoView({ behavior: 'auto', block: 'center' });
182
+ el.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
183
+ el.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
184
+ el.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }));
185
+ el.click();
186
+ return true;
187
+ },
188
+
189
+ /**
190
+ * 5. Session State Exporter
191
+ * Exports tokens and localStorage to bypass UI login (Zero-Second Auth).
192
+ * @returns {Object}
193
+ */
194
+ exportSession: function () {
195
+ const storage = {};
196
+ for (let i = 0; i < localStorage.length; i++) {
197
+ const key = localStorage.key(i);
198
+ storage[key] = localStorage.getItem(key);
199
+ }
200
+ return {
201
+ url: window.location.href,
202
+ cookies: document.cookie,
203
+ localStorage: storage
204
+ };
205
+ },
206
+
207
+ /**
208
+ * Restore session state
209
+ * @param {Object} data
210
+ */
211
+ importSession: function (data) {
212
+ if (data && data.localStorage) {
213
+ for (const [k, v] of Object.entries(data.localStorage)) {
214
+ localStorage.setItem(k, v);
215
+ }
216
+ }
217
+ return { status: 'SESSION_IMPORTED' };
218
+ },
219
+
220
+ /**
221
+ * 6. Zone.js & Microtask Stability Sync Engine
222
+ * Checks if framework microtasks, promises, and animations are fully settled.
223
+ * @returns {boolean}
224
+ */
225
+ isStable: function () {
226
+ try {
227
+ if (window.getAllAngularTestabilities) {
228
+ const testabilities = window.getAllAngularTestabilities();
229
+ if (testabilities && testabilities.length > 0) {
230
+ return testabilities.every(t => t.isStable());
231
+ }
232
+ }
233
+ const hasBlocker = document.querySelector(
234
+ '.p-blockui, .p-progress-spinner, .mat-mdc-progress-bar, .spinner, .loading, .ng-animating'
235
+ );
236
+ return !hasBlocker;
237
+ } catch (e) {
238
+ return true;
239
+ }
240
+ },
241
+
242
+ /**
243
+ * Asynchronously wait for framework to become stable
244
+ * @param {number} [timeoutMs=10000]
245
+ * @returns {Promise<boolean>}
246
+ */
247
+ waitForStable: function (timeoutMs = 10000) {
248
+ const self = this;
249
+ const start = Date.now();
250
+ return new Promise((resolve) => {
251
+ const interval = setInterval(() => {
252
+ if (self.isStable()) {
253
+ clearInterval(interval);
254
+ resolve(true);
255
+ } else if (Date.now() - start > timeoutMs) {
256
+ clearInterval(interval);
257
+ resolve(false);
258
+ }
259
+ }, 100);
260
+ });
261
+ },
262
+
263
+ /**
264
+ * 7. CDP Network Drain & In-Flight Traffic Monitor (Network Idle 0)
265
+ */
266
+ getActiveRequests: function () {
267
+ return activeNetworkRequests;
268
+ },
269
+
270
+ waitForNetworkIdle: function (idleTimeMs = 500, timeoutMs = 10000) {
271
+ const start = Date.now();
272
+ let idleStart = null;
273
+ return new Promise((resolve) => {
274
+ const interval = setInterval(() => {
275
+ if (activeNetworkRequests === 0) {
276
+ if (!idleStart) idleStart = Date.now();
277
+ if (Date.now() - idleStart >= idleTimeMs) {
278
+ clearInterval(interval);
279
+ resolve(true);
280
+ }
281
+ } else {
282
+ idleStart = null;
283
+ }
284
+ if (Date.now() - start > timeoutMs) {
285
+ clearInterval(interval);
286
+ resolve(false);
287
+ }
288
+ }, 50);
289
+ });
290
+ },
291
+
292
+ /**
293
+ * 8. Action Safety Firewall (Blast Radius Containment)
294
+ */
295
+ firewall: {
296
+ DANGEROUS_PATTERNS: /\b(delete|remove|purge|wipe|drop|pay|purchase|checkout|terminate|destroy|transfer|reset)\b/i,
297
+ DANGEROUS_SELECTORS: ['[data-action*="delete"]', '[data-action*="pay"]', '.btn-danger', '.p-button-danger', '[aria-label*="delete" i]'],
298
+
299
+ isDangerous: function (element, actionName = 'click') {
300
+ if (!element) return false;
301
+ const text = (element.innerText || element.textContent || element.value || element.getAttribute('aria-label') || '').trim();
302
+ if (this.DANGEROUS_PATTERNS.test(text)) return true;
303
+ for (const sel of this.DANGEROUS_SELECTORS) {
304
+ if (element.matches && element.matches(sel)) return true;
305
+ if (element.closest && element.closest(sel)) return true;
306
+ }
307
+ return false;
308
+ },
309
+
310
+ checkSafeAction: function (element, actionName = 'click') {
311
+ const dangerous = this.isDangerous(element, actionName);
312
+ if (!dangerous) {
313
+ return { allowed: true, reason: 'ACTION_SAFE' };
314
+ }
315
+ if (window.__ALLOW_DESTRUCTIVE__ === true) {
316
+ return { allowed: true, reason: 'BYPASS_FLAG_ENABLED' };
317
+ }
318
+ return {
319
+ allowed: false,
320
+ reason: 'DANGEROUS_ACTION_BLOCKED',
321
+ message: `Action '${actionName}' on destructive element was blocked by Safety Firewall. Set window.__ALLOW_DESTRUCTIVE__ = true to override.`
322
+ };
323
+ }
324
+ },
325
+
326
+ /**
327
+ * 9. Multimodal Vision-Grounded Set-of-Marks (SoM) Engine
328
+ * Pierces Shadow DOM and inspects HTML5 Canvas elements for coordinate grounding.
329
+ */
330
+ som: {
331
+ getInteractiveMarks: function () {
332
+ const marks = [];
333
+ let idCounter = 1;
334
+
335
+ function traverse(root) {
336
+ if (!root || !root.querySelectorAll) return;
337
+ const candidates = root.querySelectorAll('button, a, input, select, textarea, [role="button"], canvas, svg');
338
+ candidates.forEach(el => {
339
+ const rect = el.getBoundingClientRect();
340
+ if (rect.width > 5 && rect.height > 5 && window.getComputedStyle(el).visibility !== 'hidden') {
341
+ const label = el.getAttribute('aria-label') || el.innerText || el.value || el.tagName.toLowerCase();
342
+ marks.push({
343
+ mark_id: idCounter++,
344
+ tag: el.tagName.toLowerCase(),
345
+ label: (label || '').trim().substring(0, 50),
346
+ x: Math.round(rect.left),
347
+ y: Math.round(rect.top),
348
+ width: Math.round(rect.width),
349
+ height: Math.round(rect.height),
350
+ center_x: Math.round(rect.left + rect.width / 2),
351
+ center_y: Math.round(rect.top + rect.height / 2)
352
+ });
353
+ }
354
+ // Pierce Shadow DOM
355
+ if (el.shadowRoot) {
356
+ traverse(el.shadowRoot);
357
+ }
358
+ });
359
+ }
360
+
361
+ traverse(document);
362
+ return marks;
363
+ },
364
+
365
+ overlayMarks: function () {
366
+ this.clearMarks();
367
+ const container = document.createElement('div');
368
+ container.id = '__qa_som_overlay';
369
+ container.style.position = 'fixed';
370
+ container.style.top = '0';
371
+ container.style.left = '0';
372
+ container.style.width = '100vw';
373
+ container.style.height = '100vh';
374
+ container.style.pointerEvents = 'none';
375
+ container.style.zIndex = '999999';
376
+
377
+ const marks = this.getInteractiveMarks();
378
+ marks.forEach(m => {
379
+ const badge = document.createElement('div');
380
+ badge.style.position = 'absolute';
381
+ badge.style.left = m.x + 'px';
382
+ badge.style.top = Math.max(0, m.y - 18) + 'px';
383
+ badge.style.background = '#FF0055';
384
+ badge.style.color = '#FFFFFF';
385
+ badge.style.fontSize = '12px';
386
+ badge.style.fontWeight = 'bold';
387
+ badge.style.padding = '1px 4px';
388
+ badge.style.borderRadius = '3px';
389
+ badge.innerText = m.mark_id;
390
+
391
+ const box = document.createElement('div');
392
+ box.style.position = 'absolute';
393
+ box.style.left = m.x + 'px';
394
+ box.style.top = m.y + 'px';
395
+ box.style.width = m.width + 'px';
396
+ box.style.height = m.height + 'px';
397
+ box.style.border = '2px solid #FF0055';
398
+
399
+ container.appendChild(box);
400
+ container.appendChild(badge);
401
+ });
402
+
403
+ document.body.appendChild(container);
404
+ return marks;
405
+ },
406
+
407
+ clearMarks: function () {
408
+ const existing = document.getElementById('__qa_som_overlay');
409
+ if (existing) existing.remove();
410
+ }
411
+ },
412
+
413
+ /**
414
+ * 7. Ultra-Compact Accessibility Object Model (AOM) Distillation
415
+ * Filters noise (wrapper divs, styling spans) and returns an ultra-sparse
416
+ * tree representation (Role, Name, State, TestId) capped at maxDepth (default: 3).
417
+ * Prevents context rot / token bloat by condensing thousands of DOM nodes into ~300 tokens.
418
+ * @param {Element|Document} [root=document]
419
+ * @param {number} [maxDepth=3]
420
+ * @returns {Array<{role: string, name: string, state?: Object, testId?: string, depth: number}>}
421
+ */
422
+ getCompactAOM: function (root = document, maxDepth = 3) {
423
+ const aomNodes = [];
424
+ const INTERACTIVE_ROLES = new Set([
425
+ 'button', 'link', 'textbox', 'checkbox', 'radio', 'combobox', 'menuitem',
426
+ 'tab', 'switch', 'dialog', 'alert', 'searchbox', 'spinbutton', 'option', 'treeitem'
427
+ ]);
428
+
429
+ function getRole(el) {
430
+ const explicitRole = el.getAttribute('role');
431
+ if (explicitRole) return explicitRole.toLowerCase();
432
+ const tag = el.tagName.toLowerCase();
433
+ if (tag === 'button') return 'button';
434
+ if (tag === 'a' && el.hasAttribute('href')) return 'link';
435
+ if (tag === 'input') {
436
+ const type = (el.getAttribute('type') || 'text').toLowerCase();
437
+ if (['checkbox', 'radio', 'button', 'submit'].includes(type)) return type;
438
+ return 'textbox';
439
+ }
440
+ if (tag === 'select') return 'combobox';
441
+ if (tag === 'textarea') return 'textbox';
442
+ return null;
443
+ }
444
+
445
+ function getAccessibleName(el) {
446
+ return (
447
+ el.getAttribute('aria-label') ||
448
+ el.getAttribute('aria-placeholder') ||
449
+ el.getAttribute('placeholder') ||
450
+ el.getAttribute('title') ||
451
+ (el.innerText ? el.innerText.trim().slice(0, 50).replace(/\s+/g, ' ') : '') ||
452
+ el.getAttribute('name') ||
453
+ el.getAttribute('value') ||
454
+ ''
455
+ );
456
+ }
457
+
458
+ function getState(el) {
459
+ const state = {};
460
+ if (el.disabled || el.getAttribute('aria-disabled') === 'true') state.disabled = true;
461
+ if (el.getAttribute('aria-expanded') !== null) state.expanded = el.getAttribute('aria-expanded') === 'true';
462
+ if (el.checked || el.getAttribute('aria-checked') === 'true') state.checked = true;
463
+ if (el.getAttribute('aria-invalid') === 'true') state.invalid = true;
464
+ if (el.getAttribute('aria-selected') === 'true') state.selected = true;
465
+ return Object.keys(state).length > 0 ? state : undefined;
466
+ }
467
+
468
+ function isVisible(el) {
469
+ if (!el || el.nodeType !== 1) return false;
470
+ const style = window.getComputedStyle(el);
471
+ if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') return false;
472
+ const rect = el.getBoundingClientRect();
473
+ return rect.width > 0 && rect.height > 0;
474
+ }
475
+
476
+ function walk(node, currentDepth) {
477
+ if (currentDepth > maxDepth || !node) return;
478
+ const children = node.children || [];
479
+ for (let i = 0; i < children.length; i++) {
480
+ const child = children[i];
481
+ if (!isVisible(child)) continue;
482
+
483
+ const role = getRole(child);
484
+ const testId = child.getAttribute('data-testid') || child.getAttribute('data-cy') || child.id || '';
485
+
486
+ if (role && (INTERACTIVE_ROLES.has(role) || testId)) {
487
+ aomNodes.push({
488
+ role: role,
489
+ name: getAccessibleName(child),
490
+ state: getState(child),
491
+ testId: testId || undefined,
492
+ depth: currentDepth
493
+ });
494
+ }
495
+
496
+ // Pierce Shadow DOM
497
+ if (child.shadowRoot) {
498
+ walk(child.shadowRoot, currentDepth + 1);
499
+ }
500
+ walk(child, currentDepth + 1);
501
+ }
502
+ }
503
+
504
+ walk(root === document ? document.body : root, 1);
505
+ return aomNodes;
506
+ },
507
+
508
+ /**
509
+ * 8. Dynamic Stability Probe (Event-Driven Wait Weaver)
510
+ * Replaces flaky static sleeps (Thread.sleep / cy.wait) by dynamically probing:
511
+ * 1. Coordinate / Layout Stability: Bounding rect unchanged for 100ms consecutive duration.
512
+ * 2. Form Editable State: Ensures form inputs are editable (!disabled && !readOnly).
513
+ * 3. Network Quiescence: Ensures active in-flight network requests == 0.
514
+ * @param {string|Element} target
515
+ * @param {Object} [options]
516
+ * @returns {Promise<boolean>}
517
+ */
518
+ probeStability: function (target, options = {}) {
519
+ const opts = Object.assign({
520
+ checkPosition: true,
521
+ checkEditable: false,
522
+ stabilityDuration: 100,
523
+ timeout: 5000
524
+ }, options);
525
+
526
+ return new Promise((resolve) => {
527
+ const startTime = Date.now();
528
+ let el = null;
529
+ if (typeof target === 'string') {
530
+ el = document.querySelector(target);
531
+ } else if (target && target.nodeType === 1) {
532
+ el = target;
533
+ }
534
+
535
+ let lastRect = null;
536
+ let stableSince = null;
537
+
538
+ function check() {
539
+ const now = Date.now();
540
+ if (now - startTime > opts.timeout) {
541
+ return resolve(false);
542
+ }
543
+
544
+ if (!el && typeof target === 'string') {
545
+ el = document.querySelector(target);
546
+ }
547
+
548
+ if (el) {
549
+ if (opts.checkEditable) {
550
+ const tag = el.tagName.toLowerCase();
551
+ if (tag === 'input' || tag === 'textarea' || tag === 'select') {
552
+ if (el.disabled || el.readOnly) {
553
+ return setTimeout(check, 25);
554
+ }
555
+ }
556
+ }
557
+
558
+ if (opts.checkPosition) {
559
+ const rect = el.getBoundingClientRect();
560
+ if (
561
+ lastRect &&
562
+ Math.abs(rect.top - lastRect.top) < 1 &&
563
+ Math.abs(rect.left - lastRect.left) < 1 &&
564
+ Math.abs(rect.width - lastRect.width) < 1 &&
565
+ Math.abs(rect.height - lastRect.height) < 1
566
+ ) {
567
+ if (!stableSince) stableSince = now;
568
+ if (now - stableSince >= opts.stabilityDuration) {
569
+ return resolve(true);
570
+ }
571
+ } else {
572
+ lastRect = rect;
573
+ stableSince = null;
574
+ }
575
+ return setTimeout(check, 25);
576
+ }
577
+ }
578
+
579
+ if (activeNetworkRequests === 0) {
580
+ return resolve(true);
581
+ }
582
+ setTimeout(check, 25);
583
+ }
584
+
585
+ check();
586
+ });
587
+ }
588
+ };
589
+
590
+ window.__qa = qa;
591
+ window.__helper = qa;
592
+ return qa;
593
+ })();