@one2x/playwright 1.57.0-alpha.12 → 1.57.0-alpha.14
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/lib/mcp/browser/browserContextFactory.js +4 -9
- package/lib/mcp/browser/config.js +1 -2
- package/lib/mcp/browser/tools/mouse.js +235 -1
- package/lib/mcp/browser/tools/screenshot.js +1 -1
- package/lib/mcp/browser/tools/snapshot.js +31 -55
- package/lib/mcp/sdk/http.js +2 -4
- package/package.json +2 -2
|
@@ -63,9 +63,8 @@ class BaseContextFactory {
|
|
|
63
63
|
}
|
|
64
64
|
_obtainBrowser(clientInfo) {
|
|
65
65
|
const key = this._calcFingerprint();
|
|
66
|
-
if (BaseContextFactory._cache.has(key))
|
|
66
|
+
if (BaseContextFactory._cache.has(key))
|
|
67
67
|
return BaseContextFactory._cache.get(key);
|
|
68
|
-
}
|
|
69
68
|
(0, import_log.testDebug)(`obtain browser (${this._logName})`);
|
|
70
69
|
const browserPromise = this._doObtainBrowser(clientInfo);
|
|
71
70
|
void browserPromise.then((browser) => {
|
|
@@ -292,7 +291,6 @@ class SharedContextFactory {
|
|
|
292
291
|
static create(config) {
|
|
293
292
|
if (SharedContextFactory._instance)
|
|
294
293
|
return SharedContextFactory._instance;
|
|
295
|
-
;
|
|
296
294
|
const baseConfig = { ...config, sharedBrowserContext: false };
|
|
297
295
|
const baseFactory = contextFactory(baseConfig);
|
|
298
296
|
SharedContextFactory._instance = new SharedContextFactory(baseFactory);
|
|
@@ -335,15 +333,12 @@ async function computeTracesDir(config, clientInfo) {
|
|
|
335
333
|
return await (0, import_config.outputFile)(config, clientInfo, `traces`, { origin: "code", reason: "Collecting trace" });
|
|
336
334
|
}
|
|
337
335
|
function stableStringify(obj) {
|
|
338
|
-
if (obj === null || obj === void 0)
|
|
336
|
+
if (obj === null || obj === void 0)
|
|
339
337
|
return String(obj);
|
|
340
|
-
|
|
341
|
-
if (typeof obj !== "object") {
|
|
338
|
+
if (typeof obj !== "object")
|
|
342
339
|
return JSON.stringify(obj);
|
|
343
|
-
|
|
344
|
-
if (Array.isArray(obj)) {
|
|
340
|
+
if (Array.isArray(obj))
|
|
345
341
|
return "[" + obj.map((item) => stableStringify(item)).join(",") + "]";
|
|
346
|
-
}
|
|
347
342
|
const sortedKeys = Object.keys(obj).sort();
|
|
348
343
|
const pairs = sortedKeys.map((key) => {
|
|
349
344
|
return JSON.stringify(key) + ":" + stableStringify(obj[key]);
|
|
@@ -397,9 +397,8 @@ function configFromURLParams(url) {
|
|
|
397
397
|
const viewportSize = getParam("viewport-size");
|
|
398
398
|
if (viewportSize)
|
|
399
399
|
options.viewportSize = resolutionParser("--viewport-size", viewportSize);
|
|
400
|
-
if (!Object.values(options).some((v) => v !== void 0))
|
|
400
|
+
if (!Object.values(options).some((v) => v !== void 0))
|
|
401
401
|
return {};
|
|
402
|
-
}
|
|
403
402
|
return configFromCLIOptions(options);
|
|
404
403
|
}
|
|
405
404
|
function queryToBoolean(value) {
|
|
@@ -24,6 +24,7 @@ module.exports = __toCommonJS(mouse_exports);
|
|
|
24
24
|
var import_bundle = require("../../sdk/bundle");
|
|
25
25
|
var import_tool = require("./tool");
|
|
26
26
|
var import_snapshot = require("./snapshot");
|
|
27
|
+
var import_utils = require("./utils");
|
|
27
28
|
const elementSchema = import_bundle.z.object({
|
|
28
29
|
element: import_bundle.z.string().describe("Human-readable element description used to obtain permission to interact with the element")
|
|
29
30
|
});
|
|
@@ -132,9 +133,242 @@ const mouseDragToXY = (0, import_tool.defineTabTool)({
|
|
|
132
133
|
response.setIncludeAutoScreenshot();
|
|
133
134
|
}
|
|
134
135
|
});
|
|
136
|
+
const inspectAtXY = (0, import_tool.defineTabTool)({
|
|
137
|
+
capability: "core",
|
|
138
|
+
schema: {
|
|
139
|
+
name: "browser_inspect_element_at_xy",
|
|
140
|
+
title: "Inspect elements at coordinates",
|
|
141
|
+
description: "Inspect all meaningful elements at x,y coordinates (excluding parent containers). Returns element details for generating test assertions including suggested selector, HTML, and computed styles",
|
|
142
|
+
inputSchema: elementSchema.extend({
|
|
143
|
+
x: import_bundle.z.number().describe("X coordinate"),
|
|
144
|
+
y: import_bundle.z.number().describe("Y coordinate")
|
|
145
|
+
}),
|
|
146
|
+
type: "readOnly"
|
|
147
|
+
},
|
|
148
|
+
handle: async (tab, params, response) => {
|
|
149
|
+
const elementHandlesData = await tab.page.evaluateHandle(({ x, y }) => {
|
|
150
|
+
const allElements = document.elementsFromPoint(x, y);
|
|
151
|
+
const meaningfulElements = allElements.filter((element, index) => {
|
|
152
|
+
for (let i = 0; i < allElements.length; i++) {
|
|
153
|
+
if (i !== index && element.contains(allElements[i]))
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
return true;
|
|
157
|
+
});
|
|
158
|
+
return meaningfulElements;
|
|
159
|
+
}, params);
|
|
160
|
+
const elementHandlesArray = await elementHandlesData.evaluateHandle((arr) => arr);
|
|
161
|
+
const elementHandles = [];
|
|
162
|
+
const length = await elementHandlesArray.evaluate((arr) => arr.length);
|
|
163
|
+
for (let i = 0; i < length; i++) {
|
|
164
|
+
const handle = await elementHandlesArray.evaluateHandle((arr, index) => arr[index], i);
|
|
165
|
+
const element = handle.asElement();
|
|
166
|
+
if (element)
|
|
167
|
+
elementHandles.push(element);
|
|
168
|
+
}
|
|
169
|
+
const elementsData = [];
|
|
170
|
+
for (const elementHandle of elementHandles) {
|
|
171
|
+
const pathToRoot = await elementHandle.getPathWithRefs().catch(() => "");
|
|
172
|
+
const elementData = await elementHandle.evaluate((element) => {
|
|
173
|
+
const computedStyle = window.getComputedStyle(element);
|
|
174
|
+
function serializeAttributes(el) {
|
|
175
|
+
const essentialAttrs = ["id", "class", "type", "name", "role", "aria-label"];
|
|
176
|
+
const attrs = [];
|
|
177
|
+
for (const attrName of essentialAttrs) {
|
|
178
|
+
const value = el.getAttribute(attrName);
|
|
179
|
+
if (value)
|
|
180
|
+
attrs.push(` ${attrName}="${value}"`);
|
|
181
|
+
}
|
|
182
|
+
return attrs.join("");
|
|
183
|
+
}
|
|
184
|
+
function buildTree(el) {
|
|
185
|
+
return {
|
|
186
|
+
element: el,
|
|
187
|
+
expanded: false,
|
|
188
|
+
childNodes: Array.from(el.children).map((child) => buildTree(child))
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function renderTree(node) {
|
|
192
|
+
const tag = node.element.tagName.toLowerCase();
|
|
193
|
+
const attrs = serializeAttributes(node.element);
|
|
194
|
+
const openTag = `<${tag}${attrs}>`;
|
|
195
|
+
const closeTag = `</${tag}>`;
|
|
196
|
+
const selfClosing = ["img", "input", "br", "hr", "meta", "link"];
|
|
197
|
+
if (selfClosing.includes(tag))
|
|
198
|
+
return `<${tag}${attrs} />`;
|
|
199
|
+
if (node.childNodes.length === 0) {
|
|
200
|
+
const text = node.element.textContent?.trim() || "";
|
|
201
|
+
if (text && text.length <= 50)
|
|
202
|
+
return openTag + text + closeTag;
|
|
203
|
+
else if (text)
|
|
204
|
+
return openTag + text.substring(0, 47) + "..." + closeTag;
|
|
205
|
+
return openTag + closeTag;
|
|
206
|
+
}
|
|
207
|
+
if (!node.expanded) {
|
|
208
|
+
const count = node.element.children.length;
|
|
209
|
+
return `${openTag}<!-- ${count} child element${count !== 1 ? "s" : ""} removed -->${closeTag}`;
|
|
210
|
+
}
|
|
211
|
+
const childrenHtml = node.childNodes.map((child) => renderTree(child)).join("");
|
|
212
|
+
return openTag + childrenHtml + closeTag;
|
|
213
|
+
}
|
|
214
|
+
function getExpandableNodes(node) {
|
|
215
|
+
const queue = [node];
|
|
216
|
+
const expandable = [];
|
|
217
|
+
while (queue.length > 0) {
|
|
218
|
+
const current = queue.shift();
|
|
219
|
+
if (current.childNodes.length > 0 && !current.expanded)
|
|
220
|
+
expandable.push(current);
|
|
221
|
+
if (current.expanded)
|
|
222
|
+
queue.push(...current.childNodes);
|
|
223
|
+
}
|
|
224
|
+
return expandable;
|
|
225
|
+
}
|
|
226
|
+
function truncateIterativeDeepening(el, maxBudget) {
|
|
227
|
+
if (el.outerHTML.length <= maxBudget)
|
|
228
|
+
return el.outerHTML;
|
|
229
|
+
const root = buildTree(el);
|
|
230
|
+
let currentHtml = renderTree(root);
|
|
231
|
+
while (currentHtml.length < maxBudget) {
|
|
232
|
+
const expandable = getExpandableNodes(root);
|
|
233
|
+
if (expandable.length === 0)
|
|
234
|
+
break;
|
|
235
|
+
let expanded = false;
|
|
236
|
+
for (const node of expandable) {
|
|
237
|
+
node.expanded = true;
|
|
238
|
+
const newHtml = renderTree(root);
|
|
239
|
+
if (newHtml.length <= maxBudget) {
|
|
240
|
+
currentHtml = newHtml;
|
|
241
|
+
expanded = true;
|
|
242
|
+
break;
|
|
243
|
+
} else {
|
|
244
|
+
node.expanded = false;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (!expanded)
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
return currentHtml;
|
|
251
|
+
}
|
|
252
|
+
const ariaRef = element._ariaRef;
|
|
253
|
+
return {
|
|
254
|
+
ref: ariaRef?.ref || null,
|
|
255
|
+
role: ariaRef?.role || null,
|
|
256
|
+
element: ariaRef?.name || element.tagName.toLowerCase(),
|
|
257
|
+
truncatedHTML: truncateIterativeDeepening(element, 300),
|
|
258
|
+
tagName: element.tagName.toLowerCase(),
|
|
259
|
+
computedStyles: {
|
|
260
|
+
display: computedStyle.display,
|
|
261
|
+
visibility: computedStyle.visibility,
|
|
262
|
+
opacity: computedStyle.opacity,
|
|
263
|
+
position: computedStyle.position,
|
|
264
|
+
zIndex: computedStyle.zIndex,
|
|
265
|
+
width: computedStyle.width,
|
|
266
|
+
height: computedStyle.height,
|
|
267
|
+
backgroundColor: computedStyle.backgroundColor,
|
|
268
|
+
color: computedStyle.color,
|
|
269
|
+
fontSize: computedStyle.fontSize,
|
|
270
|
+
fontFamily: computedStyle.fontFamily
|
|
271
|
+
},
|
|
272
|
+
textContent: element.textContent?.trim() || "",
|
|
273
|
+
innerText: element.innerText?.trim() || "",
|
|
274
|
+
value: element.value || void 0,
|
|
275
|
+
checked: element.checked || void 0,
|
|
276
|
+
disabled: element.disabled || void 0,
|
|
277
|
+
readonly: element.readOnly || void 0
|
|
278
|
+
};
|
|
279
|
+
});
|
|
280
|
+
elementsData.push({
|
|
281
|
+
...elementData,
|
|
282
|
+
pathToRoot
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
let suggestedSelectors = "";
|
|
286
|
+
if (elementsData.length > 0 && elementsData[0].ref) {
|
|
287
|
+
try {
|
|
288
|
+
const topElement = elementsData[0];
|
|
289
|
+
const { locator } = await tab.refLocator({
|
|
290
|
+
element: topElement.element,
|
|
291
|
+
ref: topElement.ref
|
|
292
|
+
});
|
|
293
|
+
suggestedSelectors = (await (0, import_utils.generateLocators)(locator)).map((s) => `\`page.${s}\``).join("\n");
|
|
294
|
+
} catch (e) {
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
let result = "";
|
|
298
|
+
if (elementsData.length > 1)
|
|
299
|
+
result += `\u26A0\uFE0F Warning: Found ${elementsData.length} elements at (${params.x}, ${params.y})
|
|
300
|
+
|
|
301
|
+
`;
|
|
302
|
+
elementsData.forEach((elementInfo, index) => {
|
|
303
|
+
const isTopmost = index === 0;
|
|
304
|
+
const header = isTopmost ? `**Element at (${params.x}, ${params.y}) - Topmost (will receive clicks):**` : `**Element ${index + 1} at (${params.x}, ${params.y}) - Below topmost:**`;
|
|
305
|
+
result += header + "\n";
|
|
306
|
+
result += `- RefId: ${elementInfo.ref || "N/A"}
|
|
307
|
+
`;
|
|
308
|
+
result += `- Path to root: ${elementInfo.pathToRoot}
|
|
309
|
+
`;
|
|
310
|
+
if (isTopmost && suggestedSelectors)
|
|
311
|
+
result += `- Suggested Playwright Selector:
|
|
312
|
+
${suggestedSelectors}
|
|
313
|
+
`;
|
|
314
|
+
result += `
|
|
315
|
+
**HTML (truncated to 300 chars):**
|
|
316
|
+
\`\`\`html
|
|
317
|
+
${elementInfo.truncatedHTML}
|
|
318
|
+
\`\`\`
|
|
319
|
+
`;
|
|
320
|
+
result += `
|
|
321
|
+
**Key Computed Styles:**
|
|
322
|
+
`;
|
|
323
|
+
result += `- Display: ${elementInfo.computedStyles.display}
|
|
324
|
+
`;
|
|
325
|
+
result += `- Visibility: ${elementInfo.computedStyles.visibility}
|
|
326
|
+
`;
|
|
327
|
+
result += `- Opacity: ${elementInfo.computedStyles.opacity}
|
|
328
|
+
`;
|
|
329
|
+
result += `- Position: ${elementInfo.computedStyles.position}
|
|
330
|
+
`;
|
|
331
|
+
result += `- Z-Index: ${elementInfo.computedStyles.zIndex}
|
|
332
|
+
`;
|
|
333
|
+
result += `- Width: ${elementInfo.computedStyles.width}
|
|
334
|
+
`;
|
|
335
|
+
result += `- Height: ${elementInfo.computedStyles.height}
|
|
336
|
+
`;
|
|
337
|
+
result += `- Background Color: ${elementInfo.computedStyles.backgroundColor}
|
|
338
|
+
`;
|
|
339
|
+
result += `- Color: ${elementInfo.computedStyles.color}
|
|
340
|
+
`;
|
|
341
|
+
result += `
|
|
342
|
+
**Element Properties:**
|
|
343
|
+
`;
|
|
344
|
+
result += `- Tag: ${elementInfo.tagName}
|
|
345
|
+
`;
|
|
346
|
+
result += `- Text Content: "${elementInfo.textContent}"
|
|
347
|
+
`;
|
|
348
|
+
result += `- Inner Text: "${elementInfo.innerText}"
|
|
349
|
+
`;
|
|
350
|
+
if (elementInfo.value !== void 0)
|
|
351
|
+
result += `- Value: "${elementInfo.value}"
|
|
352
|
+
`;
|
|
353
|
+
if (elementInfo.checked !== void 0)
|
|
354
|
+
result += `- Checked: ${elementInfo.checked}
|
|
355
|
+
`;
|
|
356
|
+
if (elementInfo.disabled !== void 0)
|
|
357
|
+
result += `- Disabled: ${elementInfo.disabled}
|
|
358
|
+
`;
|
|
359
|
+
if (elementInfo.readonly !== void 0)
|
|
360
|
+
result += `- Read Only: ${elementInfo.readonly}
|
|
361
|
+
`;
|
|
362
|
+
if (index < elementsData.length - 1)
|
|
363
|
+
result += "\n---\n\n";
|
|
364
|
+
});
|
|
365
|
+
response.addResult(result);
|
|
366
|
+
}
|
|
367
|
+
});
|
|
135
368
|
var mouse_default = [
|
|
136
369
|
mouseMove,
|
|
137
370
|
mouseClick,
|
|
138
371
|
mouseDrag,
|
|
139
|
-
mouseDragToXY
|
|
372
|
+
mouseDragToXY,
|
|
373
|
+
inspectAtXY
|
|
140
374
|
];
|
|
@@ -51,7 +51,7 @@ const screenshot = (0, import_tool.defineTabTool)({
|
|
|
51
51
|
schema: {
|
|
52
52
|
name: "browser_take_screenshot",
|
|
53
53
|
title: "Take a screenshot",
|
|
54
|
-
description: `Take a screenshot of the current page. You can
|
|
54
|
+
description: `Take a screenshot of the current page. You can call tools ends with '_xy' based on the screenshot coordinates.`,
|
|
55
55
|
inputSchema: screenshotSchema,
|
|
56
56
|
type: "readOnly"
|
|
57
57
|
},
|
|
@@ -88,9 +88,8 @@ const click = (0, import_tool.defineTabTool)({
|
|
|
88
88
|
options
|
|
89
89
|
);
|
|
90
90
|
});
|
|
91
|
-
if (!result)
|
|
91
|
+
if (!result)
|
|
92
92
|
return;
|
|
93
|
-
}
|
|
94
93
|
if (result.mode === "auto" && result.interceptorInfo) {
|
|
95
94
|
const actionName = params.doubleClick ? ".dblclick({ allowIntercept: true })" : ".click({ allowIntercept: true })";
|
|
96
95
|
(0, import_utils.addInterceptorWarning)(response, resolved, actionName, result.interceptorInfo);
|
|
@@ -133,9 +132,8 @@ const drag = (0, import_tool.defineTabTool)({
|
|
|
133
132
|
{}
|
|
134
133
|
);
|
|
135
134
|
});
|
|
136
|
-
if (!result)
|
|
135
|
+
if (!result)
|
|
137
136
|
return;
|
|
138
|
-
}
|
|
139
137
|
if (result.mode === "auto" && result.interceptorInfo)
|
|
140
138
|
(0, import_utils.addInterceptorWarning)(response, start.resolved, ".dragTo(target, { allowIntercept: true })", result.interceptorInfo);
|
|
141
139
|
const needsAllowIntercept = result.mode === "auto" && result.interceptorInfo || result.mode === "always";
|
|
@@ -163,9 +161,8 @@ const hover = (0, import_tool.defineTabTool)({
|
|
|
163
161
|
{}
|
|
164
162
|
);
|
|
165
163
|
});
|
|
166
|
-
if (!result)
|
|
164
|
+
if (!result)
|
|
167
165
|
return;
|
|
168
|
-
}
|
|
169
166
|
if (result.mode === "auto" && result.interceptorInfo)
|
|
170
167
|
(0, import_utils.addInterceptorWarning)(response, resolved, ".hover({ allowIntercept: true })", result.interceptorInfo);
|
|
171
168
|
const needsAllowIntercept = result.mode === "auto" && result.interceptorInfo || result.mode === "always";
|
|
@@ -213,7 +210,7 @@ const pickLocator = (0, import_tool.defineTabTool)({
|
|
|
213
210
|
const elementInspect = (0, import_tool.defineTool)({
|
|
214
211
|
capability: "core",
|
|
215
212
|
schema: {
|
|
216
|
-
name: "
|
|
213
|
+
name: "browser_inspect_element",
|
|
217
214
|
title: "Inspect element for testing",
|
|
218
215
|
description: "Get element details for generating test assertions - returns suggested selector, HTML, and computed styles",
|
|
219
216
|
inputSchema: elementSchema,
|
|
@@ -226,31 +223,19 @@ const elementInspect = (0, import_tool.defineTool)({
|
|
|
226
223
|
let elementDetails;
|
|
227
224
|
const suggestedSelectors = (await (0, import_utils.generateLocators)(locator)).map((s) => `\`page.${s}\``).join("\n");
|
|
228
225
|
try {
|
|
226
|
+
const elementHandle = await locator.elementHandle();
|
|
227
|
+
if (!elementHandle)
|
|
228
|
+
throw new Error("Element not found");
|
|
229
|
+
const pathToRoot = await elementHandle.getPathWithRefs();
|
|
229
230
|
elementDetails = await locator.evaluate((element) => {
|
|
230
231
|
const computedStyle = window.getComputedStyle(element);
|
|
231
|
-
const path = [];
|
|
232
|
-
let current = element;
|
|
233
|
-
while (current && current !== document.documentElement) {
|
|
234
|
-
const ariaRef = current._ariaRef;
|
|
235
|
-
const tagName = current.tagName?.toLowerCase() || "unknown";
|
|
236
|
-
let nodeInfo = tagName;
|
|
237
|
-
if (ariaRef?.ref) {
|
|
238
|
-
nodeInfo += ` [${ariaRef.ref}]`;
|
|
239
|
-
if (ariaRef.role) {
|
|
240
|
-
nodeInfo += ` [${ariaRef.role}]`;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
path.unshift(nodeInfo);
|
|
244
|
-
current = current.parentElement;
|
|
245
|
-
}
|
|
246
232
|
function serializeAttributes(el) {
|
|
247
233
|
const essentialAttrs = ["id", "class", "type", "name", "role", "aria-label"];
|
|
248
234
|
const attrs = [];
|
|
249
235
|
for (const attrName of essentialAttrs) {
|
|
250
236
|
const value = el.getAttribute(attrName);
|
|
251
|
-
if (value)
|
|
237
|
+
if (value)
|
|
252
238
|
attrs.push(` ${attrName}="${value}"`);
|
|
253
|
-
}
|
|
254
239
|
}
|
|
255
240
|
return attrs.join("");
|
|
256
241
|
}
|
|
@@ -267,16 +252,14 @@ const elementInspect = (0, import_tool.defineTool)({
|
|
|
267
252
|
const openTag = `<${tag}${attrs}>`;
|
|
268
253
|
const closeTag = `</${tag}>`;
|
|
269
254
|
const selfClosing = ["img", "input", "br", "hr", "meta", "link"];
|
|
270
|
-
if (selfClosing.includes(tag))
|
|
255
|
+
if (selfClosing.includes(tag))
|
|
271
256
|
return `<${tag}${attrs} />`;
|
|
272
|
-
}
|
|
273
257
|
if (node.childNodes.length === 0) {
|
|
274
258
|
const text = node.element.textContent?.trim() || "";
|
|
275
|
-
if (text && text.length <= 50)
|
|
259
|
+
if (text && text.length <= 50)
|
|
276
260
|
return openTag + text + closeTag;
|
|
277
|
-
|
|
261
|
+
else if (text)
|
|
278
262
|
return openTag + text.substring(0, 47) + "..." + closeTag;
|
|
279
|
-
}
|
|
280
263
|
return openTag + closeTag;
|
|
281
264
|
}
|
|
282
265
|
if (!node.expanded) {
|
|
@@ -290,27 +273,23 @@ const elementInspect = (0, import_tool.defineTool)({
|
|
|
290
273
|
const queue = [node];
|
|
291
274
|
const expandable = [];
|
|
292
275
|
while (queue.length > 0) {
|
|
293
|
-
const
|
|
294
|
-
if (
|
|
295
|
-
expandable.push(
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
queue.push(...current2.childNodes);
|
|
299
|
-
}
|
|
276
|
+
const current = queue.shift();
|
|
277
|
+
if (current.childNodes.length > 0 && !current.expanded)
|
|
278
|
+
expandable.push(current);
|
|
279
|
+
if (current.expanded)
|
|
280
|
+
queue.push(...current.childNodes);
|
|
300
281
|
}
|
|
301
282
|
return expandable;
|
|
302
283
|
}
|
|
303
284
|
function truncateIterativeDeepening(el, maxBudget) {
|
|
304
|
-
if (el.outerHTML.length <= maxBudget)
|
|
285
|
+
if (el.outerHTML.length <= maxBudget)
|
|
305
286
|
return el.outerHTML;
|
|
306
|
-
}
|
|
307
287
|
const root = buildTree(el);
|
|
308
288
|
let currentHtml = renderTree(root);
|
|
309
289
|
while (currentHtml.length < maxBudget) {
|
|
310
290
|
const expandable = getExpandableNodes(root);
|
|
311
|
-
if (expandable.length === 0)
|
|
291
|
+
if (expandable.length === 0)
|
|
312
292
|
break;
|
|
313
|
-
}
|
|
314
293
|
let expanded = false;
|
|
315
294
|
for (const node of expandable) {
|
|
316
295
|
node.expanded = true;
|
|
@@ -323,16 +302,14 @@ const elementInspect = (0, import_tool.defineTool)({
|
|
|
323
302
|
node.expanded = false;
|
|
324
303
|
}
|
|
325
304
|
}
|
|
326
|
-
if (!expanded)
|
|
305
|
+
if (!expanded)
|
|
327
306
|
break;
|
|
328
|
-
}
|
|
329
307
|
}
|
|
330
308
|
return currentHtml;
|
|
331
309
|
}
|
|
332
310
|
return {
|
|
333
311
|
outerHTML: element.outerHTML,
|
|
334
312
|
truncatedHTML: truncateIterativeDeepening(element, 300),
|
|
335
|
-
pathToRoot: path.join(" > "),
|
|
336
313
|
tagName: element.tagName.toLowerCase(),
|
|
337
314
|
computedStyles: {
|
|
338
315
|
display: computedStyle.display,
|
|
@@ -354,10 +331,12 @@ const elementInspect = (0, import_tool.defineTool)({
|
|
|
354
331
|
zIndex: computedStyle.zIndex,
|
|
355
332
|
transform: computedStyle.transform
|
|
356
333
|
},
|
|
357
|
-
attributes:
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
334
|
+
attributes: (() => {
|
|
335
|
+
const attrs = {};
|
|
336
|
+
for (const attr of Array.from(element.attributes))
|
|
337
|
+
attrs[attr.name] = attr.value;
|
|
338
|
+
return attrs;
|
|
339
|
+
})(),
|
|
361
340
|
textContent: element.textContent?.trim() || "",
|
|
362
341
|
innerText: element.innerText?.trim() || "",
|
|
363
342
|
value: element.value || void 0,
|
|
@@ -367,6 +346,7 @@ const elementInspect = (0, import_tool.defineTool)({
|
|
|
367
346
|
bounds: element.getBoundingClientRect()
|
|
368
347
|
};
|
|
369
348
|
});
|
|
349
|
+
elementDetails.pathToRoot = pathToRoot;
|
|
370
350
|
} catch (error) {
|
|
371
351
|
response.addResult(`Error inspecting element "${params.element}": ${error instanceof Error ? error.message : String(error)}
|
|
372
352
|
|
|
@@ -403,22 +383,18 @@ ${elementDetails.truncatedHTML}
|
|
|
403
383
|
- Tag: ${elementDetails.tagName}
|
|
404
384
|
- Text Content: "${elementDetails.textContent}"
|
|
405
385
|
- Inner Text: "${elementDetails.innerText}"`;
|
|
406
|
-
if (elementDetails.value !== void 0)
|
|
386
|
+
if (elementDetails.value !== void 0)
|
|
407
387
|
result += `
|
|
408
388
|
- Value: "${elementDetails.value}"`;
|
|
409
|
-
|
|
410
|
-
if (elementDetails.checked !== void 0) {
|
|
389
|
+
if (elementDetails.checked !== void 0)
|
|
411
390
|
result += `
|
|
412
391
|
- Checked: ${elementDetails.checked}`;
|
|
413
|
-
|
|
414
|
-
if (elementDetails.disabled !== void 0) {
|
|
392
|
+
if (elementDetails.disabled !== void 0)
|
|
415
393
|
result += `
|
|
416
394
|
- Disabled: ${elementDetails.disabled}`;
|
|
417
|
-
|
|
418
|
-
if (elementDetails.readonly !== void 0) {
|
|
395
|
+
if (elementDetails.readonly !== void 0)
|
|
419
396
|
result += `
|
|
420
397
|
- Read Only: ${elementDetails.readonly}`;
|
|
421
|
-
}
|
|
422
398
|
response.addResult(result);
|
|
423
399
|
}
|
|
424
400
|
});
|
package/lib/mcp/sdk/http.js
CHANGED
|
@@ -241,16 +241,14 @@ function configFromInitRequest(body) {
|
|
|
241
241
|
for (const [snakeKey, camelKey] of Object.entries(keyMap)) {
|
|
242
242
|
if (playwrightConfig[snakeKey] !== void 0) {
|
|
243
243
|
let value = playwrightConfig[snakeKey];
|
|
244
|
-
if (snakeKey === "no_sandbox")
|
|
244
|
+
if (snakeKey === "no_sandbox")
|
|
245
245
|
value = !value;
|
|
246
|
-
}
|
|
247
246
|
options[camelKey] = value;
|
|
248
247
|
}
|
|
249
248
|
}
|
|
250
249
|
for (const key of Object.keys(playwrightConfig)) {
|
|
251
|
-
if (!key.includes("_") && options[key] === void 0)
|
|
250
|
+
if (!key.includes("_") && options[key] === void 0)
|
|
252
251
|
options[key] = playwrightConfig[key];
|
|
253
|
-
}
|
|
254
252
|
}
|
|
255
253
|
testDebug("Converted to CLIOptions:", options);
|
|
256
254
|
return (0, import_config.configFromCLIOptions)(options);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@one2x/playwright",
|
|
3
|
-
"version": "1.57.0-alpha.
|
|
3
|
+
"version": "1.57.0-alpha.14",
|
|
4
4
|
"description": "A high-level API to automate web browsers",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"license": "Apache-2.0",
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"content-type": "^1.0.5",
|
|
68
|
-
"playwright-core": "npm:@one2x/playwright-core@1.57.0-alpha.
|
|
68
|
+
"playwright-core": "npm:@one2x/playwright-core@1.57.0-alpha.14",
|
|
69
69
|
"raw-body": "^2.5.2"
|
|
70
70
|
},
|
|
71
71
|
"optionalDependencies": {
|