playwright 1.56.0-alpha-2025-08-21 → 1.56.0-alpha-2025-08-23

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/index.js CHANGED
@@ -222,7 +222,7 @@ const playwrightFixtures = {
222
222
  if ((0, import_utils.debugMode)() === "inspector")
223
223
  testInfo._setDebugMode();
224
224
  playwright._defaultContextOptions = _combinedContextOptions;
225
- playwright._defaultContextTimeout = process.env.PLAYWRIGHT_TEST_DEBUGGER_MCP ? 5e3 : actionTimeout || 0;
225
+ playwright._defaultContextTimeout = process.env.PLAYWRIGHT_DEBUGGER_MCP ? 5e3 : actionTimeout || 0;
226
226
  playwright._defaultContextNavigationTimeout = navigationTimeout || 0;
227
227
  await use();
228
228
  playwright._defaultContextOptions = void 0;
@@ -38,11 +38,12 @@ var import_tools = require("./tools");
38
38
  var import_exports = require("../sdk/exports");
39
39
  var import_mdb = require("../sdk/mdb");
40
40
  var import_util = require("../../util");
41
+ const tools = [import_tools.snapshot, import_tools.pickLocator, import_tools.evaluate];
41
42
  class BrowserBackend {
42
43
  constructor(page) {
43
44
  this.name = "Playwright";
44
45
  this.version = "0.0.1";
45
- this._tools = [import_tools.snapshot, import_tools.pickLocator, import_tools.evaluate];
46
+ this._tools = tools;
46
47
  this._page = page;
47
48
  }
48
49
  async initialize() {
@@ -72,14 +73,18 @@ const doneToolSchema = (0, import_exports.defineToolSchema)({
72
73
  type: "destructive"
73
74
  });
74
75
  async function runBrowserBackendOnError(page, message) {
75
- if (!process.env.PLAYWRIGHT_TEST_DEBUGGER_MCP)
76
+ if (!process.env.PLAYWRIGHT_DEBUGGER_MCP)
76
77
  return;
77
- const introMessage = `Paused on error:
78
-
78
+ const snapshot2 = await page._snapshotForAI();
79
+ const introMessage = `### Paused on error:
79
80
  ${(0, import_util.stripAnsiEscapes)(message())}
80
81
 
81
- Try recovering from the error prior to continuing.`;
82
- await (0, import_mdb.runOnPauseBackendLoop)(process.env.PLAYWRIGHT_TEST_DEBUGGER_MCP, new BrowserBackend(page), introMessage);
82
+ ### Current page snapshot:
83
+ ${snapshot2}
84
+
85
+ ### Task
86
+ Try recovering from the error prior to continuing, use following tools to recover: ${tools.map((tool) => tool.schema.name).join(", ")}`;
87
+ await (0, import_mdb.runOnPauseBackendLoop)(process.env.PLAYWRIGHT_DEBUGGER_MCP, new BrowserBackend(page), introMessage);
83
88
  }
84
89
  // Annotate the CommonJS export names for ESM import in node:
85
90
  0 && (module.exports = {
@@ -39,9 +39,9 @@ var import_tool = require("./tool.js");
39
39
  var mcp = __toESM(require("../sdk/bundle"));
40
40
  const snapshot = (0, import_tool.defineTool)({
41
41
  schema: {
42
- name: "browser_snapshot",
43
- title: "Page snapshot",
44
- description: "Capture accessibility snapshot of the current page, this is better than screenshot",
42
+ name: "playwright_test_browser_snapshot",
43
+ title: "Capture page snapshot",
44
+ description: "Capture page snapshot for debugging",
45
45
  inputSchema: mcp.z.object({}),
46
46
  type: "readOnly"
47
47
  },
@@ -63,23 +63,21 @@ const elementSchema = mcp.z.object({
63
63
  });
64
64
  const pickLocator = (0, import_tool.defineTool)({
65
65
  schema: {
66
- name: "browser_pick_locator",
67
- title: "Pick locator",
68
- description: "Pick a locator for the given element",
66
+ name: "playwright_test_generate_locator",
67
+ title: "Create locator for element",
68
+ description: "Generate locator for the given element to use in tests",
69
69
  inputSchema: elementSchema,
70
70
  type: "readOnly"
71
71
  },
72
72
  handle: async (page, params) => {
73
73
  const locator = await refLocator(page, params);
74
- const locatorString = await generateLocator(locator);
75
- return {
76
- content: [
77
- {
78
- type: "text",
79
- text: locatorString
80
- }
81
- ]
82
- };
74
+ try {
75
+ const { resolvedSelector } = await locator._resolveSelector();
76
+ const locatorString = (0, import_utils.asLocator)("javascript", resolvedSelector);
77
+ return { content: [{ type: "text", text: locatorString }] };
78
+ } catch (e) {
79
+ throw new Error(`Ref not found, likely because element was removed. Use ${snapshot.schema.name} to see what elements are currently on the page.`);
80
+ }
83
81
  }
84
82
  });
85
83
  const evaluateSchema = mcp.z.object({
@@ -89,8 +87,8 @@ const evaluateSchema = mcp.z.object({
89
87
  });
90
88
  const evaluate = (0, import_tool.defineTool)({
91
89
  schema: {
92
- name: "browser_evaluate",
93
- title: "Evaluate JavaScript",
90
+ name: "playwright_test_evaluate_on_pause",
91
+ title: "Evaluate in page",
94
92
  description: "Evaluate JavaScript expression on page or element",
95
93
  inputSchema: evaluateSchema,
96
94
  type: "destructive"
@@ -115,14 +113,6 @@ async function refLocator(page, elementRef) {
115
113
  throw new Error(`Ref ${elementRef.ref} not found in the current page snapshot. Try capturing new snapshot.`);
116
114
  return page.locator(`aria-ref=${elementRef.ref}`).describe(elementRef.element);
117
115
  }
118
- async function generateLocator(locator) {
119
- try {
120
- const { resolvedSelector } = await locator._resolveSelector();
121
- return (0, import_utils.asLocator)("javascript", resolvedSelector);
122
- } catch (e) {
123
- throw new Error("Ref not found, likely because element was removed. Use browser_snapshot to see what elements are currently on the page.");
124
- }
125
- }
126
116
  // Annotate the CommonJS export names for ESM import in node:
127
117
  0 && (module.exports = {
128
118
  elementSchema,
@@ -29,8 +29,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
29
  var mdb_exports = {};
30
30
  __export(mdb_exports, {
31
31
  MDBBackend: () => MDBBackend,
32
- runOnPauseBackendLoop: () => runOnPauseBackendLoop,
33
- runToolsBackend: () => runToolsBackend
32
+ runMainBackend: () => runMainBackend,
33
+ runOnPauseBackendLoop: () => runOnPauseBackendLoop
34
34
  });
35
35
  module.exports = __toCommonJS(mdb_exports);
36
36
  var import_utilsBundle = require("playwright-core/lib/utilsBundle");
@@ -39,15 +39,23 @@ var import_bundle = require("./bundle.js");
39
39
  var import_bundle2 = require("./bundle.js");
40
40
  var mcpBundle = __toESM(require("./bundle.js"));
41
41
  var import_tool = require("./tool.js");
42
+ var mcpServer = __toESM(require("./server.js"));
42
43
  var mcpHttp = __toESM(require("./http.js"));
43
- var import_call = require("./call.js");
44
+ var import_server = require("./server.js");
45
+ const mdbDebug = (0, import_utilsBundle.debug)("pw:mcp:mdb");
44
46
  const errorsDebug = (0, import_utilsBundle.debug)("pw:mcp:errors");
45
47
  class MDBBackend {
46
- constructor() {
48
+ constructor(topLevelBackend) {
47
49
  this._stack = [];
50
+ this._initialized = false;
51
+ this._topLevelBackend = topLevelBackend;
48
52
  }
49
53
  async initialize(server) {
50
- this._server = server;
54
+ if (this._initialized)
55
+ return;
56
+ this._initialized = true;
57
+ const transport = await (0, import_server.wrapInProcess)(this._topLevelBackend);
58
+ await this._pushClient(transport);
51
59
  }
52
60
  async listTools() {
53
61
  const response = await this._client().listTools();
@@ -56,9 +64,11 @@ class MDBBackend {
56
64
  async callTool(name, args) {
57
65
  if (name === pushToolsSchema.name)
58
66
  return await this._pushTools(pushToolsSchema.inputSchema.parse(args || {}));
59
- this._interruptPromise = new import_utils.ManualPromise();
67
+ const interruptPromise = new import_utils.ManualPromise();
68
+ this._interruptPromise = interruptPromise;
60
69
  let [entry] = this._stack;
61
70
  while (entry && !entry.toolNames.includes(name)) {
71
+ mdbDebug("popping client from stack for ", name);
62
72
  this._stack.shift();
63
73
  await entry.client.close();
64
74
  entry = this._stack[0];
@@ -68,16 +78,22 @@ class MDBBackend {
68
78
  this._client().callTool({
69
79
  name,
70
80
  arguments: args
71
- }).then((result) => {
72
- resultPromise.resolve(result);
81
+ }).then((result2) => {
82
+ resultPromise.resolve(result2);
73
83
  }).catch((e) => {
84
+ mdbDebug("error in client call", e);
74
85
  if (this._stack.length < 2)
75
86
  throw e;
76
87
  this._stack.shift();
77
88
  const prevEntry = this._stack[0];
78
- void prevEntry.resultPromise.then((result) => resultPromise.resolve(result));
89
+ void prevEntry.resultPromise.then((result2) => resultPromise.resolve(result2));
79
90
  });
80
- return await Promise.race([this._interruptPromise, resultPromise]);
91
+ const result = await Promise.race([interruptPromise, resultPromise]);
92
+ if (interruptPromise.isDone())
93
+ mdbDebug("client call intercepted", result);
94
+ else
95
+ mdbDebug("client call result", result);
96
+ return result;
81
97
  }
82
98
  _client() {
83
99
  const [entry] = this._stack;
@@ -86,22 +102,28 @@ class MDBBackend {
86
102
  return entry.client;
87
103
  }
88
104
  async _pushTools(params) {
105
+ mdbDebug("pushing tools to the stack", params.mcpUrl);
106
+ const transport = new import_bundle2.StreamableHTTPClientTransport(new URL(params.mcpUrl));
107
+ await this._pushClient(transport, params.introMessage);
108
+ return { content: [{ type: "text", text: "Tools pushed" }] };
109
+ }
110
+ async _pushClient(transport, introMessage) {
111
+ mdbDebug("pushing client to the stack");
89
112
  const client = new mcpBundle.Client({ name: "Internal client", version: "0.0.0" });
90
113
  client.setRequestHandler(import_bundle.PingRequestSchema, () => ({}));
91
- const transport = new import_bundle2.StreamableHTTPClientTransport(new URL(params.mcpUrl));
92
114
  await client.connect(transport);
115
+ mdbDebug("connected to the new client");
116
+ const { tools } = await client.listTools();
117
+ this._stack.unshift({ client, toolNames: tools.map((tool) => tool.name), resultPromise: void 0 });
118
+ mdbDebug("new tools added to the stack:", tools.map((tool) => tool.name));
119
+ mdbDebug("interrupting current call:", !!this._interruptPromise);
93
120
  this._interruptPromise?.resolve({
94
121
  content: [{
95
122
  type: "text",
96
- text: params.introMessage || ""
123
+ text: introMessage || ""
97
124
  }]
98
125
  });
99
126
  this._interruptPromise = void 0;
100
- const { tools } = await client.listTools();
101
- this._stack.unshift({ client, toolNames: tools.map((tool) => tool.name), resultPromise: void 0 });
102
- await this._server.notification({
103
- method: "notifications/tools/list_changed"
104
- });
105
127
  return { content: [{ type: "text", text: "Tools pushed" }] };
106
128
  }
107
129
  }
@@ -115,20 +137,17 @@ const pushToolsSchema = (0, import_tool.defineToolSchema)({
115
137
  }),
116
138
  type: "readOnly"
117
139
  });
118
- async function runToolsBackend(backendFactory, options) {
119
- const mdbBackend = new MDBBackend();
120
- const mdbBackendFactory = {
121
- name: "Playwright MDB",
122
- nameInConfig: "playwright-mdb",
123
- version: "0.0.0",
140
+ async function runMainBackend(backendFactory, options) {
141
+ const mdbBackend = new MDBBackend(backendFactory.create());
142
+ const factory = {
143
+ ...backendFactory,
124
144
  create: () => mdbBackend
125
145
  };
126
- const mdbUrl = await startAsHttp(mdbBackendFactory, options);
127
- const backendUrl = await startAsHttp(backendFactory, { port: 0 });
128
- const result = await (0, import_call.callTool)(mdbUrl, pushToolsSchema.name, { mcpUrl: backendUrl });
129
- if (result.isError)
130
- errorsDebug("Failed to push tools", result.content);
131
- return mdbUrl;
146
+ const url = await startAsHttp(factory, { port: options?.port || 0 });
147
+ process.env.PLAYWRIGHT_DEBUGGER_MCP = url;
148
+ if (options?.port !== void 0)
149
+ return url;
150
+ await mcpServer.connect(factory, new mcpBundle.StdioServerTransport(), false);
132
151
  }
133
152
  async function runOnPauseBackendLoop(mdbUrl, backend, introMessage) {
134
153
  const wrappedBackend = new OnceTimeServerBackendWrapper(backend);
@@ -179,8 +198,8 @@ class OnceTimeServerBackendWrapper {
179
198
  async callTool(name, args) {
180
199
  return this._backend.callTool(name, args);
181
200
  }
182
- serverClosed() {
183
- this._backend.serverClosed?.();
201
+ serverClosed(server) {
202
+ this._backend.serverClosed?.(server);
184
203
  this._selfDestructPromise.resolve();
185
204
  }
186
205
  async waitForClosed() {
@@ -190,6 +209,6 @@ class OnceTimeServerBackendWrapper {
190
209
  // Annotate the CommonJS export names for ESM import in node:
191
210
  0 && (module.exports = {
192
211
  MDBBackend,
193
- runOnPauseBackendLoop,
194
- runToolsBackend
212
+ runMainBackend,
213
+ runOnPauseBackendLoop
195
214
  });
@@ -95,7 +95,7 @@ function createServer(name, version, backend, runHeartbeat) {
95
95
  errorsDebug(e);
96
96
  }
97
97
  });
98
- addServerListener(server, "close", () => backend.serverClosed?.());
98
+ addServerListener(server, "close", () => backend.serverClosed?.(server));
99
99
  return server;
100
100
  }
101
101
  const startHeartbeat = (server) => {
@@ -37,11 +37,11 @@ var import_listTests = require("./listTests");
37
37
  var import_runTests = require("./runTests");
38
38
  var import_tools = require("../browser/tools");
39
39
  class TestServerBackend {
40
- constructor(resolvedLocation) {
40
+ constructor(resolvedLocation, options) {
41
41
  this.name = "Playwright";
42
42
  this.version = "0.0.1";
43
43
  this._tools = [import_listTests.listTests, import_runTests.runTests];
44
- this._context = new import_context.Context(resolvedLocation);
44
+ this._context = new import_context.Context(resolvedLocation, options);
45
45
  }
46
46
  async listTools() {
47
47
  return [
@@ -56,7 +56,11 @@ class TestServerBackend {
56
56
  if (!tool)
57
57
  throw new Error(`Tool not found: ${name}. Available tools: ${this._tools.map((tool2) => tool2.schema.name).join(", ")}`);
58
58
  const parsedArguments = tool.schema.inputSchema.parse(args || {});
59
- return await tool.handle(this._context, parsedArguments);
59
+ const result = await tool.handle(this._context, parsedArguments);
60
+ const stdio = this._context.takeStdio();
61
+ if (stdio.trim())
62
+ result.content.push({ type: "text", text: stdio });
63
+ return result;
60
64
  }
61
65
  serverClosed() {
62
66
  void this._context.close();
@@ -23,20 +23,42 @@ __export(context_exports, {
23
23
  module.exports = __toCommonJS(context_exports);
24
24
  var import_testRunner = require("../../runner/testRunner");
25
25
  class Context {
26
- constructor(configLocation) {
26
+ constructor(configLocation, options) {
27
+ this._stdio = [];
27
28
  this.configLocation = configLocation;
29
+ this.options = options;
28
30
  }
29
31
  async createTestRunner() {
30
32
  if (this._testRunner)
31
33
  await this._testRunner.stopTests();
32
34
  const testRunner = new import_testRunner.TestRunner(this.configLocation, {});
33
- await testRunner.initialize({});
35
+ await testRunner.initialize({
36
+ sendStdioEvents: true,
37
+ muteConsole: this.options?.muteConsole
38
+ });
39
+ testRunner.on(import_testRunner.TestRunnerEvent.StdioChunk, (chunk, stdio) => {
40
+ this._stdio.push({ chunk, stdio });
41
+ });
42
+ this._testRunner = testRunner;
43
+ testRunner.on(import_testRunner.TestRunnerEvent.TestFilesChanged, (testFiles) => {
44
+ this._testRunner?.emit(import_testRunner.TestRunnerEvent.TestFilesChanged, testFiles);
45
+ });
34
46
  this._testRunner = testRunner;
35
47
  return testRunner;
36
48
  }
49
+ takeStdio() {
50
+ const text = this._stdio.map((entry) => chunkToPayload(entry.stdio, entry.chunk)).join("\n");
51
+ this._stdio = [];
52
+ return text;
53
+ }
37
54
  async close() {
38
55
  }
39
56
  }
57
+ function chunkToPayload(type, chunk) {
58
+ if (chunk instanceof Uint8Array)
59
+ return "<binary>";
60
+ return `[${type}] ${chunk}`;
61
+ }
40
62
  // Annotate the CommonJS export names for ESM import in node:
41
63
  0 && (module.exports = {
42
64
  Context
@@ -65,7 +65,8 @@ const runTests = (0, import_tool.defineTool)({
65
65
  const result = await testRunner.runTests(reporter, {
66
66
  testIds: params.tests?.map((test) => test.id),
67
67
  // For automatic recovery
68
- timeout: 0
68
+ timeout: 0,
69
+ workers: 1
69
70
  });
70
71
  const text = stream.content();
71
72
  return {
package/lib/program.js CHANGED
@@ -46,6 +46,8 @@ var testServer = __toESM(require("./runner/testServer"));
46
46
  var import_watchMode = require("./runner/watchMode");
47
47
  var import_testRunner = require("./runner/testRunner");
48
48
  var import_reporters = require("./runner/reporters");
49
+ var import_backend = require("./mcp/test/backend");
50
+ var import_mdb = require("./mcp/sdk/mdb");
49
51
  function addTestCommand(program3) {
50
52
  const command = program3.command("test [test-filter...]");
51
53
  command.description("run tests with Playwright Test");
@@ -139,6 +141,25 @@ Arguments [dir]:
139
141
  Examples:
140
142
  $ npx playwright merge-reports playwright-report`);
141
143
  }
144
+ function addMCPServerCommand(program3) {
145
+ const command = program3.command("run-mcp-server", { hidden: true });
146
+ command.description("Interact with the test runner over MCP");
147
+ command.option("-c, --config <file>", `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`);
148
+ command.option("--host <host>", "host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.");
149
+ command.option("--port <port>", "port to listen on for SSE transport.");
150
+ command.action(async (options) => {
151
+ const resolvedLocation = (0, import_configLoader.resolveConfigLocation)(options.config);
152
+ const backendFactory = {
153
+ name: "Playwright Test Runner",
154
+ nameInConfig: "playwright-test-runner",
155
+ version: "0.0.0",
156
+ create: () => new import_backend.TestServerBackend(resolvedLocation, { muteConsole: options.port === void 0 })
157
+ };
158
+ const mdbUrl = await (0, import_mdb.runMainBackend)(backendFactory, { port: options.port === void 0 ? void 0 : +options.port });
159
+ if (mdbUrl)
160
+ console.error("MCP Listening on: ", mdbUrl);
161
+ });
162
+ }
142
163
  async function runTests(args, opts) {
143
164
  await (0, import_utils.startProfiling)();
144
165
  const cliOverrides = overridesFromOptions(opts);
@@ -336,6 +357,7 @@ addTestCommand(import_program.program);
336
357
  addShowReportCommand(import_program.program);
337
358
  addMergeReportsCommand(import_program.program);
338
359
  addClearCacheCommand(import_program.program);
360
+ addMCPServerCommand(import_program.program);
339
361
  addDevServerCommand(import_program.program);
340
362
  addTestServerCommand(import_program.program);
341
363
  // Annotate the CommonJS export names for ESM import in node:
@@ -36,6 +36,8 @@ module.exports = __toCommonJS(testRunner_exports);
36
36
  var import_events = __toESM(require("events"));
37
37
  var import_fs = __toESM(require("fs"));
38
38
  var import_path = __toESM(require("path"));
39
+ var import_util = __toESM(require("util"));
40
+ var import_utilsBundle = require("playwright-core/lib/utilsBundle");
39
41
  var import_server = require("playwright-core/lib/server");
40
42
  var import_utils = require("playwright-core/lib/utils");
41
43
  var import_configLoader = require("../common/configLoader");
@@ -46,13 +48,17 @@ var import_webServerPlugin = require("../plugins/webServerPlugin");
46
48
  var import_base = require("../reporters/base");
47
49
  var import_internalReporter = require("../reporters/internalReporter");
48
50
  var import_compilationCache = require("../transform/compilationCache");
49
- var import_util = require("../util");
51
+ var import_util2 = require("../util");
50
52
  var import_reporters = require("./reporters");
51
53
  var import_tasks = require("./tasks");
52
54
  var import_lastRun = require("./lastRun");
53
55
  const TestRunnerEvent = {
54
- TestFilesChanged: "testFilesChanged"
56
+ TestFilesChanged: "testFilesChanged",
57
+ StdioChunk: "stdioChunk"
55
58
  };
59
+ const originalDebugLog = import_utilsBundle.debug.log;
60
+ const originalStdoutWrite = process.stdout.write;
61
+ const originalStderrWrite = process.stderr.write;
56
62
  class TestRunner extends import_events.default {
57
63
  constructor(configLocation, configCLIOverrides) {
58
64
  super();
@@ -73,6 +79,10 @@ class TestRunner extends import_events.default {
73
79
  async initialize(params) {
74
80
  this._watchTestDirs = !!params.watchTestDirs;
75
81
  this._populateDependenciesOnList = !!params.populateDependenciesOnList;
82
+ this._setInterceptStdio({
83
+ sendStdioEvents: !!params.sendStdioEvents,
84
+ muteConsole: !!params.muteConsole
85
+ });
76
86
  }
77
87
  resizeTerminal(params) {
78
88
  process.stdout.columns = params.cols;
@@ -307,6 +317,10 @@ class TestRunner extends import_events.default {
307
317
  async closeGracefully() {
308
318
  (0, import_utils.gracefullyProcessExitDoNotHang)(0);
309
319
  }
320
+ async stop() {
321
+ this._setInterceptStdio({ sendStdioEvents: false, muteConsole: false });
322
+ await this.runGlobalTeardown();
323
+ }
310
324
  async _loadConfig(overrides) {
311
325
  try {
312
326
  const config = await (0, import_configLoader.loadConfig)(this.configLocation, overrides);
@@ -319,7 +333,7 @@ class TestRunner extends import_events.default {
319
333
  }
320
334
  return { config };
321
335
  } catch (e) {
322
- return { config: null, error: (0, import_util.serializeError)(e) };
336
+ return { config: null, error: (0, import_util2.serializeError)(e) };
323
337
  }
324
338
  }
325
339
  async _loadConfigOrReportError(reporter, overrides) {
@@ -332,6 +346,38 @@ class TestRunner extends import_events.default {
332
346
  await reporter.onExit();
333
347
  return null;
334
348
  }
349
+ _setInterceptStdio(options) {
350
+ if (process.env.PWTEST_DEBUG)
351
+ return;
352
+ if (options.sendStdioEvents || options.muteConsole) {
353
+ if (import_utilsBundle.debug.log === originalDebugLog) {
354
+ import_utilsBundle.debug.log = (...args) => {
355
+ const string = import_util.default.format(...args) + "\n";
356
+ return originalStderrWrite.apply(process.stderr, [string]);
357
+ };
358
+ }
359
+ const stdoutWrite = (chunk) => {
360
+ if (options.sendStdioEvents)
361
+ this.emit(TestRunnerEvent.StdioChunk, chunk, "stdout");
362
+ if (!options.muteConsole)
363
+ originalStdoutWrite.apply(process.stdout, [chunk]);
364
+ return true;
365
+ };
366
+ const stderrWrite = (chunk) => {
367
+ if (options.sendStdioEvents)
368
+ this.emit(TestRunnerEvent.StdioChunk, chunk, "stderr");
369
+ if (!options.muteConsole)
370
+ originalStderrWrite.apply(process.stderr, [chunk]);
371
+ return true;
372
+ };
373
+ process.stdout.write = stdoutWrite;
374
+ process.stderr.write = stderrWrite;
375
+ } else {
376
+ import_utilsBundle.debug.log = originalDebugLog;
377
+ process.stdout.write = originalStdoutWrite;
378
+ process.stderr.write = originalStderrWrite;
379
+ }
380
+ }
335
381
  }
336
382
  function printInternalError(e) {
337
383
  console.error("Internal error:", e);
@@ -28,13 +28,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
  var testServer_exports = {};
30
30
  __export(testServer_exports, {
31
- TestRunnerEvent: () => TestRunnerEvent,
32
31
  TestServerDispatcher: () => TestServerDispatcher,
33
32
  runTestServer: () => runTestServer,
34
33
  runUIMode: () => runUIMode
35
34
  });
36
35
  module.exports = __toCommonJS(testServer_exports);
37
- var import_util = __toESM(require("util"));
38
36
  var import_server = require("playwright-core/lib/server");
39
37
  var import_utils = require("playwright-core/lib/utils");
40
38
  var import_utilsBundle = require("playwright-core/lib/utilsBundle");
@@ -43,9 +41,6 @@ var import_list = __toESM(require("../reporters/list"));
43
41
  var import_reporters = require("./reporters");
44
42
  var import_sigIntWatcher = require("./sigIntWatcher");
45
43
  var import_testRunner = require("./testRunner");
46
- const originalDebugLog = import_utilsBundle.debug.log;
47
- const originalStdoutWrite = process.stdout.write;
48
- const originalStderrWrite = process.stderr.write;
49
44
  class TestServer {
50
45
  constructor(configLocation, configCLIOverrides) {
51
46
  this._configLocation = configLocation;
@@ -56,13 +51,9 @@ class TestServer {
56
51
  return await (0, import_server.startTraceViewerServer)({ ...options, transport: this._dispatcher.transport });
57
52
  }
58
53
  async stop() {
59
- await this._dispatcher?._setInterceptStdio(false);
60
- await this._dispatcher?.runGlobalTeardown();
54
+ await this._dispatcher?.stop();
61
55
  }
62
56
  }
63
- const TestRunnerEvent = {
64
- TestFilesChanged: "testFilesChanged"
65
- };
66
57
  class TestServerDispatcher {
67
58
  constructor(configLocation, configCLIOverrides) {
68
59
  this._serializer = require.resolve("./uiModeReporter");
@@ -78,7 +69,8 @@ class TestServerDispatcher {
78
69
  }
79
70
  };
80
71
  this._dispatchEvent = (method, params) => this.transport.sendEvent?.(method, params);
81
- this._testRunner.on(TestRunnerEvent.TestFilesChanged, (testFiles) => this._dispatchEvent("testFilesChanged", { testFiles }));
72
+ this._testRunner.on(import_testRunner.TestRunnerEvent.TestFilesChanged, (testFiles) => this._dispatchEvent("testFilesChanged", { testFiles }));
73
+ this._testRunner.on(import_testRunner.TestRunnerEvent.StdioChunk, (chunk, stdio) => this._dispatchEvent("stdio", chunkToPayload(stdio, chunk)));
82
74
  }
83
75
  async _wireReporter(messageSink) {
84
76
  return await (0, import_reporters.createReporterForTestServer)(this._serializer, messageSink);
@@ -93,10 +85,10 @@ class TestServerDispatcher {
93
85
  async initialize(params) {
94
86
  this._serializer = params.serializer || require.resolve("./uiModeReporter");
95
87
  this._closeOnDisconnect = !!params.closeOnDisconnect;
96
- await this._setInterceptStdio(!!params.interceptStdio);
97
88
  await this._testRunner.initialize({
98
- watchTestDirs: !!params.watchTestDirs,
99
- populateDependenciesOnList: !!params.populateDependenciesOnList
89
+ ...params,
90
+ sendStdioEvents: !!params.interceptStdio,
91
+ muteConsole: !!params.interceptStdio
100
92
  });
101
93
  }
102
94
  async ping() {
@@ -167,31 +159,8 @@ class TestServerDispatcher {
167
159
  async stopTests() {
168
160
  await this._testRunner.stopTests();
169
161
  }
170
- async _setInterceptStdio(intercept) {
171
- if (process.env.PWTEST_DEBUG)
172
- return;
173
- if (intercept) {
174
- if (import_utilsBundle.debug.log === originalDebugLog) {
175
- import_utilsBundle.debug.log = (...args) => {
176
- const string = import_util.default.format(...args) + "\n";
177
- return originalStderrWrite.apply(process.stderr, [string]);
178
- };
179
- }
180
- const stdoutWrite = (chunk) => {
181
- this._dispatchEvent("stdio", chunkToPayload("stdout", chunk));
182
- return true;
183
- };
184
- const stderrWrite = (chunk) => {
185
- this._dispatchEvent("stdio", chunkToPayload("stderr", chunk));
186
- return true;
187
- };
188
- process.stdout.write = stdoutWrite;
189
- process.stderr.write = stderrWrite;
190
- } else {
191
- import_utilsBundle.debug.log = originalDebugLog;
192
- process.stdout.write = originalStdoutWrite;
193
- process.stderr.write = originalStderrWrite;
194
- }
162
+ async stop() {
163
+ await this._testRunner.stop();
195
164
  }
196
165
  async closeGracefully() {
197
166
  await this._testRunner.closeGracefully();
@@ -257,7 +226,6 @@ function chunkToPayload(type, chunk) {
257
226
  }
258
227
  // Annotate the CommonJS export names for ESM import in node:
259
228
  0 && (module.exports = {
260
- TestRunnerEvent,
261
229
  TestServerDispatcher,
262
230
  runTestServer,
263
231
  runUIMode
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playwright",
3
- "version": "1.56.0-alpha-2025-08-21",
3
+ "version": "1.56.0-alpha-2025-08-23",
4
4
  "description": "A high-level API to automate web browsers",
5
5
  "repository": {
6
6
  "type": "git",
@@ -60,7 +60,7 @@
60
60
  },
61
61
  "license": "Apache-2.0",
62
62
  "dependencies": {
63
- "playwright-core": "1.56.0-alpha-2025-08-21"
63
+ "playwright-core": "1.56.0-alpha-2025-08-23"
64
64
  },
65
65
  "optionalDependencies": {
66
66
  "fsevents": "2.3.2"
@@ -1,42 +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 __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") {
10
- for (let key of __getOwnPropNames(from))
11
- if (!__hasOwnProp.call(to, key) && key !== except)
12
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
- }
14
- return to;
15
- };
16
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
- // If the importer is in node compatibility mode or this is not an ESM
18
- // file that has been converted to a CommonJS file using a Babel-
19
- // compatible transform (i.e. "__esModule" has not been set), then set
20
- // "default" to the CommonJS "module.exports" for node compatibility.
21
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
- mod
23
- ));
24
- var import_path = __toESM(require("path"));
25
- var import_utilsBundle = require("playwright-core/lib/utilsBundle");
26
- var import_configLoader = require("../../common/configLoader");
27
- var import_backend = require("./backend.js");
28
- var import_mdb = require("../sdk/mdb");
29
- import_utilsBundle.program.version("Version 0.0.1").name("playwright-test-mcp").option("--config <file>", 'Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"').option("--host <host>", "host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.").option("--port <port>", "port to listen on for SSE transport.").action(async (options) => {
30
- const resolvedLocation = (0, import_configLoader.resolveConfigLocation)(options.config);
31
- console.error("Test config: ", import_path.default.relative(process.cwd(), resolvedLocation.resolvedConfigFile ?? resolvedLocation.configDir));
32
- const backendFactory = {
33
- name: "Playwright Test",
34
- nameInConfig: "playwright-test-mcp",
35
- version: "0.0.0",
36
- create: () => new import_backend.TestServerBackend(resolvedLocation)
37
- };
38
- const mdbUrl = await (0, import_mdb.runToolsBackend)(backendFactory, { port: 9224 });
39
- process.env.PLAYWRIGHT_TEST_DEBUGGER_MCP = mdbUrl;
40
- console.error("MCP Listening on: ", mdbUrl);
41
- });
42
- void import_utilsBundle.program.parseAsync(process.argv);