playwright-e2e-mcp 0.1.0 → 0.1.2
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 +101 -34
- package/dist/http.d.ts +19 -1
- package/dist/http.js +98 -9
- package/dist/http.js.map +1 -1
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +13 -0
- package/dist/server.js +25 -12
- package/dist/server.js.map +1 -1
- package/dist/tools/compare-visual-state.js +11 -8
- package/dist/tools/compare-visual-state.js.map +1 -1
- package/dist/tools/generate-e2e-test.js +46 -10
- package/dist/tools/generate-e2e-test.js.map +1 -1
- package/dist/tools/inspect-page.js +2 -0
- package/dist/tools/inspect-page.js.map +1 -1
- package/dist/tools/list-tests.js +8 -1
- package/dist/tools/list-tests.js.map +1 -1
- package/dist/tools/run-test.js +1 -1
- package/dist/tools/run-test.js.map +1 -1
- package/dist/tools/shared.d.ts +9 -2
- package/dist/tools/shared.js +135 -10
- package/dist/tools/shared.js.map +1 -1
- package/dist/tools/validate-selector.js +2 -0
- package/dist/tools/validate-selector.js.map +1 -1
- package/dist/types/index.d.ts +7 -0
- package/dist/utils/change-analyzer.js +31 -26
- package/dist/utils/change-analyzer.js.map +1 -1
- package/dist/utils/image-diff.js +23 -4
- package/dist/utils/image-diff.js.map +1 -1
- package/dist/utils/path-utils.d.ts +25 -3
- package/dist/utils/path-utils.js +159 -7
- package/dist/utils/path-utils.js.map +1 -1
- package/dist/utils/playwright-runner.d.ts +14 -10
- package/dist/utils/playwright-runner.js +128 -30
- package/dist/utils/playwright-runner.js.map +1 -1
- package/dist/utils/report-parser.js +7 -6
- package/dist/utils/report-parser.js.map +1 -1
- package/dist/utils/trace-reader.js +23 -6
- package/dist/utils/trace-reader.js.map +1 -1
- package/dist/utils/url-policy.d.ts +29 -0
- package/dist/utils/url-policy.js +99 -0
- package/dist/utils/url-policy.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cross-platform path handling.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Most path logic here is *lexical* (string based, no fs access) so that
|
|
5
5
|
* Windows paths behave correctly when unit tests run on macOS/Linux and
|
|
6
6
|
* vice versa. Backslashes are normalized everywhere, drive letters and
|
|
7
7
|
* UNC prefixes are preserved, and user-supplied paths are sandboxed to
|
|
8
8
|
* the project root to block "../../etc/passwd" style escapes.
|
|
9
|
+
* realPathLenient / assertRealPathInside add the symlink-aware check.
|
|
9
10
|
*/
|
|
10
11
|
/** Replace backslashes with slashes, collapsing duplicate separators. */
|
|
11
12
|
export declare function normalizeSeparators(p: string): string;
|
|
@@ -25,6 +26,8 @@ export declare function resolvePath(base: string, target: string): string;
|
|
|
25
26
|
export declare function isPathInside(child: string, parent: string): boolean;
|
|
26
27
|
/** Expand a leading `~` to the user's home directory. */
|
|
27
28
|
export declare function expandHome(p: string): string;
|
|
29
|
+
/** True when any segment of `p` starts with '-' (it could be parsed as a CLI option). */
|
|
30
|
+
export declare function hasOptionLikeSegment(p: string): boolean;
|
|
28
31
|
/**
|
|
29
32
|
* Sandbox a user-supplied path to the project root.
|
|
30
33
|
*
|
|
@@ -49,7 +52,26 @@ export declare function tempFilePath(prefix: string, extension?: string): string
|
|
|
49
52
|
/** True when a filename looks like a Playwright test/spec file. */
|
|
50
53
|
export declare function isTestFile(p: string): boolean;
|
|
51
54
|
/**
|
|
52
|
-
* Validate
|
|
53
|
-
*
|
|
55
|
+
* Validate one extra Playwright CLI argument: only flags from
|
|
56
|
+
* ALLOWED_CLI_FLAGS get through, in `--flag` or `--flag=value` form, and
|
|
57
|
+
* each value must pass that flag's check.
|
|
54
58
|
*/
|
|
55
59
|
export declare function sanitizeCliArg(arg: string): string;
|
|
60
|
+
/**
|
|
61
|
+
* Symlink-resolved form of `p`. When `p` does not exist yet (a file about
|
|
62
|
+
* to be written), its nearest existing ancestor is resolved and the
|
|
63
|
+
* missing tail re-appended, so a symlinked parent directory is still seen.
|
|
64
|
+
*/
|
|
65
|
+
export declare function realPathLenient(p: string): Promise<string>;
|
|
66
|
+
/**
|
|
67
|
+
* Write `data` to `target` inside `root` without following a symlink at
|
|
68
|
+
* the final path component (a planted link, even a dangling one, could
|
|
69
|
+
* otherwise redirect the write outside the root). Parent directories are
|
|
70
|
+
* created and re-checked against the root after creation.
|
|
71
|
+
*/
|
|
72
|
+
export declare function writeFileInsideRoot(target: string, data: string | Uint8Array, root: string): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Filesystem-aware companion to sanitizeUserPath: throws INVALID_PATH when
|
|
75
|
+
* `target`, after resolving symlinks, is not inside `root` (also resolved).
|
|
76
|
+
*/
|
|
77
|
+
export declare function assertRealPathInside(target: string, root: string): Promise<void>;
|
package/dist/utils/path-utils.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cross-platform path handling.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Most path logic here is *lexical* (string based, no fs access) so that
|
|
5
5
|
* Windows paths behave correctly when unit tests run on macOS/Linux and
|
|
6
6
|
* vice versa. Backslashes are normalized everywhere, drive letters and
|
|
7
7
|
* UNC prefixes are preserved, and user-supplied paths are sandboxed to
|
|
8
8
|
* the project root to block "../../etc/passwd" style escapes.
|
|
9
|
+
* realPathLenient / assertRealPathInside add the symlink-aware check.
|
|
9
10
|
*/
|
|
10
11
|
import { randomBytes } from 'node:crypto';
|
|
12
|
+
import { constants as fsConstants } from 'node:fs';
|
|
13
|
+
import { lstat, mkdir, open, realpath } from 'node:fs/promises';
|
|
11
14
|
import os from 'node:os';
|
|
12
15
|
import path from 'node:path';
|
|
13
16
|
import { PlaywrightMcpError } from '../types/index.js';
|
|
@@ -113,6 +116,12 @@ export function expandHome(p) {
|
|
|
113
116
|
}
|
|
114
117
|
return p;
|
|
115
118
|
}
|
|
119
|
+
/** True when any segment of `p` starts with '-' (it could be parsed as a CLI option). */
|
|
120
|
+
export function hasOptionLikeSegment(p) {
|
|
121
|
+
return normalizeSeparators(p)
|
|
122
|
+
.split('/')
|
|
123
|
+
.some((segment) => segment.startsWith('-'));
|
|
124
|
+
}
|
|
116
125
|
/**
|
|
117
126
|
* Sandbox a user-supplied path to the project root.
|
|
118
127
|
*
|
|
@@ -153,10 +162,16 @@ export function sanitizeUserPath(input, projectRoot) {
|
|
|
153
162
|
const resolved = resolvePath(root, expanded);
|
|
154
163
|
if (!isPathInside(resolved, root)) {
|
|
155
164
|
throw new PlaywrightMcpError(`Path "${raw}" resolves outside the project root`, 'INVALID_PATH', {
|
|
156
|
-
hint: `Only paths inside ${root} are allowed. Directory traversal (..)
|
|
165
|
+
hint: `Only paths inside ${root} are allowed. Directory traversal (..) and absolute paths outside the project are rejected.`,
|
|
157
166
|
details: `resolved=${resolved}`,
|
|
158
167
|
});
|
|
159
168
|
}
|
|
169
|
+
// Checked below the root only, so a root that itself has such a segment still works.
|
|
170
|
+
if (hasOptionLikeSegment(relativeToRoot(root, resolved))) {
|
|
171
|
+
throw new PlaywrightMcpError(`Path "${raw}" has a segment starting with "-"`, 'INVALID_PATH', {
|
|
172
|
+
hint: 'Path segments may not start with "-": the path could be read as a command-line option.',
|
|
173
|
+
});
|
|
174
|
+
}
|
|
160
175
|
return resolved;
|
|
161
176
|
}
|
|
162
177
|
/** Convert a normalized path to the host platform's native form. */
|
|
@@ -192,20 +207,157 @@ export function tempFilePath(prefix, extension = '.json') {
|
|
|
192
207
|
export function isTestFile(p) {
|
|
193
208
|
return TEST_FILE_RE.test(normalizeSeparators(p));
|
|
194
209
|
}
|
|
210
|
+
const intIn = (min, max) => (value) => /^\d+$/.test(value) && Number(value) >= min && Number(value) <= max;
|
|
211
|
+
const oneOf = (...values) => (value) => values.includes(value);
|
|
212
|
+
/** No leading '-' (would be read as another option), no control characters. */
|
|
213
|
+
const plainText = (value) => value !== '' && !value.startsWith('-') && !/[\x00-\x1f\x7f]/.test(value);
|
|
214
|
+
/**
|
|
215
|
+
* Playwright CLI flags callers may pass through `args`, each with the only
|
|
216
|
+
* values it accepts. Anything that points Playwright at another file or
|
|
217
|
+
* directory (--config, --output, --tsconfig, --reporter…), blocks the run
|
|
218
|
+
* (--ui, --debug) or hands a value to another program unchecked is absent.
|
|
219
|
+
* Value-taking flags must use `--flag=value`, so a bare flag can never
|
|
220
|
+
* swallow the arguments appended after it.
|
|
221
|
+
*/
|
|
222
|
+
const ALLOWED_CLI_FLAGS = new Map([
|
|
223
|
+
['--headed', null],
|
|
224
|
+
['-x', null],
|
|
225
|
+
['--fail-on-flaky-tests', null],
|
|
226
|
+
['--forbid-only', null],
|
|
227
|
+
['--fully-parallel', null],
|
|
228
|
+
['--pass-with-no-tests', null],
|
|
229
|
+
['--quiet', null],
|
|
230
|
+
['--ignore-snapshots', null],
|
|
231
|
+
['--no-deps', null],
|
|
232
|
+
['--list', null],
|
|
233
|
+
['--project', { test: (v) => plainText(v) && /^[\w .@:+/-]+$/.test(v), describe: 'a project name' }],
|
|
234
|
+
['--repeat-each', { test: intIn(1, 100), describe: 'an integer 1-100' }],
|
|
235
|
+
['--max-failures', { test: intIn(0, 1000), describe: 'an integer 0-1000' }],
|
|
236
|
+
['--global-timeout', { test: intIn(0, 3_600_000), describe: 'milliseconds, at most 3600000' }],
|
|
237
|
+
['--timeout', { test: intIn(0, 3_600_000), describe: 'milliseconds, at most 3600000' }],
|
|
238
|
+
['--retries', { test: intIn(0, 10), describe: 'an integer 0-10' }],
|
|
239
|
+
['--workers', { test: (v) => intIn(1, 64)(v) || /^([1-9]\d?|100)%$/.test(v), describe: '1-64 or a percentage' }],
|
|
240
|
+
['-j', { test: (v) => intIn(1, 64)(v) || /^([1-9]\d?|100)%$/.test(v), describe: '1-64 or a percentage' }],
|
|
241
|
+
['--shard', { test: (v) => /^[1-9]\d{0,3}\/[1-9]\d{0,3}$/.test(v), describe: 'current/total, e.g. 1/3' }],
|
|
242
|
+
['--grep-invert', { test: plainText, describe: 'a pattern not starting with "-"' }],
|
|
243
|
+
[
|
|
244
|
+
'--trace',
|
|
245
|
+
{
|
|
246
|
+
test: oneOf('on', 'off', 'on-first-retry', 'on-all-retries', 'retain-on-failure', 'retain-on-first-failure', 'retain-on-failure-and-retries'),
|
|
247
|
+
describe: 'a Playwright trace mode',
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
['--update-snapshots', { optional: true, test: oneOf('all', 'changed', 'missing', 'none'), describe: 'all, changed, missing or none' }],
|
|
251
|
+
['-u', { optional: true, test: oneOf('all', 'changed', 'missing', 'none'), describe: 'all, changed, missing or none' }],
|
|
252
|
+
// The value is handed to `git diff`, so it must be a plain ref, never an option.
|
|
253
|
+
[
|
|
254
|
+
'--only-changed',
|
|
255
|
+
{ optional: true, test: (v) => /^[A-Za-z0-9._/~^@{}][A-Za-z0-9._/~^@{}-]*$/.test(v) && v.length <= 200, describe: 'a git ref' },
|
|
256
|
+
],
|
|
257
|
+
]);
|
|
195
258
|
/**
|
|
196
|
-
* Validate
|
|
197
|
-
*
|
|
259
|
+
* Validate one extra Playwright CLI argument: only flags from
|
|
260
|
+
* ALLOWED_CLI_FLAGS get through, in `--flag` or `--flag=value` form, and
|
|
261
|
+
* each value must pass that flag's check.
|
|
198
262
|
*/
|
|
199
263
|
export function sanitizeCliArg(arg) {
|
|
200
264
|
const raw = typeof arg === 'string' ? arg.trim() : '';
|
|
201
265
|
if (raw === '') {
|
|
202
266
|
throw new PlaywrightMcpError('Argument must not be empty', 'INVALID_PATH');
|
|
203
267
|
}
|
|
204
|
-
if (/[\0;&|><`$]/.test(raw)) {
|
|
205
|
-
throw new PlaywrightMcpError(`Argument "${raw}" contains forbidden
|
|
206
|
-
hint: 'Arguments are executed directly (no shell); remove ; & | > < `
|
|
268
|
+
if (/[\0;&|><`$]/.test(raw) || /[\x00-\x1f\x7f]/.test(raw)) {
|
|
269
|
+
throw new PlaywrightMcpError(`Argument "${raw}" contains forbidden characters`, 'INVALID_PATH', {
|
|
270
|
+
hint: 'Arguments are executed directly (no shell); remove control characters and ; & | > < ` $.',
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
const eq = raw.indexOf('=');
|
|
274
|
+
const flag = eq === -1 ? raw : raw.slice(0, eq);
|
|
275
|
+
const value = eq === -1 ? undefined : raw.slice(eq + 1);
|
|
276
|
+
if (!ALLOWED_CLI_FLAGS.has(flag)) {
|
|
277
|
+
throw new PlaywrightMcpError(`Argument "${raw}" is not an allowed Playwright flag`, 'INVALID_PATH', {
|
|
278
|
+
hint: `Allowed flags: ${[...ALLOWED_CLI_FLAGS.keys()].join(', ')}. Pass test files via testFiles and the config via config.`,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
const check = ALLOWED_CLI_FLAGS.get(flag) ?? null;
|
|
282
|
+
if (check === null) {
|
|
283
|
+
if (value !== undefined) {
|
|
284
|
+
throw new PlaywrightMcpError(`Flag "${flag}" does not take a value`, 'INVALID_PATH');
|
|
285
|
+
}
|
|
286
|
+
return raw;
|
|
287
|
+
}
|
|
288
|
+
if (value === undefined) {
|
|
289
|
+
if (check.optional)
|
|
290
|
+
return raw;
|
|
291
|
+
throw new PlaywrightMcpError(`Flag "${flag}" needs a value: use ${flag}=<value>`, 'INVALID_PATH', {
|
|
292
|
+
hint: `${flag} takes ${check.describe}.`,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
if (!check.test(value)) {
|
|
296
|
+
throw new PlaywrightMcpError(`Invalid value for ${flag}: "${value}"`, 'INVALID_PATH', {
|
|
297
|
+
hint: `${flag} takes ${check.describe}.`,
|
|
207
298
|
});
|
|
208
299
|
}
|
|
209
300
|
return raw;
|
|
210
301
|
}
|
|
302
|
+
/**
|
|
303
|
+
* Symlink-resolved form of `p`. When `p` does not exist yet (a file about
|
|
304
|
+
* to be written), its nearest existing ancestor is resolved and the
|
|
305
|
+
* missing tail re-appended, so a symlinked parent directory is still seen.
|
|
306
|
+
*/
|
|
307
|
+
export async function realPathLenient(p) {
|
|
308
|
+
const normalized = normalizePath(p);
|
|
309
|
+
const tail = [];
|
|
310
|
+
let current = normalized;
|
|
311
|
+
for (;;) {
|
|
312
|
+
try {
|
|
313
|
+
const real = normalizePath(await realpath(toNativePath(current)));
|
|
314
|
+
return tail.length === 0 ? real : normalizePath(`${real}/${tail.reverse().join('/')}`);
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
const parent = normalizePath(path.posix.dirname(current));
|
|
318
|
+
if (parent === current || parent === '' || parent === '.')
|
|
319
|
+
return normalized;
|
|
320
|
+
tail.push(path.posix.basename(current));
|
|
321
|
+
current = parent;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Write `data` to `target` inside `root` without following a symlink at
|
|
327
|
+
* the final path component (a planted link, even a dangling one, could
|
|
328
|
+
* otherwise redirect the write outside the root). Parent directories are
|
|
329
|
+
* created and re-checked against the root after creation.
|
|
330
|
+
*/
|
|
331
|
+
export async function writeFileInsideRoot(target, data, root) {
|
|
332
|
+
const native = toNativePath(target);
|
|
333
|
+
const info = await lstat(native).catch(() => null);
|
|
334
|
+
if (info?.isSymbolicLink()) {
|
|
335
|
+
throw new PlaywrightMcpError(`Refusing to write through a symlink: ${normalizePath(target)}`, 'INVALID_PATH', {
|
|
336
|
+
hint: 'Replace the symlink with a regular file or pick another path.',
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
await mkdir(path.dirname(native), { recursive: true });
|
|
340
|
+
await assertRealPathInside(path.posix.dirname(normalizePath(target)), root);
|
|
341
|
+
// O_NOFOLLOW closes the gap between the lstat above and the open (POSIX only).
|
|
342
|
+
const flags = fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_TRUNC | (fsConstants.O_NOFOLLOW ?? 0);
|
|
343
|
+
const handle = await open(native, flags, 0o644);
|
|
344
|
+
try {
|
|
345
|
+
await handle.writeFile(data);
|
|
346
|
+
}
|
|
347
|
+
finally {
|
|
348
|
+
await handle.close();
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Filesystem-aware companion to sanitizeUserPath: throws INVALID_PATH when
|
|
353
|
+
* `target`, after resolving symlinks, is not inside `root` (also resolved).
|
|
354
|
+
*/
|
|
355
|
+
export async function assertRealPathInside(target, root) {
|
|
356
|
+
const [realTarget, realRoot] = await Promise.all([realPathLenient(target), realPathLenient(root)]);
|
|
357
|
+
if (!isPathInside(realTarget, realRoot)) {
|
|
358
|
+
throw new PlaywrightMcpError(`Path "${normalizePath(target)}" resolves outside the project root`, 'INVALID_PATH', {
|
|
359
|
+
hint: `A symlink points outside ${normalizePath(root)}. Only paths inside the project root are allowed.`,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
211
363
|
//# sourceMappingURL=path-utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-utils.js","sourceRoot":"","sources":["../../src/utils/path-utils.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"path-utils.js","sourceRoot":"","sources":["../../src/utils/path-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -37,6 +37,20 @@ export interface ProcessOutcome {
|
|
|
37
37
|
* Children are tracked so shutdown/disconnect can never orphan them.
|
|
38
38
|
*/
|
|
39
39
|
export declare function runProcess(command: string, args: string[], options: RunProcessOptions): Promise<ProcessOutcome>;
|
|
40
|
+
/**
|
|
41
|
+
* Cap how many children may run at once; 0 means no cap. The HTTP bridge
|
|
42
|
+
* sets one (PW_MCP_MAX_CHILDREN, default 4) so remote callers cannot
|
|
43
|
+
* start an unbounded number of browsers and test runs.
|
|
44
|
+
*/
|
|
45
|
+
export declare function setMaxChildren(limit: number): void;
|
|
46
|
+
/**
|
|
47
|
+
* When on (the HTTP bridge turns it on), children get only an allowlisted
|
|
48
|
+
* environment, so code run on behalf of a remote caller cannot read
|
|
49
|
+
* deployment credentials.
|
|
50
|
+
*/
|
|
51
|
+
export declare function setScrubChildEnv(enabled: boolean): void;
|
|
52
|
+
/** Environment for a spawned child: process.env plus overrides, allowlisted when scrubbing. */
|
|
53
|
+
export declare function childEnv(overrides?: Record<string, string>): NodeJS.ProcessEnv;
|
|
40
54
|
/**
|
|
41
55
|
* Force-kill every child still running (and its tree). Called on
|
|
42
56
|
* shutdown and when the MCP client disconnects so no browser or test
|
|
@@ -51,13 +65,3 @@ export declare function killActiveChildren(): number;
|
|
|
51
65
|
export declare function runTests(options: RunTestOptions): Promise<RunTestResult>;
|
|
52
66
|
/** List tests via `playwright test --list`, falling back to file scan. */
|
|
53
67
|
export declare function listTests(options: ListTestsOptions): Promise<ListTestsResult>;
|
|
54
|
-
export interface ServerProbe {
|
|
55
|
-
reachable: boolean;
|
|
56
|
-
status?: number;
|
|
57
|
-
error?: string;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Quick reachability check for the app under test, used to tell the
|
|
61
|
-
* user "start your dev server" instead of surfacing ECONNREFUSED.
|
|
62
|
-
*/
|
|
63
|
-
export declare function probeServer(url: string, timeoutMs?: number): Promise<ServerProbe>;
|
|
@@ -14,7 +14,7 @@ import path from 'node:path';
|
|
|
14
14
|
import { PlaywrightMcpError } from '../types/index.js';
|
|
15
15
|
import { findTestFiles, missingPlaywrightMessage, detectProject, } from './project-detector.js';
|
|
16
16
|
import { extractJsonFromText, extractTestTitles, diagnoseOutput, parseReportJson, parseProjectNames, } from './report-parser.js';
|
|
17
|
-
import { isAbsolutePath, isPathInside, normalizePath, relativeToRoot, sanitizeCliArg, tempFilePath, toNativePath, } from './path-utils.js';
|
|
17
|
+
import { hasOptionLikeSegment, isAbsolutePath, isPathInside, normalizePath, relativeToRoot, sanitizeCliArg, tempFilePath, toNativePath, } from './path-utils.js';
|
|
18
18
|
import { logger } from './logger.js';
|
|
19
19
|
const DEFAULT_RUN_TIMEOUT_MS = 120_000;
|
|
20
20
|
const DEFAULT_LIST_TIMEOUT_MS = 60_000;
|
|
@@ -145,6 +145,21 @@ export function runProcess(command, args, options) {
|
|
|
145
145
|
chunks.shift();
|
|
146
146
|
}
|
|
147
147
|
};
|
|
148
|
+
if (maxChildren > 0 && activeChildren.size >= maxChildren) {
|
|
149
|
+
resolve({
|
|
150
|
+
code: null,
|
|
151
|
+
signalName: null,
|
|
152
|
+
stdout: '',
|
|
153
|
+
stderr: '',
|
|
154
|
+
timedOut: false,
|
|
155
|
+
aborted: false,
|
|
156
|
+
spawnError: {
|
|
157
|
+
code: 'EBUSY',
|
|
158
|
+
message: `Too many runs in progress (limit ${maxChildren}). Try again when one finishes.`,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
148
163
|
let child;
|
|
149
164
|
try {
|
|
150
165
|
child = spawn(command, args, {
|
|
@@ -238,6 +253,86 @@ export function runProcess(command, args, options) {
|
|
|
238
253
|
}
|
|
239
254
|
/** Every child spawned by runProcess that has not settled yet. */
|
|
240
255
|
const activeChildren = new Set();
|
|
256
|
+
let maxChildren = 0;
|
|
257
|
+
/**
|
|
258
|
+
* Cap how many children may run at once; 0 means no cap. The HTTP bridge
|
|
259
|
+
* sets one (PW_MCP_MAX_CHILDREN, default 4) so remote callers cannot
|
|
260
|
+
* start an unbounded number of browsers and test runs.
|
|
261
|
+
*/
|
|
262
|
+
export function setMaxChildren(limit) {
|
|
263
|
+
maxChildren = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 0;
|
|
264
|
+
}
|
|
265
|
+
let scrubChildEnv = false;
|
|
266
|
+
/**
|
|
267
|
+
* When on (the HTTP bridge turns it on), children get only an allowlisted
|
|
268
|
+
* environment, so code run on behalf of a remote caller cannot read
|
|
269
|
+
* deployment credentials.
|
|
270
|
+
*/
|
|
271
|
+
export function setScrubChildEnv(enabled) {
|
|
272
|
+
scrubChildEnv = enabled;
|
|
273
|
+
}
|
|
274
|
+
/** Variables a child keeps in scrubbed mode (compared upper-case). */
|
|
275
|
+
const CHILD_ENV_ALLOW = new Set([
|
|
276
|
+
'PATH',
|
|
277
|
+
'PATHEXT',
|
|
278
|
+
'HOME',
|
|
279
|
+
'USERPROFILE',
|
|
280
|
+
'TMPDIR',
|
|
281
|
+
'TEMP',
|
|
282
|
+
'TMP',
|
|
283
|
+
'LANG',
|
|
284
|
+
'LANGUAGE',
|
|
285
|
+
'TZ',
|
|
286
|
+
'CI',
|
|
287
|
+
'SYSTEMROOT',
|
|
288
|
+
'SYSTEMDRIVE',
|
|
289
|
+
'WINDIR',
|
|
290
|
+
'COMSPEC',
|
|
291
|
+
'APPDATA',
|
|
292
|
+
'LOCALAPPDATA',
|
|
293
|
+
'PROGRAMDATA',
|
|
294
|
+
'PROGRAMFILES',
|
|
295
|
+
'PROGRAMFILES(X86)',
|
|
296
|
+
'XDG_CACHE_HOME',
|
|
297
|
+
'XDG_CONFIG_HOME',
|
|
298
|
+
'XDG_RUNTIME_DIR',
|
|
299
|
+
'DISPLAY',
|
|
300
|
+
]);
|
|
301
|
+
const CHILD_ENV_ALLOW_PREFIXES = ['LC_', 'NPM_CONFIG_', 'PLAYWRIGHT_'];
|
|
302
|
+
/** Even allowlisted names are dropped when they look like a secret. */
|
|
303
|
+
const SECRET_ENV_RE = /(TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|PRIVATE|API_?KEY|ACCESS_?KEY|AUTH|SESSION|COOKIE|DATABASE_URL|_DSN$)/i;
|
|
304
|
+
/** Names listed in PW_MCP_PASSTHROUGH_ENV (comma-separated), never the bridge token. */
|
|
305
|
+
function passthroughNames() {
|
|
306
|
+
return new Set((process.env.PW_MCP_PASSTHROUGH_ENV ?? '')
|
|
307
|
+
.split(',')
|
|
308
|
+
.map((name) => name.trim().toUpperCase())
|
|
309
|
+
.filter((name) => name && name !== 'PW_MCP_HTTP_TOKEN'));
|
|
310
|
+
}
|
|
311
|
+
/** A URL with userinfo (`scheme://user:pass@host`), e.g. an authenticated proxy. */
|
|
312
|
+
const URL_CREDENTIALS_RE = /:\/\/[^/\s@]*@/;
|
|
313
|
+
function keepInScrubbedEnv(key, value, passthrough) {
|
|
314
|
+
const upper = key.toUpperCase();
|
|
315
|
+
if (upper === 'PW_MCP_HTTP_TOKEN')
|
|
316
|
+
return false;
|
|
317
|
+
if (passthrough.has(upper))
|
|
318
|
+
return true;
|
|
319
|
+
if (SECRET_ENV_RE.test(key))
|
|
320
|
+
return false;
|
|
321
|
+
if (value !== undefined && URL_CREDENTIALS_RE.test(value))
|
|
322
|
+
return false;
|
|
323
|
+
return CHILD_ENV_ALLOW.has(upper) || CHILD_ENV_ALLOW_PREFIXES.some((prefix) => upper.startsWith(prefix));
|
|
324
|
+
}
|
|
325
|
+
/** Environment for a spawned child: process.env plus overrides, allowlisted when scrubbing. */
|
|
326
|
+
export function childEnv(overrides = {}) {
|
|
327
|
+
const env = {};
|
|
328
|
+
const passthrough = scrubChildEnv ? passthroughNames() : new Set();
|
|
329
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
330
|
+
if (scrubChildEnv && !keepInScrubbedEnv(key, value, passthrough))
|
|
331
|
+
continue;
|
|
332
|
+
env[key] = value;
|
|
333
|
+
}
|
|
334
|
+
return { ...env, ...overrides };
|
|
335
|
+
}
|
|
241
336
|
/**
|
|
242
337
|
* Force-kill every child still running (and its tree). Called on
|
|
243
338
|
* shutdown and when the MCP client disconnects so no browser or test
|
|
@@ -260,6 +355,13 @@ function sanitizeTestFile(file, root) {
|
|
|
260
355
|
hint: `Only test paths inside ${root} are allowed.`,
|
|
261
356
|
});
|
|
262
357
|
}
|
|
358
|
+
// Test files go on the command line as positionals; a segment like
|
|
359
|
+
// "--output=/x" would be parsed by Playwright as an option.
|
|
360
|
+
if (hasOptionLikeSegment(relativeToRoot(root, resolved))) {
|
|
361
|
+
throw new PlaywrightMcpError(`Test path "${file}" has a segment starting with "-"`, 'INVALID_PATH', {
|
|
362
|
+
hint: 'Rename the file or folder so no path segment starts with "-".',
|
|
363
|
+
});
|
|
364
|
+
}
|
|
263
365
|
return resolved;
|
|
264
366
|
}
|
|
265
367
|
/** Best-effort `retries: N` from the config source text. */
|
|
@@ -383,13 +485,18 @@ export async function runTests(options) {
|
|
|
383
485
|
args.push('--trace=retain-on-failure');
|
|
384
486
|
for (const extra of options.extraArgs ?? [])
|
|
385
487
|
args.push(sanitizeCliArg(extra));
|
|
488
|
+
// The JSON reporter must be the only reporter and come last: results are
|
|
489
|
+
// parsed from its output. sanitizeCliArg rejects --reporter; this keeps
|
|
490
|
+
// the invariant if that ever changes.
|
|
491
|
+
if (args.some((arg) => arg.startsWith('--reporter'))) {
|
|
492
|
+
throw new PlaywrightMcpError('Caller arguments may not set --reporter', 'INVALID_PATH');
|
|
493
|
+
}
|
|
386
494
|
args.push('--reporter=json');
|
|
387
|
-
const env = {
|
|
388
|
-
...process.env,
|
|
495
|
+
const env = childEnv({
|
|
389
496
|
PLAYWRIGHT_JSON_OUTPUT_NAME: reportPath,
|
|
390
497
|
FORCE_COLOR: '0',
|
|
391
498
|
PW_TEST_HTML_REPORT_OPEN: 'never',
|
|
392
|
-
};
|
|
499
|
+
});
|
|
393
500
|
const timeoutMs = clampInt(options.timeoutMs, 1_000, 3_600_000) ?? DEFAULT_RUN_TIMEOUT_MS;
|
|
394
501
|
const startedAt = Date.now();
|
|
395
502
|
logger.debug('Starting Playwright run', { args: args.join(' '), cwd: root, timeoutMs });
|
|
@@ -425,7 +532,9 @@ export async function runTests(options) {
|
|
|
425
532
|
hint =
|
|
426
533
|
outcome.spawnError.code === 'ENOENT'
|
|
427
534
|
? 'Node.js could not be located on PATH. Reinstall Node >= 18 and retry.'
|
|
428
|
-
:
|
|
535
|
+
: outcome.spawnError.code === 'EBUSY'
|
|
536
|
+
? 'The server is at its limit of concurrent runs; retry when one finishes.'
|
|
537
|
+
: `Failed to start Playwright: ${outcome.spawnError.message}`;
|
|
429
538
|
}
|
|
430
539
|
else if (outcome.aborted) {
|
|
431
540
|
errorKind = 'CLIENT_DISCONNECT';
|
|
@@ -486,17 +595,23 @@ async function runPlaywrightList(cli, options, configPath) {
|
|
|
486
595
|
if (configPath)
|
|
487
596
|
args.push('--config', toNativePath(configPath));
|
|
488
597
|
args.push('--reporter=json');
|
|
489
|
-
const env = {
|
|
490
|
-
...process.env,
|
|
598
|
+
const env = childEnv({
|
|
491
599
|
PLAYWRIGHT_JSON_OUTPUT_NAME: listReportPath,
|
|
492
600
|
FORCE_COLOR: '0',
|
|
493
|
-
};
|
|
601
|
+
});
|
|
494
602
|
const outcome = await runProcess(process.execPath, [cli, ...args], {
|
|
495
603
|
cwd: root,
|
|
496
604
|
env,
|
|
497
605
|
timeoutMs: DEFAULT_LIST_TIMEOUT_MS,
|
|
498
606
|
signal: options.signal,
|
|
499
607
|
});
|
|
608
|
+
if (outcome.spawnError) {
|
|
609
|
+
throw new PlaywrightMcpError(outcome.spawnError.message, 'SPAWN_FAILED', {
|
|
610
|
+
hint: outcome.spawnError.code === 'EBUSY'
|
|
611
|
+
? 'The server is at its limit of concurrent runs; retry when one finishes.'
|
|
612
|
+
: 'Could not start Playwright.',
|
|
613
|
+
});
|
|
614
|
+
}
|
|
500
615
|
const raw = (await readFileText(listReportPath)) ?? extractJsonFromText(outcome.stdout) ?? undefined;
|
|
501
616
|
if (raw === undefined) {
|
|
502
617
|
const diagnosis = diagnoseOutput(outcome.stderr || outcome.stdout);
|
|
@@ -525,8 +640,11 @@ export async function listTests(options) {
|
|
|
525
640
|
throw new PlaywrightMcpError(`Config file not found: ${configPath}`, 'NO_CONFIG');
|
|
526
641
|
}
|
|
527
642
|
}
|
|
528
|
-
const cli = await resolvePlaywrightCli(root);
|
|
529
|
-
if (
|
|
643
|
+
const cli = options.noSpawn ? null : await resolvePlaywrightCli(root);
|
|
644
|
+
if (options.noSpawn) {
|
|
645
|
+
// Source scan only (restricted callers); not an error.
|
|
646
|
+
}
|
|
647
|
+
else if (cli === null) {
|
|
530
648
|
const detection = await detectProject(root);
|
|
531
649
|
const { message, hint } = missingPlaywrightMessage(detection);
|
|
532
650
|
error = { kind: 'NO_PLAYWRIGHT', message, hint };
|
|
@@ -571,24 +689,4 @@ export async function listTests(options) {
|
|
|
571
689
|
error,
|
|
572
690
|
};
|
|
573
691
|
}
|
|
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
692
|
//# sourceMappingURL=playwright-runner.js.map
|