playwright 1.57.0-alpha-2025-11-10 → 1.57.0-alpha-2025-11-12

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.
@@ -207,24 +207,11 @@ class CopilotGenerator {
207
207
  await deleteFile(`.github/chatmodes/ \u{1F3AD} planner.chatmode.md`, "legacy planner chatmode");
208
208
  await deleteFile(`.github/chatmodes/\u{1F3AD} generator.chatmode.md`, "legacy generator chatmode");
209
209
  await deleteFile(`.github/chatmodes/\u{1F3AD} healer.chatmode.md`, "legacy healer chatmode");
210
+ await deleteFile(`.github/agents/ \u{1F3AD} planner.agent.md`, "legacy planner agent");
211
+ await deleteFile(`.github/agents/\u{1F3AD} generator.agent.md`, "legacy generator agent");
212
+ await deleteFile(`.github/agents/\u{1F3AD} healer.agent.md`, "legacy healer agent");
210
213
  await VSCodeGenerator.appendToMCPJson();
211
- const cwdFolder = import_path.default.basename(process.cwd());
212
- const mcpConfig = {
213
- "mcpServers": {
214
- "playwright-test": {
215
- "type": "stdio",
216
- "command": "npx",
217
- "args": [
218
- `--prefix=/home/runner/work/${cwdFolder}/${cwdFolder}`,
219
- "playwright",
220
- "run-test-mcp-server",
221
- "--headless",
222
- `--config=/home/runner/work/${cwdFolder}/${cwdFolder}`
223
- ],
224
- "tools": ["*"]
225
- }
226
- }
227
- };
214
+ const mcpConfig = { mcpServers: CopilotGenerator.mcpServers };
228
215
  if (!import_fs.default.existsSync(".github/copilot-setup-steps.yml")) {
229
216
  const yaml2 = import_fs.default.readFileSync(import_path.default.join(__dirname, "copilot-setup-steps.yml"), "utf-8");
230
217
  await writeFile(".github/workflows/copilot-setup-steps.yml", yaml2, "\u{1F527}", "GitHub Copilot setup steps");
@@ -241,10 +228,11 @@ class CopilotGenerator {
241
228
  const examples = agent.examples.length ? ` Examples: ${agent.examples.map((example) => `<example>${example}</example>`).join("")}` : "";
242
229
  const lines = [];
243
230
  const header = {
244
- name: agent.header.name,
245
- description: agent.header.description + examples,
246
- tools: agent.header.tools,
247
- model: "Claude Sonnet 4"
231
+ "name": agent.header.name,
232
+ "description": agent.header.description + examples,
233
+ "tools": agent.header.tools,
234
+ "model": "Claude Sonnet 4",
235
+ "mcp-servers": CopilotGenerator.mcpServers
248
236
  };
249
237
  lines.push(`---`);
250
238
  lines.push(import_utilsBundle.yaml.stringify(header) + `---`);
@@ -253,6 +241,19 @@ class CopilotGenerator {
253
241
  lines.push("");
254
242
  return lines.join("\n");
255
243
  }
244
+ static {
245
+ this.mcpServers = {
246
+ "playwright-test": {
247
+ "type": "stdio",
248
+ "command": "npx",
249
+ "args": [
250
+ "playwright",
251
+ "run-test-mcp-server"
252
+ ],
253
+ "tools": ["*"]
254
+ }
255
+ };
256
+ }
256
257
  }
257
258
  class VSCodeGenerator {
258
259
  static async init(config, projectName) {
@@ -5,7 +5,6 @@ model: sonnet
5
5
  color: green
6
6
  tools:
7
7
  - search
8
- - edit
9
8
  - playwright-test/browser_click
10
9
  - playwright-test/browser_close
11
10
  - playwright-test/browser_console_messages
@@ -24,6 +23,7 @@ tools:
24
23
  - playwright-test/browser_type
25
24
  - playwright-test/browser_wait_for
26
25
  - playwright-test/planner_setup_page
26
+ - playwright-test/planner_save_plan
27
27
  ---
28
28
 
29
29
  You are an expert web test planner with extensive experience in quality assurance, user experience testing, and test
@@ -61,52 +61,7 @@ You will:
61
61
 
62
62
  5. **Create Documentation**
63
63
 
64
- Save your test plan as requested:
65
- - Executive summary of the tested page/application
66
- - Individual scenarios as separate sections
67
- - Each scenario formatted with numbered steps
68
- - Each test case with proposed file name for implementation
69
- - Clear expected results for verification
70
-
71
- <example-spec>
72
- # TodoMVC Application - Comprehensive Test Plan
73
-
74
- ## Application Overview
75
-
76
- The TodoMVC application is a React-based todo list manager that provides core task management functionality. The
77
- application features:
78
-
79
- - **Task Management**: Add, edit, complete, and delete individual todos
80
- - **Bulk Operations**: Mark all todos as complete/incomplete and clear all completed todos
81
- - **Filtering**: View todos by All, Active, or Completed status
82
- - **URL Routing**: Support for direct navigation to filtered views via URLs
83
- - **Counter Display**: Real-time count of active (incomplete) todos
84
- - **Persistence**: State maintained during session (browser refresh behavior not tested)
85
-
86
- ## Test Scenarios
87
-
88
- ### 1. Adding New Todos
89
-
90
- **Seed:** `tests/seed.spec.ts`
91
-
92
- #### 1.1 Add Valid Todo
93
-
94
- **File** `tests/adding-new-todos/add-valid-todo.spec.ts`
95
-
96
- **Steps:**
97
- 1. Click in the "What needs to be done?" input field
98
- 2. Type "Buy groceries"
99
- 3. Press Enter key
100
-
101
- **Expected Results:**
102
- - Todo appears in the list with unchecked checkbox
103
- - Counter shows "1 item left"
104
- - Input field is cleared and ready for next entry
105
- - Todo list controls become visible (Mark all as complete checkbox)
106
-
107
- #### 1.2
108
- ...
109
- </example-spec>
64
+ Submit your test plan using `planner_save_plan` tool.
110
65
 
111
66
  **Quality Standards**:
112
67
  - Write steps that are specific enough for any tester to follow
@@ -164,6 +164,7 @@ function configFromCLIOptions(cliOptions) {
164
164
  contextOptions,
165
165
  cdpEndpoint: cliOptions.cdpEndpoint,
166
166
  cdpHeaders: cliOptions.cdpHeader,
167
+ initPage: cliOptions.initPage,
167
168
  initScript: cliOptions.initScript
168
169
  },
169
170
  server: {
@@ -208,6 +209,9 @@ function configFromEnv() {
208
209
  options.headless = envToBoolean(process.env.PLAYWRIGHT_MCP_HEADLESS);
209
210
  options.host = envToString(process.env.PLAYWRIGHT_MCP_HOST);
210
211
  options.ignoreHttpsErrors = envToBoolean(process.env.PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS);
212
+ const initPage = envToString(process.env.PLAYWRIGHT_MCP_INIT_PAGE);
213
+ if (initPage)
214
+ options.initPage = [initPage];
211
215
  const initScript = envToString(process.env.PLAYWRIGHT_MCP_INIT_SCRIPT);
212
216
  if (initScript)
213
217
  options.initScript = [initScript];
@@ -75,6 +75,7 @@ class Context {
75
75
  const { browserContext } = await this._ensureBrowserContext();
76
76
  const page = await browserContext.newPage();
77
77
  this._currentTab = this._tabs.find((t) => t.page === page);
78
+ await this._currentTab.initializedPromise;
78
79
  return this._currentTab;
79
80
  }
80
81
  async selectTab(index) {
@@ -29,6 +29,7 @@ var import_utils2 = require("./tools/utils");
29
29
  var import_log = require("../log");
30
30
  var import_dialogs = require("./tools/dialogs");
31
31
  var import_files = require("./tools/files");
32
+ var import_transform = require("../../transform/transform");
32
33
  const TabEvents = {
33
34
  modalState: "modalState"
34
35
  };
@@ -64,7 +65,7 @@ class Tab extends import_events.EventEmitter {
64
65
  page.setDefaultNavigationTimeout(this.context.config.timeouts.navigation);
65
66
  page.setDefaultTimeout(this.context.config.timeouts.action);
66
67
  page[tabSymbol] = this;
67
- this._initializedPromise = this._initialize();
68
+ this.initializedPromise = this._initialize();
68
69
  }
69
70
  static forPage(page) {
70
71
  return page[tabSymbol];
@@ -85,6 +86,14 @@ class Tab extends import_events.EventEmitter {
85
86
  const requests = await this.page.requests().catch(() => []);
86
87
  for (const request of requests)
87
88
  this._requests.add(request);
89
+ for (const initPage of this.context.config.browser.initPage || []) {
90
+ try {
91
+ const { default: func } = await (0, import_transform.requireOrImport)(initPage);
92
+ await func({ page: this.page });
93
+ } catch (e) {
94
+ (0, import_log.logUnhandledError)(e);
95
+ }
96
+ }
88
97
  }
89
98
  modalStates() {
90
99
  return this._modalStates;
@@ -166,11 +175,11 @@ class Tab extends import_events.EventEmitter {
166
175
  await this.waitForLoadState("load", { timeout: 5e3 });
167
176
  }
168
177
  async consoleMessages(type) {
169
- await this._initializedPromise;
178
+ await this.initializedPromise;
170
179
  return this._consoleMessages.filter((message) => type ? message.type === type : true);
171
180
  }
172
181
  async requests() {
173
- await this._initializedPromise;
182
+ await this.initializedPromise;
174
183
  return this._requests;
175
184
  }
176
185
  async captureSnapshot() {
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var runCode_exports = {};
30
+ __export(runCode_exports, {
31
+ default: () => runCode_default
32
+ });
33
+ module.exports = __toCommonJS(runCode_exports);
34
+ var import_vm = __toESM(require("vm"));
35
+ var import_utils = require("playwright-core/lib/utils");
36
+ var import_bundle = require("../../sdk/bundle");
37
+ var import_tool = require("./tool");
38
+ const codeSchema = import_bundle.z.object({
39
+ code: import_bundle.z.string().describe(`Playwright code snippet to run. The snippet should access the \`page\` object to interact with the page. Can make multiple statements. For example: \`await page.getByRole('button', { name: 'Submit' }).click();\``)
40
+ });
41
+ const runCode = (0, import_tool.defineTabTool)({
42
+ capability: "core",
43
+ schema: {
44
+ name: "browser_run_code",
45
+ title: "Run Playwright code",
46
+ description: "Run Playwright code snippet",
47
+ inputSchema: codeSchema,
48
+ type: "action"
49
+ },
50
+ handle: async (tab, params, response) => {
51
+ response.setIncludeSnapshot();
52
+ response.addCode(params.code);
53
+ const __end__ = new import_utils.ManualPromise();
54
+ const context = {
55
+ page: tab.page,
56
+ __end__
57
+ };
58
+ import_vm.default.createContext(context);
59
+ await tab.waitForCompletion(async () => {
60
+ const snippet = `(async () => {
61
+ try {
62
+ ${params.code};
63
+ __end__.resolve();
64
+ } catch (e) {
65
+ __end__.reject(e);
66
+ }
67
+ })()`;
68
+ import_vm.default.runInContext(snippet, context);
69
+ await __end__;
70
+ });
71
+ }
72
+ });
73
+ var runCode_default = [
74
+ runCode
75
+ ];
@@ -44,6 +44,7 @@ var import_mouse = __toESM(require("./tools/mouse"));
44
44
  var import_navigate = __toESM(require("./tools/navigate"));
45
45
  var import_network = __toESM(require("./tools/network"));
46
46
  var import_pdf = __toESM(require("./tools/pdf"));
47
+ var import_runCode = __toESM(require("./tools/runCode"));
47
48
  var import_snapshot = __toESM(require("./tools/snapshot"));
48
49
  var import_screenshot = __toESM(require("./tools/screenshot"));
49
50
  var import_tabs = __toESM(require("./tools/tabs"));
@@ -63,6 +64,7 @@ const browserTools = [
63
64
  ...import_network.default,
64
65
  ...import_mouse.default,
65
66
  ...import_pdf.default,
67
+ ...import_runCode.default,
66
68
  ...import_screenshot.default,
67
69
  ...import_snapshot.default,
68
70
  ...import_tabs.default,
@@ -42,7 +42,7 @@ var import_proxyBackend = require("./sdk/proxyBackend");
42
42
  var import_browserServerBackend = require("./browser/browserServerBackend");
43
43
  var import_extensionContextFactory = require("./extension/extensionContextFactory");
44
44
  function decorateCommand(command, version) {
45
- command.option("--allowed-hosts <hosts...>", "comma-separated list of hosts this server is allowed to serve from. Defaults to the host the server is bound to. Pass '*' to disable the host check.", import_config.commaSeparatedList).option("--allowed-origins <origins>", "semicolon-separated list of origins to allow the browser to request. Default is to allow all.", import_config.semicolonSeparatedList).option("--blocked-origins <origins>", "semicolon-separated list of origins to block the browser from requesting. Blocklist is evaluated before allowlist. If used without the allowlist, requests not matching the blocklist are still allowed.", import_config.semicolonSeparatedList).option("--block-service-workers", "block service workers").option("--browser <browser>", "browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge.").option("--caps <caps>", "comma-separated list of additional capabilities to enable, possible values: vision, pdf.", import_config.commaSeparatedList).option("--cdp-endpoint <endpoint>", "CDP endpoint to connect to.").option("--cdp-header <headers...>", "CDP headers to send with the connect request, multiple can be specified.", import_config.headerParser).option("--config <path>", "path to the configuration file.").option("--device <device>", 'device to emulate, for example: "iPhone 15"').option("--executable-path <path>", "path to the browser executable.").option("--extension", 'Connect to a running browser instance (Edge/Chrome only). Requires the "Playwright MCP Bridge" browser extension to be installed.').option("--grant-permissions <permissions...>", 'List of permissions to grant to the browser context, for example "geolocation", "clipboard-read", "clipboard-write".', import_config.commaSeparatedList).option("--headless", "run browser in headless mode, headed by default").option("--host <host>", "host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.").option("--ignore-https-errors", "ignore https errors").option("--init-script <path...>", "path to JavaScript file to add as an initialization script. The script will be evaluated in every page before any of the page's scripts. Can be specified multiple times.").option("--isolated", "keep the browser profile in memory, do not save it to disk.").option("--image-responses <mode>", 'whether to send image responses to the client. Can be "allow" or "omit", Defaults to "allow".').option("--no-sandbox", "disable the sandbox for all process types that are normally sandboxed.").option("--output-dir <path>", "path to the directory for output files.").option("--port <port>", "port to listen on for SSE transport.").option("--proxy-bypass <bypass>", 'comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"').option("--proxy-server <proxy>", 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"').option("--save-session", "Whether to save the Playwright MCP session into the output directory.").option("--save-trace", "Whether to save the Playwright Trace of the session into the output directory.").option("--save-video <size>", 'Whether to save the video of the session into the output directory. For example "--save-video=800x600"', import_config.resolutionParser.bind(null, "--save-video")).option("--secrets <path>", "path to a file containing secrets in the dotenv format", import_config.dotenvFileLoader).option("--shared-browser-context", "reuse the same browser context between all connected HTTP clients.").option("--storage-state <path>", "path to the storage state file for isolated sessions.").option("--test-id-attribute <attribute>", 'specify the attribute to use for test ids, defaults to "data-testid"').option("--timeout-action <timeout>", "specify action timeout in milliseconds, defaults to 5000ms", import_config.numberParser).option("--timeout-navigation <timeout>", "specify navigation timeout in milliseconds, defaults to 60000ms", import_config.numberParser).option("--user-agent <ua string>", "specify user agent string").option("--user-data-dir <path>", "path to the user data directory. If not specified, a temporary directory will be created.").option("--viewport-size <size>", 'specify browser viewport size in pixels, for example "1280x720"', import_config.resolutionParser.bind(null, "--viewport-size")).addOption(new import_utilsBundle.ProgramOption("--connect-tool", "Allow to switch between different browser connection methods.").hideHelp()).addOption(new import_utilsBundle.ProgramOption("--vision", "Legacy option, use --caps=vision instead").hideHelp()).action(async (options) => {
45
+ command.option("--allowed-hosts <hosts...>", "comma-separated list of hosts this server is allowed to serve from. Defaults to the host the server is bound to. Pass '*' to disable the host check.", import_config.commaSeparatedList).option("--allowed-origins <origins>", "semicolon-separated list of origins to allow the browser to request. Default is to allow all.", import_config.semicolonSeparatedList).option("--blocked-origins <origins>", "semicolon-separated list of origins to block the browser from requesting. Blocklist is evaluated before allowlist. If used without the allowlist, requests not matching the blocklist are still allowed.", import_config.semicolonSeparatedList).option("--block-service-workers", "block service workers").option("--browser <browser>", "browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge.").option("--caps <caps>", "comma-separated list of additional capabilities to enable, possible values: vision, pdf.", import_config.commaSeparatedList).option("--cdp-endpoint <endpoint>", "CDP endpoint to connect to.").option("--cdp-header <headers...>", "CDP headers to send with the connect request, multiple can be specified.", import_config.headerParser).option("--config <path>", "path to the configuration file.").option("--device <device>", 'device to emulate, for example: "iPhone 15"').option("--executable-path <path>", "path to the browser executable.").option("--extension", 'Connect to a running browser instance (Edge/Chrome only). Requires the "Playwright MCP Bridge" browser extension to be installed.').option("--grant-permissions <permissions...>", 'List of permissions to grant to the browser context, for example "geolocation", "clipboard-read", "clipboard-write".', import_config.commaSeparatedList).option("--headless", "run browser in headless mode, headed by default").option("--host <host>", "host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.").option("--ignore-https-errors", "ignore https errors").option("--init-page <path...>", "path to TypeScript file to evaluate on Playwright page object").option("--init-script <path...>", "path to JavaScript file to add as an initialization script. The script will be evaluated in every page before any of the page's scripts. Can be specified multiple times.").option("--isolated", "keep the browser profile in memory, do not save it to disk.").option("--image-responses <mode>", 'whether to send image responses to the client. Can be "allow" or "omit", Defaults to "allow".').option("--no-sandbox", "disable the sandbox for all process types that are normally sandboxed.").option("--output-dir <path>", "path to the directory for output files.").option("--port <port>", "port to listen on for SSE transport.").option("--proxy-bypass <bypass>", 'comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"').option("--proxy-server <proxy>", 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"').option("--save-session", "Whether to save the Playwright MCP session into the output directory.").option("--save-trace", "Whether to save the Playwright Trace of the session into the output directory.").option("--save-video <size>", 'Whether to save the video of the session into the output directory. For example "--save-video=800x600"', import_config.resolutionParser.bind(null, "--save-video")).option("--secrets <path>", "path to a file containing secrets in the dotenv format", import_config.dotenvFileLoader).option("--shared-browser-context", "reuse the same browser context between all connected HTTP clients.").option("--storage-state <path>", "path to the storage state file for isolated sessions.").option("--test-id-attribute <attribute>", 'specify the attribute to use for test ids, defaults to "data-testid"').option("--timeout-action <timeout>", "specify action timeout in milliseconds, defaults to 5000ms", import_config.numberParser).option("--timeout-navigation <timeout>", "specify navigation timeout in milliseconds, defaults to 60000ms", import_config.numberParser).option("--user-agent <ua string>", "specify user agent string").option("--user-data-dir <path>", "path to the user data directory. If not specified, a temporary directory will be created.").option("--viewport-size <size>", 'specify browser viewport size in pixels, for example "1280x720"', import_config.resolutionParser.bind(null, "--viewport-size")).addOption(new import_utilsBundle.ProgramOption("--connect-tool", "Allow to switch between different browser connection methods.").hideHelp()).addOption(new import_utilsBundle.ProgramOption("--vision", "Legacy option, use --caps=vision instead").hideHelp()).action(async (options) => {
46
46
  (0, import_watchdog.setupExitWatchdog)();
47
47
  if (options.vision) {
48
48
  console.error("The --vision option is deprecated, use --caps=vision instead");
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,12 +17,24 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
  var plannerTools_exports = {};
20
30
  __export(plannerTools_exports, {
21
- setupPage: () => setupPage
31
+ saveTestPlan: () => saveTestPlan,
32
+ setupPage: () => setupPage,
33
+ submitTestPlan: () => submitTestPlan
22
34
  });
23
35
  module.exports = __toCommonJS(plannerTools_exports);
36
+ var import_fs = __toESM(require("fs"));
37
+ var import_path = __toESM(require("path"));
24
38
  var import_bundle = require("../sdk/bundle");
25
39
  var import_testTool = require("./testTool");
26
40
  const setupPage = (0, import_testTool.defineTestTool)({
@@ -40,7 +54,91 @@ const setupPage = (0, import_testTool.defineTestTool)({
40
54
  return { content: [] };
41
55
  }
42
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."))
67
+ }))
68
+ }))
69
+ });
70
+ const submitTestPlan = (0, import_testTool.defineTestTool)({
71
+ schema: {
72
+ name: "planner_submit_plan",
73
+ title: "Submit test plan",
74
+ description: "Submit the test plan to the test planner",
75
+ inputSchema: planSchema,
76
+ type: "readOnly"
77
+ },
78
+ handle: async (context, params) => {
79
+ return {
80
+ content: [{
81
+ type: "text",
82
+ text: JSON.stringify(params, null, 2)
83
+ }]
84
+ };
85
+ }
86
+ });
87
+ const saveTestPlan = (0, import_testTool.defineTestTool)({
88
+ schema: {
89
+ name: "planner_save_plan",
90
+ title: "Save test plan as markdown file",
91
+ description: "Save the test plan as a markdown file",
92
+ 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
+ }),
96
+ type: "readOnly"
97
+ },
98
+ handle: async (context, params) => {
99
+ const lines = [];
100
+ lines.push(`# ${params.name}`);
101
+ lines.push(``);
102
+ lines.push(`## Application Overview`);
103
+ lines.push(``);
104
+ lines.push(params.overview);
105
+ lines.push(``);
106
+ lines.push(`## Test Scenarios`);
107
+ for (let i = 0; i < params.suites.length; i++) {
108
+ lines.push(``);
109
+ const suite = params.suites[i];
110
+ lines.push(`### ${i + 1}. ${suite.name}`);
111
+ lines.push(``);
112
+ lines.push(`**Seed:** \`${suite.seedFile}\``);
113
+ for (let j = 0; j < suite.tests.length; j++) {
114
+ lines.push(``);
115
+ const test = suite.tests[j];
116
+ lines.push(`#### ${i + 1}.${j + 1}. ${test.name}`);
117
+ lines.push(``);
118
+ lines.push(`**File:** \`${test.file}\``);
119
+ lines.push(``);
120
+ 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}`);
127
+ }
128
+ }
129
+ lines.push(``);
130
+ await import_fs.default.promises.writeFile(import_path.default.resolve(context.rootPath, params.fileName), lines.join("\n"));
131
+ return {
132
+ content: [{
133
+ type: "text",
134
+ text: `Test plan saved to ${params.fileName}`
135
+ }]
136
+ };
137
+ }
138
+ });
43
139
  // Annotate the CommonJS export names for ESM import in node:
44
140
  0 && (module.exports = {
45
- setupPage
141
+ saveTestPlan,
142
+ setupPage,
143
+ submitTestPlan
46
144
  });
@@ -44,7 +44,9 @@ class TestServerBackend {
44
44
  this.name = "Playwright";
45
45
  this.version = "0.0.1";
46
46
  this._tools = [
47
+ plannerTools.saveTestPlan,
47
48
  plannerTools.setupPage,
49
+ plannerTools.submitTestPlan,
48
50
  generatorTools.setupPage,
49
51
  generatorTools.generatorReadLog,
50
52
  generatorTools.generatorWriteTest,
@@ -34,6 +34,7 @@ __export(testContext_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(testContext_exports);
36
36
  var import_fs = __toESM(require("fs"));
37
+ var import_os = __toESM(require("os"));
37
38
  var import_path = __toESM(require("path"));
38
39
  var import_utils = require("playwright-core/lib/utils");
39
40
  var import_base = require("../../reporters/base");
@@ -74,6 +75,10 @@ class TestContext {
74
75
  constructor(pushClient, options) {
75
76
  this._pushClient = pushClient;
76
77
  this.options = options;
78
+ if (options?.headless !== void 0)
79
+ this.computedHeaded = !options.headless;
80
+ else
81
+ this.computedHeaded = !process.env.CI && !(import_os.default.platform() === "linux" && !process.env.DISPLAY);
77
82
  }
78
83
  initialize(rootPath, configLocation) {
79
84
  this.configLocation = configLocation;
@@ -144,7 +149,7 @@ class TestContext {
144
149
  async runSeedTest(seedFile, projectName, progress) {
145
150
  await this.runWithGlobalSetup(async (testRunner, reporter) => {
146
151
  const result = await testRunner.runTests(reporter, {
147
- headed: !this.options?.headless,
152
+ headed: this.computedHeaded,
148
153
  locations: ["/" + (0, import_utils.escapeRegExp)(seedFile) + "/"],
149
154
  projects: [projectName],
150
155
  timeout: 0,
@@ -91,7 +91,7 @@ const debugTest = (0, import_testTool.defineTestTool)({
91
91
  handle: async (context, params, progress) => {
92
92
  await context.runWithGlobalSetup(async (testRunner, reporter) => {
93
93
  await testRunner.runTests(reporter, {
94
- headed: !context.options?.headless,
94
+ headed: context.computedHeaded,
95
95
  testIds: [params.test.id],
96
96
  // For automatic recovery
97
97
  timeout: 0,
@@ -53,6 +53,7 @@ class TestTracing {
53
53
  type: "context-options",
54
54
  origin: "testRunner",
55
55
  browserName: "",
56
+ playwrightVersion: (0, import_utils.getPlaywrightVersion)(),
56
57
  options: {},
57
58
  platform: process.platform,
58
59
  wallTime: Date.now(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playwright",
3
- "version": "1.57.0-alpha-2025-11-10",
3
+ "version": "1.57.0-alpha-2025-11-12",
4
4
  "description": "A high-level API to automate web browsers",
5
5
  "repository": {
6
6
  "type": "git",
@@ -64,7 +64,7 @@
64
64
  },
65
65
  "license": "Apache-2.0",
66
66
  "dependencies": {
67
- "playwright-core": "1.57.0-alpha-2025-11-10"
67
+ "playwright-core": "1.57.0-alpha-2025-11-12"
68
68
  },
69
69
  "optionalDependencies": {
70
70
  "fsevents": "2.3.2"