diff --git a/packages/playwright-core/src/server/trace/viewer/traceViewer.ts b/packages/playwright-core/src/server/trace/viewer/traceViewer.ts index b8dc3e5314..cc404c18ba 100644 --- a/packages/playwright-core/src/server/trace/viewer/traceViewer.ts +++ b/packages/playwright-core/src/server/trace/viewer/traceViewer.ts @@ -106,8 +106,17 @@ export async function installRootRedirect(server: HttpServer, traceUrls: string[ const params = new URLSearchParams(); if (path.sep !== path.posix.sep) params.set('pathSeparator', path.sep); - for (const traceUrl of traceUrls) - params.append('trace', traceUrl); + for (const traceUrl of traceUrls) { + if (traceUrl.startsWith('http://') || traceUrl.startsWith('https://')) { + params.append('trace', traceUrl); + continue; + } + + // /trace/file?path=/path/to/trace.zip + const url = new URL('/trace/file', server.urlPrefix('precise')); + url.searchParams.set('path', traceUrl); + params.append('trace', url.toString()); + } if (server.wsGuid()) params.append('ws', server.wsGuid()!); if (options?.isServer) diff --git a/packages/trace-viewer/src/sw/traceModelBackends.ts b/packages/trace-viewer/src/sw/traceModelBackends.ts index 19c5fc2dee..1318b298a7 100644 --- a/packages/trace-viewer/src/sw/traceModelBackends.ts +++ b/packages/trace-viewer/src/sw/traceModelBackends.ts @@ -30,9 +30,8 @@ export class ZipTraceModelBackend implements TraceModelBackend { constructor(traceURL: string, progress: Progress) { this._traceURL = traceURL; - zipjs.configure({ baseURL: self.location.href } as any); this._zipReader = new zipjs.ZipReader( - new zipjs.HttpReader(formatUrl(traceURL), { mode: 'cors', preventHeadRequest: true } as any), + new zipjs.HttpReader(traceURL, { mode: 'cors', preventHeadRequest: true } as any), { useWebWorkers: false }); this._entriesPromise = this._zipReader.getEntries({ onprogress: progress }).then(entries => { const map = new Map(); @@ -87,8 +86,8 @@ export class FetchTraceModelBackend implements TraceModelBackend { constructor(traceURL: string) { this._traceURL = traceURL; - this._entriesPromise = fetch('/trace/file?path=' + encodeURIComponent(traceURL)).then(async response => { - const json = JSON.parse(await response.text()); + this._entriesPromise = fetch(traceURL).then(async response => { + const json = await response.json(); const entries = new Map(); for (const entry of json.entries) entries.set(entry.name, entry.path); @@ -126,17 +125,12 @@ export class FetchTraceModelBackend implements TraceModelBackend { private async _readEntry(entryName: string): Promise { const entries = await this._entriesPromise; - const fileName = entries.get(entryName); - if (!fileName) + const filePath = entries.get(entryName); + if (!filePath) return; - return fetch('/trace/file?path=' + encodeURIComponent(fileName)); + + const url = new URL(this.traceURL()); + url.searchParams.set('path', filePath); + return fetch(url); } } - -function formatUrl(trace: string) { - let url = trace.startsWith('http') || trace.startsWith('blob') ? trace : `file?path=${encodeURIComponent(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); - return url; -} diff --git a/packages/trace-viewer/src/ui/embeddedWorkbenchLoader.tsx b/packages/trace-viewer/src/ui/embeddedWorkbenchLoader.tsx index c8b8aa216c..23801f95da 100644 --- a/packages/trace-viewer/src/ui/embeddedWorkbenchLoader.tsx +++ b/packages/trace-viewer/src/ui/embeddedWorkbenchLoader.tsx @@ -21,6 +21,7 @@ import './embeddedWorkbenchLoader.css'; import { Workbench } from './workbench'; import { currentTheme, toggleTheme } from '@web/theme'; import type { SourceLocation } from './modelUtil'; +import { filePathToTraceURL } from './uiModeTraceView'; function openPage(url: string, target?: string) { if (url) @@ -40,7 +41,15 @@ export const EmbeddedWorkbenchLoader: React.FunctionComponent = () => { React.useEffect(() => { window.addEventListener('message', async ({ data: { method, params } }) => { if (method === 'loadTraceRequested') { - setTraceURLs(params.traceUrl ? [params.traceUrl] : []); + if (params.traceUrl) { + // the param is called URL, but VS Code sends a path + const url = params.traceUrl.startsWith('http') + ? params.traceUrl + : filePathToTraceURL(params.traceUrl).toString(); + setTraceURLs([url]); + } else { + setTraceURLs([]); + } setProcessingErrorMessage(null); } else if (method === 'applyTheme') { if (currentTheme() !== params.theme) diff --git a/packages/trace-viewer/src/ui/uiModeTraceView.tsx b/packages/trace-viewer/src/ui/uiModeTraceView.tsx index cf35d89007..8e98a8ccd7 100644 --- a/packages/trace-viewer/src/ui/uiModeTraceView.tsx +++ b/packages/trace-viewer/src/ui/uiModeTraceView.tsx @@ -54,7 +54,7 @@ export const TraceView: React.FC<{ // Test finished. const attachment = result && result.duration >= 0 && result.attachments.find(a => a.name === 'trace'); if (attachment && attachment.path) { - loadSingleTraceFile(attachment.path).then(model => setModel({ model, isLive: false })); + loadSingleTraceFile(filePathToTraceURL(attachment.path)).then(model => setModel({ model, isLive: false })); return; } @@ -72,7 +72,7 @@ export const TraceView: React.FC<{ // Start polling running test. pollTimer.current = setTimeout(async () => { try { - const model = await loadSingleTraceFile(traceLocation); + const model = await loadSingleTraceFile(filePathToTraceURL(traceLocation)); setModel({ model, isLive: true }); } catch { setModel(undefined); @@ -108,11 +108,25 @@ const outputDirForTestCase = (testCase: reporterTypes.TestCase): string | undefi return undefined; }; -async function loadSingleTraceFile(url: string): Promise { +async function loadSingleTraceFile(traceURL: URL): Promise { const params = new URLSearchParams(); - params.set('trace', url); + params.set('trace', formatUrl(traceURL).toString()); params.set('limit', '1'); const response = await fetch(`contexts?${params.toString()}`); const contextEntries = await response.json() as ContextEntry[]; return new MultiTraceModel(contextEntries); } + +function formatUrl(traceURL: URL) { + // Dropbox does not support cors. + if (traceURL.hostname === 'dropbox.com') + traceURL.hostname = 'dl.dropboxusercontent.com'; + + return traceURL; +} + +export function filePathToTraceURL(path: string) { + const url = new URL('file', location.href); + url.searchParams.set('path', path); + return url; +}