diff --git a/packages/playwright-core/src/web/traceViewer/snapshotServer.ts b/packages/playwright-core/src/web/traceViewer/snapshotServer.ts index b106635124..1aaf00e6fe 100644 --- a/packages/playwright-core/src/web/traceViewer/snapshotServer.ts +++ b/packages/playwright-core/src/web/traceViewer/snapshotServer.ts @@ -39,9 +39,12 @@ export class SnapshotServer { return new Response(renderedSnapshot.html, { status: 200, headers: { 'Content-Type': 'text/html' } }); } - serveSnapshotSize(pathname: string, searchParams: URLSearchParams): Response { - const snapshot = this._snapshot(pathname.substring('/snapshotSize'.length), searchParams); - return this._respondWithJson(snapshot ? snapshot.viewport() : {}); + serveSnapshotInfo(pathname: string, searchParams: URLSearchParams): Response { + const snapshot = this._snapshot(pathname.substring('/snapshotInfo'.length), searchParams); + return this._respondWithJson(snapshot ? { + viewport: snapshot.viewport(), + url: snapshot.snapshot().frameUrl + } : {}); } private _snapshot(pathname: string, params: URLSearchParams) { diff --git a/packages/playwright-core/src/web/traceViewer/sw.ts b/packages/playwright-core/src/web/traceViewer/sw.ts index d9d4c72b87..7f0acd92d3 100644 --- a/packages/playwright-core/src/web/traceViewer/sw.ts +++ b/packages/playwright-core/src/web/traceViewer/sw.ts @@ -74,10 +74,10 @@ async function doFetch(event: FetchEvent): Promise { }); } - if (relativePath.startsWith('/snapshotSize/')) { + if (relativePath.startsWith('/snapshotInfo/')) { if (!snapshotServer) return new Response(null, { status: 404 }); - return snapshotServer.serveSnapshotSize(relativePath, url.searchParams); + return snapshotServer.serveSnapshotInfo(relativePath, url.searchParams); } if (relativePath.startsWith('/snapshot/')) { diff --git a/packages/playwright-core/src/web/traceViewer/ui/snapshotTab.css b/packages/playwright-core/src/web/traceViewer/ui/snapshotTab.css index 765cf6ff9f..ea18e526d1 100644 --- a/packages/playwright-core/src/web/traceViewer/ui/snapshotTab.css +++ b/packages/playwright-core/src/web/traceViewer/ui/snapshotTab.css @@ -64,6 +64,17 @@ padding: 10px; } +.snapshot-url { + background-color: var(--background); + margin: 10px; + padding: 4px; + border-radius: 3px; + height: 28px; + display: flex; + align-items: center; + overflow: hidden; +} + .snapshot-container { display: block; background: white; diff --git a/packages/playwright-core/src/web/traceViewer/ui/snapshotTab.tsx b/packages/playwright-core/src/web/traceViewer/ui/snapshotTab.tsx index edce0c1ddd..7aaeed6775 100644 --- a/packages/playwright-core/src/web/traceViewer/ui/snapshotTab.tsx +++ b/packages/playwright-core/src/web/traceViewer/ui/snapshotTab.tsx @@ -23,8 +23,8 @@ import { ActionTraceEvent } from '../../../server/trace/common/traceEvents'; export const SnapshotTab: React.FunctionComponent<{ action: ActionTraceEvent | undefined, - defaultSnapshotSize: Size, -}> = ({ action, defaultSnapshotSize }) => { + defaultSnapshotInfo: { viewport: Size, url: string }, +}> = ({ action, defaultSnapshotInfo }) => { const [measure, ref] = useMeasure(); const [snapshotIndex, setSnapshotIndex] = React.useState(0); @@ -35,7 +35,7 @@ export const SnapshotTab: React.FunctionComponent<{ const snapshots = [actionSnapshot ? { ...actionSnapshot, title: 'action' } : undefined, snapshotMap.get('before'), snapshotMap.get('after')].filter(Boolean) as { title: string, snapshotName: string }[]; let snapshotUrl = 'data:text/html,'; - let snapshotSizeUrl: string | undefined; + let snapshotInfoUrl: string | undefined; let pointX: number | undefined; let pointY: number | undefined; if (action) { @@ -43,7 +43,7 @@ export const SnapshotTab: React.FunctionComponent<{ if (snapshot && snapshot.snapshotName) { const traceUrl = new URL(window.location.href).searchParams.get('trace'); snapshotUrl = new URL(`snapshot/${action.metadata.pageId}?trace=${traceUrl}&name=${snapshot.snapshotName}`, window.location.href).toString(); - snapshotSizeUrl = new URL(`snapshotSize/${action.metadata.pageId}?trace=${traceUrl}&name=${snapshot.snapshotName}`, window.location.href).toString(); + snapshotInfoUrl = new URL(`snapshotInfo/${action.metadata.pageId}?trace=${traceUrl}&name=${snapshot.snapshotName}`, window.location.href).toString(); if (snapshot.snapshotName.includes('action')) { pointX = action.metadata.point?.x; pointY = action.metadata.point?.y; @@ -57,12 +57,13 @@ export const SnapshotTab: React.FunctionComponent<{ }, [snapshotIndex, snapshots]); const iframeRef = React.useRef(null); - const [snapshotSize, setSnapshotSize] = React.useState(defaultSnapshotSize); + const [snapshotInfo, setSnapshotInfo] = React.useState(defaultSnapshotInfo); React.useEffect(() => { (async () => { - if (snapshotSizeUrl) { - const response = await fetch(snapshotSizeUrl); - setSnapshotSize(await response.json()); + if (snapshotInfoUrl) { + const response = await fetch(snapshotInfoUrl); + const info = await response.json(); + setSnapshotInfo(info); } if (!iframeRef.current) return; @@ -71,8 +72,9 @@ export const SnapshotTab: React.FunctionComponent<{ } catch (e) { } })(); - }, [iframeRef, snapshotUrl, snapshotSizeUrl, pointX, pointY]); + }, [iframeRef, snapshotUrl, snapshotInfoUrl, pointX, pointY]); + const snapshotSize = snapshotInfo.viewport; const scale = Math.min(measure.width / snapshotSize.width, measure.height / snapshotSize.height, 1); const scaledSize = { width: snapshotSize.width * scale, @@ -96,6 +98,7 @@ export const SnapshotTab: React.FunctionComponent<{ ; })} +
{snapshotInfo.url}
- + { ]); }); -test('should capture iframe', async ({ page, server, browserName, runAndTrace }) => { +test('should show snapshot URL', async ({ page, runAndTrace, server }) => { + const traceViewer = await runAndTrace(async () => { + await page.goto(server.EMPTY_PAGE); + await page.evaluate('2+2'); + }); + await traceViewer.snapshotFrame('page.evaluate'); + await expect(traceViewer.page.locator('.snapshot-url')).toHaveText(server.EMPTY_PAGE); +}); + +test('should capture iframe', async ({ page, server, runAndTrace }) => { await page.route('**/empty.html', route => { route.fulfill({ body: '',