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.
Files changed (51) hide show
  1. package/README.md +57 -18
  2. package/dist/apps/index.d.ts +29 -0
  3. package/dist/apps/index.js +52 -0
  4. package/dist/apps/index.js.map +1 -0
  5. package/dist/apps/test-report-html.d.ts +15 -0
  6. package/dist/apps/test-report-html.js +430 -0
  7. package/dist/apps/test-report-html.js.map +1 -0
  8. package/dist/index.js +2 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/oauth.d.ts +12 -2
  11. package/dist/oauth.js +44 -6
  12. package/dist/oauth.js.map +1 -1
  13. package/dist/server.d.ts +14 -2
  14. package/dist/server.js +107 -8
  15. package/dist/server.js.map +1 -1
  16. package/dist/tools/audit-tests.js +24 -6
  17. package/dist/tools/audit-tests.js.map +1 -1
  18. package/dist/tools/compare-visual-state.js +7 -1
  19. package/dist/tools/compare-visual-state.js.map +1 -1
  20. package/dist/tools/generate-e2e-test.js +27 -12
  21. package/dist/tools/generate-e2e-test.js.map +1 -1
  22. package/dist/tools/inspect-page.js +5 -12
  23. package/dist/tools/inspect-page.js.map +1 -1
  24. package/dist/tools/quarantine.js +7 -1
  25. package/dist/tools/quarantine.js.map +1 -1
  26. package/dist/tools/shared.d.ts +10 -0
  27. package/dist/tools/shared.js +15 -0
  28. package/dist/tools/shared.js.map +1 -1
  29. package/dist/tools/structured.d.ts +2 -0
  30. package/dist/tools/structured.js +4 -2
  31. package/dist/tools/structured.js.map +1 -1
  32. package/dist/tools/suggest-fix.js +6 -1
  33. package/dist/tools/suggest-fix.js.map +1 -1
  34. package/dist/types/index.d.ts +9 -0
  35. package/dist/utils/client-metadata.d.ts +46 -0
  36. package/dist/utils/client-metadata.js +174 -0
  37. package/dist/utils/client-metadata.js.map +1 -0
  38. package/dist/utils/compact-schema.d.ts +20 -0
  39. package/dist/utils/compact-schema.js +55 -0
  40. package/dist/utils/compact-schema.js.map +1 -0
  41. package/dist/utils/confirm.d.ts +68 -0
  42. package/dist/utils/confirm.js +120 -0
  43. package/dist/utils/confirm.js.map +1 -0
  44. package/dist/utils/locator-map.d.ts +25 -0
  45. package/dist/utils/locator-map.js +113 -0
  46. package/dist/utils/locator-map.js.map +1 -0
  47. package/examples/github-actions/playwright-e2e.yml +1 -1
  48. package/package.json +1 -1
  49. package/skill/playwright-e2e-mcp/SKILL.md +12 -5
  50. package/skill/playwright-e2e-mcp/references/setup.md +2 -2
  51. 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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"}
@@ -38,7 +38,7 @@ jobs:
38
38
 
39
39
  - name: Run Playwright E2E diagnosis action
40
40
  id: e2e
41
- uses: xsignon-secure/E2E@v0.9.0
41
+ uses: xsignon-secure/E2E@v0.11.0
42
42
  with:
43
43
  # Directory containing playwright.config.* (default is workspace root)
44
44
  project-root: .
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playwright-e2e-mcp",
3
- "version": "0.9.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.9.0 and works with 0.2.0 or
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. If `doctor` says a newer version is out, pass its upgrade command on to
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`); work with what is there and
50
- mention the profile when a task needs a page tool.
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.9.0`) or the
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` halves the tool-definition context |
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.9.0
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`: compact map of interactive elements with verified locators; `aria` (0.5.0+): accessibility snapshot of the page or of `selector`, with a `toMatchAriaSnapshot` assertion |
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+) |