playwright 1.56.0-alpha-2025-09-24 → 1.56.0-alpha-1758747822000

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.
@@ -107,7 +107,7 @@ function saveAsClaudeCode(agent) {
107
107
  }
108
108
  const lines = [];
109
109
  lines.push(`---`);
110
- lines.push(`name: ${agent.header.name}`);
110
+ lines.push(`name: playwright-test-${agent.header.name}`);
111
111
  lines.push(`description: ${agent.header.description}. Examples: ${agent.examples.map((example) => `<example>${example}</example>`).join("")}`);
112
112
  lines.push(`tools: ${agent.header.tools.map((tool) => asClaudeTool(tool)).join(", ")}`);
113
113
  lines.push(`model: ${agent.header.model}`);
@@ -143,10 +143,10 @@ function saveAsOpencodeJson(agents) {
143
143
  result["agent"] = {};
144
144
  for (const agent of agents) {
145
145
  const tools = {};
146
- result["agent"][agent.header.name] = {
146
+ result["agent"]["playwright-test-" + agent.header.name] = {
147
147
  description: agent.header.description,
148
148
  mode: "subagent",
149
- prompt: `{file:.opencode/prompts/${agent.header.name}.md}`,
149
+ prompt: `{file:.opencode/prompts/playwright-test-${agent.header.name}.md}`,
150
150
  tools
151
151
  };
152
152
  for (const tool of agent.header.tools)
@@ -172,7 +172,7 @@ async function initClaudeCodeRepo() {
172
172
  const agents = await loadAgents();
173
173
  await import_fs.default.promises.mkdir(".claude/agents", { recursive: true });
174
174
  for (const agent of agents)
175
- await writeFile(`.claude/agents/${agent.header.name}.md`, saveAsClaudeCode(agent));
175
+ await writeFile(`.claude/agents/playwright-test-${agent.header.name}.md`, saveAsClaudeCode(agent));
176
176
  await writeFile(".mcp.json", JSON.stringify({
177
177
  mcpServers: {
178
178
  "playwright-test": {
@@ -224,7 +224,7 @@ async function initVSCodeRepo() {
224
224
  const agents = await loadAgents();
225
225
  await import_fs.default.promises.mkdir(".github/chatmodes", { recursive: true });
226
226
  for (const agent of agents)
227
- await writeFile(`.github/chatmodes/${agent.header.name}.chatmode.md`, saveAsVSCodeChatmode(agent));
227
+ await writeFile(`.github/chatmodes/${agent.header.name === "planner" ? " " : ""}\u{1F3AD} ${agent.header.name}.chatmode.md`, saveAsVSCodeChatmode(agent));
228
228
  await import_fs.default.promises.mkdir(".vscode", { recursive: true });
229
229
  const mcpJsonPath = ".vscode/mcp.json";
230
230
  let mcpJson = {
@@ -252,7 +252,7 @@ async function initOpencodeRepo() {
252
252
  const prompt = [agent.instructions];
253
253
  prompt.push("");
254
254
  prompt.push(...agent.examples.map((example) => `<example>${example}</example>`));
255
- await writeFile(`.opencode/prompts/${agent.header.name}.md`, prompt.join("\n"));
255
+ await writeFile(`.opencode/prompts/playwright-test-${agent.header.name}.md`, prompt.join("\n"));
256
256
  }
257
257
  await writeFile("opencode.json", saveAsOpencodeJson(agents));
258
258
  }
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: playwright-test-generator
2
+ name: generator
3
3
  description: Use this agent when you need to create automated browser tests using Playwright
4
4
  model: sonnet
5
5
  color: blue
@@ -104,9 +104,9 @@ Your process is methodical and thorough:
104
104
  Context: User wants to test a login flow on their web application.
105
105
  user: 'I need a test that logs into my app at localhost:3000 with username admin@test.com and password 123456, then
106
106
  verifies the dashboard page loads'
107
- assistant: 'I'll use the playwright-test-generator agent to create and validate this login test for you'
107
+ assistant: 'I'll use the generator agent to create and validate this login test for you'
108
108
  <commentary>
109
- The user needs a specific browser automation test created, which is exactly what the playwright-test-generator agent
109
+ The user needs a specific browser automation test created, which is exactly what the generator agent
110
110
  is designed for.
111
111
  </commentary>
112
112
  </example>
@@ -114,9 +114,9 @@ Your process is methodical and thorough:
114
114
  Context: User has built a new checkout flow and wants to ensure it works correctly.
115
115
  user: 'Can you create a test that adds items to cart, proceeds to checkout, fills in payment details, and confirms the
116
116
  order?'
117
- assistant: 'I'll use the playwright-test-generator agent to build a comprehensive checkout flow test'
117
+ assistant: 'I'll use the generator agent to build a comprehensive checkout flow test'
118
118
  <commentary>
119
- This is a complex user journey that needs to be automated and tested, perfect for the playwright-test-generator
119
+ This is a complex user journey that needs to be automated and tested, perfect for the generator
120
120
  agent.
121
121
  </commentary>
122
122
  </example>
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: playwright-test-healer
2
+ name: healer
3
3
  description: Use this agent when you need to debug and fix failing Playwright tests
4
4
  color: red
5
5
  model: sonnet
@@ -58,17 +58,17 @@ Key principles:
58
58
  <example>
59
59
  Context: A developer has a failing Playwright test that needs to be debugged and fixed.
60
60
  user: 'The login test is failing, can you fix it?'
61
- assistant: 'I'll use the playwright-test-healer agent to debug and fix the failing login test.'
61
+ assistant: 'I'll use the healer agent to debug and fix the failing login test.'
62
62
  <commentary>
63
63
  The user has identified a specific failing test that needs debugging and fixing, which is exactly what the
64
- playwright-test-healer agent is designed for.
64
+ healer agent is designed for.
65
65
  </commentary>
66
66
  </example>
67
67
 
68
68
  <example>
69
69
  Context: After running a test suite, several tests are reported as failing.
70
70
  user: 'Test user-registration.spec.ts is broken after the recent changes'
71
- assistant: 'Let me use the playwright-test-healer agent to investigate and fix the user-registration test.'
71
+ assistant: 'Let me use the healer agent to investigate and fix the user-registration test.'
72
72
  <commentary>
73
73
  A specific test file is failing and needs debugging, which requires the systematic approach of the
74
74
  playwright-test-healer agent.
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: playwright-test-planner
2
+ name: planner
3
3
  description: Use this agent when you need to create comprehensive test plan for a web application or website
4
4
  model: sonnet
5
5
  color: green
@@ -117,19 +117,19 @@ professional formatting suitable for sharing with development and QA teams.
117
117
  <example>
118
118
  Context: User wants to test a new e-commerce checkout flow.
119
119
  user: 'I need test scenarios for our new checkout process at https://mystore.com/checkout'
120
- assistant: 'I'll use the playwright-test-planner agent to navigate to your checkout page and create comprehensive test
120
+ assistant: 'I'll use the planner agent to navigate to your checkout page and create comprehensive test
121
121
  scenarios.'
122
122
  <commentary>
123
- The user needs test planning for a specific web page, so use the playwright-test-planner agent to explore and create
123
+ The user needs test planning for a specific web page, so use the planner agent to explore and create
124
124
  test scenarios.
125
125
  </commentary>
126
126
  </example>
127
127
  <example>
128
128
  Context: User has deployed a new feature and wants thorough testing coverage.
129
129
  user: 'Can you help me test our new user dashboard at https://app.example.com/dashboard?'
130
- assistant: 'I'll launch the playwright-test-planner agent to explore your dashboard and develop detailed test
130
+ assistant: 'I'll launch the planner agent to explore your dashboard and develop detailed test
131
131
  scenarios.'
132
132
  <commentary>
133
- This requires web exploration and test scenario creation, perfect for the playwright-test-planner agent.
133
+ This requires web exploration and test scenario creation, perfect for the planner agent.
134
134
  </commentary>
135
135
  </example>
package/lib/program.js CHANGED
@@ -174,12 +174,12 @@ function addInitAgentsCommand(program3) {
174
174
  const command = program3.command("init-agents", { hidden: true });
175
175
  command.description("Initialize repository agents for the Claude Code");
176
176
  const option = command.createOption("--loop <loop>", "Agentic loop provider");
177
- option.choices(["claude", "opencode", "vscode"]);
177
+ option.choices(["code", "claude", "opencode"]);
178
178
  command.addOption(option);
179
179
  command.action(async (opts) => {
180
180
  if (opts.loop === "opencode")
181
181
  await (0, import_generateAgents.initOpencodeRepo)();
182
- else if (opts.loop === "vscode")
182
+ else if (opts.loop === "code")
183
183
  await (0, import_generateAgents.initVSCodeRepo)();
184
184
  else if (opts.loop === "claude")
185
185
  await (0, import_generateAgents.initClaudeCodeRepo)();
@@ -197,7 +197,8 @@ async function runTests(args, opts) {
197
197
  config.cliProjectFilter = opts.project || void 0;
198
198
  config.cliPassWithNoTests = !!opts.passWithNoTests;
199
199
  config.cliLastFailed = !!opts.lastFailed;
200
- config.cliLastRunFile = opts.lastRunFile ? import_path.default.resolve(process.cwd(), opts.lastRunFile) : void 0;
200
+ config.cliTestList = opts.testList ? import_path.default.resolve(process.cwd(), opts.testList) : void 0;
201
+ config.cliTestListInvert = opts.testListInvert ? import_path.default.resolve(process.cwd(), opts.testListInvert) : void 0;
201
202
  (0, import_projectUtils.filterProjects)(config.projects, config.cliProjectFilter);
202
203
  if (opts.ui || opts.uiHost || opts.uiPort) {
203
204
  if (opts.onlyChanged)
@@ -356,7 +357,6 @@ const testOptions = [
356
357
  ["--headed", { description: `Run tests in headed browsers (default: headless)` }],
357
358
  ["--ignore-snapshots", { description: `Ignore screenshot and snapshot expectations` }],
358
359
  ["--last-failed", { description: `Only re-run the failures` }],
359
- ["--last-run-file <file>", { description: `Path to the last-run file (default: "test-results/.last-run.json")` }],
360
360
  ["--list", { description: `Collect all the tests and report them, but do not run` }],
361
361
  ["--max-failures <N>", { description: `Stop after the first N failures` }],
362
362
  ["--no-deps", { description: `Do not run project dependencies` }],
@@ -369,6 +369,8 @@ const testOptions = [
369
369
  ["--reporter <reporter>", { description: `Reporter to use, comma-separated, can be ${import_config.builtInReporters.map((name) => `"${name}"`).join(", ")} (default: "${import_config.defaultReporter}")` }],
370
370
  ["--retries <retries>", { description: `Maximum retry count for flaky tests, zero for no retries (default: no retries)` }],
371
371
  ["--shard <shard>", { description: `Shard tests and execute only the selected shard, specify in the form "current/all", 1-based, for example "3/5"` }],
372
+ ["--test-list <file>", { description: `Path to a file containing a list of tests to run. See https://playwright.dev/docs/test-cli for more details.` }],
373
+ ["--test-list-invert <file>", { description: `Path to a file containing a list of tests to skip. See https://playwright.dev/docs/test-cli for more details.` }],
372
374
  ["--timeout <timeout>", { description: `Specify test timeout threshold in milliseconds, zero for unlimited (default: ${import_config.defaultTimeout})` }],
373
375
  ["--trace <mode>", { description: `Force tracing mode`, choices: kTraceModes }],
374
376
  ["--tsconfig <path>", { description: `Path to a single tsconfig applicable to all imported files (default: look up tsconfig for each imported file separately)` }],
@@ -37,27 +37,17 @@ var import_projectUtils = require("./projectUtils");
37
37
  class LastRunReporter {
38
38
  constructor(config) {
39
39
  this._config = config;
40
- if (config.cliLastRunFile) {
41
- this._lastRunFile = config.cliLastRunFile;
42
- } else {
43
- const [project] = (0, import_projectUtils.filterProjects)(config.projects, config.cliProjectFilter);
44
- if (project)
45
- this._lastRunFile = import_path.default.join(project.project.outputDir, ".last-run.json");
46
- }
40
+ const [project] = (0, import_projectUtils.filterProjects)(config.projects, config.cliProjectFilter);
41
+ if (project)
42
+ this._lastRunFile = import_path.default.join(project.project.outputDir, ".last-run.json");
47
43
  }
48
- async applyFilter() {
44
+ async filterLastFailed() {
49
45
  if (!this._lastRunFile)
50
46
  return;
51
47
  try {
52
48
  const lastRunInfo = JSON.parse(await import_fs.default.promises.readFile(this._lastRunFile, "utf8"));
53
- if (lastRunInfo.filterTests) {
54
- const filterTestIds = new Set(lastRunInfo.filterTests);
55
- this._config.preOnlyTestFilters.push((test) => filterTestIds.has(test.id));
56
- }
57
- if (this._config.cliLastFailed) {
58
- const failedTestIds = new Set(lastRunInfo.failedTests ?? []);
59
- this._config.postShardTestFilters.push((test) => failedTestIds.has(test.id));
60
- }
49
+ const failedTestIds = new Set(lastRunInfo.failedTests);
50
+ this._config.postShardTestFilters.push((test) => failedTestIds.has(test.id));
61
51
  } catch {
62
52
  }
63
53
  }
@@ -32,10 +32,13 @@ __export(loadUtils_exports, {
32
32
  createRootSuite: () => createRootSuite,
33
33
  loadFileSuites: () => loadFileSuites,
34
34
  loadGlobalHook: () => loadGlobalHook,
35
- loadReporter: () => loadReporter
35
+ loadReporter: () => loadReporter,
36
+ loadTestList: () => loadTestList
36
37
  });
37
38
  module.exports = __toCommonJS(loadUtils_exports);
38
39
  var import_path = __toESM(require("path"));
40
+ var import_fs = __toESM(require("fs"));
41
+ var import_utils = require("playwright-core/lib/utils");
39
42
  var import_loaderHost = require("./loaderHost");
40
43
  var import_util = require("../util");
41
44
  var import_projectUtils = require("./projectUtils");
@@ -290,11 +293,41 @@ function sourceMapSources(file, cache) {
290
293
  return sources;
291
294
  }
292
295
  }
296
+ async function loadTestList(config, filePath) {
297
+ try {
298
+ const content = await import_fs.default.promises.readFile(filePath, "utf-8");
299
+ const lines = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
300
+ const descriptions = lines.map((line) => {
301
+ const delimiter = line.includes("\u203A") ? "\u203A" : ">";
302
+ const tokens = line.split(delimiter).map((token) => token.trim());
303
+ let project;
304
+ if (tokens[0].startsWith("[")) {
305
+ if (!tokens[0].endsWith("]"))
306
+ throw new Error(`Malformed test description: ${line}`);
307
+ project = tokens[0].substring(1, tokens[0].length - 1);
308
+ tokens.shift();
309
+ }
310
+ return { project, file: (0, import_utils.toPosixPath)((0, import_util.parseLocationArg)(tokens[0]).file), titlePath: tokens.slice(1) };
311
+ });
312
+ return (test) => descriptions.some((d) => {
313
+ const [projectName, , ...titles] = test.titlePath();
314
+ if (d.project !== void 0 && d.project !== projectName)
315
+ return false;
316
+ const relativeFile = (0, import_utils.toPosixPath)(import_path.default.relative(config.config.rootDir, test.location.file));
317
+ if (relativeFile !== d.file)
318
+ return false;
319
+ return d.titlePath.length === titles.length && d.titlePath.every((_, index) => titles[index] === d.titlePath[index]);
320
+ });
321
+ } catch (e) {
322
+ throw (0, import_util.errorWithFile)(filePath, "Cannot read test list file: " + e.message);
323
+ }
324
+ }
293
325
  // Annotate the CommonJS export names for ESM import in node:
294
326
  0 && (module.exports = {
295
327
  collectProjectsAndTestFiles,
296
328
  createRootSuite,
297
329
  loadFileSuites,
298
330
  loadGlobalHook,
299
- loadReporter
331
+ loadReporter,
332
+ loadTestList
300
333
  });
@@ -249,10 +249,18 @@ function createLoadTask(mode, options) {
249
249
  const changedFiles = await (0, import_vcs.detectChangedTestFiles)(testRun.config.cliOnlyChanged, testRun.config.configDir);
250
250
  testRun.config.preOnlyTestFilters.push((test) => changedFiles.has(test.location.file));
251
251
  }
252
+ if (testRun.config.cliTestList) {
253
+ const testListFilter = await (0, import_loadUtils.loadTestList)(testRun.config, testRun.config.cliTestList);
254
+ testRun.config.preOnlyTestFilters.push(testListFilter);
255
+ }
256
+ if (testRun.config.cliTestListInvert) {
257
+ const testListInvertFilter = await (0, import_loadUtils.loadTestList)(testRun.config, testRun.config.cliTestListInvert);
258
+ testRun.config.preOnlyTestFilters.push((test) => !testListInvertFilter(test));
259
+ }
252
260
  const { rootSuite, topLevelProjects } = await (0, import_loadUtils.createRootSuite)(testRun, options.failOnLoadErrors ? errors : softErrors, !!options.filterOnly);
253
261
  testRun.rootSuite = rootSuite;
254
262
  testRun.failureTracker.onRootSuite(rootSuite, topLevelProjects);
255
- if (options.failOnLoadErrors && !testRun.rootSuite.allTests().length && !testRun.config.cliPassWithNoTests && !testRun.config.config.shard && !testRun.config.cliOnlyChanged) {
263
+ if (options.failOnLoadErrors && !testRun.rootSuite.allTests().length && !testRun.config.cliPassWithNoTests && !testRun.config.config.shard && !testRun.config.cliOnlyChanged && !testRun.config.cliTestList && !testRun.config.cliTestListInvert) {
256
264
  if (testRun.config.cliArgs.length) {
257
265
  throw new Error([
258
266
  `No tests found.`,
@@ -365,7 +365,8 @@ async function runAllTestsWithConfig(config) {
365
365
  (0, import_webServerPlugin.webServerPluginsForConfig)(config).forEach((p) => config.plugins.push({ factory: p }));
366
366
  const reporters = await (0, import_reporters.createReporters)(config, listOnly ? "list" : "test", false);
367
367
  const lastRun = new import_lastRun.LastRunReporter(config);
368
- await lastRun.applyFilter();
368
+ if (config.cliLastFailed)
369
+ await lastRun.filterLastFailed();
369
370
  const reporter = new import_internalReporter.InternalReporter([...reporters, lastRun]);
370
371
  const tasks = listOnly ? [
371
372
  (0, import_tasks.createLoadTask)("in-process", { failOnLoadErrors: true, filterOnly: false }),
package/lib/util.js CHANGED
@@ -48,6 +48,7 @@ __export(util_exports, {
48
48
  getPackageJsonPath: () => getPackageJsonPath,
49
49
  mergeObjects: () => mergeObjects,
50
50
  normalizeAndSaveAttachment: () => normalizeAndSaveAttachment,
51
+ parseLocationArg: () => parseLocationArg,
51
52
  relativeFilePath: () => relativeFilePath,
52
53
  removeDirAndLogToConsole: () => removeDirAndLogToConsole,
53
54
  resolveImportSpecifierAfterMapping: () => resolveImportSpecifierAfterMapping,
@@ -105,14 +106,18 @@ function serializeError(error) {
105
106
  value: import_util.default.inspect(error)
106
107
  };
107
108
  }
109
+ function parseLocationArg(arg) {
110
+ const match = /^(.*?):(\d+):?(\d+)?$/.exec(arg);
111
+ return {
112
+ file: match ? match[1] : arg,
113
+ line: match ? parseInt(match[2], 10) : null,
114
+ column: match?.[3] ? parseInt(match[3], 10) : null
115
+ };
116
+ }
108
117
  function createFileFiltersFromArguments(args) {
109
118
  return args.map((arg) => {
110
- const match = /^(.*?):(\d+):?(\d+)?$/.exec(arg);
111
- return {
112
- re: forceRegExp(match ? match[1] : arg),
113
- line: match ? parseInt(match[2], 10) : null,
114
- column: match?.[3] ? parseInt(match[3], 10) : null
115
- };
119
+ const parsed = parseLocationArg(arg);
120
+ return { re: forceRegExp(parsed.file), line: parsed.line, column: parsed.column };
116
121
  });
117
122
  }
118
123
  function createFileMatcherFromArguments(args) {
@@ -385,6 +390,7 @@ function stripAnsiEscapes(str) {
385
390
  getPackageJsonPath,
386
391
  mergeObjects,
387
392
  normalizeAndSaveAttachment,
393
+ parseLocationArg,
388
394
  relativeFilePath,
389
395
  removeDirAndLogToConsole,
390
396
  resolveImportSpecifierAfterMapping,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playwright",
3
- "version": "1.56.0-alpha-2025-09-24",
3
+ "version": "1.56.0-alpha-1758747822000",
4
4
  "description": "A high-level API to automate web browsers",
5
5
  "repository": {
6
6
  "type": "git",
@@ -64,7 +64,7 @@
64
64
  },
65
65
  "license": "Apache-2.0",
66
66
  "dependencies": {
67
- "playwright-core": "1.56.0-alpha-2025-09-24"
67
+ "playwright-core": "1.56.0-alpha-1758747822000"
68
68
  },
69
69
  "optionalDependencies": {
70
70
  "fsevents": "2.3.2"