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.
- package/LICENSE +21 -0
- package/README.md +497 -0
- package/dist/http.d.ts +27 -0
- package/dist/http.js +156 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +23 -0
- package/dist/server.js +225 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/compare-visual-state.d.ts +77 -0
- package/dist/tools/compare-visual-state.js +223 -0
- package/dist/tools/compare-visual-state.js.map +1 -0
- package/dist/tools/diagnose-flaky.d.ts +87 -0
- package/dist/tools/diagnose-flaky.js +258 -0
- package/dist/tools/diagnose-flaky.js.map +1 -0
- package/dist/tools/generate-e2e-test.d.ts +51 -0
- package/dist/tools/generate-e2e-test.js +274 -0
- package/dist/tools/generate-e2e-test.js.map +1 -0
- package/dist/tools/get-failure.d.ts +26 -0
- package/dist/tools/get-failure.js +220 -0
- package/dist/tools/get-failure.js.map +1 -0
- package/dist/tools/inspect-page.d.ts +57 -0
- package/dist/tools/inspect-page.js +124 -0
- package/dist/tools/inspect-page.js.map +1 -0
- package/dist/tools/list-tests.d.ts +35 -0
- package/dist/tools/list-tests.js +88 -0
- package/dist/tools/list-tests.js.map +1 -0
- package/dist/tools/run-test.d.ts +70 -0
- package/dist/tools/run-test.js +169 -0
- package/dist/tools/run-test.js.map +1 -0
- package/dist/tools/shared.d.ts +86 -0
- package/dist/tools/shared.js +490 -0
- package/dist/tools/shared.js.map +1 -0
- package/dist/tools/validate-selector.d.ts +32 -0
- package/dist/tools/validate-selector.js +93 -0
- package/dist/tools/validate-selector.js.map +1 -0
- package/dist/types/index.d.ts +344 -0
- package/dist/types/index.js +51 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/change-analyzer.d.ts +46 -0
- package/dist/utils/change-analyzer.js +290 -0
- package/dist/utils/change-analyzer.js.map +1 -0
- package/dist/utils/image-diff.d.ts +61 -0
- package/dist/utils/image-diff.js +373 -0
- package/dist/utils/image-diff.js.map +1 -0
- package/dist/utils/logger.d.ts +21 -0
- package/dist/utils/logger.js +132 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/path-utils.d.ts +55 -0
- package/dist/utils/path-utils.js +211 -0
- package/dist/utils/path-utils.js.map +1 -0
- package/dist/utils/playwright-runner.d.ts +63 -0
- package/dist/utils/playwright-runner.js +594 -0
- package/dist/utils/playwright-runner.js.map +1 -0
- package/dist/utils/project-detector.d.ts +43 -0
- package/dist/utils/project-detector.js +255 -0
- package/dist/utils/project-detector.js.map +1 -0
- package/dist/utils/report-parser.d.ts +60 -0
- package/dist/utils/report-parser.js +501 -0
- package/dist/utils/report-parser.js.map +1 -0
- package/dist/utils/trace-reader.d.ts +109 -0
- package/dist/utils/trace-reader.js +608 -0
- package/dist/utils/trace-reader.js.map +1 -0
- package/examples/sample-test.spec.ts +49 -0
- package/package.json +66 -0
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Playwright execution logic.
|
|
3
|
+
*
|
|
4
|
+
* Runs the project's local Playwright CLI as a child process (never a
|
|
5
|
+
* shell), streams and caps its output, enforces a hard wall-clock
|
|
6
|
+
* timeout with process-*group* cleanup, collects the JSON report, and
|
|
7
|
+
* converts every failure mode — missing install, dead dev server,
|
|
8
|
+
* syntax error, browser crash, full disk, client disconnect — into a
|
|
9
|
+
* structured result.
|
|
10
|
+
*/
|
|
11
|
+
import { spawn } from 'node:child_process';
|
|
12
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import { PlaywrightMcpError } from '../types/index.js';
|
|
15
|
+
import { findTestFiles, missingPlaywrightMessage, detectProject, } from './project-detector.js';
|
|
16
|
+
import { extractJsonFromText, extractTestTitles, diagnoseOutput, parseReportJson, parseProjectNames, } from './report-parser.js';
|
|
17
|
+
import { isAbsolutePath, isPathInside, normalizePath, relativeToRoot, sanitizeCliArg, tempFilePath, toNativePath, } from './path-utils.js';
|
|
18
|
+
import { logger } from './logger.js';
|
|
19
|
+
const DEFAULT_RUN_TIMEOUT_MS = 120_000;
|
|
20
|
+
const DEFAULT_LIST_TIMEOUT_MS = 60_000;
|
|
21
|
+
/** Keep only the tail of combined child output in memory. */
|
|
22
|
+
const OUTPUT_CAP_CHARS = 400_000;
|
|
23
|
+
/** Characters of stdout/stderr echoed back in the tool result. */
|
|
24
|
+
const TAIL_CHARS = 8_000;
|
|
25
|
+
const GRACEFUL_KILL_MS = 3_000;
|
|
26
|
+
const ABORT_KILL_MS = 1_000;
|
|
27
|
+
/* ------------------------------------------------------------------ */
|
|
28
|
+
/* Small helpers */
|
|
29
|
+
/* ------------------------------------------------------------------ */
|
|
30
|
+
async function fileExists(p) {
|
|
31
|
+
try {
|
|
32
|
+
return (await stat(p)).isFile();
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function readFileText(p) {
|
|
39
|
+
try {
|
|
40
|
+
return await readFile(p, 'utf8');
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function clampInt(value, min, max) {
|
|
47
|
+
if (value === undefined || !Number.isFinite(value))
|
|
48
|
+
return undefined;
|
|
49
|
+
return Math.max(min, Math.min(max, Math.trunc(value)));
|
|
50
|
+
}
|
|
51
|
+
function sanitizeGrep(grep) {
|
|
52
|
+
const raw = grep.trim();
|
|
53
|
+
if (raw === '' || /[\0\n\r]/.test(raw) || raw.length > 500) {
|
|
54
|
+
throw new PlaywrightMcpError('Invalid --grep expression', 'INVALID_PATH', {
|
|
55
|
+
hint: 'Provide a plain regular expression without NUL/newline characters (max 500 chars).',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return raw;
|
|
59
|
+
}
|
|
60
|
+
function tail(text, limit = TAIL_CHARS) {
|
|
61
|
+
if (text.length <= limit)
|
|
62
|
+
return text;
|
|
63
|
+
return `…(truncated)…\n${text.slice(text.length - limit)}`;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Locate the project's local Playwright CLI (playwright/cli.js), which
|
|
67
|
+
* we invoke directly with `node` — no shell, no npx, cross-platform.
|
|
68
|
+
*/
|
|
69
|
+
export async function resolvePlaywrightCli(projectRoot) {
|
|
70
|
+
let dir = normalizePath(projectRoot);
|
|
71
|
+
for (let depth = 0; depth < 64; depth += 1) {
|
|
72
|
+
for (const pkg of ['playwright', '@playwright/test']) {
|
|
73
|
+
const cli = path.join(dir, 'node_modules', ...pkg.split('/'), 'cli.js');
|
|
74
|
+
if (await fileExists(cli))
|
|
75
|
+
return cli;
|
|
76
|
+
}
|
|
77
|
+
const parent = normalizePath(path.dirname(dir));
|
|
78
|
+
if (parent === dir)
|
|
79
|
+
break;
|
|
80
|
+
dir = parent;
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Kill a child and its descendants. POSIX: signal the whole process
|
|
86
|
+
* group (child is spawned detached). Windows: taskkill /T walks the
|
|
87
|
+
* tree, /F only on the forced pass.
|
|
88
|
+
*/
|
|
89
|
+
function killTree(child, force) {
|
|
90
|
+
const pid = child.pid;
|
|
91
|
+
if (pid === undefined)
|
|
92
|
+
return;
|
|
93
|
+
try {
|
|
94
|
+
if (process.platform === 'win32') {
|
|
95
|
+
const args = ['/pid', String(pid), '/T'];
|
|
96
|
+
if (force)
|
|
97
|
+
args.push('/F');
|
|
98
|
+
const killer = spawn('taskkill', args, { stdio: 'ignore', windowsHide: true });
|
|
99
|
+
killer.on('error', () => {
|
|
100
|
+
try {
|
|
101
|
+
child.kill('SIGKILL');
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
/* already exited */
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
try {
|
|
110
|
+
process.kill(-pid, force ? 'SIGKILL' : 'SIGINT');
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
try {
|
|
114
|
+
child.kill(force ? 'SIGKILL' : 'SIGINT');
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
/* already exited */
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
/* process already gone */
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Spawn a process with output capping, tree-kill timeout and abort.
|
|
128
|
+
* Children are tracked so shutdown/disconnect can never orphan them.
|
|
129
|
+
*/
|
|
130
|
+
export function runProcess(command, args, options) {
|
|
131
|
+
return new Promise((resolve) => {
|
|
132
|
+
let settled = false;
|
|
133
|
+
let timedOut = false;
|
|
134
|
+
let aborted = false;
|
|
135
|
+
let forceTimer;
|
|
136
|
+
let timeoutTimer;
|
|
137
|
+
const stdoutChunks = [];
|
|
138
|
+
const stderrChunks = [];
|
|
139
|
+
let capturedChars = 0;
|
|
140
|
+
const push = (chunks, chunk) => {
|
|
141
|
+
chunks.push(chunk);
|
|
142
|
+
capturedChars += chunk.length;
|
|
143
|
+
while (capturedChars > OUTPUT_CAP_CHARS && chunks.length > 1) {
|
|
144
|
+
capturedChars -= chunks[0].length;
|
|
145
|
+
chunks.shift();
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
let child;
|
|
149
|
+
try {
|
|
150
|
+
child = spawn(command, args, {
|
|
151
|
+
cwd: options.cwd,
|
|
152
|
+
env: options.env,
|
|
153
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
154
|
+
detached: process.platform !== 'win32',
|
|
155
|
+
windowsHide: true,
|
|
156
|
+
});
|
|
157
|
+
activeChildren.add(child);
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
resolve({
|
|
161
|
+
code: null,
|
|
162
|
+
signalName: null,
|
|
163
|
+
stdout: '',
|
|
164
|
+
stderr: '',
|
|
165
|
+
timedOut: false,
|
|
166
|
+
aborted: false,
|
|
167
|
+
spawnError: {
|
|
168
|
+
code: 'ESPAWN',
|
|
169
|
+
message: err instanceof Error ? err.message : String(err),
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const shutdown = (graceMs) => {
|
|
175
|
+
// Windows has no SIGINT semantics for console children — taskkill
|
|
176
|
+
// /T /F straight away; POSIX gets a graceful SIGINT first.
|
|
177
|
+
killTree(child, process.platform === 'win32');
|
|
178
|
+
if (forceTimer === undefined) {
|
|
179
|
+
forceTimer = setTimeout(() => {
|
|
180
|
+
killTree(child, true);
|
|
181
|
+
}, graceMs);
|
|
182
|
+
forceTimer.unref?.();
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
child.stdout?.setEncoding('utf8');
|
|
186
|
+
child.stderr?.setEncoding('utf8');
|
|
187
|
+
child.stdout?.on('data', (chunk) => push(stdoutChunks, chunk));
|
|
188
|
+
child.stderr?.on('data', (chunk) => push(stderrChunks, chunk));
|
|
189
|
+
const onAbort = () => {
|
|
190
|
+
if (settled)
|
|
191
|
+
return;
|
|
192
|
+
aborted = true;
|
|
193
|
+
shutdown(ABORT_KILL_MS);
|
|
194
|
+
};
|
|
195
|
+
if (options.signal) {
|
|
196
|
+
if (options.signal.aborted) {
|
|
197
|
+
aborted = true;
|
|
198
|
+
shutdown(ABORT_KILL_MS);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
options.signal.addEventListener('abort', onAbort, { once: true });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
timeoutTimer = setTimeout(() => {
|
|
205
|
+
if (settled)
|
|
206
|
+
return;
|
|
207
|
+
timedOut = true;
|
|
208
|
+
shutdown(GRACEFUL_KILL_MS);
|
|
209
|
+
}, options.timeoutMs);
|
|
210
|
+
timeoutTimer.unref?.();
|
|
211
|
+
let spawnError = null;
|
|
212
|
+
const finish = (code, signalName) => {
|
|
213
|
+
if (settled)
|
|
214
|
+
return;
|
|
215
|
+
settled = true;
|
|
216
|
+
activeChildren.delete(child);
|
|
217
|
+
if (timeoutTimer)
|
|
218
|
+
clearTimeout(timeoutTimer);
|
|
219
|
+
if (forceTimer)
|
|
220
|
+
clearTimeout(forceTimer);
|
|
221
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
222
|
+
resolve({
|
|
223
|
+
code,
|
|
224
|
+
signalName,
|
|
225
|
+
stdout: stdoutChunks.join(''),
|
|
226
|
+
stderr: stderrChunks.join(''),
|
|
227
|
+
timedOut,
|
|
228
|
+
aborted,
|
|
229
|
+
spawnError,
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
child.on('error', (err) => {
|
|
233
|
+
spawnError = { code: err.code ?? 'ESPAWN', message: err.message };
|
|
234
|
+
finish(null, null);
|
|
235
|
+
});
|
|
236
|
+
child.on('close', (code, signal) => finish(code, signal));
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
/** Every child spawned by runProcess that has not settled yet. */
|
|
240
|
+
const activeChildren = new Set();
|
|
241
|
+
/**
|
|
242
|
+
* Force-kill every child still running (and its tree). Called on
|
|
243
|
+
* shutdown and when the MCP client disconnects so no browser or test
|
|
244
|
+
* worker outlives the server.
|
|
245
|
+
*/
|
|
246
|
+
export function killActiveChildren() {
|
|
247
|
+
const children = [...activeChildren];
|
|
248
|
+
for (const child of children)
|
|
249
|
+
killTree(child, true);
|
|
250
|
+
activeChildren.clear();
|
|
251
|
+
return children.length;
|
|
252
|
+
}
|
|
253
|
+
/* ------------------------------------------------------------------ */
|
|
254
|
+
/* run-test */
|
|
255
|
+
/* ------------------------------------------------------------------ */
|
|
256
|
+
function sanitizeTestFile(file, root) {
|
|
257
|
+
const resolved = normalizePath(isAbsolutePath(file) ? file : path.join(root, file));
|
|
258
|
+
if (!isPathInside(resolved, root)) {
|
|
259
|
+
throw new PlaywrightMcpError(`Test path "${file}" is outside the project root`, 'INVALID_PATH', {
|
|
260
|
+
hint: `Only test paths inside ${root} are allowed.`,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
return resolved;
|
|
264
|
+
}
|
|
265
|
+
/** Best-effort `retries: N` from the config source text. */
|
|
266
|
+
async function readConfigRetries(configPath) {
|
|
267
|
+
if (!configPath)
|
|
268
|
+
return null;
|
|
269
|
+
const source = await readFileText(configPath);
|
|
270
|
+
if (source === null)
|
|
271
|
+
return null;
|
|
272
|
+
const match = /\bretries\s*:\s*(\d+)/.exec(source);
|
|
273
|
+
return match ? Number(match[1]) : null;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* The blueprint's "flakiness" cure: unless the caller asks otherwise,
|
|
277
|
+
* retry failing tests once (--retries=1) before telling the agent they
|
|
278
|
+
* failed. A test that passes on that retry is reported as *flaky*, not
|
|
279
|
+
* failed. Explicit `retries` or retryOnFailure: false win; a config
|
|
280
|
+
* that already enables retries is left alone.
|
|
281
|
+
*/
|
|
282
|
+
async function resolveAutoRetry(configPath, options) {
|
|
283
|
+
if (options.retries !== undefined) {
|
|
284
|
+
return { apply: false, reason: 'caller set retries explicitly' };
|
|
285
|
+
}
|
|
286
|
+
if (options.retryOnFailure === false) {
|
|
287
|
+
return { apply: false, reason: 'retryOnFailure disabled by caller' };
|
|
288
|
+
}
|
|
289
|
+
const configRetries = await readConfigRetries(configPath);
|
|
290
|
+
if (configRetries !== null && configRetries > 0) {
|
|
291
|
+
return { apply: false, reason: `config already sets retries: ${configRetries}` };
|
|
292
|
+
}
|
|
293
|
+
return { apply: true, reason: 'server-level auto retry-once enabled' };
|
|
294
|
+
}
|
|
295
|
+
async function resolveProjectFlag(configPath, browser) {
|
|
296
|
+
if (!browser)
|
|
297
|
+
return {};
|
|
298
|
+
const source = configPath ? await readFileText(configPath) : null;
|
|
299
|
+
if (source === null) {
|
|
300
|
+
return browser === 'chromium'
|
|
301
|
+
? {}
|
|
302
|
+
: {
|
|
303
|
+
hint: `No config file was found to verify project names, so the run used the default browser (chromium) instead of ${browser}.`,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
const names = parseProjectNames(source);
|
|
307
|
+
if (names.includes(browser))
|
|
308
|
+
return { project: browser };
|
|
309
|
+
if (names.length === 0) {
|
|
310
|
+
return browser === 'chromium'
|
|
311
|
+
? {}
|
|
312
|
+
: {
|
|
313
|
+
hint: `playwright.config does not define a project named "${browser}", so the run used the default browser (chromium). Add a "${browser}" project to test it.`,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
hint: `playwright.config has no project named "${browser}" (found: ${names.join(', ')}); the run used the config's default project(s).`,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Execute Playwright tests and return a structured result. Throws
|
|
322
|
+
* PlaywrightMcpError only for precondition failures (no Playwright
|
|
323
|
+
* installed, bad paths); test failures come back as data.
|
|
324
|
+
*/
|
|
325
|
+
export async function runTests(options) {
|
|
326
|
+
const root = normalizePath(options.projectRoot);
|
|
327
|
+
if (options.signal?.aborted) {
|
|
328
|
+
throw new PlaywrightMcpError('Request cancelled before the run started', 'CLIENT_DISCONNECT', {
|
|
329
|
+
hint: 'The MCP client disconnected.',
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
const cli = await resolvePlaywrightCli(root);
|
|
333
|
+
if (cli === null) {
|
|
334
|
+
const detection = await detectProject(root);
|
|
335
|
+
const { message, hint } = missingPlaywrightMessage(detection);
|
|
336
|
+
throw new PlaywrightMcpError(message, 'NO_PLAYWRIGHT', { hint });
|
|
337
|
+
}
|
|
338
|
+
let configPath;
|
|
339
|
+
if (options.configPath) {
|
|
340
|
+
configPath = normalizePath(options.configPath);
|
|
341
|
+
if (!isPathInside(configPath, root)) {
|
|
342
|
+
throw new PlaywrightMcpError('Config path is outside the project root', 'INVALID_PATH', {
|
|
343
|
+
hint: `The playwright.config must live inside ${root}.`,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
if (!(await fileExists(configPath))) {
|
|
347
|
+
throw new PlaywrightMcpError(`Config file not found: ${configPath}`, 'NO_CONFIG', {
|
|
348
|
+
hint: 'Check the config path, or omit it to let Playwright auto-detect playwright.config.*.',
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const testFiles = (options.testFiles ?? []).map((file) => sanitizeTestFile(file, root));
|
|
353
|
+
const reportPath = tempFilePath('pw-report', '.json');
|
|
354
|
+
const { project, hint: projectHint } = await resolveProjectFlag(configPath, options.browser);
|
|
355
|
+
const autoRetryState = await resolveAutoRetry(configPath, options);
|
|
356
|
+
const args = ['test'];
|
|
357
|
+
if (configPath)
|
|
358
|
+
args.push('--config', toNativePath(configPath));
|
|
359
|
+
for (const file of testFiles) {
|
|
360
|
+
args.push(relativeToRoot(root, file));
|
|
361
|
+
}
|
|
362
|
+
if (options.grep)
|
|
363
|
+
args.push(`--grep=${sanitizeGrep(options.grep)}`);
|
|
364
|
+
if (options.lastFailed)
|
|
365
|
+
args.push('--last-failed');
|
|
366
|
+
if (project)
|
|
367
|
+
args.push(`--project=${project}`);
|
|
368
|
+
if (options.headed)
|
|
369
|
+
args.push('--headed');
|
|
370
|
+
const workers = clampInt(options.workers, 1, 64);
|
|
371
|
+
if (workers !== undefined)
|
|
372
|
+
args.push(`--workers=${workers}`);
|
|
373
|
+
const retries = clampInt(options.retries, 0, 10);
|
|
374
|
+
if (retries !== undefined)
|
|
375
|
+
args.push(`--retries=${retries}`);
|
|
376
|
+
else if (autoRetryState.apply)
|
|
377
|
+
args.push(`--retries=1`);
|
|
378
|
+
const testTimeout = clampInt(options.testTimeoutMs, 1_000, 3_600_000);
|
|
379
|
+
if (testTimeout !== undefined)
|
|
380
|
+
args.push(`--timeout=${testTimeout}`);
|
|
381
|
+
const hasTraceFlag = (options.extraArgs ?? []).some((extra) => extra.startsWith('--trace'));
|
|
382
|
+
if (!hasTraceFlag)
|
|
383
|
+
args.push('--trace=retain-on-failure');
|
|
384
|
+
for (const extra of options.extraArgs ?? [])
|
|
385
|
+
args.push(sanitizeCliArg(extra));
|
|
386
|
+
args.push('--reporter=json');
|
|
387
|
+
const env = {
|
|
388
|
+
...process.env,
|
|
389
|
+
PLAYWRIGHT_JSON_OUTPUT_NAME: reportPath,
|
|
390
|
+
FORCE_COLOR: '0',
|
|
391
|
+
PW_TEST_HTML_REPORT_OPEN: 'never',
|
|
392
|
+
};
|
|
393
|
+
const timeoutMs = clampInt(options.timeoutMs, 1_000, 3_600_000) ?? DEFAULT_RUN_TIMEOUT_MS;
|
|
394
|
+
const startedAt = Date.now();
|
|
395
|
+
logger.debug('Starting Playwright run', { args: args.join(' '), cwd: root, timeoutMs });
|
|
396
|
+
const outcome = await runProcess(process.execPath, [cli, ...args], {
|
|
397
|
+
cwd: root,
|
|
398
|
+
env,
|
|
399
|
+
timeoutMs,
|
|
400
|
+
signal: options.signal,
|
|
401
|
+
});
|
|
402
|
+
const durationMs = Date.now() - startedAt;
|
|
403
|
+
const displayCommand = ['playwright', ...args].join(' ');
|
|
404
|
+
const rawReport = (await readFileText(reportPath)) ?? extractJsonFromText(outcome.stdout);
|
|
405
|
+
const parsed = rawReport
|
|
406
|
+
? parseReportJson(rawReport, root)
|
|
407
|
+
: {
|
|
408
|
+
stats: null,
|
|
409
|
+
failures: [],
|
|
410
|
+
failuresTruncated: false,
|
|
411
|
+
tests: [],
|
|
412
|
+
specCount: 0,
|
|
413
|
+
projects: [],
|
|
414
|
+
configPath: null,
|
|
415
|
+
parseError: 'No JSON report was produced.',
|
|
416
|
+
};
|
|
417
|
+
const combined = `${outcome.stderr}\n${outcome.stdout}`;
|
|
418
|
+
const diagnosis = diagnoseOutput(combined);
|
|
419
|
+
let errorKind;
|
|
420
|
+
let errorMessage;
|
|
421
|
+
let hint = projectHint;
|
|
422
|
+
if (outcome.spawnError) {
|
|
423
|
+
errorKind = 'SPAWN_FAILED';
|
|
424
|
+
errorMessage = outcome.spawnError.message;
|
|
425
|
+
hint =
|
|
426
|
+
outcome.spawnError.code === 'ENOENT'
|
|
427
|
+
? 'Node.js could not be located on PATH. Reinstall Node >= 18 and retry.'
|
|
428
|
+
: `Failed to start Playwright: ${outcome.spawnError.message}`;
|
|
429
|
+
}
|
|
430
|
+
else if (outcome.aborted) {
|
|
431
|
+
errorKind = 'CLIENT_DISCONNECT';
|
|
432
|
+
errorMessage = 'Run aborted because the MCP client disconnected.';
|
|
433
|
+
hint = 'Child processes and browsers were terminated.';
|
|
434
|
+
}
|
|
435
|
+
else if (outcome.timedOut) {
|
|
436
|
+
errorKind = 'TIMEOUT';
|
|
437
|
+
errorMessage = `Run exceeded the ${timeoutMs}ms limit and was killed.`;
|
|
438
|
+
const timeoutHint = 'Partial results are shown below. Raise timeoutMs, reduce workers, or run a single test file. Playwright may also be configured with `timeout` in playwright.config.*.';
|
|
439
|
+
hint = [timeoutHint, diagnosis?.hint].filter(Boolean).join('\n');
|
|
440
|
+
}
|
|
441
|
+
else if (diagnosis && (outcome.code !== 0 || parsed.failures.length === 0)) {
|
|
442
|
+
errorKind = diagnosis.errorKind;
|
|
443
|
+
errorMessage = diagnosis.errorMessage;
|
|
444
|
+
hint = [hint, diagnosis.hint].filter(Boolean).join('\n');
|
|
445
|
+
}
|
|
446
|
+
else if (parsed.parseError && outcome.code !== 0) {
|
|
447
|
+
errorKind = 'REPORT_MISSING';
|
|
448
|
+
errorMessage = parsed.parseError;
|
|
449
|
+
hint = 'Playwright exited with an error before writing a report. See stderr below.';
|
|
450
|
+
}
|
|
451
|
+
const ok = outcome.code === 0 && !outcome.timedOut && !outcome.aborted && !outcome.spawnError;
|
|
452
|
+
const partial = outcome.timedOut ||
|
|
453
|
+
outcome.aborted ||
|
|
454
|
+
(outcome.code !== 0 && parsed.failures.length === 0 && rawReport === null);
|
|
455
|
+
return {
|
|
456
|
+
ok,
|
|
457
|
+
exitCode: outcome.code,
|
|
458
|
+
signalName: outcome.signalName,
|
|
459
|
+
durationMs,
|
|
460
|
+
timedOut: outcome.timedOut,
|
|
461
|
+
killed: outcome.timedOut || outcome.aborted,
|
|
462
|
+
partial,
|
|
463
|
+
stats: parsed.stats,
|
|
464
|
+
failures: parsed.failures,
|
|
465
|
+
failuresTruncated: parsed.failuresTruncated,
|
|
466
|
+
stdoutTail: tail(outcome.stdout),
|
|
467
|
+
stderrTail: tail(outcome.stderr),
|
|
468
|
+
truncatedOutput: outcome.stdout.length + outcome.stderr.length > TAIL_CHARS,
|
|
469
|
+
reportPath: rawReport ? reportPath : undefined,
|
|
470
|
+
configPath: configPath ?? (parsed.configPath ? normalizePath(parsed.configPath) : undefined),
|
|
471
|
+
command: displayCommand,
|
|
472
|
+
autoRetry: autoRetryState.apply,
|
|
473
|
+
lastFailed: options.lastFailed === true,
|
|
474
|
+
errorKind,
|
|
475
|
+
errorMessage,
|
|
476
|
+
hint,
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
/* ------------------------------------------------------------------ */
|
|
480
|
+
/* list-tests */
|
|
481
|
+
/* ------------------------------------------------------------------ */
|
|
482
|
+
async function runPlaywrightList(cli, options, configPath) {
|
|
483
|
+
const root = normalizePath(options.projectRoot);
|
|
484
|
+
const listReportPath = tempFilePath('pw-list', '.json');
|
|
485
|
+
const args = ['test', '--list'];
|
|
486
|
+
if (configPath)
|
|
487
|
+
args.push('--config', toNativePath(configPath));
|
|
488
|
+
args.push('--reporter=json');
|
|
489
|
+
const env = {
|
|
490
|
+
...process.env,
|
|
491
|
+
PLAYWRIGHT_JSON_OUTPUT_NAME: listReportPath,
|
|
492
|
+
FORCE_COLOR: '0',
|
|
493
|
+
};
|
|
494
|
+
const outcome = await runProcess(process.execPath, [cli, ...args], {
|
|
495
|
+
cwd: root,
|
|
496
|
+
env,
|
|
497
|
+
timeoutMs: DEFAULT_LIST_TIMEOUT_MS,
|
|
498
|
+
signal: options.signal,
|
|
499
|
+
});
|
|
500
|
+
const raw = (await readFileText(listReportPath)) ?? extractJsonFromText(outcome.stdout) ?? undefined;
|
|
501
|
+
if (raw === undefined) {
|
|
502
|
+
const diagnosis = diagnoseOutput(outcome.stderr || outcome.stdout);
|
|
503
|
+
throw new PlaywrightMcpError(diagnosis?.errorMessage ?? '`playwright test --list` produced no output.', diagnosis?.errorKind ?? 'REPORT_MISSING', { hint: diagnosis?.hint ?? 'Check that the config compiles and at least one test file exists.' });
|
|
504
|
+
}
|
|
505
|
+
const parsed = parseReportJson(raw, root);
|
|
506
|
+
if (parsed.tests.length === 0 && outcome.code !== 0) {
|
|
507
|
+
const diagnosis = diagnoseOutput(outcome.stderr || outcome.stdout);
|
|
508
|
+
throw new PlaywrightMcpError(diagnosis?.errorMessage ?? parsed.parseError ?? '`playwright test --list` failed.', diagnosis?.errorKind ?? 'REPORT_MISSING', { hint: diagnosis?.hint ?? 'See stderr for details.' });
|
|
509
|
+
}
|
|
510
|
+
return parsed.tests;
|
|
511
|
+
}
|
|
512
|
+
/** List tests via `playwright test --list`, falling back to file scan. */
|
|
513
|
+
export async function listTests(options) {
|
|
514
|
+
const root = normalizePath(options.projectRoot);
|
|
515
|
+
const limit = clampInt(options.limit, 1, 5_000) ?? 500;
|
|
516
|
+
const testDir = options.testDir ?? null;
|
|
517
|
+
let error;
|
|
518
|
+
let collected = [];
|
|
519
|
+
let source = 'file-scan';
|
|
520
|
+
let listedOk = false;
|
|
521
|
+
let configPath;
|
|
522
|
+
if (options.configPath) {
|
|
523
|
+
configPath = normalizePath(options.configPath);
|
|
524
|
+
if (!(await fileExists(configPath))) {
|
|
525
|
+
throw new PlaywrightMcpError(`Config file not found: ${configPath}`, 'NO_CONFIG');
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
const cli = await resolvePlaywrightCli(root);
|
|
529
|
+
if (cli === null) {
|
|
530
|
+
const detection = await detectProject(root);
|
|
531
|
+
const { message, hint } = missingPlaywrightMessage(detection);
|
|
532
|
+
error = { kind: 'NO_PLAYWRIGHT', message, hint };
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
try {
|
|
536
|
+
collected = await runPlaywrightList(cli, options, configPath);
|
|
537
|
+
source = 'playwright-list';
|
|
538
|
+
listedOk = true;
|
|
539
|
+
}
|
|
540
|
+
catch (err) {
|
|
541
|
+
const mcpError = err instanceof PlaywrightMcpError
|
|
542
|
+
? err
|
|
543
|
+
: new PlaywrightMcpError(err instanceof Error ? err.message : String(err), 'UNKNOWN');
|
|
544
|
+
error = { kind: mcpError.kind, message: mcpError.message, hint: mcpError.hint };
|
|
545
|
+
logger.warn('playwright --list failed; falling back to file scan', { error: mcpError.message });
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
if (!listedOk) {
|
|
549
|
+
const files = await findTestFiles(root, { testDir, limit: 200 });
|
|
550
|
+
for (const file of files) {
|
|
551
|
+
const content = await readFileText(path.join(root, file));
|
|
552
|
+
if (content === null)
|
|
553
|
+
continue;
|
|
554
|
+
for (const entry of extractTestTitles(content)) {
|
|
555
|
+
collected.push({ file, title: entry.title, line: entry.line });
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
source = 'file-scan';
|
|
559
|
+
}
|
|
560
|
+
const filter = options.filter?.trim().toLowerCase();
|
|
561
|
+
const filtered = filter
|
|
562
|
+
? collected.filter((test) => `${test.file} › ${test.title}`.toLowerCase().includes(filter))
|
|
563
|
+
: collected;
|
|
564
|
+
const tests = filtered.slice(0, limit);
|
|
565
|
+
return {
|
|
566
|
+
tests,
|
|
567
|
+
total: filtered.length,
|
|
568
|
+
truncated: filtered.length > tests.length,
|
|
569
|
+
testDir,
|
|
570
|
+
source,
|
|
571
|
+
error,
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Quick reachability check for the app under test, used to tell the
|
|
576
|
+
* user "start your dev server" instead of surfacing ECONNREFUSED.
|
|
577
|
+
*/
|
|
578
|
+
export async function probeServer(url, timeoutMs = 2_000) {
|
|
579
|
+
try {
|
|
580
|
+
const response = await fetch(url, {
|
|
581
|
+
method: 'GET',
|
|
582
|
+
redirect: 'follow',
|
|
583
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
584
|
+
});
|
|
585
|
+
return { reachable: true, status: response.status };
|
|
586
|
+
}
|
|
587
|
+
catch (err) {
|
|
588
|
+
return {
|
|
589
|
+
reachable: false,
|
|
590
|
+
error: err instanceof Error ? err.message : String(err),
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
//# sourceMappingURL=playwright-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright-runner.js","sourceRoot":"","sources":["../../src/utils/playwright-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project detection: locate playwright.config.*, verify Playwright is
|
|
3
|
+
* installed, identify the package manager, and discover test files.
|
|
4
|
+
*
|
|
5
|
+
* Supports monorepos by walking up to the filesystem root and picking
|
|
6
|
+
* the *nearest* directory containing a Playwright config. When several
|
|
7
|
+
* configs live side by side every candidate is returned so the caller
|
|
8
|
+
* can ask the user which one to use.
|
|
9
|
+
*/
|
|
10
|
+
import type { ProjectDetection } from '../types/index.js';
|
|
11
|
+
/**
|
|
12
|
+
* Resolve the starting directory: accepts an existing dir, an existing
|
|
13
|
+
* file (uses its parent), a not-yet-existing path (nearest existing
|
|
14
|
+
* ancestor), and falls back to cwd.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveStartDir(input?: string): Promise<string>;
|
|
17
|
+
/**
|
|
18
|
+
* Detect the Playwright project around `startDir`.
|
|
19
|
+
*
|
|
20
|
+
* Walks upward looking for (in order of precedence) the nearest
|
|
21
|
+
* playwright.config.*, any package.json as a root fallback, and
|
|
22
|
+
* node_modules installs of @playwright/test or playwright.
|
|
23
|
+
*/
|
|
24
|
+
export declare function detectProject(startDir?: string): Promise<ProjectDetection>;
|
|
25
|
+
/** Pick a config by 1-based index or exact path from the candidates list. */
|
|
26
|
+
export declare function selectConfig(candidates: string[], selection?: string | number): string | null;
|
|
27
|
+
/** Install instructions for a project where Playwright is missing. */
|
|
28
|
+
export declare function missingPlaywrightMessage(detection: ProjectDetection): {
|
|
29
|
+
message: string;
|
|
30
|
+
hint: string;
|
|
31
|
+
};
|
|
32
|
+
export interface FindTestFilesOptions {
|
|
33
|
+
/** Scan only this directory (already validated to be inside the root). */
|
|
34
|
+
testDir?: string | null;
|
|
35
|
+
maxDepth?: number;
|
|
36
|
+
limit?: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Recursively find Playwright spec files under `root`, skipping
|
|
40
|
+
* node_modules/build/report directories. Returns sorted, project-relative
|
|
41
|
+
* posix paths.
|
|
42
|
+
*/
|
|
43
|
+
export declare function findTestFiles(root: string, options?: FindTestFilesOptions): Promise<string[]>;
|