diff --git a/packages/playwright-core/src/client/tracing.ts b/packages/playwright-core/src/client/tracing.ts index a3c7bd53a3e0e..f6f02a6fc7b8b 100644 --- a/packages/playwright-core/src/client/tracing.ts +++ b/packages/playwright-core/src/client/tracing.ts @@ -22,6 +22,7 @@ import type * as channels from '@protocol/channels'; export class Tracing extends ChannelOwner implements api.Tracing { private _includeSources = false; + private _additionalSources = new Set(); private _isLive = false; _tracesDir: string | undefined; private _stacksId: string | undefined; @@ -58,6 +59,8 @@ export class Tracing extends ChannelOwner implements ap } async group(name: string, options: { location?: { file: string, line?: number, column?: number } } = {}) { + if (options.location) + this._additionalSources.add(options.location.file); await this._channel.tracingGroup({ name, location: options.location }); } @@ -90,6 +93,9 @@ export class Tracing extends ChannelOwner implements ap private async _doStopChunk(filePath: string | undefined) { this._resetStackCounter(); + const additionalSources = [...this._additionalSources]; + this._additionalSources.clear(); + if (!filePath) { // Not interested in artifacts. await this._channel.tracingStopChunk({ mode: 'discard' }); @@ -106,7 +112,7 @@ export class Tracing extends ChannelOwner implements ap if (isLocal) { const result = await this._channel.tracingStopChunk({ mode: 'entries' }); - await localUtils.zip({ zipFile: filePath, entries: result.entries!, mode: 'write', stacksId: this._stacksId, includeSources: this._includeSources }); + await localUtils.zip({ zipFile: filePath, entries: result.entries!, mode: 'write', stacksId: this._stacksId, includeSources: this._includeSources, additionalSources }); return; } @@ -124,7 +130,7 @@ export class Tracing extends ChannelOwner implements ap await artifact.saveAs(filePath); await artifact.delete(); - await localUtils.zip({ zipFile: filePath, entries: [], mode: 'append', stacksId: this._stacksId, includeSources: this._includeSources }); + await localUtils.zip({ zipFile: filePath, entries: [], mode: 'append', stacksId: this._stacksId, includeSources: this._includeSources, additionalSources }); } _resetStackCounter() { diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 06622ef91f8a3..b395a6cad7e99 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -278,6 +278,7 @@ scheme.LocalUtilsZipParams = tObject({ stacksId: tOptional(tString), mode: tEnum(['write', 'append']), includeSources: tBoolean, + additionalSources: tOptional(tArray(tString)), }); scheme.LocalUtilsZipResult = tOptional(tObject({})); scheme.LocalUtilsHarOpenParams = tObject({ diff --git a/packages/playwright-core/src/server/localUtils.ts b/packages/playwright-core/src/server/localUtils.ts index 685a0f64f0f10..2a1cf62d5b1fd 100644 --- a/packages/playwright-core/src/server/localUtils.ts +++ b/packages/playwright-core/src/server/localUtils.ts @@ -67,7 +67,7 @@ export async function zip(progress: Progress, stackSessions: Map(); + const sourceFiles = new Set(params.additionalSources); for (const { stack } of stackSession?.callStacks || []) { if (!stack) continue; @@ -75,7 +75,7 @@ export async function zip(progress: Progress, stackSessions: Map { test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/36483' }); + test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/39302' }); + + const sourceFile = test.info().outputPath('source.js'); + await fs.promises.writeFile(sourceFile, 'buddy beaver'); const traceViewer = await test.step('create trace with groups', async () => { await page.context().tracing.group('ignored group'); @@ -125,15 +129,21 @@ test('should show tracing.group in the action list with location', async ({ runA await context.tracing.group('inner group 2'); await expect(page.getByText('Hello')).toBeVisible(); await context.tracing.groupEnd(); + await context.tracing.group('inner group 3', { location: { file: sourceFile, line: 1, column: 1 } }); + await expect(page.getByText('Hello')).toBeVisible(); + await context.tracing.groupEnd(); await context.tracing.groupEnd(); }); }); + await fs.promises.rm(sourceFile); + await expect(traceViewer.actionTitles).toHaveText([ /outer group/, /Navigate/, /inner group 1 {{ eager_beaver }}/, /inner group 2/, + /inner group 3/, /toBeVisible/, ]); @@ -145,12 +155,16 @@ test('should show tracing.group in the action list with location', async ({ runA /inner group 1 {{ eager_beaver }}/, /Click.*locator/, /inner group 2/, + /inner group 3/, ]); await traceViewer.showSourceTab(); await expect(traceViewer.sourceCodeTab.locator('.source-line-running')).toHaveText(/DO NOT TOUCH THIS LINE/); await traceViewer.selectAction('inner group 2'); await expect(traceViewer.sourceCodeTab.locator('.source-line-running')).toContainText("await context.tracing.group('inner group 2');"); + + await traceViewer.selectAction('inner group 3'); + await expect(traceViewer.sourceCodeTab.locator('.source-line-running')).toContainText('buddy beaver'); }); test('should open simple trace viewer', async ({ showTraceViewer }) => {