playwright-e2e-mcp 0.5.0 → 0.6.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.
- package/README.md +267 -1060
- package/dist/cli/ci.d.ts +5 -0
- package/dist/cli/ci.js +97 -5
- package/dist/cli/ci.js.map +1 -1
- package/dist/cli/doctor.d.ts +31 -0
- package/dist/cli/doctor.js +200 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/init.js +2 -1
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/review.d.ts +50 -0
- package/dist/cli/review.js +148 -0
- package/dist/cli/review.js.map +1 -0
- package/dist/http.d.ts +15 -1
- package/dist/http.js +44 -8
- package/dist/http.js.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/oauth.d.ts +10 -0
- package/dist/oauth.js +29 -1
- package/dist/oauth.js.map +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +35 -3
- package/dist/server.js.map +1 -1
- package/dist/tools/audit-accessibility.d.ts +21 -0
- package/dist/tools/audit-accessibility.js +8 -1
- package/dist/tools/audit-accessibility.js.map +1 -1
- package/dist/tools/audit-performance.d.ts +21 -0
- package/dist/tools/audit-performance.js +13 -1
- package/dist/tools/audit-performance.js.map +1 -1
- package/dist/tools/audit-tests.d.ts +62 -0
- package/dist/tools/audit-tests.js +209 -0
- package/dist/tools/audit-tests.js.map +1 -0
- package/dist/tools/bisect-failure.d.ts +42 -0
- package/dist/tools/bisect-failure.js +291 -0
- package/dist/tools/bisect-failure.js.map +1 -0
- package/dist/tools/compare-visual-state.d.ts +21 -0
- package/dist/tools/diagnose-flaky.js +20 -3
- package/dist/tools/diagnose-flaky.js.map +1 -1
- package/dist/tools/get-failure.js +11 -2
- package/dist/tools/get-failure.js.map +1 -1
- package/dist/tools/get-run-status.js +4 -3
- package/dist/tools/get-run-status.js.map +1 -1
- package/dist/tools/inspect-page.d.ts +21 -0
- package/dist/tools/list-tests.js +5 -1
- package/dist/tools/list-tests.js.map +1 -1
- package/dist/tools/probe-script.js +33 -1
- package/dist/tools/probe-script.js.map +1 -1
- package/dist/tools/run-test.js +3 -2
- package/dist/tools/run-test.js.map +1 -1
- package/dist/tools/shared.d.ts +17 -1
- package/dist/tools/shared.js +69 -5
- package/dist/tools/shared.js.map +1 -1
- package/dist/tools/structured.d.ts +326 -0
- package/dist/tools/structured.js +170 -0
- package/dist/tools/structured.js.map +1 -0
- package/dist/tools/suggest-fix.js +14 -4
- package/dist/tools/suggest-fix.js.map +1 -1
- package/dist/tools/validate-selector.d.ts +21 -0
- package/dist/utils/attempt-limiter.d.ts +31 -0
- package/dist/utils/attempt-limiter.js +58 -0
- package/dist/utils/attempt-limiter.js.map +1 -0
- package/dist/utils/bisect.d.ts +31 -0
- package/dist/utils/bisect.js +47 -0
- package/dist/utils/bisect.js.map +1 -0
- package/dist/utils/flake-rules.d.ts +46 -0
- package/dist/utils/flake-rules.js +327 -0
- package/dist/utils/flake-rules.js.map +1 -0
- package/dist/utils/project-detector.js +2 -2
- package/dist/utils/project-detector.js.map +1 -1
- package/dist/utils/serverless-chromium.d.ts +32 -0
- package/dist/utils/serverless-chromium.js +64 -0
- package/dist/utils/serverless-chromium.js.map +1 -0
- package/package.json +7 -5
- package/skill/playwright-e2e-mcp/SKILL.md +14 -2
- package/skill/playwright-e2e-mcp/references/setup.md +2 -2
- package/skill/playwright-e2e-mcp/references/tools.md +32 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool — audit-tests: find the code in a Playwright suite that makes tests
|
|
3
|
+
* flaky (hard waits, one-shot assertions, promises nobody awaits, `.only`,
|
|
4
|
+
* layout selectors…) and rank it by how unstable those tests have actually
|
|
5
|
+
* been in the local run history. With `fix: true` it rewrites the
|
|
6
|
+
* mechanical cases (web-first assertions, missing awaits, `.only`), re-runs
|
|
7
|
+
* the files it touched, and restores them if the run fails.
|
|
8
|
+
*/
|
|
9
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import { PlaywrightMcpError } from '../types/index.js';
|
|
12
|
+
import { applyFlakeFixes, scanFlakeRisks, SEVERITY_WEIGHT } from '../utils/flake-rules.js';
|
|
13
|
+
import { joinProjectPath, isPathInside, sanitizeUserPath, toNativePath, writeFileInsideRoot } from '../utils/path-utils.js';
|
|
14
|
+
import { runTests } from '../utils/playwright-runner.js';
|
|
15
|
+
import { detectProject, findTestFiles } from '../utils/project-detector.js';
|
|
16
|
+
import { readHistory, recordRun, testStats } from '../utils/run-history.js';
|
|
17
|
+
import { clipLines, codeFence, formatDuration, guard, markdownCell, resolveConfigSelection, resolveProjectRoot, toolText, } from './shared.js';
|
|
18
|
+
const MAX_FILE_BYTES = 1_000_000;
|
|
19
|
+
const auditTestsInput = z.object({
|
|
20
|
+
projectRoot: z.string().optional().describe('Project directory; defaults to the server working directory'),
|
|
21
|
+
testDir: z.string().optional().describe('Audit only this directory (inside the project); defaults to the config testDir'),
|
|
22
|
+
filter: z.string().optional().describe('Case-insensitive substring filter on the file path'),
|
|
23
|
+
minSeverity: z.enum(['low', 'medium', 'high']).optional().describe('Report findings at or above this severity (default low)'),
|
|
24
|
+
historyRuns: z.number().int().min(1).max(1_000).optional().describe('How many recent runs of the local history to weigh in (default 50)'),
|
|
25
|
+
limit: z.number().int().min(1).max(500).optional().describe('Max findings to list (default 50); the summary counts all'),
|
|
26
|
+
fix: z
|
|
27
|
+
.boolean()
|
|
28
|
+
.optional()
|
|
29
|
+
.describe('Rewrite the mechanical findings (web-first assertions, missing awaits, .only), re-run the touched files and restore them if the run fails'),
|
|
30
|
+
verify: z.boolean().optional().describe('With fix: re-run the touched files (default true)'),
|
|
31
|
+
config: z.string().optional().describe('playwright.config path or 1-based index, for the verify run'),
|
|
32
|
+
timeoutMs: z.number().int().min(1_000).max(1_800_000).optional().describe('Wall-clock limit for the verify run'),
|
|
33
|
+
});
|
|
34
|
+
const RANK = { low: 0, medium: 1, high: 2 };
|
|
35
|
+
function sameFile(historyFile, file) {
|
|
36
|
+
return historyFile === file || file.endsWith(`/${historyFile}`) || historyFile.endsWith(`/${file}`);
|
|
37
|
+
}
|
|
38
|
+
/** Group findings by file and weigh each file by its history: code smells in tests that already flake come first. */
|
|
39
|
+
export function rankFileRisks(findings, stats) {
|
|
40
|
+
const byFile = new Map();
|
|
41
|
+
for (const finding of findings)
|
|
42
|
+
byFile.set(finding.file, [...(byFile.get(finding.file) ?? []), finding]);
|
|
43
|
+
const out = [];
|
|
44
|
+
for (const [file, list] of byFile) {
|
|
45
|
+
const mine = stats.filter((s) => sameFile(s.file, file));
|
|
46
|
+
const unstable = mine.filter((s) => s.flaky > 0 || s.flips >= 2).sort((a, b) => b.flipRate - a.flipRate);
|
|
47
|
+
const failRate = mine.reduce((worst, s) => Math.max(worst, s.failRate), 0);
|
|
48
|
+
const base = list.reduce((sum, f) => sum + SEVERITY_WEIGHT[f.severity], 0);
|
|
49
|
+
out.push({ file, findings: list, unstable, failRate, score: Math.round(base * (1 + 3 * failRate) * 10) / 10 });
|
|
50
|
+
}
|
|
51
|
+
return out.sort((a, b) => b.score - a.score || a.file.localeCompare(b.file));
|
|
52
|
+
}
|
|
53
|
+
/** True when the finding's test flaked or flipped in the history. */
|
|
54
|
+
function flakedBefore(finding, risk) {
|
|
55
|
+
return finding.test !== undefined && risk.unstable.some((s) => s.title === finding.test || s.title.endsWith(` › ${finding.test}`));
|
|
56
|
+
}
|
|
57
|
+
const ICON = { high: '🔴', medium: '🟠', low: '🟡' };
|
|
58
|
+
function counts(findings) {
|
|
59
|
+
const n = (sev) => findings.filter((f) => f.severity === sev).length;
|
|
60
|
+
return `${n('high')} / ${n('medium')} / ${n('low')}`;
|
|
61
|
+
}
|
|
62
|
+
function renderAudit(risks, scanned, historyRuns, limit) {
|
|
63
|
+
const all = risks.flatMap((r) => r.findings);
|
|
64
|
+
const lines = [];
|
|
65
|
+
if (all.length === 0) {
|
|
66
|
+
lines.push(`## ✅ No flake risks found in ${scanned} spec file${scanned === 1 ? '' : 's'}`);
|
|
67
|
+
return { markdown: lines.join('\n'), shown: [] };
|
|
68
|
+
}
|
|
69
|
+
const fixable = all.filter((f) => f.fix !== undefined).length;
|
|
70
|
+
lines.push(`## 🧪 ${all.length} flake risk${all.length === 1 ? '' : 's'} in ${risks.length} of ${scanned} spec file${scanned === 1 ? '' : 's'}`, '', historyRuns > 0
|
|
71
|
+
? `Ranked by severity and by how the tests behaved in the last ${historyRuns} recorded run${historyRuns === 1 ? '' : 's'}.`
|
|
72
|
+
: 'No run history yet, so ranked by severity alone. Runs through run-test (or load-report in CI) build it up.', fixable > 0 ? `**${fixable}** can be fixed mechanically: call again with \`fix: true\` to apply them and re-run the files.` : '', '', '| File | 🔴 / 🟠 / 🟡 | History | Risk |', '|---|---|---|---|');
|
|
73
|
+
for (const risk of risks.slice(0, 20)) {
|
|
74
|
+
const history = risk.unstable.length > 0
|
|
75
|
+
? `${risk.unstable.length} flaky test${risk.unstable.length === 1 ? '' : 's'}, worst ${Math.round(risk.failRate * 100)}% failing`
|
|
76
|
+
: risk.failRate > 0
|
|
77
|
+
? `${Math.round(risk.failRate * 100)}% failing`
|
|
78
|
+
: '—';
|
|
79
|
+
lines.push(`| \`${markdownCell(risk.file)}\` | ${counts(risk.findings)} | ${history} | ${risk.score} |`);
|
|
80
|
+
}
|
|
81
|
+
if (risks.length > 20)
|
|
82
|
+
lines.push(`| _…${risks.length - 20} more files_ | | | |`);
|
|
83
|
+
lines.push('');
|
|
84
|
+
// Findings: the riskiest files first, severity first inside a file.
|
|
85
|
+
const ordered = risks.flatMap((risk) => [...risk.findings].sort((a, b) => RANK[b.severity] - RANK[a.severity] || a.line - b.line).map((f) => ({ f, risk })));
|
|
86
|
+
const shown = ordered.slice(0, limit);
|
|
87
|
+
lines.push('### Findings', '');
|
|
88
|
+
for (const { f, risk } of shown) {
|
|
89
|
+
const flaked = flakedBefore(f, risk) ? ' · 🔥 flaky in history' : '';
|
|
90
|
+
lines.push(`- ${ICON[f.severity]} **${f.rule}** \`${f.file}:${f.line}\`${f.test ? ` in “${f.test}”` : ''}${flaked}`);
|
|
91
|
+
lines.push(` \`${clipLines(f.code, 1, 160)}\``);
|
|
92
|
+
lines.push(` ${f.message}`);
|
|
93
|
+
if (f.fix !== undefined)
|
|
94
|
+
lines.push(` Fix: \`${clipLines(f.fix.trim(), 1, 160)}\``);
|
|
95
|
+
}
|
|
96
|
+
if (ordered.length > shown.length)
|
|
97
|
+
lines.push('', `_Showing ${shown.length} of ${ordered.length}. Raise \`limit\`, set \`minSeverity\` or \`filter\`._`);
|
|
98
|
+
return { markdown: lines.join('\n'), shown: shown.map(({ f }) => f) };
|
|
99
|
+
}
|
|
100
|
+
function findingData(f) {
|
|
101
|
+
return {
|
|
102
|
+
file: f.file,
|
|
103
|
+
line: f.line,
|
|
104
|
+
rule: f.rule,
|
|
105
|
+
severity: f.severity,
|
|
106
|
+
message: f.message,
|
|
107
|
+
...(f.fix !== undefined ? { fix: f.fix } : {}),
|
|
108
|
+
...(f.test ? { test: f.test } : {}),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export const auditTestsTool = {
|
|
112
|
+
name: 'audit-tests',
|
|
113
|
+
description: 'Audit the Playwright test code for flake risks (hard waits, one-shot assertions instead of web-first ones, missing awaits, .only, force clicks, networkidle, element handles, layout selectors) and rank files by how flaky their tests have been in the run history. fix: true rewrites the mechanical ones, re-runs the touched files and restores them if the run fails.',
|
|
114
|
+
inputSchema: auditTestsInput,
|
|
115
|
+
handler: async (args, ctx) => guard('audit-tests', async () => {
|
|
116
|
+
const root = await resolveProjectRoot(args.projectRoot, ctx);
|
|
117
|
+
if (args.fix && ctx.restricted) {
|
|
118
|
+
throw new PlaywrightMcpError('fix is not available on this server', 'INVALID_PATH', {
|
|
119
|
+
hint: 'Run the audit without fix, or use a local install to apply fixes.',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
const detection = await detectProject(root);
|
|
123
|
+
let testDir = detection.testDir;
|
|
124
|
+
if (args.testDir !== undefined)
|
|
125
|
+
testDir = sanitizeUserPath(args.testDir, root);
|
|
126
|
+
else if (testDir !== null && !isPathInside(testDir, root))
|
|
127
|
+
testDir = null;
|
|
128
|
+
const wanted = args.filter?.toLowerCase();
|
|
129
|
+
const files = (await findTestFiles(root, { testDir, limit: 2_000 })).filter((f) => !wanted || f.toLowerCase().includes(wanted));
|
|
130
|
+
const minRank = RANK[args.minSeverity ?? 'low'];
|
|
131
|
+
const sources = new Map();
|
|
132
|
+
const findings = [];
|
|
133
|
+
for (const file of files) {
|
|
134
|
+
const full = joinProjectPath(root, file);
|
|
135
|
+
const info = await stat(toNativePath(full)).catch(() => null);
|
|
136
|
+
if (!info || info.size > MAX_FILE_BYTES)
|
|
137
|
+
continue;
|
|
138
|
+
const source = await readFile(toNativePath(full), 'utf8');
|
|
139
|
+
sources.set(file, source);
|
|
140
|
+
findings.push(...scanFlakeRisks(file, source).filter((f) => RANK[f.severity] >= minRank));
|
|
141
|
+
}
|
|
142
|
+
const history = await readHistory(root, args.historyRuns ?? 50).catch(() => []);
|
|
143
|
+
const risks = rankFileRisks(findings, testStats(history));
|
|
144
|
+
const { markdown, shown } = renderAudit(risks, files.length, history.length, args.limit ?? 50);
|
|
145
|
+
ctx.logger.info('audit-tests finished', { files: files.length, findings: findings.length, historyRuns: history.length });
|
|
146
|
+
const data = { findings: shown.map(findingData), total: findings.length, truncated: shown.length < findings.length };
|
|
147
|
+
if (!args.fix)
|
|
148
|
+
return toolText(markdown, data);
|
|
149
|
+
// Fix: rewrite each file's mechanical findings in one pass.
|
|
150
|
+
const out = [markdown, '', '## 🔧 Fixes'];
|
|
151
|
+
const touched = new Map();
|
|
152
|
+
const applied = [];
|
|
153
|
+
for (const risk of risks) {
|
|
154
|
+
const original = sources.get(risk.file);
|
|
155
|
+
if (original === undefined)
|
|
156
|
+
continue;
|
|
157
|
+
const result = applyFlakeFixes(original, risk.findings);
|
|
158
|
+
if (result.applied.length === 0)
|
|
159
|
+
continue;
|
|
160
|
+
const full = joinProjectPath(root, risk.file);
|
|
161
|
+
if ((await readFile(toNativePath(full), 'utf8')) !== original)
|
|
162
|
+
continue;
|
|
163
|
+
await writeFileInsideRoot(full, result.source, root);
|
|
164
|
+
touched.set(risk.file, original);
|
|
165
|
+
applied.push(...result.applied);
|
|
166
|
+
}
|
|
167
|
+
if (applied.length === 0) {
|
|
168
|
+
out.push('', 'Nothing to fix mechanically: the findings above need a judgement call (what to wait for instead).');
|
|
169
|
+
return toolText(out.join('\n'), { ...data, applied: false });
|
|
170
|
+
}
|
|
171
|
+
out.push('', `Applied ${applied.length} fix${applied.length === 1 ? '' : 'es'} in ${touched.size} file${touched.size === 1 ? '' : 's'}: ${[...touched.keys()].map((f) => `\`${f}\``).join(', ')}.`);
|
|
172
|
+
const restore = async () => {
|
|
173
|
+
for (const [file, original] of touched)
|
|
174
|
+
await writeFileInsideRoot(joinProjectPath(root, file), original, root);
|
|
175
|
+
};
|
|
176
|
+
if (args.verify === false) {
|
|
177
|
+
out.push('', '> Not verified (verify: false). Re-run the files with **run-test** to check them.');
|
|
178
|
+
return toolText(out.join('\n'), { ...data, applied: true });
|
|
179
|
+
}
|
|
180
|
+
let result;
|
|
181
|
+
try {
|
|
182
|
+
result = await runTests({
|
|
183
|
+
projectRoot: root,
|
|
184
|
+
configPath: resolveConfigSelection(detection, args.config),
|
|
185
|
+
testFiles: [...touched.keys()],
|
|
186
|
+
retries: 0,
|
|
187
|
+
retryOnFailure: false,
|
|
188
|
+
timeoutMs: args.timeoutMs,
|
|
189
|
+
signal: ctx.signal,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
await restore();
|
|
194
|
+
throw err;
|
|
195
|
+
}
|
|
196
|
+
ctx.store.setLastRun({ projectRoot: root, result, at: Date.now() });
|
|
197
|
+
await recordRun(root, result, 'audit-tests').catch(() => undefined);
|
|
198
|
+
if (result.ok) {
|
|
199
|
+
out.push('', `### ✅ Verified: the touched files pass with the fixes (${formatDuration(result.durationMs)}).`);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
await restore();
|
|
203
|
+
const first = result.failures[0];
|
|
204
|
+
out.push('', '### ❌ The touched files failed, so every fix was reverted', '', first ? `\`${first.file}${first.line ? `:${first.line}` : ''}\` ${first.title}` : '', first ? codeFence(clipLines(first.message, 8, 1_200), 'text') : (result.errorMessage ?? 'The run failed without a test failure.'), '', '> If these tests failed before the fixes too, fix that first (run-test, get-failure), then audit again.');
|
|
205
|
+
}
|
|
206
|
+
return toolText(out.join('\n'), { ...data, ok: result.ok, applied: result.ok, verified: result.ok });
|
|
207
|
+
}),
|
|
208
|
+
};
|
|
209
|
+
//# sourceMappingURL=audit-tests.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-tests.js","sourceRoot":"","sources":["../../src/tools/audit-tests.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool — bisect-failure: find the commit that broke a test. It checks the
|
|
3
|
+
* project out at commits between a good and a bad one in a temporary git
|
|
4
|
+
* worktree (the caller's checkout is never touched), runs the test there,
|
|
5
|
+
* and binary-searches to the first commit where it fails, then reports
|
|
6
|
+
* that commit, what it changed and how the test failed.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import type { ToolContext, ToolResponse } from '../types/index.js';
|
|
10
|
+
declare const bisectInput: z.ZodObject<{
|
|
11
|
+
projectRoot: z.ZodOptional<z.ZodString>;
|
|
12
|
+
test: z.ZodString;
|
|
13
|
+
grep: z.ZodOptional<z.ZodString>;
|
|
14
|
+
project: z.ZodOptional<z.ZodString>;
|
|
15
|
+
good: z.ZodOptional<z.ZodString>;
|
|
16
|
+
bad: z.ZodOptional<z.ZodString>;
|
|
17
|
+
repeat: z.ZodOptional<z.ZodNumber>;
|
|
18
|
+
maxSteps: z.ZodOptional<z.ZodNumber>;
|
|
19
|
+
stepTimeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
20
|
+
config: z.ZodOptional<z.ZodString>;
|
|
21
|
+
}, z.core.$strip>;
|
|
22
|
+
export type BisectFailureInput = z.infer<typeof bisectInput>;
|
|
23
|
+
/** A ref the caller passed, checked before it reaches git (no options, no odd characters). */
|
|
24
|
+
export declare function checkRef(ref: string, label: string): string;
|
|
25
|
+
export declare const bisectFailureTool: {
|
|
26
|
+
name: string;
|
|
27
|
+
description: string;
|
|
28
|
+
inputSchema: z.ZodObject<{
|
|
29
|
+
projectRoot: z.ZodOptional<z.ZodString>;
|
|
30
|
+
test: z.ZodString;
|
|
31
|
+
grep: z.ZodOptional<z.ZodString>;
|
|
32
|
+
project: z.ZodOptional<z.ZodString>;
|
|
33
|
+
good: z.ZodOptional<z.ZodString>;
|
|
34
|
+
bad: z.ZodOptional<z.ZodString>;
|
|
35
|
+
repeat: z.ZodOptional<z.ZodNumber>;
|
|
36
|
+
maxSteps: z.ZodOptional<z.ZodNumber>;
|
|
37
|
+
stepTimeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
38
|
+
config: z.ZodOptional<z.ZodString>;
|
|
39
|
+
}, z.core.$strip>;
|
|
40
|
+
handler: (args: BisectFailureInput, ctx: ToolContext) => Promise<ToolResponse>;
|
|
41
|
+
};
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool — bisect-failure: find the commit that broke a test. It checks the
|
|
3
|
+
* project out at commits between a good and a bad one in a temporary git
|
|
4
|
+
* worktree (the caller's checkout is never touched), runs the test there,
|
|
5
|
+
* and binary-searches to the first commit where it fails, then reports
|
|
6
|
+
* that commit, what it changed and how the test failed.
|
|
7
|
+
*/
|
|
8
|
+
import { execFile } from 'node:child_process';
|
|
9
|
+
import { existsSync } from 'node:fs';
|
|
10
|
+
import { mkdtemp, rm, symlink } from 'node:fs/promises';
|
|
11
|
+
import os from 'node:os';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
import { PlaywrightMcpError } from '../types/index.js';
|
|
15
|
+
import { bisectCommits } from '../utils/bisect.js';
|
|
16
|
+
import { normalizePath, relativeToRoot, toNativePath } from '../utils/path-utils.js';
|
|
17
|
+
import { runTests } from '../utils/playwright-runner.js';
|
|
18
|
+
import { detectProject } from '../utils/project-detector.js';
|
|
19
|
+
import { localGitInfo, readHistory } from '../utils/run-history.js';
|
|
20
|
+
import { clipLines, codeFence, formatDuration, guard, markdownCell, resolveConfigSelection, resolveProjectRoot, sanitizeTestPathArgument, toolText, } from './shared.js';
|
|
21
|
+
const LOCKFILES = ['package-lock.json', 'pnpm-lock.yaml', 'yarn.lock', 'bun.lock', 'bun.lockb'];
|
|
22
|
+
const bisectInput = z.object({
|
|
23
|
+
projectRoot: z.string().optional().describe('Project directory (inside a git repository); defaults to the server working directory'),
|
|
24
|
+
test: z.string().min(1).describe('The failing spec file, e.g. "tests/cart.spec.ts" (a :line suffix also works, but lines move between commits; prefer grep)'),
|
|
25
|
+
grep: z.string().optional().describe('Test title (or part of it) to run inside the file; keeps the search on one test'),
|
|
26
|
+
project: z.string().optional().describe('Playwright project name to run in'),
|
|
27
|
+
good: z
|
|
28
|
+
.string()
|
|
29
|
+
.optional()
|
|
30
|
+
.describe('A commit or ref where the test passed. Defaults to the newest commit in the run history where it passed'),
|
|
31
|
+
bad: z.string().optional().describe('A commit or ref where it fails (default HEAD)'),
|
|
32
|
+
repeat: z.number().int().min(1).max(10).optional().describe('Run the test this many times per commit; any failure marks the commit bad (default 1; raise it for a flaky failure)'),
|
|
33
|
+
maxSteps: z.number().int().min(1).max(30).optional().describe('Most commits to test besides the two ends (default 12, enough for 4,000 commits)'),
|
|
34
|
+
stepTimeoutMs: z.number().int().min(5_000).max(1_800_000).optional().describe('Time limit for each test run (default 180000)'),
|
|
35
|
+
config: z.string().optional().describe('playwright.config path or 1-based index'),
|
|
36
|
+
});
|
|
37
|
+
function git(args, cwd, timeout = 60_000) {
|
|
38
|
+
return new Promise((resolve) => {
|
|
39
|
+
execFile('git', args, { cwd: toNativePath(cwd), timeout, windowsHide: true, maxBuffer: 20_000_000 }, (err, stdout, stderr) => {
|
|
40
|
+
const code = err ? (typeof err.code === 'number' ? (err.code) : 1) : 0;
|
|
41
|
+
resolve({ code, stdout: String(stdout).trim(), stderr: String(stderr).trim() });
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/** A ref the caller passed, checked before it reaches git (no options, no odd characters). */
|
|
46
|
+
export function checkRef(ref, label) {
|
|
47
|
+
const value = ref.trim();
|
|
48
|
+
if (value === '' || value.startsWith('-') || !/^[\w./^~@{}-]+$/.test(value) || value.includes('..')) {
|
|
49
|
+
throw new PlaywrightMcpError(`${label} "${ref}" is not a commit or ref`, 'INVALID_PATH', {
|
|
50
|
+
hint: 'Pass a commit hash, a branch, a tag or a form like HEAD~20.',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
async function resolveCommit(ref, cwd, label) {
|
|
56
|
+
const result = await git(['rev-parse', '--verify', '--quiet', `${checkRef(ref, label)}^{commit}`], cwd);
|
|
57
|
+
if (result.code !== 0 || !/^[0-9a-f]{40,64}$/.test(result.stdout)) {
|
|
58
|
+
throw new PlaywrightMcpError(`${label} "${ref}" is not a commit in this repository`, 'INVALID_PATH', {
|
|
59
|
+
hint: 'Check the hash or ref with git log.',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return result.stdout;
|
|
63
|
+
}
|
|
64
|
+
function short(sha) {
|
|
65
|
+
return sha.slice(0, 8);
|
|
66
|
+
}
|
|
67
|
+
/** First line of the first failure, for the steps table. */
|
|
68
|
+
function failureNote(result) {
|
|
69
|
+
const first = result.failures[0];
|
|
70
|
+
if (!first)
|
|
71
|
+
return result.errorMessage?.split('\n')[0];
|
|
72
|
+
return `${first.title}: ${(first.message ?? '').split('\n').find((l) => l.trim() !== '') ?? first.failureKind}`.slice(0, 200);
|
|
73
|
+
}
|
|
74
|
+
export const bisectFailureTool = {
|
|
75
|
+
name: 'bisect-failure',
|
|
76
|
+
description: 'Find the commit that broke a test: binary-search the git history between a good and a bad commit, running the test at each step in a temporary worktree (your checkout is untouched), and report the first bad commit with its diff summary and failure. good defaults to the newest commit where the run history saw the test pass.',
|
|
77
|
+
inputSchema: bisectInput,
|
|
78
|
+
handler: async (args, ctx) => guard('bisect-failure', async () => {
|
|
79
|
+
if (ctx.restricted) {
|
|
80
|
+
throw new PlaywrightMcpError('bisect-failure is not available on this server', 'INVALID_PATH', {
|
|
81
|
+
hint: 'It runs your test code at old commits; use a local install.',
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
const root = await resolveProjectRoot(args.projectRoot, ctx);
|
|
85
|
+
const top = await git(['rev-parse', '--show-toplevel'], root);
|
|
86
|
+
if (top.code !== 0) {
|
|
87
|
+
throw new PlaywrightMcpError('The project is not inside a git repository', 'INVALID_PATH', {
|
|
88
|
+
hint: 'bisect-failure searches git history; initialise or clone the repository first.',
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
const repoTop = toNativePath(top.stdout);
|
|
92
|
+
// Ask git for the project's place in the repository: comparing paths
|
|
93
|
+
// breaks on Windows, where git and Node can spell the same folder
|
|
94
|
+
// differently (8.3 short names, drive letter case).
|
|
95
|
+
const subdir = (await git(['rev-parse', '--show-prefix'], root)).stdout.replace(/\/+$/, '');
|
|
96
|
+
const testArg = sanitizeTestPathArgument(args.test, root);
|
|
97
|
+
const lineMatch = /:(\d+)$/.exec(testArg);
|
|
98
|
+
const testRel = relativeToRoot(root, lineMatch ? testArg.slice(0, -lineMatch[0].length) : testArg);
|
|
99
|
+
const testTarget = `${testRel}${lineMatch ? lineMatch[0] : ''}`;
|
|
100
|
+
const bad = await resolveCommit(args.bad ?? 'HEAD', root, 'bad');
|
|
101
|
+
let good;
|
|
102
|
+
let goodFrom = '';
|
|
103
|
+
if (args.good) {
|
|
104
|
+
good = await resolveCommit(args.good, root, 'good');
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// The newest recorded run, at a clean commit, where this file's tests passed.
|
|
108
|
+
const history = await readHistory(root, 1_000).catch(() => []);
|
|
109
|
+
const wanted = args.grep?.toLowerCase();
|
|
110
|
+
let found;
|
|
111
|
+
for (const entry of [...history].reverse()) {
|
|
112
|
+
if (!entry.commit || entry.dirty || entry.commit === bad)
|
|
113
|
+
continue;
|
|
114
|
+
const mine = entry.tests.filter((t) => (testRel.endsWith(t.file) || t.file.endsWith(testRel)) && (!wanted || t.title.toLowerCase().includes(wanted)));
|
|
115
|
+
if (mine.length > 0 && mine.every((t) => t.status === 'passed' || t.status === 'skipped')) {
|
|
116
|
+
if ((await git(['merge-base', '--is-ancestor', entry.commit, bad], root)).code === 0) {
|
|
117
|
+
found = entry.commit;
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (!found) {
|
|
123
|
+
throw new PlaywrightMcpError('No good commit given, and the run history has no passing run of this test at an earlier commit', 'INVALID_PATH', {
|
|
124
|
+
hint: 'Pass good: a commit, tag or ref where the test passed (e.g. the last release tag, or HEAD~20).',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
good = found;
|
|
128
|
+
goodFrom = ' (from the run history)';
|
|
129
|
+
}
|
|
130
|
+
if (good === bad)
|
|
131
|
+
throw new PlaywrightMcpError('good and bad are the same commit', 'INVALID_PATH', { hint: 'Pass an older good commit.' });
|
|
132
|
+
if ((await git(['merge-base', '--is-ancestor', good, bad], root)).code !== 0) {
|
|
133
|
+
throw new PlaywrightMcpError(`good ${short(good)} is not an ancestor of bad ${short(bad)}`, 'INVALID_PATH', {
|
|
134
|
+
hint: 'Pick a good commit from the history of the bad one (git log bad).',
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
const list = await git(['rev-list', '--first-parent', '--reverse', `${good}..${bad}`], root);
|
|
138
|
+
const commits = list.stdout.split('\n').filter((c) => /^[0-9a-f]{40,64}$/.test(c));
|
|
139
|
+
if (commits.length === 0)
|
|
140
|
+
throw new PlaywrightMcpError('No commits between good and bad', 'INVALID_PATH');
|
|
141
|
+
const detection = await detectProject(root);
|
|
142
|
+
const configAbs = resolveConfigSelection(detection, args.config);
|
|
143
|
+
const configRel = configAbs ? relativeToRoot(root, configAbs) : undefined;
|
|
144
|
+
const lockAtHead = new Map();
|
|
145
|
+
for (const name of LOCKFILES) {
|
|
146
|
+
const blob = await git(['rev-parse', `HEAD:${subdir ? `${subdir}/` : ''}${name}`], root);
|
|
147
|
+
if (blob.code === 0)
|
|
148
|
+
lockAtHead.set(name, blob.stdout);
|
|
149
|
+
}
|
|
150
|
+
const tmp = await mkdtemp(path.join(os.tmpdir(), 'pw-bisect-'));
|
|
151
|
+
const worktree = path.join(tmp, 'wt');
|
|
152
|
+
const added = await git(['worktree', 'add', '--detach', worktree, bad], root, 120_000);
|
|
153
|
+
if (added.code !== 0) {
|
|
154
|
+
await rm(tmp, { recursive: true, force: true });
|
|
155
|
+
throw new PlaywrightMcpError('Could not create a temporary git worktree', 'INVALID_PATH', { details: added.stderr });
|
|
156
|
+
}
|
|
157
|
+
const projectDir = subdir ? path.join(worktree, ...subdir.split('/')) : worktree;
|
|
158
|
+
const started = Date.now();
|
|
159
|
+
const runs = new Map();
|
|
160
|
+
const depsChanged = new Set();
|
|
161
|
+
const check = async (commit) => {
|
|
162
|
+
const out = await git(['checkout', '--detach', '--force', '--quiet', commit], worktree, 120_000);
|
|
163
|
+
if (out.code !== 0)
|
|
164
|
+
return { verdict: 'skip', note: `checkout failed: ${out.stderr.split('\n')[0]}` };
|
|
165
|
+
await git(['clean', '-fdq'], worktree);
|
|
166
|
+
if (!existsSync(path.join(projectDir, ...testRel.split('/'))))
|
|
167
|
+
return { verdict: 'skip', note: 'the test file does not exist at this commit' };
|
|
168
|
+
// Reuse the installed dependencies rather than installing per commit.
|
|
169
|
+
for (const [from, to] of [
|
|
170
|
+
[path.join(toNativePath(root), 'node_modules'), path.join(projectDir, 'node_modules')],
|
|
171
|
+
[path.join(repoTop, 'node_modules'), path.join(worktree, 'node_modules')],
|
|
172
|
+
]) {
|
|
173
|
+
if (existsSync(from) && !existsSync(to))
|
|
174
|
+
await symlink(from, to, process.platform === 'win32' ? 'junction' : 'dir').catch(() => undefined);
|
|
175
|
+
}
|
|
176
|
+
for (const [name, blob] of lockAtHead) {
|
|
177
|
+
const then = await git(['rev-parse', `${commit}:${subdir ? `${subdir}/` : ''}${name}`], worktree);
|
|
178
|
+
if (then.code === 0 && then.stdout !== blob)
|
|
179
|
+
depsChanged.add(commit);
|
|
180
|
+
}
|
|
181
|
+
let result;
|
|
182
|
+
try {
|
|
183
|
+
result = await runTests({
|
|
184
|
+
projectRoot: normalizePath(projectDir),
|
|
185
|
+
configPath: configRel ? normalizePath(path.join(projectDir, ...configRel.split('/'))) : undefined,
|
|
186
|
+
testFiles: [testTarget],
|
|
187
|
+
grep: args.grep,
|
|
188
|
+
project: args.project,
|
|
189
|
+
repeatEach: args.repeat && args.repeat > 1 ? args.repeat : undefined,
|
|
190
|
+
retries: 0,
|
|
191
|
+
retryOnFailure: false,
|
|
192
|
+
timeoutMs: args.stepTimeoutMs ?? 180_000,
|
|
193
|
+
signal: ctx.signal,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
if (ctx.signal?.aborted)
|
|
198
|
+
throw err;
|
|
199
|
+
return { verdict: 'skip', note: `could not run: ${err.message.split('\n')[0]}` };
|
|
200
|
+
}
|
|
201
|
+
runs.set(commit, result);
|
|
202
|
+
const ran = (result.outcomes ?? []).filter((o) => o.status !== 'skipped').length;
|
|
203
|
+
if (result.failures.length > 0 && !result.timedOut)
|
|
204
|
+
return { verdict: 'bad', note: failureNote(result) };
|
|
205
|
+
if (result.ok && ran > 0)
|
|
206
|
+
return { verdict: 'good' };
|
|
207
|
+
return { verdict: 'skip', note: result.timedOut ? 'the run hit stepTimeoutMs' : (failureNote(result) ?? 'no test matched at this commit') };
|
|
208
|
+
};
|
|
209
|
+
try {
|
|
210
|
+
const out = [];
|
|
211
|
+
// Both ends first: a search between two passing (or two failing) commits finds nothing.
|
|
212
|
+
const atBad = await check(bad);
|
|
213
|
+
if (atBad.verdict !== 'bad') {
|
|
214
|
+
return toolText([
|
|
215
|
+
`## ⚠️ The test does not fail at ${short(bad)}`,
|
|
216
|
+
'',
|
|
217
|
+
atBad.verdict === 'good' ? 'It passed there, so there is nothing to search for.' : `It could not be tested there: ${atBad.note}.`,
|
|
218
|
+
'',
|
|
219
|
+
'> Check `test`/`grep`, or pass `bad` (a commit where it fails). For a flaky failure, raise `repeat`.',
|
|
220
|
+
].join('\n'), { ok: false, status: 'not-failing' });
|
|
221
|
+
}
|
|
222
|
+
const atGood = await check(good);
|
|
223
|
+
if (atGood.verdict !== 'good') {
|
|
224
|
+
return toolText([
|
|
225
|
+
`## ⚠️ The test does not pass at the good commit ${short(good)}${goodFrom}`,
|
|
226
|
+
'',
|
|
227
|
+
atGood.verdict === 'bad' ? `It fails there too: ${atGood.note}` : `It could not be tested there: ${atGood.note}.`,
|
|
228
|
+
'',
|
|
229
|
+
'> Pass an older `good` commit where the test passed.',
|
|
230
|
+
].join('\n'), { ok: false, status: 'good-fails' });
|
|
231
|
+
}
|
|
232
|
+
const search = await bisectCommits(commits, check, { maxSteps: args.maxSteps ?? 12, signal: ctx.signal });
|
|
233
|
+
const steps = [
|
|
234
|
+
{ commit: good, verdict: 'good' },
|
|
235
|
+
{ commit: bad, verdict: 'bad', ...(atBad.note ? { note: atBad.note } : {}) },
|
|
236
|
+
...search.steps,
|
|
237
|
+
];
|
|
238
|
+
const subjects = new Map();
|
|
239
|
+
for (const step of steps) {
|
|
240
|
+
const s = await git(['show', '-s', '--format=%s', step.commit], root);
|
|
241
|
+
subjects.set(step.commit, s.stdout.slice(0, 80));
|
|
242
|
+
}
|
|
243
|
+
let firstBadData;
|
|
244
|
+
if (search.firstBad) {
|
|
245
|
+
const sha = search.firstBad;
|
|
246
|
+
const meta = (await git(['show', '-s', '--format=%H%x1f%an%x1f%aI%x1f%s', sha], root)).stdout.split('\x1f');
|
|
247
|
+
const files = (await git(['show', '--format=', '--name-only', sha], root)).stdout.split('\n').filter(Boolean);
|
|
248
|
+
const stat = (await git(['show', '--format=', '--stat=100', sha], root)).stdout;
|
|
249
|
+
firstBadData = { sha, author: meta[1] ?? '', date: meta[2] ?? '', subject: meta[3] ?? '', files: files.slice(0, 50) };
|
|
250
|
+
out.push(`## 🎯 First bad commit: \`${short(sha)}\` ${firstBadData.subject}`, '', `**Author:** ${firstBadData.author} | **Date:** ${firstBadData.date} | **Searched:** ${commits.length} commit${commits.length === 1 ? '' : 's'} in ${search.steps.length + 2} runs, ${formatDuration(Date.now() - started)}`, '', codeFence(clipLines(stat, 25, 3_000), 'text'));
|
|
251
|
+
const run = runs.get(sha);
|
|
252
|
+
const first = run?.failures[0];
|
|
253
|
+
if (first) {
|
|
254
|
+
out.push('', `**How it fails there:** ${first.title}`, '', codeFence(clipLines(first.message ?? '', 10, 1_500), 'text'));
|
|
255
|
+
}
|
|
256
|
+
if (depsChanged.has(sha)) {
|
|
257
|
+
out.push('', '> ⚠️ Dependencies changed in this range (lockfile differs from HEAD), and every step ran with your installed node_modules. If the commit only bumps a package, that is likely the cause.');
|
|
258
|
+
}
|
|
259
|
+
out.push('', `> Next: \`git show ${short(sha)}${subdir ? ` -- ${subdir}` : ''}\` to read the change, or **get-failure** after a run-test at HEAD.`);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
out.push(`## 🔎 Narrowed to ${search.range.length} commits`, '', search.stopped === 'max-steps'
|
|
263
|
+
? `The search stopped at maxSteps (${args.maxSteps ?? 12}). Raise it, or pass the newest commit below as good.`
|
|
264
|
+
: search.stopped === 'cancelled'
|
|
265
|
+
? 'The search was cancelled.'
|
|
266
|
+
: 'The commits between could not be tested (see the steps), so the first bad commit is one of these:', '', ...search.range.slice(0, 20).map((c) => `- \`${short(c)}\``));
|
|
267
|
+
}
|
|
268
|
+
out.push('', '### Steps', '', '| Commit | Result | Subject | Note |', '|---|---|---|---|');
|
|
269
|
+
for (const step of steps) {
|
|
270
|
+
const icon = step.verdict === 'good' ? '✅ good' : step.verdict === 'bad' ? '❌ bad' : '⏭️ skip';
|
|
271
|
+
out.push(`| \`${short(step.commit)}\` | ${icon} | ${markdownCell(subjects.get(step.commit) ?? '')} | ${markdownCell((step.note ?? '').slice(0, 120))} |`);
|
|
272
|
+
}
|
|
273
|
+
const info = bad === (await resolveCommit('HEAD', root, 'HEAD')) ? await localGitInfo(root) : undefined;
|
|
274
|
+
if (info?.dirty)
|
|
275
|
+
out.push('', '> Your uncommitted changes were not part of the search: it tested committed code only.');
|
|
276
|
+
ctx.logger.info('bisect-failure finished', { commits: commits.length, steps: steps.length, found: Boolean(search.firstBad) });
|
|
277
|
+
return toolText(out.join('\n'), {
|
|
278
|
+
ok: true,
|
|
279
|
+
status: search.firstBad ? 'found' : (search.stopped ?? 'ambiguous'),
|
|
280
|
+
...(firstBadData ? { firstBad: firstBadData } : {}),
|
|
281
|
+
steps: steps.map((s) => ({ commit: s.commit, verdict: s.verdict, ...(s.note ? { note: s.note } : {}) })),
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
finally {
|
|
285
|
+
await git(['worktree', 'remove', '--force', worktree], root, 120_000);
|
|
286
|
+
await rm(tmp, { recursive: true, force: true }).catch(() => undefined);
|
|
287
|
+
await git(['worktree', 'prune'], root);
|
|
288
|
+
}
|
|
289
|
+
}),
|
|
290
|
+
};
|
|
291
|
+
//# sourceMappingURL=bisect-failure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bisect-failure.js","sourceRoot":"","sources":["../../src/tools/bisect-failure.ts"],"names":[],"mappings":"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"}
|