diff --git a/packages/playwright-core/src/tools/trace/traceSnapshot.ts b/packages/playwright-core/src/tools/trace/traceSnapshot.ts index df417abc424b6..ccac7fb5a2dcd 100644 --- a/packages/playwright-core/src/tools/trace/traceSnapshot.ts +++ b/packages/playwright-core/src/tools/trace/traceSnapshot.ts @@ -87,11 +87,12 @@ async function serveTraceSnapshot(storage: SnapshotStorage, loader: TraceLoader, const snapshotServer = new SnapshotServer(storage, sha1 => loader.resourceForSha1(sha1)); const httpServer = new HttpServer(); - httpServer.routePrefix('/snapshot', (request: any, response: any) => { + httpServer.routePrefix('/snapshot/', (request: any, response: any) => { const url = new URL('http://localhost' + request.url!); + const pageOrFrameId = url.pathname.substring('/snapshot/'.length); const searchParams = url.searchParams; searchParams.set('name', snapshotKey); - const snapshotResponse = snapshotServer.serveSnapshot(pageId, searchParams, '/snapshot'); + const snapshotResponse = snapshotServer.serveSnapshot(pageOrFrameId, searchParams, url.href); response.statusCode = snapshotResponse.status; snapshotResponse.headers.forEach((value: string, key: string) => response.setHeader(key, value)); snapshotResponse.text().then((text: string) => response.end(text)); @@ -100,7 +101,7 @@ async function serveTraceSnapshot(storage: SnapshotStorage, loader: TraceLoader, httpServer.routePrefix('/', (_request: any, response: any) => { response.statusCode = 302; - response.setHeader('Location', '/snapshot'); + response.setHeader('Location', `/snapshot/${pageId}?name=${encodeURIComponent(snapshotKey)}`); response.end(); return true; }); diff --git a/packages/trace-viewer/src/ui/callTab.tsx b/packages/trace-viewer/src/ui/callTab.tsx index 63ca7f567a4b9..69da4e94c1089 100644 --- a/packages/trace-viewer/src/ui/callTab.tsx +++ b/packages/trace-viewer/src/ui/callTab.tsx @@ -84,12 +84,15 @@ function renderDuration(action: ActionTraceEventInContext): string { } function renderProperty(property: Property) { - let text = property.text.replace(/\n/g, '↵'); + let text = property.text; + if (text.length > 1000) + text = text.slice(0, 1000) + '…'; + text = text.replace(/\n/g, '↵'); if (property.type === 'string') text = `"${text}"`; return (
Iframe content
+ + + + `, 'text/html'); + + server.setContent('/iframe-inner', ` + +Innermost
`, 'text/html'); @@ -84,7 +104,9 @@ export const test = baseTest // Navigate to another page await page.locator('a').click(); - await page.waitForURL('**/page2'); + + // Click into innermost frame + await page.frameLocator('#frame1').frameLocator('#frame2').locator('p').click(); await page.close(); const tmpDir = path.join(workerInfo.project.outputDir, 'pw-trace-cli-' + workerInfo.workerIndex); diff --git a/tests/mcp/trace-cli.spec.ts b/tests/mcp/trace-cli.spec.ts index 82668761d5fe1..3bc4c01fd08b0 100644 --- a/tests/mcp/trace-cli.spec.ts +++ b/tests/mcp/trace-cli.spec.ts @@ -175,6 +175,16 @@ test('trace snapshot --name before', async ({ runTraceCli }) => { expect(stdout).toBeTruthy(); }); +test('trace snapshot resolves inner frames', async ({ runTraceCli }) => { + const { stdout: listOutput } = await runTraceCli(['actions', '--grep', 'Click']); + const ordinals = [...listOutput.matchAll(/^\s+(\d+)\.\s/gm)].map(m => m[1]); + expect(ordinals.length).toBeGreaterThanOrEqual(2); + const anchorClickOrdinal = ordinals[ordinals.length - 1]; + + const { stdout } = await runTraceCli(['snapshot', '--name', 'after', anchorClickOrdinal]); + expect(stdout).toContain('Innermost'); +}); + test('trace screenshot saves image file', async ({ runTraceCli }, testInfo) => { const { stdout: listOutput } = await runTraceCli(['actions', '--grep', 'Navigate']); const match = listOutput.match(/^\s+(\d+)\.\s/m);