playwright 1.56.0-alpha-2025-09-16 → 1.56.0-alpha-2025-09-17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/mcp/browser/browserContextFactory.js +6 -4
- package/lib/mcp/browser/browserServerBackend.js +3 -10
- package/lib/mcp/browser/config.js +4 -2
- package/lib/mcp/browser/context.js +1 -1
- package/lib/mcp/browser/sessionLog.js +2 -2
- package/lib/mcp/sdk/mdb.js +8 -8
- package/lib/mcp/sdk/proxyBackend.js +3 -4
- package/lib/mcp/sdk/server.js +17 -2
- package/lib/mcp/test/testBackend.js +5 -10
- package/lib/mcp/test/testTools.js +4 -1
- package/lib/mcp/vscode/host.js +4 -6
- package/lib/runner/testRunner.js +6 -0
- package/lib/worker/fixtureRunner.js +2 -0
- package/package.json +2 -2
|
@@ -41,6 +41,7 @@ var import_server = require("playwright-core/lib/server");
|
|
|
41
41
|
var import_processUtils = require("./processUtils");
|
|
42
42
|
var import_log = require("../log");
|
|
43
43
|
var import_config = require("./config");
|
|
44
|
+
var import_server2 = require("../sdk/server");
|
|
44
45
|
function contextFactory(config) {
|
|
45
46
|
if (config.browser.remoteEndpoint)
|
|
46
47
|
return new RemoteContextFactory(config);
|
|
@@ -99,7 +100,7 @@ class IsolatedContextFactory extends BaseContextFactory {
|
|
|
99
100
|
async _doObtainBrowser(clientInfo) {
|
|
100
101
|
await injectCdpPort(this.config.browser);
|
|
101
102
|
const browserType = playwright[this.config.browser.browserName];
|
|
102
|
-
const tracesDir = await (0, import_config.outputFile)(this.config, clientInfo
|
|
103
|
+
const tracesDir = await (0, import_config.outputFile)(this.config, clientInfo, `traces`);
|
|
103
104
|
if (this.config.saveTrace)
|
|
104
105
|
await startTraceServer(this.config, tracesDir);
|
|
105
106
|
return browserType.launch({
|
|
@@ -153,8 +154,8 @@ class PersistentContextFactory {
|
|
|
153
154
|
async createContext(clientInfo) {
|
|
154
155
|
await injectCdpPort(this.config.browser);
|
|
155
156
|
(0, import_log.testDebug)("create browser context (persistent)");
|
|
156
|
-
const userDataDir = this.config.browser.userDataDir ?? await this._createUserDataDir(clientInfo
|
|
157
|
-
const tracesDir = await (0, import_config.outputFile)(this.config, clientInfo
|
|
157
|
+
const userDataDir = this.config.browser.userDataDir ?? await this._createUserDataDir(clientInfo);
|
|
158
|
+
const tracesDir = await (0, import_config.outputFile)(this.config, clientInfo, `traces`);
|
|
158
159
|
if (this.config.saveTrace)
|
|
159
160
|
await startTraceServer(this.config, tracesDir);
|
|
160
161
|
this._userDataDirs.add(userDataDir);
|
|
@@ -196,9 +197,10 @@ class PersistentContextFactory {
|
|
|
196
197
|
this._userDataDirs.delete(userDataDir);
|
|
197
198
|
(0, import_log.testDebug)("close browser context complete (persistent)");
|
|
198
199
|
}
|
|
199
|
-
async _createUserDataDir(
|
|
200
|
+
async _createUserDataDir(clientInfo) {
|
|
200
201
|
const dir = process.env.PWMCP_PROFILES_DIR_FOR_TEST ?? import_registry.registryDirectory;
|
|
201
202
|
const browserToken = this.config.browser.launchOptions?.channel ?? this.config.browser?.browserName;
|
|
203
|
+
const rootPath = (0, import_server2.firstRootPath)(clientInfo);
|
|
202
204
|
const rootPathToken = rootPath ? `-${createHash(rootPath)}` : "";
|
|
203
205
|
const result = import_path.default.join(dir, `mcp-${browserToken}${rootPathToken}`);
|
|
204
206
|
await import_fs.default.promises.mkdir(result, { recursive: true });
|
|
@@ -21,7 +21,6 @@ __export(browserServerBackend_exports, {
|
|
|
21
21
|
BrowserServerBackend: () => BrowserServerBackend
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(browserServerBackend_exports);
|
|
24
|
-
var import_url = require("url");
|
|
25
24
|
var import_context = require("./context");
|
|
26
25
|
var import_log = require("../log");
|
|
27
26
|
var import_response = require("./response");
|
|
@@ -34,19 +33,13 @@ class BrowserServerBackend {
|
|
|
34
33
|
this._browserContextFactory = factory;
|
|
35
34
|
this._tools = (0, import_tools.filteredTools)(config);
|
|
36
35
|
}
|
|
37
|
-
async initialize(server,
|
|
38
|
-
|
|
39
|
-
if (roots.length > 0) {
|
|
40
|
-
const firstRootUri = roots[0]?.uri;
|
|
41
|
-
const url = firstRootUri ? new URL(firstRootUri) : void 0;
|
|
42
|
-
rootPath = url ? (0, import_url.fileURLToPath)(url) : void 0;
|
|
43
|
-
}
|
|
44
|
-
this._sessionLog = this._config.saveSession ? await import_sessionLog.SessionLog.create(this._config, rootPath) : void 0;
|
|
36
|
+
async initialize(server, clientInfo) {
|
|
37
|
+
this._sessionLog = this._config.saveSession ? await import_sessionLog.SessionLog.create(this._config, clientInfo) : void 0;
|
|
45
38
|
this._context = new import_context.Context({
|
|
46
39
|
config: this._config,
|
|
47
40
|
browserContextFactory: this._browserContextFactory,
|
|
48
41
|
sessionLog: this._sessionLog,
|
|
49
|
-
clientInfo
|
|
42
|
+
clientInfo
|
|
50
43
|
});
|
|
51
44
|
}
|
|
52
45
|
async listTools() {
|
|
@@ -45,6 +45,7 @@ var import_os = __toESM(require("os"));
|
|
|
45
45
|
var import_path = __toESM(require("path"));
|
|
46
46
|
var import_playwright_core = require("playwright-core");
|
|
47
47
|
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
|
48
|
+
var import_server = require("../sdk/server");
|
|
48
49
|
const defaultConfig = {
|
|
49
50
|
browser: {
|
|
50
51
|
browserName: "chromium",
|
|
@@ -215,8 +216,9 @@ async function loadConfig(configFile) {
|
|
|
215
216
|
throw new Error(`Failed to load config file: ${configFile}, ${error}`);
|
|
216
217
|
}
|
|
217
218
|
}
|
|
218
|
-
async function outputFile(config,
|
|
219
|
-
const
|
|
219
|
+
async function outputFile(config, clientInfo, name) {
|
|
220
|
+
const rootPath = (0, import_server.firstRootPath)(clientInfo);
|
|
221
|
+
const outputDir = config.outputDir ?? (rootPath ? import_path.default.join(rootPath, ".playwright-mcp") : void 0) ?? import_path.default.join(process.env.PW_TMPDIR_FOR_TEST ?? import_os.default.tmpdir(), "playwright-mcp-output", String(clientInfo.timestamp));
|
|
220
222
|
await import_fs.default.promises.mkdir(outputDir, { recursive: true });
|
|
221
223
|
const fileName = sanitizeForFilePath(name);
|
|
222
224
|
return import_path.default.join(outputDir, fileName);
|
|
@@ -96,7 +96,7 @@ class Context {
|
|
|
96
96
|
return url;
|
|
97
97
|
}
|
|
98
98
|
async outputFile(name) {
|
|
99
|
-
return (0, import_config.outputFile)(this.config, this._clientInfo
|
|
99
|
+
return (0, import_config.outputFile)(this.config, this._clientInfo, name);
|
|
100
100
|
}
|
|
101
101
|
_onPageCreated(page) {
|
|
102
102
|
const tab = new import_tab.Tab(this, page, (tab2) => this._onPageClosed(tab2));
|
|
@@ -43,8 +43,8 @@ class SessionLog {
|
|
|
43
43
|
this._folder = sessionFolder;
|
|
44
44
|
this._file = import_path.default.join(this._folder, "session.md");
|
|
45
45
|
}
|
|
46
|
-
static async create(config,
|
|
47
|
-
const sessionFolder = await (0, import_config.outputFile)(config,
|
|
46
|
+
static async create(config, clientInfo) {
|
|
47
|
+
const sessionFolder = await (0, import_config.outputFile)(config, clientInfo, `session-${Date.now()}`);
|
|
48
48
|
await import_fs.default.promises.mkdir(sessionFolder, { recursive: true });
|
|
49
49
|
console.error(`Session: ${sessionFolder}`);
|
|
50
50
|
return new SessionLog(sessionFolder);
|
package/lib/mcp/sdk/mdb.js
CHANGED
|
@@ -48,9 +48,9 @@ class MDBBackend {
|
|
|
48
48
|
this._stack = [];
|
|
49
49
|
this._topLevelBackend = topLevelBackend;
|
|
50
50
|
}
|
|
51
|
-
async initialize(server,
|
|
52
|
-
if (!this.
|
|
53
|
-
this.
|
|
51
|
+
async initialize(server, clientInfo) {
|
|
52
|
+
if (!this._clientInfo)
|
|
53
|
+
this._clientInfo = clientInfo;
|
|
54
54
|
}
|
|
55
55
|
async listTools() {
|
|
56
56
|
const client = await this._client();
|
|
@@ -103,8 +103,8 @@ class MDBBackend {
|
|
|
103
103
|
}
|
|
104
104
|
async _pushClient(transport, introMessage) {
|
|
105
105
|
mdbDebug("pushing client to the stack");
|
|
106
|
-
const client = new mcpBundle.Client({ name: "
|
|
107
|
-
client.setRequestHandler(mcpBundle.ListRootsRequestSchema, () => ({ roots: this.
|
|
106
|
+
const client = new mcpBundle.Client({ name: "Pushing client", version: "0.0.0" }, { capabilities: { roots: {} } });
|
|
107
|
+
client.setRequestHandler(mcpBundle.ListRootsRequestSchema, () => ({ roots: this._clientInfo?.roots || [] }));
|
|
108
108
|
client.setRequestHandler(mcpBundle.PingRequestSchema, () => ({}));
|
|
109
109
|
await client.connect(transport);
|
|
110
110
|
mdbDebug("connected to the new client");
|
|
@@ -155,7 +155,7 @@ async function runOnPauseBackendLoop(backend, introMessage) {
|
|
|
155
155
|
const httpServer = await mcpHttp.startHttpServer({ port: 0 });
|
|
156
156
|
await mcpHttp.installHttpTransport(httpServer, factory);
|
|
157
157
|
const url = mcpHttp.httpAddressToString(httpServer.address());
|
|
158
|
-
const client = new mcpBundle.Client({ name: "
|
|
158
|
+
const client = new mcpBundle.Client({ name: "On-pause client", version: "0.0.0" });
|
|
159
159
|
client.setRequestHandler(mcpBundle.PingRequestSchema, () => ({}));
|
|
160
160
|
const transport = new mcpBundle.StreamableHTTPClientTransport(new URL(process.env.PLAYWRIGHT_DEBUGGER_MCP));
|
|
161
161
|
await client.connect(transport);
|
|
@@ -183,8 +183,8 @@ class ServerBackendWithCloseListener {
|
|
|
183
183
|
this._serverClosedPromise = new import_utils.ManualPromise();
|
|
184
184
|
this._backend = backend;
|
|
185
185
|
}
|
|
186
|
-
async initialize(server,
|
|
187
|
-
await this._backend.initialize?.(server,
|
|
186
|
+
async initialize(server, clientInfo) {
|
|
187
|
+
await this._backend.initialize?.(server, clientInfo);
|
|
188
188
|
}
|
|
189
189
|
async listTools() {
|
|
190
190
|
return this._backend.listTools();
|
|
@@ -37,12 +37,11 @@ const errorsDebug = (0, import_utilsBundle.debug)("pw:mcp:errors");
|
|
|
37
37
|
const { z, zodToJsonSchema } = mcpBundle;
|
|
38
38
|
class ProxyBackend {
|
|
39
39
|
constructor(mcpProviders) {
|
|
40
|
-
this._roots = [];
|
|
41
40
|
this._mcpProviders = mcpProviders;
|
|
42
41
|
this._contextSwitchTool = this._defineContextSwitchTool();
|
|
43
42
|
}
|
|
44
|
-
async initialize(server,
|
|
45
|
-
this.
|
|
43
|
+
async initialize(server, clientInfo) {
|
|
44
|
+
this._clientInfo = clientInfo;
|
|
46
45
|
}
|
|
47
46
|
async listTools() {
|
|
48
47
|
const currentClient = await this._ensureCurrentClient();
|
|
@@ -115,7 +114,7 @@ Error: ${error}
|
|
|
115
114
|
listRoots: true
|
|
116
115
|
}
|
|
117
116
|
});
|
|
118
|
-
client.setRequestHandler(mcpBundle.ListRootsRequestSchema, () => ({ roots: this.
|
|
117
|
+
client.setRequestHandler(mcpBundle.ListRootsRequestSchema, () => ({ roots: this._clientInfo?.roots || [] }));
|
|
119
118
|
client.setRequestHandler(mcpBundle.PingRequestSchema, () => ({}));
|
|
120
119
|
const transport = await factory.connect();
|
|
121
120
|
await client.connect(transport);
|
package/lib/mcp/sdk/server.js
CHANGED
|
@@ -30,10 +30,12 @@ var server_exports = {};
|
|
|
30
30
|
__export(server_exports, {
|
|
31
31
|
connect: () => connect,
|
|
32
32
|
createServer: () => createServer,
|
|
33
|
+
firstRootPath: () => firstRootPath,
|
|
33
34
|
start: () => start,
|
|
34
35
|
wrapInProcess: () => wrapInProcess
|
|
35
36
|
});
|
|
36
37
|
module.exports = __toCommonJS(server_exports);
|
|
38
|
+
var import_url = require("url");
|
|
37
39
|
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
|
38
40
|
var mcpBundle = __toESM(require("./bundle"));
|
|
39
41
|
var import_http = require("./http");
|
|
@@ -83,8 +85,13 @@ const initializeServer = async (server, backend, runHeartbeat) => {
|
|
|
83
85
|
const { roots } = await server.listRoots();
|
|
84
86
|
clientRoots = roots;
|
|
85
87
|
}
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
+
const clientInfo = {
|
|
89
|
+
name: server.getClientVersion()?.name ?? "unknown",
|
|
90
|
+
version: server.getClientVersion()?.version ?? "unknown",
|
|
91
|
+
roots: clientRoots,
|
|
92
|
+
timestamp: Date.now()
|
|
93
|
+
};
|
|
94
|
+
await backend.initialize?.(server, clientInfo);
|
|
88
95
|
if (runHeartbeat)
|
|
89
96
|
startHeartbeat(server);
|
|
90
97
|
};
|
|
@@ -128,10 +135,18 @@ async function start(serverBackendFactory, options) {
|
|
|
128
135
|
].join("\n");
|
|
129
136
|
console.error(message);
|
|
130
137
|
}
|
|
138
|
+
function firstRootPath(clientInfo) {
|
|
139
|
+
if (clientInfo.roots.length === 0)
|
|
140
|
+
return void 0;
|
|
141
|
+
const firstRootUri = clientInfo.roots[0]?.uri;
|
|
142
|
+
const url = firstRootUri ? new URL(firstRootUri) : void 0;
|
|
143
|
+
return url ? (0, import_url.fileURLToPath)(url) : void 0;
|
|
144
|
+
}
|
|
131
145
|
// Annotate the CommonJS export names for ESM import in node:
|
|
132
146
|
0 && (module.exports = {
|
|
133
147
|
connect,
|
|
134
148
|
createServer,
|
|
149
|
+
firstRootPath,
|
|
135
150
|
start,
|
|
136
151
|
wrapInProcess
|
|
137
152
|
});
|
|
@@ -31,7 +31,6 @@ __export(testBackend_exports, {
|
|
|
31
31
|
TestServerBackend: () => TestServerBackend
|
|
32
32
|
});
|
|
33
33
|
module.exports = __toCommonJS(testBackend_exports);
|
|
34
|
-
var import_url = require("url");
|
|
35
34
|
var mcp = __toESM(require("../sdk/exports"));
|
|
36
35
|
var import_testContext = require("./testContext");
|
|
37
36
|
var import_testTools = require("./testTools.js");
|
|
@@ -45,19 +44,15 @@ class TestServerBackend {
|
|
|
45
44
|
this._context = new import_testContext.TestContext(options);
|
|
46
45
|
this._configOption = configOption;
|
|
47
46
|
}
|
|
48
|
-
async initialize(server,
|
|
47
|
+
async initialize(server, clientInfo) {
|
|
49
48
|
if (this._configOption) {
|
|
50
49
|
this._context.setConfigLocation((0, import_configLoader.resolveConfigLocation)(this._configOption));
|
|
51
50
|
return;
|
|
52
51
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (folder) {
|
|
58
|
-
this._context.setConfigLocation((0, import_configLoader.resolveConfigLocation)(folder));
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
52
|
+
const rootPath = mcp.firstRootPath(clientInfo);
|
|
53
|
+
if (rootPath) {
|
|
54
|
+
this._context.setConfigLocation((0, import_configLoader.resolveConfigLocation)(rootPath));
|
|
55
|
+
return;
|
|
61
56
|
}
|
|
62
57
|
throw new Error("No config option or MCP root path provided");
|
|
63
58
|
}
|
|
@@ -144,7 +144,10 @@ const setupPage = (0, import_testTool.defineTestTool)({
|
|
|
144
144
|
let testLocation = params.testLocation;
|
|
145
145
|
if (!testLocation) {
|
|
146
146
|
testLocation = ".template.spec.ts";
|
|
147
|
-
const
|
|
147
|
+
const config = await testRunner.loadConfig();
|
|
148
|
+
const project = params.project ? config.projects.find((p) => p.project.name === params.project) : config.projects[0];
|
|
149
|
+
const testDir = project?.project.testDir || configDir;
|
|
150
|
+
const templateFile = import_path.default.join(testDir, testLocation);
|
|
148
151
|
if (!import_fs.default.existsSync(templateFile)) {
|
|
149
152
|
await import_fs.default.promises.writeFile(templateFile, `
|
|
150
153
|
import { test, expect } from '@playwright/test';
|
package/lib/mcp/vscode/host.js
CHANGED
|
@@ -50,12 +50,10 @@ class VSCodeProxyBackend {
|
|
|
50
50
|
this._defaultTransportFactory = _defaultTransportFactory;
|
|
51
51
|
this.name = "Playwright MCP Client Switcher";
|
|
52
52
|
this.version = packageJSON.version;
|
|
53
|
-
this._roots = [];
|
|
54
53
|
this._contextSwitchTool = this._defineContextSwitchTool();
|
|
55
54
|
}
|
|
56
|
-
async initialize(server,
|
|
57
|
-
this.
|
|
58
|
-
this._roots = roots;
|
|
55
|
+
async initialize(server, clientInfo) {
|
|
56
|
+
this._clientInfo = clientInfo;
|
|
59
57
|
const transport = await this._defaultTransportFactory(this);
|
|
60
58
|
await this._setCurrentClient(transport);
|
|
61
59
|
}
|
|
@@ -147,13 +145,13 @@ class VSCodeProxyBackend {
|
|
|
147
145
|
async _setCurrentClient(transport) {
|
|
148
146
|
await this._currentClient?.close();
|
|
149
147
|
this._currentClient = void 0;
|
|
150
|
-
const client = new mcpBundle.Client(this.
|
|
148
|
+
const client = new mcpBundle.Client({ name: this._clientInfo.name, version: this._clientInfo.version });
|
|
151
149
|
client.registerCapabilities({
|
|
152
150
|
roots: {
|
|
153
151
|
listRoots: true
|
|
154
152
|
}
|
|
155
153
|
});
|
|
156
|
-
client.setRequestHandler(mcpBundle.ListRootsRequestSchema, () => ({ roots: this.
|
|
154
|
+
client.setRequestHandler(mcpBundle.ListRootsRequestSchema, () => ({ roots: this._clientInfo.roots }));
|
|
157
155
|
client.setRequestHandler(mcpBundle.PingRequestSchema, () => ({}));
|
|
158
156
|
await client.connect(transport);
|
|
159
157
|
this._currentClient = client;
|
package/lib/runner/testRunner.js
CHANGED
|
@@ -95,6 +95,12 @@ class TestRunner extends import_events.default {
|
|
|
95
95
|
const executables = import_server.registry.defaultExecutables();
|
|
96
96
|
await import_server.registry.install(executables, false);
|
|
97
97
|
}
|
|
98
|
+
async loadConfig() {
|
|
99
|
+
const { config, error } = await this._loadConfig(this._configCLIOverrides);
|
|
100
|
+
if (config)
|
|
101
|
+
return config;
|
|
102
|
+
throw new Error("Failed to load config: " + (error ? error.message : "Unknown error"));
|
|
103
|
+
}
|
|
98
104
|
async runGlobalSetup(userReporters) {
|
|
99
105
|
await this.runGlobalTeardown();
|
|
100
106
|
const reporter = new import_internalReporter.InternalReporter(userReporters);
|
|
@@ -96,6 +96,8 @@ class Fixture {
|
|
|
96
96
|
this._selfTeardownComplete = (async () => {
|
|
97
97
|
try {
|
|
98
98
|
await this.registration.fn(params, useFunc, info);
|
|
99
|
+
if (!useFuncStarted.isDone())
|
|
100
|
+
throw new Error(`use() was not called in fixture "${this.registration.name}"`);
|
|
99
101
|
} catch (error) {
|
|
100
102
|
this.failed = true;
|
|
101
103
|
if (!useFuncStarted.isDone())
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "playwright",
|
|
3
|
-
"version": "1.56.0-alpha-2025-09-
|
|
3
|
+
"version": "1.56.0-alpha-2025-09-17",
|
|
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.56.0-alpha-2025-09-
|
|
67
|
+
"playwright-core": "1.56.0-alpha-2025-09-17"
|
|
68
68
|
},
|
|
69
69
|
"optionalDependencies": {
|
|
70
70
|
"fsevents": "2.3.2"
|