feat(trace-viewer): render snapshot urls (#9993)

This commit is contained in:
Pavel Feldman 2021-11-02 16:35:23 -08:00 committed by GitHub
parent 009478b8d5
commit cd47bf26e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 43 additions and 17 deletions

View File

@ -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) {

View File

@ -74,10 +74,10 @@ async function doFetch(event: FetchEvent): Promise<Response> {
});
}
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/')) {

View File

@ -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;

View File

@ -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<HTMLDivElement>();
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,<body style="background: #ddd"></body>';
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<HTMLIFrameElement>(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<{
</div>;
})}
</div>
<div className='snapshot-url' title={snapshotInfo.url}>{snapshotInfo.url}</div>
<div ref={ref} className='snapshot-wrapper'>
<div className='snapshot-container' style={{
width: snapshotSize.width + 'px',

View File

@ -83,7 +83,7 @@ export const Workbench: React.FunctionComponent<{
})();
}, [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 };
@ -121,7 +121,7 @@ export const Workbench: React.FunctionComponent<{
</div>
<SplitView sidebarSize={300} orientation='horizontal' sidebarIsFirst={true}>
<SplitView sidebarSize={300} orientation='horizontal'>
<SnapshotTab action={selectedAction} defaultSnapshotSize={defaultSnapshotSize} />
<SnapshotTab action={selectedAction} defaultSnapshotInfo={defaultSnapshotInfo} />
<TabbedPane tabs={tabs} selectedTab={selectedTab} setSelectedTab={setSelectedTab}/>
</SplitView>
<ActionList

View File

@ -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 => {
route.fulfill({
body: '<iframe src="iframe.html"></iframe>',