@pyrokine/mcp-chrome 1.1.0 → 1.4.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 +130 -54
- package/dist/anti-detection/behavior.d.ts +0 -8
- package/dist/anti-detection/behavior.d.ts.map +1 -1
- package/dist/anti-detection/behavior.js +0 -16
- package/dist/anti-detection/behavior.js.map +1 -1
- package/dist/cdp/client.d.ts +0 -2
- package/dist/cdp/client.d.ts.map +1 -1
- package/dist/cdp/client.js +30 -45
- package/dist/cdp/client.js.map +1 -1
- package/dist/cdp/launcher.d.ts +1 -8
- package/dist/cdp/launcher.d.ts.map +1 -1
- package/dist/cdp/launcher.js +11 -21
- package/dist/cdp/launcher.js.map +1 -1
- package/dist/core/auto-wait.d.ts +2 -2
- package/dist/core/auto-wait.d.ts.map +1 -1
- package/dist/core/auto-wait.js +1 -1
- package/dist/core/auto-wait.js.map +1 -1
- package/dist/core/errors.d.ts +10 -13
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +19 -25
- package/dist/core/errors.js.map +1 -1
- package/dist/core/locator.d.ts +6 -7
- package/dist/core/locator.d.ts.map +1 -1
- package/dist/core/locator.js +77 -31
- package/dist/core/locator.js.map +1 -1
- package/dist/core/retry.d.ts.map +1 -1
- package/dist/core/retry.js +1 -1
- package/dist/core/retry.js.map +1 -1
- package/dist/core/session.d.ts +37 -33
- package/dist/core/session.d.ts.map +1 -1
- package/dist/core/session.js +159 -116
- package/dist/core/session.js.map +1 -1
- package/dist/core/types.d.ts +25 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +20 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/unified-session.d.ts +105 -67
- package/dist/core/unified-session.d.ts.map +1 -1
- package/dist/core/unified-session.js +347 -186
- package/dist/core/unified-session.js.map +1 -1
- package/dist/extension/bridge.d.ts +25 -19
- package/dist/extension/bridge.d.ts.map +1 -1
- package/dist/extension/bridge.js +29 -52
- package/dist/extension/bridge.js.map +1 -1
- package/dist/extension/http-server.d.ts +13 -11
- package/dist/extension/http-server.d.ts.map +1 -1
- package/dist/extension/http-server.js +101 -95
- package/dist/extension/http-server.js.map +1 -1
- package/dist/index.js +18 -64
- package/dist/index.js.map +1 -1
- package/dist/tools/browse.d.ts +3 -80
- package/dist/tools/browse.d.ts.map +1 -1
- package/dist/tools/browse.js +135 -291
- package/dist/tools/browse.js.map +1 -1
- package/dist/tools/cookies.d.ts +3 -71
- package/dist/tools/cookies.d.ts.map +1 -1
- package/dist/tools/cookies.js +75 -157
- package/dist/tools/cookies.js.map +1 -1
- package/dist/tools/evaluate.d.ts +3 -52
- package/dist/tools/evaluate.d.ts.map +1 -1
- package/dist/tools/evaluate.js +35 -86
- package/dist/tools/evaluate.js.map +1 -1
- package/dist/tools/extract.d.ts +5 -227
- package/dist/tools/extract.d.ts.map +1 -1
- package/dist/tools/extract.js +586 -184
- package/dist/tools/extract.js.map +1 -1
- package/dist/tools/index.d.ts +9 -9
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +9 -9
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/input.d.ts +3 -258
- package/dist/tools/input.d.ts.map +1 -1
- package/dist/tools/input.js +95 -147
- package/dist/tools/input.js.map +1 -1
- package/dist/tools/logs.d.ts +3 -51
- package/dist/tools/logs.d.ts.map +1 -1
- package/dist/tools/logs.js +47 -108
- package/dist/tools/logs.js.map +1 -1
- package/dist/tools/manage.d.ts +3 -64
- package/dist/tools/manage.d.ts.map +1 -1
- package/dist/tools/manage.js +243 -373
- package/dist/tools/manage.js.map +1 -1
- package/dist/tools/schema.d.ts +16 -182
- package/dist/tools/schema.d.ts.map +1 -1
- package/dist/tools/schema.js +70 -159
- package/dist/tools/schema.js.map +1 -1
- package/dist/tools/wait.d.ts +3 -221
- package/dist/tools/wait.d.ts.map +1 -1
- package/dist/tools/wait.js +74 -145
- package/dist/tools/wait.js.map +1 -1
- package/package.json +1 -1
package/dist/cdp/launcher.js
CHANGED
|
@@ -8,6 +8,7 @@ import { existsSync, mkdirSync } from 'fs';
|
|
|
8
8
|
import { homedir, platform } from 'os';
|
|
9
9
|
import { join } from 'path';
|
|
10
10
|
import { BrowserNotFoundError, TimeoutError } from '../core/errors.js';
|
|
11
|
+
import { DEFAULT_TIMEOUT } from '../core/types.js';
|
|
11
12
|
/**
|
|
12
13
|
* 默认 profile 目录(固定路径,保留 cookies 和登录状态)
|
|
13
14
|
*/
|
|
@@ -54,18 +55,11 @@ export class BrowserLauncher {
|
|
|
54
55
|
get port() {
|
|
55
56
|
return this._port;
|
|
56
57
|
}
|
|
57
|
-
_endpoint = '';
|
|
58
|
-
get endpoint() {
|
|
59
|
-
return this._endpoint;
|
|
60
|
-
}
|
|
61
|
-
get isRunning() {
|
|
62
|
-
return this.process !== null && !this.process.killed;
|
|
63
|
-
}
|
|
64
58
|
/**
|
|
65
59
|
* 启动浏览器
|
|
66
60
|
*/
|
|
67
61
|
async launch(options = {}) {
|
|
68
|
-
const { executablePath, port = 0, incognito = false, headless = false, userDataDir, timeout =
|
|
62
|
+
const { executablePath, port = 0, incognito = false, headless = false, userDataDir, timeout = DEFAULT_TIMEOUT, } = options;
|
|
69
63
|
// 查找 Chrome
|
|
70
64
|
const chromePath = executablePath ?? findChrome();
|
|
71
65
|
if (!chromePath) {
|
|
@@ -79,8 +73,7 @@ export class BrowserLauncher {
|
|
|
79
73
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
80
74
|
});
|
|
81
75
|
// 等待 CDP 端点就绪
|
|
82
|
-
|
|
83
|
-
return this._endpoint;
|
|
76
|
+
return this.waitForEndpoint(timeout);
|
|
84
77
|
}
|
|
85
78
|
/**
|
|
86
79
|
* 关闭浏览器
|
|
@@ -91,15 +84,6 @@ export class BrowserLauncher {
|
|
|
91
84
|
this.process = null;
|
|
92
85
|
}
|
|
93
86
|
}
|
|
94
|
-
/**
|
|
95
|
-
* 强制关闭浏览器
|
|
96
|
-
*/
|
|
97
|
-
forceClose() {
|
|
98
|
-
if (this.process && !this.process.killed) {
|
|
99
|
-
this.process.kill('SIGKILL');
|
|
100
|
-
this.process = null;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
87
|
/**
|
|
104
88
|
* 构建启动参数
|
|
105
89
|
*/
|
|
@@ -164,7 +148,7 @@ export class BrowserLauncher {
|
|
|
164
148
|
this.process.stderr?.on('data', (data) => {
|
|
165
149
|
stderr += data.toString();
|
|
166
150
|
// 解析 DevTools listening on ws://...
|
|
167
|
-
const match = stderr.match(/DevTools listening on (ws
|
|
151
|
+
const match = stderr.match(/DevTools listening on (ws:\/\/\S+)/);
|
|
168
152
|
if (match) {
|
|
169
153
|
clearTimeout(timer);
|
|
170
154
|
// 解析端口
|
|
@@ -181,7 +165,13 @@ export class BrowserLauncher {
|
|
|
181
165
|
});
|
|
182
166
|
this.process.on('exit', (code) => {
|
|
183
167
|
clearTimeout(timer);
|
|
184
|
-
if (code
|
|
168
|
+
if (code === 0) {
|
|
169
|
+
// Chrome 退出码 0:通常是把请求委托给了已运行的实例后自行退出,
|
|
170
|
+
// 此时没有 DevTools 端点可连接
|
|
171
|
+
reject(new Error('浏览器进程已退出(code 0),可能已有相同 profile 的 Chrome 实例在运行。\n'
|
|
172
|
+
+ '请关闭已运行的 Chrome 或指定不同的 userDataDir'));
|
|
173
|
+
}
|
|
174
|
+
else if (code !== null) {
|
|
185
175
|
reject(new Error(`浏览器进程退出,代码: ${code}\n${stderr}`));
|
|
186
176
|
}
|
|
187
177
|
});
|
package/dist/cdp/launcher.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launcher.js","sourceRoot":"","sources":["../../src/cdp/launcher.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAe,KAAK,EAAC,MAAM,eAAe,CAAA;AACjD,OAAO,EAAC,UAAU,EAAE,SAAS,EAAC,MAAM,IAAI,CAAA;AACxC,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAC,MAAM,IAAI,CAAA;AACpC,OAAO,EAAC,IAAI,EAAC,MAAM,MAAM,CAAA;AACzB,OAAO,EAAC,oBAAoB,EAAE,YAAY,EAAC,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"launcher.js","sourceRoot":"","sources":["../../src/cdp/launcher.ts"],"names":[],"mappings":"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"}
|
package/dist/core/auto-wait.d.ts
CHANGED
|
@@ -22,8 +22,8 @@ export interface AutoWaitOptions {
|
|
|
22
22
|
export declare class AutoWait {
|
|
23
23
|
private cdp;
|
|
24
24
|
private sessionId;
|
|
25
|
-
private timeout;
|
|
26
|
-
private deadline;
|
|
25
|
+
private readonly timeout;
|
|
26
|
+
private readonly deadline;
|
|
27
27
|
constructor(cdp: CDPClient, sessionId: string, options?: AutoWaitOptions);
|
|
28
28
|
/**
|
|
29
29
|
* 等待元素可用(非禁用状态)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-wait.d.ts","sourceRoot":"","sources":["../../src/core/auto-wait.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"auto-wait.d.ts","sourceRoot":"","sources":["../../src/core/auto-wait.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,kBAAkB,CAAA;AAI/C;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,wBAAwB;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,QAAQ;IAKb,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,SAAS;IALrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;gBAGrB,GAAG,EAAE,SAAS,EACd,SAAS,EAAE,MAAM,EACzB,OAAO,GAAE,eAAoB;IAMjC;;;;;OAKG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BnD;;;;;OAKG;IACG,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmC7D;;;;;OAKG;IACG,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA+BrD;;;;OAIG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BnD;;;;OAIG;IACG,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOrD;;;;OAIG;IACG,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtD;;;;OAIG;IACH,OAAO,CAAC,IAAI;IAIZ;;OAEG;IACH,OAAO,CAAC,SAAS;CAGpB"}
|
package/dist/core/auto-wait.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-wait.js","sourceRoot":"","sources":["../../src/core/auto-wait.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAC,SAAS,EAAC,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"auto-wait.js","sourceRoot":"","sources":["../../src/core/auto-wait.ts"],"names":[],"mappings":"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"}
|
package/dist/core/errors.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export declare abstract class BrowserError extends Error {
|
|
|
21
21
|
abstract readonly code: string;
|
|
22
22
|
abstract readonly suggestion: string;
|
|
23
23
|
readonly context: ErrorContext;
|
|
24
|
-
constructor(message: string);
|
|
24
|
+
protected constructor(message: string);
|
|
25
25
|
/**
|
|
26
26
|
* 转换为 JSON 格式,用于 MCP 响应
|
|
27
27
|
*/
|
|
@@ -89,14 +89,6 @@ export declare class TimeoutError extends BrowserError {
|
|
|
89
89
|
readonly suggestion = "\u8BF7\u589E\u52A0\u8D85\u65F6\u65F6\u95F4\uFF0C\u6216\u68C0\u67E5\u64CD\u4F5C\u6761\u4EF6\u662F\u5426\u80FD\u6EE1\u8DB3";
|
|
90
90
|
constructor(message: string);
|
|
91
91
|
}
|
|
92
|
-
/**
|
|
93
|
-
* 无效参数错误
|
|
94
|
-
*/
|
|
95
|
-
export declare class InvalidArgumentError extends BrowserError {
|
|
96
|
-
readonly code = "INVALID_ARGUMENT";
|
|
97
|
-
readonly suggestion: string;
|
|
98
|
-
constructor(message: string, suggestion?: string);
|
|
99
|
-
}
|
|
100
92
|
/**
|
|
101
93
|
* CDP 协议错误
|
|
102
94
|
*/
|
|
@@ -105,10 +97,6 @@ export declare class CDPError extends BrowserError {
|
|
|
105
97
|
readonly suggestion = "\u8FD9\u662F\u4E00\u4E2A CDP \u534F\u8BAE\u9519\u8BEF\uFF0C\u8BF7\u68C0\u67E5\u64CD\u4F5C\u662F\u5426\u6B63\u786E";
|
|
106
98
|
constructor(message: string);
|
|
107
99
|
}
|
|
108
|
-
/**
|
|
109
|
-
* 格式化日志记录(参考 Playwright Waiter)
|
|
110
|
-
*/
|
|
111
|
-
export declare function formatLogRecording(logs: string[]): string;
|
|
112
100
|
/**
|
|
113
101
|
* 格式化错误为 MCP 工具响应
|
|
114
102
|
* 特别处理 ZodError,转换为结构化的 INVALID_ARGUMENT 错误
|
|
@@ -121,4 +109,13 @@ export declare function formatErrorResponse(error: unknown): {
|
|
|
121
109
|
}>;
|
|
122
110
|
isError: boolean;
|
|
123
111
|
};
|
|
112
|
+
/**
|
|
113
|
+
* 格式化成功响应为 MCP 工具响应
|
|
114
|
+
*/
|
|
115
|
+
export declare function formatResponse(data: unknown): {
|
|
116
|
+
content: Array<{
|
|
117
|
+
type: 'text';
|
|
118
|
+
text: string;
|
|
119
|
+
}>;
|
|
120
|
+
};
|
|
124
121
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,YAAY,CAAA;AAEtC;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,8BAAsB,YAAa,SAAQ,KAAK;IAC5C,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IACpC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAK;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,YAAY,CAAA;AAEtC;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,8BAAsB,YAAa,SAAQ,KAAK;IAC5C,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IACpC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAK;IAEnC,SAAS,aAAa,OAAO,EAAE,MAAM;IAKrC;;OAEG;IAEH,MAAM,IAAI,MAAM;CAsBnB;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,YAAY;IACpD,QAAQ,CAAC,IAAI,wBAAuB;IACpC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,SAAkB,OAAO,EAAE,YAAY,CAAA;gBAE3B,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAQzC;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,YAAY;IAClD,QAAQ,CAAC,IAAI,uBAA4B;IACzC,QAAQ,CAAC,UAAU,6KACqC;;CAK3D;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,YAAY;IAClD,QAAQ,CAAC,IAAI,uBAA4B;IACzC,QAAQ,CAAC,UAAU,yHACyD;;CAK/E;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,YAAY;IACjD,QAAQ,CAAC,IAAI,sBAA2B;IACxC,QAAQ,CAAC,UAAU,qFAAqC;IACxD,SAAkB,OAAO,EAAE,YAAY,CAAA;gBAE3B,QAAQ,EAAE,MAAM;CAI/B;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,YAAY;IAClD,QAAQ,CAAC,IAAI,uBAAsB;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAA;IACvB,SAAkB,OAAO,EAAE,YAAY,CAAA;gBAE3B,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,EAAO,EAAE,GAAG,CAAC,EAAE,MAAM;IAUrE,MAAM,IAAI,MAAM;CAkB5B;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,YAAY;IACpD,QAAQ,CAAC,IAAI,wBAA6B;IAC1C,QAAQ,CAAC,UAAU,gGAAoB;IACvC,SAAkB,OAAO,EAAE,YAAY,CAAA;gBAE3B,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAI3C;AAED;;GAEG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAC1C,QAAQ,CAAC,IAAI,aAAkB;IAC/B,QAAQ,CAAC,UAAU,8HAAyB;gBAEhC,OAAO,EAAE,MAAM;CAG9B;AAED;;GAEG;AACH,qBAAa,QAAS,SAAQ,YAAY;IACtC,QAAQ,CAAC,IAAI,eAAoB;IACjC,QAAQ,CAAC,UAAU,uHAA4B;gBAEnC,OAAO,EAAE,MAAM;CAG9B;AA8CD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG;IACjD,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,EAAE,OAAO,CAAC;CACpB,CAsDA;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG;IAC3C,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD,CAIA"}
|
package/dist/core/errors.js
CHANGED
|
@@ -19,6 +19,7 @@ export class BrowserError extends Error {
|
|
|
19
19
|
/**
|
|
20
20
|
* 转换为 JSON 格式,用于 MCP 响应
|
|
21
21
|
*/
|
|
22
|
+
// noinspection JSUnusedGlobalSymbols — 被 formatErrorResponse 动态调用
|
|
22
23
|
toJSON() {
|
|
23
24
|
const result = {
|
|
24
25
|
error: {
|
|
@@ -139,17 +140,6 @@ export class TimeoutError extends BrowserError {
|
|
|
139
140
|
super(message);
|
|
140
141
|
}
|
|
141
142
|
}
|
|
142
|
-
/**
|
|
143
|
-
* 无效参数错误
|
|
144
|
-
*/
|
|
145
|
-
export class InvalidArgumentError extends BrowserError {
|
|
146
|
-
code = 'INVALID_ARGUMENT';
|
|
147
|
-
suggestion;
|
|
148
|
-
constructor(message, suggestion) {
|
|
149
|
-
super(message);
|
|
150
|
-
this.suggestion = suggestion ?? '请检查参数是否正确';
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
143
|
/**
|
|
154
144
|
* CDP 协议错误
|
|
155
145
|
*/
|
|
@@ -160,19 +150,6 @@ export class CDPError extends BrowserError {
|
|
|
160
150
|
super(message);
|
|
161
151
|
}
|
|
162
152
|
}
|
|
163
|
-
/**
|
|
164
|
-
* 格式化日志记录(参考 Playwright Waiter)
|
|
165
|
-
*/
|
|
166
|
-
export function formatLogRecording(logs) {
|
|
167
|
-
if (logs.length === 0) {
|
|
168
|
-
return '';
|
|
169
|
-
}
|
|
170
|
-
const header = ' logs ';
|
|
171
|
-
const width = 60;
|
|
172
|
-
const leftPad = '='.repeat(Math.floor((width - header.length) / 2));
|
|
173
|
-
const rightPad = '='.repeat(width - header.length - leftPad.length);
|
|
174
|
-
return `\n${leftPad}${header}${rightPad}\n${logs.join('\n')}\n${'='.repeat(width)}`;
|
|
175
|
-
}
|
|
176
153
|
/**
|
|
177
154
|
* ZodError 类型检测
|
|
178
155
|
*/
|
|
@@ -189,7 +166,16 @@ function isZodError(error) {
|
|
|
189
166
|
*/
|
|
190
167
|
function detectVisibilityHint(errorMessage) {
|
|
191
168
|
const msg = errorMessage.toLowerCase();
|
|
192
|
-
const keywords = [
|
|
169
|
+
const keywords = [
|
|
170
|
+
'hidden',
|
|
171
|
+
'visible',
|
|
172
|
+
'visibility',
|
|
173
|
+
'focus',
|
|
174
|
+
'blur',
|
|
175
|
+
'active tab',
|
|
176
|
+
'not active',
|
|
177
|
+
'capturevisibletab',
|
|
178
|
+
];
|
|
193
179
|
if (keywords.some(kw => msg.includes(kw))) {
|
|
194
180
|
return '此操作可能需要 tab 在前台。请使用 browse(action="attach", targetId="<id>", activate=true) 激活目标 tab';
|
|
195
181
|
}
|
|
@@ -248,4 +234,12 @@ export function formatErrorResponse(error) {
|
|
|
248
234
|
isError: true,
|
|
249
235
|
};
|
|
250
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* 格式化成功响应为 MCP 工具响应
|
|
239
|
+
*/
|
|
240
|
+
export function formatResponse(data) {
|
|
241
|
+
return {
|
|
242
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
243
|
+
};
|
|
244
|
+
}
|
|
251
245
|
//# sourceMappingURL=errors.js.map
|
package/dist/core/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAWH;;GAEG;AACH,MAAM,OAAgB,YAAa,SAAQ,KAAK;IAGnC,OAAO,GAAiB,EAAE,CAAA;IAEnC,
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/core/locator.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ type NodeId = number;
|
|
|
24
24
|
export interface LocatorOptions {
|
|
25
25
|
/** 超时时间(毫秒),默认 30000 */
|
|
26
26
|
timeout?: number;
|
|
27
|
+
/** 第 N 个匹配元素(从 0 开始,默认 0 即第一个) */
|
|
28
|
+
nth?: number;
|
|
27
29
|
/** 获取当前 URL 的回调(用于错误上下文) */
|
|
28
30
|
getUrl?: () => string | undefined;
|
|
29
31
|
}
|
|
@@ -35,9 +37,10 @@ export declare class Locator {
|
|
|
35
37
|
private target;
|
|
36
38
|
private sessionId?;
|
|
37
39
|
private logs;
|
|
38
|
-
private timeout;
|
|
39
|
-
private deadline;
|
|
40
|
-
private
|
|
40
|
+
private readonly timeout;
|
|
41
|
+
private readonly deadline;
|
|
42
|
+
private readonly nth;
|
|
43
|
+
private readonly getUrl?;
|
|
41
44
|
constructor(cdp: CDPClient, target: Target, sessionId?: string | undefined, options?: LocatorOptions);
|
|
42
45
|
/**
|
|
43
46
|
* 查找元素,返回节点 ID(带重试)
|
|
@@ -58,10 +61,6 @@ export declare class Locator {
|
|
|
58
61
|
* @returns 函数返回值
|
|
59
62
|
*/
|
|
60
63
|
evaluateOn<T>(fn: string): Promise<T>;
|
|
61
|
-
/**
|
|
62
|
-
* 获取定位日志
|
|
63
|
-
*/
|
|
64
|
-
getLogs(): string[];
|
|
65
64
|
/**
|
|
66
65
|
* 查找元素的内部实现(单次尝试)
|
|
67
66
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locator.d.ts","sourceRoot":"","sources":["../../src/core/locator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,kBAAkB,CAAA;AAG/C,OAAO,EACH,KAAK,GAAG,
|
|
1
|
+
{"version":3,"file":"locator.d.ts","sourceRoot":"","sources":["../../src/core/locator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,kBAAkB,CAAA;AAG/C,OAAO,EACH,KAAK,GAAG,EAaR,KAAK,KAAK,EACV,KAAK,MAAM,EACd,MAAM,YAAY,CAAA;AAEnB;;GAEG;AACH,KAAK,MAAM,GAAG,MAAM,CAAC;AAOrB;;GAEG;AACH,MAAM,WAAW,cAAc;IAC3B,wBAAwB;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;CACrC;AAyDD;;GAEG;AACH,qBAAa,OAAO;IAQZ,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,SAAS,CAAC;IATtB,OAAO,CAAC,IAAI,CAAe;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAA0B;gBAGtC,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,YAAA,EAC1B,OAAO,GAAE,cAAmB;IAQhC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAY7B;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,KAAK,CAAC;IAczC;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IASpC;;;;;OAKG;IACG,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IA6B3C;;OAEG;YACW,YAAY;IAoC1B;;OAEG;YACW,mBAAmB;IA+CjC;;OAEG;YACW,UAAU;IAiBxB;;OAEG;YACW,WAAW;IAsEzB;;OAEG;YACW,iBAAiB;IAiB/B;;OAEG;YACW,WAAW;IAazB;;OAEG;YACW,SAAS;IAavB;;OAEG;YACW,YAAY;IAQ1B;;OAEG;YACW,SAAS;IAOvB;;OAEG;YACW,gBAAgB;IAqC9B;;OAEG;YACW,WAAW;IAOzB;;OAEG;YACW,iBAAiB;IAoC/B;;OAEG;YACW,mBAAmB;IAgCjC;;OAEG;YACW,gBAAgB;IAgB9B;;OAEG;YACW,WAAW;IAgBzB;;;;;OAKG;IACH,OAAO,CAAC,IAAI;IAIZ;;OAEG;IACH,OAAO,CAAC,SAAS;IAIjB;;OAEG;IACH,OAAO,CAAC,GAAG;CAGd"}
|
package/dist/core/locator.js
CHANGED
|
@@ -14,8 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { ElementNotFoundError } from './errors.js';
|
|
16
16
|
import { withRetry } from './retry.js';
|
|
17
|
-
import { isAltTarget, isCoordinateTarget, isCSSTarget, isCSSTextTarget, isLabelTarget, isPlaceholderTarget, isRoleTarget, isTestIdTarget, isTextTarget, isTitleTarget, isXPathTarget, } from './types.js';
|
|
18
|
-
const DEFAULT_TIMEOUT = 30000;
|
|
17
|
+
import { DEFAULT_TIMEOUT, isAltTarget, isCoordinateTarget, isCSSTarget, isCSSTextTarget, isLabelTarget, isPlaceholderTarget, isRoleTarget, isTestIdTarget, isTextTarget, isTitleTarget, isXPathTarget, } from './types.js';
|
|
19
18
|
/**
|
|
20
19
|
* 转义 XPath 字符串中的引号
|
|
21
20
|
* XPath 1.0 没有转义机制,需要用 concat() 拼接单双引号
|
|
@@ -79,6 +78,7 @@ export class Locator {
|
|
|
79
78
|
logs = [];
|
|
80
79
|
timeout;
|
|
81
80
|
deadline;
|
|
81
|
+
nth;
|
|
82
82
|
getUrl;
|
|
83
83
|
constructor(cdp, target, sessionId, options = {}) {
|
|
84
84
|
this.cdp = cdp;
|
|
@@ -86,6 +86,7 @@ export class Locator {
|
|
|
86
86
|
this.sessionId = sessionId;
|
|
87
87
|
this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
88
88
|
this.deadline = Date.now() + this.timeout;
|
|
89
|
+
this.nth = options.nth ?? 0;
|
|
89
90
|
this.getUrl = options.getUrl;
|
|
90
91
|
}
|
|
91
92
|
/**
|
|
@@ -150,12 +151,6 @@ export class Locator {
|
|
|
150
151
|
}
|
|
151
152
|
return result.value;
|
|
152
153
|
}
|
|
153
|
-
/**
|
|
154
|
-
* 获取定位日志
|
|
155
|
-
*/
|
|
156
|
-
getLogs() {
|
|
157
|
-
return this.logs;
|
|
158
|
-
}
|
|
159
154
|
/**
|
|
160
155
|
* 查找元素的内部实现(单次尝试)
|
|
161
156
|
*/
|
|
@@ -198,12 +193,13 @@ export class Locator {
|
|
|
198
193
|
*/
|
|
199
194
|
async findByAccessibility() {
|
|
200
195
|
const { role, name } = this.target;
|
|
201
|
-
this.log(`使用可访问性树定位: role=${role}, name=${name ?? '(any)'}`);
|
|
196
|
+
this.log(`使用可访问性树定位: role=${role}, name=${name ?? '(any)'}${this.nth > 0 ? `, nth=${this.nth}` : ''}`);
|
|
202
197
|
// 启用可访问性
|
|
203
198
|
await this.send('Accessibility.enable');
|
|
204
199
|
// 获取整个可访问性树
|
|
205
200
|
const { nodes } = (await this.send('Accessibility.getFullAXTree'));
|
|
206
|
-
//
|
|
201
|
+
// 查找匹配的节点,跳过前 nth 个
|
|
202
|
+
let matchCount = 0;
|
|
207
203
|
for (const node of nodes) {
|
|
208
204
|
if (node.role?.value?.toLowerCase() !== role.toLowerCase()) {
|
|
209
205
|
continue;
|
|
@@ -212,7 +208,13 @@ export class Locator {
|
|
|
212
208
|
continue;
|
|
213
209
|
}
|
|
214
210
|
if (node.backendDOMNodeId) {
|
|
215
|
-
this.
|
|
211
|
+
if (matchCount < this.nth) {
|
|
212
|
+
++matchCount;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
this.log(`找到元素: backendDOMNodeId=${node.backendDOMNodeId}${this.nth > 0 ?
|
|
216
|
+
`(第 ${this.nth} 个)` :
|
|
217
|
+
''}`);
|
|
216
218
|
// 将 backendDOMNodeId 转换为 nodeId
|
|
217
219
|
const { nodeIds } = (await this.send('DOM.pushNodesByBackendIdsToFrontend', {
|
|
218
220
|
backendNodeIds: [node.backendDOMNodeId],
|
|
@@ -229,7 +231,7 @@ export class Locator {
|
|
|
229
231
|
*/
|
|
230
232
|
async findByText() {
|
|
231
233
|
const { text, exact = false } = this.target;
|
|
232
|
-
this.log(`使用文本内容定位: text=${text}, exact=${exact}`);
|
|
234
|
+
this.log(`使用文本内容定位: text=${text}, exact=${exact}${this.nth > 0 ? `, nth=${this.nth}` : ''}`);
|
|
233
235
|
// 使用 XPath 查找包含文本的元素(正确转义引号)
|
|
234
236
|
// 用 . 而非 text():text() 只匹配直接子文本节点,. 匹配完整 textContent
|
|
235
237
|
const escapedText = escapeXPathString(text);
|
|
@@ -243,7 +245,7 @@ export class Locator {
|
|
|
243
245
|
*/
|
|
244
246
|
async findByLabel() {
|
|
245
247
|
const { label, exact = false } = this.target;
|
|
246
|
-
this.log(`使用 label 定位: label=${label}, exact=${exact}`);
|
|
248
|
+
this.log(`使用 label 定位: label=${label}, exact=${exact}${this.nth > 0 ? `, nth=${this.nth}` : ''}`);
|
|
247
249
|
// 转义 JS 字符串
|
|
248
250
|
const escapedLabel = escapeJSString(label);
|
|
249
251
|
// 使用 Runtime.evaluate 执行复杂查询
|
|
@@ -252,34 +254,45 @@ export class Locator {
|
|
|
252
254
|
(function() {
|
|
253
255
|
const targetLabel = "${escapedLabel}";
|
|
254
256
|
const isExact = ${exact};
|
|
257
|
+
const nth = ${this.nth};
|
|
255
258
|
const labels = document.querySelectorAll('label');
|
|
259
|
+
let matchCount = 0;
|
|
256
260
|
for (const label of labels) {
|
|
257
261
|
const text = label.textContent?.trim() ?? '';
|
|
258
262
|
const match = isExact ? text === targetLabel : text.includes(targetLabel);
|
|
259
263
|
if (match) {
|
|
264
|
+
let found = null;
|
|
260
265
|
// 1. 通过 for 属性找关联元素
|
|
261
266
|
if (label.htmlFor) {
|
|
262
267
|
const input = document.getElementById(label.htmlFor);
|
|
263
|
-
if (input)
|
|
268
|
+
if (input) found = input;
|
|
264
269
|
}
|
|
265
270
|
// 2. 找 label 内部的 input
|
|
266
|
-
|
|
267
|
-
|
|
271
|
+
if (!found) {
|
|
272
|
+
const inner = label.querySelector('input, select, textarea');
|
|
273
|
+
if (inner) found = inner;
|
|
274
|
+
}
|
|
268
275
|
// 3. 通过 label.id 推断(如 userName-label → #userName)
|
|
269
|
-
if (label.id && label.id.endsWith('-label')) {
|
|
276
|
+
if (!found && label.id && label.id.endsWith('-label')) {
|
|
270
277
|
const inputId = label.id.slice(0, -6);
|
|
271
278
|
const input = document.getElementById(inputId);
|
|
272
|
-
if (input)
|
|
279
|
+
if (input) found = input;
|
|
273
280
|
}
|
|
274
281
|
// 4. 在父元素的相邻兄弟中查找 input
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
282
|
+
if (!found) {
|
|
283
|
+
const parent = label.parentElement;
|
|
284
|
+
if (parent) {
|
|
285
|
+
for (const sibling of parent.parentElement?.children ?? []) {
|
|
286
|
+
if (sibling === parent) continue;
|
|
287
|
+
const input = sibling.querySelector('input, select, textarea');
|
|
288
|
+
if (input) { found = input; break; }
|
|
289
|
+
}
|
|
281
290
|
}
|
|
282
291
|
}
|
|
292
|
+
if (found) {
|
|
293
|
+
if (matchCount < nth) { ++matchCount; continue; }
|
|
294
|
+
return found;
|
|
295
|
+
}
|
|
283
296
|
}
|
|
284
297
|
}
|
|
285
298
|
return null;
|
|
@@ -297,7 +310,9 @@ export class Locator {
|
|
|
297
310
|
*/
|
|
298
311
|
async findByPlaceholder() {
|
|
299
312
|
const { placeholder, exact = false } = this.target;
|
|
300
|
-
this.log(`使用 placeholder 定位: placeholder=${placeholder}, exact=${exact}
|
|
313
|
+
this.log(`使用 placeholder 定位: placeholder=${placeholder}, exact=${exact}${this.nth > 0 ?
|
|
314
|
+
`, nth=${this.nth}` :
|
|
315
|
+
''}`);
|
|
301
316
|
const escaped = escapeCSSAttributeValue(placeholder);
|
|
302
317
|
const css = exact
|
|
303
318
|
? `[placeholder="${escaped}"]`
|
|
@@ -309,7 +324,7 @@ export class Locator {
|
|
|
309
324
|
*/
|
|
310
325
|
async findByTitle() {
|
|
311
326
|
const { title, exact = false } = this.target;
|
|
312
|
-
this.log(`使用 title 属性定位: title=${title}, exact=${exact}`);
|
|
327
|
+
this.log(`使用 title 属性定位: title=${title}, exact=${exact}${this.nth > 0 ? `, nth=${this.nth}` : ''}`);
|
|
313
328
|
const escaped = escapeCSSAttributeValue(title);
|
|
314
329
|
const css = exact ? `[title="${escaped}"]` : `[title*="${escaped}"]`;
|
|
315
330
|
return this.findByCSSInternal(css);
|
|
@@ -319,7 +334,7 @@ export class Locator {
|
|
|
319
334
|
*/
|
|
320
335
|
async findByAlt() {
|
|
321
336
|
const { alt, exact = false } = this.target;
|
|
322
|
-
this.log(`使用 alt 属性定位: alt=${alt}, exact=${exact}`);
|
|
337
|
+
this.log(`使用 alt 属性定位: alt=${alt}, exact=${exact}${this.nth > 0 ? `, nth=${this.nth}` : ''}`);
|
|
323
338
|
const escaped = escapeCSSAttributeValue(alt);
|
|
324
339
|
const css = exact ? `[alt="${escaped}"]` : `[alt*="${escaped}"]`;
|
|
325
340
|
return this.findByCSSInternal(css);
|
|
@@ -329,7 +344,7 @@ export class Locator {
|
|
|
329
344
|
*/
|
|
330
345
|
async findByTestId() {
|
|
331
346
|
const { testId } = this.target;
|
|
332
|
-
this.log(`使用 data-testid 定位: testId=${testId}`);
|
|
347
|
+
this.log(`使用 data-testid 定位: testId=${testId}${this.nth > 0 ? `, nth=${this.nth}` : ''}`);
|
|
333
348
|
const escaped = escapeCSSAttributeValue(testId);
|
|
334
349
|
return this.findByCSSInternal(`[data-testid="${escaped}"]`);
|
|
335
350
|
}
|
|
@@ -346,7 +361,9 @@ export class Locator {
|
|
|
346
361
|
*/
|
|
347
362
|
async findByCSSAndText() {
|
|
348
363
|
const { css, text, exact = false } = this.target;
|
|
349
|
-
this.log(`使用 CSS+text 组合定位: css=${css}, text=${text}, exact=${exact}
|
|
364
|
+
this.log(`使用 CSS+text 组合定位: css=${css}, text=${text}, exact=${exact}${this.nth > 0 ?
|
|
365
|
+
`, nth=${this.nth}` :
|
|
366
|
+
''}`);
|
|
350
367
|
const escapedCSS = escapeJSString(css);
|
|
351
368
|
const escapedText = escapeJSString(text);
|
|
352
369
|
const { result } = (await this.send('Runtime.evaluate', {
|
|
@@ -355,9 +372,12 @@ export class Locator {
|
|
|
355
372
|
const elements = document.querySelectorAll("${escapedCSS}");
|
|
356
373
|
const target = "${escapedText}";
|
|
357
374
|
const isExact = ${exact};
|
|
375
|
+
const nth = ${this.nth};
|
|
376
|
+
let matchCount = 0;
|
|
358
377
|
for (const el of elements) {
|
|
359
378
|
const content = (el.textContent ?? '').trim();
|
|
360
379
|
if (isExact ? content === target : content.includes(target)) {
|
|
380
|
+
if (matchCount < nth) { ++matchCount; continue; }
|
|
361
381
|
return el;
|
|
362
382
|
}
|
|
363
383
|
}
|
|
@@ -385,6 +405,19 @@ export class Locator {
|
|
|
385
405
|
async findByCSSInternal(css) {
|
|
386
406
|
// 确保 DOM 已启用
|
|
387
407
|
const { root } = (await this.send('DOM.getDocument'));
|
|
408
|
+
if (this.nth > 0) {
|
|
409
|
+
// nth > 0:用 querySelectorAll 取第 nth 个
|
|
410
|
+
const { nodeIds } = (await this.send('DOM.querySelectorAll', {
|
|
411
|
+
nodeId: root.nodeId,
|
|
412
|
+
selector: css,
|
|
413
|
+
}));
|
|
414
|
+
if (this.nth >= nodeIds.length) {
|
|
415
|
+
this.log(`第 ${this.nth} 个匹配元素不存在(共 ${nodeIds.length} 个): ${css}`);
|
|
416
|
+
throw new ElementNotFoundError(this.target, this.timeout, this.logs, this.getUrl?.());
|
|
417
|
+
}
|
|
418
|
+
this.log(`找到元素: nodeId=${nodeIds[this.nth]}(第 ${this.nth} 个)`);
|
|
419
|
+
return nodeIds[this.nth];
|
|
420
|
+
}
|
|
388
421
|
const { nodeId } = (await this.send('DOM.querySelector', {
|
|
389
422
|
nodeId: root.nodeId,
|
|
390
423
|
selector: css,
|
|
@@ -400,9 +433,22 @@ export class Locator {
|
|
|
400
433
|
* XPath 定位内部实现
|
|
401
434
|
*/
|
|
402
435
|
async findByXPathInternal(xpath) {
|
|
436
|
+
const escapedXPath = escapeJSString(xpath);
|
|
437
|
+
if (this.nth > 0) {
|
|
438
|
+
// nth > 0:用 ORDERED_NODE_SNAPSHOT_TYPE 取第 nth 个
|
|
439
|
+
const { result } = (await this.send('Runtime.evaluate', {
|
|
440
|
+
expression: `(function() { var r = document.evaluate('${escapedXPath}', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); return r.snapshotLength > ${this.nth} ? r.snapshotItem(${this.nth}) : null })()`,
|
|
441
|
+
returnByValue: false,
|
|
442
|
+
}));
|
|
443
|
+
if (!result.objectId || result.subtype === 'null') {
|
|
444
|
+
this.log(`第 ${this.nth} 个匹配元素不存在: ${xpath}`);
|
|
445
|
+
throw new ElementNotFoundError(this.target, this.timeout, this.logs, this.getUrl?.());
|
|
446
|
+
}
|
|
447
|
+
return this.objectIdToNodeId(result.objectId);
|
|
448
|
+
}
|
|
403
449
|
// 使用 Runtime.evaluate 执行 XPath
|
|
404
450
|
const { result } = (await this.send('Runtime.evaluate', {
|
|
405
|
-
expression: `document.evaluate('${
|
|
451
|
+
expression: `document.evaluate('${escapedXPath}', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue`,
|
|
406
452
|
returnByValue: false,
|
|
407
453
|
}));
|
|
408
454
|
if (!result.objectId) {
|
|
@@ -432,7 +478,7 @@ export class Locator {
|
|
|
432
478
|
async getBoxModel(nodeId) {
|
|
433
479
|
const { model } = (await this.send('DOM.getBoxModel', { nodeId }));
|
|
434
480
|
// content 是 [x1,y1, x2,y2, x3,y3, x4,y4] 格式的四个角坐标
|
|
435
|
-
const [x1, y1, x2, , , ,
|
|
481
|
+
const [x1, y1, x2, , , , , y4] = model.content;
|
|
436
482
|
return {
|
|
437
483
|
x: x1,
|
|
438
484
|
y: y1,
|