From c890510d867741fe2d864c38c5408b28997aa4a9 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Fri, 22 Oct 2021 15:59:17 -0800 Subject: [PATCH] feat(traceviewer): show load progress (#9726) --- .../playwright-core/src/web/traceViewer/sw.ts | 17 ++++++++++------ .../src/web/traceViewer/traceModel.ts | 10 +++++----- .../src/web/traceViewer/ui/workbench.css | 13 ++++++++++++ .../src/web/traceViewer/ui/workbench.tsx | 20 +++++++++++++++---- 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/packages/playwright-core/src/web/traceViewer/sw.ts b/packages/playwright-core/src/web/traceViewer/sw.ts index bd4d846b70..cf7a514bd2 100644 --- a/packages/playwright-core/src/web/traceViewer/sw.ts +++ b/packages/playwright-core/src/web/traceViewer/sw.ts @@ -32,13 +32,16 @@ const scopePath = new URL(self.registration.scope).pathname; const loadedTraces = new Map(); -async function loadTrace(trace: string, clientId: string): Promise { +async function loadTrace(trace: string, clientId: string, progress: (done: number, total: number) => void): Promise { const entry = loadedTraces.get(trace); if (entry) return entry.traceModel; const traceModel = new TraceModel(); - const url = trace.startsWith('http') || trace.startsWith('blob') ? trace : `/file?path=${trace}`; - await traceModel.load(url); + let url = trace.startsWith('http') || trace.startsWith('blob') ? trace : `/file?path=${trace}`; + // Dropbox does not support cors. + if (url.startsWith('https://www.dropbox.com/')) + url = 'https://dl.dropboxusercontent.com/' + url.substring('https://www.dropbox.com/'.length); + await traceModel.load(url, progress); const snapshotServer = new SnapshotServer(traceModel.storage()); loadedTraces.set(trace, { traceModel, snapshotServer, clientId }); return traceModel; @@ -47,8 +50,8 @@ async function loadTrace(trace: string, clientId: string): Promise { // @ts-ignore async function doFetch(event: FetchEvent): Promise { const request = event.request; - const snapshotUrl = request.mode === 'navigate' ? - request.url : (await self.clients.get(event.clientId))!.url; + const client = await self.clients.get(event.clientId); + const snapshotUrl = request.mode === 'navigate' ? request.url : client!.url; const traceUrl = new URL(snapshotUrl).searchParams.get('trace')!; const { snapshotServer } = loadedTraces.get(traceUrl) || {}; @@ -62,7 +65,9 @@ async function doFetch(event: FetchEvent): Promise { } if (relativePath === '/context') { - const traceModel = await loadTrace(traceUrl, event.clientId); + const traceModel = await loadTrace(traceUrl, event.clientId, (done: number, total: number) => { + client.postMessage({ method: 'progress', params: { done, total } }); + }); return new Response(JSON.stringify(traceModel!.contextEntry), { status: 200, headers: { 'Content-Type': 'application/json' } diff --git a/packages/playwright-core/src/web/traceViewer/traceModel.ts b/packages/playwright-core/src/web/traceViewer/traceModel.ts index 3f5878aa3f..14ab4c759f 100644 --- a/packages/playwright-core/src/web/traceViewer/traceModel.ts +++ b/packages/playwright-core/src/web/traceViewer/traceModel.ts @@ -37,13 +37,13 @@ export class TraceModel { this.contextEntry = createEmptyContext(); } - async load(traceURL: string) { - 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; + async load(traceURL: string, progress: (done: number, total: number) => void) { + const zipReader = new zipjs.ZipReader( + new zipjs.HttpReader(traceURL, { mode: 'cors' }), + { useWebWorkers: false }) as zip.ZipReader; let traceEntry: zip.Entry | undefined; let networkEntry: zip.Entry | undefined; - for (const entry of await zipReader.getEntries()) { + for (const entry of await zipReader.getEntries({ onprogress: progress })) { if (entry.filename.endsWith('.trace')) traceEntry = entry; if (entry.filename.endsWith('.network')) diff --git a/packages/playwright-core/src/web/traceViewer/ui/workbench.css b/packages/playwright-core/src/web/traceViewer/ui/workbench.css index 2c982bb024..d5bf2713fa 100644 --- a/packages/playwright-core/src/web/traceViewer/ui/workbench.css +++ b/packages/playwright-core/src/web/traceViewer/ui/workbench.css @@ -25,6 +25,19 @@ font-weight: bold; } +.progress { + position: relative; + margin: auto; + width: 400px; + height: 10px; + border: 1px solid #aaa; +} + +.inner-progress { + background-color: #aaa; + height: 100%; +} + .workbench { contain: size; user-select: none; diff --git a/packages/playwright-core/src/web/traceViewer/ui/workbench.tsx b/packages/playwright-core/src/web/traceViewer/ui/workbench.tsx index 701b0cb7c7..97bc55862d 100644 --- a/packages/playwright-core/src/web/traceViewer/ui/workbench.tsx +++ b/packages/playwright-core/src/web/traceViewer/ui/workbench.tsx @@ -36,6 +36,7 @@ export const Workbench: React.FunctionComponent<{ const [selectedAction, setSelectedAction] = React.useState(); const [highlightedAction, setHighlightedAction] = React.useState(); const [selectedTab, setSelectedTab] = React.useState('logs'); + const [progress, setProgress] = React.useState<{ done: number, total: number }>({ done: 0, total: 0 }); const handleDropEvent = (event: any) => { event.preventDefault(); @@ -52,7 +53,15 @@ export const Workbench: React.FunctionComponent<{ React.useEffect(() => { (async () => { if (traceURL) { + const swListener = (event: any) => { + if (event.data.method === 'progress') + setProgress(event.data.params); + }; + navigator.serviceWorker.addEventListener('message', swListener); + setProgress({ done: 0, total: 1 }); const contextEntry = (await fetch(`context?trace=${traceURL}`).then(response => response.json())) as ContextEntry; + navigator.serviceWorker.removeEventListener('message', swListener); + setProgress({ done: 0, total: 0 }); modelUtil.indexModel(contextEntry); setContextEntry(contextEntry); } else { @@ -64,18 +73,21 @@ export const Workbench: React.FunctionComponent<{ const defaultSnapshotSize = contextEntry.options.viewport || { width: 1280, height: 720 }; const boundaries = { minimum: contextEntry.startTime, maximum: contextEntry.endTime }; - if (!traceURL) { + if (!traceURL || progress.total) { return
🎭
Playwright
-
+
+
} + {!progress.total &&
{ event.preventDefault(); }} onDrop={event => handleDropEvent(event)}> - Drop Playwright Trace here -
+ Drop Playwright Trace here +
} ; }