@pedropaulovc/playwright 1.59.0-next → 1.59.0-next.1
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/common/config.js +2 -3
- package/lib/index.js +1 -1
- package/lib/isomorphic/teleReceiver.js +0 -1
- package/lib/mcp/browser/response.js +20 -21
- package/lib/mcp/browser/tab.js +4 -4
- package/lib/mcp/browser/tools/common.js +1 -3
- package/lib/mcp/browser/tools/console.js +1 -1
- package/lib/mcp/browser/tools/dialogs.js +0 -1
- package/lib/mcp/browser/tools/evaluate.js +6 -6
- package/lib/mcp/browser/tools/keyboard.js +8 -1
- package/lib/mcp/browser/tools/network.js +1 -1
- package/lib/mcp/browser/tools/pdf.js +2 -2
- package/lib/mcp/browser/tools/runCode.js +2 -4
- package/lib/mcp/browser/tools/screenshot.js +6 -9
- package/lib/mcp/browser/tools/snapshot.js +4 -2
- package/lib/mcp/browser/tools/utils.js +2 -2
- package/lib/mcp/program.js +1 -0
- package/lib/mcp/terminal/SKILL.md +158 -0
- package/lib/mcp/terminal/cli.js +2 -291
- package/lib/mcp/terminal/command.js +1 -1
- package/lib/mcp/terminal/commands.js +19 -19
- package/lib/mcp/terminal/help.json +10 -10
- package/lib/mcp/terminal/program.js +310 -0
- package/lib/program.js +1 -0
- package/lib/reporters/base.js +5 -1
- package/lib/transform/transform.js +2 -2
- package/package.json +4 -3
- package/types/test.d.ts +37 -1
- package/lib/mcp/browser/actions.d.js +0 -16
- package/lib/mcp/browser/tools/navigateAndWait.js +0 -66
- package/lib/mcp/browser/tools/snapshotViewport.js +0 -63
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var program_exports = {};
|
|
30
|
+
__export(program_exports, {
|
|
31
|
+
program: () => program
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(program_exports);
|
|
34
|
+
var import_child_process = require("child_process");
|
|
35
|
+
var import_crypto = __toESM(require("crypto"));
|
|
36
|
+
var import_fs = __toESM(require("fs"));
|
|
37
|
+
var import_net = __toESM(require("net"));
|
|
38
|
+
var import_os = __toESM(require("os"));
|
|
39
|
+
var import_path = __toESM(require("path"));
|
|
40
|
+
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
|
41
|
+
var import_socketConnection = require("./socketConnection");
|
|
42
|
+
const debugCli = (0, import_utilsBundle.debug)("pw:cli");
|
|
43
|
+
class Session {
|
|
44
|
+
constructor(name, connection) {
|
|
45
|
+
this._nextMessageId = 1;
|
|
46
|
+
this._callbacks = /* @__PURE__ */ new Map();
|
|
47
|
+
this.name = name;
|
|
48
|
+
this._connection = connection;
|
|
49
|
+
this._connection.onmessage = (message) => this._onMessage(message);
|
|
50
|
+
this._connection.onclose = () => this.close();
|
|
51
|
+
}
|
|
52
|
+
async run(args) {
|
|
53
|
+
return await this._send("run", { args });
|
|
54
|
+
}
|
|
55
|
+
async stop() {
|
|
56
|
+
await this._send("stop");
|
|
57
|
+
this.close();
|
|
58
|
+
}
|
|
59
|
+
async _send(method, params = {}) {
|
|
60
|
+
const messageId = this._nextMessageId++;
|
|
61
|
+
const message = {
|
|
62
|
+
id: messageId,
|
|
63
|
+
method,
|
|
64
|
+
params
|
|
65
|
+
};
|
|
66
|
+
await this._connection.send(message);
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
this._callbacks.set(messageId, { resolve, reject });
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
close() {
|
|
72
|
+
for (const callback of this._callbacks.values())
|
|
73
|
+
callback.reject(new Error("Session closed"));
|
|
74
|
+
this._callbacks.clear();
|
|
75
|
+
this._connection.close();
|
|
76
|
+
}
|
|
77
|
+
_onMessage(object) {
|
|
78
|
+
if (object.id && this._callbacks.has(object.id)) {
|
|
79
|
+
const callback = this._callbacks.get(object.id);
|
|
80
|
+
this._callbacks.delete(object.id);
|
|
81
|
+
if (object.error)
|
|
82
|
+
callback.reject(new Error(object.error));
|
|
83
|
+
else
|
|
84
|
+
callback.resolve(object.result);
|
|
85
|
+
} else if (object.id) {
|
|
86
|
+
throw new Error(`Unexpected message id: ${object.id}`);
|
|
87
|
+
} else {
|
|
88
|
+
throw new Error(`Unexpected message without id: ${JSON.stringify(object)}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
class SessionManager {
|
|
93
|
+
async list() {
|
|
94
|
+
const dir = daemonSocketDir;
|
|
95
|
+
try {
|
|
96
|
+
const files = await import_fs.default.promises.readdir(dir);
|
|
97
|
+
const sessions = [];
|
|
98
|
+
for (const file of files) {
|
|
99
|
+
if (file.endsWith("-user-data")) {
|
|
100
|
+
const sessionName = file.slice(0, -"-user-data".length);
|
|
101
|
+
const live = await this._canConnect(sessionName);
|
|
102
|
+
sessions.push({ name: sessionName, live });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return sessions;
|
|
106
|
+
} catch {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async run(args) {
|
|
111
|
+
const sessionName = this._resolveSessionName(args.session);
|
|
112
|
+
const session = await this._connect(sessionName);
|
|
113
|
+
const result = await session.run(args);
|
|
114
|
+
console.log(result);
|
|
115
|
+
session.close();
|
|
116
|
+
}
|
|
117
|
+
async stop(sessionName) {
|
|
118
|
+
sessionName = this._resolveSessionName(sessionName);
|
|
119
|
+
if (!await this._canConnect(sessionName)) {
|
|
120
|
+
console.log(`Session '${sessionName}' is not running.`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const session = await this._connect(sessionName);
|
|
124
|
+
await session.stop();
|
|
125
|
+
console.log(`Session '${sessionName}' stopped.`);
|
|
126
|
+
}
|
|
127
|
+
async delete(sessionName) {
|
|
128
|
+
sessionName = this._resolveSessionName(sessionName);
|
|
129
|
+
if (await this._canConnect(sessionName)) {
|
|
130
|
+
const session = await this._connect(sessionName);
|
|
131
|
+
await session.stop();
|
|
132
|
+
}
|
|
133
|
+
const userDataDir = import_path.default.resolve(daemonSocketDir, `${sessionName}-user-data`);
|
|
134
|
+
try {
|
|
135
|
+
await import_fs.default.promises.rm(userDataDir, { recursive: true });
|
|
136
|
+
console.log(`Deleted user data for session '${sessionName}'.`);
|
|
137
|
+
} catch (e) {
|
|
138
|
+
if (e.code === "ENOENT")
|
|
139
|
+
console.log(`No user data found for session '${sessionName}'.`);
|
|
140
|
+
else
|
|
141
|
+
throw e;
|
|
142
|
+
}
|
|
143
|
+
if (import_os.default.platform() !== "win32") {
|
|
144
|
+
const socketPath = this._daemonSocketPath(sessionName);
|
|
145
|
+
await import_fs.default.promises.unlink(socketPath).catch(() => {
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async _connect(sessionName) {
|
|
150
|
+
const socketPath = process.env.PLAYWRIGHT_DAEMON_SOCKET_PATH || this._daemonSocketPath(sessionName);
|
|
151
|
+
debugCli(`Connecting to daemon at ${socketPath}`);
|
|
152
|
+
const socketExists = await import_fs.default.promises.stat(socketPath).then((stat) => stat?.isSocket() ?? false).catch(() => false);
|
|
153
|
+
if (socketExists) {
|
|
154
|
+
debugCli(`Socket file exists, attempting to connect...`);
|
|
155
|
+
try {
|
|
156
|
+
return await this._connectToSocket(sessionName, socketPath);
|
|
157
|
+
} catch (e) {
|
|
158
|
+
if (import_os.default.platform() !== "win32")
|
|
159
|
+
await import_fs.default.promises.unlink(socketPath).catch(() => {
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (process.env.PLAYWRIGHT_DAEMON_SOCKET_PATH)
|
|
164
|
+
throw new Error(`Socket path ${socketPath} does not exist`);
|
|
165
|
+
const userDataDir = import_path.default.resolve(daemonSocketDir, `${sessionName}-user-data`);
|
|
166
|
+
const child = spawnDaemon(socketPath, userDataDir, {
|
|
167
|
+
detached: true,
|
|
168
|
+
stdio: "ignore",
|
|
169
|
+
cwd: process.cwd()
|
|
170
|
+
// Will be used as root.
|
|
171
|
+
});
|
|
172
|
+
child.unref();
|
|
173
|
+
const maxRetries = 50;
|
|
174
|
+
const retryDelay = 100;
|
|
175
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
176
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
177
|
+
try {
|
|
178
|
+
return await this._connectToSocket(sessionName, socketPath);
|
|
179
|
+
} catch (e) {
|
|
180
|
+
if (e.code !== "ENOENT")
|
|
181
|
+
throw e;
|
|
182
|
+
debugCli(`Retrying to connect to daemon at ${socketPath} (${i + 1}/${maxRetries})`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
throw new Error(`Failed to connect to daemon at ${socketPath} after ${maxRetries * retryDelay}ms`);
|
|
186
|
+
}
|
|
187
|
+
async _connectToSocket(sessionName, socketPath) {
|
|
188
|
+
const socket = await new Promise((resolve, reject) => {
|
|
189
|
+
const socket2 = import_net.default.createConnection(socketPath, () => {
|
|
190
|
+
debugCli(`Connected to daemon at ${socketPath}`);
|
|
191
|
+
resolve(socket2);
|
|
192
|
+
});
|
|
193
|
+
socket2.on("error", reject);
|
|
194
|
+
});
|
|
195
|
+
return new Session(sessionName, new import_socketConnection.SocketConnection(socket));
|
|
196
|
+
}
|
|
197
|
+
async _canConnect(sessionName) {
|
|
198
|
+
const socketPath = this._daemonSocketPath(sessionName);
|
|
199
|
+
return new Promise((resolve) => {
|
|
200
|
+
const socket = import_net.default.createConnection(socketPath, () => {
|
|
201
|
+
socket.destroy();
|
|
202
|
+
resolve(true);
|
|
203
|
+
});
|
|
204
|
+
socket.on("error", () => {
|
|
205
|
+
resolve(false);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
_resolveSessionName(sessionName) {
|
|
210
|
+
if (sessionName)
|
|
211
|
+
return sessionName;
|
|
212
|
+
if (process.env.PLAYWRIGHT_CLI_SESSION)
|
|
213
|
+
return process.env.PLAYWRIGHT_CLI_SESSION;
|
|
214
|
+
return "default";
|
|
215
|
+
}
|
|
216
|
+
_daemonSocketPath(sessionName) {
|
|
217
|
+
const socketName = `${sessionName}.sock`;
|
|
218
|
+
if (import_os.default.platform() === "win32")
|
|
219
|
+
return `\\\\.\\pipe\\${socketDirHash}-${socketName}`;
|
|
220
|
+
return import_path.default.join(daemonSocketDir, socketName);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async function handleSessionCommand(sessionManager, args) {
|
|
224
|
+
const subcommand = args._[0].split("-").slice(1).join("-");
|
|
225
|
+
if (subcommand === "list") {
|
|
226
|
+
const sessions = await sessionManager.list();
|
|
227
|
+
console.log("Sessions:");
|
|
228
|
+
for (const session of sessions) {
|
|
229
|
+
const liveMarker = session.live ? " (live)" : "";
|
|
230
|
+
console.log(` ${session.name}${liveMarker}`);
|
|
231
|
+
}
|
|
232
|
+
if (sessions.length === 0)
|
|
233
|
+
console.log(" (no sessions)");
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (subcommand === "stop") {
|
|
237
|
+
await sessionManager.stop(args._[1]);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (subcommand === "stop-all") {
|
|
241
|
+
const sessions = await sessionManager.list();
|
|
242
|
+
for (const session of sessions)
|
|
243
|
+
await sessionManager.stop(session.name);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (subcommand === "delete") {
|
|
247
|
+
await sessionManager.delete(args._[1]);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
console.error(`Unknown session subcommand: ${subcommand}`);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
const socketDirHash = (() => {
|
|
254
|
+
const hash = import_crypto.default.createHash("sha1");
|
|
255
|
+
hash.update(require.resolve("../../../package.json"));
|
|
256
|
+
return hash.digest("hex");
|
|
257
|
+
})();
|
|
258
|
+
const daemonSocketDir = (() => {
|
|
259
|
+
let localCacheDir;
|
|
260
|
+
if (process.platform === "linux")
|
|
261
|
+
localCacheDir = process.env.XDG_CACHE_HOME || import_path.default.join(import_os.default.homedir(), ".cache");
|
|
262
|
+
if (process.platform === "darwin")
|
|
263
|
+
localCacheDir = import_path.default.join(import_os.default.homedir(), "Library", "Caches");
|
|
264
|
+
if (process.platform === "win32")
|
|
265
|
+
localCacheDir = process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local");
|
|
266
|
+
if (!localCacheDir)
|
|
267
|
+
throw new Error("Unsupported platform: " + process.platform);
|
|
268
|
+
return import_path.default.join(localCacheDir, "ms-playwright", "daemon", "daemon", socketDirHash);
|
|
269
|
+
})();
|
|
270
|
+
function spawnDaemon(socketPath, userDataDir, options) {
|
|
271
|
+
const cliPath = import_path.default.join(__dirname, "../../../cli.js");
|
|
272
|
+
debugCli(`Will launch daemon process: ${cliPath}`);
|
|
273
|
+
return (0, import_child_process.spawn)(process.execPath, [cliPath, "run-mcp-server", `--daemon=${socketPath}`, `--user-data-dir=${userDataDir}`], options);
|
|
274
|
+
}
|
|
275
|
+
async function program(options) {
|
|
276
|
+
const argv = process.argv.slice(2);
|
|
277
|
+
const args = require("minimist")(argv);
|
|
278
|
+
const help = require("./help.json");
|
|
279
|
+
const commandName = args._[0];
|
|
280
|
+
if (args.version || args.v) {
|
|
281
|
+
console.log(options.version);
|
|
282
|
+
process.exit(0);
|
|
283
|
+
}
|
|
284
|
+
const command = help.commands[commandName];
|
|
285
|
+
if (args.help || args.h) {
|
|
286
|
+
if (command) {
|
|
287
|
+
console.log(command);
|
|
288
|
+
} else {
|
|
289
|
+
console.log("playwright-cli - run playwright mcp commands from terminal\n");
|
|
290
|
+
console.log(help.global);
|
|
291
|
+
}
|
|
292
|
+
process.exit(0);
|
|
293
|
+
}
|
|
294
|
+
if (!command) {
|
|
295
|
+
console.error(`Unknown command: ${commandName}
|
|
296
|
+
`);
|
|
297
|
+
console.log(help.global);
|
|
298
|
+
process.exit(1);
|
|
299
|
+
}
|
|
300
|
+
const sessionManager = new SessionManager();
|
|
301
|
+
if (commandName.startsWith("session")) {
|
|
302
|
+
await handleSessionCommand(sessionManager, args);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
await sessionManager.run(args);
|
|
306
|
+
}
|
|
307
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
308
|
+
0 && (module.exports = {
|
|
309
|
+
program
|
|
310
|
+
});
|
package/lib/program.js
CHANGED
|
@@ -388,6 +388,7 @@ const testOptions = [
|
|
|
388
388
|
["--repeat-each <N>", { description: `Run each test N times (default: 1)` }],
|
|
389
389
|
["--reporter <reporter>", { description: `Reporter to use, comma-separated, can be ${import_config.builtInReporters.map((name) => `"${name}"`).join(", ")} (default: "${import_config.defaultReporter}")` }],
|
|
390
390
|
["--retries <retries>", { description: `Maximum retry count for flaky tests, zero for no retries (default: no retries)` }],
|
|
391
|
+
["--run-agents <mode>", { description: `Run agents to generate the code for page.perform`, choices: ["missing", "all", "none"], preset: "none" }],
|
|
391
392
|
["--shard <shard>", { description: `Shard tests and execute only the selected shard, specify in the form "current/all", 1-based, for example "3/5"` }],
|
|
392
393
|
["--test-list <file>", { description: `Path to a file containing a list of tests to run. See https://playwright.dev/docs/test-cli for more details.` }],
|
|
393
394
|
["--test-list-invert <file>", { description: `Path to a file containing a list of tests to skip. See https://playwright.dev/docs/test-cli for more details.` }],
|
package/lib/reporters/base.js
CHANGED
|
@@ -368,10 +368,14 @@ function formatFailure(screen, config, test, index, options) {
|
|
|
368
368
|
resultLines.push(screen.colors.dim(` ${relativePath}`));
|
|
369
369
|
if (attachment.name === "trace") {
|
|
370
370
|
const packageManagerCommand = (0, import_utils.getPackageManagerExecCommand)();
|
|
371
|
-
resultLines.push(screen.colors.dim(` Usage:`));
|
|
371
|
+
resultLines.push(screen.colors.dim(` Usage (GUI):`));
|
|
372
372
|
resultLines.push("");
|
|
373
373
|
resultLines.push(screen.colors.dim(` ${packageManagerCommand} playwright show-trace ${quotePathIfNeeded(relativePath)}`));
|
|
374
374
|
resultLines.push("");
|
|
375
|
+
resultLines.push(screen.colors.dim(` Usage (markdown, LLM friendly):`));
|
|
376
|
+
resultLines.push("");
|
|
377
|
+
resultLines.push(screen.colors.dim(` ${packageManagerCommand} playwright export-trace ${quotePathIfNeeded(relativePath)}`));
|
|
378
|
+
resultLines.push("");
|
|
375
379
|
}
|
|
376
380
|
} else {
|
|
377
381
|
if (attachment.contentType.startsWith("text/") && attachment.body) {
|
|
@@ -187,8 +187,8 @@ function setTransformData(pluginName, value) {
|
|
|
187
187
|
}
|
|
188
188
|
function transformHook(originalCode, filename, moduleUrl) {
|
|
189
189
|
let inputSourceMap;
|
|
190
|
-
if (filename.endsWith(".md")
|
|
191
|
-
const transformed = transformMDToTS(originalCode, filename);
|
|
190
|
+
if (filename.endsWith(".md")) {
|
|
191
|
+
const transformed = (0, import_md.transformMDToTS)(originalCode, filename);
|
|
192
192
|
originalCode = transformed.code;
|
|
193
193
|
inputSourceMap = transformed.map;
|
|
194
194
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pedropaulovc/playwright",
|
|
3
|
-
"version": "1.59.0-next",
|
|
3
|
+
"version": "1.59.0-next.1",
|
|
4
4
|
"description": "A high-level API to automate web browsers",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
|
-
"url": "git+https://github.com/
|
|
7
|
+
"url": "git+https://github.com/pedropaulovc/playwright.git"
|
|
8
8
|
},
|
|
9
9
|
"homepage": "https://playwright.dev",
|
|
10
10
|
"engines": {
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"./lib/mcp/program": "./lib/mcp/program.js",
|
|
27
27
|
"./lib/mcp/sdk/bundle": "./lib/mcp/sdk/bundle.js",
|
|
28
28
|
"./lib/mcp/sdk/exports": "./lib/mcp/sdk/exports.js",
|
|
29
|
+
"./lib/mcp/terminal/program": "./lib/mcp/terminal/program.js",
|
|
29
30
|
"./lib/program": "./lib/program.js",
|
|
30
31
|
"./lib/reporters/base": "./lib/reporters/base.js",
|
|
31
32
|
"./lib/reporters/list": "./lib/reporters/list.js",
|
|
@@ -64,7 +65,7 @@
|
|
|
64
65
|
},
|
|
65
66
|
"license": "Apache-2.0",
|
|
66
67
|
"dependencies": {
|
|
67
|
-
"playwright-core": "1.59.0-next"
|
|
68
|
+
"@pedropaulovc/playwright-core": "1.59.0-next.1"
|
|
68
69
|
},
|
|
69
70
|
"optionalDependencies": {
|
|
70
71
|
"fsevents": "2.3.2"
|
package/types/test.d.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions, Page, LaunchOptions, ViewportSize, Geolocation, HTTPCredentials, Locator, APIResponse, PageScreenshotOptions } from 'playwright-core';
|
|
18
|
+
import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions, Page, PageAgent, LaunchOptions, ViewportSize, Geolocation, HTTPCredentials, Locator, APIResponse, PageScreenshotOptions } from 'playwright-core';
|
|
19
19
|
export * from 'playwright-core';
|
|
20
20
|
|
|
21
21
|
export type BlobReporterOptions = { outputDir?: string, fileName?: string };
|
|
@@ -1611,6 +1611,14 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
|
|
|
1611
1611
|
*/
|
|
1612
1612
|
retries?: number;
|
|
1613
1613
|
|
|
1614
|
+
/**
|
|
1615
|
+
* Whether to run LLM agent for [PageAgent](https://playwright.dev/docs/api/class-pageagent):
|
|
1616
|
+
* - "all" disregards existing cache and performs all actions via LLM
|
|
1617
|
+
* - "missing" only performs actions that don't have generated cache actions
|
|
1618
|
+
* - "none" does not talk to LLM at all, relies on the cached actions (default)
|
|
1619
|
+
*/
|
|
1620
|
+
runAgents?: "all"|"missing"|"none";
|
|
1621
|
+
|
|
1614
1622
|
/**
|
|
1615
1623
|
* Shard tests and execute only the selected shard. Specify in the one-based form like `{ total: 5, current: 2 }`.
|
|
1616
1624
|
*
|
|
@@ -2067,6 +2075,14 @@ export interface FullConfig<TestArgs = {}, WorkerArgs = {}> {
|
|
|
2067
2075
|
*/
|
|
2068
2076
|
rootDir: string;
|
|
2069
2077
|
|
|
2078
|
+
/**
|
|
2079
|
+
* Whether to run LLM agent for [PageAgent](https://playwright.dev/docs/api/class-pageagent):
|
|
2080
|
+
* - "all" disregards existing cache and performs all actions via LLM
|
|
2081
|
+
* - "missing" only performs actions that don't have generated cache actions
|
|
2082
|
+
* - "none" does not talk to LLM at all, relies on the cached actions (default)
|
|
2083
|
+
*/
|
|
2084
|
+
runAgents: "all"|"missing"|"none";
|
|
2085
|
+
|
|
2070
2086
|
/**
|
|
2071
2087
|
* See [testConfig.shard](https://playwright.dev/docs/api/class-testconfig#test-config-shard).
|
|
2072
2088
|
*/
|
|
@@ -6934,6 +6950,24 @@ export interface PlaywrightWorkerOptions {
|
|
|
6934
6950
|
export type ScreenshotMode = 'off' | 'on' | 'only-on-failure' | 'on-first-failure';
|
|
6935
6951
|
export type TraceMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | 'on-all-retries' | 'retain-on-first-failure';
|
|
6936
6952
|
export type VideoMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry';
|
|
6953
|
+
export type AgentOptions = {
|
|
6954
|
+
provider?: {
|
|
6955
|
+
api: 'openai' | 'openai-compatible' | 'anthropic' | 'google';
|
|
6956
|
+
apiEndpoint?: string;
|
|
6957
|
+
apiKey: string;
|
|
6958
|
+
apiTimeout?: number;
|
|
6959
|
+
model: string;
|
|
6960
|
+
},
|
|
6961
|
+
limits?: {
|
|
6962
|
+
maxTokens?: number;
|
|
6963
|
+
maxActions?: number;
|
|
6964
|
+
maxActionRetries?: number;
|
|
6965
|
+
};
|
|
6966
|
+
cachePathTemplate?: string;
|
|
6967
|
+
runAgents?: 'all' | 'missing' | 'none';
|
|
6968
|
+
secrets?: { [key: string]: string };
|
|
6969
|
+
systemPrompt?: string;
|
|
6970
|
+
};
|
|
6937
6971
|
|
|
6938
6972
|
/**
|
|
6939
6973
|
* Playwright Test provides many options to configure test environment,
|
|
@@ -6974,6 +7008,7 @@ export type VideoMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry';
|
|
|
6974
7008
|
*
|
|
6975
7009
|
*/
|
|
6976
7010
|
export interface PlaywrightTestOptions {
|
|
7011
|
+
agentOptions: AgentOptions | undefined;
|
|
6977
7012
|
/**
|
|
6978
7013
|
* Whether to automatically download all the attachments. Defaults to `true` where all the downloads are accepted.
|
|
6979
7014
|
*
|
|
@@ -7683,6 +7718,7 @@ export interface PlaywrightTestArgs {
|
|
|
7683
7718
|
*
|
|
7684
7719
|
*/
|
|
7685
7720
|
request: APIRequestContext;
|
|
7721
|
+
agent: PageAgent;
|
|
7686
7722
|
}
|
|
7687
7723
|
|
|
7688
7724
|
type ExcludeProps<A, B> = {
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __copyProps = (to, from, except, desc) => {
|
|
7
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
-
for (let key of __getOwnPropNames(from))
|
|
9
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
-
}
|
|
12
|
-
return to;
|
|
13
|
-
};
|
|
14
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
-
var actions_d_exports = {};
|
|
16
|
-
module.exports = __toCommonJS(actions_d_exports);
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
var navigateAndWait_exports = {};
|
|
20
|
-
__export(navigateAndWait_exports, {
|
|
21
|
-
default: () => navigateAndWait_default
|
|
22
|
-
});
|
|
23
|
-
module.exports = __toCommonJS(navigateAndWait_exports);
|
|
24
|
-
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
|
25
|
-
var import_tool = require("./tool");
|
|
26
|
-
const navigateThenWaitFor = (0, import_tool.defineTool)({
|
|
27
|
-
capability: "core",
|
|
28
|
-
schema: {
|
|
29
|
-
name: "browser_navigate_then_wait_for",
|
|
30
|
-
title: "Navigate to URL and wait",
|
|
31
|
-
description: "Navigate to a URL and then wait for text to appear or disappear or a specified time to pass",
|
|
32
|
-
inputSchema: import_mcpBundle.z.object({
|
|
33
|
-
url: import_mcpBundle.z.string().describe("The URL to navigate to"),
|
|
34
|
-
time: import_mcpBundle.z.number().optional().describe("The time to wait in seconds after navigation"),
|
|
35
|
-
text: import_mcpBundle.z.string().optional().describe("The text to wait for after navigation"),
|
|
36
|
-
textGone: import_mcpBundle.z.string().optional().describe("The text to wait for to disappear after navigation")
|
|
37
|
-
}),
|
|
38
|
-
type: "action"
|
|
39
|
-
},
|
|
40
|
-
handle: async (context, params, response) => {
|
|
41
|
-
if (!params.text && !params.textGone && !params.time)
|
|
42
|
-
throw new Error("Either time, text or textGone must be provided");
|
|
43
|
-
const tab = await context.ensureTab();
|
|
44
|
-
await tab.navigate(params.url);
|
|
45
|
-
response.addCode(`await page.goto('${params.url}');`);
|
|
46
|
-
if (params.time) {
|
|
47
|
-
response.addCode(`await new Promise(f => setTimeout(f, ${params.time} * 1000));`);
|
|
48
|
-
await new Promise((f) => setTimeout(f, Math.min(3e4, params.time * 1e3)));
|
|
49
|
-
}
|
|
50
|
-
const locator = params.text ? tab.page.getByText(params.text).first() : void 0;
|
|
51
|
-
const goneLocator = params.textGone ? tab.page.getByText(params.textGone).first() : void 0;
|
|
52
|
-
if (goneLocator) {
|
|
53
|
-
response.addCode(`await page.getByText(${JSON.stringify(params.textGone)}).first().waitFor({ state: 'hidden' });`);
|
|
54
|
-
await goneLocator.waitFor({ state: "hidden" });
|
|
55
|
-
}
|
|
56
|
-
if (locator) {
|
|
57
|
-
response.addCode(`await page.getByText(${JSON.stringify(params.text)}).first().waitFor({ state: 'visible' });`);
|
|
58
|
-
await locator.waitFor({ state: "visible" });
|
|
59
|
-
}
|
|
60
|
-
response.addResult(`Navigated to ${params.url} and waited for ${params.text || params.textGone || params.time}`);
|
|
61
|
-
response.setIncludeSnapshot();
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
var navigateAndWait_default = [
|
|
65
|
-
navigateThenWaitFor
|
|
66
|
-
];
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
var snapshotViewport_exports = {};
|
|
30
|
-
__export(snapshotViewport_exports, {
|
|
31
|
-
default: () => snapshotViewport_default
|
|
32
|
-
});
|
|
33
|
-
module.exports = __toCommonJS(snapshotViewport_exports);
|
|
34
|
-
var import_fs = __toESM(require("fs"));
|
|
35
|
-
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
|
36
|
-
var import_tool = require("./tool");
|
|
37
|
-
const snapshotViewport = (0, import_tool.defineTool)({
|
|
38
|
-
capability: "core",
|
|
39
|
-
schema: {
|
|
40
|
-
name: "browser_snapshot_viewport",
|
|
41
|
-
title: "Viewport snapshot",
|
|
42
|
-
description: "Capture accessibility snapshot of only what is visible in the current viewport",
|
|
43
|
-
inputSchema: import_mcpBundle.z.object({
|
|
44
|
-
filename: import_mcpBundle.z.string().optional().describe("Save snapshot to markdown file instead of returning it in the response.")
|
|
45
|
-
}),
|
|
46
|
-
type: "readOnly"
|
|
47
|
-
},
|
|
48
|
-
handle: async (context, params, response) => {
|
|
49
|
-
const tab = await context.ensureTab();
|
|
50
|
-
const snapshotResult = await tab.page._snapshotForAI({ track: "response", viewportOnly: true });
|
|
51
|
-
const viewportSnapshot = snapshotResult.full;
|
|
52
|
-
if (params.filename) {
|
|
53
|
-
const fileName = await response.addFile(params.filename, { origin: "llm", reason: "Saved viewport snapshot" });
|
|
54
|
-
await import_fs.default.promises.writeFile(fileName, viewportSnapshot);
|
|
55
|
-
response.setIncludeMetaOnly();
|
|
56
|
-
} else {
|
|
57
|
-
response.addResult(viewportSnapshot);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
var snapshotViewport_default = [
|
|
62
|
-
snapshotViewport
|
|
63
|
-
];
|