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

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.
Files changed (35) hide show
  1. package/lib/index.js +45 -4
  2. package/lib/isomorphic/testServerConnection.js +0 -7
  3. package/lib/matchers/expect.js +7 -19
  4. package/lib/matchers/toBeTruthy.js +2 -0
  5. package/lib/matchers/toEqual.js +2 -0
  6. package/lib/matchers/toMatchText.js +3 -0
  7. package/lib/mcp/browser/backend.js +88 -0
  8. package/lib/mcp/browser/tool.js +30 -0
  9. package/lib/mcp/browser/tools.js +132 -0
  10. package/lib/mcp/{bundle.js → sdk/bundle.js} +5 -2
  11. package/lib/mcp/sdk/call.js +49 -0
  12. package/lib/mcp/{exports.js → sdk/exports.js} +12 -10
  13. package/lib/mcp/{transport.js → sdk/http.js} +51 -68
  14. package/lib/mcp/sdk/mdb.js +195 -0
  15. package/lib/mcp/{proxyBackend.js → sdk/proxyBackend.js} +8 -10
  16. package/lib/mcp/{server.js → sdk/server.js} +43 -14
  17. package/lib/mcp/{tool.js → sdk/tool.js} +6 -1
  18. package/lib/mcp/test/backend.js +68 -0
  19. package/lib/mcp/test/context.js +43 -0
  20. package/lib/mcp/test/listTests.js +88 -0
  21. package/lib/mcp/test/program.js +42 -0
  22. package/lib/mcp/test/runTests.js +82 -0
  23. package/lib/mcp/test/streams.js +41 -0
  24. package/lib/mcp/test/tool.js +30 -0
  25. package/lib/mcpBundleImpl.js +17 -13
  26. package/lib/runner/dispatcher.js +1 -22
  27. package/lib/runner/failureTracker.js +0 -14
  28. package/lib/runner/testRunner.js +2 -25
  29. package/lib/runner/testServer.js +2 -8
  30. package/lib/runner/watchMode.js +1 -53
  31. package/lib/runner/workerHost.js +2 -6
  32. package/lib/worker/testInfo.js +1 -24
  33. package/lib/worker/workerMain.js +0 -5
  34. package/package.json +3 -3
  35. /package/lib/mcp/{inProcessTransport.js → sdk/inProcessTransport.js} +0 -0
@@ -158,8 +158,7 @@ class Dispatcher {
158
158
  _createWorker(testGroup, parallelIndex, loaderData) {
159
159
  const projectConfig = this._config.projects.find((p) => p.id === testGroup.projectId);
160
160
  const outputDir = projectConfig.project.outputDir;
161
- const recoverFromStepErrors = this._failureTracker.canRecoverFromStepError();
162
- const worker = new import_workerHost.WorkerHost(testGroup, parallelIndex, loaderData, recoverFromStepErrors, this._extraEnvByProjectId.get(testGroup.projectId) || {}, outputDir);
161
+ const worker = new import_workerHost.WorkerHost(testGroup, parallelIndex, loaderData, this._extraEnvByProjectId.get(testGroup.projectId) || {}, outputDir);
163
162
  const handleOutput = (params) => {
164
163
  const chunk = chunkFromParams(params);
165
164
  if (worker.didFail()) {
@@ -312,24 +311,6 @@ class JobDispatcher {
312
311
  steps.delete(params.stepId);
313
312
  this._reporter.onStepEnd?.(test, result, step);
314
313
  }
315
- _onStepRecoverFromError(resumeAfterStepError, params) {
316
- const data = this._dataByTestId.get(params.testId);
317
- if (!data) {
318
- resumeAfterStepError({ stepId: params.stepId, status: "failed" });
319
- return;
320
- }
321
- const { steps } = data;
322
- const step = steps.get(params.stepId);
323
- if (!step) {
324
- resumeAfterStepError({ stepId: params.stepId, status: "failed" });
325
- return;
326
- }
327
- const testError = {
328
- ...params.error,
329
- location: step.location
330
- };
331
- this._failureTracker.recoverFromStepError(params.stepId, testError, resumeAfterStepError);
332
- }
333
314
  _onAttach(params) {
334
315
  const data = this._dataByTestId.get(params.testId);
335
316
  if (!data) {
@@ -455,13 +436,11 @@ class JobDispatcher {
455
436
  })
456
437
  };
457
438
  worker.runTestGroup(runPayload);
458
- const resumeAfterStepError = worker.resumeAfterStepError.bind(worker);
459
439
  this._listeners = [
460
440
  import_utils.eventsHelper.addEventListener(worker, "testBegin", this._onTestBegin.bind(this)),
461
441
  import_utils.eventsHelper.addEventListener(worker, "testEnd", this._onTestEnd.bind(this)),
462
442
  import_utils.eventsHelper.addEventListener(worker, "stepBegin", this._onStepBegin.bind(this)),
463
443
  import_utils.eventsHelper.addEventListener(worker, "stepEnd", this._onStepEnd.bind(this)),
464
- import_utils.eventsHelper.addEventListener(worker, "stepRecoverFromError", this._onStepRecoverFromError.bind(this, resumeAfterStepError)),
465
444
  import_utils.eventsHelper.addEventListener(worker, "attach", this._onAttach.bind(this)),
466
445
  import_utils.eventsHelper.addEventListener(worker, "done", this._onDone.bind(this)),
467
446
  import_utils.eventsHelper.addEventListener(worker, "exit", this.onExit.bind(this))
@@ -27,12 +27,6 @@ class FailureTracker {
27
27
  this._failureCount = 0;
28
28
  this._hasWorkerErrors = false;
29
29
  }
30
- canRecoverFromStepError() {
31
- return !!this._recoverFromStepErrorHandler;
32
- }
33
- setRecoverFromStepErrorHandler(recoverFromStepErrorHandler) {
34
- this._recoverFromStepErrorHandler = recoverFromStepErrorHandler;
35
- }
36
30
  onRootSuite(rootSuite) {
37
31
  this._rootSuite = rootSuite;
38
32
  }
@@ -40,14 +34,6 @@ class FailureTracker {
40
34
  if (test.outcome() === "unexpected" && test.results.length > test.retries)
41
35
  ++this._failureCount;
42
36
  }
43
- recoverFromStepError(stepId, error, resumeAfterStepError) {
44
- if (!this._recoverFromStepErrorHandler) {
45
- resumeAfterStepError({ stepId, status: "failed" });
46
- return;
47
- }
48
- void this._recoverFromStepErrorHandler(stepId, error).then(resumeAfterStepError).catch(() => {
49
- });
50
- }
51
37
  onWorkerError() {
52
38
  this._hasWorkerErrors = true;
53
39
  }
@@ -51,8 +51,7 @@ var import_reporters = require("./reporters");
51
51
  var import_tasks = require("./tasks");
52
52
  var import_lastRun = require("./lastRun");
53
53
  const TestRunnerEvent = {
54
- TestFilesChanged: "testFilesChanged",
55
- RecoverFromStepError: "recoverFromStepError"
54
+ TestFilesChanged: "testFilesChanged"
56
55
  };
57
56
  class TestRunner extends import_events.default {
58
57
  constructor(configLocation, configCLIOverrides) {
@@ -63,8 +62,6 @@ class TestRunner extends import_events.default {
63
62
  this._queue = Promise.resolve();
64
63
  this._watchTestDirs = false;
65
64
  this._populateDependenciesOnList = false;
66
- this._recoverFromStepErrors = false;
67
- this._resumeAfterStepErrors = /* @__PURE__ */ new Map();
68
65
  this.configLocation = configLocation;
69
66
  this._configCLIOverrides = configCLIOverrides;
70
67
  this._watcher = new import_fsWatcher.Watcher((events) => {
@@ -76,7 +73,6 @@ class TestRunner extends import_events.default {
76
73
  async initialize(params) {
77
74
  this._watchTestDirs = !!params.watchTestDirs;
78
75
  this._populateDependenciesOnList = !!params.populateDependenciesOnList;
79
- this._recoverFromStepErrors = !!params.recoverFromStepErrors;
80
76
  }
81
77
  resizeTerminal(params) {
82
78
  process.stdout.columns = params.cols;
@@ -232,6 +228,7 @@ class TestRunner extends import_events.default {
232
228
  ...this._configCLIOverrides,
233
229
  repeatEach: 1,
234
230
  retries: 0,
231
+ timeout: params.timeout,
235
232
  preserveOutputDir: true,
236
233
  reporter: params.reporters ? params.reporters.map((r) => [r]) : void 0,
237
234
  use: {
@@ -274,7 +271,6 @@ class TestRunner extends import_events.default {
274
271
  ...(0, import_tasks.createRunTestsTasks)(config)
275
272
  ];
276
273
  const testRun = new import_tasks.TestRun(config, reporter);
277
- testRun.failureTracker.setRecoverFromStepErrorHandler(this._recoverFromStepError.bind(this));
278
274
  const run = (0, import_tasks.runTasks)(testRun, tasks, 0, stop).then(async (status) => {
279
275
  this._testRun = void 0;
280
276
  return status;
@@ -282,24 +278,6 @@ class TestRunner extends import_events.default {
282
278
  this._testRun = { run, stop };
283
279
  return { status: await run };
284
280
  }
285
- async _recoverFromStepError(stepId, error) {
286
- if (!this._recoverFromStepErrors)
287
- return { stepId, status: "failed" };
288
- const recoveryPromise = new import_utils.ManualPromise();
289
- this._resumeAfterStepErrors.set(stepId, recoveryPromise);
290
- if (!error?.message || !error?.location)
291
- return { stepId, status: "failed" };
292
- this.emit(TestRunnerEvent.RecoverFromStepError, stepId, error.message, error.location);
293
- const recoveredResult = await recoveryPromise;
294
- if (recoveredResult.stepId !== stepId)
295
- return { stepId, status: "failed" };
296
- return recoveredResult;
297
- }
298
- async resumeAfterStepError(params) {
299
- const recoveryPromise = this._resumeAfterStepErrors.get(params.stepId);
300
- if (recoveryPromise)
301
- recoveryPromise.resolve(params);
302
- }
303
281
  async watch(fileNames) {
304
282
  this._watchedTestDependencies = /* @__PURE__ */ new Set();
305
283
  for (const fileName of fileNames) {
@@ -325,7 +303,6 @@ class TestRunner extends import_events.default {
325
303
  async stopTests() {
326
304
  this._testRun?.stop?.resolve();
327
305
  await this._testRun?.run;
328
- this._resumeAfterStepErrors.clear();
329
306
  }
330
307
  async closeGracefully() {
331
308
  (0, import_utils.gracefullyProcessExitDoNotHang)(0);
@@ -61,8 +61,7 @@ class TestServer {
61
61
  }
62
62
  }
63
63
  const TestRunnerEvent = {
64
- TestFilesChanged: "testFilesChanged",
65
- RecoverFromStepError: "recoverFromStepError"
64
+ TestFilesChanged: "testFilesChanged"
66
65
  };
67
66
  class TestServerDispatcher {
68
67
  constructor(configLocation, configCLIOverrides) {
@@ -80,7 +79,6 @@ class TestServerDispatcher {
80
79
  };
81
80
  this._dispatchEvent = (method, params) => this.transport.sendEvent?.(method, params);
82
81
  this._testRunner.on(TestRunnerEvent.TestFilesChanged, (testFiles) => this._dispatchEvent("testFilesChanged", { testFiles }));
83
- this._testRunner.on(TestRunnerEvent.RecoverFromStepError, (stepId, message, location) => this._dispatchEvent("recoverFromStepError", { stepId, message, location }));
84
82
  }
85
83
  async _wireReporter(messageSink) {
86
84
  return await (0, import_reporters.createReporterForTestServer)(this._serializer, messageSink);
@@ -98,8 +96,7 @@ class TestServerDispatcher {
98
96
  await this._setInterceptStdio(!!params.interceptStdio);
99
97
  await this._testRunner.initialize({
100
98
  watchTestDirs: !!params.watchTestDirs,
101
- populateDependenciesOnList: !!params.populateDependenciesOnList,
102
- recoverFromStepErrors: !!params.recoverFromStepErrors
99
+ populateDependenciesOnList: !!params.populateDependenciesOnList
103
100
  });
104
101
  }
105
102
  async ping() {
@@ -161,9 +158,6 @@ class TestServerDispatcher {
161
158
  const { status } = await this._testRunner.runTests(wireReporter, params);
162
159
  return { status };
163
160
  }
164
- async resumeAfterStepError(params) {
165
- await this._testRunner.resumeAfterStepError(params);
166
- }
167
161
  async watch(params) {
168
162
  await this._testRunner.watch(params.fileNames);
169
163
  }
@@ -31,7 +31,6 @@ __export(watchMode_exports, {
31
31
  runWatchModeLoop: () => runWatchModeLoop
32
32
  });
33
33
  module.exports = __toCommonJS(watchMode_exports);
34
- var import_fs = __toESM(require("fs"));
35
34
  var import_path = __toESM(require("path"));
36
35
  var import_readline = __toESM(require("readline"));
37
36
  var import_stream = require("stream");
@@ -43,8 +42,6 @@ var import_utilsBundle = require("../utilsBundle");
43
42
  var import_testServer = require("./testServer");
44
43
  var import_teleSuiteUpdater = require("../isomorphic/teleSuiteUpdater");
45
44
  var import_testServerConnection = require("../isomorphic/testServerConnection");
46
- var import_util = require("../util");
47
- var import_babelBundle = require("../transform/babelBundle");
48
45
  class InMemoryTransport extends import_stream.EventEmitter {
49
46
  constructor(send) {
50
47
  super();
@@ -116,36 +113,10 @@ async function runWatchModeLoop(configLocation, initialOptions) {
116
113
  });
117
114
  });
118
115
  testServerConnection.onReport((report2) => teleSuiteUpdater.processTestReportEvent(report2));
119
- testServerConnection.onRecoverFromStepError(({ stepId, message, location }) => {
120
- process.stdout.write(`
121
- Test error occurred.
122
- `);
123
- process.stdout.write("\n" + createErrorCodeframe(message, location) + "\n");
124
- process.stdout.write(`
125
- ${import_utils2.colors.dim("Try recovering from the error. Press")} ${import_utils2.colors.bold("c")} ${import_utils2.colors.dim("to continue or")} ${import_utils2.colors.bold("t")} ${import_utils2.colors.dim("to throw the error")}
126
- `);
127
- readKeyPress((text) => {
128
- if (text === "c") {
129
- process.stdout.write(`
130
- ${import_utils2.colors.dim("Continuing after recovery...")}
131
- `);
132
- testServerConnection.resumeAfterStepError({ stepId, status: "recovered", value: void 0 }).catch(() => {
133
- });
134
- } else if (text === "t") {
135
- process.stdout.write(`
136
- ${import_utils2.colors.dim("Throwing error...")}
137
- `);
138
- testServerConnection.resumeAfterStepError({ stepId, status: "failed" }).catch(() => {
139
- });
140
- }
141
- return text;
142
- });
143
- });
144
116
  await testServerConnection.initialize({
145
117
  interceptStdio: false,
146
118
  watchTestDirs: true,
147
- populateDependenciesOnList: true,
148
- recoverFromStepErrors: !process.env.PWTEST_RECOVERY_DISABLED
119
+ populateDependenciesOnList: true
149
120
  });
150
121
  await testServerConnection.runGlobalSetup({});
151
122
  const { report } = await testServerConnection.listTests({});
@@ -418,29 +389,6 @@ async function toggleShowBrowser() {
418
389
  `);
419
390
  }
420
391
  }
421
- function createErrorCodeframe(message, location) {
422
- let source;
423
- try {
424
- source = import_fs.default.readFileSync(location.file, "utf-8") + "\n//";
425
- } catch (e) {
426
- return;
427
- }
428
- return (0, import_babelBundle.codeFrameColumns)(
429
- source,
430
- {
431
- start: {
432
- line: location.line,
433
- column: location.column
434
- }
435
- },
436
- {
437
- highlightCode: true,
438
- linesAbove: 5,
439
- linesBelow: 5,
440
- message: (0, import_util.stripAnsiEscapes)(message).split("\n")[0] || void 0
441
- }
442
- );
443
- }
444
392
  // Annotate the CommonJS export names for ESM import in node:
445
393
  0 && (module.exports = {
446
394
  runWatchModeLoop
@@ -39,7 +39,7 @@ var import_ipc = require("../common/ipc");
39
39
  var import_folders = require("../isomorphic/folders");
40
40
  let lastWorkerIndex = 0;
41
41
  class WorkerHost extends import_processHost.ProcessHost {
42
- constructor(testGroup, parallelIndex, config, recoverFromStepErrors, extraEnv, outputDir) {
42
+ constructor(testGroup, parallelIndex, config, extraEnv, outputDir) {
43
43
  const workerIndex = lastWorkerIndex++;
44
44
  super(require.resolve("../worker/workerMain.js"), `worker-${workerIndex}`, {
45
45
  ...extraEnv,
@@ -56,8 +56,7 @@ class WorkerHost extends import_processHost.ProcessHost {
56
56
  repeatEachIndex: testGroup.repeatEachIndex,
57
57
  projectId: testGroup.projectId,
58
58
  config,
59
- artifactsDir: import_path.default.join(outputDir, (0, import_folders.artifactsFolderName)(workerIndex)),
60
- recoverFromStepErrors
59
+ artifactsDir: import_path.default.join(outputDir, (0, import_folders.artifactsFolderName)(workerIndex))
61
60
  };
62
61
  }
63
62
  async start() {
@@ -78,9 +77,6 @@ class WorkerHost extends import_processHost.ProcessHost {
78
77
  runTestGroup(runPayload) {
79
78
  this.sendMessageNoReply({ method: "runTestGroup", params: runPayload });
80
79
  }
81
- resumeAfterStepError(result) {
82
- this.sendMessageNoReply({ method: "resumeAfterStepError", params: result });
83
- }
84
80
  hash() {
85
81
  return this._hash;
86
82
  }
@@ -43,7 +43,7 @@ var import_testTracing = require("./testTracing");
43
43
  var import_util2 = require("./util");
44
44
  var import_transform = require("../transform/transform");
45
45
  class TestInfoImpl {
46
- constructor(configInternal, projectInternal, workerParams, test, retry, onStepBegin, onStepRecoverFromError, onStepEnd, onAttach) {
46
+ constructor(configInternal, projectInternal, workerParams, test, retry, onStepBegin, onStepEnd, onAttach) {
47
47
  this._snapshotNames = { lastAnonymousSnapshotIndex: 0, lastNamedSnapshotIndex: {} };
48
48
  this._ariaSnapshotNames = { lastAnonymousSnapshotIndex: 0, lastNamedSnapshotIndex: {} };
49
49
  this._wasInterrupted = false;
@@ -61,7 +61,6 @@ class TestInfoImpl {
61
61
  this.errors = [];
62
62
  this.testId = test?.id ?? "";
63
63
  this._onStepBegin = onStepBegin;
64
- this._onStepRecoverFromError = onStepRecoverFromError;
65
64
  this._onStepEnd = onStepEnd;
66
65
  this._onAttach = onAttach;
67
66
  this._startTime = (0, import_utils.monotonicTime)();
@@ -85,7 +84,6 @@ class TestInfoImpl {
85
84
  this.fn = test?.fn ?? (() => {
86
85
  });
87
86
  this.expectedStatus = test?.expectedStatus ?? "skipped";
88
- this._recoverFromStepErrorResults = workerParams.recoverFromStepErrors ? /* @__PURE__ */ new Map() : void 0;
89
87
  this._timeoutManager = new import_timeoutManager.TimeoutManager(this.project.timeout);
90
88
  if (configInternal.configCLIOverrides.debug)
91
89
  this._setDebugMode();
@@ -203,22 +201,6 @@ class TestInfoImpl {
203
201
  steps: [],
204
202
  attachmentIndices: [],
205
203
  info: new TestStepInfoImpl(this, stepId, data.title, parentStep?.info),
206
- recoverFromStepError: async (error) => {
207
- if (!this._recoverFromStepErrorResults)
208
- return { stepId, status: "failed" };
209
- const payload = {
210
- testId: this.testId,
211
- stepId,
212
- error: (0, import_util.serializeError)(error)
213
- };
214
- this._onStepRecoverFromError(payload);
215
- const recoveryPromise = new import_utils.ManualPromise();
216
- this._recoverFromStepErrorResults.set(stepId, recoveryPromise);
217
- const recoveryResult = await recoveryPromise;
218
- if (recoveryResult.stepId !== stepId)
219
- return { stepId, status: "failed" };
220
- return recoveryResult;
221
- },
222
204
  complete: (result) => {
223
205
  if (step.endWallTime)
224
206
  return;
@@ -287,11 +269,6 @@ ${(0, import_utils.stringifyStackFrames)(step.boxedStack).join("\n")}`;
287
269
  }
288
270
  return step;
289
271
  }
290
- resumeAfterStepError(result) {
291
- const recoveryPromise = this._recoverFromStepErrorResults?.get(result.stepId);
292
- if (recoveryPromise)
293
- recoveryPromise.resolve(result);
294
- }
295
272
  _interrupt() {
296
273
  this._wasInterrupted = true;
297
274
  this._timeoutManager.interrupt();
@@ -95,7 +95,6 @@ class WorkerMain extends import_process.ProcessRunner {
95
95
  const fakeTestInfo = new import_testInfo.TestInfoImpl(this._config, this._project, this._params, void 0, 0, () => {
96
96
  }, () => {
97
97
  }, () => {
98
- }, () => {
99
98
  });
100
99
  const runnable = { type: "teardown" };
101
100
  await fakeTestInfo._runWithTimeout(runnable, () => this._loadIfNeeded()).catch(() => {
@@ -217,9 +216,6 @@ class WorkerMain extends import_process.ProcessRunner {
217
216
  this._runFinished.resolve();
218
217
  }
219
218
  }
220
- resumeAfterStepError(params) {
221
- this._currentTest?.resumeAfterStepError(params);
222
- }
223
219
  async _runTest(test, retry, nextTest) {
224
220
  const testInfo = new import_testInfo.TestInfoImpl(
225
221
  this._config,
@@ -228,7 +224,6 @@ class WorkerMain extends import_process.ProcessRunner {
228
224
  test,
229
225
  retry,
230
226
  (stepBeginPayload) => this.dispatchEvent("stepBegin", stepBeginPayload),
231
- (stepRecoverFromErrorPayload) => this.dispatchEvent("stepRecoverFromError", stepRecoverFromErrorPayload),
232
227
  (stepEndPayload) => this.dispatchEvent("stepEnd", stepEndPayload),
233
228
  (attachment) => this.dispatchEvent("attach", attachment)
234
229
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playwright",
3
- "version": "1.55.0",
3
+ "version": "1.56.0-alpha-2025-08-21",
4
4
  "description": "A high-level API to automate web browsers",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,7 +21,7 @@
21
21
  "./package.json": "./package.json",
22
22
  "./lib/common/configLoader": "./lib/common/configLoader.js",
23
23
  "./lib/fsWatcher": "./lib/fsWatcher.js",
24
- "./lib/mcp": "./lib/mcp/exports.js",
24
+ "./lib/mcp/sdk/exports": "./lib/mcp/sdk/exports.js",
25
25
  "./lib/program": "./lib/program.js",
26
26
  "./lib/reporters/base": "./lib/reporters/base.js",
27
27
  "./lib/reporters/list": "./lib/reporters/list.js",
@@ -60,7 +60,7 @@
60
60
  },
61
61
  "license": "Apache-2.0",
62
62
  "dependencies": {
63
- "playwright-core": "1.55.0"
63
+ "playwright-core": "1.56.0-alpha-2025-08-21"
64
64
  },
65
65
  "optionalDependencies": {
66
66
  "fsevents": "2.3.2"