playwright-e2e-mcp 0.4.1 → 0.5.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 +215 -43
- package/dist/cli/init.js +3 -2
- package/dist/cli/init.js.map +1 -1
- package/dist/http.d.ts +6 -1
- package/dist/http.js +13 -3
- package/dist/http.js.map +1 -1
- package/dist/oauth.d.ts +34 -0
- package/dist/oauth.js +346 -0
- package/dist/oauth.js.map +1 -0
- package/dist/server.d.ts +2 -2
- package/dist/server.js +31 -4
- package/dist/server.js.map +1 -1
- package/dist/tools/analyze-history.d.ts +12 -1
- package/dist/tools/analyze-history.js +89 -5
- package/dist/tools/analyze-history.js.map +1 -1
- package/dist/tools/audit-accessibility.d.ts +85 -0
- package/dist/tools/audit-accessibility.js +4 -1
- package/dist/tools/audit-accessibility.js.map +1 -1
- package/dist/tools/audit-performance.d.ts +333 -0
- package/dist/tools/audit-performance.js +377 -0
- package/dist/tools/audit-performance.js.map +1 -0
- package/dist/tools/compare-visual-state.d.ts +84 -0
- package/dist/tools/generate-e2e-test.d.ts +90 -0
- package/dist/tools/generate-e2e-test.js +223 -34
- package/dist/tools/generate-e2e-test.js.map +1 -1
- package/dist/tools/get-failure.js +2 -1
- package/dist/tools/get-failure.js.map +1 -1
- package/dist/tools/inspect-page.d.ts +94 -0
- package/dist/tools/inspect-page.js +42 -5
- package/dist/tools/inspect-page.js.map +1 -1
- package/dist/tools/load-report.d.ts +48 -0
- package/dist/tools/load-report.js +167 -0
- package/dist/tools/load-report.js.map +1 -0
- package/dist/tools/probe-script.d.ts +7 -0
- package/dist/tools/probe-script.js +256 -7
- package/dist/tools/probe-script.js.map +1 -1
- package/dist/tools/shared.d.ts +137 -2
- package/dist/tools/shared.js +155 -8
- package/dist/tools/shared.js.map +1 -1
- package/dist/tools/suggest-fix.d.ts +35 -1
- package/dist/tools/suggest-fix.js +197 -17
- package/dist/tools/suggest-fix.js.map +1 -1
- package/dist/tools/validate-selector.d.ts +84 -0
- package/dist/tools/validate-selector.js +7 -1
- package/dist/tools/validate-selector.js.map +1 -1
- package/dist/types/index.d.ts +28 -0
- package/dist/utils/project-detector.d.ts +7 -0
- package/dist/utils/project-detector.js +11 -0
- package/dist/utils/project-detector.js.map +1 -1
- package/dist/utils/report-parser.d.ts +28 -3
- package/dist/utils/report-parser.js +77 -11
- package/dist/utils/report-parser.js.map +1 -1
- package/dist/utils/run-history.d.ts +66 -3
- package/dist/utils/run-history.js +149 -3
- package/dist/utils/run-history.js.map +1 -1
- package/package.json +5 -3
- package/skill/playwright-e2e-mcp/SKILL.md +71 -15
- package/skill/playwright-e2e-mcp/references/setup.md +1 -1
- package/skill/playwright-e2e-mcp/references/tools.md +67 -6
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool 13 — audit-performance: load a page in a real browser and return the
|
|
3
|
+
* lab Web Vitals (LCP, CLS, TBT, INP during `actions`, FCP, TTFB) rated
|
|
4
|
+
* against Google's thresholds or the caller's budgets, what each one is
|
|
5
|
+
* caused by (the LCP element and the elements that shifted, as verified
|
|
6
|
+
* Playwright locators; long tasks; render-blocking and heavy requests) and a
|
|
7
|
+
* budget check to paste into a spec.
|
|
8
|
+
*
|
|
9
|
+
* It takes the same page state as inspect-page (storageState, headers,
|
|
10
|
+
* actions, routes), so signed-in dashboards and later steps of a flow can be
|
|
11
|
+
* measured, and it can emulate a mid-range phone on a slow 4G network.
|
|
12
|
+
* Lab numbers vary between loads, so `runs` reports the median.
|
|
13
|
+
*/
|
|
14
|
+
import { z } from 'zod';
|
|
15
|
+
import { PlaywrightMcpError } from '../types/index.js';
|
|
16
|
+
import { assertHttpUrl, bestLocator, describePageState, describeRouteHits, guard, loginHint, pageStateShape, resolvePageState, resolveProjectRoot, runBrowserScript, toolError, toolText, } from './shared.js';
|
|
17
|
+
import { assertUrlAllowed } from '../utils/url-policy.js';
|
|
18
|
+
/**
|
|
19
|
+
* Lighthouse's mobile preset: a mid-range phone (4x slower CPU) on slow 4G
|
|
20
|
+
* (150 ms RTT and 1.6 Mbps, with DevTools' packet-level adjustment).
|
|
21
|
+
*/
|
|
22
|
+
export const MOBILE_PROFILE = {
|
|
23
|
+
cpuThrottle: 4,
|
|
24
|
+
network: { latencyMs: 562.5, downloadBps: Math.round((1_638.4 * 1024 * 0.9) / 8), uploadBps: Math.round((750 * 1024 * 0.9) / 8) },
|
|
25
|
+
viewport: { width: 412, height: 823 },
|
|
26
|
+
};
|
|
27
|
+
export const METRICS = [
|
|
28
|
+
{ id: 'lcp', label: 'LCP · largest contentful paint', good: 2_500, poor: 4_000, unit: 'ms' },
|
|
29
|
+
{ id: 'cls', label: 'CLS · cumulative layout shift', good: 0.1, poor: 0.25, unit: 'score' },
|
|
30
|
+
{ id: 'inp', label: 'INP · slowest interaction (lab)', good: 200, poor: 500, unit: 'ms' },
|
|
31
|
+
{ id: 'tbt', label: 'TBT · total blocking time', good: 200, poor: 600, unit: 'ms' },
|
|
32
|
+
{ id: 'fcp', label: 'FCP · first contentful paint', good: 1_800, poor: 3_000, unit: 'ms' },
|
|
33
|
+
{ id: 'ttfb', label: 'TTFB · time to first byte', good: 800, poor: 1_800, unit: 'ms' },
|
|
34
|
+
];
|
|
35
|
+
const budgetsShape = z
|
|
36
|
+
.object({
|
|
37
|
+
lcpMs: z.number().min(0).optional(),
|
|
38
|
+
cls: z.number().min(0).optional(),
|
|
39
|
+
inpMs: z.number().min(0).optional(),
|
|
40
|
+
tbtMs: z.number().min(0).optional(),
|
|
41
|
+
fcpMs: z.number().min(0).optional(),
|
|
42
|
+
ttfbMs: z.number().min(0).optional(),
|
|
43
|
+
transferKb: z.number().min(0).optional().describe('Total bytes over the wire, in kB'),
|
|
44
|
+
requests: z.number().int().min(0).optional(),
|
|
45
|
+
})
|
|
46
|
+
.describe('Fail the audit when a metric exceeds its budget (default: rate against Google thresholds)');
|
|
47
|
+
const auditPerformanceInput = z.object({
|
|
48
|
+
url: z.string().describe('Full URL of the page to measure (http/https)'),
|
|
49
|
+
projectRoot: z.string().optional().describe('Project whose Playwright launches the browser (default: server root)'),
|
|
50
|
+
throttle: z
|
|
51
|
+
.enum(['none', 'mobile'])
|
|
52
|
+
.optional()
|
|
53
|
+
.describe("'mobile': phone viewport, 4x CPU slowdown and slow 4G like Lighthouse mobile; 'none' (default): desktop, unthrottled"),
|
|
54
|
+
cpuSlowdown: z.number().int().min(1).max(20).optional().describe('Override the CPU slowdown factor'),
|
|
55
|
+
runs: z.number().int().min(1).max(5).optional().describe('Load the page this many times and report the median (default 1)'),
|
|
56
|
+
budgets: budgetsShape.optional(),
|
|
57
|
+
waitFor: z.string().optional().describe('Wait for this selector first'),
|
|
58
|
+
...pageStateShape,
|
|
59
|
+
waitUntil: z
|
|
60
|
+
.enum(['load', 'domcontentloaded', 'networkidle'])
|
|
61
|
+
.optional()
|
|
62
|
+
.describe('Navigation wait condition (default load)'),
|
|
63
|
+
settleMs: z.number().int().min(0).max(10_000).optional().describe('Keep observing after load for late paints and shifts (default 1500)'),
|
|
64
|
+
timeoutMs: z.number().int().min(5_000).max(300_000).optional().describe('Limit per run in ms (default 60000)'),
|
|
65
|
+
});
|
|
66
|
+
export const auditPerformanceSchema = auditPerformanceInput;
|
|
67
|
+
export function rate(id, value) {
|
|
68
|
+
const spec = METRICS.find((m) => m.id === id);
|
|
69
|
+
if (value <= spec.good)
|
|
70
|
+
return 'good';
|
|
71
|
+
if (value <= spec.poor)
|
|
72
|
+
return 'needs-improvement';
|
|
73
|
+
return 'poor';
|
|
74
|
+
}
|
|
75
|
+
export function metricValue(scan, id) {
|
|
76
|
+
switch (id) {
|
|
77
|
+
case 'lcp':
|
|
78
|
+
return scan.lcpMs;
|
|
79
|
+
case 'cls':
|
|
80
|
+
return scan.cls;
|
|
81
|
+
case 'inp':
|
|
82
|
+
return scan.inpMs;
|
|
83
|
+
case 'tbt':
|
|
84
|
+
return scan.tbtMs;
|
|
85
|
+
case 'fcp':
|
|
86
|
+
return scan.fcpMs;
|
|
87
|
+
case 'ttfb':
|
|
88
|
+
return scan.ttfbMs;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function budgetFor(budgets, id) {
|
|
92
|
+
if (!budgets)
|
|
93
|
+
return undefined;
|
|
94
|
+
return id === 'cls' ? budgets.cls : budgets[`${id}Ms`];
|
|
95
|
+
}
|
|
96
|
+
export function formatMs(ms) {
|
|
97
|
+
return ms >= 1_000 ? `${(ms / 1_000).toFixed(ms >= 10_000 ? 1 : 2)} s` : `${Math.round(ms)} ms`;
|
|
98
|
+
}
|
|
99
|
+
export function formatBytes(bytes) {
|
|
100
|
+
if (bytes >= 1_000_000)
|
|
101
|
+
return `${(bytes / 1_000_000).toFixed(1)} MB`;
|
|
102
|
+
if (bytes >= 1_000)
|
|
103
|
+
return `${Math.round(bytes / 1_000)} kB`;
|
|
104
|
+
return `${bytes} B`;
|
|
105
|
+
}
|
|
106
|
+
function formatMetric(spec, value) {
|
|
107
|
+
return spec.unit === 'score' ? value.toFixed(3) : formatMs(value);
|
|
108
|
+
}
|
|
109
|
+
function median(values) {
|
|
110
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
111
|
+
const mid = Math.floor(sorted.length / 2);
|
|
112
|
+
return sorted.length % 2 === 1 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Combine several runs: every number is the median of the runs that
|
|
116
|
+
* measured it, and the details (LCP element, resources) come from the run
|
|
117
|
+
* whose LCP is closest to that median.
|
|
118
|
+
*/
|
|
119
|
+
export function mergeRuns(runs) {
|
|
120
|
+
if (runs.length === 1)
|
|
121
|
+
return { data: runs[0] };
|
|
122
|
+
const scans = runs.map((run) => run.perf);
|
|
123
|
+
const pick = (get) => {
|
|
124
|
+
const values = scans.map(get).filter((v) => typeof v === 'number');
|
|
125
|
+
return values.length > 0 ? median(values) : undefined;
|
|
126
|
+
};
|
|
127
|
+
const lcp = pick((s) => s.lcpMs);
|
|
128
|
+
let representative = 0;
|
|
129
|
+
if (lcp !== undefined) {
|
|
130
|
+
let best = Infinity;
|
|
131
|
+
scans.forEach((scan, i) => {
|
|
132
|
+
const distance = Math.abs((scan.lcpMs ?? Infinity) - lcp);
|
|
133
|
+
if (distance < best) {
|
|
134
|
+
best = distance;
|
|
135
|
+
representative = i;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
const base = runs[representative];
|
|
140
|
+
const perf = {
|
|
141
|
+
...base.perf,
|
|
142
|
+
ttfbMs: pick((s) => s.ttfbMs),
|
|
143
|
+
fcpMs: pick((s) => s.fcpMs),
|
|
144
|
+
lcpMs: lcp,
|
|
145
|
+
cls: Math.round((pick((s) => s.cls) ?? 0) * 1_000) / 1_000,
|
|
146
|
+
tbtMs: Math.round(pick((s) => s.tbtMs) ?? 0),
|
|
147
|
+
inpMs: pick((s) => s.inpMs),
|
|
148
|
+
transferBytes: Math.round(pick((s) => s.transferBytes) ?? 0),
|
|
149
|
+
requests: Math.round(pick((s) => s.requests) ?? 0),
|
|
150
|
+
};
|
|
151
|
+
const lcps = scans.map((s) => s.lcpMs).filter((v) => typeof v === 'number');
|
|
152
|
+
return {
|
|
153
|
+
data: { ...base, perf },
|
|
154
|
+
spread: lcps.length > 1 ? { lcp: [Math.min(...lcps), Math.max(...lcps)] } : undefined,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
export function metricRows(scan, budgets) {
|
|
158
|
+
return METRICS.map((spec) => {
|
|
159
|
+
const value = metricValue(scan, spec.id);
|
|
160
|
+
const budget = budgetFor(budgets, spec.id);
|
|
161
|
+
return {
|
|
162
|
+
spec,
|
|
163
|
+
value,
|
|
164
|
+
rating: value === undefined ? undefined : rate(spec.id, value),
|
|
165
|
+
budget,
|
|
166
|
+
overBudget: value !== undefined && budget !== undefined && value > budget,
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
function locatorOf(elements, index) {
|
|
171
|
+
const element = index >= 0 ? elements[index] : undefined;
|
|
172
|
+
if (!element)
|
|
173
|
+
return undefined;
|
|
174
|
+
return bestLocator(element) ?? element.selector;
|
|
175
|
+
}
|
|
176
|
+
/** Concrete causes and fixes for the metrics that are not good. */
|
|
177
|
+
export function perfFindings(data) {
|
|
178
|
+
const scan = data.perf;
|
|
179
|
+
const out = [];
|
|
180
|
+
const blocking = scan.renderBlocking;
|
|
181
|
+
if (scan.lcpMs !== undefined && rate('lcp', scan.lcpMs) !== 'good') {
|
|
182
|
+
const where = locatorOf(data.elements, scan.lcpElement);
|
|
183
|
+
const what = scan.lcpUrl ? `the ${scan.lcpTag ?? 'element'} loading \`${scan.lcpUrl}\`` : `a <${scan.lcpTag ?? '?'}> element`;
|
|
184
|
+
const tips = [];
|
|
185
|
+
if (scan.lcpUrl)
|
|
186
|
+
tips.push('preload it or set `fetchpriority="high"`, never `loading="lazy"` above the fold', 'serve it compressed (AVIF/WebP) at display size');
|
|
187
|
+
if (blocking.length > 0)
|
|
188
|
+
tips.push(`cut the ${blocking.length} render-blocking request(s) listed below`);
|
|
189
|
+
if (scan.ttfbMs !== undefined && rate('ttfb', scan.ttfbMs) !== 'good')
|
|
190
|
+
tips.push('the server answers slowly, so cache or stream the HTML');
|
|
191
|
+
if (tips.length === 0)
|
|
192
|
+
tips.push('render it from the HTML rather than after client-side JavaScript');
|
|
193
|
+
out.push(`**LCP ${formatMs(scan.lcpMs)}** is ${what}${where ? ` (\`${where}\`)` : ''}: ${tips.join('; ')}.`);
|
|
194
|
+
}
|
|
195
|
+
if (rate('cls', scan.cls) !== 'good') {
|
|
196
|
+
const moved = scan.shiftElements.map((i) => locatorOf(data.elements, i)).filter((l) => l !== undefined);
|
|
197
|
+
out.push(`**CLS ${scan.cls.toFixed(3)}**${moved.length ? `: these moved: ${moved.map((l) => `\`${l}\``).join(', ')}` : ''}. Reserve their space (width/height or aspect-ratio on media, min-height on late content, \`font-display: optional\` or size-adjusted fallbacks for web fonts).`);
|
|
198
|
+
}
|
|
199
|
+
if (rate('tbt', scan.tbtMs) !== 'good') {
|
|
200
|
+
out.push(`**TBT ${formatMs(scan.tbtMs)}** from ${scan.longTasks} long task(s), the longest ${formatMs(scan.longestTaskMs)}: split or defer JavaScript${scan.thirdPartyOrigins > 0 ? `, and load the ${scan.thirdPartyOrigins} third-party origin(s) after interaction` : ''}.`);
|
|
201
|
+
}
|
|
202
|
+
if (scan.inpMs !== undefined && rate('inp', scan.inpMs) !== 'good') {
|
|
203
|
+
out.push(`**INP ${formatMs(scan.inpMs)}**${scan.inpTarget ? ` on ${scan.inpTarget}` : ''}: the handler blocks the next paint; yield (\`await scheduler.yield()\`) or move work off the main thread.`);
|
|
204
|
+
}
|
|
205
|
+
if (scan.fcpMs !== undefined && rate('fcp', scan.fcpMs) !== 'good' && blocking.length > 0 && !out.some((l) => l.startsWith('**LCP'))) {
|
|
206
|
+
out.push(`**FCP ${formatMs(scan.fcpMs)}**: ${blocking.length} render-blocking request(s) delay the first paint; inline critical CSS and \`defer\` scripts.`);
|
|
207
|
+
}
|
|
208
|
+
return out;
|
|
209
|
+
}
|
|
210
|
+
export function budgetCheck(url, budgets) {
|
|
211
|
+
const lcp = budgets?.lcpMs ?? 2_500;
|
|
212
|
+
const cls = budgets?.cls ?? 0.1;
|
|
213
|
+
return [
|
|
214
|
+
'```ts',
|
|
215
|
+
"test('stays within the performance budget', async ({ page }) => {",
|
|
216
|
+
` await page.goto(${JSON.stringify(url)});`,
|
|
217
|
+
' const vitals = await page.evaluate(() => new Promise<{ lcp: number; cls: number }>((resolve) => {',
|
|
218
|
+
' let lcp = 0;',
|
|
219
|
+
' let cls = 0;',
|
|
220
|
+
" new PerformanceObserver((list) => { for (const e of list.getEntries()) lcp = e.startTime; }).observe({ type: 'largest-contentful-paint', buffered: true });",
|
|
221
|
+
" new PerformanceObserver((list) => { for (const e of list.getEntries() as any[]) if (!e.hadRecentInput) cls += e.value; }).observe({ type: 'layout-shift', buffered: true });",
|
|
222
|
+
' setTimeout(() => resolve({ lcp, cls }), 1_500);',
|
|
223
|
+
' }));',
|
|
224
|
+
` expect(vitals.lcp).toBeLessThan(${lcp});`,
|
|
225
|
+
` expect(vitals.cls).toBeLessThan(${cls});`,
|
|
226
|
+
'});',
|
|
227
|
+
'```',
|
|
228
|
+
].join('\n');
|
|
229
|
+
}
|
|
230
|
+
const ICON = { good: '🟢 good', 'needs-improvement': '🟠 needs improvement', poor: '🔴 poor' };
|
|
231
|
+
export function renderPerformance(url, data, options) {
|
|
232
|
+
const scan = data.perf;
|
|
233
|
+
const rows = metricRows(scan, options.budgets);
|
|
234
|
+
const measured = rows.filter((r) => r.value !== undefined);
|
|
235
|
+
const budgets = options.budgets;
|
|
236
|
+
const overBudget = rows.filter((r) => r.overBudget).map((r) => r.spec.id.toUpperCase());
|
|
237
|
+
if (budgets?.transferKb !== undefined && scan.transferBytes / 1_000 > budgets.transferKb)
|
|
238
|
+
overBudget.push('transfer size');
|
|
239
|
+
if (budgets?.requests !== undefined && scan.requests > budgets.requests)
|
|
240
|
+
overBudget.push('request count');
|
|
241
|
+
const weak = measured.filter((r) => r.rating !== 'good');
|
|
242
|
+
const lines = [];
|
|
243
|
+
if (budgets) {
|
|
244
|
+
lines.push(overBudget.length > 0
|
|
245
|
+
? `## ❌ ${overBudget.length} performance budget${overBudget.length === 1 ? '' : 's'} exceeded (${overBudget.join(', ')})`
|
|
246
|
+
: '## ✅ Within every performance budget');
|
|
247
|
+
}
|
|
248
|
+
else if (weak.length === 0) {
|
|
249
|
+
lines.push('## ✅ All measured Web Vitals are good');
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
lines.push(`## ⚡ ${weak.length} of ${measured.length} metrics need work (${weak.map((r) => r.spec.id.toUpperCase()).join(', ')})`);
|
|
253
|
+
}
|
|
254
|
+
lines.push('');
|
|
255
|
+
lines.push(`**URL:** ${url}${data.finalUrl && data.finalUrl !== url ? ` → ${data.finalUrl}` : ''} | **Profile:** ${options.profile} | **Runs:** ${options.runs}${options.runs > 1 ? ' (median)' : ''}`);
|
|
256
|
+
if (options.stateLine)
|
|
257
|
+
lines.push(`**Page state:** ${options.stateLine}`);
|
|
258
|
+
if (data.actionsDone?.length)
|
|
259
|
+
lines.push(`**Steps run:** ${data.actionsDone.join('; ')}`);
|
|
260
|
+
lines.push('');
|
|
261
|
+
lines.push(`| Metric | Value | Rating |${budgets ? ' Budget |' : ''}`, `| --- | --- | --- |${budgets ? ' --- |' : ''}`);
|
|
262
|
+
for (const row of rows) {
|
|
263
|
+
if (row.value === undefined) {
|
|
264
|
+
if (row.spec.id === 'inp')
|
|
265
|
+
continue; // only measured when actions ran
|
|
266
|
+
lines.push(`| ${row.spec.label} | n/a | — |${budgets ? ' — |' : ''}`);
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
let value = formatMetric(row.spec, row.value);
|
|
270
|
+
if (row.spec.id === 'lcp' && options.spread)
|
|
271
|
+
value += ` (${formatMs(options.spread.lcp[0])}–${formatMs(options.spread.lcp[1])})`;
|
|
272
|
+
const budget = row.budget === undefined ? '—' : `${row.overBudget ? '❌' : '✅'} ≤ ${formatMetric(row.spec, row.budget)}`;
|
|
273
|
+
lines.push(`| ${row.spec.label} | ${value} | ${ICON[row.rating]} |${budgets ? ` ${budget} |` : ''}`);
|
|
274
|
+
}
|
|
275
|
+
lines.push('');
|
|
276
|
+
const findings = perfFindings(data);
|
|
277
|
+
if (findings.length > 0) {
|
|
278
|
+
lines.push('### What to fix', '');
|
|
279
|
+
for (const finding of findings)
|
|
280
|
+
lines.push(`- ${finding}`);
|
|
281
|
+
lines.push('');
|
|
282
|
+
}
|
|
283
|
+
lines.push('### Network', '');
|
|
284
|
+
const transferBudget = budgets?.transferKb !== undefined ? ` (budget ${budgets.transferKb} kB ${scan.transferBytes / 1_000 > budgets.transferKb ? '❌' : '✅'})` : '';
|
|
285
|
+
const requestBudget = budgets?.requests !== undefined ? ` (budget ${budgets.requests} ${scan.requests > budgets.requests ? '❌' : '✅'})` : '';
|
|
286
|
+
lines.push(`**Transfer:** ${formatBytes(scan.transferBytes)}${transferBudget} in ${scan.requests} requests${requestBudget}` +
|
|
287
|
+
(scan.thirdPartyOrigins > 0 ? ` · third-party: ${formatBytes(scan.thirdPartyBytes)} from ${scan.thirdPartyOrigins} origin(s)` : '') +
|
|
288
|
+
` · DOM: ${scan.domNodes} nodes${scan.jsHeapBytes ? ` · JS heap: ${formatBytes(scan.jsHeapBytes)}` : ''}`);
|
|
289
|
+
if (scan.byType.length > 0) {
|
|
290
|
+
lines.push(`**By type:** ${scan.byType.slice(0, 6).map((t) => `${t.type} ${formatBytes(t.transferBytes)} (${t.requests})`).join(' · ')}`);
|
|
291
|
+
}
|
|
292
|
+
const largest = scan.largest.filter((r) => r.transferBytes > 0);
|
|
293
|
+
if (largest.length > 0) {
|
|
294
|
+
lines.push('', '**Heaviest requests:**');
|
|
295
|
+
for (const r of largest)
|
|
296
|
+
lines.push(`- ${formatBytes(r.transferBytes)} · ${r.type} · \`${r.url}\`${r.durationMs ? ` · ${formatMs(r.durationMs)}` : ''}`);
|
|
297
|
+
}
|
|
298
|
+
if (scan.renderBlocking.length > 0) {
|
|
299
|
+
lines.push('', '**Render-blocking:**');
|
|
300
|
+
for (const r of scan.renderBlocking)
|
|
301
|
+
lines.push(`- ${r.type} \`${r.url}\`${r.durationMs ? ` · ${formatMs(r.durationMs)}` : ''}`);
|
|
302
|
+
}
|
|
303
|
+
if (options.routeLines && options.routeLines.length > 0)
|
|
304
|
+
lines.push('', '**Network mocks:**', ...options.routeLines);
|
|
305
|
+
lines.push('');
|
|
306
|
+
lines.push('Lab numbers from one headless Chromium; field data (CrUX, RUM) can differ. INP is measured only when `actions` interact with the page.', '', '**Keep it fast:** add a budget check to the spec that reaches this page:', '', budgetCheck(data.finalUrl ?? url, budgets));
|
|
307
|
+
return lines.join('\n').trimEnd();
|
|
308
|
+
}
|
|
309
|
+
/* ------------------------------------------------------------------ */
|
|
310
|
+
/* Tool */
|
|
311
|
+
/* ------------------------------------------------------------------ */
|
|
312
|
+
export const auditPerformanceTool = {
|
|
313
|
+
name: 'audit-performance',
|
|
314
|
+
description: "Measure a page's lab Web Vitals (LCP, CLS, TBT, FCP, TTFB, and INP when actions interact) in headless Chromium, optionally as a throttled phone and as the median of several runs. Rates them against Google thresholds or your budgets, names the LCP element and shifting elements as verified locators, lists heavy and render-blocking requests, and returns a budget check for the spec. Accepts storageState/headers/actions/routes.",
|
|
315
|
+
inputSchema: auditPerformanceSchema,
|
|
316
|
+
handler: async (args, ctx) => guard('audit-performance', async () => {
|
|
317
|
+
const url = assertHttpUrl(args.url);
|
|
318
|
+
await assertUrlAllowed(url);
|
|
319
|
+
if (args.browser && args.browser !== 'chromium') {
|
|
320
|
+
throw new PlaywrightMcpError('audit-performance measures in Chromium only', 'INVALID_PATH', {
|
|
321
|
+
hint: 'Web Vitals, byte counts and throttling come from the Chrome DevTools Protocol. Leave browser unset.',
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
const root = await resolveProjectRoot(args.projectRoot, ctx);
|
|
325
|
+
const state = await resolvePageState(args, root, url, ctx);
|
|
326
|
+
const timeoutMs = args.timeoutMs ?? 60_000;
|
|
327
|
+
const mobile = args.throttle === 'mobile';
|
|
328
|
+
const cpuThrottle = args.cpuSlowdown ?? (mobile ? MOBILE_PROFILE.cpuThrottle : 1);
|
|
329
|
+
const runs = args.runs ?? 1;
|
|
330
|
+
const profile = [
|
|
331
|
+
mobile ? 'mobile, slow 4G' : 'desktop, unthrottled network',
|
|
332
|
+
cpuThrottle > 1 ? `${cpuThrottle}x CPU slowdown` : undefined,
|
|
333
|
+
]
|
|
334
|
+
.filter(Boolean)
|
|
335
|
+
.join(', ');
|
|
336
|
+
const results = [];
|
|
337
|
+
for (let run = 1; run <= runs; run += 1) {
|
|
338
|
+
ctx.progress?.(run - 1, runs, `Loading ${url} (run ${run} of ${runs})`);
|
|
339
|
+
const outcome = await runBrowserScript({
|
|
340
|
+
...state,
|
|
341
|
+
viewport: state.viewport ?? (mobile ? MOBILE_PROFILE.viewport : undefined),
|
|
342
|
+
mode: 'perf',
|
|
343
|
+
projectRoot: root,
|
|
344
|
+
url,
|
|
345
|
+
waitFor: args.waitFor,
|
|
346
|
+
waitUntil: args.waitUntil ?? 'load',
|
|
347
|
+
settleMs: args.settleMs ?? 1_500,
|
|
348
|
+
mobile,
|
|
349
|
+
cpuThrottle,
|
|
350
|
+
network: mobile ? MOBILE_PROFILE.network : undefined,
|
|
351
|
+
gotoTimeout: Math.max(5_000, timeoutMs - 10_000),
|
|
352
|
+
waitTimeout: Math.min(10_000, Math.max(3_000, timeoutMs - 10_000)),
|
|
353
|
+
startedAt: Date.now(),
|
|
354
|
+
}, { timeoutMs, signal: ctx.signal });
|
|
355
|
+
if (!outcome.ok || !outcome.data?.perf) {
|
|
356
|
+
const done = outcome.actionsDone?.length ? `\n\nSteps completed before the failure: ${outcome.actionsDone.join('; ')}` : '';
|
|
357
|
+
return toolError(outcome.kind ?? 'UNKNOWN', `Could not measure ${url}${runs > 1 ? ` (run ${run} of ${runs})` : ''}: ${outcome.error ?? 'no performance data'}${done}`, outcome.hint, outcome.stderrTail);
|
|
358
|
+
}
|
|
359
|
+
results.push(outcome.data);
|
|
360
|
+
}
|
|
361
|
+
ctx.progress?.(runs, runs, 'Done');
|
|
362
|
+
const { data, spread } = mergeRuns(results);
|
|
363
|
+
let text = renderPerformance(url, data, {
|
|
364
|
+
budgets: args.budgets,
|
|
365
|
+
profile,
|
|
366
|
+
runs,
|
|
367
|
+
spread,
|
|
368
|
+
stateLine: describePageState(state, root),
|
|
369
|
+
routeLines: describeRouteHits(state, data.routeHits),
|
|
370
|
+
});
|
|
371
|
+
const login = loginHint(data.finalUrl, state);
|
|
372
|
+
if (login)
|
|
373
|
+
text += `\n\n> 🔒 ${login}`;
|
|
374
|
+
return toolText(text);
|
|
375
|
+
}),
|
|
376
|
+
};
|
|
377
|
+
//# sourceMappingURL=audit-performance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-performance.js","sourceRoot":"","sources":["../../src/tools/audit-performance.ts"],"names":[],"mappings":"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"}
|
|
@@ -29,10 +29,38 @@ declare const compareInput: z.ZodObject<{
|
|
|
29
29
|
url: z.ZodOptional<z.ZodString>;
|
|
30
30
|
ms: z.ZodOptional<z.ZodNumber>;
|
|
31
31
|
}, z.core.$strip>>>;
|
|
32
|
+
routes: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
33
|
+
url: z.ZodString;
|
|
34
|
+
method: z.ZodOptional<z.ZodString>;
|
|
35
|
+
status: z.ZodOptional<z.ZodNumber>;
|
|
36
|
+
json: z.ZodOptional<z.ZodUnknown>;
|
|
37
|
+
body: z.ZodOptional<z.ZodString>;
|
|
38
|
+
contentType: z.ZodOptional<z.ZodString>;
|
|
39
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
40
|
+
abort: z.ZodOptional<z.ZodBoolean>;
|
|
41
|
+
delayMs: z.ZodOptional<z.ZodNumber>;
|
|
42
|
+
}, z.core.$strip>>>;
|
|
32
43
|
viewport: z.ZodOptional<z.ZodObject<{
|
|
33
44
|
width: z.ZodNumber;
|
|
34
45
|
height: z.ZodNumber;
|
|
35
46
|
}, z.core.$strip>>;
|
|
47
|
+
har: z.ZodOptional<z.ZodObject<{
|
|
48
|
+
path: z.ZodString;
|
|
49
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
50
|
+
record: "record";
|
|
51
|
+
replay: "replay";
|
|
52
|
+
}>>;
|
|
53
|
+
url: z.ZodOptional<z.ZodString>;
|
|
54
|
+
notFound: z.ZodOptional<z.ZodEnum<{
|
|
55
|
+
abort: "abort";
|
|
56
|
+
fallback: "fallback";
|
|
57
|
+
}>>;
|
|
58
|
+
}, z.core.$strip>>;
|
|
59
|
+
browser: z.ZodOptional<z.ZodEnum<{
|
|
60
|
+
chromium: "chromium";
|
|
61
|
+
firefox: "firefox";
|
|
62
|
+
webkit: "webkit";
|
|
63
|
+
}>>;
|
|
36
64
|
url: z.ZodString;
|
|
37
65
|
name: z.ZodString;
|
|
38
66
|
projectRoot: z.ZodOptional<z.ZodString>;
|
|
@@ -74,10 +102,38 @@ export declare const compareVisualStateSchema: z.ZodObject<{
|
|
|
74
102
|
url: z.ZodOptional<z.ZodString>;
|
|
75
103
|
ms: z.ZodOptional<z.ZodNumber>;
|
|
76
104
|
}, z.core.$strip>>>;
|
|
105
|
+
routes: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
106
|
+
url: z.ZodString;
|
|
107
|
+
method: z.ZodOptional<z.ZodString>;
|
|
108
|
+
status: z.ZodOptional<z.ZodNumber>;
|
|
109
|
+
json: z.ZodOptional<z.ZodUnknown>;
|
|
110
|
+
body: z.ZodOptional<z.ZodString>;
|
|
111
|
+
contentType: z.ZodOptional<z.ZodString>;
|
|
112
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
113
|
+
abort: z.ZodOptional<z.ZodBoolean>;
|
|
114
|
+
delayMs: z.ZodOptional<z.ZodNumber>;
|
|
115
|
+
}, z.core.$strip>>>;
|
|
77
116
|
viewport: z.ZodOptional<z.ZodObject<{
|
|
78
117
|
width: z.ZodNumber;
|
|
79
118
|
height: z.ZodNumber;
|
|
80
119
|
}, z.core.$strip>>;
|
|
120
|
+
har: z.ZodOptional<z.ZodObject<{
|
|
121
|
+
path: z.ZodString;
|
|
122
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
123
|
+
record: "record";
|
|
124
|
+
replay: "replay";
|
|
125
|
+
}>>;
|
|
126
|
+
url: z.ZodOptional<z.ZodString>;
|
|
127
|
+
notFound: z.ZodOptional<z.ZodEnum<{
|
|
128
|
+
abort: "abort";
|
|
129
|
+
fallback: "fallback";
|
|
130
|
+
}>>;
|
|
131
|
+
}, z.core.$strip>>;
|
|
132
|
+
browser: z.ZodOptional<z.ZodEnum<{
|
|
133
|
+
chromium: "chromium";
|
|
134
|
+
firefox: "firefox";
|
|
135
|
+
webkit: "webkit";
|
|
136
|
+
}>>;
|
|
81
137
|
url: z.ZodString;
|
|
82
138
|
name: z.ZodString;
|
|
83
139
|
projectRoot: z.ZodOptional<z.ZodString>;
|
|
@@ -121,10 +177,38 @@ export declare const compareVisualStateTool: {
|
|
|
121
177
|
url: z.ZodOptional<z.ZodString>;
|
|
122
178
|
ms: z.ZodOptional<z.ZodNumber>;
|
|
123
179
|
}, z.core.$strip>>>;
|
|
180
|
+
routes: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
181
|
+
url: z.ZodString;
|
|
182
|
+
method: z.ZodOptional<z.ZodString>;
|
|
183
|
+
status: z.ZodOptional<z.ZodNumber>;
|
|
184
|
+
json: z.ZodOptional<z.ZodUnknown>;
|
|
185
|
+
body: z.ZodOptional<z.ZodString>;
|
|
186
|
+
contentType: z.ZodOptional<z.ZodString>;
|
|
187
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
188
|
+
abort: z.ZodOptional<z.ZodBoolean>;
|
|
189
|
+
delayMs: z.ZodOptional<z.ZodNumber>;
|
|
190
|
+
}, z.core.$strip>>>;
|
|
124
191
|
viewport: z.ZodOptional<z.ZodObject<{
|
|
125
192
|
width: z.ZodNumber;
|
|
126
193
|
height: z.ZodNumber;
|
|
127
194
|
}, z.core.$strip>>;
|
|
195
|
+
har: z.ZodOptional<z.ZodObject<{
|
|
196
|
+
path: z.ZodString;
|
|
197
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
198
|
+
record: "record";
|
|
199
|
+
replay: "replay";
|
|
200
|
+
}>>;
|
|
201
|
+
url: z.ZodOptional<z.ZodString>;
|
|
202
|
+
notFound: z.ZodOptional<z.ZodEnum<{
|
|
203
|
+
abort: "abort";
|
|
204
|
+
fallback: "fallback";
|
|
205
|
+
}>>;
|
|
206
|
+
}, z.core.$strip>>;
|
|
207
|
+
browser: z.ZodOptional<z.ZodEnum<{
|
|
208
|
+
chromium: "chromium";
|
|
209
|
+
firefox: "firefox";
|
|
210
|
+
webkit: "webkit";
|
|
211
|
+
}>>;
|
|
128
212
|
url: z.ZodString;
|
|
129
213
|
name: z.ZodString;
|
|
130
214
|
projectRoot: z.ZodOptional<z.ZodString>;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { z } from 'zod';
|
|
11
11
|
import type { ToolContext, ToolResponse } from '../types/index.js';
|
|
12
12
|
import type { ElementInfo } from '../types/index.js';
|
|
13
|
+
import type { ResolvedPageState } from './shared.js';
|
|
13
14
|
declare const generateInput: z.ZodObject<{
|
|
14
15
|
description: z.ZodString;
|
|
15
16
|
pageUrl: z.ZodOptional<z.ZodString>;
|
|
@@ -20,6 +21,29 @@ declare const generateInput: z.ZodObject<{
|
|
|
20
21
|
write: z.ZodOptional<z.ZodBoolean>;
|
|
21
22
|
overwrite: z.ZodOptional<z.ZodBoolean>;
|
|
22
23
|
liveInspect: z.ZodOptional<z.ZodBoolean>;
|
|
24
|
+
steps: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
25
|
+
type: z.ZodEnum<{
|
|
26
|
+
check: "check";
|
|
27
|
+
click: "click";
|
|
28
|
+
dblclick: "dblclick";
|
|
29
|
+
fill: "fill";
|
|
30
|
+
goto: "goto";
|
|
31
|
+
hover: "hover";
|
|
32
|
+
press: "press";
|
|
33
|
+
select: "select";
|
|
34
|
+
uncheck: "uncheck";
|
|
35
|
+
wait: "wait";
|
|
36
|
+
}>;
|
|
37
|
+
locator: z.ZodOptional<z.ZodString>;
|
|
38
|
+
value: z.ZodOptional<z.ZodString>;
|
|
39
|
+
url: z.ZodOptional<z.ZodString>;
|
|
40
|
+
ms: z.ZodOptional<z.ZodNumber>;
|
|
41
|
+
}, z.core.$strip>>>;
|
|
42
|
+
storageState: z.ZodOptional<z.ZodString>;
|
|
43
|
+
assertScope: z.ZodOptional<z.ZodString>;
|
|
44
|
+
verify: z.ZodOptional<z.ZodBoolean>;
|
|
45
|
+
project: z.ZodOptional<z.ZodString>;
|
|
46
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
23
47
|
}, z.core.$strip>;
|
|
24
48
|
export type GenerateE2ETestInput = z.infer<typeof generateInput>;
|
|
25
49
|
export declare const generateE2ETestSchema: z.ZodObject<{
|
|
@@ -32,6 +56,29 @@ export declare const generateE2ETestSchema: z.ZodObject<{
|
|
|
32
56
|
write: z.ZodOptional<z.ZodBoolean>;
|
|
33
57
|
overwrite: z.ZodOptional<z.ZodBoolean>;
|
|
34
58
|
liveInspect: z.ZodOptional<z.ZodBoolean>;
|
|
59
|
+
steps: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
60
|
+
type: z.ZodEnum<{
|
|
61
|
+
check: "check";
|
|
62
|
+
click: "click";
|
|
63
|
+
dblclick: "dblclick";
|
|
64
|
+
fill: "fill";
|
|
65
|
+
goto: "goto";
|
|
66
|
+
hover: "hover";
|
|
67
|
+
press: "press";
|
|
68
|
+
select: "select";
|
|
69
|
+
uncheck: "uncheck";
|
|
70
|
+
wait: "wait";
|
|
71
|
+
}>;
|
|
72
|
+
locator: z.ZodOptional<z.ZodString>;
|
|
73
|
+
value: z.ZodOptional<z.ZodString>;
|
|
74
|
+
url: z.ZodOptional<z.ZodString>;
|
|
75
|
+
ms: z.ZodOptional<z.ZodNumber>;
|
|
76
|
+
}, z.core.$strip>>>;
|
|
77
|
+
storageState: z.ZodOptional<z.ZodString>;
|
|
78
|
+
assertScope: z.ZodOptional<z.ZodString>;
|
|
79
|
+
verify: z.ZodOptional<z.ZodBoolean>;
|
|
80
|
+
project: z.ZodOptional<z.ZodString>;
|
|
81
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
35
82
|
}, z.core.$strip>;
|
|
36
83
|
/**
|
|
37
84
|
* Live elements worth a step: those whose accessible name or test id shares
|
|
@@ -43,6 +90,26 @@ export declare function pickLiveLocators(description: string, elements: ElementI
|
|
|
43
90
|
role?: string;
|
|
44
91
|
name?: string;
|
|
45
92
|
}>;
|
|
93
|
+
/** One walked step as Playwright code. */
|
|
94
|
+
export declare function stepCode(action: NonNullable<ResolvedPageState['actions']>[number]): string;
|
|
95
|
+
/**
|
|
96
|
+
* End-state assertions: the URL the flow lands on (path only, so query
|
|
97
|
+
* ids do not pin it) and the accessibility snapshot of the scope, or its
|
|
98
|
+
* headings when the snapshot is too long to be a useful assertion.
|
|
99
|
+
*/
|
|
100
|
+
export declare function endStateAssertions(options: {
|
|
101
|
+
entryUrl: string;
|
|
102
|
+
finalUrl?: string;
|
|
103
|
+
snapshot?: string;
|
|
104
|
+
scope?: string;
|
|
105
|
+
}): string[];
|
|
106
|
+
export declare function buildWalkSpec(options: {
|
|
107
|
+
description: string;
|
|
108
|
+
entryUrl: string;
|
|
109
|
+
steps: NonNullable<ResolvedPageState['actions']>;
|
|
110
|
+
storageState?: string;
|
|
111
|
+
assertions: string[];
|
|
112
|
+
}): string;
|
|
46
113
|
export declare const generateE2ETestTool: {
|
|
47
114
|
name: string;
|
|
48
115
|
description: string;
|
|
@@ -56,6 +123,29 @@ export declare const generateE2ETestTool: {
|
|
|
56
123
|
write: z.ZodOptional<z.ZodBoolean>;
|
|
57
124
|
overwrite: z.ZodOptional<z.ZodBoolean>;
|
|
58
125
|
liveInspect: z.ZodOptional<z.ZodBoolean>;
|
|
126
|
+
steps: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
127
|
+
type: z.ZodEnum<{
|
|
128
|
+
check: "check";
|
|
129
|
+
click: "click";
|
|
130
|
+
dblclick: "dblclick";
|
|
131
|
+
fill: "fill";
|
|
132
|
+
goto: "goto";
|
|
133
|
+
hover: "hover";
|
|
134
|
+
press: "press";
|
|
135
|
+
select: "select";
|
|
136
|
+
uncheck: "uncheck";
|
|
137
|
+
wait: "wait";
|
|
138
|
+
}>;
|
|
139
|
+
locator: z.ZodOptional<z.ZodString>;
|
|
140
|
+
value: z.ZodOptional<z.ZodString>;
|
|
141
|
+
url: z.ZodOptional<z.ZodString>;
|
|
142
|
+
ms: z.ZodOptional<z.ZodNumber>;
|
|
143
|
+
}, z.core.$strip>>>;
|
|
144
|
+
storageState: z.ZodOptional<z.ZodString>;
|
|
145
|
+
assertScope: z.ZodOptional<z.ZodString>;
|
|
146
|
+
verify: z.ZodOptional<z.ZodBoolean>;
|
|
147
|
+
project: z.ZodOptional<z.ZodString>;
|
|
148
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
59
149
|
}, z.core.$strip>;
|
|
60
150
|
handler: (args: GenerateE2ETestInput, ctx: ToolContext) => Promise<ToolResponse>;
|
|
61
151
|
};
|