diff --git a/packages/trace-viewer/src/sw.ts b/packages/trace-viewer/src/sw.ts index 980f0fee9d..e3d69fdf12 100644 --- a/packages/trace-viewer/src/sw.ts +++ b/packages/trace-viewer/src/sw.ts @@ -35,15 +35,27 @@ const loadedTraces = new Map(); -async function loadTrace(trace: string, clientId: string, progress: (done: number, total: number) => void): Promise { - const entry = loadedTraces.get(trace); - clientIdToTraceUrls.set(clientId, trace); +async function loadTrace(traceUrl: string, traceFileName: string | null, clientId: string, progress: (done: number, total: number) => void): Promise { + const entry = loadedTraces.get(traceUrl); + clientIdToTraceUrls.set(clientId, traceUrl); if (entry) return entry.traceModel; const traceModel = new TraceModel(); - await traceModel.load(trace, progress); + try { + await traceModel.load(traceUrl, progress); + } catch (error: any) { + // eslint-disable-next-line no-console + console.error(error); + + if (error?.message?.includes('Cannot find .trace file') && await traceModel.hasEntry('index.html')) + throw new Error('Could not load trace. Did you upload a Playwright HTML report instead? Make sure to extract the archive first and then double-click the index.html file or put it on a web server.'); + else if (traceFileName) + throw new Error(`Could not load trace from ${traceFileName}. Make sure to upload a valid Playwright trace.`); + else + throw new Error(`Could not load trace from ${traceUrl}. Make sure a valid Playwright Trace is accessible over this url.`); + } const snapshotServer = new SnapshotServer(traceModel.storage()); - loadedTraces.set(trace, { traceModel, snapshotServer }); + loadedTraces.set(traceUrl, { traceModel, snapshotServer }); return traceModel; } @@ -65,21 +77,15 @@ async function doFetch(event: FetchEvent): Promise { if (relativePath === '/context') { try { - const traceModel = await loadTrace(traceUrl, event.clientId, (done: number, total: number) => { + const traceModel = await loadTrace(traceUrl, url.searchParams.get('traceFileName'), 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' } }); - } catch (error: unknown) { - // eslint-disable-next-line no-console - console.error(error); - const traceFileName = url.searchParams.get('traceFileName')!; - return new Response(JSON.stringify({ - error: traceFileName ? `Could not load trace from ${traceFileName}. Make sure to upload a valid Playwright trace.` : - `Could not load trace from ${traceUrl}. Make sure a valid Playwright Trace is accessible over this url.`, - }), { + } catch (error: any) { + return new Response(JSON.stringify({ error: error?.message }), { status: 500, headers: { 'Content-Type': 'application/json' } }); diff --git a/packages/trace-viewer/src/traceModel.ts b/packages/trace-viewer/src/traceModel.ts index 24b313e813..a9baa29baf 100644 --- a/packages/trace-viewer/src/traceModel.ts +++ b/packages/trace-viewer/src/traceModel.ts @@ -31,6 +31,7 @@ export class TraceModel { private _snapshotStorage: PersistentSnapshotStorage | undefined; private _entries = new Map(); private _version: number | undefined; + private _zipReader: zip.ZipReader | undefined; constructor() { this.contextEntry = createEmptyContext(); @@ -46,12 +47,12 @@ export class TraceModel { async load(traceURL: string, progress: (done: number, total: number) => void) { this.contextEntry.traceUrl = traceURL; - const zipReader = new zipjs.ZipReader( // @ts-ignore + this._zipReader = new zipjs.ZipReader( // @ts-ignore new zipjs.HttpReader(this._formatUrl(traceURL), { mode: 'cors', preventHeadRequest: true }), { useWebWorkers: false }) as zip.ZipReader; let traceEntry: zip.Entry | undefined; let networkEntry: zip.Entry | undefined; - for (const entry of await zipReader.getEntries({ onprogress: progress })) { + for (const entry of await this._zipReader.getEntries({ onprogress: progress })) { if (entry.filename.endsWith('.trace')) traceEntry = entry; if (entry.filename.endsWith('.network')) @@ -60,10 +61,13 @@ export class TraceModel { this.contextEntry.hasSource = true; this._entries.set(entry.filename, entry); } + if (!traceEntry) + throw new Error('Cannot find .trace file'); + this._snapshotStorage = new PersistentSnapshotStorage(this._entries); const traceWriter = new zipjs.TextWriter() as zip.TextWriter; - await traceEntry!.getData!(traceWriter); + await traceEntry.getData!(traceWriter); for (const line of (await traceWriter.getData()).split('\n')) this.appendEvent(line); @@ -76,6 +80,16 @@ export class TraceModel { this._build(); } + async hasEntry(filename: string): Promise { + if (!this._zipReader) + return false; + for (const entry of await this._zipReader.getEntries()) { + if (entry.filename === filename) + return true; + } + return false; + } + async resourceForSha1(sha1: string): Promise { const entry = this._entries.get('resources/' + sha1); if (!entry) diff --git a/utils/build/build.js b/utils/build/build.js index 3f11ceee84..970fed9c70 100644 --- a/utils/build/build.js +++ b/utils/build/build.js @@ -286,7 +286,7 @@ for (const webPackage of ['html-reporter', 'recorder', 'trace-viewer']) { `packages/web/src/`, ], command: 'npx', - args: ['vite', 'build'], + args: ['vite', 'build', ...(watchMode ? ['--sourcemap'] : [])], cwd: path.join(__dirname, '..', '..', 'packages', webPackage), }); } @@ -335,7 +335,7 @@ if (lintMode) { command: 'npx', args: ['tsc', ...(watchMode ? ['-w'] : []), '-p', quotePath(filePath(`packages/${webPackage}`))], shell: true, - }); + }); } }