playwright 1.58.0-alpha-2025-12-04 → 1.58.0-alpha-2025-12-05
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 +2 -2
- package/lib/agents/performTask.js +110 -2
- package/lib/index.js +5 -2
- package/lib/mcp/browser/browserContextFactory.js +2 -0
- package/lib/mcp/browser/config.js +21 -2
- package/lib/mcp/browser/tab.js +37 -3
- package/lib/mcp/browser/tools/console.js +2 -2
- package/lib/mcp/browser/tools/network.js +16 -10
- package/lib/mcp/program.js +1 -1
- package/lib/mcp/test/browserBackend.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 🎭 Playwright
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
|
4
4
|
|
|
5
5
|
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr
|
|
|
10
10
|
| :--- | :---: | :---: | :---: |
|
|
11
11
|
| Chromium <!-- GEN:chromium-version -->143.0.7499.25<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
|
12
12
|
| WebKit <!-- GEN:webkit-version -->26.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
|
13
|
-
| Firefox <!-- GEN:firefox-version -->
|
|
13
|
+
| Firefox <!-- GEN:firefox-version -->145.0.1<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
|
14
14
|
|
|
15
15
|
Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details.
|
|
16
16
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,19 +17,44 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
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
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
var performTask_exports = {};
|
|
20
30
|
__export(performTask_exports, {
|
|
31
|
+
performCache: () => performCache,
|
|
21
32
|
performTask: () => performTask
|
|
22
33
|
});
|
|
23
34
|
module.exports = __toCommonJS(performTask_exports);
|
|
35
|
+
var import_fs = __toESM(require("fs"));
|
|
36
|
+
var import_path = __toESM(require("path"));
|
|
24
37
|
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
|
25
38
|
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
|
26
39
|
var import_browserContextFactory = require("../mcp/browser/browserContextFactory");
|
|
27
40
|
var import_browserServerBackend = require("../mcp/browser/browserServerBackend");
|
|
28
41
|
var import_config = require("../mcp/browser/config");
|
|
29
42
|
var import_server = require("../mcp/sdk/server");
|
|
30
|
-
|
|
43
|
+
const resultSchema = import_mcpBundle.z.object({
|
|
44
|
+
code: import_mcpBundle.z.string().optional().describe(`
|
|
45
|
+
Generated code to perform the task using Playwright API.
|
|
46
|
+
Check out the <code> blocks and combine them. Should be presented in the following form:
|
|
47
|
+
|
|
48
|
+
perform(async ({ page }) => {
|
|
49
|
+
// generated code here.
|
|
50
|
+
});
|
|
51
|
+
`),
|
|
52
|
+
error: import_mcpBundle.z.string().optional().describe("The error that occurred if execution failed.").optional()
|
|
53
|
+
});
|
|
54
|
+
async function performTask(testInfo, context, userTask, options) {
|
|
55
|
+
const cacheStatus = await performTaskFromCache(testInfo, context, userTask);
|
|
56
|
+
if (cacheStatus === "success")
|
|
57
|
+
return;
|
|
31
58
|
const backend = new import_browserServerBackend.BrowserServerBackend(import_config.defaultConfig, (0, import_browserContextFactory.identityBrowserContextFactory)(context));
|
|
32
59
|
const client = await (0, import_server.wrapInClient)(backend, { name: "Internal", version: "0.0.0" });
|
|
33
60
|
const callTool = async (params) => {
|
|
@@ -44,12 +71,93 @@ async function performTask(context, task, options) {
|
|
|
44
71
|
tools: await backend.listTools()
|
|
45
72
|
});
|
|
46
73
|
try {
|
|
47
|
-
|
|
74
|
+
const result = await loop.run(userTask, { resultSchema: (0, import_mcpBundle.zodToJsonSchema)(resultSchema) });
|
|
75
|
+
if (result.code)
|
|
76
|
+
await updatePerformFile(testInfo, userTask, result.code, options);
|
|
48
77
|
} finally {
|
|
49
78
|
await client.close();
|
|
50
79
|
}
|
|
51
80
|
}
|
|
81
|
+
async function updatePerformFile(testInfo, userTask, taskCode, options) {
|
|
82
|
+
const relativeFile = import_path.default.relative(testInfo.project.testDir, testInfo.file);
|
|
83
|
+
const promptCacheFile = testInfo.file.replace(".spec.ts", ".cache.ts");
|
|
84
|
+
const testTitle = testInfo.title;
|
|
85
|
+
const loop = new import_mcpBundle.Loop(options.provider ?? "github", {
|
|
86
|
+
model: options.model ?? "claude-sonnet-4.5",
|
|
87
|
+
reasoning: options.reasoning,
|
|
88
|
+
temperature: options.temperature,
|
|
89
|
+
maxTokens: options.maxTokens,
|
|
90
|
+
summarize: true,
|
|
91
|
+
debug: import_utilsBundle.debug,
|
|
92
|
+
callTool: async () => ({ content: [] }),
|
|
93
|
+
tools: []
|
|
94
|
+
});
|
|
95
|
+
const resultSchema2 = import_mcpBundle.z.object({
|
|
96
|
+
code: import_mcpBundle.z.string().optional().describe(`
|
|
97
|
+
Generated code with all the perofrm routines combined or updated into the following format:
|
|
98
|
+
|
|
99
|
+
import { performCache } from '@playwright/test';
|
|
100
|
+
|
|
101
|
+
performCache({
|
|
102
|
+
file: 'tests/page/perform-task.spec.ts',
|
|
103
|
+
test: 'perform task',
|
|
104
|
+
task: 'Click the learn more button',
|
|
105
|
+
code: async ({ page }) => {
|
|
106
|
+
await page.getByRole('link', { name: 'Learn more' }).click();
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
`)
|
|
110
|
+
});
|
|
111
|
+
const existingCode = await import_fs.default.promises.readFile(promptCacheFile, "utf8").catch(() => "");
|
|
112
|
+
const task = `
|
|
113
|
+
- Create or update a perform file to include performCache block for the given task and code.
|
|
114
|
+
- Dedupe items with the same file, test, and task.
|
|
115
|
+
- Should produce code in the following format
|
|
116
|
+
|
|
117
|
+
import { performCache } from '@playwright/test';
|
|
118
|
+
|
|
119
|
+
performCache({
|
|
120
|
+
file: '<file>',
|
|
121
|
+
test: '<test>',
|
|
122
|
+
task: '<task>',
|
|
123
|
+
code: async ({ page }) => {
|
|
124
|
+
<code>
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
performCache({
|
|
129
|
+
...
|
|
130
|
+
|
|
131
|
+
## Params for the new or updated performCache block
|
|
132
|
+
<file-content>${existingCode}</file-content>
|
|
133
|
+
<file>${relativeFile}</file>
|
|
134
|
+
<test>${testTitle}</test>
|
|
135
|
+
<task>${userTask}</task>
|
|
136
|
+
<code>${taskCode}</code>
|
|
137
|
+
`;
|
|
138
|
+
const result = await loop.run(task, { resultSchema: (0, import_mcpBundle.zodToJsonSchema)(resultSchema2) });
|
|
139
|
+
if (result.code)
|
|
140
|
+
await import_fs.default.promises.writeFile(promptCacheFile, result.code);
|
|
141
|
+
}
|
|
142
|
+
const performCacheMap = /* @__PURE__ */ new Map();
|
|
143
|
+
function performCache(entry) {
|
|
144
|
+
performCacheMap.set(JSON.stringify({ ...entry, code: void 0 }), entry);
|
|
145
|
+
}
|
|
146
|
+
async function performTaskFromCache(testInfo, context, userTask) {
|
|
147
|
+
const relativeFile = import_path.default.relative(testInfo.project.testDir, testInfo.file);
|
|
148
|
+
const key = JSON.stringify({ file: relativeFile, test: testInfo.title, task: userTask });
|
|
149
|
+
const entry = performCacheMap.get(key);
|
|
150
|
+
if (!entry)
|
|
151
|
+
return "cache-miss";
|
|
152
|
+
try {
|
|
153
|
+
await entry.code({ page: context.pages()[0] });
|
|
154
|
+
return "success";
|
|
155
|
+
} catch (error) {
|
|
156
|
+
return error;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
52
159
|
// Annotate the CommonJS export names for ESM import in node:
|
|
53
160
|
0 && (module.exports = {
|
|
161
|
+
performCache,
|
|
54
162
|
performTask
|
|
55
163
|
});
|
package/lib/index.js
CHANGED
|
@@ -33,6 +33,7 @@ __export(index_exports, {
|
|
|
33
33
|
expect: () => import_expect.expect,
|
|
34
34
|
mergeExpects: () => import_expect2.mergeExpects,
|
|
35
35
|
mergeTests: () => import_testType2.mergeTests,
|
|
36
|
+
performCache: () => import_performTask2.performCache,
|
|
36
37
|
test: () => test
|
|
37
38
|
});
|
|
38
39
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -48,6 +49,7 @@ var import_expect = require("./matchers/expect");
|
|
|
48
49
|
var import_configLoader = require("./common/configLoader");
|
|
49
50
|
var import_testType2 = require("./common/testType");
|
|
50
51
|
var import_expect2 = require("./matchers/expect");
|
|
52
|
+
var import_performTask2 = require("./agents/performTask");
|
|
51
53
|
const _baseTest = import_testType.rootTestType.test;
|
|
52
54
|
(0, import_utils.setBoxedStackPrefixes)([import_path.default.dirname(require.resolve("../package.json"))]);
|
|
53
55
|
if (process["__pw_initiator__"]) {
|
|
@@ -417,9 +419,9 @@ const playwrightFixtures = {
|
|
|
417
419
|
await request.dispose();
|
|
418
420
|
}
|
|
419
421
|
},
|
|
420
|
-
_perform: async ({ context }, use) => {
|
|
422
|
+
_perform: async ({ context }, use, testInfo) => {
|
|
421
423
|
await use(async (task, options) => {
|
|
422
|
-
await (0, import_performTask.performTask)(context, task, options ?? {});
|
|
424
|
+
await (0, import_performTask.performTask)(testInfo, context, task, options ?? {});
|
|
423
425
|
});
|
|
424
426
|
}
|
|
425
427
|
};
|
|
@@ -684,5 +686,6 @@ const test = _baseTest.extend(playwrightFixtures);
|
|
|
684
686
|
expect,
|
|
685
687
|
mergeExpects,
|
|
686
688
|
mergeTests,
|
|
689
|
+
performCache,
|
|
687
690
|
test
|
|
688
691
|
});
|
|
@@ -216,6 +216,8 @@ class PersistentContextFactory {
|
|
|
216
216
|
});
|
|
217
217
|
await afterClose();
|
|
218
218
|
this._userDataDirs.delete(userDataDir);
|
|
219
|
+
if (process.env.PWMCP_PROFILES_DIR_FOR_TEST && userDataDir.startsWith(process.env.PWMCP_PROFILES_DIR_FOR_TEST))
|
|
220
|
+
await import_fs.default.promises.rm(userDataDir, { recursive: true }).catch(import_log.logUnhandledError);
|
|
219
221
|
(0, import_log.testDebug)("close browser context complete (persistent)");
|
|
220
222
|
}
|
|
221
223
|
async _createUserDataDir(clientInfo) {
|
|
@@ -32,6 +32,7 @@ __export(config_exports, {
|
|
|
32
32
|
configFromCLIOptions: () => configFromCLIOptions,
|
|
33
33
|
defaultConfig: () => defaultConfig,
|
|
34
34
|
dotenvFileLoader: () => dotenvFileLoader,
|
|
35
|
+
enumParser: () => enumParser,
|
|
35
36
|
headerParser: () => headerParser,
|
|
36
37
|
numberParser: () => numberParser,
|
|
37
38
|
outputDir: () => outputDir,
|
|
@@ -61,6 +62,9 @@ const defaultConfig = {
|
|
|
61
62
|
viewport: null
|
|
62
63
|
}
|
|
63
64
|
},
|
|
65
|
+
console: {
|
|
66
|
+
level: "info"
|
|
67
|
+
},
|
|
64
68
|
network: {
|
|
65
69
|
allowedOrigins: void 0,
|
|
66
70
|
blockedOrigins: void 0
|
|
@@ -182,6 +186,9 @@ function configFromCLIOptions(cliOptions) {
|
|
|
182
186
|
allowedHosts: cliOptions.allowedHosts
|
|
183
187
|
},
|
|
184
188
|
capabilities: cliOptions.caps,
|
|
189
|
+
console: {
|
|
190
|
+
level: cliOptions.consoleLevel
|
|
191
|
+
},
|
|
185
192
|
network: {
|
|
186
193
|
allowedOrigins: cliOptions.allowedOrigins,
|
|
187
194
|
blockedOrigins: cliOptions.blockedOrigins
|
|
@@ -213,6 +220,8 @@ function configFromEnv() {
|
|
|
213
220
|
options.cdpEndpoint = envToString(process.env.PLAYWRIGHT_MCP_CDP_ENDPOINT);
|
|
214
221
|
options.cdpHeader = headerParser(process.env.PLAYWRIGHT_MCP_CDP_HEADERS, {});
|
|
215
222
|
options.config = envToString(process.env.PLAYWRIGHT_MCP_CONFIG);
|
|
223
|
+
if (process.env.PLAYWRIGHT_MCP_CONSOLE_LEVEL)
|
|
224
|
+
options.consoleLevel = enumParser("--console-level", ["error", "warning", "info", "debug"], process.env.PLAYWRIGHT_MCP_CONSOLE_LEVEL);
|
|
216
225
|
options.device = envToString(process.env.PLAYWRIGHT_MCP_DEVICE);
|
|
217
226
|
options.executablePath = envToString(process.env.PLAYWRIGHT_MCP_EXECUTABLE_PATH);
|
|
218
227
|
options.grantPermissions = commaSeparatedList(process.env.PLAYWRIGHT_MCP_GRANT_PERMISSIONS);
|
|
@@ -226,8 +235,8 @@ function configFromEnv() {
|
|
|
226
235
|
if (initScript)
|
|
227
236
|
options.initScript = [initScript];
|
|
228
237
|
options.isolated = envToBoolean(process.env.PLAYWRIGHT_MCP_ISOLATED);
|
|
229
|
-
if (process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES
|
|
230
|
-
options.imageResponses = "omit";
|
|
238
|
+
if (process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES)
|
|
239
|
+
options.imageResponses = enumParser("--image-responses", ["allow", "omit"], process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES);
|
|
231
240
|
options.sandbox = envToBoolean(process.env.PLAYWRIGHT_MCP_SANDBOX);
|
|
232
241
|
options.outputDir = envToString(process.env.PLAYWRIGHT_MCP_OUTPUT_DIR);
|
|
233
242
|
options.port = numberParser(process.env.PLAYWRIGHT_MCP_PORT);
|
|
@@ -306,6 +315,10 @@ function mergeConfig(base, overrides) {
|
|
|
306
315
|
...pickDefined(base),
|
|
307
316
|
...pickDefined(overrides),
|
|
308
317
|
browser,
|
|
318
|
+
console: {
|
|
319
|
+
...pickDefined(base.console),
|
|
320
|
+
...pickDefined(overrides.console)
|
|
321
|
+
},
|
|
309
322
|
network: {
|
|
310
323
|
...pickDefined(base.network),
|
|
311
324
|
...pickDefined(overrides.network)
|
|
@@ -369,6 +382,11 @@ function headerParser(arg, previous) {
|
|
|
369
382
|
result[name] = value;
|
|
370
383
|
return result;
|
|
371
384
|
}
|
|
385
|
+
function enumParser(name, options, value) {
|
|
386
|
+
if (!options.includes(value))
|
|
387
|
+
throw new Error(`Invalid ${name}: ${value}. Valid values are: ${options.join(", ")}`);
|
|
388
|
+
return value;
|
|
389
|
+
}
|
|
372
390
|
function envToBoolean(value) {
|
|
373
391
|
if (value === "true" || value === "1")
|
|
374
392
|
return true;
|
|
@@ -392,6 +410,7 @@ function sanitizeForFilePath(s) {
|
|
|
392
410
|
configFromCLIOptions,
|
|
393
411
|
defaultConfig,
|
|
394
412
|
dotenvFileLoader,
|
|
413
|
+
enumParser,
|
|
395
414
|
headerParser,
|
|
396
415
|
numberParser,
|
|
397
416
|
outputDir,
|
package/lib/mcp/browser/tab.js
CHANGED
|
@@ -173,9 +173,9 @@ class Tab extends import_events.EventEmitter {
|
|
|
173
173
|
}
|
|
174
174
|
await this.waitForLoadState("load", { timeout: 5e3 });
|
|
175
175
|
}
|
|
176
|
-
async consoleMessages(
|
|
176
|
+
async consoleMessages(level) {
|
|
177
177
|
await this._initializedPromise;
|
|
178
|
-
return this._consoleMessages.filter((message) =>
|
|
178
|
+
return this._consoleMessages.filter((message) => shouldIncludeMessage(level, message.type));
|
|
179
179
|
}
|
|
180
180
|
async requests() {
|
|
181
181
|
await this._initializedPromise;
|
|
@@ -197,7 +197,7 @@ class Tab extends import_events.EventEmitter {
|
|
|
197
197
|
};
|
|
198
198
|
});
|
|
199
199
|
if (tabSnapshot) {
|
|
200
|
-
tabSnapshot.consoleMessages = this._recentConsoleMessages;
|
|
200
|
+
tabSnapshot.consoleMessages = this._recentConsoleMessages.filter((message) => shouldIncludeMessage(this.context.config.console.level, message.type));
|
|
201
201
|
this._recentConsoleMessages = [];
|
|
202
202
|
}
|
|
203
203
|
this._needsFullSnapshot = !tabSnapshot;
|
|
@@ -287,6 +287,40 @@ function renderModalStates(modalStates) {
|
|
|
287
287
|
result.push(`- [${state.description}]: can be handled by the "${state.clearedBy}" tool`);
|
|
288
288
|
return result;
|
|
289
289
|
}
|
|
290
|
+
const consoleMessageLevels = ["error", "warning", "info", "debug"];
|
|
291
|
+
function shouldIncludeMessage(thresholdLevel, type) {
|
|
292
|
+
const messageLevel = consoleLevelForMessageType(type);
|
|
293
|
+
return consoleMessageLevels.indexOf(messageLevel) <= consoleMessageLevels.indexOf(thresholdLevel);
|
|
294
|
+
}
|
|
295
|
+
function consoleLevelForMessageType(type) {
|
|
296
|
+
switch (type) {
|
|
297
|
+
case "assert":
|
|
298
|
+
case "error":
|
|
299
|
+
return "error";
|
|
300
|
+
case "warning":
|
|
301
|
+
return "warning";
|
|
302
|
+
case "count":
|
|
303
|
+
case "dir":
|
|
304
|
+
case "dirxml":
|
|
305
|
+
case "info":
|
|
306
|
+
case "log":
|
|
307
|
+
case "table":
|
|
308
|
+
case "time":
|
|
309
|
+
case "timeEnd":
|
|
310
|
+
return "info";
|
|
311
|
+
case "clear":
|
|
312
|
+
case "debug":
|
|
313
|
+
case "endGroup":
|
|
314
|
+
case "profile":
|
|
315
|
+
case "profileEnd":
|
|
316
|
+
case "startGroup":
|
|
317
|
+
case "startGroupCollapsed":
|
|
318
|
+
case "trace":
|
|
319
|
+
return "debug";
|
|
320
|
+
default:
|
|
321
|
+
return "info";
|
|
322
|
+
}
|
|
323
|
+
}
|
|
290
324
|
const tabSymbol = Symbol("tabSymbol");
|
|
291
325
|
// Annotate the CommonJS export names for ESM import in node:
|
|
292
326
|
0 && (module.exports = {
|
|
@@ -30,12 +30,12 @@ const console = (0, import_tool.defineTabTool)({
|
|
|
30
30
|
title: "Get console messages",
|
|
31
31
|
description: "Returns all console messages",
|
|
32
32
|
inputSchema: import_mcpBundle.z.object({
|
|
33
|
-
|
|
33
|
+
level: import_mcpBundle.z.enum(["error", "warning", "info", "debug"]).default("info").describe('Level of the console messages to return. Each level includes the messages of more severe levels. Defaults to "info".')
|
|
34
34
|
}),
|
|
35
35
|
type: "readOnly"
|
|
36
36
|
},
|
|
37
37
|
handle: async (tab, params, response) => {
|
|
38
|
-
const messages = await tab.consoleMessages(params.
|
|
38
|
+
const messages = await tab.consoleMessages(params.level);
|
|
39
39
|
messages.map((message) => response.addResult(message.toString()));
|
|
40
40
|
}
|
|
41
41
|
});
|
|
@@ -29,24 +29,30 @@ const requests = (0, import_tool.defineTabTool)({
|
|
|
29
29
|
name: "browser_network_requests",
|
|
30
30
|
title: "List network requests",
|
|
31
31
|
description: "Returns all network requests since loading the page",
|
|
32
|
-
inputSchema: import_mcpBundle.z.object({
|
|
32
|
+
inputSchema: import_mcpBundle.z.object({
|
|
33
|
+
includeStatic: import_mcpBundle.z.boolean().default(false).describe("Whether to include successful static resources like images, fonts, scripts, etc. Defaults to false.")
|
|
34
|
+
}),
|
|
33
35
|
type: "readOnly"
|
|
34
36
|
},
|
|
35
37
|
handle: async (tab, params, response) => {
|
|
36
38
|
const requests2 = await tab.requests();
|
|
37
|
-
for (const request of requests2)
|
|
38
|
-
|
|
39
|
+
for (const request of requests2) {
|
|
40
|
+
const rendered = await renderRequest(request, params.includeStatic);
|
|
41
|
+
if (rendered)
|
|
42
|
+
response.addResult(rendered);
|
|
43
|
+
}
|
|
39
44
|
}
|
|
40
45
|
});
|
|
41
|
-
async function renderRequest(request) {
|
|
46
|
+
async function renderRequest(request, includeStatic) {
|
|
47
|
+
const response = request._hasResponse ? await request.response() : void 0;
|
|
48
|
+
const isStaticRequest = ["document", "stylesheet", "image", "media", "font", "script", "manifest"].includes(request.resourceType());
|
|
49
|
+
const isSuccessfulRequest = !response || response.status() < 400;
|
|
50
|
+
if (isStaticRequest && isSuccessfulRequest && !includeStatic)
|
|
51
|
+
return void 0;
|
|
42
52
|
const result = [];
|
|
43
53
|
result.push(`[${request.method().toUpperCase()}] ${request.url()}`);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const response = await request.response();
|
|
47
|
-
if (response)
|
|
48
|
-
result.push(`=> [${response.status()}] ${response.statusText()}`);
|
|
49
|
-
}
|
|
54
|
+
if (response)
|
|
55
|
+
result.push(`=> [${response.status()}] ${response.statusText()}`);
|
|
50
56
|
return result.join(" ");
|
|
51
57
|
}
|
|
52
58
|
var network_default = [
|
package/lib/mcp/program.js
CHANGED
|
@@ -42,7 +42,7 @@ var import_proxyBackend = require("./sdk/proxyBackend");
|
|
|
42
42
|
var import_browserServerBackend = require("./browser/browserServerBackend");
|
|
43
43
|
var import_extensionContextFactory = require("./extension/extensionContextFactory");
|
|
44
44
|
function decorateCommand(command, version) {
|
|
45
|
-
command.option("--allowed-hosts <hosts...>", "comma-separated list of hosts this server is allowed to serve from. Defaults to the host the server is bound to. Pass '*' to disable the host check.", import_config.commaSeparatedList).option("--allowed-origins <origins>", "semicolon-separated list of TRUSTED origins to allow the browser to request. Default is to allow all.\nImportant: *does not* serve as a security boundary and *does not* affect redirects. ", import_config.semicolonSeparatedList).option("--blocked-origins <origins>", "semicolon-separated list of origins to block the browser from requesting. Blocklist is evaluated before allowlist. If used without the allowlist, requests not matching the blocklist are still allowed.\nImportant: *does not* serve as a security boundary and *does not* affect redirects.", import_config.semicolonSeparatedList).option("--block-service-workers", "block service workers").option("--browser <browser>", "browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge.").option("--caps <caps>", "comma-separated list of additional capabilities to enable, possible values: vision, pdf.", import_config.commaSeparatedList).option("--cdp-endpoint <endpoint>", "CDP endpoint to connect to.").option("--cdp-header <headers...>", "CDP headers to send with the connect request, multiple can be specified.", import_config.headerParser).option("--config <path>", "path to the configuration file.").option("--device <device>", 'device to emulate, for example: "iPhone 15"').option("--executable-path <path>", "path to the browser executable.").option("--extension", 'Connect to a running browser instance (Edge/Chrome only). Requires the "Playwright MCP Bridge" browser extension to be installed.').option("--grant-permissions <permissions...>", 'List of permissions to grant to the browser context, for example "geolocation", "clipboard-read", "clipboard-write".', import_config.commaSeparatedList).option("--headless", "run browser in headless mode, headed by default").option("--host <host>", "host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.").option("--ignore-https-errors", "ignore https errors").option("--init-page <path...>", "path to TypeScript file to evaluate on Playwright page object").option("--init-script <path...>", "path to JavaScript file to add as an initialization script. The script will be evaluated in every page before any of the page's scripts. Can be specified multiple times.").option("--isolated", "keep the browser profile in memory, do not save it to disk.").option("--image-responses <mode>", 'whether to send image responses to the client. Can be "allow" or "omit", Defaults to "allow".').option("--no-sandbox", "disable the sandbox for all process types that are normally sandboxed.").option("--output-dir <path>", "path to the directory for output files.").option("--port <port>", "port to listen on for SSE transport.").option("--proxy-bypass <bypass>", 'comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"').option("--proxy-server <proxy>", 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"').option("--save-session", "Whether to save the Playwright MCP session into the output directory.").option("--save-trace", "Whether to save the Playwright Trace of the session into the output directory.").option("--save-video <size>", 'Whether to save the video of the session into the output directory. For example "--save-video=800x600"', import_config.resolutionParser.bind(null, "--save-video")).option("--secrets <path>", "path to a file containing secrets in the dotenv format", import_config.dotenvFileLoader).option("--shared-browser-context", "reuse the same browser context between all connected HTTP clients.").option("--snapshot-mode <mode>", 'when taking snapshots for responses, specifies the mode to use. Can be "incremental", "full", or "none". Default is incremental.').option("--storage-state <path>", "path to the storage state file for isolated sessions.").option("--test-id-attribute <attribute>", 'specify the attribute to use for test ids, defaults to "data-testid"').option("--timeout-action <timeout>", "specify action timeout in milliseconds, defaults to 5000ms", import_config.numberParser).option("--timeout-navigation <timeout>", "specify navigation timeout in milliseconds, defaults to 60000ms", import_config.numberParser).option("--user-agent <ua string>", "specify user agent string").option("--user-data-dir <path>", "path to the user data directory. If not specified, a temporary directory will be created.").option("--viewport-size <size>", 'specify browser viewport size in pixels, for example "1280x720"', import_config.resolutionParser.bind(null, "--viewport-size")).addOption(new import_utilsBundle.ProgramOption("--connect-tool", "Allow to switch between different browser connection methods.").hideHelp()).addOption(new import_utilsBundle.ProgramOption("--vision", "Legacy option, use --caps=vision instead").hideHelp()).action(async (options) => {
|
|
45
|
+
command.option("--allowed-hosts <hosts...>", "comma-separated list of hosts this server is allowed to serve from. Defaults to the host the server is bound to. Pass '*' to disable the host check.", import_config.commaSeparatedList).option("--allowed-origins <origins>", "semicolon-separated list of TRUSTED origins to allow the browser to request. Default is to allow all.\nImportant: *does not* serve as a security boundary and *does not* affect redirects. ", import_config.semicolonSeparatedList).option("--blocked-origins <origins>", "semicolon-separated list of origins to block the browser from requesting. Blocklist is evaluated before allowlist. If used without the allowlist, requests not matching the blocklist are still allowed.\nImportant: *does not* serve as a security boundary and *does not* affect redirects.", import_config.semicolonSeparatedList).option("--block-service-workers", "block service workers").option("--browser <browser>", "browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge.").option("--caps <caps>", "comma-separated list of additional capabilities to enable, possible values: vision, pdf.", import_config.commaSeparatedList).option("--cdp-endpoint <endpoint>", "CDP endpoint to connect to.").option("--cdp-header <headers...>", "CDP headers to send with the connect request, multiple can be specified.", import_config.headerParser).option("--config <path>", "path to the configuration file.").option("--console-level <level>", 'level of console messages to return: "error", "warning", "info", "debug". Each level includes the messages of more severe levels.', import_config.enumParser.bind(null, "--console-level", ["error", "warning", "info", "debug"])).option("--device <device>", 'device to emulate, for example: "iPhone 15"').option("--executable-path <path>", "path to the browser executable.").option("--extension", 'Connect to a running browser instance (Edge/Chrome only). Requires the "Playwright MCP Bridge" browser extension to be installed.').option("--grant-permissions <permissions...>", 'List of permissions to grant to the browser context, for example "geolocation", "clipboard-read", "clipboard-write".', import_config.commaSeparatedList).option("--headless", "run browser in headless mode, headed by default").option("--host <host>", "host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.").option("--ignore-https-errors", "ignore https errors").option("--init-page <path...>", "path to TypeScript file to evaluate on Playwright page object").option("--init-script <path...>", "path to JavaScript file to add as an initialization script. The script will be evaluated in every page before any of the page's scripts. Can be specified multiple times.").option("--isolated", "keep the browser profile in memory, do not save it to disk.").option("--image-responses <mode>", 'whether to send image responses to the client. Can be "allow" or "omit", Defaults to "allow".', import_config.enumParser.bind(null, "--image-responses", ["allow", "omit"])).option("--no-sandbox", "disable the sandbox for all process types that are normally sandboxed.").option("--output-dir <path>", "path to the directory for output files.").option("--port <port>", "port to listen on for SSE transport.").option("--proxy-bypass <bypass>", 'comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"').option("--proxy-server <proxy>", 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"').option("--save-session", "Whether to save the Playwright MCP session into the output directory.").option("--save-trace", "Whether to save the Playwright Trace of the session into the output directory.").option("--save-video <size>", 'Whether to save the video of the session into the output directory. For example "--save-video=800x600"', import_config.resolutionParser.bind(null, "--save-video")).option("--secrets <path>", "path to a file containing secrets in the dotenv format", import_config.dotenvFileLoader).option("--shared-browser-context", "reuse the same browser context between all connected HTTP clients.").option("--snapshot-mode <mode>", 'when taking snapshots for responses, specifies the mode to use. Can be "incremental", "full", or "none". Default is incremental.').option("--storage-state <path>", "path to the storage state file for isolated sessions.").option("--test-id-attribute <attribute>", 'specify the attribute to use for test ids, defaults to "data-testid"').option("--timeout-action <timeout>", "specify action timeout in milliseconds, defaults to 5000ms", import_config.numberParser).option("--timeout-navigation <timeout>", "specify navigation timeout in milliseconds, defaults to 60000ms", import_config.numberParser).option("--user-agent <ua string>", "specify user agent string").option("--user-data-dir <path>", "path to the user data directory. If not specified, a temporary directory will be created.").option("--viewport-size <size>", 'specify browser viewport size in pixels, for example "1280x720"', import_config.resolutionParser.bind(null, "--viewport-size")).addOption(new import_utilsBundle.ProgramOption("--connect-tool", "Allow to switch between different browser connection methods.").hideHelp()).addOption(new import_utilsBundle.ProgramOption("--vision", "Legacy option, use --caps=vision instead").hideHelp()).action(async (options) => {
|
|
46
46
|
(0, import_watchdog.setupExitWatchdog)();
|
|
47
47
|
if (options.vision) {
|
|
48
48
|
console.error("The --vision option is deprecated, use --caps=vision instead");
|
|
@@ -74,7 +74,7 @@ async function generatePausedMessage(testInfo, context) {
|
|
|
74
74
|
`- Page Title: ${await page.title()}`.trim()
|
|
75
75
|
);
|
|
76
76
|
let console = testInfo.errors.length ? await import_tab.Tab.collectConsoleMessages(page) : [];
|
|
77
|
-
console = console.filter((msg) =>
|
|
77
|
+
console = console.filter((msg) => msg.type === "error");
|
|
78
78
|
if (console.length) {
|
|
79
79
|
lines.push("- Console Messages:");
|
|
80
80
|
for (const message of console)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "playwright",
|
|
3
|
-
"version": "1.58.0-alpha-2025-12-
|
|
3
|
+
"version": "1.58.0-alpha-2025-12-05",
|
|
4
4
|
"description": "A high-level API to automate web browsers",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
},
|
|
65
65
|
"license": "Apache-2.0",
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"playwright-core": "1.58.0-alpha-2025-12-
|
|
67
|
+
"playwright-core": "1.58.0-alpha-2025-12-05"
|
|
68
68
|
},
|
|
69
69
|
"optionalDependencies": {
|
|
70
70
|
"fsevents": "2.3.2"
|