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,57 @@
1
+ /**
2
+ * Tool 3 — inspect-page: launch a headless browser against a live URL
3
+ * and return the rendered DOM: element inventory with unique CSS
4
+ * selectors, visibility, boxes, text and attributes, optional HTML and
5
+ * captured console messages.
6
+ */
7
+ import { z } from 'zod';
8
+ import type { ToolContext, ToolResponse } from '../types/index.js';
9
+ declare const inspectPageInput: z.ZodObject<{
10
+ url: z.ZodString;
11
+ projectRoot: z.ZodOptional<z.ZodString>;
12
+ selector: z.ZodOptional<z.ZodString>;
13
+ waitFor: z.ZodOptional<z.ZodString>;
14
+ waitUntil: z.ZodOptional<z.ZodEnum<{
15
+ load: "load";
16
+ domcontentloaded: "domcontentloaded";
17
+ networkidle: "networkidle";
18
+ }>>;
19
+ includeHtml: z.ZodOptional<z.ZodBoolean>;
20
+ maxHtmlChars: z.ZodOptional<z.ZodNumber>;
21
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
22
+ }, z.core.$strip>;
23
+ export type InspectPageInput = z.infer<typeof inspectPageInput>;
24
+ export declare const inspectPageSchema: z.ZodObject<{
25
+ url: z.ZodString;
26
+ projectRoot: z.ZodOptional<z.ZodString>;
27
+ selector: z.ZodOptional<z.ZodString>;
28
+ waitFor: z.ZodOptional<z.ZodString>;
29
+ waitUntil: z.ZodOptional<z.ZodEnum<{
30
+ load: "load";
31
+ domcontentloaded: "domcontentloaded";
32
+ networkidle: "networkidle";
33
+ }>>;
34
+ includeHtml: z.ZodOptional<z.ZodBoolean>;
35
+ maxHtmlChars: z.ZodOptional<z.ZodNumber>;
36
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
37
+ }, z.core.$strip>;
38
+ export declare const inspectPageTool: {
39
+ name: string;
40
+ description: string;
41
+ inputSchema: z.ZodObject<{
42
+ url: z.ZodString;
43
+ projectRoot: z.ZodOptional<z.ZodString>;
44
+ selector: z.ZodOptional<z.ZodString>;
45
+ waitFor: z.ZodOptional<z.ZodString>;
46
+ waitUntil: z.ZodOptional<z.ZodEnum<{
47
+ load: "load";
48
+ domcontentloaded: "domcontentloaded";
49
+ networkidle: "networkidle";
50
+ }>>;
51
+ includeHtml: z.ZodOptional<z.ZodBoolean>;
52
+ maxHtmlChars: z.ZodOptional<z.ZodNumber>;
53
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
54
+ }, z.core.$strip>;
55
+ handler: (args: InspectPageInput, ctx: ToolContext) => Promise<ToolResponse>;
56
+ };
57
+ export {};
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Tool 3 — inspect-page: launch a headless browser against a live URL
3
+ * and return the rendered DOM: element inventory with unique CSS
4
+ * selectors, visibility, boxes, text and attributes, optional HTML and
5
+ * captured console messages.
6
+ */
7
+ import { z } from 'zod';
8
+ import { assertHttpUrl, clipLines, guard, resolveProjectRoot, runBrowserScript, toolError, toolText, } from './shared.js';
9
+ const inspectPageInput = z.object({
10
+ url: z.string().describe('Full URL of the page to open (http/https)'),
11
+ projectRoot: z
12
+ .string()
13
+ .optional()
14
+ .describe('Project whose Playwright install launches the browser; defaults to the server working directory'),
15
+ selector: z
16
+ .string()
17
+ .optional()
18
+ .describe('CSS selector: inspect matches instead of the whole DOM'),
19
+ waitFor: z
20
+ .string()
21
+ .optional()
22
+ .describe('Wait for this selector (CSS or "text=...") to appear before inspecting'),
23
+ waitUntil: z
24
+ .enum(['load', 'domcontentloaded', 'networkidle'])
25
+ .optional()
26
+ .describe('Navigation wait condition (default domcontentloaded)'),
27
+ includeHtml: z.boolean().optional().describe('Include the rendered HTML (truncated)'),
28
+ maxHtmlChars: z.number().int().min(1_000).max(200_000).optional().describe('HTML cap when includeHtml is set (default 20000)'),
29
+ timeoutMs: z
30
+ .number()
31
+ .int()
32
+ .min(1_000)
33
+ .max(300_000)
34
+ .optional()
35
+ .describe('Overall inspection limit in ms (default 45000)'),
36
+ });
37
+ export const inspectPageSchema = inspectPageInput;
38
+ function renderElement(element, position) {
39
+ const visibility = element.visible ? 'visible' : 'hidden';
40
+ const box = element.box
41
+ ? `${element.box.width}×${element.box.height} at (${element.box.x},${element.box.y})`
42
+ : 'no box';
43
+ const lines = [
44
+ `${position}. \`${element.selector}\` — **${element.tag}** · ${visibility} · ${box}`,
45
+ ];
46
+ if (element.text)
47
+ lines.push(` text: "${element.text}"`);
48
+ const attrs = Object.entries(element.attributes).filter(([name]) => name !== 'class' && name !== 'id');
49
+ if (attrs.length > 0) {
50
+ const shown = attrs
51
+ .slice(0, 8)
52
+ .map(([name, value]) => `\`${name}="${value.length > 80 ? `${value.slice(0, 80)}…` : value}"\``);
53
+ lines.push(` attrs: ${shown.join(' ')}`);
54
+ }
55
+ return lines.join('\n');
56
+ }
57
+ export const inspectPageTool = {
58
+ name: 'inspect-page',
59
+ description: 'Open a URL in a headless browser and return the rendered DOM: elements with unique CSS selectors, visibility, bounding boxes, text, attributes, captured console messages and optional HTML. Use this to understand a live page before writing or fixing selectors/tests. Requires a reachable URL (detects a missing dev server).',
60
+ inputSchema: inspectPageSchema,
61
+ handler: async (args, ctx) => guard('inspect-page', async () => {
62
+ const url = assertHttpUrl(args.url);
63
+ const root = await resolveProjectRoot(args.projectRoot, ctx);
64
+ const timeoutMs = args.timeoutMs ?? 45_000;
65
+ const outcome = await runBrowserScript({
66
+ mode: 'inspect',
67
+ projectRoot: root,
68
+ url,
69
+ selector: args.selector,
70
+ waitFor: args.waitFor,
71
+ waitUntil: args.waitUntil,
72
+ includeHtml: args.includeHtml,
73
+ maxHtmlChars: args.maxHtmlChars,
74
+ gotoTimeout: Math.max(5_000, timeoutMs - 5_000),
75
+ waitTimeout: Math.min(10_000, Math.max(3_000, timeoutMs - 5_000)),
76
+ startedAt: Date.now(),
77
+ }, { timeoutMs, signal: ctx.signal });
78
+ if (!outcome.ok) {
79
+ return toolError(outcome.kind ?? 'UNKNOWN', `Could not inspect ${url}: ${outcome.error ?? 'unknown error'}`, outcome.hint, outcome.stderrTail);
80
+ }
81
+ const data = outcome.data;
82
+ if (data.parseError) {
83
+ return toolError('INVALID_PATH', `Invalid selector "${args.selector}": ${data.parseError}`, 'inspect-page validates CSS selectors only (what document.querySelectorAll accepts).');
84
+ }
85
+ const lines = [];
86
+ const title = data.title ? `"${data.title}"` : '(no title)';
87
+ lines.push(`## 🔎 ${title}`, '');
88
+ lines.push(`**URL:** ${url}${data.finalUrl && data.finalUrl !== url ? ` → ${data.finalUrl}` : ''} `, `**Elements:** ${data.elementCount} | **Viewport:** ${data.viewport ? `${data.viewport.width}×${data.viewport.height}` : '—'} | **Took:** ${data.durationMs ?? 0}ms`, '');
89
+ const heading = args.selector
90
+ ? `### Matches for \`${args.selector}\` (${data.matchCount})`
91
+ : `### DOM sample (first ${data.elements.length} of ${data.elementCount} elements)`;
92
+ lines.push(heading, '');
93
+ if (data.elements.length === 0) {
94
+ lines.push('_No elements matched._', '');
95
+ }
96
+ else {
97
+ for (let i = 0; i < data.elements.length; i += 1) {
98
+ lines.push(renderElement(data.elements[i], i + 1), '');
99
+ }
100
+ if (data.matchCount > data.elements.length) {
101
+ lines.push(`_…and ${data.matchCount - data.elements.length} more. Narrow with \`selector\`._`, '');
102
+ }
103
+ }
104
+ const errors = (data.consoleMessages ?? []).filter((m) => m.type === 'error' || m.type === 'pageerror');
105
+ const messages = [...errors, ...(data.consoleMessages ?? []).filter((m) => m.type !== 'error' && m.type !== 'pageerror')];
106
+ if (messages.length > 0) {
107
+ lines.push('### Console', '');
108
+ for (const message of messages.slice(0, 20)) {
109
+ lines.push(`- \`${message.type}\`: ${clipLines(message.text, 1, 240)}`);
110
+ }
111
+ if (messages.length > 20)
112
+ lines.push(`- _…${messages.length - 20} more_`);
113
+ lines.push('');
114
+ }
115
+ if (data.html !== undefined) {
116
+ lines.push(`### HTML${data.htmlTruncated ? ` (truncated at ${data.html.length} chars)` : ''}`, '', '```html', data.html, '```', '');
117
+ }
118
+ if (args.selector && data.matchCount > 1) {
119
+ lines.push(`> \`${args.selector}\` matches ${data.matchCount} elements. If you need exactly one, use the first unique selector above.`);
120
+ }
121
+ return toolText(lines.join('\n'));
122
+ }),
123
+ };
124
+ //# sourceMappingURL=inspect-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspect-page.js","sourceRoot":"","sources":["../../src/tools/inspect-page.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Tool 4 — list-tests: list the project's Playwright tests (via
3
+ * `playwright test --list`, falling back to a source scan when
4
+ * Playwright is missing or the project does not compile).
5
+ */
6
+ import { z } from 'zod';
7
+ import type { ToolContext, ToolResponse } from '../types/index.js';
8
+ declare const listTestsInput: z.ZodObject<{
9
+ projectRoot: z.ZodOptional<z.ZodString>;
10
+ config: z.ZodOptional<z.ZodString>;
11
+ testDir: z.ZodOptional<z.ZodString>;
12
+ filter: z.ZodOptional<z.ZodString>;
13
+ limit: z.ZodOptional<z.ZodNumber>;
14
+ }, z.core.$strip>;
15
+ export type ListTestsInput = z.infer<typeof listTestsInput>;
16
+ export declare const listTestsSchema: z.ZodObject<{
17
+ projectRoot: z.ZodOptional<z.ZodString>;
18
+ config: z.ZodOptional<z.ZodString>;
19
+ testDir: z.ZodOptional<z.ZodString>;
20
+ filter: z.ZodOptional<z.ZodString>;
21
+ limit: z.ZodOptional<z.ZodNumber>;
22
+ }, z.core.$strip>;
23
+ export declare const listTestsTool: {
24
+ name: string;
25
+ description: string;
26
+ inputSchema: z.ZodObject<{
27
+ projectRoot: z.ZodOptional<z.ZodString>;
28
+ config: z.ZodOptional<z.ZodString>;
29
+ testDir: z.ZodOptional<z.ZodString>;
30
+ filter: z.ZodOptional<z.ZodString>;
31
+ limit: z.ZodOptional<z.ZodNumber>;
32
+ }, z.core.$strip>;
33
+ handler: (args: ListTestsInput, ctx: ToolContext) => Promise<ToolResponse>;
34
+ };
35
+ export {};
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Tool 4 — list-tests: list the project's Playwright tests (via
3
+ * `playwright test --list`, falling back to a source scan when
4
+ * Playwright is missing or the project does not compile).
5
+ */
6
+ import { z } from 'zod';
7
+ import { detectProject } from '../utils/project-detector.js';
8
+ import { listTests } from '../utils/playwright-runner.js';
9
+ import { isPathInside, normalizePath, sanitizeUserPath, relativeToRoot } from '../utils/path-utils.js';
10
+ import { guard, resolveConfigSelection, resolveProjectRoot, toolText, } from './shared.js';
11
+ const listTestsInput = z.object({
12
+ projectRoot: z.string().optional().describe('Project directory; defaults to the server working directory'),
13
+ config: z
14
+ .string()
15
+ .optional()
16
+ .describe('playwright.config path or 1-based index when the project has several configs'),
17
+ testDir: z.string().optional().describe('Restrict scanning to this directory (inside the project)'),
18
+ filter: z.string().optional().describe('Case-insensitive substring filter on "file › title"'),
19
+ limit: z.number().int().min(1).max(5_000).optional().describe('Max tests to return (default 500)'),
20
+ });
21
+ export const listTestsSchema = listTestsInput;
22
+ function renderTests(result, root) {
23
+ const lines = [];
24
+ const sourceLabel = result.source === 'playwright-list' ? 'Playwright `--list`' : 'source scan (best effort)';
25
+ lines.push(`## 📋 ${result.total} test${result.total === 1 ? '' : 's'} found`, '');
26
+ lines.push(`**Source:** ${sourceLabel}`);
27
+ if (result.testDir)
28
+ lines.push(`**testDir:** \`${relativeToRoot(root, result.testDir)}\``);
29
+ lines.push('');
30
+ if (result.error) {
31
+ lines.push(`> ⚠️ Playwright could not list tests (\`${result.error.kind}\`): ${result.error.message}`, result.error.hint ? `> ${result.error.hint.replace(/\n/g, '\n> ')}` : '', '');
32
+ }
33
+ if (result.total === 0) {
34
+ lines.push('_No tests matched._', '', '- Check `testDir` / the `filter`, or', '- Confirm test files are named `*.spec.ts` / `*.test.ts`.');
35
+ return lines.join('\n');
36
+ }
37
+ const groups = new Map();
38
+ for (const test of result.tests) {
39
+ const group = groups.get(test.file) ?? [];
40
+ group.push(test);
41
+ groups.set(test.file, group);
42
+ }
43
+ for (const [file, tests] of groups) {
44
+ lines.push(`### \`${file}\``);
45
+ for (const test of tests) {
46
+ const projects = test.projects && test.projects.length > 0 ? ` \`${test.projects.join(', ')}\`` : '';
47
+ lines.push(`- ${test.line ? `:${test.line} ` : ''}${test.title}${projects}`);
48
+ }
49
+ lines.push('');
50
+ }
51
+ if (result.truncated) {
52
+ lines.push(`_Showing ${result.tests.length} of ${result.total} — raise \`limit\` or refine \`filter\`._`, '');
53
+ }
54
+ return lines.join('\n');
55
+ }
56
+ export const listTestsTool = {
57
+ name: 'list-tests',
58
+ description: 'List the Playwright tests available in the project (file, line, full title, projects), with optional filtering. Uses `playwright test --list` when Playwright works and falls back to a source scan when the install or config is broken, reporting why.',
59
+ inputSchema: listTestsSchema,
60
+ handler: async (args, ctx) => guard('list-tests', async () => {
61
+ const root = await resolveProjectRoot(args.projectRoot, ctx);
62
+ const detection = await detectProject(root);
63
+ const configPath = resolveConfigSelection(detection, args.config);
64
+ let testDir = detection.testDir;
65
+ if (args.testDir !== undefined) {
66
+ // Throws INVALID_PATH when the path escapes the project root.
67
+ testDir = sanitizeUserPath(args.testDir, root);
68
+ }
69
+ else if (testDir !== null && !isPathInside(testDir, root)) {
70
+ testDir = null;
71
+ }
72
+ const result = await listTests({
73
+ projectRoot: root,
74
+ configPath,
75
+ testDir,
76
+ filter: args.filter,
77
+ limit: args.limit,
78
+ signal: ctx.signal,
79
+ });
80
+ ctx.logger.info('list-tests finished', {
81
+ total: result.total,
82
+ source: result.source,
83
+ errorKind: result.error?.kind,
84
+ });
85
+ return toolText(renderTests(result, normalizePath(root)));
86
+ }),
87
+ };
88
+ //# sourceMappingURL=list-tests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-tests.js","sourceRoot":"","sources":["../../src/tools/list-tests.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Tool 1 — run-test: execute Playwright tests and report structured
3
+ * results (stats, failures, diagnostics, hints).
4
+ */
5
+ import { z } from 'zod';
6
+ import type { ToolContext, ToolResponse } from '../types/index.js';
7
+ declare const runTestInput: z.ZodObject<{
8
+ projectRoot: z.ZodOptional<z.ZodString>;
9
+ testFiles: z.ZodOptional<z.ZodArray<z.ZodString>>;
10
+ grep: z.ZodOptional<z.ZodString>;
11
+ browser: z.ZodOptional<z.ZodEnum<{
12
+ chromium: "chromium";
13
+ firefox: "firefox";
14
+ webkit: "webkit";
15
+ }>>;
16
+ headed: z.ZodOptional<z.ZodBoolean>;
17
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
18
+ testTimeoutMs: z.ZodOptional<z.ZodNumber>;
19
+ workers: z.ZodOptional<z.ZodNumber>;
20
+ retries: z.ZodOptional<z.ZodNumber>;
21
+ retryOnFailure: z.ZodOptional<z.ZodBoolean>;
22
+ lastFailed: z.ZodOptional<z.ZodBoolean>;
23
+ config: z.ZodOptional<z.ZodString>;
24
+ args: z.ZodOptional<z.ZodArray<z.ZodString>>;
25
+ }, z.core.$strip>;
26
+ export type RunTestInput = z.infer<typeof runTestInput>;
27
+ export declare const runTestSchema: z.ZodObject<{
28
+ projectRoot: z.ZodOptional<z.ZodString>;
29
+ testFiles: z.ZodOptional<z.ZodArray<z.ZodString>>;
30
+ grep: z.ZodOptional<z.ZodString>;
31
+ browser: z.ZodOptional<z.ZodEnum<{
32
+ chromium: "chromium";
33
+ firefox: "firefox";
34
+ webkit: "webkit";
35
+ }>>;
36
+ headed: z.ZodOptional<z.ZodBoolean>;
37
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
38
+ testTimeoutMs: z.ZodOptional<z.ZodNumber>;
39
+ workers: z.ZodOptional<z.ZodNumber>;
40
+ retries: z.ZodOptional<z.ZodNumber>;
41
+ retryOnFailure: z.ZodOptional<z.ZodBoolean>;
42
+ lastFailed: z.ZodOptional<z.ZodBoolean>;
43
+ config: z.ZodOptional<z.ZodString>;
44
+ args: z.ZodOptional<z.ZodArray<z.ZodString>>;
45
+ }, z.core.$strip>;
46
+ export declare const runTestTool: {
47
+ name: string;
48
+ description: string;
49
+ inputSchema: z.ZodObject<{
50
+ projectRoot: z.ZodOptional<z.ZodString>;
51
+ testFiles: z.ZodOptional<z.ZodArray<z.ZodString>>;
52
+ grep: z.ZodOptional<z.ZodString>;
53
+ browser: z.ZodOptional<z.ZodEnum<{
54
+ chromium: "chromium";
55
+ firefox: "firefox";
56
+ webkit: "webkit";
57
+ }>>;
58
+ headed: z.ZodOptional<z.ZodBoolean>;
59
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
60
+ testTimeoutMs: z.ZodOptional<z.ZodNumber>;
61
+ workers: z.ZodOptional<z.ZodNumber>;
62
+ retries: z.ZodOptional<z.ZodNumber>;
63
+ retryOnFailure: z.ZodOptional<z.ZodBoolean>;
64
+ lastFailed: z.ZodOptional<z.ZodBoolean>;
65
+ config: z.ZodOptional<z.ZodString>;
66
+ args: z.ZodOptional<z.ZodArray<z.ZodString>>;
67
+ }, z.core.$strip>;
68
+ handler: (args: RunTestInput, ctx: ToolContext) => Promise<ToolResponse>;
69
+ };
70
+ export {};
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Tool 1 — run-test: execute Playwright tests and report structured
3
+ * results (stats, failures, diagnostics, hints).
4
+ */
5
+ import { z } from 'zod';
6
+ import { PlaywrightMcpError } from '../types/index.js';
7
+ import { detectProject, missingPlaywrightMessage } from '../utils/project-detector.js';
8
+ import { runTests } from '../utils/playwright-runner.js';
9
+ import { normalizePath, relativeToRoot } from '../utils/path-utils.js';
10
+ import { codeFence, clipLines, formatDuration, guard, renderFailure, resolveConfigSelection, resolveProjectRoot, sanitizeTestPathArgument, testPathExists, toolError, toolText, } from './shared.js';
11
+ const runTestInput = z.object({
12
+ projectRoot: z
13
+ .string()
14
+ .optional()
15
+ .describe('Project directory; defaults to the server working directory'),
16
+ testFiles: z
17
+ .array(z.string())
18
+ .optional()
19
+ .describe('Test files/directories relative to the project root; "file:line" is supported. Omit to run the whole suite.'),
20
+ grep: z.string().optional().describe('Only run tests whose title matches this regex'),
21
+ browser: z
22
+ .enum(['chromium', 'firefox', 'webkit'])
23
+ .optional()
24
+ .describe('Playwright project name to run (matched against playwright.config projects)'),
25
+ headed: z.boolean().optional().describe('Run with a visible browser window'),
26
+ timeoutMs: z
27
+ .number()
28
+ .int()
29
+ .min(1_000)
30
+ .max(3_600_000)
31
+ .optional()
32
+ .describe('Hard wall-clock limit for the whole run; the process tree is killed past it (default 120000)'),
33
+ testTimeoutMs: z
34
+ .number()
35
+ .int()
36
+ .min(1_000)
37
+ .max(3_600_000)
38
+ .optional()
39
+ .describe('Per-test timeout passed to Playwright'),
40
+ workers: z.number().int().min(1).max(64).optional(),
41
+ retries: z.number().int().min(0).max(10).optional(),
42
+ retryOnFailure: z
43
+ .boolean()
44
+ .optional()
45
+ .describe('Auto retry failing tests once before reporting failure (default true; ignored when retries is set)'),
46
+ lastFailed: z
47
+ .boolean()
48
+ .optional()
49
+ .describe('Only re-run tests that failed in the previous run (Playwright --last-failed) — the fast fix → re-run loop'),
50
+ config: z
51
+ .string()
52
+ .optional()
53
+ .describe('playwright.config path or 1-based index when the project has several configs'),
54
+ args: z
55
+ .array(z.string())
56
+ .optional()
57
+ .describe('Extra Playwright CLI arguments (shell metacharacters are rejected)'),
58
+ });
59
+ export const runTestSchema = runTestInput;
60
+ function renderRunResult(result, root) {
61
+ const flakyCount = result.stats?.flaky ?? 0;
62
+ const status = result.ok
63
+ ? flakyCount > 0
64
+ ? `✅ PASSED (${flakyCount} flaky)`
65
+ : '✅ PASSED'
66
+ : result.timedOut
67
+ ? '⏱️ KILLED (timeout)'
68
+ : '❌ FAILED';
69
+ const lines = [`## Playwright run — ${status}`, ''];
70
+ lines.push(`**Command:** \`${result.command}\``);
71
+ const meta = [
72
+ `duration ${formatDuration(result.durationMs)}`,
73
+ `exit ${result.exitCode ?? '—'}`,
74
+ ];
75
+ if (result.configPath)
76
+ meta.push(`config \`${relativeToRoot(root, result.configPath)}\``);
77
+ if (result.autoRetry)
78
+ meta.push('auto retry ×1');
79
+ if (result.lastFailed)
80
+ meta.push('failed tests only (--last-failed)');
81
+ if (result.timedOut)
82
+ meta.push('killed at wrapper timeout');
83
+ if (result.partial)
84
+ meta.push('partial results');
85
+ lines.push(`**${meta.join(' · ')}**`, '');
86
+ if (result.stats) {
87
+ const s = result.stats;
88
+ lines.push('| passed | failed | flaky | skipped | duration |', '| ---: | ---: | ---: | ---: | ---: |', `| ${s.expected} | ${s.unexpected} | ${s.flaky} | ${s.skipped} | ${formatDuration(s.duration)} |`, '');
89
+ }
90
+ else {
91
+ lines.push('_No test statistics were reported._', '');
92
+ }
93
+ if (flakyCount > 0) {
94
+ lines.push(`> ⚠️ **${flakyCount} flaky test(s)** failed first but passed on the automatic retry. Re-run to confirm they are stable before trusting them.`, '');
95
+ }
96
+ if (result.failures.length > 0) {
97
+ const more = result.failuresTruncated ? ' (showing the first ones)' : '';
98
+ lines.push(`### ❌ ${result.failures.length} failing test(s)${more}`, '');
99
+ for (let i = 0; i < result.failures.length; i += 1) {
100
+ lines.push(renderFailure(result.failures[i], i, result.failures.length), '');
101
+ }
102
+ lines.push('> Next: call **get-failure** with `index` to get the full stack, code frame and a suggested fix for one failure.', '');
103
+ }
104
+ if (result.errorKind) {
105
+ lines.push(`### ⚠️ ${result.errorKind}`, '', result.errorMessage ?? '', '');
106
+ if (result.hint)
107
+ lines.push(result.hint, '');
108
+ }
109
+ else if (result.hint && result.hint.trim() !== '') {
110
+ lines.push(`> ${result.hint.replace(/\n/g, '\n> ')}`, '');
111
+ }
112
+ if (!result.ok && result.stderrTail.trim() !== '') {
113
+ lines.push('<details><summary>stderr tail</summary>', '', codeFence(clipLines(result.stderrTail, 40), 'text'), '', '</details>', '');
114
+ }
115
+ return lines.join('\n');
116
+ }
117
+ export const runTestTool = {
118
+ name: 'run-test',
119
+ description: 'Run Playwright end-to-end tests in the user\'s project and return structured results: pass/fail stats, per-failure messages with file:line, and diagnostics. Detects missing Playwright installs, multiple configs, dead dev servers, syntax errors, browser crashes, timeouts (kills the process tree and returns partial results) and full disks.',
120
+ inputSchema: runTestSchema,
121
+ handler: async (args, ctx) => guard('run-test', async () => {
122
+ const root = await resolveProjectRoot(args.projectRoot, ctx);
123
+ const detection = await detectProject(root);
124
+ if (!detection.hasPlaywright) {
125
+ const { message, hint } = missingPlaywrightMessage(detection);
126
+ return toolError('NO_PLAYWRIGHT', message, hint);
127
+ }
128
+ const configPath = resolveConfigSelection(detection, args.config);
129
+ const sanitized = [];
130
+ const missing = [];
131
+ for (const raw of args.testFiles ?? []) {
132
+ const absolute = sanitizeTestPathArgument(raw, root);
133
+ sanitized.push(absolute);
134
+ if (!(await testPathExists(absolute, root)))
135
+ missing.push(raw);
136
+ }
137
+ if (missing.length > 0) {
138
+ throw new PlaywrightMcpError(`Test path(s) not found: ${missing.join(', ')}`, 'INVALID_PATH', {
139
+ hint: `Paths resolve inside ${normalizePath(root)}. Call list-tests to see the available tests.`,
140
+ });
141
+ }
142
+ const options = {
143
+ projectRoot: root,
144
+ configPath,
145
+ testFiles: sanitized,
146
+ grep: args.grep,
147
+ browser: args.browser,
148
+ headed: args.headed,
149
+ timeoutMs: args.timeoutMs,
150
+ testTimeoutMs: args.testTimeoutMs,
151
+ workers: args.workers,
152
+ retries: args.retries,
153
+ retryOnFailure: args.retryOnFailure,
154
+ lastFailed: args.lastFailed,
155
+ extraArgs: args.args,
156
+ signal: ctx.signal,
157
+ };
158
+ const result = await runTests(options);
159
+ ctx.store.setLastRun({ projectRoot: root, result, at: Date.now() });
160
+ ctx.logger.info('run-test finished', {
161
+ ok: result.ok,
162
+ exitCode: result.exitCode,
163
+ durationMs: result.durationMs,
164
+ failures: result.failures.length,
165
+ });
166
+ return toolText(renderRunResult(result, root));
167
+ }),
168
+ };
169
+ //# sourceMappingURL=run-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-test.js","sourceRoot":"","sources":["../../src/tools/run-test.ts"],"names":[],"mappings":"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"}