From fb3e8ed114f19300624fc9ecbd94a4fa8d950f05 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Wed, 5 Feb 2025 00:00:04 -0800 Subject: [PATCH] fix: reset APIRequestContext network trace between chunks (#34616) --- .../src/server/trace/recorder/tracing.ts | 14 +++++--- .../ui-mode-test-network-tab.spec.ts | 36 +++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts index c19c0a33d9..fa57a14df1 100644 --- a/packages/playwright-core/src/server/trace/recorder/tracing.ts +++ b/packages/playwright-core/src/server/trace/recorder/tracing.ts @@ -170,10 +170,16 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps this._state.recording = true; this._state.callIds.clear(); + // - Browser context network trace is shared across chunks as it contains resources + // used to serve page snapshots, so make a copy with the new name. + // - APIRequestContext network traces are chunk-specific, always start from scratch. + const preserveNetworkResources = this._context instanceof BrowserContext; if (options.name && options.name !== this._state.traceName) - this._changeTraceName(this._state, options.name); + this._changeTraceName(this._state, options.name, preserveNetworkResources); else this._allocateNewTraceFile(this._state); + if (!preserveNetworkResources) + this._fs.writeFile(this._state.networkFile, ''); this._fs.mkdir(path.dirname(this._state.traceFile)); const event: trace.TraceEvent = { @@ -267,14 +273,14 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps state.traceFile = path.join(state.tracesDir, `${state.traceName}${suffix}.trace`); } - private _changeTraceName(state: RecordingState, name: string) { + private _changeTraceName(state: RecordingState, name: string, preserveNetworkResources: boolean) { state.traceName = name; state.chunkOrdinal = 0; // Reset ordinal for the new name. this._allocateNewTraceFile(state); - // Network file survives across chunks, so make a copy with the new name. const newNetworkFile = path.join(state.tracesDir, name + '.network'); - this._fs.copyFile(state.networkFile, newNetworkFile); + if (preserveNetworkResources) + this._fs.copyFile(state.networkFile, newNetworkFile); state.networkFile = newNetworkFile; } diff --git a/tests/playwright-test/ui-mode-test-network-tab.spec.ts b/tests/playwright-test/ui-mode-test-network-tab.spec.ts index e1b3bef9ed..6905dec505 100644 --- a/tests/playwright-test/ui-mode-test-network-tab.spec.ts +++ b/tests/playwright-test/ui-mode-test-network-tab.spec.ts @@ -160,3 +160,39 @@ test('should display list of query parameters (only if present)', async ({ runUI await expect(page.getByText('Query String Parameters')).not.toBeVisible(); }); + +test('should not duplicate network entries from beforeAll', { + annotation: [ + { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/34404' }, + { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/33106' }, + ] +}, async ({ runUITest, server }) => { + const { page } = await runUITest({ + 'playwright.config.ts': ` + module.exports = { use: { trace: 'on' } }; + `, + 'a.spec.ts': ` + import { test as base, expect, request, type APIRequestContext } from '@playwright/test'; + + const test = base.extend<{}, { apiRequest: APIRequestContext }>({ + apiRequest: [async ({ }, use) => { + const apiContext = await request.newContext(); + await use(apiContext); + await apiContext.dispose(); + }, { scope: 'worker' }] + }); + + test.beforeAll(async ({ apiRequest }) => { + await apiRequest.get("${server.EMPTY_PAGE}"); + }); + + test('first test', async ({ }) => { }); + + test.afterAll(async ({ apiRequest }) => { }); + `, + }); + + await page.getByText('first test').dblclick(); + await page.getByText('Network', { exact: true }).click(); + await expect(page.getByTestId('network-list').getByText('empty.html')).toHaveCount(1); +});