From db646e5e9299e445c0b6c381becc0b410cb8192c Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Wed, 26 Aug 2026 13:06:13 -0700 Subject: [PATCH 1/6] fix(tracing): allow restarting tracing after a failed stop When tracing.stop() failed to save the chunk (e.g. a trace file write errored), tracingStop was never sent, so the server kept its recording state and every later tracing.start() on that context threw "Tracing has been already started" for the rest of the context lifetime. Fixes: https://github.com/microsoft/playwright/issues/42423 --- .../playwright-core/src/client/tracing.ts | 8 ++++++-- tests/library/tracing.spec.ts | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/playwright-core/src/client/tracing.ts b/packages/playwright-core/src/client/tracing.ts index b0257c5258191..7dff0bc6514cc 100644 --- a/packages/playwright-core/src/client/tracing.ts +++ b/packages/playwright-core/src/client/tracing.ts @@ -95,8 +95,12 @@ export class Tracing extends ChannelOwner implements ap async stop(options: { path?: string } = {}) { await this._wrapApiCall(async () => { - await this._doStopChunk(options.path); - await this._channel.tracingStop({}, kNoTimeout); + // Stop tracing even when saving the trace failed, otherwise tracing can + // never be started again on this context. + let error: Error | undefined = await this._doStopChunk(options.path).catch(e => e); + await this._channel.tracingStop({}, kNoTimeout).catch(e => error ??= e); + if (error) + throw error; }); } diff --git a/tests/library/tracing.spec.ts b/tests/library/tracing.spec.ts index 53ea505d735aa..a674f5d3222ac 100644 --- a/tests/library/tracing.spec.ts +++ b/tests/library/tracing.spec.ts @@ -421,6 +421,25 @@ test('should record network failures', async ({ context, page, server }, testInf expect(requestEvent.snapshot.time).toBeGreaterThanOrEqual(0); }); +test('should recover tracing after a failed stop', async ({ context, page, server }, testInfo) => { + test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/42423' }); + await context.tracing.start(); + // Saving fails: a parent of the destination is a file, not a directory. + const blocker = testInfo.outputPath('blocker'); + await fs.promises.writeFile(blocker, ''); + await expect(context.tracing.stop({ path: path.join(blocker, 'trace1.zip') })).rejects.toThrow(/ENOTDIR|ENOENT|EEXIST/); + + // The failed stop must not wedge tracing for the rest of the context lifetime. + await context.tracing.start(); + await page.goto(server.PREFIX + '/input/button.html'); + await page.click('button'); + await context.tracing.stop({ path: testInfo.outputPath('trace2.zip') }); + + const { events, actions } = await parseTraceRaw(testInfo.outputPath('trace2.zip')); + expect(events[0].type).toBe('context-options'); + expect(actions).toContain('Click'); +}); + test('should not crash when browser closes mid-trace', async ({ browserType, server }, testInfo) => { const browser = await browserType.launch(); const page = await browser.newPage(); From 2741e1926fe2492eaf49eda4a29a9997b4a9fe81 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Thu, 27 Aug 2026 10:41:36 -0700 Subject: [PATCH 2/6] fix(tracing): reset all tracing state when stopping fails Addresses review feedback: stop() must leave tracing in a clean state even when it throws, so that a subsequent start() works. - server: stop() discards a chunk the client failed to stop, instead of rejecting with "Must stop trace file before stopping tracing". - server: stopChunk() always releases the recording state. - client: release the stack session when saving the trace fails, otherwise later traces keep appending calls to the abandoned session. - client: delete the artifact when saving it fails. --- .../playwright-core/src/client/tracing.ts | 33 +++++++++++++---- .../src/server/trace/recorder/tracing.ts | 35 +++++++++--------- tests/library/tracing.spec.ts | 37 +++++++++++++++++++ 3 files changed, 80 insertions(+), 25 deletions(-) diff --git a/packages/playwright-core/src/client/tracing.ts b/packages/playwright-core/src/client/tracing.ts index 7dff0bc6514cc..f65b6e5532c21 100644 --- a/packages/playwright-core/src/client/tracing.ts +++ b/packages/playwright-core/src/client/tracing.ts @@ -196,12 +196,26 @@ export class Tracing extends ChannelOwner implements ap const additionalSources = [...this._additionalSources]; this._additionalSources.clear(); + const stacksId = this._stacksId; + this._stacksId = undefined; + try { + await this._saveChunk(filePath, stacksId, additionalSources); + } catch (error) { + // The stack session is owned by the chunk being stopped. Release it even when + // saving failed, otherwise later traces keep appending calls to it. + if (stacksId) + await this._connection.localUtils()?.traceDiscarded({ stacksId }).catch(() => {}); + throw error; + } + } + + private async _saveChunk(filePath: string | undefined, stacksId: string | undefined, additionalSources: string[]) { if (!filePath) { // Not interested in artifacts. await this._channel.tracingStopChunk({ mode: 'discard' }, kNoTimeout); - if (this._stacksId) - await this._connection.localUtils()!.traceDiscarded({ stacksId: this._stacksId }); + if (stacksId) + await this._connection.localUtils()!.traceDiscarded({ stacksId }); return; } @@ -213,7 +227,7 @@ export class Tracing extends ChannelOwner implements ap if (isLocal) { const result = await this._channel.tracingStopChunk({ mode: 'entries' }, kNoTimeout); - await localUtils.zip({ zipFile: filePath, entries: result.entries!, mode: 'write', stacksId: this._stacksId, includeSources: this._includeSources, additionalSources }); + await localUtils.zip({ zipFile: filePath, entries: result.entries!, mode: 'write', stacksId, includeSources: this._includeSources, additionalSources }); return; } @@ -221,17 +235,20 @@ export class Tracing extends ChannelOwner implements ap // The artifact may be missing if the browser closed while stopping tracing. if (!result.artifact) { - if (this._stacksId) - await localUtils.traceDiscarded({ stacksId: this._stacksId }); + if (stacksId) + await localUtils.traceDiscarded({ stacksId }); return; } // Save trace to the final local file. const artifact = Artifact.from(result.artifact); - await artifact.saveAs(filePath); - await artifact.delete(); + try { + await artifact.saveAs(filePath); + } finally { + await artifact.delete(); + } - await localUtils.zip({ zipFile: filePath, entries: [], mode: 'append', stacksId: this._stacksId, includeSources: this._includeSources, additionalSources }); + await localUtils.zip({ zipFile: filePath, entries: [], mode: 'append', stacksId, includeSources: this._includeSources, additionalSources }); } _resetStackCounter() { diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts index b1dbca080ecb6..3a7cd9cb129fb 100644 --- a/packages/playwright-core/src/server/trace/recorder/tracing.ts +++ b/packages/playwright-core/src/server/trace/recorder/tracing.ts @@ -327,6 +327,11 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps } async stop(progress: Progress) { + // The client stops the chunk before stopping tracing, but that may fail, e.g. when + // saving the trace hits a disk error. Discard the chunk so that tracing is always + // stopped and can be started again. + if (this._state?.recording) + await this.stopChunk(progress, { mode: 'discard' }).catch(() => {}); await progress.race(this._stop()); } @@ -392,9 +397,19 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps if (this._isStopping) throw new Error(`Tracing is already stopping`); this._isStopping = true; + try { + return await this._stopChunk(progress, params); + } finally { + // Always release the recording state, so that tracing can be stopped and started + // again even when saving the chunk failed. + this._isStopping = false; + if (this._state) + this._state.recording = false; + } + } + private async _stopChunk(progress: Progress, params: TracingTracingStopChunkParams): Promise<{ artifact?: Artifact, entries?: NameValue[] }> { if (!this._state || !this._state.recording) { - this._isStopping = false; if (params.mode !== 'discard') throw new Error(`Must start tracing before stopping`); return {}; @@ -430,11 +445,8 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps this._state.chunkFiles = new Set(); - if (params.mode === 'discard') { - this._isStopping = false; - this._state.recording = false; + if (params.mode === 'discard') return {}; - } this._fs.copyFile(this._state.networkFile, newNetworkFile); @@ -443,20 +455,9 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps this._fs.zip(entries, zipFileName); // Make sure all file operations complete. - let error: Error | undefined; try { await progress.race(this._fs.sync()); - } catch (e) { - error = e as Error; - } - - this._isStopping = false; - if (this._state) - this._state.recording = false; - - // IMPORTANT: no awaits after this point, to make sure recording state is correct. - - if (error) { + } catch (error) { // This check is here because closing the browser removes the tracesDir and tracing // cannot access removed files. Clients are ready for the missing artifact. if (!isAbortError(error) && this._context.attribution.browser && !this._context.attribution.browser.isConnected()) diff --git a/tests/library/tracing.spec.ts b/tests/library/tracing.spec.ts index a674f5d3222ac..021831556e10c 100644 --- a/tests/library/tracing.spec.ts +++ b/tests/library/tracing.spec.ts @@ -440,6 +440,43 @@ test('should recover tracing after a failed stop', async ({ context, page, serve expect(actions).toContain('Click'); }); +test('should stop tracing when the chunk was not stopped', async ({ context, page, server }, testInfo) => { + test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/42423' }); + await context.tracing.start(); + await page.goto(server.PREFIX + '/input/button.html'); + // Saving the chunk can fail before it was stopped, leaving the server recording. + await (context.tracing as any)._channel.tracingStop({}); + + await context.tracing.start(); + await page.click('button'); + await context.tracing.stop({ path: testInfo.outputPath('trace.zip') }); + + const { events, actions } = await parseTraceRaw(testInfo.outputPath('trace.zip')); + expect(events[0].type).toBe('context-options'); + expect(actions).toContain('Click'); +}); + +test('should release the stack session when saving the trace fails', async ({ browserType, server }, testInfo) => { + test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/42423' }); + // Without tracesDir the stack session owns a temporary directory of its own. + const browser = await browserType.launch({ tracesDir: undefined }); + try { + const page = await browser.newPage(); + const context = page.context(); + await context.tracing.start(); + await page.goto(server.PREFIX + '/input/button.html'); + const stacksDir = path.dirname((context.tracing as any)._stacksId); + expect(fs.existsSync(stacksDir)).toBe(true); + + const blocker = testInfo.outputPath('blocker'); + await fs.promises.writeFile(blocker, ''); + await expect(context.tracing.stop({ path: path.join(blocker, 'trace.zip') })).rejects.toThrow(/ENOTDIR|ENOENT|EEXIST/); + expect(fs.existsSync(stacksDir)).toBe(false); + } finally { + await browser.close(); + } +}); + test('should not crash when browser closes mid-trace', async ({ browserType, server }, testInfo) => { const browser = await browserType.launch(); const page = await browser.newPage(); From b6e98eba250a4f10b543757eedf3a7a6a62ad725 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Thu, 27 Aug 2026 13:43:56 -0700 Subject: [PATCH 3/6] chore(tracing): consolidate chunk discard into server stop() - server stop() unconditionally discards the chunk (a no-op when not recording), resetForReuse() delegates to it. - client: deleting the artifact no longer masks the saveAs error. - test: stack session test does not need a page. --- packages/playwright-core/src/client/tracing.ts | 9 +++++++-- .../playwright-core/src/server/trace/recorder/tracing.ts | 6 ++---- tests/library/tracing.spec.ts | 9 ++++----- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/playwright-core/src/client/tracing.ts b/packages/playwright-core/src/client/tracing.ts index f65b6e5532c21..9c03dff9f7aee 100644 --- a/packages/playwright-core/src/client/tracing.ts +++ b/packages/playwright-core/src/client/tracing.ts @@ -210,6 +210,8 @@ export class Tracing extends ChannelOwner implements ap } } + // Note: a body-of-_doStopChunk helper carved out to be guarded by the catch above, + // not a reusable operation. private async _saveChunk(filePath: string | undefined, stacksId: string | undefined, additionalSources: string[]) { if (!filePath) { // Not interested in artifacts. @@ -244,9 +246,12 @@ export class Tracing extends ChannelOwner implements ap const artifact = Artifact.from(result.artifact); try { await artifact.saveAs(filePath); - } finally { - await artifact.delete(); + } catch (error) { + // Delete the artifact best-effort, the save error is the one to surface. + await artifact.delete().catch(() => {}); + throw error; } + await artifact.delete(); await localUtils.zip({ zipFile: filePath, entries: [], mode: 'append', stacksId, includeSources: this._includeSources, additionalSources }); } diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts index 3a7cd9cb129fb..b0cc503ef0823 100644 --- a/packages/playwright-core/src/server/trace/recorder/tracing.ts +++ b/packages/playwright-core/src/server/trace/recorder/tracing.ts @@ -140,8 +140,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps async resetForReuse(progress: Progress) { // Discard previous chunk if any and ignore any errors there. - await this.stopChunk(progress, { mode: 'discard' }).catch(() => {}); - await progress.race(this._stop()); + await this.stop(progress); if (this._snapshotter) await progress.race(this._snapshotter.resetForReuse()); } @@ -330,8 +329,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps // The client stops the chunk before stopping tracing, but that may fail, e.g. when // saving the trace hits a disk error. Discard the chunk so that tracing is always // stopped and can be started again. - if (this._state?.recording) - await this.stopChunk(progress, { mode: 'discard' }).catch(() => {}); + await this.stopChunk(progress, { mode: 'discard' }).catch(() => {}); await progress.race(this._stop()); } diff --git a/tests/library/tracing.spec.ts b/tests/library/tracing.spec.ts index 021831556e10c..f61454af98d1c 100644 --- a/tests/library/tracing.spec.ts +++ b/tests/library/tracing.spec.ts @@ -456,15 +456,14 @@ test('should stop tracing when the chunk was not stopped', async ({ context, pag expect(actions).toContain('Click'); }); -test('should release the stack session when saving the trace fails', async ({ browserType, server }, testInfo) => { +test('should release the stack session when saving the trace fails', async ({ browserType }, testInfo) => { test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/42423' }); - // Without tracesDir the stack session owns a temporary directory of its own. + // Explicit undefined overrides the tracesDir the test runner passes; without + // one the stack session owns a temporary directory of its own. const browser = await browserType.launch({ tracesDir: undefined }); try { - const page = await browser.newPage(); - const context = page.context(); + const context = await browser.newContext(); await context.tracing.start(); - await page.goto(server.PREFIX + '/input/button.html'); const stacksDir = path.dirname((context.tracing as any)._stacksId); expect(fs.existsSync(stacksDir)).toBe(true); From 9af2e63f4eba3d70bf92c4231bf962e0092285b5 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Thu, 27 Aug 2026 17:02:53 -0700 Subject: [PATCH 4/6] chore(tracing): tracingStop is not expected to throw, trim comments --- packages/playwright-core/src/client/tracing.ts | 11 +++-------- .../src/server/trace/recorder/tracing.ts | 8 ++------ tests/library/tracing.spec.ts | 3 +-- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/packages/playwright-core/src/client/tracing.ts b/packages/playwright-core/src/client/tracing.ts index 9c03dff9f7aee..4f403f5679a1c 100644 --- a/packages/playwright-core/src/client/tracing.ts +++ b/packages/playwright-core/src/client/tracing.ts @@ -95,10 +95,8 @@ export class Tracing extends ChannelOwner implements ap async stop(options: { path?: string } = {}) { await this._wrapApiCall(async () => { - // Stop tracing even when saving the trace failed, otherwise tracing can - // never be started again on this context. - let error: Error | undefined = await this._doStopChunk(options.path).catch(e => e); - await this._channel.tracingStop({}, kNoTimeout).catch(e => error ??= e); + const error = await this._doStopChunk(options.path).catch(e => e); + await this._channel.tracingStop({}, kNoTimeout); if (error) throw error; }); @@ -202,16 +200,13 @@ export class Tracing extends ChannelOwner implements ap try { await this._saveChunk(filePath, stacksId, additionalSources); } catch (error) { - // The stack session is owned by the chunk being stopped. Release it even when - // saving failed, otherwise later traces keep appending calls to it. + // Release the stack session even on failure, otherwise later traces keep appending to it. if (stacksId) await this._connection.localUtils()?.traceDiscarded({ stacksId }).catch(() => {}); throw error; } } - // Note: a body-of-_doStopChunk helper carved out to be guarded by the catch above, - // not a reusable operation. private async _saveChunk(filePath: string | undefined, stacksId: string | undefined, additionalSources: string[]) { if (!filePath) { // Not interested in artifacts. diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts index b0cc503ef0823..a1d3d9564593a 100644 --- a/packages/playwright-core/src/server/trace/recorder/tracing.ts +++ b/packages/playwright-core/src/server/trace/recorder/tracing.ts @@ -139,7 +139,6 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps } async resetForReuse(progress: Progress) { - // Discard previous chunk if any and ignore any errors there. await this.stop(progress); if (this._snapshotter) await progress.race(this._snapshotter.resetForReuse()); @@ -326,9 +325,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps } async stop(progress: Progress) { - // The client stops the chunk before stopping tracing, but that may fail, e.g. when - // saving the trace hits a disk error. Discard the chunk so that tracing is always - // stopped and can be started again. + // Discard the chunk if the client failed to stop it, so that tracing can be restarted. await this.stopChunk(progress, { mode: 'discard' }).catch(() => {}); await progress.race(this._stop()); } @@ -398,8 +395,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps try { return await this._stopChunk(progress, params); } finally { - // Always release the recording state, so that tracing can be stopped and started - // again even when saving the chunk failed. + // Always release the recording state, even when saving the chunk failed. this._isStopping = false; if (this._state) this._state.recording = false; diff --git a/tests/library/tracing.spec.ts b/tests/library/tracing.spec.ts index f61454af98d1c..ad5fe3fe56cb2 100644 --- a/tests/library/tracing.spec.ts +++ b/tests/library/tracing.spec.ts @@ -458,8 +458,7 @@ test('should stop tracing when the chunk was not stopped', async ({ context, pag test('should release the stack session when saving the trace fails', async ({ browserType }, testInfo) => { test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/42423' }); - // Explicit undefined overrides the tracesDir the test runner passes; without - // one the stack session owns a temporary directory of its own. + // Override the test runner's tracesDir, so that the stack session owns a temporary directory. const browser = await browserType.launch({ tracesDir: undefined }); try { const context = await browser.newContext(); From a3b14e41f8ae986f33d0f87752d2689d439dd275 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Fri, 28 Aug 2026 10:33:13 -0700 Subject: [PATCH 5/6] test(tracing): update action titles after step formatting change --- tests/library/tracing.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/library/tracing.spec.ts b/tests/library/tracing.spec.ts index ad5fe3fe56cb2..9568b20a54b5c 100644 --- a/tests/library/tracing.spec.ts +++ b/tests/library/tracing.spec.ts @@ -437,7 +437,7 @@ test('should recover tracing after a failed stop', async ({ context, page, serve const { events, actions } = await parseTraceRaw(testInfo.outputPath('trace2.zip')); expect(events[0].type).toBe('context-options'); - expect(actions).toContain('Click'); + expect(actions).toContain(`Click locator('button')`); }); test('should stop tracing when the chunk was not stopped', async ({ context, page, server }, testInfo) => { @@ -453,7 +453,7 @@ test('should stop tracing when the chunk was not stopped', async ({ context, pag const { events, actions } = await parseTraceRaw(testInfo.outputPath('trace.zip')); expect(events[0].type).toBe('context-options'); - expect(actions).toContain('Click'); + expect(actions).toContain(`Click locator('button')`); }); test('should release the stack session when saving the trace fails', async ({ browserType }, testInfo) => { From c8845295142cb94d724445d460db79c55a86fc5d Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Fri, 28 Aug 2026 10:59:46 -0700 Subject: [PATCH 6/6] chore(tracing): make _stopChunk synchronous, drop chunk-not-stopped test The test drove the protocol out of order by calling private channel methods; the sequence is not reachable through public APIs in a regular client. The server-side recovery in stop() stays. --- .../src/server/trace/recorder/tracing.ts | 46 ++++++++++--------- tests/library/tracing.spec.ts | 16 ------- 2 files changed, 25 insertions(+), 37 deletions(-) diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts index a1d3d9564593a..4c9b282782c24 100644 --- a/packages/playwright-core/src/server/trace/recorder/tracing.ts +++ b/packages/playwright-core/src/server/trace/recorder/tracing.ts @@ -393,7 +393,27 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps throw new Error(`Tracing is already stopping`); this._isStopping = true; try { - return await this._stopChunk(progress, params); + const result = this._stopChunk(params); + if (!result) + return {}; + + // Make sure all file operations complete. + try { + await progress.race(this._fs.sync()); + } catch (error) { + // This check is here because closing the browser removes the tracesDir and tracing + // cannot access removed files. Clients are ready for the missing artifact. + if (!isAbortError(error) && this._context.attribution.browser && !this._context.attribution.browser.isConnected()) + return {}; + throw error; + } + + if (params.mode === 'entries') + return { entries: result.entries }; + + const artifact = new Artifact(this._context, result.zipFileName); + artifact.reportFinished(); + return { artifact }; } finally { // Always release the recording state, even when saving the chunk failed. this._isStopping = false; @@ -402,11 +422,11 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps } } - private async _stopChunk(progress: Progress, params: TracingTracingStopChunkParams): Promise<{ artifact?: Artifact, entries?: NameValue[] }> { + private _stopChunk(params: TracingTracingStopChunkParams): { entries: NameValue[], zipFileName: string } | undefined { if (!this._state || !this._state.recording) { if (params.mode !== 'discard') throw new Error(`Must start tracing before stopping`); - return {}; + return undefined; } this._closeAllGroups(); @@ -440,7 +460,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps this._state.chunkFiles = new Set(); if (params.mode === 'discard') - return {}; + return undefined; this._fs.copyFile(this._state.networkFile, newNetworkFile); @@ -448,23 +468,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps if (params.mode === 'archive') this._fs.zip(entries, zipFileName); - // Make sure all file operations complete. - try { - await progress.race(this._fs.sync()); - } catch (error) { - // This check is here because closing the browser removes the tracesDir and tracing - // cannot access removed files. Clients are ready for the missing artifact. - if (!isAbortError(error) && this._context.attribution.browser && !this._context.attribution.browser.isConnected()) - return {}; - throw error; - } - - if (params.mode === 'entries') - return { entries }; - - const artifact = new Artifact(this._context, zipFileName); - artifact.reportFinished(); - return { artifact }; + return { entries, zipFileName }; } private async _captureSnapshot(progress: Progress, sdkObject: SdkObject, phase: trace.ActionPhase): Promise { diff --git a/tests/library/tracing.spec.ts b/tests/library/tracing.spec.ts index 9568b20a54b5c..1af0215d244e6 100644 --- a/tests/library/tracing.spec.ts +++ b/tests/library/tracing.spec.ts @@ -440,22 +440,6 @@ test('should recover tracing after a failed stop', async ({ context, page, serve expect(actions).toContain(`Click locator('button')`); }); -test('should stop tracing when the chunk was not stopped', async ({ context, page, server }, testInfo) => { - test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/42423' }); - await context.tracing.start(); - await page.goto(server.PREFIX + '/input/button.html'); - // Saving the chunk can fail before it was stopped, leaving the server recording. - await (context.tracing as any)._channel.tracingStop({}); - - await context.tracing.start(); - await page.click('button'); - await context.tracing.stop({ path: testInfo.outputPath('trace.zip') }); - - const { events, actions } = await parseTraceRaw(testInfo.outputPath('trace.zip')); - expect(events[0].type).toBe('context-options'); - expect(actions).toContain(`Click locator('button')`); -}); - test('should release the stack session when saving the trace fails', async ({ browserType }, testInfo) => { test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/42423' }); // Override the test runner's tracesDir, so that the stack session owns a temporary directory.