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.
@@ -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.rootPath, `traces`);
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.rootPath);
157
- const tracesDir = await (0, import_config.outputFile)(this.config, clientInfo.rootPath, `traces`);
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(rootPath) {
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, clientVersion, roots) {
38
- let rootPath;
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: { ...clientVersion, rootPath }
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, rootPath, name) {
219
- const outputDir = config.outputDir ?? (rootPath ? import_path.default.join(rootPath, ".playwright-mcp") : void 0) ?? import_path.default.join(import_os.default.tmpdir(), "playwright-mcp-output", sanitizeForFilePath((/* @__PURE__ */ new Date()).toISOString()));
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.rootPath, name);
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, rootPath) {
47
- const sessionFolder = await (0, import_config.outputFile)(config, rootPath, `session-${Date.now()}`);
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);
@@ -48,9 +48,9 @@ class MDBBackend {
48
48
  this._stack = [];
49
49
  this._topLevelBackend = topLevelBackend;
50
50
  }
51
- async initialize(server, clientVersion, roots) {
52
- if (!this._roots)
53
- this._roots = roots;
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: "Internal client", version: "0.0.0" }, { capabilities: { roots: {} } });
107
- client.setRequestHandler(mcpBundle.ListRootsRequestSchema, () => ({ roots: this._roots || [] }));
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: "Internal client", version: "0.0.0" });
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, clientVersion, roots) {
187
- await this._backend.initialize?.(server, clientVersion, roots);
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, clientVersion, roots) {
45
- this._roots = roots;
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._roots }));
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);
@@ -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 clientVersion = server.getClientVersion() ?? { name: "unknown", version: "unknown" };
87
- await backend.initialize?.(server, clientVersion, clientRoots);
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, clientVersion, roots) {
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
- if (roots.length > 0) {
54
- const firstRootUri = roots[0]?.uri;
55
- const url = firstRootUri ? new URL(firstRootUri) : void 0;
56
- const folder = url ? (0, import_url.fileURLToPath)(url) : void 0;
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 templateFile = import_path.default.join(configDir, testLocation);
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';
@@ -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, clientVersion, roots) {
57
- this._clientVersion = clientVersion;
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._clientVersion);
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._roots }));
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;
@@ -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-16",
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-16"
67
+ "playwright-core": "1.56.0-alpha-2025-09-17"
68
68
  },
69
69
  "optionalDependencies": {
70
70
  "fsevents": "2.3.2"