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