mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-14 13:45:36 +03:00
feat(trace-viewer): render snapshot urls (#9993)
This commit is contained in:
parent
009478b8d5
commit
cd47bf26e8
@ -39,9 +39,12 @@ export class SnapshotServer {
|
|||||||
return new Response(renderedSnapshot.html, { status: 200, headers: { 'Content-Type': 'text/html' } });
|
return new Response(renderedSnapshot.html, { status: 200, headers: { 'Content-Type': 'text/html' } });
|
||||||
}
|
}
|
||||||
|
|
||||||
serveSnapshotSize(pathname: string, searchParams: URLSearchParams): Response {
|
serveSnapshotInfo(pathname: string, searchParams: URLSearchParams): Response {
|
||||||
const snapshot = this._snapshot(pathname.substring('/snapshotSize'.length), searchParams);
|
const snapshot = this._snapshot(pathname.substring('/snapshotInfo'.length), searchParams);
|
||||||
return this._respondWithJson(snapshot ? snapshot.viewport() : {});
|
return this._respondWithJson(snapshot ? {
|
||||||
|
viewport: snapshot.viewport(),
|
||||||
|
url: snapshot.snapshot().frameUrl
|
||||||
|
} : {});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _snapshot(pathname: string, params: URLSearchParams) {
|
private _snapshot(pathname: string, params: URLSearchParams) {
|
||||||
|
@ -74,10 +74,10 @@ async function doFetch(event: FetchEvent): Promise<Response> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relativePath.startsWith('/snapshotSize/')) {
|
if (relativePath.startsWith('/snapshotInfo/')) {
|
||||||
if (!snapshotServer)
|
if (!snapshotServer)
|
||||||
return new Response(null, { status: 404 });
|
return new Response(null, { status: 404 });
|
||||||
return snapshotServer.serveSnapshotSize(relativePath, url.searchParams);
|
return snapshotServer.serveSnapshotInfo(relativePath, url.searchParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relativePath.startsWith('/snapshot/')) {
|
if (relativePath.startsWith('/snapshot/')) {
|
||||||
|
@ -64,6 +64,17 @@
|
|||||||
padding: 10px;
|
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 {
|
.snapshot-container {
|
||||||
display: block;
|
display: block;
|
||||||
background: white;
|
background: white;
|
||||||
|
@ -23,8 +23,8 @@ import { ActionTraceEvent } from '../../../server/trace/common/traceEvents';
|
|||||||
|
|
||||||
export const SnapshotTab: React.FunctionComponent<{
|
export const SnapshotTab: React.FunctionComponent<{
|
||||||
action: ActionTraceEvent | undefined,
|
action: ActionTraceEvent | undefined,
|
||||||
defaultSnapshotSize: Size,
|
defaultSnapshotInfo: { viewport: Size, url: string },
|
||||||
}> = ({ action, defaultSnapshotSize }) => {
|
}> = ({ action, defaultSnapshotInfo }) => {
|
||||||
const [measure, ref] = useMeasure<HTMLDivElement>();
|
const [measure, ref] = useMeasure<HTMLDivElement>();
|
||||||
const [snapshotIndex, setSnapshotIndex] = React.useState(0);
|
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 }[];
|
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,<body style="background: #ddd"></body>';
|
let snapshotUrl = 'data:text/html,<body style="background: #ddd"></body>';
|
||||||
let snapshotSizeUrl: string | undefined;
|
let snapshotInfoUrl: string | undefined;
|
||||||
let pointX: number | undefined;
|
let pointX: number | undefined;
|
||||||
let pointY: number | undefined;
|
let pointY: number | undefined;
|
||||||
if (action) {
|
if (action) {
|
||||||
@ -43,7 +43,7 @@ export const SnapshotTab: React.FunctionComponent<{
|
|||||||
if (snapshot && snapshot.snapshotName) {
|
if (snapshot && snapshot.snapshotName) {
|
||||||
const traceUrl = new URL(window.location.href).searchParams.get('trace');
|
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();
|
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')) {
|
if (snapshot.snapshotName.includes('action')) {
|
||||||
pointX = action.metadata.point?.x;
|
pointX = action.metadata.point?.x;
|
||||||
pointY = action.metadata.point?.y;
|
pointY = action.metadata.point?.y;
|
||||||
@ -57,12 +57,13 @@ export const SnapshotTab: React.FunctionComponent<{
|
|||||||
}, [snapshotIndex, snapshots]);
|
}, [snapshotIndex, snapshots]);
|
||||||
|
|
||||||
const iframeRef = React.useRef<HTMLIFrameElement>(null);
|
const iframeRef = React.useRef<HTMLIFrameElement>(null);
|
||||||
const [snapshotSize, setSnapshotSize] = React.useState(defaultSnapshotSize);
|
const [snapshotInfo, setSnapshotInfo] = React.useState(defaultSnapshotInfo);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
if (snapshotSizeUrl) {
|
if (snapshotInfoUrl) {
|
||||||
const response = await fetch(snapshotSizeUrl);
|
const response = await fetch(snapshotInfoUrl);
|
||||||
setSnapshotSize(await response.json());
|
const info = await response.json();
|
||||||
|
setSnapshotInfo(info);
|
||||||
}
|
}
|
||||||
if (!iframeRef.current)
|
if (!iframeRef.current)
|
||||||
return;
|
return;
|
||||||
@ -71,8 +72,9 @@ export const SnapshotTab: React.FunctionComponent<{
|
|||||||
} catch (e) {
|
} 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 scale = Math.min(measure.width / snapshotSize.width, measure.height / snapshotSize.height, 1);
|
||||||
const scaledSize = {
|
const scaledSize = {
|
||||||
width: snapshotSize.width * scale,
|
width: snapshotSize.width * scale,
|
||||||
@ -96,6 +98,7 @@ export const SnapshotTab: React.FunctionComponent<{
|
|||||||
</div>;
|
</div>;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
<div className='snapshot-url' title={snapshotInfo.url}>{snapshotInfo.url}</div>
|
||||||
<div ref={ref} className='snapshot-wrapper'>
|
<div ref={ref} className='snapshot-wrapper'>
|
||||||
<div className='snapshot-container' style={{
|
<div className='snapshot-container' style={{
|
||||||
width: snapshotSize.width + 'px',
|
width: snapshotSize.width + 'px',
|
||||||
|
@ -83,7 +83,7 @@ export const Workbench: React.FunctionComponent<{
|
|||||||
})();
|
})();
|
||||||
}, [traceURL]);
|
}, [traceURL]);
|
||||||
|
|
||||||
const defaultSnapshotSize = contextEntry.options.viewport || { width: 1280, height: 720 };
|
const defaultSnapshotInfo = { viewport: contextEntry.options.viewport || { width: 1280, height: 720 }, url: '' };
|
||||||
const boundaries = { minimum: contextEntry.startTime, maximum: contextEntry.endTime };
|
const boundaries = { minimum: contextEntry.startTime, maximum: contextEntry.endTime };
|
||||||
|
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ export const Workbench: React.FunctionComponent<{
|
|||||||
</div>
|
</div>
|
||||||
<SplitView sidebarSize={300} orientation='horizontal' sidebarIsFirst={true}>
|
<SplitView sidebarSize={300} orientation='horizontal' sidebarIsFirst={true}>
|
||||||
<SplitView sidebarSize={300} orientation='horizontal'>
|
<SplitView sidebarSize={300} orientation='horizontal'>
|
||||||
<SnapshotTab action={selectedAction} defaultSnapshotSize={defaultSnapshotSize} />
|
<SnapshotTab action={selectedAction} defaultSnapshotInfo={defaultSnapshotInfo} />
|
||||||
<TabbedPane tabs={tabs} selectedTab={selectedTab} setSelectedTab={setSelectedTab}/>
|
<TabbedPane tabs={tabs} selectedTab={selectedTab} setSelectedTab={setSelectedTab}/>
|
||||||
</SplitView>
|
</SplitView>
|
||||||
<ActionList
|
<ActionList
|
||||||
|
@ -278,7 +278,16 @@ test('should have network requests', async ({ showTraceViewer }) => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
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 => {
|
await page.route('**/empty.html', route => {
|
||||||
route.fulfill({
|
route.fulfill({
|
||||||
body: '<iframe src="iframe.html"></iframe>',
|
body: '<iframe src="iframe.html"></iframe>',
|
||||||
|
Loading…
Reference in New Issue
Block a user