playwright-e2e-mcp 0.9.0 → 0.11.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 +57 -18
- package/dist/apps/index.d.ts +29 -0
- package/dist/apps/index.js +52 -0
- package/dist/apps/index.js.map +1 -0
- package/dist/apps/test-report-html.d.ts +15 -0
- package/dist/apps/test-report-html.js +430 -0
- package/dist/apps/test-report-html.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/oauth.d.ts +12 -2
- package/dist/oauth.js +44 -6
- package/dist/oauth.js.map +1 -1
- package/dist/server.d.ts +14 -2
- package/dist/server.js +107 -8
- package/dist/server.js.map +1 -1
- package/dist/tools/audit-tests.js +24 -6
- package/dist/tools/audit-tests.js.map +1 -1
- package/dist/tools/compare-visual-state.js +7 -1
- package/dist/tools/compare-visual-state.js.map +1 -1
- package/dist/tools/generate-e2e-test.js +27 -12
- package/dist/tools/generate-e2e-test.js.map +1 -1
- package/dist/tools/inspect-page.js +5 -12
- package/dist/tools/inspect-page.js.map +1 -1
- package/dist/tools/quarantine.js +7 -1
- package/dist/tools/quarantine.js.map +1 -1
- package/dist/tools/shared.d.ts +10 -0
- package/dist/tools/shared.js +15 -0
- package/dist/tools/shared.js.map +1 -1
- package/dist/tools/structured.d.ts +2 -0
- package/dist/tools/structured.js +4 -2
- package/dist/tools/structured.js.map +1 -1
- package/dist/tools/suggest-fix.js +6 -1
- package/dist/tools/suggest-fix.js.map +1 -1
- package/dist/types/index.d.ts +9 -0
- package/dist/utils/client-metadata.d.ts +46 -0
- package/dist/utils/client-metadata.js +174 -0
- package/dist/utils/client-metadata.js.map +1 -0
- package/dist/utils/compact-schema.d.ts +20 -0
- package/dist/utils/compact-schema.js +55 -0
- package/dist/utils/compact-schema.js.map +1 -0
- package/dist/utils/confirm.d.ts +68 -0
- package/dist/utils/confirm.js +120 -0
- package/dist/utils/confirm.js.map +1 -0
- package/dist/utils/locator-map.d.ts +25 -0
- package/dist/utils/locator-map.js +113 -0
- package/dist/utils/locator-map.js.map +1 -0
- package/examples/github-actions/playwright-e2e.yml +1 -1
- package/package.json +1 -1
- package/skill/playwright-e2e-mcp/SKILL.md +12 -5
- package/skill/playwright-e2e-mcp/references/setup.md +2 -2
- package/skill/playwright-e2e-mcp/references/tools.md +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-metadata.js","sourceRoot":"","sources":["../../src/utils/client-metadata.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smaller advertised input schemas.
|
|
3
|
+
*
|
|
4
|
+
* Tool definitions are sent to the model with every request, so each
|
|
5
|
+
* keyword in them costs tokens on every turn. This drops the keywords an
|
|
6
|
+
* agent does not need to write a correct call (length and item limits,
|
|
7
|
+
* `propertyNames`, `$schema`, an empty `additionalProperties`). The server
|
|
8
|
+
* still validates every call against the full schema, so an argument
|
|
9
|
+
* outside a limit is refused with a message that names the limit.
|
|
10
|
+
*
|
|
11
|
+
* Numeric `minimum` / `maximum` stay: they steer timeouts and sizes, where
|
|
12
|
+
* a guessed value would otherwise cost a failed call.
|
|
13
|
+
*/
|
|
14
|
+
import type { StandardSchemaWithJSON } from '@modelcontextprotocol/server';
|
|
15
|
+
export declare function compactJsonSchema(node: unknown, inMap?: boolean): unknown;
|
|
16
|
+
/**
|
|
17
|
+
* Wrap a schema so the JSON Schema listed in tools/list is compacted while
|
|
18
|
+
* validation (`~standard.validate`) still runs the original schema.
|
|
19
|
+
*/
|
|
20
|
+
export declare function withCompactJsonSchema(schema: StandardSchemaWithJSON): StandardSchemaWithJSON;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smaller advertised input schemas.
|
|
3
|
+
*
|
|
4
|
+
* Tool definitions are sent to the model with every request, so each
|
|
5
|
+
* keyword in them costs tokens on every turn. This drops the keywords an
|
|
6
|
+
* agent does not need to write a correct call (length and item limits,
|
|
7
|
+
* `propertyNames`, `$schema`, an empty `additionalProperties`). The server
|
|
8
|
+
* still validates every call against the full schema, so an argument
|
|
9
|
+
* outside a limit is refused with a message that names the limit.
|
|
10
|
+
*
|
|
11
|
+
* Numeric `minimum` / `maximum` stay: they steer timeouts and sizes, where
|
|
12
|
+
* a guessed value would otherwise cost a failed call.
|
|
13
|
+
*/
|
|
14
|
+
const DROPPED = new Set(['$schema', 'propertyNames', 'minLength', 'maxLength', 'minItems', 'maxItems']);
|
|
15
|
+
/** Keys whose value is a map of names to schemas, where names are not keywords. */
|
|
16
|
+
const SCHEMA_MAPS = new Set(['properties', 'patternProperties', '$defs', 'definitions']);
|
|
17
|
+
export function compactJsonSchema(node, inMap = false) {
|
|
18
|
+
if (Array.isArray(node))
|
|
19
|
+
return node.map((item) => compactJsonSchema(item));
|
|
20
|
+
if (!node || typeof node !== 'object')
|
|
21
|
+
return node;
|
|
22
|
+
const out = {};
|
|
23
|
+
for (const [key, value] of Object.entries(node)) {
|
|
24
|
+
if (!inMap) {
|
|
25
|
+
if (DROPPED.has(key))
|
|
26
|
+
continue;
|
|
27
|
+
// `additionalProperties: {}` allows anything, the same as leaving it out.
|
|
28
|
+
if (key === 'additionalProperties' && value && typeof value === 'object' && Object.keys(value).length === 0)
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
out[key] = compactJsonSchema(value, !inMap && SCHEMA_MAPS.has(key));
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Wrap a schema so the JSON Schema listed in tools/list is compacted while
|
|
37
|
+
* validation (`~standard.validate`) still runs the original schema.
|
|
38
|
+
*/
|
|
39
|
+
export function withCompactJsonSchema(schema) {
|
|
40
|
+
const std = schema['~standard'];
|
|
41
|
+
if (!std.jsonSchema)
|
|
42
|
+
return schema;
|
|
43
|
+
const jsonSchema = std.jsonSchema;
|
|
44
|
+
return {
|
|
45
|
+
'~standard': {
|
|
46
|
+
...std,
|
|
47
|
+
validate: std.validate.bind(std),
|
|
48
|
+
jsonSchema: {
|
|
49
|
+
input: (options) => compactJsonSchema(jsonSchema.input(options)),
|
|
50
|
+
output: (options) => compactJsonSchema(jsonSchema.output(options)),
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=compact-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compact-schema.js","sourceRoot":"","sources":["../../src/utils/compact-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC;AAExG,mFAAmF;AACnF,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,YAAY,EAAE,mBAAmB,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;AAEzF,MAAM,UAAU,iBAAiB,CAAC,IAAa,EAAE,KAAK,GAAG,KAAK;IAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5E,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAA+B,CAAC,EAAE,CAAC;QAC3E,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YAC/B,0EAA0E;YAC1E,IAAI,GAAG,KAAK,sBAAsB,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;QACxH,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,GAAG,iBAAiB,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAA8B;IAClE,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAwC,CAAC;IACvE,IAAI,CAAC,GAAG,CAAC,UAAU;QAAE,OAAO,MAAM,CAAC;IACnC,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC;IAClC,OAAO;QACL,WAAW,EAAE;YACX,GAAG,GAAG;YACN,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;YAChC,UAAU,EAAE;gBACV,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,iBAAiB,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAA4B;gBAC3F,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,iBAAiB,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAA4B;aAC9F;SACF;KACwB,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ask the person before a tool changes files in their project.
|
|
3
|
+
*
|
|
4
|
+
* Tools that write test code or project state (suggest-fix `apply`,
|
|
5
|
+
* generate-e2e-test, quarantine add/remove, compare-visual-state `baseline`)
|
|
6
|
+
* call `ctx.confirmWrite()` right before the write, with the exact change
|
|
7
|
+
* they are about to make. When the client can show a form (MCP elicitation),
|
|
8
|
+
* the server answers the call with an `input_required` result (the
|
|
9
|
+
* 2026-07-28 multi-round-trip pattern; the SDK turns it into a classic
|
|
10
|
+
* elicitation request for 2025 clients). The client shows the change, the
|
|
11
|
+
* person says yes or no, and the client retries the call with the answer.
|
|
12
|
+
*
|
|
13
|
+
* The approval is bound to the change: the question's key is a hash of the
|
|
14
|
+
* tool, title and detail, so if the retried call would write something
|
|
15
|
+
* different it asks again instead of reusing an old "yes".
|
|
16
|
+
*
|
|
17
|
+
* PW_MCP_CONFIRM_WRITES:
|
|
18
|
+
* auto (default) ask when the client supports elicitation; otherwise
|
|
19
|
+
* write as before and say no one was asked
|
|
20
|
+
* always ask, and refuse to write when the client cannot ask
|
|
21
|
+
* off never ask
|
|
22
|
+
*/
|
|
23
|
+
import type { InputRequiredResult } from '@modelcontextprotocol/server';
|
|
24
|
+
export type ConfirmMode = 'auto' | 'always' | 'off';
|
|
25
|
+
/** What the tool is about to do, shown to the person. */
|
|
26
|
+
export interface WriteConfirmation {
|
|
27
|
+
/** One line: "Apply this fix to tests/cart.spec.ts:42?" */
|
|
28
|
+
title: string;
|
|
29
|
+
/** The change itself (a diff, the file to be written, the entry to add). */
|
|
30
|
+
detail: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* - `approved`: the person said yes.
|
|
34
|
+
* - `declined`: the person said no (or cancelled): do not write.
|
|
35
|
+
* - `not-asked`: confirmation is off, or the client cannot ask: write as before.
|
|
36
|
+
*/
|
|
37
|
+
export type ConfirmOutcome = 'approved' | 'declined' | 'not-asked';
|
|
38
|
+
export type ConfirmWrite = (request: WriteConfirmation) => Promise<ConfirmOutcome>;
|
|
39
|
+
/**
|
|
40
|
+
* Thrown by `confirmWrite` when the person has to be asked. It carries the
|
|
41
|
+
* `input_required` result; the server returns it instead of a tool result.
|
|
42
|
+
* Tools must let it through (guard() rethrows it).
|
|
43
|
+
*/
|
|
44
|
+
export declare class ConfirmationPending extends Error {
|
|
45
|
+
readonly result: InputRequiredResult;
|
|
46
|
+
constructor(result: InputRequiredResult);
|
|
47
|
+
}
|
|
48
|
+
export declare function confirmMode(value?: string | undefined): ConfirmMode;
|
|
49
|
+
/** Longest change shown in the question; clients show it as plain text. */
|
|
50
|
+
export declare const MAX_DETAIL_CHARS = 4000;
|
|
51
|
+
/** Whether declared client capabilities include form-mode elicitation. */
|
|
52
|
+
export declare function supportsFormElicitation(capabilities: unknown): boolean;
|
|
53
|
+
/** The input key for one exact change: a new change gets a new question. */
|
|
54
|
+
export declare function confirmationKey(tool: string, request: WriteConfirmation): string;
|
|
55
|
+
export interface ConfirmerOptions {
|
|
56
|
+
tool: string;
|
|
57
|
+
/** Retried-call answers (`ctx.mcpReq.inputResponses`). */
|
|
58
|
+
inputResponses?: Record<string, unknown>;
|
|
59
|
+
/** Per-request envelope (2026-07-28) or undefined on 2025 connections. */
|
|
60
|
+
envelope?: Record<string, unknown>;
|
|
61
|
+
/** Capabilities from the 2025 initialize handshake. */
|
|
62
|
+
sessionCapabilities?: unknown;
|
|
63
|
+
mode?: ConfirmMode;
|
|
64
|
+
}
|
|
65
|
+
/** Build the `confirmWrite` a tool call gets in its context. */
|
|
66
|
+
export declare function createConfirmWrite(options: ConfirmerOptions): ConfirmWrite;
|
|
67
|
+
/** The line tools add to a result when the person was or was not asked. */
|
|
68
|
+
export declare function confirmationNote(outcome: ConfirmOutcome): string | undefined;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ask the person before a tool changes files in their project.
|
|
3
|
+
*
|
|
4
|
+
* Tools that write test code or project state (suggest-fix `apply`,
|
|
5
|
+
* generate-e2e-test, quarantine add/remove, compare-visual-state `baseline`)
|
|
6
|
+
* call `ctx.confirmWrite()` right before the write, with the exact change
|
|
7
|
+
* they are about to make. When the client can show a form (MCP elicitation),
|
|
8
|
+
* the server answers the call with an `input_required` result (the
|
|
9
|
+
* 2026-07-28 multi-round-trip pattern; the SDK turns it into a classic
|
|
10
|
+
* elicitation request for 2025 clients). The client shows the change, the
|
|
11
|
+
* person says yes or no, and the client retries the call with the answer.
|
|
12
|
+
*
|
|
13
|
+
* The approval is bound to the change: the question's key is a hash of the
|
|
14
|
+
* tool, title and detail, so if the retried call would write something
|
|
15
|
+
* different it asks again instead of reusing an old "yes".
|
|
16
|
+
*
|
|
17
|
+
* PW_MCP_CONFIRM_WRITES:
|
|
18
|
+
* auto (default) ask when the client supports elicitation; otherwise
|
|
19
|
+
* write as before and say no one was asked
|
|
20
|
+
* always ask, and refuse to write when the client cannot ask
|
|
21
|
+
* off never ask
|
|
22
|
+
*/
|
|
23
|
+
import { createHash } from 'node:crypto';
|
|
24
|
+
import { CLIENT_CAPABILITIES_META_KEY, inputRequired, inputResponse } from '@modelcontextprotocol/server';
|
|
25
|
+
import { PlaywrightMcpError } from '../types/index.js';
|
|
26
|
+
/**
|
|
27
|
+
* Thrown by `confirmWrite` when the person has to be asked. It carries the
|
|
28
|
+
* `input_required` result; the server returns it instead of a tool result.
|
|
29
|
+
* Tools must let it through (guard() rethrows it).
|
|
30
|
+
*/
|
|
31
|
+
export class ConfirmationPending extends Error {
|
|
32
|
+
result;
|
|
33
|
+
constructor(result) {
|
|
34
|
+
super('waiting for the person to confirm a file change');
|
|
35
|
+
this.name = 'ConfirmationPending';
|
|
36
|
+
this.result = result;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function confirmMode(value = process.env.PW_MCP_CONFIRM_WRITES) {
|
|
40
|
+
const normalized = (value ?? '').trim().toLowerCase();
|
|
41
|
+
if (normalized === 'off' || normalized === '0' || normalized === 'false')
|
|
42
|
+
return 'off';
|
|
43
|
+
if (normalized === 'always' || normalized === 'require' || normalized === 'required')
|
|
44
|
+
return 'always';
|
|
45
|
+
return 'auto';
|
|
46
|
+
}
|
|
47
|
+
/** Longest change shown in the question; clients show it as plain text. */
|
|
48
|
+
export const MAX_DETAIL_CHARS = 4_000;
|
|
49
|
+
/** Whether declared client capabilities include form-mode elicitation. */
|
|
50
|
+
export function supportsFormElicitation(capabilities) {
|
|
51
|
+
if (!capabilities || typeof capabilities !== 'object')
|
|
52
|
+
return false;
|
|
53
|
+
const elicitation = capabilities.elicitation;
|
|
54
|
+
if (!elicitation || typeof elicitation !== 'object')
|
|
55
|
+
return false;
|
|
56
|
+
const modes = elicitation;
|
|
57
|
+
// `{}` (2025-06-18) means form mode; a client that lists modes must list form.
|
|
58
|
+
if ('form' in modes || 'url' in modes)
|
|
59
|
+
return 'form' in modes;
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
/** The input key for one exact change: a new change gets a new question. */
|
|
63
|
+
export function confirmationKey(tool, request) {
|
|
64
|
+
const digest = createHash('sha256').update(`${tool}\n${request.title}\n${request.detail}`).digest('hex');
|
|
65
|
+
return `confirm_write_${digest.slice(0, 20)}`;
|
|
66
|
+
}
|
|
67
|
+
function questionText(tool, request) {
|
|
68
|
+
const detail = request.detail.length > MAX_DETAIL_CHARS
|
|
69
|
+
? `${request.detail.slice(0, MAX_DETAIL_CHARS)}\n… (${request.detail.length - MAX_DETAIL_CHARS} more characters)`
|
|
70
|
+
: request.detail;
|
|
71
|
+
return `${request.title}\n\n${detail}\n\n(${tool} asks before changing files in your project. Set PW_MCP_CONFIRM_WRITES=off to stop asking.)`;
|
|
72
|
+
}
|
|
73
|
+
/** Build the `confirmWrite` a tool call gets in its context. */
|
|
74
|
+
export function createConfirmWrite(options) {
|
|
75
|
+
return async (request) => {
|
|
76
|
+
const mode = options.mode ?? confirmMode();
|
|
77
|
+
if (mode === 'off')
|
|
78
|
+
return 'not-asked';
|
|
79
|
+
const key = confirmationKey(options.tool, request);
|
|
80
|
+
const answer = inputResponse(options.inputResponses, key);
|
|
81
|
+
if (answer.kind === 'elicit') {
|
|
82
|
+
return answer.action === 'accept' && answer.content?.confirm === true ? 'approved' : 'declined';
|
|
83
|
+
}
|
|
84
|
+
const capabilities = options.envelope?.[CLIENT_CAPABILITIES_META_KEY] ?? options.sessionCapabilities;
|
|
85
|
+
if (!supportsFormElicitation(capabilities)) {
|
|
86
|
+
if (mode === 'always') {
|
|
87
|
+
throw new PlaywrightMcpError(`Not written: ${request.title.replace(/\?$/, '')} needs your confirmation, and this client cannot ask`, 'INVALID_INPUT', {
|
|
88
|
+
hint: 'PW_MCP_CONFIRM_WRITES=always refuses file changes in clients without elicitation. Use a client that supports it, or set PW_MCP_CONFIRM_WRITES=auto.',
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return 'not-asked';
|
|
92
|
+
}
|
|
93
|
+
throw new ConfirmationPending(inputRequired({
|
|
94
|
+
inputRequests: {
|
|
95
|
+
[key]: inputRequired.elicit({
|
|
96
|
+
message: questionText(options.tool, request),
|
|
97
|
+
requestedSchema: {
|
|
98
|
+
type: 'object',
|
|
99
|
+
properties: {
|
|
100
|
+
confirm: {
|
|
101
|
+
type: 'boolean',
|
|
102
|
+
title: 'Make this change',
|
|
103
|
+
description: 'Yes writes the change. No leaves your files as they are.',
|
|
104
|
+
default: false,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
required: ['confirm'],
|
|
108
|
+
},
|
|
109
|
+
}),
|
|
110
|
+
},
|
|
111
|
+
}));
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/** The line tools add to a result when the person was or was not asked. */
|
|
115
|
+
export function confirmationNote(outcome) {
|
|
116
|
+
if (outcome === 'approved')
|
|
117
|
+
return '> ✅ You confirmed this change.';
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=confirm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirm.js","sourceRoot":"","sources":["../../src/utils/confirm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact rendering of inspect-page's locator map.
|
|
3
|
+
*
|
|
4
|
+
* Two things keep it short without losing what an agent acts on:
|
|
5
|
+
*
|
|
6
|
+
* - Alternatives are listed only when they add a different, stable way to
|
|
7
|
+
* reach the element (a test id or role next to a text locator, say).
|
|
8
|
+
* getByText and raw CSS alternatives are dropped: the best locator is
|
|
9
|
+
* already verified unique.
|
|
10
|
+
* - Repeated blocks (product cards, table rows, list items) are shown once
|
|
11
|
+
* and the rest are summarised by name, since their locators differ only
|
|
12
|
+
* in the item's name or number.
|
|
13
|
+
*/
|
|
14
|
+
import type { ElementInfo } from '../types/index.js';
|
|
15
|
+
export declare function renderLocatorLine(element: ElementInfo, position: number): string;
|
|
16
|
+
/**
|
|
17
|
+
* The shape of an element's locator with every name and number blanked,
|
|
18
|
+
* so the "Add to cart" button of card 3 and card 7 compare equal.
|
|
19
|
+
*/
|
|
20
|
+
export declare function locatorShape(element: ElementInfo): string;
|
|
21
|
+
/**
|
|
22
|
+
* Render the locator map, numbered by position in `elements`, with
|
|
23
|
+
* repeated blocks collapsed after their first occurrence.
|
|
24
|
+
*/
|
|
25
|
+
export declare function renderLocatorMap(elements: ElementInfo[]): string[];
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact rendering of inspect-page's locator map.
|
|
3
|
+
*
|
|
4
|
+
* Two things keep it short without losing what an agent acts on:
|
|
5
|
+
*
|
|
6
|
+
* - Alternatives are listed only when they add a different, stable way to
|
|
7
|
+
* reach the element (a test id or role next to a text locator, say).
|
|
8
|
+
* getByText and raw CSS alternatives are dropped: the best locator is
|
|
9
|
+
* already verified unique.
|
|
10
|
+
* - Repeated blocks (product cards, table rows, list items) are shown once
|
|
11
|
+
* and the rest are summarised by name, since their locators differ only
|
|
12
|
+
* in the item's name or number.
|
|
13
|
+
*/
|
|
14
|
+
import { formatLocator } from './locator-expr.js';
|
|
15
|
+
/** Alternatives worth a reader's tokens: they address the element another stable way. */
|
|
16
|
+
const USEFUL_ALT = /^(getByTestId|getByRole|getByLabel|getByPlaceholder)\(/;
|
|
17
|
+
function clip(text, max) {
|
|
18
|
+
const flat = text.replace(/\s+/g, ' ').trim();
|
|
19
|
+
return flat.length > max ? `${flat.slice(0, max - 1)}…` : flat;
|
|
20
|
+
}
|
|
21
|
+
function method(locator) {
|
|
22
|
+
return locator.slice(0, locator.indexOf('(') + 1);
|
|
23
|
+
}
|
|
24
|
+
function best(element) {
|
|
25
|
+
const chain = element.locators?.[0];
|
|
26
|
+
return chain ? formatLocator(chain) : element.selector;
|
|
27
|
+
}
|
|
28
|
+
export function renderLocatorLine(element, position) {
|
|
29
|
+
const first = best(element);
|
|
30
|
+
const alts = (element.locators ?? [])
|
|
31
|
+
.slice(1)
|
|
32
|
+
.map((chain) => formatLocator(chain))
|
|
33
|
+
.filter((alt) => USEFUL_ALT.test(alt) && method(alt) !== method(first))
|
|
34
|
+
.slice(0, 1)
|
|
35
|
+
.map((alt) => `\`${alt}\``);
|
|
36
|
+
const label = element.role ? `${element.role}${element.name ? ` "${clip(element.name, 60)}"` : ''}` : element.tag;
|
|
37
|
+
return `${position}. \`${first}\` — ${label}${alts.length > 0 ? ` · also ${alts.join(', ')}` : ''}`;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The shape of an element's locator with every name and number blanked,
|
|
41
|
+
* so the "Add to cart" button of card 3 and card 7 compare equal.
|
|
42
|
+
*/
|
|
43
|
+
export function locatorShape(element) {
|
|
44
|
+
const shape = best(element)
|
|
45
|
+
.replace(/'(?:[^'\\]|\\.)*'/g, "'…'")
|
|
46
|
+
.replace(/"(?:[^"\\]|\\.)*"/g, '"…"')
|
|
47
|
+
.replace(/\d+/g, '#');
|
|
48
|
+
return `${element.role ?? element.tag}|${shape}`;
|
|
49
|
+
}
|
|
50
|
+
/** The longest run of a repeated block starting at `start` (block size 1–8, at least 3 repeats, 5 hidden lines). */
|
|
51
|
+
function findRun(shapes, start) {
|
|
52
|
+
let found;
|
|
53
|
+
for (let size = 1; size <= 8 && start + size * 3 <= shapes.length; size += 1) {
|
|
54
|
+
let repeats = 1;
|
|
55
|
+
while (start + (repeats + 1) * size <= shapes.length) {
|
|
56
|
+
let same = true;
|
|
57
|
+
for (let k = 0; k < size; k += 1) {
|
|
58
|
+
if (shapes[start + repeats * size + k] !== shapes[start + k]) {
|
|
59
|
+
same = false;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!same)
|
|
64
|
+
break;
|
|
65
|
+
repeats += 1;
|
|
66
|
+
}
|
|
67
|
+
// Collapse only when it hides at least five lines; shorter runs read better in full.
|
|
68
|
+
if (repeats >= 3 && (repeats - 1) * size >= 5 && (!found || repeats * size > found.repeats * found.size))
|
|
69
|
+
found = { start, size, repeats };
|
|
70
|
+
}
|
|
71
|
+
return found;
|
|
72
|
+
}
|
|
73
|
+
/** A block's display name: its first named element (a heading or image name, usually). */
|
|
74
|
+
function blockName(block) {
|
|
75
|
+
const named = block.find((el) => el.name && el.name.trim()) ?? block.find((el) => el.text && el.text.trim());
|
|
76
|
+
const text = named?.name ?? named?.text;
|
|
77
|
+
return text ? clip(text, 40) : undefined;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Render the locator map, numbered by position in `elements`, with
|
|
81
|
+
* repeated blocks collapsed after their first occurrence.
|
|
82
|
+
*/
|
|
83
|
+
export function renderLocatorMap(elements) {
|
|
84
|
+
const shapes = elements.map(locatorShape);
|
|
85
|
+
const lines = [];
|
|
86
|
+
let i = 0;
|
|
87
|
+
while (i < elements.length) {
|
|
88
|
+
const run = findRun(shapes, i);
|
|
89
|
+
if (!run) {
|
|
90
|
+
lines.push(renderLocatorLine(elements[i], i + 1));
|
|
91
|
+
i += 1;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
for (let k = 0; k < run.size; k += 1)
|
|
95
|
+
lines.push(renderLocatorLine(elements[i + k], i + k + 1));
|
|
96
|
+
const restStart = i + run.size;
|
|
97
|
+
const restEnd = i + run.size * run.repeats;
|
|
98
|
+
const names = [];
|
|
99
|
+
for (let b = restStart; b < restEnd; b += run.size) {
|
|
100
|
+
const name = blockName(elements.slice(b, b + run.size));
|
|
101
|
+
if (name)
|
|
102
|
+
names.push(`"${name}"`);
|
|
103
|
+
}
|
|
104
|
+
const shown = names.slice(0, 30);
|
|
105
|
+
const more = names.length > shown.length ? `, …${names.length - shown.length} more` : '';
|
|
106
|
+
lines.push(`${restStart + 1}–${restEnd}. ↳ ${run.repeats - 1} more blocks like ${run.size === 1 ? i + 1 : `${i + 1}–${restStart}`}: same locators with the item's own name or number` +
|
|
107
|
+
(shown.length > 0 ? `: ${shown.join(', ')}${more}` : '') +
|
|
108
|
+
'. Pass `selector` to list one in full.');
|
|
109
|
+
i = restEnd;
|
|
110
|
+
}
|
|
111
|
+
return lines;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=locator-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locator-map.js","sourceRoot":"","sources":["../../src/utils/locator-map.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "playwright-e2e-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "MCP server and agent skill that run, diagnose and fix failing Playwright tests (TypeScript and pytest): CI report diagnosis, trace analysis, verified locator fixes, flaky verdicts, breaking commits, heal guard, accessibility and Web Vitals audits.",
|
|
5
5
|
"author": "xsignon-secure (https://github.com/xsignon-secure)",
|
|
6
6
|
"mcpName": "io.github.xsignon-secure/E2E",
|
|
@@ -13,7 +13,7 @@ and concrete next steps. It can also propose and apply a verified fix for a brok
|
|
|
13
13
|
locator, inspect pages behind a login, run long suites in the background and keep
|
|
14
14
|
a local history of every run.
|
|
15
15
|
|
|
16
|
-
This skill is written for playwright-e2e-mcp 0.
|
|
16
|
+
This skill is written for playwright-e2e-mcp 0.11.0 and works with 0.2.0 or
|
|
17
17
|
newer. If `suggest-fix`, `get-run-status` or `analyze-history` is missing, the
|
|
18
18
|
server is older than 0.2.0: use the steps that do not need those tools and tell
|
|
19
19
|
the user to update it. `diagnose-flaky`'s per-test table and `isolate` option
|
|
@@ -27,7 +27,12 @@ history views and strict-mode fixes in `suggest-fix` arrived in 0.5.0;
|
|
|
27
27
|
pytest-playwright projects (`run-test`, `get-failure`, `list-tests`) arrived in
|
|
28
28
|
0.8.0; the free self-hosted history server (`PW_MCP_HISTORY_URL` /
|
|
29
29
|
`PW_MCP_HISTORY_TOKEN`, replacing the old Pro settings, which still work) arrived
|
|
30
|
-
in 0.9.0
|
|
30
|
+
in 0.9.0; asking before changing files, the interactive test report (MCP Apps)
|
|
31
|
+
and sign-in with Client ID Metadata Documents arrived in 0.10.0; the compact
|
|
32
|
+
`inspect-page` locator map as the default view and `load-tools` (`--tools=auto`)
|
|
33
|
+
arrived in 0.11.0. If a tool that
|
|
34
|
+
writes returns `declined: true`, the user said no: do not retry the write, show
|
|
35
|
+
them the proposed change instead. If `doctor` says a newer version is out, pass its upgrade command on to
|
|
31
36
|
the user.
|
|
32
37
|
|
|
33
38
|
## Before you start
|
|
@@ -46,8 +51,10 @@ the user.
|
|
|
46
51
|
`references/writing-tests.md`. Do not fall back to shelling out to `npx playwright test` unless
|
|
47
52
|
the user asks. If only some are missing, the server may run a smaller tool
|
|
48
53
|
profile (`--tools=core` exposes `run-test`, `get-run-status`, `get-failure`,
|
|
49
|
-
`suggest-fix`, `diagnose-flaky`, `check-test-change` and `list-tests`)
|
|
50
|
-
|
|
54
|
+
`suggest-fix`, `diagnose-flaky`, `check-test-change` and `list-tests`). If a `load-tools`
|
|
55
|
+
tool is listed, call it with the profile you need (`inspect`, `triage`, `author`) or the tool
|
|
56
|
+
names, then use them; otherwise work with what is there and mention the profile when a task
|
|
57
|
+
needs a page tool.
|
|
51
58
|
3. Every tool except `get-run-status` takes an optional `projectRoot`. Leave it
|
|
52
59
|
out unless the user works in a sub-project; the server defaults to `PW_MCP_PROJECT_ROOT` or its working
|
|
53
60
|
directory, and rejects any root outside its allowed roots.
|
|
@@ -283,7 +290,7 @@ with the commit it tested. `analyze-history` views:
|
|
|
283
290
|
`selector` for one region) returns the accessibility snapshot and a
|
|
284
291
|
`toMatchAriaSnapshot` assertion to paste into a spec.
|
|
285
292
|
- **Set up CI:** when the user wants the same diagnosis on every pull request,
|
|
286
|
-
point them to the GitHub Action (`uses: xsignon-secure/E2E@v0.
|
|
293
|
+
point them to the GitHub Action (`uses: xsignon-secure/E2E@v0.11.0`) or the
|
|
287
294
|
`npx -y playwright-e2e-mcp ci` command in `references/setup.md`. It writes the
|
|
288
295
|
run, flaky verdicts and fix diffs to the job summary and one PR comment.
|
|
289
296
|
- **Accessibility:** `audit-accessibility` with `url` runs axe-core against
|
|
@@ -77,7 +77,7 @@ Set these in the server's `env` block. None of them are secrets except
|
|
|
77
77
|
| Variable | Default | Purpose |
|
|
78
78
|
| --- | --- | --- |
|
|
79
79
|
| `PW_MCP_PROJECT_ROOT` | server working directory | Default project root for every tool |
|
|
80
|
-
| `PW_MCP_TOOLS` | all | Tools to expose: names and/or profiles `core`, `triage`, `inspect`, `author` (same as the `--tools=` flag). `core`
|
|
80
|
+
| `PW_MCP_TOOLS` | all | Tools to expose: names and/or profiles `core`, `triage`, `inspect`, `author` (same as the `--tools=` flag). `core` cuts the tool-definition context by about four fifths; `auto` starts with `core` plus `load-tools`, which loads the other tools when you need them |
|
|
81
81
|
| `PW_MCP_ALLOWED_ROOTS` | none | Extra directories allowed as `projectRoot` (`:`-separated, `;` on Windows) |
|
|
82
82
|
| `PW_MCP_BLOCK_PRIVATE_URLS` | off for stdio | `1` makes the URL tools refuse loopback, private-network and cloud-metadata addresses |
|
|
83
83
|
| `PW_MCP_STORAGE_STATE` | none | Default Playwright storageState JSON (inside the project) for the page tools, so signed-in pages open logged in |
|
|
@@ -119,7 +119,7 @@ Exit code 0 means passed, 1 tests failed, 2 the run itself failed. On GitHub
|
|
|
119
119
|
Actions it also writes to the job summary. As an Action:
|
|
120
120
|
|
|
121
121
|
```yaml
|
|
122
|
-
- uses: xsignon-secure/E2E@v0.
|
|
122
|
+
- uses: xsignon-secure/E2E@v0.11.0
|
|
123
123
|
with:
|
|
124
124
|
flaky-runs: 3
|
|
125
125
|
suggest: 2
|
|
@@ -65,7 +65,7 @@ becomes the last run for `get-failure`, `suggest-fix` and `diagnose-flaky`.
|
|
|
65
65
|
| `waitUntil` | `load` \| `domcontentloaded` \| `networkidle` | Navigation wait |
|
|
66
66
|
| `includeHtml` | boolean | Include rendered HTML (capped by `maxHtmlChars`, default 20000) |
|
|
67
67
|
| `timeoutMs` | number | Default 45000 |
|
|
68
|
-
| `view` | `elements` \| `locators` \| `aria` | `locators
|
|
68
|
+
| `view` | `elements` \| `locators` \| `aria` | `locators` (default): compact map of interactive elements with verified locators, repeated blocks shown once; `elements`: full DOM inventory; `aria` (0.5.0+): accessibility snapshot of the page or of `selector`, with a `toMatchAriaSnapshot` assertion |
|
|
69
69
|
| `storageState` | string | Playwright storageState JSON inside the project (signed-in session); defaults to `PW_MCP_STORAGE_STATE` |
|
|
70
70
|
| `headers` | object | Extra HTTP headers |
|
|
71
71
|
| `actions` | array | Up to 20 steps after load: `{ type: click\|dblclick\|fill\|press\|check\|uncheck\|select\|hover\|wait\|goto, locator?, value?, url?, ms? }`. Locators may start with `frameLocator('#frame')` or use `.contentFrame()` for iframes (0.6.1+). `{ type: 'switchTab', value?: 'last'\|'first'\|'2'\|text in URL or title }` moves to a popup or new tab (0.8.0+) |
|