playwright 1.57.0-alpha-2025-10-27 → 1.57.0-alpha-2025-10-28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/agents/generateAgents.js +72 -70
- package/lib/agents/{test-coverage.prompt.md → pwt-coverage.prompt.md} +4 -4
- package/lib/agents/{test-generate.prompt.md → pwt-generate.prompt.md} +1 -1
- package/lib/agents/{generator.agent.md → pwt-generator.agent.md} +7 -20
- package/lib/agents/{test-heal.prompt.md → pwt-heal.prompt.md} +1 -1
- package/lib/agents/{healer.agent.md → pwt-healer.agent.md} +1 -23
- package/lib/agents/{test-plan.prompt.md → pwt-plan.prompt.md} +1 -1
- package/lib/agents/{planner.agent.md → pwt-planner.agent.md} +2 -23
- package/lib/mcp/program.js +20 -0
- package/lib/program.js +6 -7
- package/lib/runner/testRunner.js +1 -1
- package/package.json +2 -2
|
@@ -28,8 +28,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
var generateAgents_exports = {};
|
|
30
30
|
__export(generateAgents_exports, {
|
|
31
|
-
AgentGenerator: () => AgentGenerator,
|
|
32
31
|
ClaudeGenerator: () => ClaudeGenerator,
|
|
32
|
+
CopilotGenerator: () => CopilotGenerator,
|
|
33
33
|
OpencodeGenerator: () => OpencodeGenerator,
|
|
34
34
|
VSCodeGenerator: () => VSCodeGenerator
|
|
35
35
|
});
|
|
@@ -48,7 +48,7 @@ class AgentParser {
|
|
|
48
48
|
const source = await import_fs.default.promises.readFile(filePath, "utf-8");
|
|
49
49
|
const { header, content } = this.extractYamlAndContent(source);
|
|
50
50
|
const { instructions, examples } = this.extractInstructionsAndExamples(content);
|
|
51
|
-
return {
|
|
51
|
+
return { header, instructions, examples };
|
|
52
52
|
}
|
|
53
53
|
static extractYamlAndContent(markdown) {
|
|
54
54
|
const lines = markdown.split("\n");
|
|
@@ -92,14 +92,17 @@ class AgentParser {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
class ClaudeGenerator {
|
|
95
|
-
static async init(config, projectName) {
|
|
95
|
+
static async init(config, projectName, prompts) {
|
|
96
96
|
await initRepo(config, projectName, {
|
|
97
|
-
promptsFolder: ".claude/prompts"
|
|
97
|
+
promptsFolder: prompts ? ".claude/prompts" : void 0
|
|
98
98
|
});
|
|
99
99
|
const agents = await AgentParser.loadAgents();
|
|
100
100
|
await import_fs.default.promises.mkdir(".claude/agents", { recursive: true });
|
|
101
101
|
for (const agent of agents)
|
|
102
|
-
await writeFile(`.claude/agents/${agent.header.name}.
|
|
102
|
+
await writeFile(`.claude/agents/${agent.header.name}.md`, ClaudeGenerator.agentSpec(agent), "\u{1F916}", "agent definition");
|
|
103
|
+
await deleteFile(`.claude/agents/playwright-test-planner.md`, "legacy planner agent");
|
|
104
|
+
await deleteFile(`.claude/agents/playwright-test-generator.md`, "legacy generator agent");
|
|
105
|
+
await deleteFile(`.claude/agents/playwright-test-healer.md`, "legacy healer agent");
|
|
103
106
|
await writeFile(".mcp.json", JSON.stringify({
|
|
104
107
|
mcpServers: {
|
|
105
108
|
"playwright-test": {
|
|
@@ -112,10 +115,8 @@ class ClaudeGenerator {
|
|
|
112
115
|
}
|
|
113
116
|
static agentSpec(agent) {
|
|
114
117
|
const claudeToolMap = /* @__PURE__ */ new Map([
|
|
115
|
-
["search", ["Glob", "Grep"]],
|
|
116
|
-
["
|
|
117
|
-
["edit", ["Edit", "MultiEdit"]],
|
|
118
|
-
["write", ["Write"]]
|
|
118
|
+
["search", ["Glob", "Grep", "Read"]],
|
|
119
|
+
["edit", ["Edit", "MultiEdit", "Write"]]
|
|
119
120
|
]);
|
|
120
121
|
function asClaudeTool(tool) {
|
|
121
122
|
const [first, second] = tool.split("/");
|
|
@@ -123,10 +124,11 @@ class ClaudeGenerator {
|
|
|
123
124
|
return (claudeToolMap.get(first) || [first]).join(", ");
|
|
124
125
|
return `mcp__${first}__${second}`;
|
|
125
126
|
}
|
|
127
|
+
const examples = agent.examples.length ? ` Examples: ${agent.examples.map((example) => `<example>${example}</example>`).join("")}` : "";
|
|
126
128
|
const lines = [];
|
|
127
129
|
lines.push(`---`);
|
|
128
130
|
lines.push(`name: ${agent.header.name}`);
|
|
129
|
-
lines.push(`description: ${agent.header.description}
|
|
131
|
+
lines.push(`description: ${agent.header.description}.${examples}`);
|
|
130
132
|
lines.push(`tools: ${agent.header.tools.map((tool) => asClaudeTool(tool)).join(", ")}`);
|
|
131
133
|
lines.push(`model: ${agent.header.model}`);
|
|
132
134
|
lines.push(`color: ${agent.header.color}`);
|
|
@@ -137,27 +139,28 @@ class ClaudeGenerator {
|
|
|
137
139
|
}
|
|
138
140
|
}
|
|
139
141
|
class OpencodeGenerator {
|
|
140
|
-
static async init(config, projectName) {
|
|
142
|
+
static async init(config, projectName, prompts) {
|
|
141
143
|
await initRepo(config, projectName, {
|
|
142
|
-
|
|
143
|
-
promptsFolder: ".opencode/prompts"
|
|
144
|
+
defaultAgentName: "Build",
|
|
145
|
+
promptsFolder: prompts ? ".opencode/prompts" : void 0
|
|
144
146
|
});
|
|
145
147
|
const agents = await AgentParser.loadAgents();
|
|
146
148
|
for (const agent of agents) {
|
|
147
149
|
const prompt = [agent.instructions];
|
|
148
150
|
prompt.push("");
|
|
149
151
|
prompt.push(...agent.examples.map((example) => `<example>${example}</example>`));
|
|
150
|
-
await writeFile(`.opencode/prompts/${agent.header.name}.
|
|
152
|
+
await writeFile(`.opencode/prompts/${agent.header.name}.md`, prompt.join("\n"), "\u{1F916}", "agent definition");
|
|
151
153
|
}
|
|
154
|
+
await deleteFile(`.opencode/prompts/playwright-test-planner.md`, "legacy planner agent");
|
|
155
|
+
await deleteFile(`.opencode/prompts/playwright-test-generator.md`, "legacy generator agent");
|
|
156
|
+
await deleteFile(`.opencode/prompts/playwright-test-healer.md`, "legacy healer agent");
|
|
152
157
|
await writeFile("opencode.json", OpencodeGenerator.configuration(agents), "\u{1F527}", "opencode configuration");
|
|
153
158
|
initRepoDone();
|
|
154
159
|
}
|
|
155
160
|
static configuration(agents) {
|
|
156
161
|
const opencodeToolMap = /* @__PURE__ */ new Map([
|
|
157
|
-
["search", ["ls", "glob", "grep"]],
|
|
158
|
-
["
|
|
159
|
-
["edit", ["edit"]],
|
|
160
|
-
["write", ["write"]]
|
|
162
|
+
["search", ["ls", "glob", "grep", "read"]],
|
|
163
|
+
["edit", ["edit", "write"]]
|
|
161
164
|
]);
|
|
162
165
|
const asOpencodeTool = (tools, tool) => {
|
|
163
166
|
const [first, second] = tool.split("/");
|
|
@@ -180,7 +183,7 @@ class OpencodeGenerator {
|
|
|
180
183
|
result["agent"][agent.header.name] = {
|
|
181
184
|
description: agent.header.description,
|
|
182
185
|
mode: "subagent",
|
|
183
|
-
prompt: `{file:.opencode/prompts/${agent.header.name}.
|
|
186
|
+
prompt: `{file:.opencode/prompts/${agent.header.name}.md}`,
|
|
184
187
|
tools
|
|
185
188
|
};
|
|
186
189
|
for (const tool of agent.header.tools)
|
|
@@ -194,59 +197,57 @@ class OpencodeGenerator {
|
|
|
194
197
|
return JSON.stringify(result, null, 2);
|
|
195
198
|
}
|
|
196
199
|
}
|
|
197
|
-
class
|
|
198
|
-
static async init(config, projectName) {
|
|
199
|
-
const agentsFolder = process.env.AGENTS_FOLDER;
|
|
200
|
-
if (!agentsFolder) {
|
|
201
|
-
console.error("AGENTS_FOLDER environment variable is not set");
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
200
|
+
class CopilotGenerator {
|
|
201
|
+
static async init(config, projectName, prompts) {
|
|
204
202
|
await initRepo(config, projectName, {
|
|
205
|
-
|
|
203
|
+
defaultAgentName: "agent",
|
|
204
|
+
promptsFolder: prompts ? ".github/prompts" : void 0,
|
|
205
|
+
promptSuffix: "prompt"
|
|
206
206
|
});
|
|
207
207
|
const agents = await AgentParser.loadAgents();
|
|
208
|
-
await import_fs.default.promises.mkdir(
|
|
208
|
+
await import_fs.default.promises.mkdir(".github/agents", { recursive: true });
|
|
209
209
|
for (const agent of agents)
|
|
210
|
-
await writeFile(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
type: "stdio",
|
|
216
|
-
command: "npx",
|
|
217
|
-
args: [
|
|
218
|
-
`--prefix=${import_path.default.resolve(process.cwd())}`,
|
|
219
|
-
"playwright",
|
|
220
|
-
"run-test-mcp-server",
|
|
221
|
-
`--headless`,
|
|
222
|
-
`--config=${import_path.default.resolve(process.cwd())}`
|
|
223
|
-
],
|
|
224
|
-
tools: ["*"]
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}, null, 2));
|
|
210
|
+
await writeFile(`.github/agents/${agent.header.name}.agent.md`, CopilotGenerator.agentSpec(agent), "\u{1F916}", "agent definition");
|
|
211
|
+
await deleteFile(`.github/chatmodes/ \u{1F3AD} planner.chatmode.md`, "legacy planner chatmode");
|
|
212
|
+
await deleteFile(`.github/chatmodes/\u{1F3AD} generator.chatmode.md`, "legacy generator chatmode");
|
|
213
|
+
await deleteFile(`.github/chatmodes/\u{1F3AD} healer.chatmode.md`, "legacy healer chatmode");
|
|
214
|
+
await VSCodeGenerator.appendToMCPJson();
|
|
228
215
|
initRepoDone();
|
|
229
216
|
}
|
|
217
|
+
static agentSpec(agent) {
|
|
218
|
+
const examples = agent.examples.length ? ` Examples: ${agent.examples.map((example) => `<example>${example}</example>`).join("")}` : "";
|
|
219
|
+
const lines = [];
|
|
220
|
+
lines.push(`---`);
|
|
221
|
+
lines.push(`name: ${agent.header.name}`);
|
|
222
|
+
lines.push(`description: ${agent.header.description}.${examples}`);
|
|
223
|
+
lines.push(`tools:
|
|
224
|
+
${agent.header.tools.map((tool) => ` - ${tool}`).join("\n")}`);
|
|
225
|
+
lines.push(`model: Claude Sonnet 4`);
|
|
226
|
+
lines.push(`---`);
|
|
227
|
+
lines.push("");
|
|
228
|
+
lines.push(agent.instructions);
|
|
229
|
+
lines.push("");
|
|
230
|
+
return lines.join("\n");
|
|
231
|
+
}
|
|
230
232
|
}
|
|
231
233
|
class VSCodeGenerator {
|
|
232
234
|
static async init(config, projectName) {
|
|
233
235
|
await initRepo(config, projectName, {
|
|
234
|
-
|
|
235
|
-
agentHealer: "\u{1F3AD} healer",
|
|
236
|
-
agentGenerator: "\u{1F3AD} generator",
|
|
237
|
-
agentPlanner: "\u{1F3AD} planner",
|
|
238
|
-
promptsFolder: ".github/prompts"
|
|
236
|
+
promptsFolder: void 0
|
|
239
237
|
});
|
|
240
238
|
const agents = await AgentParser.loadAgents();
|
|
241
239
|
const nameMap = /* @__PURE__ */ new Map([
|
|
242
|
-
["playwright-test-planner", "\u{1F3AD} planner"],
|
|
240
|
+
["playwright-test-planner", " \u{1F3AD} planner"],
|
|
243
241
|
["playwright-test-generator", "\u{1F3AD} generator"],
|
|
244
242
|
["playwright-test-healer", "\u{1F3AD} healer"]
|
|
245
243
|
]);
|
|
246
|
-
await deleteFile(`.github/chatmodes/ \u{1F3AD} planner.chatmode.md`, "old planner chatmode");
|
|
247
244
|
await import_fs.default.promises.mkdir(".github/chatmodes", { recursive: true });
|
|
248
245
|
for (const agent of agents)
|
|
249
246
|
await writeFile(`.github/chatmodes/${nameMap.get(agent.header.name)}.chatmode.md`, VSCodeGenerator.agentSpec(agent), "\u{1F916}", "chatmode definition");
|
|
247
|
+
await VSCodeGenerator.appendToMCPJson();
|
|
248
|
+
initRepoDone();
|
|
249
|
+
}
|
|
250
|
+
static async appendToMCPJson() {
|
|
250
251
|
await import_fs.default.promises.mkdir(".vscode", { recursive: true });
|
|
251
252
|
const mcpJsonPath = ".vscode/mcp.json";
|
|
252
253
|
let mcpJson = {
|
|
@@ -265,7 +266,6 @@ class VSCodeGenerator {
|
|
|
265
266
|
args: ["playwright", "run-test-mcp-server"]
|
|
266
267
|
};
|
|
267
268
|
await writeFile(mcpJsonPath, JSON.stringify(mcpJson, null, 2), "\u{1F527}", "mcp configuration");
|
|
268
|
-
initRepoDone();
|
|
269
269
|
}
|
|
270
270
|
static agentSpec(agent) {
|
|
271
271
|
const vscodeToolMap = /* @__PURE__ */ new Map([
|
|
@@ -302,6 +302,7 @@ class VSCodeGenerator {
|
|
|
302
302
|
lines.push(agent.instructions);
|
|
303
303
|
for (const example of agent.examples)
|
|
304
304
|
lines.push(`<example>${example}</example>`);
|
|
305
|
+
lines.push("");
|
|
305
306
|
return lines.join("\n");
|
|
306
307
|
}
|
|
307
308
|
}
|
|
@@ -322,7 +323,7 @@ async function deleteFile(filePath, description) {
|
|
|
322
323
|
}
|
|
323
324
|
async function initRepo(config, projectName, options) {
|
|
324
325
|
const project = (0, import_seed.seedProject)(config, projectName);
|
|
325
|
-
console.log(
|
|
326
|
+
console.log(`- \u{1F3AD} Using project "${project.project.name}" as a primary project`);
|
|
326
327
|
if (!import_fs.default.existsSync("specs")) {
|
|
327
328
|
await import_fs.default.promises.mkdir("specs");
|
|
328
329
|
await writeFile(import_path.default.join("specs", "README.md"), `# Specs
|
|
@@ -335,34 +336,35 @@ This is a directory for test plans.
|
|
|
335
336
|
seedFile = (0, import_seed.defaultSeedFile)(project);
|
|
336
337
|
await writeFile(seedFile, import_seed.seedFileContent, "\u{1F331}", "default environment seed file");
|
|
337
338
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
339
|
+
if (options.promptsFolder) {
|
|
340
|
+
await import_fs.default.promises.mkdir(options.promptsFolder, { recursive: true });
|
|
341
|
+
for (const promptFile of await import_fs.default.promises.readdir(__dirname)) {
|
|
342
|
+
if (!promptFile.endsWith(".prompt.md"))
|
|
343
|
+
continue;
|
|
344
|
+
const shortName = promptFile.replace(".prompt.md", "");
|
|
345
|
+
const fileName = options.promptSuffix ? `${shortName}.${options.promptSuffix}.md` : `${shortName}.md`;
|
|
346
|
+
const content = await loadPrompt(promptFile, {
|
|
347
|
+
defaultAgentName: "default",
|
|
348
|
+
...options,
|
|
349
|
+
seedFile: import_path.default.relative(process.cwd(), seedFile)
|
|
350
|
+
});
|
|
351
|
+
await writeFile(import_path.default.join(options.promptsFolder, fileName), content, "\u{1F4DD}", "prompt template");
|
|
352
|
+
}
|
|
344
353
|
}
|
|
345
354
|
}
|
|
346
355
|
function initRepoDone() {
|
|
347
356
|
console.log("\u2705 Done.");
|
|
348
357
|
}
|
|
349
358
|
async function loadPrompt(file, params) {
|
|
350
|
-
const templateParams = {
|
|
351
|
-
agentDefault: params.agentDefault ?? "default",
|
|
352
|
-
agentHealer: params.agentHealer ?? "playwright-test-healer",
|
|
353
|
-
agentGenerator: params.agentGenerator ?? "playwright-test-generator",
|
|
354
|
-
agentPlanner: params.agentPlanner ?? "playwright-test-planner",
|
|
355
|
-
seedFile: params.seedFile
|
|
356
|
-
};
|
|
357
359
|
const content = await import_fs.default.promises.readFile(import_path.default.join(__dirname, file), "utf-8");
|
|
358
|
-
return Object.entries(
|
|
360
|
+
return Object.entries(params).reduce((acc, [key, value]) => {
|
|
359
361
|
return acc.replace(new RegExp(`\\\${${key}}`, "g"), value);
|
|
360
362
|
}, content);
|
|
361
363
|
}
|
|
362
364
|
// Annotate the CommonJS export names for ESM import in node:
|
|
363
365
|
0 && (module.exports = {
|
|
364
|
-
AgentGenerator,
|
|
365
366
|
ClaudeGenerator,
|
|
367
|
+
CopilotGenerator,
|
|
366
368
|
OpencodeGenerator,
|
|
367
369
|
VSCodeGenerator
|
|
368
370
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
2
|
+
agent: ${defaultAgentName}
|
|
3
3
|
description: Produce test coverage
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -8,7 +8,7 @@ Parameters:
|
|
|
8
8
|
- Seed file (optional): the seed file to use, defaults to `${seedFile}`
|
|
9
9
|
- Test plan file (optional): the test plan file to write, under `specs/` folder.
|
|
10
10
|
|
|
11
|
-
1. Call
|
|
11
|
+
1. Call #pwt-planner subagent with prompt:
|
|
12
12
|
|
|
13
13
|
<plan>
|
|
14
14
|
<task-text><!-- the task --></task-text>
|
|
@@ -16,7 +16,7 @@ Parameters:
|
|
|
16
16
|
<plan-file><!-- path to test plan file to generate --></plan-file>
|
|
17
17
|
</plan>
|
|
18
18
|
|
|
19
|
-
2. For each test case from the test plan file (1.1, 1.2, ...), one after another, not in parallel, call
|
|
19
|
+
2. For each test case from the test plan file (1.1, 1.2, ...), one after another, not in parallel, call #pwt-generator subagent with prompt:
|
|
20
20
|
|
|
21
21
|
<generate>
|
|
22
22
|
<test-suite><!-- Verbatim name of the test spec group w/o ordinal like "Multiplication tests" --></test-suite>
|
|
@@ -26,6 +26,6 @@ Parameters:
|
|
|
26
26
|
<body><!-- Test case content including steps and expectations --></body>
|
|
27
27
|
</generate>
|
|
28
28
|
|
|
29
|
-
3. Call
|
|
29
|
+
3. Call #pwt-healer subagent with prompt:
|
|
30
30
|
|
|
31
31
|
<heal>Run all tests and fix the failing ones one after another.</heal>
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: pwt-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
|
|
6
6
|
tools:
|
|
7
|
-
- read
|
|
8
7
|
- search
|
|
9
8
|
- playwright-test/browser_click
|
|
10
9
|
- playwright-test/browser_drag
|
|
@@ -80,22 +79,10 @@ application behavior.
|
|
|
80
79
|
</example-generation>
|
|
81
80
|
|
|
82
81
|
<example>
|
|
83
|
-
Context: User wants to
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
is designed for.
|
|
90
|
-
</commentary>
|
|
91
|
-
</example>
|
|
92
|
-
<example>
|
|
93
|
-
Context: User has built a new checkout flow and wants to ensure it works correctly.
|
|
94
|
-
user: 'Can you create a test that adds items to cart, proceeds to checkout, fills in payment details, and confirms the
|
|
95
|
-
order?'
|
|
96
|
-
assistant: 'I'll use the generator agent to build a comprehensive checkout flow test'
|
|
97
|
-
<commentary>
|
|
98
|
-
This is a complex user journey that needs to be automated and tested, perfect for the generator
|
|
99
|
-
agent.
|
|
100
|
-
</commentary>
|
|
82
|
+
Context: User wants to generate a test for the test plan item.
|
|
83
|
+
<test-suite><!-- Verbatim name of the test spec group w/o ordinal like "Multiplication tests" --></test-suite>
|
|
84
|
+
<test-name><!-- Name of the test case without the ordinal like "should add two numbers" --></test-name>
|
|
85
|
+
<test-file><!-- Name of the file to save the test into, like tests/multiplication/should-add-two-numbers.spec.ts --></test-file>
|
|
86
|
+
<seed-file><!-- Seed file path from test plan --></seed-file>
|
|
87
|
+
<body><!-- Test case content including steps and expectations --></body>
|
|
101
88
|
</example>
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: pwt-healer
|
|
3
3
|
description: Use this agent when you need to debug and fix failing Playwright tests
|
|
4
4
|
model: sonnet
|
|
5
5
|
color: red
|
|
6
6
|
tools:
|
|
7
|
-
- read
|
|
8
7
|
- search
|
|
9
|
-
- write
|
|
10
8
|
- edit
|
|
11
9
|
- playwright-test/browser_console_messages
|
|
12
10
|
- playwright-test/browser_evaluate
|
|
@@ -55,23 +53,3 @@ Key principles:
|
|
|
55
53
|
of the expected behavior.
|
|
56
54
|
- Do not ask user questions, you are not interactive tool, do the most reasonable thing possible to pass the test.
|
|
57
55
|
- Never wait for networkidle or use other discouraged or deprecated apis
|
|
58
|
-
|
|
59
|
-
<example>
|
|
60
|
-
Context: A developer has a failing Playwright test that needs to be debugged and fixed.
|
|
61
|
-
user: 'The login test is failing, can you fix it?'
|
|
62
|
-
assistant: 'I'll use the healer agent to debug and fix the failing login test.'
|
|
63
|
-
<commentary>
|
|
64
|
-
The user has identified a specific failing test that needs debugging and fixing, which is exactly what the
|
|
65
|
-
healer agent is designed for.
|
|
66
|
-
</commentary>
|
|
67
|
-
</example>
|
|
68
|
-
|
|
69
|
-
<example>
|
|
70
|
-
Context: After running a test suite, several tests are reported as failing.
|
|
71
|
-
user: 'Test user-registration.spec.ts is broken after the recent changes'
|
|
72
|
-
assistant: 'Let me use the healer agent to investigate and fix the user-registration test.'
|
|
73
|
-
<commentary>
|
|
74
|
-
A specific test file is failing and needs debugging, which requires the systematic approach of the
|
|
75
|
-
playwright-test-healer agent.
|
|
76
|
-
</commentary>
|
|
77
|
-
</example>
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: pwt-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
|
|
6
6
|
tools:
|
|
7
|
-
- read
|
|
8
7
|
- search
|
|
9
|
-
-
|
|
8
|
+
- edit
|
|
10
9
|
- playwright-test/browser_click
|
|
11
10
|
- playwright-test/browser_close
|
|
12
11
|
- playwright-test/browser_console_messages
|
|
@@ -116,23 +115,3 @@ application features:
|
|
|
116
115
|
|
|
117
116
|
**Output Format**: Always save the complete test plan as a markdown file with clear headings, numbered steps, and
|
|
118
117
|
professional formatting suitable for sharing with development and QA teams.
|
|
119
|
-
|
|
120
|
-
<example>
|
|
121
|
-
Context: User wants to test a new e-commerce checkout flow.
|
|
122
|
-
user: 'I need test scenarios for our new checkout process at https://mystore.com/checkout'
|
|
123
|
-
assistant: 'I'll use the planner agent to navigate to your checkout page and create comprehensive test
|
|
124
|
-
scenarios.'
|
|
125
|
-
<commentary>
|
|
126
|
-
The user needs test planning for a specific web page, so use the planner agent to explore and create
|
|
127
|
-
test scenarios.
|
|
128
|
-
</commentary>
|
|
129
|
-
</example>
|
|
130
|
-
<example>
|
|
131
|
-
Context: User has deployed a new feature and wants thorough testing coverage.
|
|
132
|
-
user: 'Can you help me test our new user dashboard at https://app.example.com/dashboard?'
|
|
133
|
-
assistant: 'I'll launch the planner agent to explore your dashboard and develop detailed test
|
|
134
|
-
scenarios.'
|
|
135
|
-
<commentary>
|
|
136
|
-
This requires web exploration and test scenario creation, perfect for the planner agent.
|
|
137
|
-
</commentary>
|
|
138
|
-
</example>
|
package/lib/mcp/program.js
CHANGED
|
@@ -31,7 +31,9 @@ __export(program_exports, {
|
|
|
31
31
|
decorateCommand: () => decorateCommand
|
|
32
32
|
});
|
|
33
33
|
module.exports = __toCommonJS(program_exports);
|
|
34
|
+
var import_fs = __toESM(require("fs"));
|
|
34
35
|
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
|
36
|
+
var import_server = require("playwright-core/lib/server");
|
|
35
37
|
var mcpServer = __toESM(require("./sdk/server"));
|
|
36
38
|
var import_config = require("./browser/config");
|
|
37
39
|
var import_watchdog = require("./browser/watchdog");
|
|
@@ -47,6 +49,16 @@ function decorateCommand(command, version) {
|
|
|
47
49
|
options.caps = "vision";
|
|
48
50
|
}
|
|
49
51
|
const config = await (0, import_config.resolveCLIConfig)(options);
|
|
52
|
+
if (config.saveVideo && !checkFfmpeg()) {
|
|
53
|
+
console.error(import_utilsBundle.colors.red(`
|
|
54
|
+
Error: ffmpeg required to save the video is not installed.`));
|
|
55
|
+
console.error(`
|
|
56
|
+
Please run the command below. It will install a local copy of ffmpeg and will not change any system-wide settings.`);
|
|
57
|
+
console.error(`
|
|
58
|
+
npx playwright install ffmpeg
|
|
59
|
+
`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
50
62
|
const browserContextFactory = (0, import_browserContextFactory.contextFactory)(config);
|
|
51
63
|
const extensionContextFactory = new import_extensionContextFactory.ExtensionContextFactory(config.browser.launchOptions.channel || "chrome", config.browser.userDataDir, config.browser.launchOptions.executablePath);
|
|
52
64
|
if (options.extension) {
|
|
@@ -90,6 +102,14 @@ function decorateCommand(command, version) {
|
|
|
90
102
|
await mcpServer.start(factory, config.server);
|
|
91
103
|
});
|
|
92
104
|
}
|
|
105
|
+
function checkFfmpeg() {
|
|
106
|
+
try {
|
|
107
|
+
const executable = import_server.registry.findExecutable("ffmpeg");
|
|
108
|
+
return import_fs.default.existsSync(executable.executablePath("javascript"));
|
|
109
|
+
} catch (error) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
93
113
|
// Annotate the CommonJS export names for ESM import in node:
|
|
94
114
|
0 && (module.exports = {
|
|
95
115
|
decorateCommand
|
package/lib/program.js
CHANGED
|
@@ -179,22 +179,21 @@ function addInitAgentsCommand(program3) {
|
|
|
179
179
|
const command = program3.command("init-agents");
|
|
180
180
|
command.description("Initialize repository agents");
|
|
181
181
|
const option = command.createOption("--loop <loop>", "Agentic loop provider");
|
|
182
|
-
option.choices(["
|
|
182
|
+
option.choices(["claude", "copilot", "opencode", "vscode", "vscode-legacy"]);
|
|
183
183
|
command.addOption(option);
|
|
184
184
|
command.option("-c, --config <file>", `Configuration file to find a project to use for seed test`);
|
|
185
185
|
command.option("--project <project>", "Project to use for seed test");
|
|
186
|
+
command.option("--prompts", "Whether to include prompts in the agent initialization");
|
|
186
187
|
command.action(async (opts) => {
|
|
187
188
|
const config = await (0, import_configLoader.loadConfigFromFile)(opts.config);
|
|
188
189
|
if (opts.loop === "opencode") {
|
|
189
|
-
await import_generateAgents.OpencodeGenerator.init(config, opts.project);
|
|
190
|
-
} else if (opts.loop === "vscode") {
|
|
190
|
+
await import_generateAgents.OpencodeGenerator.init(config, opts.project, opts.prompts);
|
|
191
|
+
} else if (opts.loop === "vscode-legacy") {
|
|
191
192
|
await import_generateAgents.VSCodeGenerator.init(config, opts.project);
|
|
192
193
|
} else if (opts.loop === "claude") {
|
|
193
|
-
await import_generateAgents.ClaudeGenerator.init(config, opts.project);
|
|
194
|
-
} else if (opts.loop === "generic") {
|
|
195
|
-
await import_generateAgents.AgentGenerator.init(config, opts.project);
|
|
194
|
+
await import_generateAgents.ClaudeGenerator.init(config, opts.project, opts.prompts);
|
|
196
195
|
} else {
|
|
197
|
-
|
|
196
|
+
await import_generateAgents.CopilotGenerator.init(config, opts.project, opts.prompts);
|
|
198
197
|
return;
|
|
199
198
|
}
|
|
200
199
|
});
|
package/lib/runner/testRunner.js
CHANGED
|
@@ -93,7 +93,7 @@ class TestRunner extends import_events.default {
|
|
|
93
93
|
}
|
|
94
94
|
async installBrowsers() {
|
|
95
95
|
const executables = import_server.registry.defaultExecutables();
|
|
96
|
-
await import_server.registry.install(executables
|
|
96
|
+
await import_server.registry.install(executables);
|
|
97
97
|
}
|
|
98
98
|
async loadConfig() {
|
|
99
99
|
const { config, error } = await this._loadConfig(this._configCLIOverrides);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "playwright",
|
|
3
|
-
"version": "1.57.0-alpha-2025-10-
|
|
3
|
+
"version": "1.57.0-alpha-2025-10-28",
|
|
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-10-
|
|
67
|
+
"playwright-core": "1.57.0-alpha-2025-10-28"
|
|
68
68
|
},
|
|
69
69
|
"optionalDependencies": {
|
|
70
70
|
"fsevents": "2.3.2"
|