playwright-e2e-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +497 -0
  3. package/dist/http.d.ts +27 -0
  4. package/dist/http.js +156 -0
  5. package/dist/http.js.map +1 -0
  6. package/dist/index.d.ts +12 -0
  7. package/dist/index.js +56 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/server.d.ts +23 -0
  10. package/dist/server.js +225 -0
  11. package/dist/server.js.map +1 -0
  12. package/dist/tools/compare-visual-state.d.ts +77 -0
  13. package/dist/tools/compare-visual-state.js +223 -0
  14. package/dist/tools/compare-visual-state.js.map +1 -0
  15. package/dist/tools/diagnose-flaky.d.ts +87 -0
  16. package/dist/tools/diagnose-flaky.js +258 -0
  17. package/dist/tools/diagnose-flaky.js.map +1 -0
  18. package/dist/tools/generate-e2e-test.d.ts +51 -0
  19. package/dist/tools/generate-e2e-test.js +274 -0
  20. package/dist/tools/generate-e2e-test.js.map +1 -0
  21. package/dist/tools/get-failure.d.ts +26 -0
  22. package/dist/tools/get-failure.js +220 -0
  23. package/dist/tools/get-failure.js.map +1 -0
  24. package/dist/tools/inspect-page.d.ts +57 -0
  25. package/dist/tools/inspect-page.js +124 -0
  26. package/dist/tools/inspect-page.js.map +1 -0
  27. package/dist/tools/list-tests.d.ts +35 -0
  28. package/dist/tools/list-tests.js +88 -0
  29. package/dist/tools/list-tests.js.map +1 -0
  30. package/dist/tools/run-test.d.ts +70 -0
  31. package/dist/tools/run-test.js +169 -0
  32. package/dist/tools/run-test.js.map +1 -0
  33. package/dist/tools/shared.d.ts +86 -0
  34. package/dist/tools/shared.js +490 -0
  35. package/dist/tools/shared.js.map +1 -0
  36. package/dist/tools/validate-selector.d.ts +32 -0
  37. package/dist/tools/validate-selector.js +93 -0
  38. package/dist/tools/validate-selector.js.map +1 -0
  39. package/dist/types/index.d.ts +344 -0
  40. package/dist/types/index.js +51 -0
  41. package/dist/types/index.js.map +1 -0
  42. package/dist/utils/change-analyzer.d.ts +46 -0
  43. package/dist/utils/change-analyzer.js +290 -0
  44. package/dist/utils/change-analyzer.js.map +1 -0
  45. package/dist/utils/image-diff.d.ts +61 -0
  46. package/dist/utils/image-diff.js +373 -0
  47. package/dist/utils/image-diff.js.map +1 -0
  48. package/dist/utils/logger.d.ts +21 -0
  49. package/dist/utils/logger.js +132 -0
  50. package/dist/utils/logger.js.map +1 -0
  51. package/dist/utils/path-utils.d.ts +55 -0
  52. package/dist/utils/path-utils.js +211 -0
  53. package/dist/utils/path-utils.js.map +1 -0
  54. package/dist/utils/playwright-runner.d.ts +63 -0
  55. package/dist/utils/playwright-runner.js +594 -0
  56. package/dist/utils/playwright-runner.js.map +1 -0
  57. package/dist/utils/project-detector.d.ts +43 -0
  58. package/dist/utils/project-detector.js +255 -0
  59. package/dist/utils/project-detector.js.map +1 -0
  60. package/dist/utils/report-parser.d.ts +60 -0
  61. package/dist/utils/report-parser.js +501 -0
  62. package/dist/utils/report-parser.js.map +1 -0
  63. package/dist/utils/trace-reader.d.ts +109 -0
  64. package/dist/utils/trace-reader.js +608 -0
  65. package/dist/utils/trace-reader.js.map +1 -0
  66. package/examples/sample-test.spec.ts +49 -0
  67. package/package.json +66 -0
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Tool 6 — generate-e2e-test: scaffold a Playwright test from a natural
3
+ * language description, grounded in what actually exists in the repo.
4
+ *
5
+ * Blueprint behavior: look at the agent's recent file changes, identify
6
+ * the components involved, and generate a test that uses the *correct*
7
+ * selectors (data-testid / role / label…) instead of guessing. When a
8
+ * URL is known, selectors are cross-checked against the live DOM.
9
+ */
10
+ import { z } from 'zod';
11
+ import type { ToolContext, ToolResponse } from '../types/index.js';
12
+ declare const generateInput: z.ZodObject<{
13
+ description: z.ZodString;
14
+ pageUrl: z.ZodOptional<z.ZodString>;
15
+ projectRoot: z.ZodOptional<z.ZodString>;
16
+ config: z.ZodOptional<z.ZodString>;
17
+ testDir: z.ZodOptional<z.ZodString>;
18
+ file: z.ZodOptional<z.ZodString>;
19
+ write: z.ZodOptional<z.ZodBoolean>;
20
+ overwrite: z.ZodOptional<z.ZodBoolean>;
21
+ liveInspect: z.ZodOptional<z.ZodBoolean>;
22
+ }, z.core.$strip>;
23
+ export type GenerateE2ETestInput = z.infer<typeof generateInput>;
24
+ export declare const generateE2ETestSchema: z.ZodObject<{
25
+ description: z.ZodString;
26
+ pageUrl: z.ZodOptional<z.ZodString>;
27
+ projectRoot: z.ZodOptional<z.ZodString>;
28
+ config: z.ZodOptional<z.ZodString>;
29
+ testDir: z.ZodOptional<z.ZodString>;
30
+ file: z.ZodOptional<z.ZodString>;
31
+ write: z.ZodOptional<z.ZodBoolean>;
32
+ overwrite: z.ZodOptional<z.ZodBoolean>;
33
+ liveInspect: z.ZodOptional<z.ZodBoolean>;
34
+ }, z.core.$strip>;
35
+ export declare const generateE2ETestTool: {
36
+ name: string;
37
+ description: string;
38
+ inputSchema: z.ZodObject<{
39
+ description: z.ZodString;
40
+ pageUrl: z.ZodOptional<z.ZodString>;
41
+ projectRoot: z.ZodOptional<z.ZodString>;
42
+ config: z.ZodOptional<z.ZodString>;
43
+ testDir: z.ZodOptional<z.ZodString>;
44
+ file: z.ZodOptional<z.ZodString>;
45
+ write: z.ZodOptional<z.ZodBoolean>;
46
+ overwrite: z.ZodOptional<z.ZodBoolean>;
47
+ liveInspect: z.ZodOptional<z.ZodBoolean>;
48
+ }, z.core.$strip>;
49
+ handler: (args: GenerateE2ETestInput, ctx: ToolContext) => Promise<ToolResponse>;
50
+ };
51
+ export {};
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Tool 6 — generate-e2e-test: scaffold a Playwright test from a natural
3
+ * language description, grounded in what actually exists in the repo.
4
+ *
5
+ * Blueprint behavior: look at the agent's recent file changes, identify
6
+ * the components involved, and generate a test that uses the *correct*
7
+ * selectors (data-testid / role / label…) instead of guessing. When a
8
+ * URL is known, selectors are cross-checked against the live DOM.
9
+ */
10
+ import { z } from 'zod';
11
+ import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';
12
+ import path from 'node:path';
13
+ import { PlaywrightMcpError } from '../types/index.js';
14
+ import { detectProject } from '../utils/project-detector.js';
15
+ import { analyzeRecentChanges, detectEntryUrl } from '../utils/change-analyzer.js';
16
+ import { normalizePath, relativeToRoot, resolvePath, sanitizeUserPath } from '../utils/path-utils.js';
17
+ import { assertHttpUrl, clipLines, guard, resolveConfigSelection, resolveProjectRoot, runBrowserScript, toolText, } from './shared.js';
18
+ const generateInput = z.object({
19
+ description: z
20
+ .string()
21
+ .min(3)
22
+ .describe('What the test should cover, e.g. "login with valid credentials"'),
23
+ pageUrl: z.string().optional().describe('Page the test starts on (default: baseURL from playwright.config)'),
24
+ projectRoot: z.string().optional().describe('Project directory; defaults to the server working directory'),
25
+ config: z.string().optional().describe('playwright.config path or 1-based index'),
26
+ testDir: z.string().optional().describe('Directory for the generated spec (default: detected testDir or tests/)'),
27
+ file: z.string().optional().describe('Exact file path inside the project instead of the default location'),
28
+ write: z.boolean().optional().describe('Write the file to disk (default true)'),
29
+ overwrite: z.boolean().optional().describe('Replace an existing file at the target path'),
30
+ liveInspect: z
31
+ .boolean()
32
+ .optional()
33
+ .describe('Verify discovered selectors against the live page (default true when a URL is known)'),
34
+ });
35
+ export const generateE2ETestSchema = generateInput;
36
+ /* ------------------------------------------------------------------ */
37
+ /* Helpers */
38
+ /* ------------------------------------------------------------------ */
39
+ const KIND_PRIORITY = {
40
+ 'test-id': 0,
41
+ role: 1,
42
+ aria: 2,
43
+ placeholder: 3,
44
+ name: 4,
45
+ id: 5,
46
+ text: 6,
47
+ };
48
+ function jsString(value) {
49
+ return value
50
+ .replace(/\\/g, '\\\\')
51
+ .replace(/'/g, "\\'")
52
+ .replace(/\r?\n/g, ' ')
53
+ .trim();
54
+ }
55
+ function slugify(description) {
56
+ const slug = description
57
+ .toLowerCase()
58
+ .replace(/[^a-z0-9]+/g, '-')
59
+ .replace(/^-+|-+$/g, '')
60
+ .slice(0, 60);
61
+ return slug === '' ? 'generated-test' : slug;
62
+ }
63
+ /** Best-effort live-DOM match for a candidate locator. */
64
+ function verifiedLive(candidate, elements) {
65
+ if (!elements)
66
+ return false;
67
+ return elements.some((element) => {
68
+ const values = Object.values(element.attributes);
69
+ switch (candidate.kind) {
70
+ case 'test-id':
71
+ return (element.attributes['data-testid'] === candidate.value ||
72
+ element.attributes['data-test-id'] === candidate.value ||
73
+ element.attributes['data-test'] === candidate.value ||
74
+ element.attributes['data-cy'] === candidate.value ||
75
+ values.includes(candidate.value));
76
+ case 'aria':
77
+ return element.attributes['aria-label'] === candidate.value;
78
+ case 'placeholder':
79
+ return element.attributes['placeholder'] === candidate.value;
80
+ case 'id':
81
+ return element.id === candidate.value;
82
+ case 'name':
83
+ return element.attributes['name'] === candidate.value;
84
+ case 'role':
85
+ case 'text':
86
+ return (element.text ?? '').includes(candidate.value);
87
+ default:
88
+ return false;
89
+ }
90
+ });
91
+ }
92
+ function buildSpec(options) {
93
+ const title = jsString(options.description);
94
+ const goto = options.entryUrl
95
+ ? ` await page.goto('${jsString(options.entryUrl)}');`
96
+ : [
97
+ ' // No baseURL found in playwright.config — set the start URL:',
98
+ " await page.goto('/');",
99
+ ].join('\n');
100
+ const steps = [];
101
+ if (options.selectors.length === 0) {
102
+ steps.push(' // No selectors could be discovered from recent changes.', ' // Add data-testid attributes to the components, then re-run', ' // generate-e2e-test — or use inspect-page to read the live DOM:', " // await expect(page.getByTestId('your-test-id')).toBeVisible();");
103
+ }
104
+ else {
105
+ for (const candidate of options.selectors) {
106
+ const note = `${candidate.kind} · ${candidate.file}:${candidate.line}`;
107
+ steps.push(` // ${note}`);
108
+ steps.push(` await expect(page.${candidate.locator}).toBeVisible();`);
109
+ if (candidate.kind === 'name' || candidate.kind === 'placeholder' || candidate.kind === 'aria') {
110
+ steps.push(` // await page.${candidate.locator}.fill('…');`);
111
+ }
112
+ steps.push('');
113
+ }
114
+ while (steps.length > 0 && steps[steps.length - 1] === '')
115
+ steps.pop();
116
+ }
117
+ return [
118
+ "import { test, expect } from '@playwright/test';",
119
+ '',
120
+ '/**',
121
+ ` * Generated by playwright-e2e-mcp for: "${title}"`,
122
+ ' * Selectors below come from the project source (recent changes).',
123
+ ' * Review the flow, then run it with the run-test tool.',
124
+ ' */',
125
+ `test.describe('${title}', () => {`,
126
+ ` test('${title}', async ({ page }) => {`,
127
+ goto,
128
+ '',
129
+ ...steps,
130
+ ' });',
131
+ '});',
132
+ '',
133
+ ].join('\n');
134
+ }
135
+ /* ------------------------------------------------------------------ */
136
+ /* Tool */
137
+ /* ------------------------------------------------------------------ */
138
+ export const generateE2ETestTool = {
139
+ name: 'generate-e2e-test',
140
+ description: "Scaffold a Playwright test from a description using the project's REAL selectors. Scans the agent's recent file changes (git working tree / last commit, mtime fallback), extracts data-testid / role / label / placeholder / id locators with their source locations, optionally cross-checks them against the live page, writes a ready-to-run spec, and returns next steps.",
141
+ inputSchema: generateE2ETestSchema,
142
+ handler: async (args, ctx) => guard('generate-e2e-test', async () => {
143
+ const root = await resolveProjectRoot(args.projectRoot, ctx);
144
+ const detection = await detectProject(root);
145
+ const configPath = resolveConfigSelection(detection, args.config);
146
+ // Entry URL: explicit pageUrl, else baseURL / webServer url from config.
147
+ let entryUrl;
148
+ let entryUrlSource = 'none';
149
+ if (args.pageUrl) {
150
+ entryUrl = assertHttpUrl(args.pageUrl);
151
+ entryUrlSource = 'pageUrl argument';
152
+ }
153
+ else if (configPath) {
154
+ const source = await readFile(configPath, 'utf8').catch(() => '');
155
+ const found = detectEntryUrl(source);
156
+ const candidate = found.baseURL ?? found.webServerUrl;
157
+ if (candidate) {
158
+ try {
159
+ entryUrl = assertHttpUrl(candidate);
160
+ entryUrlSource = found.baseURL ? 'baseURL from playwright.config' : 'webServer.url from playwright.config';
161
+ }
162
+ catch {
163
+ entryUrl = undefined;
164
+ }
165
+ }
166
+ }
167
+ // 1. What changed recently, and which selectors do those files declare?
168
+ const analysis = await analyzeRecentChanges(root, { limit: 12 });
169
+ // 2. Optionally verify selectors against the live DOM.
170
+ let liveElements;
171
+ let liveNote;
172
+ const wantsLive = args.liveInspect ?? Boolean(entryUrl);
173
+ if (wantsLive && entryUrl) {
174
+ const outcome = await runBrowserScript({
175
+ mode: 'inspect',
176
+ projectRoot: root,
177
+ url: entryUrl,
178
+ includeHtml: false,
179
+ gotoTimeout: 15_000,
180
+ startedAt: Date.now(),
181
+ }, { timeoutMs: 35_000, signal: ctx.signal });
182
+ if (outcome.ok) {
183
+ liveElements = outcome.data?.elements;
184
+ }
185
+ else {
186
+ liveNote = `Live check unavailable (${outcome.kind ?? 'UNKNOWN'}): ${outcome.error ?? 'unknown error'}${outcome.hint ? ` — ${outcome.hint}` : ''}`;
187
+ }
188
+ }
189
+ else if (!entryUrl) {
190
+ liveNote = 'No URL known (pass pageUrl or set baseURL in playwright.config), so selectors were not live-verified.';
191
+ }
192
+ // 3. Rank selectors: verified-live first, then by kind priority.
193
+ const ranked = analysis.selectors
194
+ .map((candidate) => ({ candidate, live: verifiedLive(candidate, liveElements) }))
195
+ .sort((a, b) => {
196
+ if (a.live !== b.live)
197
+ return a.live ? -1 : 1;
198
+ return KIND_PRIORITY[a.candidate.kind] - KIND_PRIORITY[b.candidate.kind];
199
+ })
200
+ .slice(0, 8)
201
+ .map((entry) => entry.candidate);
202
+ const liveSet = new Set(analysis.selectors.filter((candidate) => verifiedLive(candidate, liveElements)).map((c) => `${c.kind}:${c.value}`));
203
+ // 4. Build and (optionally) write the spec.
204
+ const spec = buildSpec({ description: args.description, entryUrl, selectors: ranked });
205
+ let targetPath;
206
+ const shouldWrite = args.write !== false;
207
+ if (shouldWrite) {
208
+ if (args.file) {
209
+ targetPath = sanitizeUserPath(args.file, root);
210
+ }
211
+ else {
212
+ const dir = args.testDir
213
+ ? sanitizeUserPath(args.testDir, root)
214
+ : detection.testDir
215
+ ? normalizePath(detection.testDir)
216
+ : resolvePath(root, 'tests');
217
+ targetPath = resolvePath(dir, `generated/${slugify(args.description)}.spec.ts`);
218
+ }
219
+ const exists = await stat(targetPath).then((info) => info.isFile(), () => false);
220
+ if (exists && args.overwrite !== true) {
221
+ throw new PlaywrightMcpError(`A file already exists at ${relativeToRoot(root, targetPath)}`, 'INVALID_PATH', { hint: 'Pass `overwrite: true` to replace it, or set `file` / `description` to pick a different name.' });
222
+ }
223
+ await mkdir(path.dirname(targetPath), { recursive: true });
224
+ await writeFile(targetPath, spec, 'utf8');
225
+ ctx.logger.info('generated test written', { file: targetPath, selectors: ranked.length });
226
+ }
227
+ // 5. Render.
228
+ const lines = [`## 🧪 Generated test — "${args.description}"`, ''];
229
+ if (targetPath) {
230
+ lines.push(`**File:** \`${relativeToRoot(root, targetPath)}\` (written)`);
231
+ }
232
+ else {
233
+ lines.push('_Not written (`write: false`) — code below._');
234
+ }
235
+ if (entryUrl)
236
+ lines.push(`**Entry URL:** ${entryUrl} (${entryUrlSource})`);
237
+ lines.push(`**Changes analyzed:** ${analysis.files.length} file(s) via \`${analysis.changeSource === 'git-status'
238
+ ? 'git status'
239
+ : analysis.changeSource === 'git-last-commit'
240
+ ? 'git diff HEAD~1'
241
+ : 'recent mtime'}\`${analysis.truncated ? ' (truncated)' : ''}`, '');
242
+ if (analysis.files.length > 0) {
243
+ lines.push('### Recent changes', '');
244
+ for (const file of analysis.files.slice(0, 12)) {
245
+ lines.push(`- \`${file.path}\` _(${file.source})_`);
246
+ }
247
+ lines.push('');
248
+ }
249
+ if (ranked.length > 0) {
250
+ lines.push(`### Selectors used (${ranked.length})`, '');
251
+ for (const candidate of ranked) {
252
+ const liveMark = liveElements
253
+ ? liveSet.has(`${candidate.kind}:${candidate.value}`)
254
+ ? ' ✅ live'
255
+ : ' ⚠️ not on page'
256
+ : '';
257
+ lines.push(`- \`${candidate.locator}\` — ${candidate.kind} from \`${candidate.file}:${candidate.line}\`_${liveMark}_`);
258
+ }
259
+ lines.push('');
260
+ }
261
+ else {
262
+ lines.push('### No selectors discovered', '', 'The recent changes did not expose any `data-testid`, role, label, placeholder or id attributes. The scaffold below includes commented guidance; add `data-testid` attributes to your components (or call **inspect-page** on the running app) and regenerate.', '');
263
+ }
264
+ if (liveNote)
265
+ lines.push(`> ${liveNote}`, '');
266
+ lines.push('### Test source', '', '```ts', clipLines(spec, 200, 12_000), '```', '');
267
+ lines.push('### Next steps', '');
268
+ lines.push(targetPath
269
+ ? `1. Review the flow (the scaffold provides selector-accurate building blocks).`
270
+ : `1. Save the code above, then continue.`, `2. Run it: \`run-test\` with \`testFiles: ["${targetPath ? relativeToRoot(root, targetPath) : '<file>'}"]\`.`, '3. If a locator misses, call `inspect-page` for the live DOM or `validate-selector` to prove a replacement.');
271
+ return toolText(lines.join('\n'));
272
+ }),
273
+ };
274
+ //# sourceMappingURL=generate-e2e-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-e2e-test.js","sourceRoot":"","sources":["../../src/tools/generate-e2e-test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Tool 2 — get-failure: deep analysis of a single failure from the most
3
+ * recent run-test execution (message, code frame, expected/actual,
4
+ * stack, diagnosis and concrete next steps).
5
+ */
6
+ import { z } from 'zod';
7
+ import type { ToolContext, ToolResponse } from '../types/index.js';
8
+ declare const getFailureInput: z.ZodObject<{
9
+ index: z.ZodOptional<z.ZodNumber>;
10
+ projectRoot: z.ZodOptional<z.ZodString>;
11
+ }, z.core.$strip>;
12
+ export type GetFailureInput = z.infer<typeof getFailureInput>;
13
+ export declare const getFailureSchema: z.ZodObject<{
14
+ index: z.ZodOptional<z.ZodNumber>;
15
+ projectRoot: z.ZodOptional<z.ZodString>;
16
+ }, z.core.$strip>;
17
+ export declare const getFailureTool: {
18
+ name: string;
19
+ description: string;
20
+ inputSchema: z.ZodObject<{
21
+ index: z.ZodOptional<z.ZodNumber>;
22
+ projectRoot: z.ZodOptional<z.ZodString>;
23
+ }, z.core.$strip>;
24
+ handler: (args: GetFailureInput, ctx: ToolContext) => Promise<ToolResponse>;
25
+ };
26
+ export {};
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Tool 2 — get-failure: deep analysis of a single failure from the most
3
+ * recent run-test execution (message, code frame, expected/actual,
4
+ * stack, diagnosis and concrete next steps).
5
+ */
6
+ import { readFile } from 'node:fs/promises';
7
+ import { z } from 'zod';
8
+ import { PlaywrightMcpError } from '../types/index.js';
9
+ import { parseReportJson } from '../utils/report-parser.js';
10
+ import { readFailureTrace } from '../utils/trace-reader.js';
11
+ import { codeFence, clipLines, failureHint, formatDuration, guard, toolText, } from './shared.js';
12
+ const getFailureInput = z.object({
13
+ index: z
14
+ .number()
15
+ .int()
16
+ .min(1)
17
+ .optional()
18
+ .describe('1-based failure index from the last run (default 1)'),
19
+ projectRoot: z.string().optional().describe('Only used to re-read the stored report'),
20
+ });
21
+ export const getFailureSchema = getFailureInput;
22
+ function nextSteps(failure, index) {
23
+ const steps = [];
24
+ const location = failure.file ? `${failure.file}${failure.line ? `:${failure.line}` : ''}` : undefined;
25
+ if (location) {
26
+ steps.push(`Re-run only this test: \`run-test\` with \`testFiles: ["${location}"]\`, \`headed: true\` to watch it live.`);
27
+ }
28
+ if (failure.failureKind === 'assertion' || failure.failureKind === 'timeout') {
29
+ if (location) {
30
+ steps.push(`Single-step with Playwright UI mode: \`run-test\` with \`args: ["--headed", "--debug"]\` on \`${location}\`.`);
31
+ }
32
+ }
33
+ if (/locator|selector|resolved to|waiting for/i.test(failure.message)) {
34
+ const selectorMatch = /locator\((['"`])(.+?)\1\)/.exec(failure.message);
35
+ const selector = selectorMatch ? selectorMatch[2] : '<your-selector>';
36
+ steps.push(`Verify the selector against the live page: \`validate-selector\` with \`selector: "${selector}"\`, or \`inspect-page\` to see the real DOM.`);
37
+ }
38
+ if (failure.failureKind === 'server-unreachable') {
39
+ steps.push('Start the dev server, then re-run the same test with `run-test`.');
40
+ }
41
+ if (failure.failureKind === 'browser-crash') {
42
+ steps.push('Run `npx playwright install --force` and close other Chrome/automation instances, then retry.');
43
+ }
44
+ steps.push(`Ask for this failure again with \`index: ${index}\` after re-running to compare.`);
45
+ return steps;
46
+ }
47
+ async function renderTraceContext(failure) {
48
+ if (!failure.tracePath) {
49
+ return [
50
+ '### DOM at failure',
51
+ '',
52
+ '> No trace was captured for this attempt. `run-test` passes `--trace=retain-on-failure` automatically; you can also set `trace: "retain-on-failure"` in playwright.config.* before running tests directly.',
53
+ '',
54
+ ].join('\n');
55
+ }
56
+ const trace = await readFailureTrace(failure.tracePath).catch(() => null);
57
+ if (trace === null) {
58
+ return [
59
+ '### DOM at failure',
60
+ '',
61
+ `> The trace archive is missing (\`${failure.tracePath}\`). The run may have been killed before Playwright flushed it — re-run the test to capture a fresh trace.`,
62
+ '',
63
+ ].join('\n');
64
+ }
65
+ const lines = ['### DOM at failure (Playwright trace)', ''];
66
+ if (trace.action) {
67
+ const selector = trace.action.selector ? ` \`${trace.action.selector}\`` : '';
68
+ const at = trace.atMs !== undefined ? ` at +${(trace.atMs / 1_000).toFixed(1)}s` : '';
69
+ lines.push(`**Failed action:** \`${trace.action.api}\`${selector}${at}`);
70
+ if (trace.action.params)
71
+ lines.push(`**Params:** \`${clipLines(trace.action.params, 1, 300)}\``);
72
+ lines.push('');
73
+ }
74
+ if (trace.actionLog.length > 0) {
75
+ lines.push('**Action log (last steps):**', '');
76
+ for (const entry of trace.actionLog) {
77
+ const marker = entry.failed ? '✗' : '•';
78
+ const summary = entry.summary ? ` \`${clipLines(entry.summary, 1, 160)}\`` : '';
79
+ lines.push(`- ${marker} \`${entry.api}\`${summary}`);
80
+ }
81
+ lines.push('');
82
+ }
83
+ if (trace.failedRequests.length > 0) {
84
+ const of = trace.networkTotal > 0 ? ` of ${trace.networkTotal} requests` : '';
85
+ lines.push(`### Network requests that failed (${trace.failedRequests.length}${of})`, '');
86
+ for (const req of trace.failedRequests) {
87
+ const status = req.status !== undefined
88
+ ? `**${req.status}${req.statusText ? ` ${req.statusText}` : ''}**`
89
+ : `**no response** (${req.errorText ?? 'request failed'})`;
90
+ const kind = req.resourceType ? ` (${req.resourceType})` : '';
91
+ const err = req.errorText && req.status !== undefined ? ` — ${clipLines(req.errorText, 1, 160)}` : '';
92
+ const url = clipLines(req.url, 1, 200);
93
+ lines.push(`- \`${req.method} ${url}\` → ${status}${kind}${err}`);
94
+ }
95
+ lines.push('');
96
+ }
97
+ if (trace.consoleMessages.length > 0) {
98
+ lines.push('### Console before the failure', '');
99
+ for (const msg of trace.consoleMessages) {
100
+ const icon = msg.type === 'error' ? '❌' : '⚠️';
101
+ const where = msg.location ? ` — \`${clipLines(msg.location, 1, 160)}\`` : '';
102
+ lines.push(`- ${icon} ${clipLines(msg.text, 1, 300)}${where}`);
103
+ }
104
+ lines.push('');
105
+ }
106
+ const html = trace.snippet ?? trace.snapshotHtml;
107
+ if (html) {
108
+ const title = trace.snippet
109
+ ? 'Parent container at failure:'
110
+ : `DOM snapshot at failure${trace.snapshotTruncated ? ' (truncated)' : ''}:`;
111
+ lines.push(title, '', codeFence(html, 'html'), '');
112
+ }
113
+ if (trace.errorContext) {
114
+ lines.push('Page accessibility tree at failure (Playwright `error-context`):', '', codeFence(clipLines(trace.errorContext, 60, 5_000), 'markdown'), '');
115
+ }
116
+ for (const warning of trace.warnings)
117
+ lines.push(`> ⚠️ ${warning}`);
118
+ if (trace.warnings.length > 0)
119
+ lines.push('');
120
+ return lines.join('\n');
121
+ }
122
+ async function renderFailureDetail(failure, index, total) {
123
+ const lines = [
124
+ `## Failure ${index} of ${total} — ${failure.title}`,
125
+ '',
126
+ ];
127
+ const location = failure.file
128
+ ? `\`${failure.file}${failure.line ? `:${failure.line}` : ''}\``
129
+ : '_configuration_';
130
+ const meta = [
131
+ failure.project ? `project **${failure.project}**` : undefined,
132
+ failure.retry !== undefined ? `attempt ${failure.retry + 1}` : undefined,
133
+ failure.durationMs !== undefined ? `took ${formatDuration(failure.durationMs)}` : undefined,
134
+ `status **${failure.status}**`,
135
+ ].filter(Boolean);
136
+ lines.push(`**File:** ${location} | ${meta.join(' · ')}`, '');
137
+ lines.push('### Error', '', codeFence(clipLines(failure.message, 30, 5_000), 'text'), '');
138
+ if (failure.expected !== undefined || failure.actual !== undefined) {
139
+ lines.push('### Expected vs actual', '');
140
+ if (failure.expected !== undefined)
141
+ lines.push(`- **Expected:** ${codeFence(failure.expected, 'text')}`);
142
+ if (failure.actual !== undefined)
143
+ lines.push(`- **Actual:** ${codeFence(failure.actual, 'text')}`);
144
+ lines.push('');
145
+ }
146
+ if (failure.stack && failure.stack.trim() !== '') {
147
+ lines.push('### Stack', '', codeFence(clipLines(failure.stack, 30, 4_000), 'text'), '');
148
+ }
149
+ if (failure.stdout && failure.stdout.trim() !== '') {
150
+ lines.push('### Test console output', '', codeFence(clipLines(failure.stdout, 20, 3_000), 'text'), '');
151
+ }
152
+ lines.push(await renderTraceContext(failure));
153
+ lines.push('### Diagnosis', '', `- **Kind:** \`${failure.failureKind}\``, `- ${failureHint(failure.failureKind)}`, '');
154
+ lines.push('### Next steps', '');
155
+ for (const [i, step] of nextSteps(failure, index).entries()) {
156
+ lines.push(`${i + 1}. ${step}`);
157
+ }
158
+ lines.push('');
159
+ return lines.join('\n');
160
+ }
161
+ export const getFailureTool = {
162
+ name: 'get-failure',
163
+ description: 'Analyze one failure from the most recent run-test execution: full message and code frame, expected vs actual, stack trace, failed network requests (4xx/5xx and dead endpoints), console errors, failure kind (assertion/timeout/browser-crash/syntax/config/server-unreachable) and concrete next steps. Use run-test first.',
164
+ inputSchema: getFailureSchema,
165
+ handler: async (args, ctx) => guard('get-failure', async () => {
166
+ const record = ctx.store.lastRun;
167
+ if (!record) {
168
+ throw new PlaywrightMcpError('No test run has happened yet', 'REPORT_MISSING', {
169
+ hint: 'Call run-test first — get-failure analyzes the failures of the most recent run.',
170
+ });
171
+ }
172
+ let failures = record.result.failures;
173
+ // The run-level result caps failures; re-read the JSON report for
174
+ // the full list when more failures were recorded than shown.
175
+ if (record.result.reportPath && record.result.failuresTruncated) {
176
+ const raw = await readFile(record.result.reportPath, 'utf8').catch(() => null);
177
+ if (raw !== null) {
178
+ const parsed = parseReportJson(raw, record.projectRoot, 200);
179
+ if (parsed.failures.length > failures.length)
180
+ failures = parsed.failures;
181
+ }
182
+ }
183
+ const total = failures.length;
184
+ if (total === 0) {
185
+ const stats = record.result.stats;
186
+ const summary = stats
187
+ ? `Last run: ${stats.expected} passed, ${stats.unexpected} failed, ${stats.flaky} flaky, ${stats.skipped} skipped.`
188
+ : 'Last run produced no failures.';
189
+ return toolText([
190
+ '## ✅ No failures to analyze',
191
+ '',
192
+ summary,
193
+ '',
194
+ record.result.errorKind
195
+ ? `Note: the run reported \`${record.result.errorKind}\` — ${record.result.errorMessage ?? ''}`
196
+ : '',
197
+ ]
198
+ .filter(Boolean)
199
+ .join('\n'));
200
+ }
201
+ const index = Math.min(args.index ?? 1, total);
202
+ const failure = failures[index - 1];
203
+ const parts = [await renderFailureDetail(failure, index, total)];
204
+ if (total > 1) {
205
+ parts.push('### All failures in this run', '');
206
+ for (let i = 0; i < failures.length; i += 1) {
207
+ const f = failures[i];
208
+ parts.push(`${i + 1}. ${i + 1 === index ? '**→** ' : ''}${f.title} — \`${f.failureKind}\`${f.file ? ` (${f.file}${f.line ? `:${f.line}` : ''})` : ''}`);
209
+ }
210
+ parts.push('');
211
+ }
212
+ const run = record.result;
213
+ parts.push('### Run context', '', `- **Command:** \`${run.command}\``, `- **Duration:** ${formatDuration(run.durationMs)} · **Exit:** ${run.exitCode ?? '—'}`, run.errorKind ? `- **Run issue:** \`${run.errorKind}\` — ${run.errorMessage ?? ''}` : undefined, run.hint ? `- **Hint:** ${run.hint.replace(/\n/g, ' ')}` : undefined, '');
214
+ if (run.stderrTail.trim() !== '') {
215
+ parts.push('<details><summary>stderr tail</summary>', '', codeFence(clipLines(run.stderrTail, 50, 5_000), 'text'), '', '</details>', '');
216
+ }
217
+ return toolText(parts.filter((part) => part !== undefined).join('\n'));
218
+ }),
219
+ };
220
+ //# sourceMappingURL=get-failure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-failure.js","sourceRoot":"","sources":["../../src/tools/get-failure.ts"],"names":[],"mappings":"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"}