diff --git a/packages/playwright-core/src/client/tracing.ts b/packages/playwright-core/src/client/tracing.ts index 2481741e3e..63e9e5dea3 100644 --- a/packages/playwright-core/src/client/tracing.ts +++ b/packages/playwright-core/src/client/tracing.ts @@ -96,7 +96,7 @@ export class Tracing extends ChannelOwner implements ap if (isLocal) { const result = await this._channel.tracingStopChunk({ mode: 'entries' }); - await this._connection.localUtils()._channel.zip({ zipFile: filePath, entries: result.entries!, mode: 'write', stacksId: this._stacksId, includeSources: this._includeSources }); + await this._connection.localUtils()._channel.zip({ zipFile: filePath, entries: result.entries!, mode: 'write', stacksId: this._stacksId, tracingId: this._guid, includeSources: this._includeSources }); return; } @@ -114,7 +114,7 @@ export class Tracing extends ChannelOwner implements ap await artifact.saveAs(filePath); await artifact.delete(); - await this._connection.localUtils()._channel.zip({ zipFile: filePath, entries: [], mode: 'append', stacksId: this._stacksId, includeSources: this._includeSources }); + await this._connection.localUtils()._channel.zip({ zipFile: filePath, entries: [], mode: 'append', stacksId: this._stacksId, tracingId: this._guid, includeSources: this._includeSources }); } _resetStackCounter() { diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index cdcef14996..e52fb5d8ee 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -250,6 +250,7 @@ scheme.LocalUtilsZipParams = tObject({ zipFile: tString, entries: tArray(tType('NameValue')), stacksId: tOptional(tString), + tracingId: tString, mode: tEnum(['write', 'append']), includeSources: tBoolean, }); diff --git a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts index b6f8fe80ac..d37d3fc05a 100644 --- a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts @@ -75,18 +75,27 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels. } }; - for (const entry of params.entries) - addFile(entry.value, entry.name); + for (const entry of params.entries) { + let name = entry.name; + // Prefix network trace with the tracing(context) guid, it can be used later to deduplicate + // network traces from the same context saved for beforeAll/test/afterAll hooks. Trace + // viewer relies on .trace.trace, .trace.network and .trace.stacks files to have the same + // prefix, so we keep them consistent. + if (name === 'trace.network' || name === 'trace.trace') + name = params.tracingId + '.' + name; + addFile(entry.value, name); + } // Add stacks and the sources. const stackSession = params.stacksId ? this._stackSessions.get(params.stacksId) : undefined; if (stackSession?.callStacks.length) { await stackSession.writer; + const stacksFileName = params.tracingId + '.trace.stacks'; if (process.env.PW_LIVE_TRACE_STACKS) { - zipFile.addFile(stackSession.file, 'trace.stacks'); + zipFile.addFile(stackSession.file, stacksFileName); } else { const buffer = Buffer.from(JSON.stringify(serializeClientSideCallMetadata(stackSession.callStacks))); - zipFile.addBuffer(buffer, 'trace.stacks'); + zipFile.addBuffer(buffer, stacksFileName); } } diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts index 86a1fcfcea..8bdd407ce0 100644 --- a/packages/playwright-core/src/server/trace/recorder/tracing.ts +++ b/packages/playwright-core/src/server/trace/recorder/tracing.ts @@ -352,10 +352,8 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps const newNetworkFile = path.join(this._state.tracesDir, this._state.traceName + `-pwnetcopy-${this._state.chunkOrdinal}.network`); const entries: NameValue[] = []; - entries.push({ name: 'trace.trace', value: this._state.traceFile }); - // Prefix network trace with the context guid, it can be used later to deduplicate - // network traces from the same context saved for beforeAll/test/afterAll hooks. - entries.push({ name: `${this._context.guid}.trace.network`, value: newNetworkFile }); + entries.push({ name: `trace.trace`, value: this._state.traceFile }); + entries.push({ name: `trace.network`, value: newNetworkFile }); for (const sha1 of new Set([...this._state.traceSha1s, ...this._state.networkSha1s])) entries.push({ name: path.join('resources', sha1), value: path.join(this._state.resourcesDir, sha1) }); diff --git a/packages/playwright/src/worker/testTracing.ts b/packages/playwright/src/worker/testTracing.ts index c0440b5424..3d63c01951 100644 --- a/packages/playwright/src/worker/testTracing.ts +++ b/packages/playwright/src/worker/testTracing.ts @@ -342,7 +342,7 @@ async function mergeTraceFiles(fileName: string, temporaryTraceFiles: string[]) // it will contain all previous events. entryName = networkTraceEntries.get(entry.fileName); if (!entryName) { - entryName = i + '-trace.network'; + entryName = i + '-' + entry.fileName; networkTraceEntries.set(entry.fileName, entryName); } } else if (entry.fileName.match(/[\d-]*trace\./)) { diff --git a/packages/protocol/src/channels.d.ts b/packages/protocol/src/channels.d.ts index 30f8c03088..1114598093 100644 --- a/packages/protocol/src/channels.d.ts +++ b/packages/protocol/src/channels.d.ts @@ -443,6 +443,7 @@ export type LocalUtilsZipParams = { zipFile: string, entries: NameValue[], stacksId?: string, + tracingId: string, mode: 'write' | 'append', includeSources: boolean, }; diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index 77501efa9b..f3b949ef3e 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -569,6 +569,7 @@ LocalUtils: type: array items: NameValue stacksId: string? + tracingId: string mode: type: enum literals: diff --git a/packages/trace-viewer/src/sw/traceModel.ts b/packages/trace-viewer/src/sw/traceModel.ts index 602ff4e075..8230824f5d 100644 --- a/packages/trace-viewer/src/sw/traceModel.ts +++ b/packages/trace-viewer/src/sw/traceModel.ts @@ -43,7 +43,7 @@ export class TraceModel { const ordinals: string[] = []; let hasSource = false; for (const entryName of await this._backend.entryNames()) { - const match = entryName.match(/(.+)\.trace/); + const match = entryName.match(/(.+)\.trace$/); if (match) ordinals.push(match[1] || ''); if (entryName.includes('src@')) diff --git a/tests/library/tracing.spec.ts b/tests/library/tracing.spec.ts index dfd4fc168d..ef3afe809d 100644 --- a/tests/library/tracing.spec.ts +++ b/tests/library/tracing.spec.ts @@ -240,9 +240,9 @@ test('should respect tracesDir and name', async ({ browserType, server, mode }, expect(resourceNames(resources)).toEqual([ 'resources/XXX.css', 'resources/XXX.html', - 'trace.network', - 'trace.stacks', - 'trace.trace', + expect.stringMatching(/trace.network$/), + expect.stringMatching(/trace.stacks$/), + expect.stringMatching(/trace.trace$/), ]); } @@ -253,9 +253,9 @@ test('should respect tracesDir and name', async ({ browserType, server, mode }, 'resources/XXX.css', 'resources/XXX.html', 'resources/XXX.html', - 'trace.network', - 'trace.stacks', - 'trace.trace', + expect.stringMatching(/trace.network$/), + expect.stringMatching(/trace.stacks$/), + expect.stringMatching(/trace.trace$/), ]); } });