@smoothdeploy/playwright 1.57.1 → 1.58.1-beta-1770383926000

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.

Potentially problematic release.


This version of @smoothdeploy/playwright might be problematic. Click here for more details.

Files changed (264) hide show
  1. package/ThirdPartyNotices.txt +1188 -65
  2. package/lib/agents/agentParser.js +89 -0
  3. package/lib/agents/generateAgents.js +27 -74
  4. package/lib/agents/generateAgents.js.map +7 -0
  5. package/lib/agents/playwright-test-planner.agent.md +1 -0
  6. package/lib/common/config.js +6 -4
  7. package/lib/common/config.js.map +7 -0
  8. package/lib/common/configLoader.js.map +7 -0
  9. package/lib/common/esmLoaderHost.js +2 -0
  10. package/lib/common/esmLoaderHost.js.map +7 -0
  11. package/lib/common/expectBundle.js +2 -17
  12. package/lib/common/expectBundle.js.map +7 -0
  13. package/lib/common/expectBundleImpl.js +132 -132
  14. package/lib/common/expectBundleImpl.js.map +7 -0
  15. package/lib/common/fixtures.js.map +7 -0
  16. package/lib/common/globals.js.map +7 -0
  17. package/lib/common/ipc.js.map +7 -0
  18. package/lib/common/poolBuilder.js.map +7 -0
  19. package/lib/common/process.js +28 -0
  20. package/lib/common/process.js.map +7 -0
  21. package/lib/common/suiteUtils.js.map +7 -0
  22. package/lib/common/test.js.map +7 -0
  23. package/lib/common/testLoader.js.map +7 -0
  24. package/lib/common/testType.js.map +7 -0
  25. package/lib/common/validators.js +10 -10
  26. package/lib/common/validators.js.map +7 -0
  27. package/lib/fsWatcher.js.map +7 -0
  28. package/lib/index.js +205 -12
  29. package/lib/index.js.map +7 -0
  30. package/lib/internalsForTest.js.map +7 -0
  31. package/lib/isomorphic/events.js.map +7 -0
  32. package/lib/isomorphic/folders.js.map +7 -0
  33. package/lib/isomorphic/stringInternPool.js.map +7 -0
  34. package/lib/isomorphic/teleReceiver.js +15 -2
  35. package/lib/isomorphic/teleReceiver.js.map +7 -0
  36. package/lib/isomorphic/teleSuiteUpdater.js +24 -4
  37. package/lib/isomorphic/teleSuiteUpdater.js.map +7 -0
  38. package/lib/isomorphic/testServerConnection.js.map +7 -0
  39. package/lib/isomorphic/testServerInterface.js.map +7 -0
  40. package/lib/isomorphic/testTree.js +13 -18
  41. package/lib/isomorphic/testTree.js.map +7 -0
  42. package/lib/isomorphic/types.d.js.map +7 -0
  43. package/lib/loader/loaderMain.js.map +7 -0
  44. package/lib/matchers/expect.js +2 -15
  45. package/lib/matchers/expect.js.map +7 -0
  46. package/lib/matchers/matcherHint.js +1 -44
  47. package/lib/matchers/matcherHint.js.map +7 -0
  48. package/lib/matchers/matchers.js +3 -2
  49. package/lib/matchers/matchers.js.map +7 -0
  50. package/lib/matchers/toBeTruthy.js +5 -3
  51. package/lib/matchers/toBeTruthy.js.map +7 -0
  52. package/lib/matchers/toEqual.js +4 -3
  53. package/lib/matchers/toEqual.js.map +7 -0
  54. package/lib/matchers/toHaveURL.js +5 -6
  55. package/lib/matchers/toHaveURL.js.map +7 -0
  56. package/lib/matchers/toMatchAriaSnapshot.js +5 -5
  57. package/lib/matchers/toMatchAriaSnapshot.js.map +7 -0
  58. package/lib/matchers/toMatchSnapshot.js +9 -8
  59. package/lib/matchers/toMatchSnapshot.js.map +7 -0
  60. package/lib/matchers/toMatchText.js +9 -9
  61. package/lib/matchers/toMatchText.js.map +7 -0
  62. package/lib/mcp/browser/actions.d.js.map +7 -0
  63. package/lib/mcp/browser/browserContextFactory.js +62 -29
  64. package/lib/mcp/browser/browserContextFactory.js.map +7 -0
  65. package/lib/mcp/browser/browserServerBackend.js +17 -9
  66. package/lib/mcp/browser/browserServerBackend.js.map +7 -0
  67. package/lib/mcp/browser/codegen.js.map +7 -0
  68. package/lib/mcp/browser/config.js +65 -12
  69. package/lib/mcp/browser/config.js.map +7 -0
  70. package/lib/mcp/browser/context.js +71 -94
  71. package/lib/mcp/browser/context.js.map +7 -0
  72. package/lib/mcp/browser/response.js +172 -131
  73. package/lib/mcp/browser/response.js.map +7 -0
  74. package/lib/mcp/browser/sessionLog.js +19 -104
  75. package/lib/mcp/browser/sessionLog.js.map +7 -0
  76. package/lib/mcp/browser/tab.js +92 -41
  77. package/lib/mcp/browser/tab.js.map +7 -0
  78. package/lib/mcp/browser/tools/common.js +8 -6
  79. package/lib/mcp/browser/tools/common.js.map +7 -0
  80. package/lib/mcp/browser/tools/console.js +7 -5
  81. package/lib/mcp/browser/tools/console.js.map +7 -0
  82. package/lib/mcp/browser/tools/dialogs.js +4 -4
  83. package/lib/mcp/browser/tools/dialogs.js.map +7 -0
  84. package/lib/mcp/browser/tools/evaluate.js +11 -19
  85. package/lib/mcp/browser/tools/evaluate.js.map +7 -0
  86. package/lib/mcp/browser/tools/files.js +3 -3
  87. package/lib/mcp/browser/tools/files.js.map +7 -0
  88. package/lib/mcp/browser/tools/form.js +9 -19
  89. package/lib/mcp/browser/tools/form.js.map +7 -0
  90. package/lib/mcp/browser/tools/install.js +6 -3
  91. package/lib/mcp/browser/tools/install.js.map +7 -0
  92. package/lib/mcp/browser/tools/keyboard.js +34 -11
  93. package/lib/mcp/browser/tools/keyboard.js.map +7 -0
  94. package/lib/mcp/browser/tools/mouse.js +11 -11
  95. package/lib/mcp/browser/tools/mouse.js.map +7 -0
  96. package/lib/mcp/browser/tools/navigate.js +14 -5
  97. package/lib/mcp/browser/tools/navigate.js.map +7 -0
  98. package/lib/mcp/browser/tools/network.js +20 -11
  99. package/lib/mcp/browser/tools/network.js.map +7 -0
  100. package/lib/mcp/browser/tools/open.js +57 -0
  101. package/lib/mcp/browser/tools/pdf.js +9 -19
  102. package/lib/mcp/browser/tools/pdf.js.map +7 -0
  103. package/lib/mcp/browser/tools/runCode.js +11 -8
  104. package/lib/mcp/browser/tools/runCode.js.map +7 -0
  105. package/lib/mcp/browser/tools/screenshot.js +16 -29
  106. package/lib/mcp/browser/tools/screenshot.js.map +7 -0
  107. package/lib/mcp/browser/tools/snapshot.js +21 -29
  108. package/lib/mcp/browser/tools/snapshot.js.map +7 -0
  109. package/lib/mcp/browser/tools/tabs.js +12 -12
  110. package/lib/mcp/browser/tools/tabs.js.map +7 -0
  111. package/lib/mcp/browser/tools/tool.js +2 -4
  112. package/lib/mcp/browser/tools/tool.js.map +7 -0
  113. package/lib/mcp/browser/tools/tracing.js +6 -6
  114. package/lib/mcp/browser/tools/tracing.js.map +7 -0
  115. package/lib/mcp/browser/tools/utils.js +49 -44
  116. package/lib/mcp/browser/tools/utils.js.map +7 -0
  117. package/lib/mcp/browser/tools/verify.js +24 -34
  118. package/lib/mcp/browser/tools/verify.js.map +7 -0
  119. package/lib/mcp/browser/tools/wait.js +6 -6
  120. package/lib/mcp/browser/tools/wait.js.map +7 -0
  121. package/lib/mcp/browser/tools.js +3 -1
  122. package/lib/mcp/browser/tools.js.map +7 -0
  123. package/lib/mcp/browser/watchdog.js.map +7 -0
  124. package/lib/mcp/config.d.js.map +7 -0
  125. package/lib/mcp/extension/cdpRelay.js +1 -1
  126. package/lib/mcp/extension/cdpRelay.js.map +7 -0
  127. package/lib/mcp/extension/extensionContextFactory.js +6 -5
  128. package/lib/mcp/extension/extensionContextFactory.js.map +7 -0
  129. package/lib/mcp/extension/protocol.js.map +7 -0
  130. package/lib/mcp/index.js.map +7 -0
  131. package/lib/mcp/log.js.map +7 -0
  132. package/lib/mcp/program.js +15 -20
  133. package/lib/mcp/program.js.map +7 -0
  134. package/lib/mcp/sdk/bundle.js.map +7 -0
  135. package/lib/mcp/sdk/exports.js +0 -2
  136. package/lib/mcp/sdk/exports.js.map +7 -0
  137. package/lib/mcp/sdk/http.js +20 -55
  138. package/lib/mcp/sdk/http.js.map +7 -0
  139. package/lib/mcp/sdk/inProcessTransport.js.map +7 -0
  140. package/lib/mcp/sdk/proxyBackend.js.map +7 -0
  141. package/lib/mcp/sdk/server.js +29 -4
  142. package/lib/mcp/sdk/server.js.map +7 -0
  143. package/lib/mcp/sdk/tool.js +2 -2
  144. package/lib/mcp/sdk/tool.js.map +7 -0
  145. package/lib/mcp/terminal/cli.js +296 -0
  146. package/lib/mcp/terminal/command.js +56 -0
  147. package/lib/mcp/terminal/commands.js +333 -0
  148. package/lib/mcp/terminal/daemon.js +129 -0
  149. package/lib/mcp/terminal/help.json +32 -0
  150. package/lib/mcp/terminal/helpGenerator.js +88 -0
  151. package/lib/mcp/terminal/socketConnection.js +80 -0
  152. package/lib/mcp/test/browserBackend.js +3 -13
  153. package/lib/mcp/test/browserBackend.js.map +7 -0
  154. package/lib/mcp/test/generatorTools.js +9 -9
  155. package/lib/mcp/test/generatorTools.js.map +7 -0
  156. package/lib/mcp/test/plannerTools.js +23 -22
  157. package/lib/mcp/test/plannerTools.js.map +7 -0
  158. package/lib/mcp/test/seed.js.map +7 -0
  159. package/lib/mcp/test/streams.js.map +7 -0
  160. package/lib/mcp/test/testBackend.js +6 -6
  161. package/lib/mcp/test/testBackend.js.map +7 -0
  162. package/lib/mcp/test/testContext.js +9 -3
  163. package/lib/mcp/test/testContext.js.map +7 -0
  164. package/lib/mcp/test/testTool.js.map +7 -0
  165. package/lib/mcp/test/testTools.js +12 -10
  166. package/lib/mcp/test/testTools.js.map +7 -0
  167. package/lib/mcpBundleImpl.js.map +7 -0
  168. package/lib/plugins/gitCommitInfoPlugin.js.map +7 -0
  169. package/lib/plugins/index.js.map +7 -0
  170. package/lib/plugins/webServerPlugin.js.map +7 -0
  171. package/lib/program.js +18 -4
  172. package/lib/program.js.map +7 -0
  173. package/lib/reporters/base.js +29 -4
  174. package/lib/reporters/base.js.map +7 -0
  175. package/lib/reporters/blob.js +3 -0
  176. package/lib/reporters/blob.js.map +7 -0
  177. package/lib/reporters/dot.js +17 -0
  178. package/lib/reporters/dot.js.map +7 -0
  179. package/lib/reporters/empty.js.map +7 -0
  180. package/lib/reporters/github.js.map +7 -0
  181. package/lib/reporters/html.js +13 -3
  182. package/lib/reporters/html.js.map +7 -0
  183. package/lib/reporters/internalReporter.js +6 -0
  184. package/lib/reporters/internalReporter.js.map +7 -0
  185. package/lib/reporters/json.js.map +7 -0
  186. package/lib/reporters/junit.js.map +7 -0
  187. package/lib/reporters/line.js +18 -0
  188. package/lib/reporters/line.js.map +7 -0
  189. package/lib/reporters/list.js +22 -0
  190. package/lib/reporters/list.js.map +7 -0
  191. package/lib/reporters/listModeReporter.js.map +7 -0
  192. package/lib/reporters/markdown.js.map +7 -0
  193. package/lib/reporters/merge.js +25 -8
  194. package/lib/reporters/merge.js.map +7 -0
  195. package/lib/reporters/multiplexer.js +8 -0
  196. package/lib/reporters/multiplexer.js.map +7 -0
  197. package/lib/reporters/reporterV2.js.map +7 -0
  198. package/lib/reporters/smoothdeploy.js +191 -0
  199. package/lib/reporters/teleEmitter.js +22 -4
  200. package/lib/reporters/teleEmitter.js.map +7 -0
  201. package/lib/reporters/versions/blobV1.js.map +7 -0
  202. package/lib/runner/dispatcher.js +20 -4
  203. package/lib/runner/dispatcher.js.map +7 -0
  204. package/lib/runner/failureTracker.js.map +7 -0
  205. package/lib/runner/lastRun.js.map +7 -0
  206. package/lib/runner/loadUtils.js +2 -2
  207. package/lib/runner/loadUtils.js.map +7 -0
  208. package/lib/runner/loaderHost.js.map +7 -0
  209. package/lib/runner/processHost.js +19 -0
  210. package/lib/runner/processHost.js.map +7 -0
  211. package/lib/runner/projectUtils.js +1 -1
  212. package/lib/runner/projectUtils.js.map +7 -0
  213. package/lib/runner/rebase.js.map +7 -0
  214. package/lib/runner/reporters.js +3 -1
  215. package/lib/runner/reporters.js.map +7 -0
  216. package/lib/runner/sigIntWatcher.js.map +7 -0
  217. package/lib/runner/storage.js +91 -0
  218. package/lib/runner/taskRunner.js.map +7 -0
  219. package/lib/runner/tasks.js.map +7 -0
  220. package/lib/runner/testGroups.js +14 -6
  221. package/lib/runner/testGroups.js.map +7 -0
  222. package/lib/runner/testRunner.js +13 -4
  223. package/lib/runner/testRunner.js.map +7 -0
  224. package/lib/runner/testServer.js +2 -2
  225. package/lib/runner/testServer.js.map +7 -0
  226. package/lib/runner/uiModeReporter.js.map +7 -0
  227. package/lib/runner/vcs.js.map +7 -0
  228. package/lib/runner/watchMode.js +2 -1
  229. package/lib/runner/watchMode.js.map +7 -0
  230. package/lib/runner/workerHost.js +6 -0
  231. package/lib/runner/workerHost.js.map +7 -0
  232. package/lib/third_party/pirates.js.map +7 -0
  233. package/lib/third_party/tsconfig-loader.js.map +7 -0
  234. package/lib/transform/babelBundle.js +3 -0
  235. package/lib/transform/babelBundle.js.map +7 -0
  236. package/lib/transform/babelBundleImpl.js +134 -134
  237. package/lib/transform/babelBundleImpl.js.map +7 -0
  238. package/lib/transform/compilationCache.js +2 -0
  239. package/lib/transform/compilationCache.js.map +7 -0
  240. package/lib/transform/esmLoader.js +10 -11
  241. package/lib/transform/esmLoader.js.map +7 -0
  242. package/lib/transform/md.js +221 -0
  243. package/lib/transform/portTransport.js.map +7 -0
  244. package/lib/transform/transform.js +18 -8
  245. package/lib/transform/transform.js.map +7 -0
  246. package/lib/util.js +3 -6
  247. package/lib/util.js.map +7 -0
  248. package/lib/utilsBundle.js +7 -0
  249. package/lib/utilsBundle.js.map +7 -0
  250. package/lib/utilsBundleImpl.js +51 -48
  251. package/lib/utilsBundleImpl.js.map +7 -0
  252. package/lib/worker/fixtureRunner.js +6 -2
  253. package/lib/worker/fixtureRunner.js.map +7 -0
  254. package/lib/worker/testInfo.js +39 -19
  255. package/lib/worker/testInfo.js.map +7 -0
  256. package/lib/worker/testTracing.js.map +7 -0
  257. package/lib/worker/timeoutManager.js.map +7 -0
  258. package/lib/worker/util.js.map +7 -0
  259. package/lib/worker/workerMain.js +17 -16
  260. package/lib/worker/workerMain.js.map +7 -0
  261. package/package.json +2 -2
  262. package/test.mjs +1 -0
  263. package/types/test.d.ts +26 -6
  264. package/types/testReporter.d.ts +1 -0
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var socketConnection_exports = {};
20
+ __export(socketConnection_exports, {
21
+ SocketConnection: () => SocketConnection
22
+ });
23
+ module.exports = __toCommonJS(socketConnection_exports);
24
+ var import_utilsBundle = require("playwright-core/lib/utilsBundle");
25
+ const daemonDebug = (0, import_utilsBundle.debug)("pw:daemon");
26
+ class SocketConnection {
27
+ constructor(socket) {
28
+ this._pendingBuffers = [];
29
+ this._socket = socket;
30
+ socket.on("data", (buffer) => this._onData(buffer));
31
+ socket.on("close", () => {
32
+ this.onclose?.();
33
+ });
34
+ socket.on("error", (e) => daemonDebug(`error: ${e.message}`));
35
+ }
36
+ async send(message) {
37
+ await new Promise((resolve, reject) => {
38
+ this._socket.write(`${JSON.stringify(message)}
39
+ `, (error) => {
40
+ if (error)
41
+ reject(error);
42
+ else
43
+ resolve(void 0);
44
+ });
45
+ });
46
+ }
47
+ close() {
48
+ this._socket.destroy();
49
+ }
50
+ _onData(buffer) {
51
+ let end = buffer.indexOf("\n");
52
+ if (end === -1) {
53
+ this._pendingBuffers.push(buffer);
54
+ return;
55
+ }
56
+ this._pendingBuffers.push(buffer.slice(0, end));
57
+ const message = Buffer.concat(this._pendingBuffers).toString();
58
+ this._dispatchMessage(message);
59
+ let start = end + 1;
60
+ end = buffer.indexOf("\n", start);
61
+ while (end !== -1) {
62
+ const message2 = buffer.toString(void 0, start, end);
63
+ this._dispatchMessage(message2);
64
+ start = end + 1;
65
+ end = buffer.indexOf("\n", start);
66
+ }
67
+ this._pendingBuffers = [buffer.slice(start)];
68
+ }
69
+ _dispatchMessage(message) {
70
+ try {
71
+ this.onmessage?.(JSON.parse(message));
72
+ } catch (e) {
73
+ daemonDebug("failed to dispatch message", e);
74
+ }
75
+ }
76
+ }
77
+ // Annotate the CommonJS export names for ESM import in node:
78
+ 0 && (module.exports = {
79
+ SocketConnection
80
+ });
@@ -25,13 +25,14 @@ var import_config = require("../browser/config");
25
25
  var import_browserServerBackend = require("../browser/browserServerBackend");
26
26
  var import_tab = require("../browser/tab");
27
27
  var import_util = require("../../util");
28
+ var import_browserContextFactory = require("../browser/browserContextFactory");
28
29
  function createCustomMessageHandler(testInfo, context) {
29
30
  let backend;
30
31
  return async (data) => {
31
32
  if (data.initialize) {
32
33
  if (backend)
33
34
  throw new Error("MCP backend is already initialized");
34
- backend = new import_browserServerBackend.BrowserServerBackend({ ...import_config.defaultConfig, capabilities: ["testing"] }, identityFactory(context));
35
+ backend = new import_browserServerBackend.BrowserServerBackend({ ...import_config.defaultConfig, capabilities: ["testing"] }, (0, import_browserContextFactory.identityBrowserContextFactory)(context));
35
36
  await backend.initialize(data.initialize.clientInfo);
36
37
  const pausedMessage = await generatePausedMessage(testInfo, context);
37
38
  return { initialize: { pausedMessage } };
@@ -73,7 +74,7 @@ async function generatePausedMessage(testInfo, context) {
73
74
  `- Page Title: ${await page.title()}`.trim()
74
75
  );
75
76
  let console = testInfo.errors.length ? await import_tab.Tab.collectConsoleMessages(page) : [];
76
- console = console.filter((msg) => !msg.type || msg.type === "error");
77
+ console = console.filter((msg) => msg.type === "error");
77
78
  if (console.length) {
78
79
  lines.push("- Console Messages:");
79
80
  for (const message of console)
@@ -91,17 +92,6 @@ async function generatePausedMessage(testInfo, context) {
91
92
  lines.push(`### Task`, `Try recovering from the error prior to continuing`);
92
93
  return lines.join("\n");
93
94
  }
94
- function identityFactory(browserContext) {
95
- return {
96
- createContext: async (clientInfo, abortSignal, toolName) => {
97
- return {
98
- browserContext,
99
- close: async () => {
100
- }
101
- };
102
- }
103
- };
104
- }
105
95
  // Annotate the CommonJS export names for ESM import in node:
106
96
  0 && (module.exports = {
107
97
  createCustomMessageHandler
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mcp/test/browserBackend.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as mcp from '../sdk/exports';\nimport { defaultConfig } from '../browser/config';\nimport { BrowserServerBackend } from '../browser/browserServerBackend';\nimport { Tab } from '../browser/tab';\nimport { stripAnsiEscapes } from '../../util';\n\nimport type * as playwright from '../../../index';\nimport type { Page } from '../../../../playwright-core/src/client/page';\nimport type { BrowserContextFactory } from '../browser/browserContextFactory';\nimport type { TestInfo } from '../../../test';\n\nexport type BrowserMCPRequest = {\n initialize?: { clientInfo: mcp.ClientInfo },\n listTools?: {},\n callTool?: { name: string, arguments: mcp.CallToolRequest['params']['arguments'] },\n close?: {},\n};\n\nexport type BrowserMCPResponse = {\n initialize?: { pausedMessage: string },\n listTools?: mcp.Tool[],\n callTool?: mcp.CallToolResult,\n close?: {},\n};\n\nexport function createCustomMessageHandler(testInfo: TestInfo, context: playwright.BrowserContext) {\n let backend: BrowserServerBackend | undefined;\n return async (data: BrowserMCPRequest): Promise<BrowserMCPResponse> => {\n if (data.initialize) {\n if (backend)\n throw new Error('MCP backend is already initialized');\n backend = new BrowserServerBackend({ ...defaultConfig, capabilities: ['testing'] }, identityFactory(context));\n await backend.initialize(data.initialize.clientInfo);\n const pausedMessage = await generatePausedMessage(testInfo, context);\n return { initialize: { pausedMessage } };\n }\n\n if (data.listTools) {\n if (!backend)\n throw new Error('MCP backend is not initialized');\n return { listTools: await backend.listTools() };\n }\n\n if (data.callTool) {\n if (!backend)\n throw new Error('MCP backend is not initialized');\n return { callTool: await backend.callTool(data.callTool.name, data.callTool.arguments) };\n }\n\n if (data.close) {\n backend?.serverClosed();\n backend = undefined;\n return { close: {} };\n }\n\n throw new Error('Unknown MCP request');\n };\n}\n\nasync function generatePausedMessage(testInfo: TestInfo, context: playwright.BrowserContext) {\n const lines: string[] = [];\n\n if (testInfo.errors.length) {\n lines.push(`### Paused on error:`);\n for (const error of testInfo.errors)\n lines.push(stripAnsiEscapes(error.message || ''));\n } else {\n lines.push(`### Paused at end of test. ready for interaction`);\n }\n\n for (let i = 0; i < context.pages().length; i++) {\n const page = context.pages()[i];\n const stateSuffix = context.pages().length > 1 ? (i + 1) + ' of ' + (context.pages().length) : 'state';\n lines.push(\n '',\n `### Page ${stateSuffix}`,\n `- Page URL: ${page.url()}`,\n `- Page Title: ${await page.title()}`.trim()\n );\n // Only print console errors when pausing on error, not when everything works as expected.\n let console = testInfo.errors.length ? await Tab.collectConsoleMessages(page) : [];\n console = console.filter(msg => !msg.type || msg.type === 'error');\n if (console.length) {\n lines.push('- Console Messages:');\n for (const message of console)\n lines.push(` - ${message.toString()}`);\n }\n lines.push(\n `- Page Snapshot:`,\n '```yaml',\n (await (page as Page)._snapshotForAI()).full,\n '```',\n );\n }\n\n lines.push('');\n if (testInfo.errors.length)\n lines.push(`### Task`, `Try recovering from the error prior to continuing`);\n\n return lines.join('\\n');\n}\n\nfunction identityFactory(browserContext: playwright.BrowserContext): BrowserContextFactory {\n return {\n createContext: async (clientInfo: mcp.ClientInfo, abortSignal: AbortSignal, toolName: string | undefined) => {\n return {\n browserContext,\n close: async () => {}\n };\n }\n };\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBA,oBAA8B;AAC9B,kCAAqC;AACrC,iBAAoB;AACpB,kBAAiC;AAqB1B,SAAS,2BAA2B,UAAoB,SAAoC;AACjG,MAAI;AACJ,SAAO,OAAO,SAAyD;AACrE,QAAI,KAAK,YAAY;AACnB,UAAI;AACF,cAAM,IAAI,MAAM,oCAAoC;AACtD,gBAAU,IAAI,iDAAqB,EAAE,GAAG,6BAAe,cAAc,CAAC,SAAS,EAAE,GAAG,gBAAgB,OAAO,CAAC;AAC5G,YAAM,QAAQ,WAAW,KAAK,WAAW,UAAU;AACnD,YAAM,gBAAgB,MAAM,sBAAsB,UAAU,OAAO;AACnE,aAAO,EAAE,YAAY,EAAE,cAAc,EAAE;AAAA,IACzC;AAEA,QAAI,KAAK,WAAW;AAClB,UAAI,CAAC;AACH,cAAM,IAAI,MAAM,gCAAgC;AAClD,aAAO,EAAE,WAAW,MAAM,QAAQ,UAAU,EAAE;AAAA,IAChD;AAEA,QAAI,KAAK,UAAU;AACjB,UAAI,CAAC;AACH,cAAM,IAAI,MAAM,gCAAgC;AAClD,aAAO,EAAE,UAAU,MAAM,QAAQ,SAAS,KAAK,SAAS,MAAM,KAAK,SAAS,SAAS,EAAE;AAAA,IACzF;AAEA,QAAI,KAAK,OAAO;AACd,eAAS,aAAa;AACtB,gBAAU;AACV,aAAO,EAAE,OAAO,CAAC,EAAE;AAAA,IACrB;AAEA,UAAM,IAAI,MAAM,qBAAqB;AAAA,EACvC;AACF;AAEA,eAAe,sBAAsB,UAAoB,SAAoC;AAC3F,QAAM,QAAkB,CAAC;AAEzB,MAAI,SAAS,OAAO,QAAQ;AAC1B,UAAM,KAAK,sBAAsB;AACjC,eAAW,SAAS,SAAS;AAC3B,YAAM,SAAK,8BAAiB,MAAM,WAAW,EAAE,CAAC;AAAA,EACpD,OAAO;AACL,UAAM,KAAK,kDAAkD;AAAA,EAC/D;AAEA,WAAS,IAAI,GAAG,IAAI,QAAQ,MAAM,EAAE,QAAQ,KAAK;AAC/C,UAAM,OAAO,QAAQ,MAAM,EAAE,CAAC;AAC9B,UAAM,cAAc,QAAQ,MAAM,EAAE,SAAS,IAAK,IAAI,IAAK,SAAU,QAAQ,MAAM,EAAE,SAAU;AAC/F,UAAM;AAAA,MACF;AAAA,MACA,YAAY,WAAW;AAAA,MACvB,eAAe,KAAK,IAAI,CAAC;AAAA,MACzB,iBAAiB,MAAM,KAAK,MAAM,CAAC,GAAG,KAAK;AAAA,IAC/C;AAEA,QAAI,UAAU,SAAS,OAAO,SAAS,MAAM,eAAI,uBAAuB,IAAI,IAAI,CAAC;AACjF,cAAU,QAAQ,OAAO,SAAO,CAAC,IAAI,QAAQ,IAAI,SAAS,OAAO;AACjE,QAAI,QAAQ,QAAQ;AAClB,YAAM,KAAK,qBAAqB;AAChC,iBAAW,WAAW;AACpB,cAAM,KAAK,OAAO,QAAQ,SAAS,CAAC,EAAE;AAAA,IAC1C;AACA,UAAM;AAAA,MACF;AAAA,MACA;AAAA,OACC,MAAO,KAAc,eAAe,GAAG;AAAA,MACxC;AAAA,IACJ;AAAA,EACF;AAEA,QAAM,KAAK,EAAE;AACb,MAAI,SAAS,OAAO;AAClB,UAAM,KAAK,YAAY,mDAAmD;AAE5E,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,gBAAgB,gBAAkE;AACzF,SAAO;AAAA,IACL,eAAe,OAAO,YAA4B,aAA0B,aAAiC;AAC3G,aAAO;AAAA,QACL;AAAA,QACA,OAAO,YAAY;AAAA,QAAC;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -35,7 +35,7 @@ __export(generatorTools_exports, {
35
35
  module.exports = __toCommonJS(generatorTools_exports);
36
36
  var import_fs = __toESM(require("fs"));
37
37
  var import_path = __toESM(require("path"));
38
- var import_bundle = require("../sdk/bundle");
38
+ var import_mcpBundle = require("playwright-core/lib/mcpBundle");
39
39
  var import_testTool = require("./testTool");
40
40
  var import_testContext = require("./testContext");
41
41
  const setupPage = (0, import_testTool.defineTestTool)({
@@ -43,10 +43,10 @@ const setupPage = (0, import_testTool.defineTestTool)({
43
43
  name: "generator_setup_page",
44
44
  title: "Setup generator page",
45
45
  description: "Setup the page for test.",
46
- inputSchema: import_bundle.z.object({
47
- plan: import_bundle.z.string().describe("The plan for the test. This should be the actual test plan with all the steps."),
48
- project: import_bundle.z.string().optional().describe('Project to use for setup. For example: "chromium", if no project is provided uses the first project in the config.'),
49
- seedFile: import_bundle.z.string().optional().describe('A seed file contains a single test that is used to setup the page for testing, for example: "tests/seed.spec.ts". If no seed file is provided, a default seed file is created.')
46
+ inputSchema: import_mcpBundle.z.object({
47
+ plan: import_mcpBundle.z.string().describe("The plan for the test. This should be the actual test plan with all the steps."),
48
+ project: import_mcpBundle.z.string().optional().describe('Project to use for setup. For example: "chromium", if no project is provided uses the first project in the config.'),
49
+ seedFile: import_mcpBundle.z.string().optional().describe('A seed file contains a single test that is used to setup the page for testing, for example: "tests/seed.spec.ts". If no seed file is provided, a default seed file is created.')
50
50
  }),
51
51
  type: "readOnly"
52
52
  },
@@ -62,7 +62,7 @@ const generatorReadLog = (0, import_testTool.defineTestTool)({
62
62
  name: "generator_read_log",
63
63
  title: "Retrieve test log",
64
64
  description: "Retrieve the performed test log",
65
- inputSchema: import_bundle.z.object({}),
65
+ inputSchema: import_mcpBundle.z.object({}),
66
66
  type: "readOnly"
67
67
  },
68
68
  handle: async (context) => {
@@ -80,9 +80,9 @@ const generatorWriteTest = (0, import_testTool.defineTestTool)({
80
80
  name: "generator_write_test",
81
81
  title: "Write test",
82
82
  description: "Write the generated test to the test file",
83
- inputSchema: import_bundle.z.object({
84
- fileName: import_bundle.z.string().describe("The file to write the test to"),
85
- code: import_bundle.z.string().describe("The generated test code")
83
+ inputSchema: import_mcpBundle.z.object({
84
+ fileName: import_mcpBundle.z.string().describe("The file to write the test to"),
85
+ code: import_mcpBundle.z.string().describe("The generated test code")
86
86
  }),
87
87
  type: "readOnly"
88
88
  },
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mcp/test/generatorTools.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'fs';\nimport path from 'path';\n\nimport { z } from '../sdk/bundle';\nimport { defineTestTool } from './testTool';\nimport { GeneratorJournal } from './testContext';\n\nexport const setupPage = defineTestTool({\n schema: {\n name: 'generator_setup_page',\n title: 'Setup generator page',\n description: 'Setup the page for test.',\n inputSchema: z.object({\n plan: z.string().describe('The plan for the test. This should be the actual test plan with all the steps.'),\n project: z.string().optional().describe('Project to use for setup. For example: \"chromium\", if no project is provided uses the first project in the config.'),\n seedFile: z.string().optional().describe('A seed file contains a single test that is used to setup the page for testing, for example: \"tests/seed.spec.ts\". If no seed file is provided, a default seed file is created.'),\n }),\n type: 'readOnly',\n },\n\n handle: async (context, params) => {\n const seed = await context.getOrCreateSeedFile(params.seedFile, params.project);\n context.generatorJournal = new GeneratorJournal(context.rootPath, params.plan, seed);\n const { output, status } = await context.runSeedTest(seed.file, seed.projectName);\n return { content: [{ type: 'text', text: output }], isError: status !== 'paused' };\n },\n});\n\nexport const generatorReadLog = defineTestTool({\n schema: {\n name: 'generator_read_log',\n title: 'Retrieve test log',\n description: 'Retrieve the performed test log',\n inputSchema: z.object({}),\n type: 'readOnly',\n },\n\n handle: async context => {\n if (!context.generatorJournal)\n throw new Error(`Please setup page using \"${setupPage.schema.name}\" first.`);\n const result = context.generatorJournal.journal();\n return { content: [{\n type: 'text',\n text: result,\n }] };\n },\n});\n\nexport const generatorWriteTest = defineTestTool({\n schema: {\n name: 'generator_write_test',\n title: 'Write test',\n description: 'Write the generated test to the test file',\n inputSchema: z.object({\n fileName: z.string().describe('The file to write the test to'),\n code: z.string().describe('The generated test code'),\n }),\n type: 'readOnly',\n },\n\n handle: async (context, params) => {\n if (!context.generatorJournal)\n throw new Error(`Please setup page using \"${setupPage.schema.name}\" first.`);\n\n const testRunner = context.existingTestRunner();\n if (!testRunner)\n throw new Error('No test runner found, please setup page and perform actions first.');\n const config = await testRunner.loadConfig();\n\n const dirs: string[] = [];\n for (const project of config.projects) {\n const testDir = path.relative(context.rootPath, project.project.testDir).replace(/\\\\/g, '/');\n const fileName = params.fileName.replace(/\\\\/g, '/');\n if (fileName.startsWith(testDir)) {\n const resolvedFile = path.resolve(context.rootPath, fileName);\n await fs.promises.mkdir(path.dirname(resolvedFile), { recursive: true });\n await fs.promises.writeFile(resolvedFile, params.code);\n return {\n content: [{\n type: 'text',\n text: `### Result\\nTest written to ${params.fileName}`,\n }]\n };\n }\n dirs.push(testDir);\n }\n throw new Error(`Test file did not match any of the test dirs: ${dirs.join(', ')}`);\n },\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["path", "fs"]
7
+ }
@@ -35,16 +35,16 @@ __export(plannerTools_exports, {
35
35
  module.exports = __toCommonJS(plannerTools_exports);
36
36
  var import_fs = __toESM(require("fs"));
37
37
  var import_path = __toESM(require("path"));
38
- var import_bundle = require("../sdk/bundle");
38
+ var import_mcpBundle = require("playwright-core/lib/mcpBundle");
39
39
  var import_testTool = require("./testTool");
40
40
  const setupPage = (0, import_testTool.defineTestTool)({
41
41
  schema: {
42
42
  name: "planner_setup_page",
43
43
  title: "Setup planner page",
44
44
  description: "Setup the page for test planning",
45
- inputSchema: import_bundle.z.object({
46
- project: import_bundle.z.string().optional().describe('Project to use for setup. For example: "chromium", if no project is provided uses the first project in the config.'),
47
- seedFile: import_bundle.z.string().optional().describe('A seed file contains a single test that is used to setup the page for testing, for example: "tests/seed.spec.ts". If no seed file is provided, a default seed file is created.')
45
+ inputSchema: import_mcpBundle.z.object({
46
+ project: import_mcpBundle.z.string().optional().describe('Project to use for setup. For example: "chromium", if no project is provided uses the first project in the config.'),
47
+ seedFile: import_mcpBundle.z.string().optional().describe('A seed file contains a single test that is used to setup the page for testing, for example: "tests/seed.spec.ts". If no seed file is provided, a default seed file is created.')
48
48
  }),
49
49
  type: "readOnly"
50
50
  },
@@ -54,16 +54,18 @@ const setupPage = (0, import_testTool.defineTestTool)({
54
54
  return { content: [{ type: "text", text: output }], isError: status !== "paused" };
55
55
  }
56
56
  });
57
- const planSchema = import_bundle.z.object({
58
- overview: import_bundle.z.string().describe("A brief overview of the application to be tested"),
59
- suites: import_bundle.z.array(import_bundle.z.object({
60
- name: import_bundle.z.string().describe("The name of the suite"),
61
- seedFile: import_bundle.z.string().describe("A seed file that was used to setup the page for testing."),
62
- tests: import_bundle.z.array(import_bundle.z.object({
63
- name: import_bundle.z.string().describe("The name of the test"),
64
- file: import_bundle.z.string().describe('The file the test should be saved to, for example: "tests/<suite-name>/<test-name>.spec.ts".'),
65
- steps: import_bundle.z.array(import_bundle.z.string().describe(`The steps to be executed to perform the test. For example: 'Click on the "Submit" button'`)),
66
- expectedResults: import_bundle.z.array(import_bundle.z.string().describe("The expected results of the steps for test to verify."))
57
+ const planSchema = import_mcpBundle.z.object({
58
+ overview: import_mcpBundle.z.string().describe("A brief overview of the application to be tested"),
59
+ suites: import_mcpBundle.z.array(import_mcpBundle.z.object({
60
+ name: import_mcpBundle.z.string().describe("The name of the suite"),
61
+ seedFile: import_mcpBundle.z.string().describe("A seed file that was used to setup the page for testing."),
62
+ tests: import_mcpBundle.z.array(import_mcpBundle.z.object({
63
+ name: import_mcpBundle.z.string().describe("The name of the test"),
64
+ file: import_mcpBundle.z.string().describe('The file the test should be saved to, for example: "tests/<suite-name>/<test-name>.spec.ts".'),
65
+ steps: import_mcpBundle.z.array(import_mcpBundle.z.object({
66
+ perform: import_mcpBundle.z.string().optional().describe(`Action to perform. For example: 'Click on the "Submit" button'.`),
67
+ expect: import_mcpBundle.z.string().array().describe(`Expected result of the action where appropriate. For example: 'The page should show the "Thank you for your submission" message'`)
68
+ }))
67
69
  }))
68
70
  }))
69
71
  });
@@ -90,8 +92,8 @@ const saveTestPlan = (0, import_testTool.defineTestTool)({
90
92
  title: "Save test plan as markdown file",
91
93
  description: "Save the test plan as a markdown file",
92
94
  inputSchema: planSchema.extend({
93
- name: import_bundle.z.string().describe('The name of the test plan, for example: "Test Plan".'),
94
- fileName: import_bundle.z.string().describe('The file to save the test plan to, for example: "spec/test.plan.md". Relative to the workspace root.')
95
+ name: import_mcpBundle.z.string().describe('The name of the test plan, for example: "Test Plan".'),
96
+ fileName: import_mcpBundle.z.string().describe('The file to save the test plan to, for example: "spec/test.plan.md". Relative to the workspace root.')
95
97
  }),
96
98
  type: "readOnly"
97
99
  },
@@ -118,12 +120,11 @@ const saveTestPlan = (0, import_testTool.defineTestTool)({
118
120
  lines.push(`**File:** \`${test.file}\``);
119
121
  lines.push(``);
120
122
  lines.push(`**Steps:**`);
121
- for (let k = 0; k < test.steps.length; k++)
122
- lines.push(` ${k + 1}. ${test.steps[k]}`);
123
- lines.push(``);
124
- lines.push(`**Expected Results:**`);
125
- for (const result of test.expectedResults)
126
- lines.push(` - ${result}`);
123
+ for (let k = 0; k < test.steps.length; k++) {
124
+ lines.push(` ${k + 1}. ${test.steps[k].perform ?? "-"}`);
125
+ for (const expect of test.steps[k].expect)
126
+ lines.push(` - expect: ${expect}`);
127
+ }
127
128
  }
128
129
  }
129
130
  lines.push(``);
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mcp/test/plannerTools.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'fs';\nimport path from 'path';\n\nimport { z } from '../sdk/bundle';\nimport { defineTestTool } from './testTool';\n\nexport const setupPage = defineTestTool({\n schema: {\n name: 'planner_setup_page',\n title: 'Setup planner page',\n description: 'Setup the page for test planning',\n inputSchema: z.object({\n project: z.string().optional().describe('Project to use for setup. For example: \"chromium\", if no project is provided uses the first project in the config.'),\n seedFile: z.string().optional().describe('A seed file contains a single test that is used to setup the page for testing, for example: \"tests/seed.spec.ts\". If no seed file is provided, a default seed file is created.'),\n }),\n type: 'readOnly',\n },\n\n handle: async (context, params) => {\n const seed = await context.getOrCreateSeedFile(params.seedFile, params.project);\n const { output, status } = await context.runSeedTest(seed.file, seed.projectName);\n return { content: [{ type: 'text', text: output }], isError: status !== 'paused' };\n },\n});\n\nconst planSchema = z.object({\n overview: z.string().describe('A brief overview of the application to be tested'),\n suites: z.array(z.object({\n name: z.string().describe('The name of the suite'),\n seedFile: z.string().describe('A seed file that was used to setup the page for testing.'),\n tests: z.array(z.object({\n name: z.string().describe('The name of the test'),\n file: z.string().describe('The file the test should be saved to, for example: \"tests/<suite-name>/<test-name>.spec.ts\".'),\n steps: z.array(z.string().describe(`The steps to be executed to perform the test. For example: 'Click on the \"Submit\" button'`)),\n expectedResults: z.array(z.string().describe('The expected results of the steps for test to verify.')),\n })),\n })),\n});\n\nexport const submitTestPlan = defineTestTool({\n schema: {\n name: 'planner_submit_plan',\n title: 'Submit test plan',\n description: 'Submit the test plan to the test planner',\n inputSchema: planSchema,\n type: 'readOnly',\n },\n\n handle: async (context, params) => {\n return {\n content: [{\n type: 'text',\n text: JSON.stringify(params, null, 2),\n }],\n };\n },\n});\n\nexport const saveTestPlan = defineTestTool({\n schema: {\n name: 'planner_save_plan',\n title: 'Save test plan as markdown file',\n description: 'Save the test plan as a markdown file',\n inputSchema: planSchema.extend({\n name: z.string().describe('The name of the test plan, for example: \"Test Plan\".'),\n fileName: z.string().describe('The file to save the test plan to, for example: \"spec/test.plan.md\". Relative to the workspace root.'),\n }),\n type: 'readOnly',\n },\n\n handle: async (context, params) => {\n const lines: string[] = [];\n lines.push(`# ${params.name}`);\n lines.push(``);\n lines.push(`## Application Overview`);\n lines.push(``);\n lines.push(params.overview);\n lines.push(``);\n lines.push(`## Test Scenarios`);\n for (let i = 0; i < params.suites.length; i++) {\n lines.push(``);\n const suite = params.suites[i];\n lines.push(`### ${i + 1}. ${suite.name}`);\n lines.push(``);\n lines.push(`**Seed:** \\`${suite.seedFile}\\``);\n for (let j = 0; j < suite.tests.length; j++) {\n lines.push(``);\n const test = suite.tests[j];\n lines.push(`#### ${i + 1}.${j + 1}. ${test.name}`);\n lines.push(``);\n lines.push(`**File:** \\`${test.file}\\``);\n lines.push(``);\n lines.push(`**Steps:**`);\n for (let k = 0; k < test.steps.length; k++)\n lines.push(` ${k + 1}. ${test.steps[k]}`);\n lines.push(``);\n lines.push(`**Expected Results:**`);\n for (const result of test.expectedResults)\n lines.push(` - ${result}`);\n }\n }\n lines.push(``);\n await fs.promises.writeFile(path.resolve(context.rootPath, params.fileName), lines.join('\\n'));\n return {\n content: [{\n type: 'text',\n text: `Test plan saved to ${params.fileName}`,\n }],\n };\n },\n});\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,gBAAe;AACf,kBAAiB;AAEjB,oBAAkB;AAClB,sBAA+B;AAExB,MAAM,gBAAY,gCAAe;AAAA,EACtC,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,aAAa,gBAAE,OAAO;AAAA,MACpB,SAAS,gBAAE,OAAO,EAAE,SAAS,EAAE,SAAS,oHAAoH;AAAA,MAC5J,UAAU,gBAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gLAAgL;AAAA,IAC3N,CAAC;AAAA,IACD,MAAM;AAAA,EACR;AAAA,EAEA,QAAQ,OAAO,SAAS,WAAW;AACjC,UAAM,OAAO,MAAM,QAAQ,oBAAoB,OAAO,UAAU,OAAO,OAAO;AAC9E,UAAM,EAAE,QAAQ,OAAO,IAAI,MAAM,QAAQ,YAAY,KAAK,MAAM,KAAK,WAAW;AAChF,WAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,OAAO,CAAC,GAAG,SAAS,WAAW,SAAS;AAAA,EACnF;AACF,CAAC;AAED,MAAM,aAAa,gBAAE,OAAO;AAAA,EAC1B,UAAU,gBAAE,OAAO,EAAE,SAAS,kDAAkD;AAAA,EAChF,QAAQ,gBAAE,MAAM,gBAAE,OAAO;AAAA,IACvB,MAAM,gBAAE,OAAO,EAAE,SAAS,uBAAuB;AAAA,IACjD,UAAU,gBAAE,OAAO,EAAE,SAAS,0DAA0D;AAAA,IACxF,OAAO,gBAAE,MAAM,gBAAE,OAAO;AAAA,MACtB,MAAM,gBAAE,OAAO,EAAE,SAAS,sBAAsB;AAAA,MAChD,MAAM,gBAAE,OAAO,EAAE,SAAS,8FAA8F;AAAA,MACxH,OAAO,gBAAE,MAAM,gBAAE,OAAO,EAAE,SAAS,2FAA2F,CAAC;AAAA,MAC/H,iBAAiB,gBAAE,MAAM,gBAAE,OAAO,EAAE,SAAS,uDAAuD,CAAC;AAAA,IACvG,CAAC,CAAC;AAAA,EACJ,CAAC,CAAC;AACJ,CAAC;AAEM,MAAM,qBAAiB,gCAAe;AAAA,EAC3C,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,aAAa;AAAA,IACb,MAAM;AAAA,EACR;AAAA,EAEA,QAAQ,OAAO,SAAS,WAAW;AACjC,WAAO;AAAA,MACL,SAAS,CAAC;AAAA,QACR,MAAM;AAAA,QACN,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC;AAAA,MACtC,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;AAEM,MAAM,mBAAe,gCAAe;AAAA,EACzC,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,aAAa,WAAW,OAAO;AAAA,MAC7B,MAAM,gBAAE,OAAO,EAAE,SAAS,sDAAsD;AAAA,MAChF,UAAU,gBAAE,OAAO,EAAE,SAAS,sGAAsG;AAAA,IACtI,CAAC;AAAA,IACD,MAAM;AAAA,EACR;AAAA,EAEA,QAAQ,OAAO,SAAS,WAAW;AACjC,UAAM,QAAkB,CAAC;AACzB,UAAM,KAAK,KAAK,OAAO,IAAI,EAAE;AAC7B,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,yBAAyB;AACpC,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,OAAO,QAAQ;AAC1B,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,mBAAmB;AAC9B,aAAS,IAAI,GAAG,IAAI,OAAO,OAAO,QAAQ,KAAK;AAC7C,YAAM,KAAK,EAAE;AACb,YAAM,QAAQ,OAAO,OAAO,CAAC;AAC7B,YAAM,KAAK,OAAO,IAAI,CAAC,KAAK,MAAM,IAAI,EAAE;AACxC,YAAM,KAAK,EAAE;AACb,YAAM,KAAK,eAAe,MAAM,QAAQ,IAAI;AAC5C,eAAS,IAAI,GAAG,IAAI,MAAM,MAAM,QAAQ,KAAK;AAC3C,cAAM,KAAK,EAAE;AACb,cAAM,OAAO,MAAM,MAAM,CAAC;AAC1B,cAAM,KAAK,QAAQ,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,EAAE;AACjD,cAAM,KAAK,EAAE;AACb,cAAM,KAAK,eAAe,KAAK,IAAI,IAAI;AACvC,cAAM,KAAK,EAAE;AACb,cAAM,KAAK,YAAY;AACvB,iBAAS,IAAI,GAAG,IAAI,KAAK,MAAM,QAAQ;AACrC,gBAAM,KAAK,KAAK,IAAI,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,EAAE;AAC3C,cAAM,KAAK,EAAE;AACb,cAAM,KAAK,uBAAuB;AAClC,mBAAW,UAAU,KAAK;AACxB,gBAAM,KAAK,OAAO,MAAM,EAAE;AAAA,MAC9B;AAAA,IACF;AACA,UAAM,KAAK,EAAE;AACb,UAAM,UAAAA,QAAG,SAAS,UAAU,YAAAC,QAAK,QAAQ,QAAQ,UAAU,OAAO,QAAQ,GAAG,MAAM,KAAK,IAAI,CAAC;AAC7F,WAAO;AAAA,MACL,SAAS,CAAC;AAAA,QACR,MAAM;AAAA,QACN,MAAM,sBAAsB,OAAO,QAAQ;AAAA,MAC7C,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;",
6
+ "names": ["fs", "path"]
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mcp/test/seed.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'fs';\nimport path from 'path';\n\nimport { mkdirIfNeeded } from 'playwright-core/lib/utils';\nimport { collectFilesForProject, findTopLevelProjects } from '../../runner/projectUtils';\n\nimport type { FullConfigInternal, FullProjectInternal } from '../../common/config';\n\nexport function seedProject(config: FullConfigInternal, projectName?: string) {\n if (!projectName)\n return findTopLevelProjects(config)[0];\n const project = config.projects.find(p => p.project.name === projectName);\n if (!project)\n throw new Error(`Project ${projectName} not found`);\n return project;\n}\n\nexport async function findSeedFile(project: FullProjectInternal) {\n const files = await collectFilesForProject(project);\n return files.find(file => path.basename(file).includes('seed'));\n}\n\nexport function defaultSeedFile(project: FullProjectInternal) {\n const testDir = project.project.testDir;\n return path.resolve(testDir, 'seed.spec.ts');\n}\n\nexport async function ensureSeedFile(project: FullProjectInternal) {\n const seedFile = await findSeedFile(project);\n if (seedFile)\n return seedFile;\n const seedFilePath = defaultSeedFile(project);\n await mkdirIfNeeded(seedFilePath);\n await fs.promises.writeFile(seedFilePath, seedFileContent);\n return seedFilePath;\n}\n\nexport const seedFileContent = `import { test, expect } from '@playwright/test';\n\ntest.describe('Test group', () => {\n test('seed', async ({ page }) => {\n // generate code here.\n });\n});\n`;\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,gBAAe;AACf,kBAAiB;AAEjB,mBAA8B;AAC9B,0BAA6D;AAItD,SAAS,YAAY,QAA4B,aAAsB;AAC5E,MAAI,CAAC;AACH,eAAO,0CAAqB,MAAM,EAAE,CAAC;AACvC,QAAM,UAAU,OAAO,SAAS,KAAK,OAAK,EAAE,QAAQ,SAAS,WAAW;AACxE,MAAI,CAAC;AACH,UAAM,IAAI,MAAM,WAAW,WAAW,YAAY;AACpD,SAAO;AACT;AAEA,eAAsB,aAAa,SAA8B;AAC/D,QAAM,QAAQ,UAAM,4CAAuB,OAAO;AAClD,SAAO,MAAM,KAAK,UAAQ,YAAAA,QAAK,SAAS,IAAI,EAAE,SAAS,MAAM,CAAC;AAChE;AAEO,SAAS,gBAAgB,SAA8B;AAC5D,QAAM,UAAU,QAAQ,QAAQ;AAChC,SAAO,YAAAA,QAAK,QAAQ,SAAS,cAAc;AAC7C;AAEA,eAAsB,eAAe,SAA8B;AACjE,QAAM,WAAW,MAAM,aAAa,OAAO;AAC3C,MAAI;AACF,WAAO;AACT,QAAM,eAAe,gBAAgB,OAAO;AAC5C,YAAM,4BAAc,YAAY;AAChC,QAAM,UAAAC,QAAG,SAAS,UAAU,cAAc,eAAe;AACzD,SAAO;AACT;AAEO,MAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
6
+ "names": ["path", "fs"]
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mcp/test/streams.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Writable } from 'stream';\n\nimport { stripAnsiEscapes } from '../../util';\n\nexport class StringWriteStream extends Writable {\n private _output: string[];\n private _prefix: string;\n\n constructor(output: string[], stdio: 'stdout' | 'stderr') {\n super();\n this._output = output;\n this._prefix = stdio === 'stdout' ? '' : '[err] ';\n }\n\n override _write(chunk: any, encoding: any, callback: any) {\n let text = stripAnsiEscapes(chunk.toString());\n if (text.endsWith('\\n'))\n text = text.slice(0, -1);\n if (text)\n this._output.push(this._prefix + text);\n callback();\n }\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,oBAAyB;AAEzB,kBAAiC;AAE1B,MAAM,0BAA0B,uBAAS;AAAA,EAI9C,YAAY,QAAkB,OAA4B;AACxD,UAAM;AACN,SAAK,UAAU;AACf,SAAK,UAAU,UAAU,WAAW,KAAK;AAAA,EAC3C;AAAA,EAES,OAAO,OAAY,UAAe,UAAe;AACxD,QAAI,WAAO,8BAAiB,MAAM,SAAS,CAAC;AAC5C,QAAI,KAAK,SAAS,IAAI;AACpB,aAAO,KAAK,MAAM,GAAG,EAAE;AACzB,QAAI;AACF,WAAK,QAAQ,KAAK,KAAK,UAAU,IAAI;AACvC,aAAS;AAAA,EACX;AACF;",
6
+ "names": []
7
+ }
@@ -31,15 +31,15 @@ __export(testBackend_exports, {
31
31
  TestServerBackend: () => TestServerBackend
32
32
  });
33
33
  module.exports = __toCommonJS(testBackend_exports);
34
+ var import_mcpBundle = require("playwright-core/lib/mcpBundle");
34
35
  var mcp = __toESM(require("../sdk/exports"));
35
36
  var import_testContext = require("./testContext");
36
37
  var testTools = __toESM(require("./testTools.js"));
37
38
  var generatorTools = __toESM(require("./generatorTools.js"));
38
39
  var plannerTools = __toESM(require("./plannerTools.js"));
39
40
  var import_tools = require("../browser/tools");
40
- var import_bundle = require("../sdk/bundle");
41
41
  class TestServerBackend {
42
- constructor(configOption, options) {
42
+ constructor(configPath, options) {
43
43
  this.name = "Playwright";
44
44
  this.version = "0.0.1";
45
45
  this._tools = [
@@ -55,10 +55,10 @@ class TestServerBackend {
55
55
  ...import_tools.browserTools.map((tool) => wrapBrowserTool(tool))
56
56
  ];
57
57
  this._options = options || {};
58
- this._configOption = configOption;
58
+ this._configPath = configPath;
59
59
  }
60
60
  async initialize(clientInfo) {
61
- this._context = new import_testContext.TestContext(clientInfo, this._configOption, this._options);
61
+ this._context = new import_testContext.TestContext(clientInfo, this._configPath, this._options);
62
62
  }
63
63
  async listTools() {
64
64
  return this._tools.map((tool) => mcp.toMcpTool(tool.schema));
@@ -74,13 +74,13 @@ class TestServerBackend {
74
74
  }
75
75
  }
76
76
  serverClosed() {
77
- void this._context.close();
77
+ void this._context?.close();
78
78
  }
79
79
  }
80
80
  const typesWithIntent = ["action", "assertion", "input"];
81
81
  function wrapBrowserTool(tool) {
82
82
  const inputSchema = typesWithIntent.includes(tool.schema.type) ? tool.schema.inputSchema.extend({
83
- intent: import_bundle.z.string().describe("The intent of the call, for example the test step description plan idea")
83
+ intent: import_mcpBundle.z.string().describe("The intent of the call, for example the test step description plan idea")
84
84
  }) : tool.schema.inputSchema;
85
85
  return {
86
86
  schema: {
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mcp/test/testBackend.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as mcp from '../sdk/exports';\nimport { TestContext } from './testContext';\nimport * as testTools from './testTools.js';\nimport * as generatorTools from './generatorTools.js';\nimport * as plannerTools from './plannerTools.js';\nimport { browserTools } from '../browser/tools';\nimport { z as zod } from '../sdk/bundle';\n\nimport type { TestTool } from './testTool';\nimport type { Tool as BrowserTool } from '../browser/tools/tool';\n\nexport class TestServerBackend implements mcp.ServerBackend {\n readonly name = 'Playwright';\n readonly version = '0.0.1';\n private _tools: TestTool<any>[] = [\n plannerTools.saveTestPlan,\n plannerTools.setupPage,\n plannerTools.submitTestPlan,\n generatorTools.setupPage,\n generatorTools.generatorReadLog,\n generatorTools.generatorWriteTest,\n testTools.listTests,\n testTools.runTests,\n testTools.debugTest,\n ...browserTools.map(tool => wrapBrowserTool(tool)),\n ];\n private _options: { muteConsole?: boolean, headless?: boolean };\n private _context: TestContext | undefined;\n private _configOption: string | undefined;\n\n constructor(configOption: string | undefined, options?: { muteConsole?: boolean, headless?: boolean }) {\n this._options = options || {};\n this._configOption = configOption;\n }\n\n async initialize(clientInfo: mcp.ClientInfo): Promise<void> {\n this._context = new TestContext(clientInfo, this._configOption, this._options);\n }\n\n async listTools(): Promise<mcp.Tool[]> {\n return this._tools.map(tool => mcp.toMcpTool(tool.schema));\n }\n\n async callTool(name: string, args: mcp.CallToolRequest['params']['arguments']): Promise<mcp.CallToolResult> {\n const tool = this._tools.find(tool => tool.schema.name === name);\n if (!tool)\n throw new Error(`Tool not found: ${name}. Available tools: ${this._tools.map(tool => tool.schema.name).join(', ')}`);\n try {\n return await tool.handle(this._context!, tool.schema.inputSchema.parse(args || {}));\n } catch (e) {\n return { content: [{ type: 'text', text: String(e) }], isError: true };\n }\n }\n\n serverClosed() {\n void this._context!.close();\n }\n}\n\nconst typesWithIntent = ['action', 'assertion', 'input'];\n\nfunction wrapBrowserTool(tool: BrowserTool): TestTool {\n const inputSchema = typesWithIntent.includes(tool.schema.type) ? (tool.schema.inputSchema as any).extend({\n intent: zod.string().describe('The intent of the call, for example the test step description plan idea')\n }) : tool.schema.inputSchema;\n return {\n schema: {\n ...tool.schema,\n inputSchema,\n },\n handle: async (context: TestContext, params: any) => {\n const response = await context.sendMessageToPausedTest({ callTool: { name: tool.schema.name, arguments: params } });\n return response.callTool!;\n },\n };\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,UAAqB;AACrB,yBAA4B;AAC5B,gBAA2B;AAC3B,qBAAgC;AAChC,mBAA8B;AAC9B,mBAA6B;AAC7B,oBAA0B;AAKnB,MAAM,kBAA+C;AAAA,EAmB1D,YAAY,cAAkC,SAAyD;AAlBvG,SAAS,OAAO;AAChB,SAAS,UAAU;AACnB,SAAQ,SAA0B;AAAA,MAChC,aAAa;AAAA,MACb,aAAa;AAAA,MACb,aAAa;AAAA,MACb,eAAe;AAAA,MACf,eAAe;AAAA,MACf,eAAe;AAAA,MACf,UAAU;AAAA,MACV,UAAU;AAAA,MACV,UAAU;AAAA,MACV,GAAG,0BAAa,IAAI,UAAQ,gBAAgB,IAAI,CAAC;AAAA,IACnD;AAME,SAAK,WAAW,WAAW,CAAC;AAC5B,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,MAAM,WAAW,YAA2C;AAC1D,SAAK,WAAW,IAAI,+BAAY,YAAY,KAAK,eAAe,KAAK,QAAQ;AAAA,EAC/E;AAAA,EAEA,MAAM,YAAiC;AACrC,WAAO,KAAK,OAAO,IAAI,UAAQ,IAAI,UAAU,KAAK,MAAM,CAAC;AAAA,EAC3D;AAAA,EAEA,MAAM,SAAS,MAAc,MAA+E;AAC1G,UAAM,OAAO,KAAK,OAAO,KAAK,CAAAA,UAAQA,MAAK,OAAO,SAAS,IAAI;AAC/D,QAAI,CAAC;AACH,YAAM,IAAI,MAAM,mBAAmB,IAAI,sBAAsB,KAAK,OAAO,IAAI,CAAAA,UAAQA,MAAK,OAAO,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE;AACrH,QAAI;AACF,aAAO,MAAM,KAAK,OAAO,KAAK,UAAW,KAAK,OAAO,YAAY,MAAM,QAAQ,CAAC,CAAC,CAAC;AAAA,IACpF,SAAS,GAAG;AACV,aAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,OAAO,CAAC,EAAE,CAAC,GAAG,SAAS,KAAK;AAAA,IACvE;AAAA,EACF;AAAA,EAEA,eAAe;AACb,SAAK,KAAK,SAAU,MAAM;AAAA,EAC5B;AACF;AAEA,MAAM,kBAAkB,CAAC,UAAU,aAAa,OAAO;AAEvD,SAAS,gBAAgB,MAA6B;AACpD,QAAM,cAAc,gBAAgB,SAAS,KAAK,OAAO,IAAI,IAAK,KAAK,OAAO,YAAoB,OAAO;AAAA,IACvG,QAAQ,cAAAC,EAAI,OAAO,EAAE,SAAS,yEAAyE;AAAA,EACzG,CAAC,IAAI,KAAK,OAAO;AACjB,SAAO;AAAA,IACL,QAAQ;AAAA,MACN,GAAG,KAAK;AAAA,MACR;AAAA,IACF;AAAA,IACA,QAAQ,OAAO,SAAsB,WAAgB;AACnD,YAAM,WAAW,MAAM,QAAQ,wBAAwB,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,MAAM,WAAW,OAAO,EAAE,CAAC;AAClH,aAAO,SAAS;AAAA,IAClB;AAAA,EACF;AACF;",
6
+ "names": ["tool", "zod"]
7
+ }
@@ -62,7 +62,7 @@ class GeneratorJournal {
62
62
  const result = [];
63
63
  result.push(`# Plan`);
64
64
  result.push(this._plan);
65
- result.push(`# Seed file: ${import_path.default.relative(this._rootPath, this._seed.file)}`);
65
+ result.push(`# Seed file: ${(0, import_utils.toPosixPath)(import_path.default.relative(this._rootPath, this._seed.file))}`);
66
66
  result.push("```ts");
67
67
  result.push(this._seed.content);
68
68
  result.push("```");
@@ -172,7 +172,7 @@ class TestContext {
172
172
  const { testRunner, screen, claimStdio, releaseStdio } = testRunnerAndScreen;
173
173
  claimStdio();
174
174
  try {
175
- const setupReporter = new import_list.default({ configDir, screen, includeTestId: true });
175
+ const setupReporter = new MCPListReporter({ configDir, screen, includeTestId: true });
176
176
  const { status: status2 } = await testRunner.runGlobalSetup([setupReporter]);
177
177
  if (status2 !== "passed")
178
178
  return { output: testRunnerAndScreen.output.join("\n"), status: status2 };
@@ -191,7 +191,7 @@ class TestContext {
191
191
  }
192
192
  };
193
193
  try {
194
- const reporter = new import_list.default({ configDir, screen, includeTestId: true });
194
+ const reporter = new MCPListReporter({ configDir, screen, includeTestId: true });
195
195
  status = await Promise.race([
196
196
  testRunner.runTests(reporter, params).then((result) => result.status),
197
197
  testRunnerAndScreen.waitForTestPaused().then(() => "paused")
@@ -271,6 +271,12 @@ const bestPracticesMarkdown = `
271
271
  - NEVER! use page.waitForTimeout()
272
272
  - NEVER! use page.evaluate()
273
273
  `;
274
+ class MCPListReporter extends import_list.default {
275
+ async onTestPaused() {
276
+ await new Promise(() => {
277
+ });
278
+ }
279
+ }
274
280
  // Annotate the CommonJS export names for ESM import in node:
275
281
  0 && (module.exports = {
276
282
  GeneratorJournal,
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mcp/test/testContext.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'fs';\nimport os from 'os';\nimport path from 'path';\n\nimport { noColors, escapeRegExp, ManualPromise } from 'playwright-core/lib/utils';\n\nimport { terminalScreen } from '../../reporters/base';\nimport ListReporter from '../../reporters/list';\nimport { StringWriteStream } from './streams';\nimport { fileExistsAsync } from '../../util';\nimport { TestRunner, TestRunnerEvent } from '../../runner/testRunner';\nimport { ensureSeedFile, seedProject } from './seed';\nimport { firstRootPath } from '../sdk/exports';\nimport { resolveConfigLocation } from '../../common/configLoader';\nimport { parseResponse } from '../browser/response';\nimport { logUnhandledError } from '../log';\n\nimport type { TerminalScreen } from '../../reporters/base';\nimport type { FullResultStatus, RunTestsParams } from '../../runner/testRunner';\nimport type { ConfigLocation } from '../../common/config';\nimport type { ClientInfo } from '../sdk/exports';\nimport type { BrowserMCPRequest, BrowserMCPResponse } from './browserBackend';\n\nexport type SeedFile = {\n file: string;\n content: string;\n};\n\nexport class GeneratorJournal {\n private _rootPath: string;\n private _plan: string;\n private _seed: SeedFile;\n private _steps: { title: string, code: string }[];\n\n constructor(rootPath: string, plan: string, seed: SeedFile) {\n this._rootPath = rootPath;\n this._plan = plan;\n this._seed = seed;\n this._steps = [];\n }\n\n logStep(title: string | undefined, code: string) {\n if (title)\n this._steps.push({ title, code });\n }\n\n journal() {\n const result: string[] = [];\n result.push(`# Plan`);\n result.push(this._plan);\n result.push(`# Seed file: ${path.relative(this._rootPath, this._seed.file)}`);\n result.push('```ts');\n result.push(this._seed.content);\n result.push('```');\n result.push(`# Steps`);\n result.push(this._steps.map(step => `### ${step.title}\n\\`\\`\\`ts\n${step.code}\n\\`\\`\\``).join('\\n\\n'));\n result.push(bestPracticesMarkdown);\n return result.join('\\n\\n');\n }\n}\n\ntype TestRunnerAndScreen = {\n testRunner: TestRunner;\n screen: TerminalScreen;\n claimStdio: () => void;\n releaseStdio: () => void;\n output: string[];\n waitForTestPaused: () => Promise<void>;\n sendMessageToPausedTest?: (params: { request: BrowserMCPRequest }) => Promise<{ response: BrowserMCPResponse, error?: any }>;\n};\n\nexport class TestContext {\n private _clientInfo: ClientInfo;\n private _testRunnerAndScreen: TestRunnerAndScreen | undefined;\n readonly computedHeaded: boolean;\n private readonly _configLocation: ConfigLocation;\n readonly rootPath: string;\n generatorJournal: GeneratorJournal | undefined;\n\n constructor(clientInfo: ClientInfo, configPath: string | undefined, options?: { muteConsole?: boolean, headless?: boolean }) {\n this._clientInfo = clientInfo;\n\n const rootPath = firstRootPath(clientInfo);\n this._configLocation = resolveConfigLocation(configPath || rootPath);\n this.rootPath = rootPath || this._configLocation.configDir;\n\n if (options?.headless !== undefined)\n this.computedHeaded = !options.headless;\n else\n this.computedHeaded = !process.env.CI && !(os.platform() === 'linux' && !process.env.DISPLAY);\n }\n\n existingTestRunner(): TestRunner | undefined {\n return this._testRunnerAndScreen?.testRunner;\n }\n\n private async _cleanupTestRunner() {\n if (!this._testRunnerAndScreen)\n return;\n await this._testRunnerAndScreen.testRunner.stopTests();\n this._testRunnerAndScreen.claimStdio();\n try {\n await this._testRunnerAndScreen.testRunner.runGlobalTeardown();\n } finally {\n this._testRunnerAndScreen.releaseStdio();\n this._testRunnerAndScreen = undefined;\n }\n }\n\n async createTestRunner() {\n await this._cleanupTestRunner();\n\n const testRunner = new TestRunner(this._configLocation, {});\n await testRunner.initialize({});\n const testPaused = new ManualPromise<void>();\n const testRunnerAndScreen: TestRunnerAndScreen = {\n ...createScreen(),\n testRunner,\n waitForTestPaused: () => testPaused,\n };\n this._testRunnerAndScreen = testRunnerAndScreen;\n\n testRunner.on(TestRunnerEvent.TestPaused, params => {\n testRunnerAndScreen.sendMessageToPausedTest = params.sendMessage;\n testPaused.resolve();\n });\n return testRunnerAndScreen;\n }\n\n async getOrCreateSeedFile(seedFile: string | undefined, projectName: string | undefined) {\n const configDir = this._configLocation.configDir;\n const { testRunner } = await this.createTestRunner();\n const config = await testRunner.loadConfig();\n const project = seedProject(config, projectName);\n\n if (!seedFile) {\n seedFile = await ensureSeedFile(project);\n } else {\n const candidateFiles: string[] = [];\n const testDir = project.project.testDir;\n candidateFiles.push(path.resolve(testDir, seedFile));\n candidateFiles.push(path.resolve(configDir, seedFile));\n candidateFiles.push(path.resolve(this.rootPath, seedFile));\n let resolvedSeedFile: string | undefined;\n for (const candidateFile of candidateFiles) {\n if (await fileExistsAsync(candidateFile)) {\n resolvedSeedFile = candidateFile;\n break;\n }\n }\n if (!resolvedSeedFile)\n throw new Error('seed test not found.');\n seedFile = resolvedSeedFile;\n }\n\n const seedFileContent = await fs.promises.readFile(seedFile, 'utf8');\n return {\n file: seedFile,\n content: seedFileContent,\n projectName: project.project.name,\n };\n }\n\n async runSeedTest(seedFile: string, projectName: string): Promise<{ output: string, status: FullResultStatus | 'paused' }> {\n const result = await this.runTestsWithGlobalSetupAndPossiblePause({\n headed: this.computedHeaded,\n locations: ['/' + escapeRegExp(seedFile) + '/'],\n projects: [projectName],\n timeout: 0,\n workers: 1,\n pauseAtEnd: true,\n disableConfigReporters: true,\n failOnLoadErrors: true,\n });\n if (result.status === 'passed')\n result.output += '\\nError: seed test not found.';\n else if (result.status !== 'paused')\n result.output += '\\nError while running the seed test.';\n return result;\n }\n\n async runTestsWithGlobalSetupAndPossiblePause(params: RunTestsParams): Promise<{ output: string, status: FullResultStatus | 'paused' }> {\n const configDir = this._configLocation.configDir;\n const testRunnerAndScreen = await this.createTestRunner();\n const { testRunner, screen, claimStdio, releaseStdio } = testRunnerAndScreen;\n\n claimStdio();\n try {\n const setupReporter = new ListReporter({ configDir, screen, includeTestId: true });\n const { status } = await testRunner.runGlobalSetup([setupReporter]);\n if (status !== 'passed')\n return { output: testRunnerAndScreen.output.join('\\n'), status };\n } finally {\n releaseStdio();\n }\n\n let status: FullResultStatus | 'paused' = 'passed';\n\n const cleanup = async () => {\n claimStdio();\n try {\n const result = await testRunner.runGlobalTeardown();\n if (status === 'passed')\n status = result.status;\n } finally {\n releaseStdio();\n }\n };\n\n try {\n const reporter = new ListReporter({ configDir, screen, includeTestId: true });\n status = await Promise.race([\n testRunner.runTests(reporter, params).then(result => result.status),\n testRunnerAndScreen.waitForTestPaused().then(() => 'paused' as const),\n ]);\n\n if (status === 'paused') {\n const response = await testRunnerAndScreen.sendMessageToPausedTest!({ request: { initialize: { clientInfo: this._clientInfo } } });\n if (response.error)\n throw new Error(response.error.message);\n testRunnerAndScreen.output.push(response.response.initialize!.pausedMessage);\n return { output: testRunnerAndScreen.output.join('\\n'), status };\n }\n } catch (e) {\n status = 'failed';\n testRunnerAndScreen.output.push(String(e));\n await cleanup();\n return { output: testRunnerAndScreen.output.join('\\n'), status };\n }\n\n await cleanup();\n return { output: testRunnerAndScreen.output.join('\\n'), status };\n }\n\n async close() {\n await this._cleanupTestRunner().catch(logUnhandledError);\n }\n\n async sendMessageToPausedTest(request: BrowserMCPRequest): Promise<BrowserMCPResponse> {\n const sendMessage = this._testRunnerAndScreen?.sendMessageToPausedTest;\n if (!sendMessage)\n throw new Error('Must setup test before interacting with the page');\n const result = await sendMessage({ request });\n if (result.error)\n throw new Error(result.error.message);\n if (typeof request?.callTool?.arguments?.['intent'] === 'string') {\n const response = parseResponse(result.response.callTool!);\n if (response && !response.isError && response.code)\n this.generatorJournal?.logStep(request.callTool.arguments['intent'], response.code);\n }\n return result.response;\n }\n}\n\nexport function createScreen() {\n const output: string[] = [];\n const stdout = new StringWriteStream(output, 'stdout');\n const stderr = new StringWriteStream(output, 'stderr');\n\n const screen = {\n ...terminalScreen,\n isTTY: false,\n colors: noColors,\n stdout: stdout as unknown as NodeJS.WriteStream,\n stderr: stderr as unknown as NodeJS.WriteStream,\n };\n\n /* eslint-disable no-restricted-properties */\n const originalStdoutWrite = process.stdout.write;\n const originalStderrWrite = process.stderr.write;\n\n const claimStdio = () => {\n process.stdout.write = (chunk: string | Buffer) => {\n stdout.write(chunk);\n return true;\n };\n process.stderr.write = (chunk: string | Buffer) => {\n stderr.write(chunk);\n return true;\n };\n };\n\n const releaseStdio = () => {\n process.stdout.write = originalStdoutWrite;\n process.stderr.write = originalStderrWrite;\n };\n /* eslint-enable no-restricted-properties */\n\n return { screen, claimStdio, releaseStdio, output };\n}\n\nconst bestPracticesMarkdown = `\n# Best practices\n- Do not improvise, do not add directives that were not asked for\n- Use clear, descriptive assertions to validate the expected behavior\n- Use reliable locators from this log\n- Use local variables for locators that are used multiple times\n- Use Playwright waiting assertions and best practices from this log\n- NEVER! use page.waitForLoadState()\n- NEVER! use page.waitForNavigation()\n- NEVER! use page.waitForTimeout()\n- NEVER! use page.evaluate()\n`;\n"],
5
+ "mappings": "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",
6
+ "names": ["path", "os", "fs", "ListReporter", "status"]
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mcp/test/testTool.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { z } from 'zod';\nimport type { TestContext } from './testContext.js';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { ToolSchema } from '../sdk/tool.js';\n\nexport type TestTool<Input extends z.Schema = z.Schema> = {\n schema: ToolSchema<Input>;\n handle: (context: TestContext, params: z.output<Input>) => Promise<CallToolResult>;\n};\n\nexport function defineTestTool<Input extends z.Schema>(tool: TestTool<Input>): TestTool<Input> {\n return tool;\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA0BO,SAAS,eAAuC,MAAwC;AAC7F,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -33,7 +33,7 @@ __export(testTools_exports, {
33
33
  runTests: () => runTests
34
34
  });
35
35
  module.exports = __toCommonJS(testTools_exports);
36
- var import_bundle = require("../sdk/bundle");
36
+ var import_mcpBundle = require("playwright-core/lib/mcpBundle");
37
37
  var import_listModeReporter = __toESM(require("../../reporters/listModeReporter"));
38
38
  var import_testTool = require("./testTool");
39
39
  const listTests = (0, import_testTool.defineTestTool)({
@@ -41,7 +41,7 @@ const listTests = (0, import_testTool.defineTestTool)({
41
41
  name: "test_list",
42
42
  title: "List tests",
43
43
  description: "List tests",
44
- inputSchema: import_bundle.z.object({}),
44
+ inputSchema: import_mcpBundle.z.object({}),
45
45
  type: "readOnly"
46
46
  },
47
47
  handle: async (context) => {
@@ -56,15 +56,15 @@ const runTests = (0, import_testTool.defineTestTool)({
56
56
  name: "test_run",
57
57
  title: "Run tests",
58
58
  description: "Run tests",
59
- inputSchema: import_bundle.z.object({
60
- locations: import_bundle.z.array(import_bundle.z.string()).optional().describe('Folder, file or location to run: "test/e2e" or "test/e2e/file.spec.ts" or "test/e2e/file.spec.ts:20"'),
61
- projects: import_bundle.z.array(import_bundle.z.string()).optional().describe('Projects to run, projects from playwright.config.ts, by default runs all projects. Running with "chromium" is a good start')
59
+ inputSchema: import_mcpBundle.z.object({
60
+ locations: import_mcpBundle.z.array(import_mcpBundle.z.string()).optional().describe('Folder, file or location to run: "test/e2e" or "test/e2e/file.spec.ts" or "test/e2e/file.spec.ts:20"'),
61
+ projects: import_mcpBundle.z.array(import_mcpBundle.z.string()).optional().describe('Projects to run, projects from playwright.config.ts, by default runs all projects. Running with "chromium" is a good start')
62
62
  }),
63
63
  type: "readOnly"
64
64
  },
65
65
  handle: async (context, params) => {
66
66
  const { output } = await context.runTestsWithGlobalSetupAndPossiblePause({
67
- locations: params.locations,
67
+ locations: params.locations ?? [],
68
68
  projects: params.projects,
69
69
  disableConfigReporters: true
70
70
  });
@@ -76,10 +76,10 @@ const debugTest = (0, import_testTool.defineTestTool)({
76
76
  name: "test_debug",
77
77
  title: "Debug single test",
78
78
  description: "Debug single test",
79
- inputSchema: import_bundle.z.object({
80
- test: import_bundle.z.object({
81
- id: import_bundle.z.string().describe("Test ID to debug."),
82
- title: import_bundle.z.string().describe("Human readable test title for granting permission to debug the test.")
79
+ inputSchema: import_mcpBundle.z.object({
80
+ test: import_mcpBundle.z.object({
81
+ id: import_mcpBundle.z.string().describe("Test ID to debug."),
82
+ title: import_mcpBundle.z.string().describe("Human readable test title for granting permission to debug the test.")
83
83
  })
84
84
  }),
85
85
  type: "readOnly"
@@ -87,6 +87,8 @@ const debugTest = (0, import_testTool.defineTestTool)({
87
87
  handle: async (context, params) => {
88
88
  const { output, status } = await context.runTestsWithGlobalSetupAndPossiblePause({
89
89
  headed: context.computedHeaded,
90
+ locations: [],
91
+ // we can make this faster by passing the test's location, so we don't need to scan all tests to find the ID
90
92
  testIds: [params.test.id],
91
93
  // For automatic recovery
92
94
  timeout: 0,
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mcp/test/testTools.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { z } from '../sdk/bundle';\nimport ListModeReporter from '../../reporters/listModeReporter';\nimport { defineTestTool } from './testTool';\n\nexport const listTests = defineTestTool({\n schema: {\n name: 'test_list',\n title: 'List tests',\n description: 'List tests',\n inputSchema: z.object({}),\n type: 'readOnly',\n },\n\n handle: async context => {\n const { testRunner, screen, output } = await context.createTestRunner();\n const reporter = new ListModeReporter({ screen, includeTestId: true });\n await testRunner.listTests(reporter, {});\n return { content: output.map(text => ({ type: 'text', text })) };\n },\n});\n\nexport const runTests = defineTestTool({\n schema: {\n name: 'test_run',\n title: 'Run tests',\n description: 'Run tests',\n inputSchema: z.object({\n locations: z.array(z.string()).optional().describe('Folder, file or location to run: \"test/e2e\" or \"test/e2e/file.spec.ts\" or \"test/e2e/file.spec.ts:20\"'),\n projects: z.array(z.string()).optional().describe('Projects to run, projects from playwright.config.ts, by default runs all projects. Running with \"chromium\" is a good start'),\n }),\n type: 'readOnly',\n },\n\n handle: async (context, params) => {\n const { output } = await context.runTestsWithGlobalSetupAndPossiblePause({\n locations: params.locations,\n projects: params.projects,\n disableConfigReporters: true,\n });\n return { content: [{ type: 'text', text: output }] };\n },\n});\n\nexport const debugTest = defineTestTool({\n schema: {\n name: 'test_debug',\n title: 'Debug single test',\n description: 'Debug single test',\n inputSchema: z.object({\n test: z.object({\n id: z.string().describe('Test ID to debug.'),\n title: z.string().describe('Human readable test title for granting permission to debug the test.'),\n }),\n }),\n type: 'readOnly',\n },\n\n handle: async (context, params) => {\n const { output, status } = await context.runTestsWithGlobalSetupAndPossiblePause({\n headed: context.computedHeaded,\n testIds: [params.test.id],\n // For automatic recovery\n timeout: 0,\n workers: 1,\n pauseOnError: true,\n disableConfigReporters: true,\n actionTimeout: 5000,\n });\n return { content: [{ type: 'text', text: output }], isError: status !== 'paused' && status !== 'passed' };\n },\n});\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,oBAAkB;AAClB,8BAA6B;AAC7B,sBAA+B;AAExB,MAAM,gBAAY,gCAAe;AAAA,EACtC,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,aAAa,gBAAE,OAAO,CAAC,CAAC;AAAA,IACxB,MAAM;AAAA,EACR;AAAA,EAEA,QAAQ,OAAM,YAAW;AACvB,UAAM,EAAE,YAAY,QAAQ,OAAO,IAAI,MAAM,QAAQ,iBAAiB;AACtE,UAAM,WAAW,IAAI,wBAAAA,QAAiB,EAAE,QAAQ,eAAe,KAAK,CAAC;AACrE,UAAM,WAAW,UAAU,UAAU,CAAC,CAAC;AACvC,WAAO,EAAE,SAAS,OAAO,IAAI,WAAS,EAAE,MAAM,QAAQ,KAAK,EAAE,EAAE;AAAA,EACjE;AACF,CAAC;AAEM,MAAM,eAAW,gCAAe;AAAA,EACrC,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,aAAa,gBAAE,OAAO;AAAA,MACpB,WAAW,gBAAE,MAAM,gBAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,sGAAsG;AAAA,MACzJ,UAAU,gBAAE,MAAM,gBAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,4HAA4H;AAAA,IAChL,CAAC;AAAA,IACD,MAAM;AAAA,EACR;AAAA,EAEA,QAAQ,OAAO,SAAS,WAAW;AACjC,UAAM,EAAE,OAAO,IAAI,MAAM,QAAQ,wCAAwC;AAAA,MACvE,WAAW,OAAO;AAAA,MAClB,UAAU,OAAO;AAAA,MACjB,wBAAwB;AAAA,IAC1B,CAAC;AACD,WAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,OAAO,CAAC,EAAE;AAAA,EACrD;AACF,CAAC;AAEM,MAAM,gBAAY,gCAAe;AAAA,EACtC,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,aAAa,gBAAE,OAAO;AAAA,MACpB,MAAM,gBAAE,OAAO;AAAA,QACb,IAAI,gBAAE,OAAO,EAAE,SAAS,mBAAmB;AAAA,QAC3C,OAAO,gBAAE,OAAO,EAAE,SAAS,sEAAsE;AAAA,MACnG,CAAC;AAAA,IACH,CAAC;AAAA,IACD,MAAM;AAAA,EACR;AAAA,EAEA,QAAQ,OAAO,SAAS,WAAW;AACjC,UAAM,EAAE,QAAQ,OAAO,IAAI,MAAM,QAAQ,wCAAwC;AAAA,MAC/E,QAAQ,QAAQ;AAAA,MAChB,SAAS,CAAC,OAAO,KAAK,EAAE;AAAA;AAAA,MAExB,SAAS;AAAA,MACT,SAAS;AAAA,MACT,cAAc;AAAA,MACd,wBAAwB;AAAA,MACxB,eAAe;AAAA,IACjB,CAAC;AACD,WAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,OAAO,CAAC,GAAG,SAAS,WAAW,YAAY,WAAW,SAAS;AAAA,EAC1G;AACF,CAAC;",
6
+ "names": ["ListModeReporter"]
7
+ }