From cdfe075b2aaf2285657bb5f3f285dbe078735f00 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Tue, 12 Oct 2021 20:21:06 -0800 Subject: [PATCH] chore(traceviewer): drop intermediate iframe, add drop handler (#9455) --- .../playwright-core/src/utils/httpServer.ts | 12 ++++ .../src/web/traceViewer/snapshotRenderer.ts | 17 ++++++ .../src/web/traceViewer/snapshotServer.ts | 57 ------------------- .../playwright-core/src/web/traceViewer/sw.ts | 4 +- .../src/web/traceViewer/traceModel.ts | 2 +- .../src/web/traceViewer/ui/actionList.css | 13 ++--- .../src/web/traceViewer/ui/actionList.tsx | 11 +--- .../src/web/traceViewer/ui/snapshotTab.tsx | 5 +- .../src/web/traceViewer/ui/workbench.tsx | 14 +++-- tests/trace-viewer/trace-viewer.spec.ts | 4 +- 10 files changed, 50 insertions(+), 89 deletions(-) diff --git a/packages/playwright-core/src/utils/httpServer.ts b/packages/playwright-core/src/utils/httpServer.ts index ab87f905f2..95715dc6e4 100644 --- a/packages/playwright-core/src/utils/httpServer.ts +++ b/packages/playwright-core/src/utils/httpServer.ts @@ -113,6 +113,18 @@ export class HttpServer { } private _onRequest(request: http.IncomingMessage, response: http.ServerResponse) { + response.setHeader('Access-Control-Allow-Origin', '*'); + response.setHeader('Access-Control-Request-Method', '*'); + response.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET'); + if (request.headers.origin) + response.setHeader('Access-Control-Allow-Headers', request.headers.origin); + + if (request.method === 'OPTIONS') { + response.writeHead(200); + response.end(); + return; + } + request.on('error', () => response.end()); try { if (!request.url) { diff --git a/packages/playwright-core/src/web/traceViewer/snapshotRenderer.ts b/packages/playwright-core/src/web/traceViewer/snapshotRenderer.ts index e0b93d1a49..aea85cfc81 100644 --- a/packages/playwright-core/src/web/traceViewer/snapshotRenderer.ts +++ b/packages/playwright-core/src/web/traceViewer/snapshotRenderer.ts @@ -220,6 +220,23 @@ function snapshotScript() { element.scrollLeft = +element.getAttribute(scrollLeftAttribute)!; element.removeAttribute(scrollLeftAttribute); } + + const search = new URL(window.location.href).searchParams; + const pointX = search.get('pointX'); + const pointY = search.get('pointY'); + if (pointX) { + const pointElement = document.createElement('x-pw-pointer'); + pointElement.style.position = 'fixed'; + pointElement.style.backgroundColor = 'red'; + pointElement.style.width = '20px'; + pointElement.style.height = '20px'; + pointElement.style.borderRadius = '10px'; + pointElement.style.margin = '-10px 0 0 -10px'; + pointElement.style.zIndex = '2147483647'; + pointElement.style.left = pointX + 'px'; + pointElement.style.top = pointY + 'px'; + document.documentElement.appendChild(pointElement); + } }; window.addEventListener('load', onLoad); } diff --git a/packages/playwright-core/src/web/traceViewer/snapshotServer.ts b/packages/playwright-core/src/web/traceViewer/snapshotServer.ts index bb2eb0fdde..b106635124 100644 --- a/packages/playwright-core/src/web/traceViewer/snapshotServer.ts +++ b/packages/playwright-core/src/web/traceViewer/snapshotServer.ts @@ -30,36 +30,6 @@ export class SnapshotServer { this._snapshotStorage = snapshotStorage; } - static serveSnapshotRoot(): Response { - return new Response(` - - - - - `, { - status: 200, - headers: { - 'Cache-Control': 'public, max-age=31536000', - 'Content-Type': 'text/html' - } - }); - } - serveSnapshot(pathname: string, searchParams: URLSearchParams, snapshotUrl: string): Response { const snapshot = this._snapshot(pathname.substring('/snapshot'.length), searchParams); if (!snapshot) @@ -132,33 +102,6 @@ declare global { } } -function rootScript() { - const pointElement = document.createElement('div'); - pointElement.style.position = 'fixed'; - pointElement.style.backgroundColor = 'red'; - pointElement.style.width = '20px'; - pointElement.style.height = '20px'; - pointElement.style.borderRadius = '10px'; - pointElement.style.margin = '-10px 0 0 -10px'; - pointElement.style.zIndex = '2147483647'; - - const iframe = document.createElement('iframe'); - document.body.appendChild(iframe); - (window as any).showSnapshot = async (url: string, options: { point?: Point } = {}) => { - iframe.src = url; - if (options.point) { - pointElement.style.left = options.point.x + 'px'; - pointElement.style.top = options.point.y + 'px'; - document.documentElement.appendChild(pointElement); - } else { - pointElement.remove(); - } - }; - window.addEventListener('message', event => { - window.showSnapshot(window.location.href + event.data.snapshotUrl); - }, false); -} - function removeHash(url: string) { try { const u = new URL(url); diff --git a/packages/playwright-core/src/web/traceViewer/sw.ts b/packages/playwright-core/src/web/traceViewer/sw.ts index 3811c3bb0e..93212fadba 100644 --- a/packages/playwright-core/src/web/traceViewer/sw.ts +++ b/packages/playwright-core/src/web/traceViewer/sw.ts @@ -31,7 +31,7 @@ let snapshotServer: SnapshotServer | undefined; async function loadTrace(trace: string): Promise { const traceModel = new TraceModel(); - const url = trace.startsWith('http') ? trace : `/file?path=${trace}`; + const url = trace.startsWith('http') || trace.startsWith('blob') ? trace : `/file?path=${trace}`; await traceModel.load(url); return traceModel; } @@ -53,8 +53,6 @@ async function doFetch(event: FetchEvent): Promise { headers: { 'Content-Type': 'application/json' } }); } - if (pathname === '/snapshot/') - return SnapshotServer.serveSnapshotRoot(); if (pathname.startsWith('/snapshotSize/')) return snapshotServer!.serveSnapshotSize(pathname, searchParams); if (pathname.startsWith('/snapshot/')) diff --git a/packages/playwright-core/src/web/traceViewer/traceModel.ts b/packages/playwright-core/src/web/traceViewer/traceModel.ts index 608d67edeb..0c731e0d9e 100644 --- a/packages/playwright-core/src/web/traceViewer/traceModel.ts +++ b/packages/playwright-core/src/web/traceViewer/traceModel.ts @@ -43,7 +43,7 @@ export class TraceModel { } async load(traceURL: string) { - const response = await fetch(traceURL); + const response = await fetch(traceURL, { mode: 'cors' }); const blob = await response.blob(); const zipReader = new zipjs.ZipReader(new zipjs.BlobReader(blob), { useWebWorkers: false }) as zip.ZipReader; let traceEntry: zip.Entry | undefined; diff --git a/packages/playwright-core/src/web/traceViewer/ui/actionList.css b/packages/playwright-core/src/web/traceViewer/ui/actionList.css index c530eafb27..181454f657 100644 --- a/packages/playwright-core/src/web/traceViewer/ui/actionList.css +++ b/packages/playwright-core/src/web/traceViewer/ui/actionList.css @@ -124,13 +124,8 @@ } .no-actions-entry { - height: 400px; - display: grid; - place-items: center; - text-align: center; -} - -.no-actions-entry-text { - font-weight: bold; - font-size: 1.3rem; + flex: auto; + display: flex; + align-items: center; + justify-content: center; } diff --git a/packages/playwright-core/src/web/traceViewer/ui/actionList.tsx b/packages/playwright-core/src/web/traceViewer/ui/actionList.tsx index b62f333261..1a99507de2 100644 --- a/packages/playwright-core/src/web/traceViewer/ui/actionList.tsx +++ b/packages/playwright-core/src/web/traceViewer/ui/actionList.tsx @@ -72,16 +72,7 @@ export const ActionList: React.FC = ({ }} ref={actionListRef} > - {actions.length === 0 &&
-
-
- No actions recorded -
-
- Make sure that the right context was used when recording the trace. -
-
-
} + {actions.length === 0 &&
No actions recorded
} {actions.map(action => { const { metadata } = action; const selectedSuffix = action === selectedAction ? ' selected' : ''; diff --git a/packages/playwright-core/src/web/traceViewer/ui/snapshotTab.tsx b/packages/playwright-core/src/web/traceViewer/ui/snapshotTab.tsx index ac33873f3e..1a1e831e81 100644 --- a/packages/playwright-core/src/web/traceViewer/ui/snapshotTab.tsx +++ b/packages/playwright-core/src/web/traceViewer/ui/snapshotTab.tsx @@ -66,8 +66,7 @@ export const SnapshotTab: React.FunctionComponent<{ if (!iframeRef.current) return; try { - const point = pointX === undefined ? undefined : { x: pointX, y: pointY }; - (iframeRef.current.contentWindow as any).showSnapshot(snapshotUrl, { point }); + iframeRef.current.src = snapshotUrl + (pointX === undefined ? '' : `&pointX=${pointX}&pointY=${pointY}`); } catch (e) { } })(); @@ -102,7 +101,7 @@ export const SnapshotTab: React.FunctionComponent<{ height: snapshotSize.height + 'px', transform: `translate(${-snapshotSize.width * (1 - scale) / 2 + (measure.width - scaledSize.width) / 2}px, ${-snapshotSize.height * (1 - scale) / 2 + (measure.height - scaledSize.height) / 2}px) scale(${scale})`, }}> - + ; diff --git a/packages/playwright-core/src/web/traceViewer/ui/workbench.tsx b/packages/playwright-core/src/web/traceViewer/ui/workbench.tsx index a6d759d126..7e06cc6ab0 100644 --- a/packages/playwright-core/src/web/traceViewer/ui/workbench.tsx +++ b/packages/playwright-core/src/web/traceViewer/ui/workbench.tsx @@ -31,19 +31,19 @@ import * as modelUtil from './modelUtil'; export const Workbench: React.FunctionComponent<{ }> = () => { + const [traceURL, setTraceURL] = React.useState(new URL(window.location.href).searchParams.get('trace')!); const [contextEntry, setContextEntry] = React.useState(emptyContext); const [selectedAction, setSelectedAction] = React.useState(); const [highlightedAction, setHighlightedAction] = React.useState(); const [selectedTab, setSelectedTab] = React.useState('logs'); - const trace = new URL(window.location.href).searchParams.get('trace'); React.useEffect(() => { (async () => { - const contextEntry = (await fetch(`/context?trace=${trace}`).then(response => response.json())) as ContextEntry; + const contextEntry = (await fetch(`/context?trace=${traceURL}`).then(response => response.json())) as ContextEntry; modelUtil.indexModel(contextEntry); setContextEntry(contextEntry); })(); - }, [trace]); + }, [traceURL]); const actions = React.useMemo(() => { const actions: ActionTraceEvent[] = []; @@ -61,7 +61,13 @@ export const Workbench: React.FunctionComponent<{ const consoleCount = errors + warnings; const networkCount = selectedAction ? modelUtil.resourcesForAction(selectedAction).length : 0; - return
+ return
{ event.preventDefault(); }} + onDrop={event => { + event.preventDefault(); + const url = URL.createObjectURL(event.dataTransfer.files[0]); + setTraceURL(url.toString()); + }}>
🎭
Playwright
diff --git a/tests/trace-viewer/trace-viewer.spec.ts b/tests/trace-viewer/trace-viewer.spec.ts index 95b81d49d9..32d5e0a28c 100644 --- a/tests/trace-viewer/trace-viewer.spec.ts +++ b/tests/trace-viewer/trace-viewer.spec.ts @@ -84,9 +84,9 @@ class TraceViewerPage { async snapshotFrame(actionName: string, ordinal: number = 0, hasSubframe: boolean = false): Promise { await this.selectAction(actionName, ordinal); - while (this.page.frames().length < (hasSubframe ? 4 : 3)) + while (this.page.frames().length < (hasSubframe ? 3 : 2)) await this.page.waitForEvent('frameattached'); - return this.page.mainFrame().childFrames()[0].childFrames()[0]; + return this.page.mainFrame().childFrames()[0]; } }