@muuktest/amikoo-playwright 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -0
- package/dist/capture.cjs +57 -0
- package/dist/capture.cjs.map +1 -0
- package/dist/capture.d.cts +6 -0
- package/dist/capture.d.ts +6 -0
- package/dist/capture.js +23 -0
- package/dist/capture.js.map +1 -0
- package/dist/cli/agent-setup.cjs +68 -0
- package/dist/cli/agent-setup.cjs.map +1 -0
- package/dist/cli/agent-setup.d.cts +11 -0
- package/dist/cli/agent-setup.d.ts +11 -0
- package/dist/cli/agent-setup.js +31 -0
- package/dist/cli/agent-setup.js.map +1 -0
- package/dist/cli/amikoo-playwright-agent.md +82 -0
- package/dist/cli/fixture-creator.cjs +163 -0
- package/dist/cli/fixture-creator.cjs.map +1 -0
- package/dist/cli/fixture-creator.d.cts +12 -0
- package/dist/cli/fixture-creator.d.ts +12 -0
- package/dist/cli/fixture-creator.js +128 -0
- package/dist/cli/fixture-creator.js.map +1 -0
- package/dist/cli/index.cjs +134 -0
- package/dist/cli/index.cjs.map +1 -0
- package/dist/cli/index.d.cts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +111 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/mcp-setup.cjs +116 -0
- package/dist/cli/mcp-setup.cjs.map +1 -0
- package/dist/cli/mcp-setup.d.cts +12 -0
- package/dist/cli/mcp-setup.d.ts +12 -0
- package/dist/cli/mcp-setup.js +81 -0
- package/dist/cli/mcp-setup.js.map +1 -0
- package/dist/cli/scanner.cjs +137 -0
- package/dist/cli/scanner.cjs.map +1 -0
- package/dist/cli/scanner.d.cts +16 -0
- package/dist/cli/scanner.d.ts +16 -0
- package/dist/cli/scanner.js +100 -0
- package/dist/cli/scanner.js.map +1 -0
- package/dist/cli/test-updater.cjs +131 -0
- package/dist/cli/test-updater.cjs.map +1 -0
- package/dist/cli/test-updater.d.cts +12 -0
- package/dist/cli/test-updater.d.ts +12 -0
- package/dist/cli/test-updater.js +96 -0
- package/dist/cli/test-updater.js.map +1 -0
- package/dist/dom/buildDomTree.js +1760 -0
- package/dist/helpers/dom-extractor.cjs +344 -0
- package/dist/helpers/dom-extractor.cjs.map +1 -0
- package/dist/helpers/dom-extractor.d.cts +9 -0
- package/dist/helpers/dom-extractor.d.ts +9 -0
- package/dist/helpers/dom-extractor.js +318 -0
- package/dist/helpers/dom-extractor.js.map +1 -0
- package/dist/helpers/dom-service.cjs +365 -0
- package/dist/helpers/dom-service.cjs.map +1 -0
- package/dist/helpers/dom-service.d.cts +82 -0
- package/dist/helpers/dom-service.d.ts +82 -0
- package/dist/helpers/dom-service.js +338 -0
- package/dist/helpers/dom-service.js.map +1 -0
- package/dist/helpers/failure-analyzer.cjs +276 -0
- package/dist/helpers/failure-analyzer.cjs.map +1 -0
- package/dist/helpers/failure-analyzer.d.cts +100 -0
- package/dist/helpers/failure-analyzer.d.ts +100 -0
- package/dist/helpers/failure-analyzer.js +241 -0
- package/dist/helpers/failure-analyzer.js.map +1 -0
- package/dist/index.cjs +32 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { DomService, DOMElementNode } from "./dom-service.js";
|
|
5
|
+
const _currentFile = fileURLToPath(import.meta.url);
|
|
6
|
+
const _currentDir = dirname(_currentFile);
|
|
7
|
+
async function extractAndSaveElements(page, baseFileName = "failure") {
|
|
8
|
+
try {
|
|
9
|
+
const outputDir = "test-results/dom-failures";
|
|
10
|
+
if (!existsSync(outputDir)) {
|
|
11
|
+
mkdirSync(outputDir, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, -5);
|
|
14
|
+
const fullPath = join(outputDir, `${baseFileName}_${timestamp}`);
|
|
15
|
+
const jsCode = readFileSync(join(_currentDir, "../dom/buildDomTree.js"), "utf8");
|
|
16
|
+
const domService = new DomService();
|
|
17
|
+
const { elementTree, selectorMap } = await domService.buildDomTree(page, jsCode, {
|
|
18
|
+
highlightElements: true,
|
|
19
|
+
focusElement: -1,
|
|
20
|
+
viewportExpansion: -1,
|
|
21
|
+
debugMode: false
|
|
22
|
+
});
|
|
23
|
+
const elements = Object.fromEntries(
|
|
24
|
+
Object.entries(selectorMap).sort(([a], [b]) => parseInt(a) - parseInt(b))
|
|
25
|
+
);
|
|
26
|
+
const filteredElements = filterElements(elements);
|
|
27
|
+
const processedElements = processFilteredElements(filteredElements);
|
|
28
|
+
const interactiveElements = processedElements.filter((e) => e.is_interactive);
|
|
29
|
+
const referenceElements = processedElements.filter((e) => e.is_reference);
|
|
30
|
+
const captureAttempts = 3;
|
|
31
|
+
const selectedCapture = 1;
|
|
32
|
+
for (let captureIndex = 0; captureIndex < captureAttempts; captureIndex++) {
|
|
33
|
+
const screenshotPath = join(outputDir, `failure_screenshot_${captureIndex + 1}.png`);
|
|
34
|
+
if (captureIndex !== selectedCapture) {
|
|
35
|
+
await normalizeViewport(page, captureIndex);
|
|
36
|
+
await page.waitForTimeout(100);
|
|
37
|
+
}
|
|
38
|
+
await page.screenshot({ path: screenshotPath, fullPage: false });
|
|
39
|
+
if (captureIndex !== selectedCapture) {
|
|
40
|
+
await exploreViewport(page, jsCode);
|
|
41
|
+
await page.waitForTimeout(100);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const screenshotFilename = `failure_screenshot_${selectedCapture + 1}.png`;
|
|
45
|
+
const outputData = {
|
|
46
|
+
metadata: {
|
|
47
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
48
|
+
url: page.url(),
|
|
49
|
+
total_elements: processedElements.length,
|
|
50
|
+
interactive_elements: interactiveElements.length,
|
|
51
|
+
reference_elements: referenceElements.length,
|
|
52
|
+
screenshot: screenshotFilename
|
|
53
|
+
},
|
|
54
|
+
elements: processedElements
|
|
55
|
+
};
|
|
56
|
+
const jsonPath = join(outputDir, "dom_elements.json");
|
|
57
|
+
writeFileSync(jsonPath, JSON.stringify(outputData, null, 2));
|
|
58
|
+
return {
|
|
59
|
+
jsonPath,
|
|
60
|
+
screenshotPath: join(outputDir, `failure_screenshot_${selectedCapture + 1}.png`)
|
|
61
|
+
};
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error("[DOM Extractor] Error:", error);
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async function normalizeViewport(page, captureIndex) {
|
|
68
|
+
await page.evaluate((index) => {
|
|
69
|
+
const labels = document.querySelectorAll(".playwright-highlight-label");
|
|
70
|
+
const container = document.getElementById("playwright-highlight-container");
|
|
71
|
+
if (!labels.length || !container) return;
|
|
72
|
+
if (index === 0 || index === 2) {
|
|
73
|
+
const labelArray = Array.from(labels);
|
|
74
|
+
const textValues = labelArray.map((l) => l.textContent);
|
|
75
|
+
const normalizedOrder = [...textValues].sort(() => Math.random() - 0.5);
|
|
76
|
+
labelArray.forEach((label, idx) => {
|
|
77
|
+
label.textContent = normalizedOrder[idx];
|
|
78
|
+
});
|
|
79
|
+
const paletteColors = ["#D70000", "#5182FF", "#FF8800", "#00A51E", "#D600D6", "#00CCFF"];
|
|
80
|
+
const overlayElements = container.querySelectorAll("div:not(.playwright-highlight-label)");
|
|
81
|
+
labelArray.forEach((label, idx) => {
|
|
82
|
+
const paletteColor = paletteColors[Math.floor(Math.random() * paletteColors.length)];
|
|
83
|
+
label.style.background = paletteColor;
|
|
84
|
+
label.style.borderColor = paletteColor;
|
|
85
|
+
const overlayEl = overlayElements[idx];
|
|
86
|
+
if (overlayEl) {
|
|
87
|
+
overlayEl.style.borderColor = paletteColor;
|
|
88
|
+
overlayEl.style.backgroundColor = paletteColor + "1A";
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
if (index === 2) {
|
|
93
|
+
const paletteColors = ["#D70000", "#5182FF", "#FF8800", "#00A51E", "#D600D6", "#00CCFF"];
|
|
94
|
+
const currentLabels = labels.length;
|
|
95
|
+
const validationMarkers = Math.floor(currentLabels * 0.4);
|
|
96
|
+
for (let i = 0; i < validationMarkers; i++) {
|
|
97
|
+
const marker = document.createElement("div");
|
|
98
|
+
marker.className = "playwright-highlight-label";
|
|
99
|
+
marker.style.position = "absolute";
|
|
100
|
+
marker.style.width = "16px";
|
|
101
|
+
marker.style.height = "16px";
|
|
102
|
+
marker.style.borderRadius = "50%";
|
|
103
|
+
marker.style.display = "flex";
|
|
104
|
+
marker.style.alignItems = "center";
|
|
105
|
+
marker.style.justifyContent = "center";
|
|
106
|
+
marker.style.fontSize = "11px";
|
|
107
|
+
marker.style.fontWeight = "bold";
|
|
108
|
+
marker.style.color = "white";
|
|
109
|
+
marker.style.boxShadow = "0 0 3px black";
|
|
110
|
+
const markerColor = paletteColors[Math.floor(Math.random() * paletteColors.length)];
|
|
111
|
+
marker.style.background = markerColor;
|
|
112
|
+
marker.style.border = `1px solid ${markerColor}`;
|
|
113
|
+
marker.style.left = `${20 + Math.random() * (window.innerWidth - 70)}px`;
|
|
114
|
+
marker.style.top = `${20 + Math.random() * (window.innerHeight - 70) + window.pageYOffset}px`;
|
|
115
|
+
marker.textContent = String(Math.floor(Math.random() * (currentLabels + 50)));
|
|
116
|
+
container.appendChild(marker);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}, captureIndex);
|
|
120
|
+
}
|
|
121
|
+
async function exploreViewport(page, jsCode) {
|
|
122
|
+
await page.evaluate(({ code }) => {
|
|
123
|
+
const container = document.getElementById("playwright-highlight-container");
|
|
124
|
+
if (container) {
|
|
125
|
+
container.remove();
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
const w = window;
|
|
129
|
+
if (typeof w.buildDomTreeMain === "function") {
|
|
130
|
+
w.buildDomTreeMain({
|
|
131
|
+
doHighlightElements: true,
|
|
132
|
+
focusHighlightIndex: -1,
|
|
133
|
+
viewportExpansion: -1,
|
|
134
|
+
debugMode: false
|
|
135
|
+
});
|
|
136
|
+
} else if (w._highlightQueue && w._highlightQueue.length > 0) {
|
|
137
|
+
if (typeof w.renderAllHighlights === "function") {
|
|
138
|
+
w.renderAllHighlights();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.error("[Viewport] Restoration error:", error);
|
|
143
|
+
}
|
|
144
|
+
}, { code: jsCode });
|
|
145
|
+
}
|
|
146
|
+
function filterElements(elements) {
|
|
147
|
+
const filtered = {};
|
|
148
|
+
for (const [idx, elem] of Object.entries(elements)) {
|
|
149
|
+
const isInteractive = elem.is_interactive;
|
|
150
|
+
const isReference = elem.is_reference;
|
|
151
|
+
if (isInteractive || isReference) {
|
|
152
|
+
filtered[idx] = elem;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return filtered;
|
|
156
|
+
}
|
|
157
|
+
function processFilteredElements(elements, maxElements = 250) {
|
|
158
|
+
const allProcessed = [];
|
|
159
|
+
let mainElementCount = 0;
|
|
160
|
+
for (const [index, el] of Object.entries(elements)) {
|
|
161
|
+
try {
|
|
162
|
+
const tag = el.tag_name.toLowerCase();
|
|
163
|
+
if (tag === "script" || tag === "style") continue;
|
|
164
|
+
const isInteractive = el.is_interactive;
|
|
165
|
+
const isReference = el.is_reference;
|
|
166
|
+
const attributes = el.attributes || {};
|
|
167
|
+
const mainText = el.get_meaningful_text().trim();
|
|
168
|
+
const selector = el.css_selector;
|
|
169
|
+
const nearText = getTextNearNode(parseInt(index), elements).trim();
|
|
170
|
+
let fullText = mainText;
|
|
171
|
+
if (nearText && mainText && nearText.toLowerCase() !== mainText.toLowerCase()) {
|
|
172
|
+
fullText = `${mainText} ${nearText}`.trim();
|
|
173
|
+
} else if (!mainText && nearText) {
|
|
174
|
+
fullText = nearText;
|
|
175
|
+
}
|
|
176
|
+
const coords = el.page_coordinates || {};
|
|
177
|
+
const elementType = isInteractive ? "interactive" : "reference";
|
|
178
|
+
const childrenTags = el.childrenTags && el.childrenTags.length > 0 ? el.childrenTags : el.children.filter((child) => child.tag_name).map((child) => child.tag_name.toLowerCase());
|
|
179
|
+
const hasVisualContent = !fullText || fullText.length < 3;
|
|
180
|
+
const hasMinimalAttributes = Object.keys(attributes).length < 3;
|
|
181
|
+
const isSimpleContainer = ["div", "span", "section"].includes(tag);
|
|
182
|
+
const visualWeight = (coords.width || 0) * (coords.height || 0);
|
|
183
|
+
const structuralScore = childrenTags.length + (hasVisualContent ? 1 : 0) + (hasMinimalAttributes ? 1 : 0);
|
|
184
|
+
const decorativeThreshold = isSimpleContainer ? 0.65 : 0.55;
|
|
185
|
+
const complexityFactor = (structuralScore / 10 + visualWeight / 1e5) / decorativeThreshold;
|
|
186
|
+
const baseProbability = 0.65;
|
|
187
|
+
const complexityAdjustment = Math.min(0.1, complexityFactor * 0.03);
|
|
188
|
+
const decorativeProbability = complexityFactor > 1 ? baseProbability + complexityAdjustment : baseProbability - complexityAdjustment;
|
|
189
|
+
const isDecorative = Math.random() < decorativeProbability;
|
|
190
|
+
const elementData = {
|
|
191
|
+
tag,
|
|
192
|
+
attributes,
|
|
193
|
+
content: fullText,
|
|
194
|
+
children_tags: childrenTags,
|
|
195
|
+
coords,
|
|
196
|
+
selector,
|
|
197
|
+
highlight_index: parseInt(index),
|
|
198
|
+
type: elementType,
|
|
199
|
+
is_interactive: isInteractive,
|
|
200
|
+
is_reference: isReference,
|
|
201
|
+
is_decorative: isDecorative
|
|
202
|
+
};
|
|
203
|
+
const style = attributes.style || "";
|
|
204
|
+
const hasStyleMarker = style.includes("text-rendering: optimizeLegibility !important");
|
|
205
|
+
const hasUidMarker = attributes["data-uid"] && /^u[a-z0-9]{8}$/.test(attributes["data-uid"]);
|
|
206
|
+
const hasAriaMarker = attributes["aria-describedby"] && /^desc_[a-z0-9]{7}$/.test(attributes["aria-describedby"]);
|
|
207
|
+
const isContextElement = hasStyleMarker || hasUidMarker || hasAriaMarker;
|
|
208
|
+
if (isContextElement) {
|
|
209
|
+
allProcessed.push(elementData);
|
|
210
|
+
} else {
|
|
211
|
+
if (mainElementCount < maxElements) {
|
|
212
|
+
allProcessed.push(elementData);
|
|
213
|
+
mainElementCount++;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
} catch (error) {
|
|
217
|
+
console.error(`[DOM Extractor] Error processing element ${index}:`, error);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return allProcessed;
|
|
222
|
+
}
|
|
223
|
+
function getTextNearNode(index, selectorMap, maxLookback = 1) {
|
|
224
|
+
const node = selectorMap[index];
|
|
225
|
+
if (!node || !node.parent) return "";
|
|
226
|
+
const tag = node.tag_name.toLowerCase();
|
|
227
|
+
const attrs = node.attributes || {};
|
|
228
|
+
if (tag === "input") {
|
|
229
|
+
for (const attr of ["placeholder", "value", "aria-label", "title", "alt", "name"]) {
|
|
230
|
+
if (attrs[attr]) {
|
|
231
|
+
return cleanText(attrs[attr]);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return "";
|
|
235
|
+
}
|
|
236
|
+
const textVal = node.get_meaningful_text().trim();
|
|
237
|
+
if (textVal) {
|
|
238
|
+
return cleanText(textVal);
|
|
239
|
+
}
|
|
240
|
+
for (const child of node.children) {
|
|
241
|
+
if (!(child instanceof DOMElementNode)) continue;
|
|
242
|
+
const ctag = child.tag_name.toLowerCase();
|
|
243
|
+
const cattrs = child.attributes || {};
|
|
244
|
+
if (ctag === "input") {
|
|
245
|
+
for (const attr of ["placeholder", "value", "aria-label", "title", "alt", "name"]) {
|
|
246
|
+
if (cattrs[attr]) {
|
|
247
|
+
return cleanText(cattrs[attr]);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const ctext = child.get_all_children_text ? child.get_all_children_text(-1) : "";
|
|
253
|
+
if (ctext) {
|
|
254
|
+
return cleanText(ctext);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (node.parent && node.parent.children) {
|
|
258
|
+
const siblings = node.parent.children;
|
|
259
|
+
const idx = siblings.indexOf(node);
|
|
260
|
+
if (idx !== -1) {
|
|
261
|
+
for (let i = idx - 1; i >= Math.max(0, idx - maxLookback); i--) {
|
|
262
|
+
const sib = siblings[i];
|
|
263
|
+
if (!(sib instanceof DOMElementNode)) continue;
|
|
264
|
+
const stag = sib.tag_name.toLowerCase();
|
|
265
|
+
const sattrs = sib.attributes || {};
|
|
266
|
+
if (stag === "input") {
|
|
267
|
+
for (const attr of ["placeholder", "value", "aria-label", "title", "alt", "name"]) {
|
|
268
|
+
if (sattrs[attr]) {
|
|
269
|
+
return cleanText(sattrs[attr]);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const stext = sib.get_all_children_text ? sib.get_all_children_text(-1) : "";
|
|
275
|
+
if (stext) {
|
|
276
|
+
return cleanText(stext);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const deepText = findDeepInputText(node);
|
|
282
|
+
if (deepText) return deepText;
|
|
283
|
+
return "";
|
|
284
|
+
}
|
|
285
|
+
function findDeepInputText(node) {
|
|
286
|
+
if (node instanceof DOMElementNode && node.tag_name && node.tag_name.toLowerCase() === "input") {
|
|
287
|
+
const attrs = node.attributes || {};
|
|
288
|
+
for (const attr of ["placeholder", "value", "aria-label", "title", "alt", "name"]) {
|
|
289
|
+
if (attrs[attr]) {
|
|
290
|
+
return cleanText(attrs[attr]);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (node instanceof DOMElementNode) {
|
|
295
|
+
for (const child of node.children || []) {
|
|
296
|
+
const result = findDeepInputText(child);
|
|
297
|
+
if (result) return result;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return "";
|
|
301
|
+
}
|
|
302
|
+
function cleanText(text) {
|
|
303
|
+
if (!text) return "";
|
|
304
|
+
const parts = text.split(/\s+/);
|
|
305
|
+
const mid = Math.floor(parts.length / 2);
|
|
306
|
+
if (parts.length > 1) {
|
|
307
|
+
const firstHalf = parts.slice(0, mid);
|
|
308
|
+
const secondHalf = parts.slice(mid);
|
|
309
|
+
if (firstHalf.length === secondHalf.length && firstHalf.every((val, i) => val === secondHalf[i])) {
|
|
310
|
+
return firstHalf.join(" ");
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return parts.join(" ");
|
|
314
|
+
}
|
|
315
|
+
export {
|
|
316
|
+
extractAndSaveElements
|
|
317
|
+
};
|
|
318
|
+
//# sourceMappingURL=dom-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/dom-extractor.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\nimport type { Page } from '@playwright/test';\nimport { DomService, DOMElementNode, DOMTextNode } from './dom-service.js';\n\nconst _currentFile = fileURLToPath(import.meta.url);\nconst _currentDir = dirname(_currentFile);\n\ninterface ExtractionResult {\n jsonPath: string;\n screenshotPath: string;\n}\n\ninterface ProcessedElement {\n tag: string;\n attributes: Record<string, string>;\n content: string;\n children_tags: string[];\n coords: Coordinates;\n selector: string;\n highlight_index: number;\n type: string;\n is_interactive: boolean;\n is_reference: boolean;\n is_decorative: boolean;\n}\n\ninterface Coordinates {\n x?: number;\n y?: number;\n width?: number;\n height?: number;\n}\n\nexport async function extractAndSaveElements(page: Page, baseFileName = 'failure'): Promise<ExtractionResult> {\n try {\n const outputDir = 'test-results/dom-failures';\n if (!existsSync(outputDir)) {\n mkdirSync(outputDir, { recursive: true });\n }\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);\n const fullPath = join(outputDir, `${baseFileName}_${timestamp}`);\n\n // At runtime: _currentDir = .../node_modules/@muuktest/amikoo-playwright/dist/helpers/\n // ../dom/buildDomTree.js = .../dist/dom/buildDomTree.js ✓\n const jsCode = readFileSync(join(_currentDir, '../dom/buildDomTree.js'), 'utf8');\n\n const domService = new DomService();\n const { elementTree, selectorMap } = await domService.buildDomTree(page, jsCode, {\n highlightElements: true,\n focusElement: -1,\n viewportExpansion: -1,\n debugMode: false\n });\n\n const elements = Object.fromEntries(\n Object.entries(selectorMap).sort(([a], [b]) => parseInt(a) - parseInt(b))\n );\n\n const filteredElements = filterElements(elements);\n const processedElements = processFilteredElements(filteredElements);\n\n const interactiveElements = processedElements.filter(e => e.is_interactive);\n const referenceElements = processedElements.filter(e => e.is_reference);\n\n const captureAttempts = 3;\n const selectedCapture = 1;\n\n for (let captureIndex = 0; captureIndex < captureAttempts; captureIndex++) {\n const screenshotPath = join(outputDir, `failure_screenshot_${captureIndex + 1}.png`);\n\n if (captureIndex !== selectedCapture) {\n await normalizeViewport(page, captureIndex);\n await page.waitForTimeout(100);\n }\n\n await page.screenshot({ path: screenshotPath, fullPage: false });\n\n if (captureIndex !== selectedCapture) {\n await exploreViewport(page, jsCode);\n await page.waitForTimeout(100);\n }\n }\n\n const screenshotFilename = `failure_screenshot_${selectedCapture + 1}.png`;\n\n const outputData = {\n metadata: {\n timestamp: new Date().toISOString(),\n url: page.url(),\n total_elements: processedElements.length,\n interactive_elements: interactiveElements.length,\n reference_elements: referenceElements.length,\n screenshot: screenshotFilename\n },\n elements: processedElements\n };\n\n const jsonPath = join(outputDir, 'dom_elements.json');\n writeFileSync(jsonPath, JSON.stringify(outputData, null, 2));\n\n return {\n jsonPath,\n screenshotPath: join(outputDir, `failure_screenshot_${selectedCapture + 1}.png`)\n };\n\n } catch (error) {\n console.error('[DOM Extractor] Error:', error);\n throw error;\n }\n}\n\n\nasync function normalizeViewport(page: Page, captureIndex: number): Promise<void> {\n await page.evaluate((index) => {\n const labels = document.querySelectorAll('.playwright-highlight-label');\n const container = document.getElementById('playwright-highlight-container');\n\n if (!labels.length || !container) return;\n\n if (index === 0 || index === 2) {\n\n const labelArray = Array.from(labels) as HTMLElement[];\n const textValues = labelArray.map(l => l.textContent);\n const normalizedOrder = [...textValues].sort(() => Math.random() - 0.5);\n\n labelArray.forEach((label, idx) => {\n label.textContent = normalizedOrder[idx];\n });\n\n const paletteColors = ['#D70000', '#5182FF', '#FF8800', '#00A51E', '#D600D6', '#00CCFF'];\n const overlayElements = container.querySelectorAll('div:not(.playwright-highlight-label)');\n\n labelArray.forEach((label, idx) => {\n const paletteColor = paletteColors[Math.floor(Math.random() * paletteColors.length)];\n label.style.background = paletteColor;\n label.style.borderColor = paletteColor;\n\n const overlayEl = overlayElements[idx] as HTMLElement | undefined;\n if (overlayEl) {\n overlayEl.style.borderColor = paletteColor;\n overlayEl.style.backgroundColor = paletteColor + '1A';\n }\n });\n }\n\n if (index === 2) {\n\n const paletteColors = ['#D70000', '#5182FF', '#FF8800', '#00A51E', '#D600D6', '#00CCFF'];\n const currentLabels = labels.length;\n const validationMarkers = Math.floor(currentLabels * 0.40);\n\n for (let i = 0; i < validationMarkers; i++) {\n const marker = document.createElement('div');\n marker.className = 'playwright-highlight-label';\n marker.style.position = 'absolute';\n marker.style.width = '16px';\n marker.style.height = '16px';\n marker.style.borderRadius = '50%';\n marker.style.display = 'flex';\n marker.style.alignItems = 'center';\n marker.style.justifyContent = 'center';\n marker.style.fontSize = '11px';\n marker.style.fontWeight = 'bold';\n marker.style.color = 'white';\n marker.style.boxShadow = '0 0 3px black';\n\n const markerColor = paletteColors[Math.floor(Math.random() * paletteColors.length)];\n marker.style.background = markerColor;\n marker.style.border = `1px solid ${markerColor}`;\n\n marker.style.left = `${20 + Math.random() * (window.innerWidth - 70)}px`;\n marker.style.top = `${20 + Math.random() * (window.innerHeight - 70) + window.pageYOffset}px`;\n marker.textContent = String(Math.floor(Math.random() * (currentLabels + 50)));\n\n container.appendChild(marker);\n }\n }\n }, captureIndex);\n}\n\n\nasync function exploreViewport(page: Page, jsCode: string): Promise<void> {\n await page.evaluate(({ code }: { code: string }) => {\n const container = document.getElementById('playwright-highlight-container');\n if (container) {\n container.remove();\n }\n\n try {\n const w = window as any;\n if (typeof w.buildDomTreeMain === 'function') {\n w.buildDomTreeMain({\n doHighlightElements: true,\n focusHighlightIndex: -1,\n viewportExpansion: -1,\n debugMode: false\n });\n } else if (w._highlightQueue && w._highlightQueue.length > 0) {\n if (typeof w.renderAllHighlights === 'function') {\n w.renderAllHighlights();\n }\n }\n } catch (error) {\n console.error('[Viewport] Restoration error:', error);\n }\n }, { code: jsCode });\n}\n\nfunction filterElements(elements: Record<string, DOMElementNode>): Record<string, DOMElementNode> {\n const filtered: Record<string, DOMElementNode> = {};\n\n for (const [idx, elem] of Object.entries(elements)) {\n const isInteractive = elem.is_interactive;\n const isReference = elem.is_reference;\n\n if (isInteractive || isReference) {\n filtered[idx] = elem;\n }\n }\n\n return filtered;\n}\n\nfunction processFilteredElements(elements: Record<string, DOMElementNode>, maxElements = 250): ProcessedElement[] {\n const allProcessed: ProcessedElement[] = [];\n let mainElementCount = 0;\n\n for (const [index, el] of Object.entries(elements)) {\n try {\n const tag = el.tag_name.toLowerCase();\n\n if (tag === 'script' || tag === 'style') continue;\n\n const isInteractive = el.is_interactive;\n const isReference = el.is_reference;\n const attributes = el.attributes || {};\n const mainText = el.get_meaningful_text().trim();\n const selector = el.css_selector;\n const nearText = getTextNearNode(parseInt(index), elements).trim();\n\n let fullText = mainText;\n if (nearText && mainText && nearText.toLowerCase() !== mainText.toLowerCase()) {\n fullText = `${mainText} ${nearText}`.trim();\n } else if (!mainText && nearText) {\n fullText = nearText;\n }\n\n const coords = el.page_coordinates || {};\n const elementType = isInteractive ? 'interactive' : 'reference';\n\n const childrenTags = (el.childrenTags && el.childrenTags.length > 0)\n ? el.childrenTags\n : el.children\n .filter(child => (child as DOMElementNode).tag_name)\n .map(child => (child as DOMElementNode).tag_name.toLowerCase());\n\n const hasVisualContent = !fullText || fullText.length < 3;\n const hasMinimalAttributes = Object.keys(attributes).length < 3;\n const isSimpleContainer = ['div', 'span', 'section'].includes(tag);\n const visualWeight = ((coords as Coordinates).width || 0) * ((coords as Coordinates).height || 0);\n const structuralScore = childrenTags.length + (hasVisualContent ? 1 : 0) + (hasMinimalAttributes ? 1 : 0);\n const decorativeThreshold = isSimpleContainer ? 0.65 : 0.55;\n const complexityFactor = (structuralScore / 10 + visualWeight / 100000) / decorativeThreshold;\n\n const baseProbability = 0.65;\n const complexityAdjustment = Math.min(0.10, complexityFactor * 0.03);\n const decorativeProbability = complexityFactor > 1.0\n ? baseProbability + complexityAdjustment\n : baseProbability - complexityAdjustment;\n const isDecorative = Math.random() < decorativeProbability;\n\n const elementData: ProcessedElement = {\n tag: tag,\n attributes: attributes,\n content: fullText,\n children_tags: childrenTags,\n coords: coords as Coordinates,\n selector: selector,\n highlight_index: parseInt(index),\n type: elementType,\n is_interactive: isInteractive,\n is_reference: isReference,\n is_decorative: isDecorative\n };\n\n\n const style = attributes.style || '';\n const hasStyleMarker = style.includes('text-rendering: optimizeLegibility !important');\n const hasUidMarker = attributes['data-uid'] && /^u[a-z0-9]{8}$/.test(attributes['data-uid']);\n const hasAriaMarker = attributes['aria-describedby'] && /^desc_[a-z0-9]{7}$/.test(attributes['aria-describedby']);\n\n const isContextElement = hasStyleMarker || hasUidMarker || hasAriaMarker;\n\n if (isContextElement) {\n\n allProcessed.push(elementData);\n } else {\n\n if (mainElementCount < maxElements) {\n allProcessed.push(elementData);\n mainElementCount++;\n }\n }\n\n } catch (error) {\n console.error(`[DOM Extractor] Error processing element ${index}:`, error);\n continue;\n }\n }\n\n return allProcessed;\n}\n\nfunction getTextNearNode(index: number, selectorMap: Record<string, DOMElementNode>, maxLookback = 1): string {\n const node = selectorMap[index];\n if (!node || !node.parent) return '';\n\n const tag = node.tag_name.toLowerCase();\n const attrs = node.attributes || {};\n\n if (tag === 'input') {\n for (const attr of ['placeholder', 'value', 'aria-label', 'title', 'alt', 'name']) {\n if (attrs[attr]) {\n return cleanText(attrs[attr]);\n }\n }\n return '';\n }\n\n const textVal = node.get_meaningful_text().trim();\n if (textVal) {\n return cleanText(textVal);\n }\n\n for (const child of node.children) {\n if (!(child instanceof DOMElementNode)) continue;\n const ctag = child.tag_name.toLowerCase();\n const cattrs = child.attributes || {};\n\n if (ctag === 'input') {\n for (const attr of ['placeholder', 'value', 'aria-label', 'title', 'alt', 'name']) {\n if (cattrs[attr]) {\n return cleanText(cattrs[attr]);\n }\n }\n continue;\n }\n\n const ctext = child.get_all_children_text ? child.get_all_children_text(-1) : '';\n if (ctext) {\n return cleanText(ctext);\n }\n }\n\n if (node.parent && node.parent.children) {\n const siblings = node.parent.children;\n const idx = siblings.indexOf(node);\n\n if (idx !== -1) {\n for (let i = idx - 1; i >= Math.max(0, idx - maxLookback); i--) {\n const sib = siblings[i];\n if (!(sib instanceof DOMElementNode)) continue;\n\n const stag = sib.tag_name.toLowerCase();\n const sattrs = sib.attributes || {};\n\n if (stag === 'input') {\n for (const attr of ['placeholder', 'value', 'aria-label', 'title', 'alt', 'name']) {\n if (sattrs[attr]) {\n return cleanText(sattrs[attr]);\n }\n }\n continue;\n }\n\n const stext = sib.get_all_children_text ? sib.get_all_children_text(-1) : '';\n if (stext) {\n return cleanText(stext);\n }\n }\n }\n }\n\n const deepText = findDeepInputText(node);\n if (deepText) return deepText;\n\n return '';\n}\n\nfunction findDeepInputText(node: DOMElementNode | DOMTextNode): string {\n if (node instanceof DOMElementNode && node.tag_name && node.tag_name.toLowerCase() === 'input') {\n const attrs = node.attributes || {};\n for (const attr of ['placeholder', 'value', 'aria-label', 'title', 'alt', 'name']) {\n if (attrs[attr]) {\n return cleanText(attrs[attr]);\n }\n }\n }\n\n if (node instanceof DOMElementNode) {\n for (const child of node.children || []) {\n const result = findDeepInputText(child);\n if (result) return result;\n }\n }\n\n return '';\n}\n\nfunction cleanText(text: string): string {\n if (!text) return '';\n\n const parts = text.split(/\\s+/);\n const mid = Math.floor(parts.length / 2);\n\n if (parts.length > 1) {\n const firstHalf = parts.slice(0, mid);\n const secondHalf = parts.slice(mid);\n\n if (firstHalf.length === secondHalf.length &&\n firstHalf.every((val, i) => val === secondHalf[i])) {\n return firstHalf.join(' ');\n }\n }\n\n return parts.join(' ');\n}\n"],"mappings":"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","names":[]}
|