@progress/kendo-e2e 4.12.1 → 4.14.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 +76 -0
- package/dist/mcp/index.js +444 -0
- package/dist/mcp/package.json +1 -0
- package/dist/selenium/browser.d.ts +8 -0
- package/dist/selenium/browser.js +9 -1
- package/dist/selenium/browser.js.map +1 -1
- package/dist/selenium/driver-manager.d.ts +2 -0
- package/dist/selenium/driver-manager.js +7 -1
- package/dist/selenium/driver-manager.js.map +1 -1
- package/dist/settings/settings.d.ts +1 -0
- package/dist/settings/settings.js +4 -0
- package/dist/settings/settings.js.map +1 -1
- package/package.json +8 -6
package/README.md
CHANGED
|
@@ -50,3 +50,79 @@ Key points:
|
|
|
50
50
|
- All interactions have automatic waiting built-in
|
|
51
51
|
- Avoid Page Object pattern for simple component tests
|
|
52
52
|
```
|
|
53
|
+
|
|
54
|
+
### AI/MCP Server
|
|
55
|
+
|
|
56
|
+
kendo-e2e includes a **Model Context Protocol (MCP) server** that enables AI assistants (like Claude Desktop or GitHub Copilot) to directly interact with browsers for intelligent test generation.
|
|
57
|
+
|
|
58
|
+
#### Quick Setup
|
|
59
|
+
|
|
60
|
+
Add to your MCP settings (Claude Desktop or VS Code):
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"mcpServers": {
|
|
65
|
+
"kendo-e2e": {
|
|
66
|
+
"command": "npx",
|
|
67
|
+
"args": ["-y", "@progress/kendo-e2e/dist/mcp/index.js"]
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Or run directly:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npx kendo-e2e-mcp
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### What It Enables
|
|
80
|
+
|
|
81
|
+
The MCP server provides AI assistants with 8 powerful tools:
|
|
82
|
+
|
|
83
|
+
- **Browser control** - Navigate, execute scripts, manage sessions
|
|
84
|
+
- **Smart DOM snapshots** - Get filtered page structure with Kendo component detection
|
|
85
|
+
- **Selector testing** - Validate CSS/XPath selectors before generating code
|
|
86
|
+
- **Element interactions** - Click, type, hover with automatic waiting
|
|
87
|
+
- **Element queries** - Check visibility, text, attributes
|
|
88
|
+
|
|
89
|
+
#### AI-Powered Workflow
|
|
90
|
+
|
|
91
|
+
1. **AI navigates** to your application
|
|
92
|
+
2. **AI analyzes** page structure using DOM snapshots
|
|
93
|
+
3. **AI tests** selectors to find stable locators
|
|
94
|
+
4. **AI generates** kendo-e2e test code
|
|
95
|
+
5. **You run** the generated tests
|
|
96
|
+
|
|
97
|
+
#### Example
|
|
98
|
+
|
|
99
|
+
Ask your AI assistant: *"Generate a test for the Kendo Grid filtering on https://demos.telerik.com/kendo-ui/grid"*
|
|
100
|
+
|
|
101
|
+
The AI will:
|
|
102
|
+
1. Navigate to the page
|
|
103
|
+
2. Capture DOM structure (detecting Kendo widgets via `data-role` attributes)
|
|
104
|
+
3. Test selectors to find reliable locators
|
|
105
|
+
4. Generate clean kendo-e2e test code:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { Browser } from '@progress/kendo-e2e';
|
|
109
|
+
|
|
110
|
+
test('should filter grid', async () => {
|
|
111
|
+
const browser = new Browser();
|
|
112
|
+
await browser.navigateTo('https://demos.telerik.com/kendo-ui/grid');
|
|
113
|
+
await browser.click('.k-grid-header .k-filterable');
|
|
114
|
+
await browser.type('.k-filter-menu input', 'John');
|
|
115
|
+
await browser.click('.k-filter-menu .k-primary');
|
|
116
|
+
await browser.expect('.k-grid tbody tr').toHaveCount(5);
|
|
117
|
+
await browser.close();
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### Benefits
|
|
122
|
+
|
|
123
|
+
- ✅ **Context-aware** - AI understands your page structure
|
|
124
|
+
- ✅ **Kendo-optimized** - Detects Kendo widgets automatically
|
|
125
|
+
- ✅ **Fast iteration** - Test and refine selectors before generating code
|
|
126
|
+
- ✅ **No manual exploration** - AI discovers elements for you
|
|
127
|
+
|
|
128
|
+
See [MCP Server Documentation](./src/mcp/README.md) for detailed tool reference.
|
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/mcp/standalone.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { createRequire } from "module";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
var require2 = createRequire(import.meta.url);
|
|
9
|
+
var { Browser } = require2("../selenium/browser.js");
|
|
10
|
+
function mcpResult(payload, summary) {
|
|
11
|
+
const parts = [];
|
|
12
|
+
if (summary) parts.push({ type: "text", text: summary });
|
|
13
|
+
parts.push({ type: "text", text: JSON.stringify(payload) });
|
|
14
|
+
return { content: parts };
|
|
15
|
+
}
|
|
16
|
+
var browsers = /* @__PURE__ */ new Map();
|
|
17
|
+
var sessionCounter = 0;
|
|
18
|
+
var server = new McpServer({
|
|
19
|
+
name: "kendo-e2e-mcp",
|
|
20
|
+
version: "1.0.0",
|
|
21
|
+
description: "Kendo E2E MCP: Browser automation for AI-powered test generation. Provides: (1) Browser lifecycle, (2) DOM snapshot and selector testing, (3) Element interactions with automatic waiting."
|
|
22
|
+
});
|
|
23
|
+
server.tool(
|
|
24
|
+
"browser-navigate",
|
|
25
|
+
"Navigate browser to URL. Auto-starts new browser if no session exists. LLM: Use this as first step to interact with a page.",
|
|
26
|
+
{
|
|
27
|
+
url: z.string().url().describe("URL to navigate to"),
|
|
28
|
+
sessionId: z.string().optional().describe("Existing session (optional)"),
|
|
29
|
+
mobileEmulation: z.object({ deviceName: z.string().optional() }).optional()
|
|
30
|
+
},
|
|
31
|
+
async ({ url, sessionId, mobileEmulation }) => {
|
|
32
|
+
let browser;
|
|
33
|
+
let isNewSession = false;
|
|
34
|
+
if (sessionId && browsers.has(sessionId)) {
|
|
35
|
+
browser = browsers.get(sessionId);
|
|
36
|
+
} else {
|
|
37
|
+
sessionId = `session-${++sessionCounter}`;
|
|
38
|
+
browser = new Browser(mobileEmulation ? { mobileEmulation } : void 0);
|
|
39
|
+
browsers.set(sessionId, browser);
|
|
40
|
+
isNewSession = true;
|
|
41
|
+
}
|
|
42
|
+
await browser.navigateTo(url);
|
|
43
|
+
return mcpResult(
|
|
44
|
+
{ sessionId, url, newSession: isNewSession },
|
|
45
|
+
isNewSession ? `Started session ${sessionId} and navigated to ${url}` : `Navigated ${sessionId} to ${url}`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
server.tool(
|
|
50
|
+
"browser-close",
|
|
51
|
+
"Close browser session. LLM: Call when done.",
|
|
52
|
+
{
|
|
53
|
+
sessionId: z.string().optional()
|
|
54
|
+
},
|
|
55
|
+
async ({ sessionId }) => {
|
|
56
|
+
if (sessionId) {
|
|
57
|
+
const browser = browsers.get(sessionId);
|
|
58
|
+
if (!browser) throw new Error(`Session not found: ${sessionId}`);
|
|
59
|
+
await browser.close();
|
|
60
|
+
browsers.delete(sessionId);
|
|
61
|
+
return mcpResult({ sessionId, closed: true }, `Closed ${sessionId}`);
|
|
62
|
+
} else {
|
|
63
|
+
for (const [id, browser] of browsers) {
|
|
64
|
+
await browser.close().catch(() => {
|
|
65
|
+
});
|
|
66
|
+
browsers.delete(id);
|
|
67
|
+
}
|
|
68
|
+
return mcpResult({ closedAll: true }, "Closed all sessions");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
server.tool(
|
|
73
|
+
"browser-execute-script",
|
|
74
|
+
"Execute JavaScript in browser. Use for complex operations.",
|
|
75
|
+
{
|
|
76
|
+
sessionId: z.string().optional(),
|
|
77
|
+
script: z.string().describe("JavaScript code with 'return' statement"),
|
|
78
|
+
args: z.array(z.any()).optional().default([])
|
|
79
|
+
},
|
|
80
|
+
async ({ sessionId, script, args = [] }) => {
|
|
81
|
+
const browser = sessionId ? browsers.get(sessionId) : Array.from(browsers.values())[0];
|
|
82
|
+
if (!browser) throw new Error("No browser session");
|
|
83
|
+
const result = await browser.driver.executeScript(script, ...args);
|
|
84
|
+
return mcpResult({ sessionId, result }, `Executed script`);
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
var domSnapshotScript = `
|
|
88
|
+
const opts = arguments[0] || {};
|
|
89
|
+
const rootSelector = opts.rootSelector;
|
|
90
|
+
const filteredTags = new Set(["script", "style", "link", "meta", "noscript"]);
|
|
91
|
+
const norm = (s) => (s ?? "").replace(/\\s+/g, " ").trim();
|
|
92
|
+
|
|
93
|
+
function snapElement(el) {
|
|
94
|
+
const tag = el.tagName ? el.tagName.toLowerCase() : "";
|
|
95
|
+
if (filteredTags.has(tag)) return null;
|
|
96
|
+
|
|
97
|
+
const id = el.id || undefined;
|
|
98
|
+
const classes = el.classList ? Array.from(el.classList) : [];
|
|
99
|
+
const role = el.getAttribute?.("role") || undefined;
|
|
100
|
+
const dataRole = el.getAttribute?.("data-role") || undefined;
|
|
101
|
+
const type = (tag === "input" || tag === "button") && el.type ? String(el.type) : undefined;
|
|
102
|
+
|
|
103
|
+
const aria = {};
|
|
104
|
+
if (el.hasAttributes?.()) {
|
|
105
|
+
for (const attr of Array.from(el.attributes)) {
|
|
106
|
+
if (attr.name?.startsWith("aria-")) aria[attr.name] = attr.value;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let hidden = false;
|
|
111
|
+
let frame;
|
|
112
|
+
try {
|
|
113
|
+
const style = window.getComputedStyle?.(el);
|
|
114
|
+
if (style) {
|
|
115
|
+
hidden = style.display === "none" || style.visibility === "hidden" ||
|
|
116
|
+
parseFloat(style.opacity || "1") === 0 || el.offsetParent === null;
|
|
117
|
+
}
|
|
118
|
+
if (!hidden) {
|
|
119
|
+
const rect = el.getBoundingClientRect();
|
|
120
|
+
if (rect && (rect.width > 0 || rect.height > 0)) {
|
|
121
|
+
frame = [
|
|
122
|
+
Math.round(rect.left + window.scrollX),
|
|
123
|
+
Math.round(rect.top + window.scrollY),
|
|
124
|
+
Math.round(rect.width),
|
|
125
|
+
Math.round(rect.height)
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
} catch (e) {}
|
|
130
|
+
|
|
131
|
+
const content = [];
|
|
132
|
+
if (el.childNodes) {
|
|
133
|
+
for (const child of Array.from(el.childNodes)) {
|
|
134
|
+
if (child.nodeType === 3) {
|
|
135
|
+
const text = norm(child.textContent);
|
|
136
|
+
if (text) content.push(text);
|
|
137
|
+
} else if (child.nodeType === 1) {
|
|
138
|
+
const childEl = snapElement(child);
|
|
139
|
+
if (childEl) content.push(childEl);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
element: tag,
|
|
146
|
+
...(id ? { id } : {}),
|
|
147
|
+
...(classes.length ? { classes } : {}),
|
|
148
|
+
...(role ? { role } : {}),
|
|
149
|
+
...(dataRole ? { dataRole } : {}),
|
|
150
|
+
...(type ? { type } : {}),
|
|
151
|
+
...(Object.keys(aria).length ? { aria } : {}),
|
|
152
|
+
...(frame ? { frame } : {}),
|
|
153
|
+
...(hidden ? { hidden } : {}),
|
|
154
|
+
...(content.length ? { content } : {})
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const root = rootSelector ? document.querySelector(rootSelector) : document.body;
|
|
159
|
+
if (!root) return { error: "root-not-found" };
|
|
160
|
+
return snapElement(root);
|
|
161
|
+
`;
|
|
162
|
+
function treeToHtml(node, indent = 0) {
|
|
163
|
+
if (!node) return "";
|
|
164
|
+
if (typeof node === "string") return node;
|
|
165
|
+
const spaces = " ".repeat(indent);
|
|
166
|
+
const tag = node.element || "unknown";
|
|
167
|
+
const attrs = [];
|
|
168
|
+
if (node.id) attrs.push(`id="${node.id}"`);
|
|
169
|
+
if (node.classes?.length) attrs.push(`class="${node.classes.join(" ")}"`);
|
|
170
|
+
if (node.role) attrs.push(`role="${node.role}"`);
|
|
171
|
+
if (node.dataRole) attrs.push(`data-role="${node.dataRole}"`);
|
|
172
|
+
if (node.type) attrs.push(`type="${node.type}"`);
|
|
173
|
+
if (node.frame) attrs.push(`frame="${node.frame.join(" ")}"`);
|
|
174
|
+
if (node.hidden) attrs.push(`hidden`);
|
|
175
|
+
if (node.aria) {
|
|
176
|
+
for (const [key, value] of Object.entries(node.aria)) {
|
|
177
|
+
attrs.push(`${key}="${value}"`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const attrStr = attrs.length > 0 ? " " + attrs.join(" ") : "";
|
|
181
|
+
const voidTags = /* @__PURE__ */ new Set(["br", "hr", "img", "input"]);
|
|
182
|
+
if (voidTags.has(tag)) return `${spaces}<${tag}${attrStr}>`;
|
|
183
|
+
if (!node.content?.length) return `${spaces}<${tag}${attrStr}/>`;
|
|
184
|
+
let result = `${spaces}<${tag}${attrStr}>`;
|
|
185
|
+
const hasElements = node.content.some((c) => typeof c !== "string");
|
|
186
|
+
if (hasElements) {
|
|
187
|
+
result += "\n";
|
|
188
|
+
for (const item of node.content) result += treeToHtml(item, indent + 1) + "\n";
|
|
189
|
+
result += spaces;
|
|
190
|
+
} else {
|
|
191
|
+
result += node.content.join("");
|
|
192
|
+
}
|
|
193
|
+
return result + `</${tag}>`;
|
|
194
|
+
}
|
|
195
|
+
server.tool(
|
|
196
|
+
"dom-snapshot",
|
|
197
|
+
"Get filtered DOM tree with positioning, visibility, semantic info. Elements include: tag, id, classes, role, data-role (Kendo), aria, frame [x,y,w,h], hidden. LLM: Use to understand page structure and identify selectors.",
|
|
198
|
+
{
|
|
199
|
+
sessionId: z.string().optional(),
|
|
200
|
+
rootSelector: z.string().optional(),
|
|
201
|
+
format: z.enum(["html", "json"]).default("html"),
|
|
202
|
+
includeScreenshot: z.boolean().default(false)
|
|
203
|
+
},
|
|
204
|
+
async ({ sessionId, rootSelector, format = "html", includeScreenshot = false }) => {
|
|
205
|
+
const browser = sessionId ? browsers.get(sessionId) : Array.from(browsers.values())[0];
|
|
206
|
+
if (!browser) throw new Error("No browser session");
|
|
207
|
+
const tree = await browser.driver.executeScript(domSnapshotScript, { rootSelector });
|
|
208
|
+
let screenshot;
|
|
209
|
+
if (includeScreenshot) {
|
|
210
|
+
try {
|
|
211
|
+
screenshot = await browser.driver.takeScreenshot();
|
|
212
|
+
} catch {
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (tree.error) return { content: [{ type: "text", text: `Error: ${tree.error}` }] };
|
|
216
|
+
if (format === "html") {
|
|
217
|
+
const html = treeToHtml(tree);
|
|
218
|
+
const content = [{ type: "text", text: "DOM snapshot:" }, { type: "text", text: html }];
|
|
219
|
+
if (screenshot) content.push({ type: "image", data: screenshot, mimeType: "image/png" });
|
|
220
|
+
return { content };
|
|
221
|
+
} else {
|
|
222
|
+
const result = mcpResult({ tree, rootSelector }, "DOM snapshot (JSON)");
|
|
223
|
+
if (screenshot) result.content.push({ type: "image", data: screenshot, mimeType: "image/png" });
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
);
|
|
228
|
+
var domSelectorScript = `
|
|
229
|
+
const selector = arguments[0];
|
|
230
|
+
const selectorType = arguments[1];
|
|
231
|
+
const isXPath = selectorType === "xpath" || (!selectorType && (
|
|
232
|
+
selector.includes("/") || selector.startsWith("//") || selector.includes("[@")
|
|
233
|
+
));
|
|
234
|
+
|
|
235
|
+
let matched = [];
|
|
236
|
+
try {
|
|
237
|
+
if (isXPath) {
|
|
238
|
+
const result = document.evaluate(selector, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
|
239
|
+
for (let i = 0; i < result.snapshotLength; i++) {
|
|
240
|
+
const node = result.snapshotItem(i);
|
|
241
|
+
if (node?.nodeType === 1) matched.push(node);
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
matched = Array.from(document.querySelectorAll(selector));
|
|
245
|
+
}
|
|
246
|
+
} catch (e) {
|
|
247
|
+
return { error: String(e), selector };
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
selector,
|
|
252
|
+
selectorType: isXPath ? "xpath" : "css",
|
|
253
|
+
matchCount: matched.length,
|
|
254
|
+
matches: matched.slice(0, 10).map((el) => ({
|
|
255
|
+
tag: el.tagName.toLowerCase(),
|
|
256
|
+
id: el.id || undefined,
|
|
257
|
+
classes: el.classList ? Array.from(el.classList) : [],
|
|
258
|
+
text: el.textContent?.replace(/\\s+/g, " ").trim().substring(0, 100)
|
|
259
|
+
}))
|
|
260
|
+
};
|
|
261
|
+
`;
|
|
262
|
+
server.tool(
|
|
263
|
+
"dom-test-selector",
|
|
264
|
+
"Test CSS/XPath selector, return match count and details. Auto-detects type. LLM: Use to validate selectors before generating tests.",
|
|
265
|
+
{
|
|
266
|
+
sessionId: z.string().optional(),
|
|
267
|
+
selector: z.string(),
|
|
268
|
+
selectorType: z.enum(["css", "xpath"]).optional()
|
|
269
|
+
},
|
|
270
|
+
async ({ sessionId, selector, selectorType }) => {
|
|
271
|
+
const browser = sessionId ? browsers.get(sessionId) : Array.from(browsers.values())[0];
|
|
272
|
+
if (!browser) throw new Error("No browser session");
|
|
273
|
+
const result = await browser.driver.executeScript(domSelectorScript, selector, selectorType);
|
|
274
|
+
if (result.error) return mcpResult({ error: result.error, selector }, `Error: ${result.error}`);
|
|
275
|
+
return mcpResult(result, `${result.selectorType.toUpperCase()} selector "${selector}" matched ${result.matchCount} elements`);
|
|
276
|
+
}
|
|
277
|
+
);
|
|
278
|
+
server.tool(
|
|
279
|
+
"dom-page-info",
|
|
280
|
+
"Get page context: title, URL, viewport, readyState, element counts. LLM: Quick page context without full DOM.",
|
|
281
|
+
{
|
|
282
|
+
sessionId: z.string().optional()
|
|
283
|
+
},
|
|
284
|
+
async ({ sessionId }) => {
|
|
285
|
+
const browser = sessionId ? browsers.get(sessionId) : Array.from(browsers.values())[0];
|
|
286
|
+
if (!browser) throw new Error("No browser session");
|
|
287
|
+
const info = await browser.driver.executeScript(`
|
|
288
|
+
return {
|
|
289
|
+
title: document.title,
|
|
290
|
+
url: window.location.href,
|
|
291
|
+
readyState: document.readyState,
|
|
292
|
+
viewport: { width: window.innerWidth, height: window.innerHeight },
|
|
293
|
+
counts: {
|
|
294
|
+
forms: document.forms?.length || 0,
|
|
295
|
+
links: document.links?.length || 0,
|
|
296
|
+
images: document.images?.length || 0
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
`);
|
|
300
|
+
return mcpResult({ info }, `Page: "${info.title}"`);
|
|
301
|
+
}
|
|
302
|
+
);
|
|
303
|
+
server.tool(
|
|
304
|
+
"element-interact",
|
|
305
|
+
"Interact with element: click, type, clear, hover, scrollIntoView. Uses automatic waiting. LLM: Use for all element interactions.",
|
|
306
|
+
{
|
|
307
|
+
sessionId: z.string().optional(),
|
|
308
|
+
selector: z.string(),
|
|
309
|
+
action: z.enum(["click", "type", "clear", "hover", "scrollIntoView"]),
|
|
310
|
+
value: z.string().optional(),
|
|
311
|
+
timeout: z.number().optional().default(1e4)
|
|
312
|
+
},
|
|
313
|
+
async ({ sessionId, selector, action, value, timeout = 1e4 }) => {
|
|
314
|
+
const browser = sessionId ? browsers.get(sessionId) : Array.from(browsers.values())[0];
|
|
315
|
+
if (!browser) throw new Error("No browser session");
|
|
316
|
+
try {
|
|
317
|
+
switch (action) {
|
|
318
|
+
case "click":
|
|
319
|
+
await browser.click(selector, { timeout });
|
|
320
|
+
return mcpResult({ selector, action }, `Clicked ${selector}`);
|
|
321
|
+
case "type":
|
|
322
|
+
if (!value) throw new Error("'value' required for type");
|
|
323
|
+
await browser.type(selector, value, { timeout });
|
|
324
|
+
return mcpResult({ selector, action, value }, `Typed "${value}" into ${selector}`);
|
|
325
|
+
case "clear":
|
|
326
|
+
await browser.clear(selector, { timeout });
|
|
327
|
+
return mcpResult({ selector, action }, `Cleared ${selector}`);
|
|
328
|
+
case "hover":
|
|
329
|
+
await browser.hover(selector, { timeout });
|
|
330
|
+
return mcpResult({ selector, action }, `Hovered ${selector}`);
|
|
331
|
+
case "scrollIntoView":
|
|
332
|
+
const element = await browser.find(selector, { timeout });
|
|
333
|
+
await browser.driver.executeScript("arguments[0].scrollIntoView(true);", element);
|
|
334
|
+
return mcpResult({ selector, action }, `Scrolled ${selector} into view`);
|
|
335
|
+
}
|
|
336
|
+
} catch (error) {
|
|
337
|
+
return mcpResult({ selector, action, error: error.message }, `Failed: ${error.message}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
);
|
|
341
|
+
server.tool(
|
|
342
|
+
"element-find",
|
|
343
|
+
"Find element(s) and get properties: text, attributes, visibility, enabled. LLM: Query element state before interaction.",
|
|
344
|
+
{
|
|
345
|
+
sessionId: z.string().optional(),
|
|
346
|
+
selector: z.string(),
|
|
347
|
+
multiple: z.boolean().optional().default(false),
|
|
348
|
+
properties: z.array(z.enum(["text", "enabled", "visible", "tag", "id", "classes"])).optional().default(["text", "visible"]),
|
|
349
|
+
attributes: z.array(z.string()).optional().default([]),
|
|
350
|
+
timeout: z.number().optional().default(1e4)
|
|
351
|
+
},
|
|
352
|
+
async ({ sessionId, selector, multiple = false, properties = ["text", "visible"], attributes = [], timeout = 1e4 }) => {
|
|
353
|
+
const browser = sessionId ? browsers.get(sessionId) : Array.from(browsers.values())[0];
|
|
354
|
+
if (!browser) throw new Error("No browser session");
|
|
355
|
+
try {
|
|
356
|
+
if (multiple) {
|
|
357
|
+
const elements = await browser.findAll(selector);
|
|
358
|
+
const results = [];
|
|
359
|
+
for (const el of elements.slice(0, 20)) {
|
|
360
|
+
const info = {};
|
|
361
|
+
for (const prop of properties) {
|
|
362
|
+
switch (prop) {
|
|
363
|
+
case "text":
|
|
364
|
+
info.text = await el.getText();
|
|
365
|
+
break;
|
|
366
|
+
case "enabled":
|
|
367
|
+
info.enabled = await el.isEnabled();
|
|
368
|
+
break;
|
|
369
|
+
case "visible":
|
|
370
|
+
info.visible = await el.isDisplayed();
|
|
371
|
+
break;
|
|
372
|
+
case "tag":
|
|
373
|
+
info.tag = await el.getTagName();
|
|
374
|
+
break;
|
|
375
|
+
case "id":
|
|
376
|
+
info.id = await el.getAttribute("id");
|
|
377
|
+
break;
|
|
378
|
+
case "classes":
|
|
379
|
+
const cls = await el.getAttribute("class");
|
|
380
|
+
info.classes = cls ? cls.split(/\s+/) : [];
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
for (const attr of attributes) info[attr] = await el.getAttribute(attr);
|
|
385
|
+
results.push(info);
|
|
386
|
+
}
|
|
387
|
+
return mcpResult(
|
|
388
|
+
{ selector, count: elements.length, elements: results },
|
|
389
|
+
`Found ${elements.length} elements`
|
|
390
|
+
);
|
|
391
|
+
} else {
|
|
392
|
+
const el = await browser.find(selector, { timeout });
|
|
393
|
+
const info = {};
|
|
394
|
+
for (const prop of properties) {
|
|
395
|
+
switch (prop) {
|
|
396
|
+
case "text":
|
|
397
|
+
info.text = await el.getText();
|
|
398
|
+
break;
|
|
399
|
+
case "enabled":
|
|
400
|
+
info.enabled = await el.isEnabled();
|
|
401
|
+
break;
|
|
402
|
+
case "visible":
|
|
403
|
+
info.visible = await el.isDisplayed();
|
|
404
|
+
break;
|
|
405
|
+
case "tag":
|
|
406
|
+
info.tag = await el.getTagName();
|
|
407
|
+
break;
|
|
408
|
+
case "id":
|
|
409
|
+
info.id = await el.getAttribute("id");
|
|
410
|
+
break;
|
|
411
|
+
case "classes":
|
|
412
|
+
const cls = await el.getAttribute("class");
|
|
413
|
+
info.classes = cls ? cls.split(/\s+/) : [];
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
for (const attr of attributes) info[attr] = await el.getAttribute(attr);
|
|
418
|
+
return mcpResult({ selector, element: info }, `Found ${selector}`);
|
|
419
|
+
}
|
|
420
|
+
} catch (error) {
|
|
421
|
+
return mcpResult({ selector, error: error.message }, `Failed: ${error.message}`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
);
|
|
425
|
+
var transport = new StdioServerTransport();
|
|
426
|
+
var cleanUp = async () => {
|
|
427
|
+
for (const [id, browser] of browsers) {
|
|
428
|
+
try {
|
|
429
|
+
await browser.close();
|
|
430
|
+
} catch {
|
|
431
|
+
}
|
|
432
|
+
browsers.delete(id);
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
transport.onclose = cleanUp;
|
|
436
|
+
process.on("SIGINT", async () => {
|
|
437
|
+
await cleanUp();
|
|
438
|
+
process.exit(0);
|
|
439
|
+
});
|
|
440
|
+
process.on("SIGTERM", async () => {
|
|
441
|
+
await cleanUp();
|
|
442
|
+
process.exit(0);
|
|
443
|
+
});
|
|
444
|
+
await server.connect(transport);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -12,6 +12,7 @@ interface BrowserOptions {
|
|
|
12
12
|
pixelRatio: number;
|
|
13
13
|
} | any;
|
|
14
14
|
enableBidi?: boolean;
|
|
15
|
+
chromeArguments?: string[];
|
|
15
16
|
}
|
|
16
17
|
/**
|
|
17
18
|
* Browser automation class with automatic waiting and modern web testing features.
|
|
@@ -96,6 +97,13 @@ export declare class Browser extends WebApp {
|
|
|
96
97
|
* const browser = new Browser({ mobileEmulation: { deviceName: "iPhone 14 Pro Max" }, enableBidi: true });
|
|
97
98
|
* ```
|
|
98
99
|
*
|
|
100
|
+
* __Example 6: Passing Custom Chrome Arguments__
|
|
101
|
+
* ```typescript
|
|
102
|
+
* const browser = new Browser({
|
|
103
|
+
* chromeArguments: ['--allow-file-access-from-files', '--disable-web-security']
|
|
104
|
+
* });
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
99
107
|
* [em]: https://chromedriver.chromium.org/mobile-emulation
|
|
100
108
|
* [devem]: https://developer.chrome.com/devtools/docs/device-mode
|
|
101
109
|
*/
|
package/dist/selenium/browser.js
CHANGED
|
@@ -110,6 +110,13 @@ class Browser extends web_app_1.WebApp {
|
|
|
110
110
|
* const browser = new Browser({ mobileEmulation: { deviceName: "iPhone 14 Pro Max" }, enableBidi: true });
|
|
111
111
|
* ```
|
|
112
112
|
*
|
|
113
|
+
* __Example 6: Passing Custom Chrome Arguments__
|
|
114
|
+
* ```typescript
|
|
115
|
+
* const browser = new Browser({
|
|
116
|
+
* chromeArguments: ['--allow-file-access-from-files', '--disable-web-security']
|
|
117
|
+
* });
|
|
118
|
+
* ```
|
|
119
|
+
*
|
|
113
120
|
* [em]: https://chromedriver.chromium.org/mobile-emulation
|
|
114
121
|
* [devem]: https://developer.chrome.com/devtools/docs/device-mode
|
|
115
122
|
*/
|
|
@@ -130,7 +137,8 @@ class Browser extends web_app_1.WebApp {
|
|
|
130
137
|
}
|
|
131
138
|
driver = (_a = options.driver) !== null && _a !== void 0 ? _a : new driver_manager_1.DriverManager().getDriver({
|
|
132
139
|
mobileEmulation: options.mobileEmulation,
|
|
133
|
-
enableBidi: options.enableBidi
|
|
140
|
+
enableBidi: options.enableBidi,
|
|
141
|
+
chromeArguments: options.chromeArguments
|
|
134
142
|
});
|
|
135
143
|
}
|
|
136
144
|
super(driver);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/selenium/browser.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,wEAA+C;AAC/C,2DAAuE;AACvE,4DAA6D;AAC7D,qDAAiD;AACjD,uCAAmC;AAEnC,yDAAwG;AAA/F,wGAAA,EAAE,OAAA;AAAE,yGAAA,GAAG,OAAA;AAAqB,2GAAA,KAAK,OAAA;AAAE,gHAAA,UAAU,OAAA;AAAE,yHAAA,mBAAmB,OAAA;
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/selenium/browser.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,wEAA+C;AAC/C,2DAAuE;AACvE,4DAA6D;AAC7D,qDAAiD;AACjD,uCAAmC;AAEnC,yDAAwG;AAA/F,wGAAA,EAAE,OAAA;AAAE,yGAAA,GAAG,OAAA;AAAqB,2GAAA,KAAK,OAAA;AAAE,gHAAA,UAAU,OAAA;AAAE,yHAAA,mBAAmB,OAAA;AAS3E,SAAS,QAAQ,CAAC,GAAQ;IACtB,OAAO,GAAG,IAAI,OAAO,GAAG,CAAC,UAAU,KAAK,UAAU,CAAC;AACvD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,MAAa,OAAQ,SAAQ,gBAAM;IAC/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiDG;IACH,YACI,eAAoD,EACpD,eAAsG,EACtG,UAAoB;;QAEpB,IAAI,MAAyB,CAAC;QAE9B,gEAAgE;QAChE,IAAI,eAAe,IAAI,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;YAC/C,MAAM,GAAG,eAAe,CAAC;QAC7B,CAAC;aAAM,CAAC;YACJ,MAAM,OAAO,GAAoB,eAAkC,IAAI,EAAE,CAAC;YAE1E,IAAI,eAAe,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;gBAC9C,OAAO,CAAC,eAAe,GAAG,eAAe,CAAC;YAC9C,CAAC;YACD,IAAI,UAAU,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;gBACjD,OAAO,CAAC,UAAU,GAAG,UAAU,CAAC;YACpC,CAAC;YAED,MAAM,GAAG,MAAA,OAAO,CAAC,MAAM,mCAAI,IAAI,8BAAa,EAAE,CAAC,SAAS,CAAC;gBACrD,eAAe,EAAE,OAAO,CAAC,eAAe;gBACxC,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,eAAe,EAAE,OAAO,CAAC,eAAe;aAC3C,CAAC,CAAC;QACP,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACU,KAAK;;YACd,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAC7B,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACU,UAAU,CAAC,GAAW;;YAC/B,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;QACzC,CAAC;KAAA;IAEY,OAAO;;YAChB,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC;QACzD,CAAC;KAAA;IAEY,OAAO,CAAC,IAAiE;;;YAClF,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC;YAClE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC;gBAClC,KAAK,EAAE,MAAA,IAAI,CAAC,KAAK,mCAAI,WAAW,CAAC,KAAK;gBACtC,MAAM,EAAE,MAAA,IAAI,CAAC,MAAM,mCAAI,WAAW,CAAC,MAAM;gBACzC,CAAC,EAAE,MAAA,IAAI,CAAC,CAAC,mCAAI,WAAW,CAAC,CAAC;gBAC1B,CAAC,EAAE,MAAA,IAAI,CAAC,CAAC,mCAAI,WAAW,CAAC,CAAC;aAC7B,CAAC,CAAC;QACP,CAAC;KAAA;IAEY,4BAA4B;;YACrC,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YAC1C,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,2BAA2B,CAAW,CAAC;YAC9F,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,mCAAmC,CAAW,CAAC;YACtG,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,cAAc,GAAG,YAAY,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC,CAAC;QAC1F,CAAC;KAAA;IAED;;;;;;;;;;;;;;;OAeG;IACU,YAAY,CAAC,KAAa,EAAE,MAAc;;YACnD,IAAI,CAAC;gBACD,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YAC1C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,IAAI,MAAM,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAChI,CAAC;QACL,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACU,OAAO;;YAChB,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC;QAC3C,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACU,cAAc,CAAC,cAAkB;;YAC1C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAC/C,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC/C,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACU,aAAa;;YACtB,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;QAC7C,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACU,cAAc;;YACvB,MAAM,YAAY,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;YACnE,MAAM,WAAW,GAAG,YAAY,CAAC,cAAc,EAAE,CAAC,WAAW,EAAE,CAAC;YAChE,OAAO,WAAW,CAAC;QACvB,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACU,0BAA0B;6DAAC,WAAW,GAAG,MAAM,EAAE,YAAY,GAAG,CAAC,gBAAgB,CAAC;YAC3F,MAAM,IAAI,CAAC,IAAI,CAAC,uBAAE,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC;YACrC,MAAM,GAAG,GAAG,IAAI,qBAAU,CAAC,IAAI,CAAC,MAAM,CAAC;iBAClC,OAAO,CAAC,WAAW,CAAC;iBACpB,YAAY,CAAC,YAAY,CAAC,CAAC;YAChC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;YACnC,OAAO,MAAM,CAAC,UAAgB,CAAC;QACnC,CAAC;KAAA;IAED;;;;;;;;;;;;;;;OAeG;IACU,SAAS;;YAClB,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,cAAI,CAAC,OAAO,CAAC,CAAC;QACxD,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACU,YAAY;6DAAC,WAAW,GAAG,CAAC,aAAa,CAAC,EAAE,QAAQ,GAAG,eAAK,CAAC,MAAM;YAC5E,MAAM,MAAM,GAAG,EAAE,CAAC;YAClB,MAAM,YAAY,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;YACnE,MAAM,QAAQ,GAAG,YAAY,CAAC,WAAW,EAAE,CAAC,WAAW,EAAE,CAAC;YAC1D,MAAM,WAAW,GAAG,YAAY,CAAC,cAAc,EAAE,CAAC,WAAW,EAAE,CAAC;YAEhE,oCAAoC;YACpC,cAAc;YACd,2EAA2E;YAC3E,EAAE;YACF,wDAAwD;YACxD,IAAI,WAAW,KAAK,QAAQ,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAC9E,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,cAAI,CAAC,OAAO,CAAC,CAAC;gBACjE,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;oBACvB,6GAA6G;oBAC7G,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;wBACtC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;oBAC/B,CAAC;gBACL,CAAC;YACL,CAAC;YAED,gBAAgB;YAChB,IAAI,cAAc,GAAG,MAAM,CAAC;YAC5B,KAAK,MAAM,WAAW,IAAI,WAAW,EAAE,CAAC;gBACpC,cAAc,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,KAAa,EAAE,EAAE;oBACrD,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC;gBACtE,CAAC,CAAC,CAAC;YACP,CAAC;YAED,OAAO,cAAc,CAAC;QAC1B,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACU,aAAa;6DAAC,MAAc,EAAE,YAAY,GAAG,CAAC,EAAE,WAAW,GAAG,CAAC;YACxE,IAAI,CAAC;gBACD,qCAAqC;gBACrC,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;oBACnB,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;gBACpE,CAAC;gBAED,qBAAqB;gBACrB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;gBAEvD,oCAAoC;gBACpC,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;oBAClB,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;gBACnE,CAAC;gBAED,OAAO,MAAM,CAAC;YAClB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CAAC,wCAAwC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACtH,CAAC;QACL,CAAC;KAAA;CACJ;AA5bD,0BA4bC"}
|
|
@@ -17,6 +17,8 @@ export interface DriverOptions {
|
|
|
17
17
|
} | any;
|
|
18
18
|
/** Enable BiDi (Bidirectional communication) protocol */
|
|
19
19
|
enableBidi?: boolean;
|
|
20
|
+
/** Custom Chrome command-line arguments (e.g., ['--allow-file-access-from-files', '--disable-gpu']) */
|
|
21
|
+
chromeArguments?: string[];
|
|
20
22
|
}
|
|
21
23
|
/**
|
|
22
24
|
* Factory class for creating and configuring Selenium WebDriver instances.
|
|
@@ -140,8 +140,14 @@ class DriverManager {
|
|
|
140
140
|
* ```
|
|
141
141
|
*/
|
|
142
142
|
getChromeOptions(args = this.DEFAULT_CHROMIUM_OPTIONS, options = {}) {
|
|
143
|
+
var _a;
|
|
143
144
|
const chromeOptions = new chrome_1.Options();
|
|
144
|
-
|
|
145
|
+
// Merge arguments in priority order:
|
|
146
|
+
// 1. Custom arguments from options (highest priority)
|
|
147
|
+
// 2. Arguments from Settings/environment variables
|
|
148
|
+
// 3. Default arguments (lowest priority)
|
|
149
|
+
const argumentsToUse = (_a = options.chromeArguments) !== null && _a !== void 0 ? _a : [...args, ...settings_1.Settings.chromeArguments];
|
|
150
|
+
argumentsToUse.forEach(argument => {
|
|
145
151
|
chromeOptions.addArguments(argument);
|
|
146
152
|
});
|
|
147
153
|
if (settings_1.Settings.headless) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"driver-manager.js","sourceRoot":"","sources":["../../src/selenium/driver-manager.ts"],"names":[],"mappings":";;;AAAA,2DAAkF;AAClF,sDAAqE;AACrE,kDAAiE;AACjE,wDAAgH;AAChH,sDAAqE;AACrE,6EAA6E;AAC7E,mDAAgD;
|
|
1
|
+
{"version":3,"file":"driver-manager.js","sourceRoot":"","sources":["../../src/selenium/driver-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,6 +21,10 @@ class Settings {
|
|
|
21
21
|
static get timeout() {
|
|
22
22
|
return +process.env["TIMEOUT"] || 30000;
|
|
23
23
|
}
|
|
24
|
+
static get chromeArguments() {
|
|
25
|
+
const args = process.env["CHROME_ARGUMENTS"];
|
|
26
|
+
return args ? args.split(';').map(arg => arg.trim()).filter(arg => arg.length > 0) : [];
|
|
27
|
+
}
|
|
24
28
|
}
|
|
25
29
|
exports.Settings = Settings;
|
|
26
30
|
//# sourceMappingURL=settings.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/settings/settings.ts"],"names":[],"mappings":";;;AAAA,MAAa,QAAQ;IACV,MAAM,KAAK,WAAW;QACzB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,QAAQ,CAAC;IACnD,CAAC;IAEM,MAAM,KAAK,YAAY;QAC1B,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC;IACjD,CAAC;IAEM,MAAM,KAAK,aAAa;QAC3B,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,GAAG,CAAC;IACjD,CAAC;IAEM,MAAM,KAAK,QAAQ;QACtB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACtC,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,iBAAiB,EAAE,KAAK,MAAM,CAAC;IACvE,CAAC;IAEM,MAAM,KAAK,OAAO;QACrB,OAAO,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;IACzC,CAAC;IAEM,MAAM,KAAK,OAAO;QACrB,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC;IAC5C,CAAC;CACJ;
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/settings/settings.ts"],"names":[],"mappings":";;;AAAA,MAAa,QAAQ;IACV,MAAM,KAAK,WAAW;QACzB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,QAAQ,CAAC;IACnD,CAAC;IAEM,MAAM,KAAK,YAAY;QAC1B,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC;IACjD,CAAC;IAEM,MAAM,KAAK,aAAa;QAC3B,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,GAAG,CAAC;IACjD,CAAC;IAEM,MAAM,KAAK,QAAQ;QACtB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACtC,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,iBAAiB,EAAE,KAAK,MAAM,CAAC;IACvE,CAAC;IAEM,MAAM,KAAK,OAAO;QACrB,OAAO,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;IACzC,CAAC;IAEM,MAAM,KAAK,OAAO;QACrB,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC;IAC5C,CAAC;IAEM,MAAM,KAAK,eAAe;QAC7B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAC7C,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5F,CAAC;CACJ;AA9BD,4BA8BC"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@progress/kendo-e2e",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.14.0",
|
|
4
4
|
"description": "Kendo UI end-to-end test utilities.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"snapshot": "./dist/snapshot-cli/index.js"
|
|
7
|
+
"snapshot": "./dist/snapshot-cli/index.js",
|
|
8
|
+
"kendo-e2e-mcp": "./dist/mcp/index.js"
|
|
8
9
|
},
|
|
9
10
|
"typings": "dist/index.d.ts",
|
|
10
11
|
"files": [
|
|
@@ -13,7 +14,8 @@
|
|
|
13
14
|
],
|
|
14
15
|
"scripts": {
|
|
15
16
|
"lint": "npx eslint ./src/**/*.ts ./tests/**/*.ts",
|
|
16
|
-
"build": "npx tsc",
|
|
17
|
+
"build": "npx tsc && npm run build:mcp",
|
|
18
|
+
"build:mcp": "npx esbuild src/mcp/standalone.ts --bundle --platform=node --format=esm --outfile=dist/mcp/index.js --external:@modelcontextprotocol/sdk --external:zod && echo '{\"type\":\"module\"}' > dist/mcp/package.json && chmod +x dist/mcp/index.js",
|
|
17
19
|
"test:a11y": "npx jest tests/a11y",
|
|
18
20
|
"test:components": "npx jest tests/components",
|
|
19
21
|
"test:e2e": "npx jest tests/e2e",
|
|
@@ -58,7 +60,8 @@
|
|
|
58
60
|
"homepage": "https://github.com/telerik/kendo-e2e#readme",
|
|
59
61
|
"dependencies": {
|
|
60
62
|
"@axe-core/webdriverjs": "4.11.0",
|
|
61
|
-
"@
|
|
63
|
+
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
64
|
+
"@types/selenium-webdriver": "^4.35.5",
|
|
62
65
|
"commander": "^14.0.2",
|
|
63
66
|
"glob": "^11.1.0",
|
|
64
67
|
"http-server": "^14.1.1",
|
|
@@ -67,7 +70,7 @@
|
|
|
67
70
|
"looks-same": "^7.3.0",
|
|
68
71
|
"rgb2hex": "^0.2.5",
|
|
69
72
|
"sanitize-html": "^2.17.0",
|
|
70
|
-
"selenium-webdriver": "^4.
|
|
73
|
+
"selenium-webdriver": "^4.39.0",
|
|
71
74
|
"sharp": "^0.34.5",
|
|
72
75
|
"yauzl": "^3.2.0"
|
|
73
76
|
},
|
|
@@ -89,7 +92,6 @@
|
|
|
89
92
|
"react": "^19.2.0",
|
|
90
93
|
"react-dom": "^19.2.0",
|
|
91
94
|
"ts-jest": "^29.4.5",
|
|
92
|
-
"ts-node": "^10.9.2",
|
|
93
95
|
"tslib": "^2.8.1",
|
|
94
96
|
"typescript": "~5.9.3"
|
|
95
97
|
}
|