diff --git a/docs/src/api/class-tracing.md b/docs/src/api/class-tracing.md index d43458473d..8dcf3a7563 100644 --- a/docs/src/api/class-tracing.md +++ b/docs/src/api/class-tracing.md @@ -133,11 +133,15 @@ Whether to capture DOM snapshot on every action. Whether to include source files for trace actions. +### option: Tracing.start.title +- `title` <[string]> + +Trace name to be shown in the Trace Viewer. + ## async method: Tracing.startChunk Start a new trace chunk. If you'd like to record multiple traces on the same [BrowserContext], use [`method: Tracing.start`] once, and then create multiple trace chunks with [`method: Tracing.startChunk`] and [`method: Tracing.stopChunk`]. - ```js await context.tracing.start({ screenshots: true, snapshots: true }); const page = await context.newPage(); @@ -234,6 +238,11 @@ await context.Tracing.StopChunkAsync(new TracingStopChunkOptions }); ``` +### option: Tracing.startChunk.title +- `title` <[string]> + +Trace name to be shown in the Trace Viewer. + ## async method: Tracing.stop diff --git a/docs/src/test-api/class-testinfo.md b/docs/src/test-api/class-testinfo.md index 75ea2162fe..b21ca1db8d 100644 --- a/docs/src/test-api/class-testinfo.md +++ b/docs/src/test-api/class-testinfo.md @@ -362,6 +362,11 @@ test.beforeEach(async ({ page }, testInfo) => { The title of the currently running test as passed to `test(title, testFunction)`. +## property: TestInfo.titlePath +- type: <[Array]<[string]>> + +The full title path starting with the project. + ## property: TestInfo.workerIndex - type: <[int]> diff --git a/packages/playwright-core/src/client/tracing.ts b/packages/playwright-core/src/client/tracing.ts index 4d007051ab..a27d7bfe39 100644 --- a/packages/playwright-core/src/client/tracing.ts +++ b/packages/playwright-core/src/client/tracing.ts @@ -43,19 +43,19 @@ export class Tracing implements api.Tracing { }; } - async start(options: { name?: string, snapshots?: boolean, screenshots?: boolean, sources?: boolean } = {}) { + async start(options: { name?: string, title?: string, snapshots?: boolean, screenshots?: boolean, sources?: boolean } = {}) { if (options.sources) this._context._instrumentation!.addListener(this._instrumentationListener); await this._context._wrapApiCall(async (channel: channels.BrowserContextChannel) => { await channel.tracingStart(options); - await channel.tracingStartChunk(); + await channel.tracingStartChunk({ title: options.title }); }); } - async startChunk() { + async startChunk(options: { title?: string } = {}) { this._sources = new Set(); await this._context._wrapApiCall(async (channel: channels.BrowserContextChannel) => { - await channel.tracingStartChunk(); + await channel.tracingStartChunk(options); }); } diff --git a/packages/playwright-core/src/dispatchers/browserContextDispatcher.ts b/packages/playwright-core/src/dispatchers/browserContextDispatcher.ts index 61ff3fbf6d..ad55475c6a 100644 --- a/packages/playwright-core/src/dispatchers/browserContextDispatcher.ts +++ b/packages/playwright-core/src/dispatchers/browserContextDispatcher.ts @@ -192,7 +192,7 @@ export class BrowserContextDispatcher extends Dispatcher { - await this._context.tracing.startChunk(); + await this._context.tracing.startChunk(params); } async tracingStopChunk(params: channels.BrowserContextTracingStopChunkParams): Promise { diff --git a/packages/playwright-core/src/protocol/channels.ts b/packages/playwright-core/src/protocol/channels.ts index 684fe8b3da..82ef354922 100644 --- a/packages/playwright-core/src/protocol/channels.ts +++ b/packages/playwright-core/src/protocol/channels.ts @@ -558,7 +558,6 @@ export type BrowserTypeLaunchPersistentContextParams = { forcedColors?: 'active' | 'none', acceptDownloads?: boolean, baseURL?: string, - _debugName?: string, recordVideo?: { dir: string, size?: { @@ -631,7 +630,6 @@ export type BrowserTypeLaunchPersistentContextOptions = { forcedColors?: 'active' | 'none', acceptDownloads?: boolean, baseURL?: string, - _debugName?: string, recordVideo?: { dir: string, size?: { @@ -725,7 +723,6 @@ export type BrowserNewContextParams = { forcedColors?: 'active' | 'none', acceptDownloads?: boolean, baseURL?: string, - _debugName?: string, recordVideo?: { dir: string, size?: { @@ -785,7 +782,6 @@ export type BrowserNewContextOptions = { forcedColors?: 'active' | 'none', acceptDownloads?: boolean, baseURL?: string, - _debugName?: string, recordVideo?: { dir: string, size?: { @@ -900,7 +896,7 @@ export interface BrowserContextChannel extends EventTargetChannel { recorderSupplementEnable(params: BrowserContextRecorderSupplementEnableParams, metadata?: Metadata): Promise; newCDPSession(params: BrowserContextNewCDPSessionParams, metadata?: Metadata): Promise; tracingStart(params: BrowserContextTracingStartParams, metadata?: Metadata): Promise; - tracingStartChunk(params?: BrowserContextTracingStartChunkParams, metadata?: Metadata): Promise; + tracingStartChunk(params: BrowserContextTracingStartChunkParams, metadata?: Metadata): Promise; tracingStopChunk(params: BrowserContextTracingStopChunkParams, metadata?: Metadata): Promise; tracingStop(params?: BrowserContextTracingStopParams, metadata?: Metadata): Promise; harExport(params?: BrowserContextHarExportParams, metadata?: Metadata): Promise; @@ -1113,8 +1109,12 @@ export type BrowserContextTracingStartOptions = { screenshots?: boolean, }; export type BrowserContextTracingStartResult = void; -export type BrowserContextTracingStartChunkParams = {}; -export type BrowserContextTracingStartChunkOptions = {}; +export type BrowserContextTracingStartChunkParams = { + title?: string, +}; +export type BrowserContextTracingStartChunkOptions = { + title?: string, +}; export type BrowserContextTracingStartChunkResult = void; export type BrowserContextTracingStopChunkParams = { save: boolean, @@ -3538,7 +3538,6 @@ export type AndroidDeviceLaunchBrowserParams = { reducedMotion?: 'reduce' | 'no-preference', forcedColors?: 'active' | 'none', acceptDownloads?: boolean, - _debugName?: string, recordVideo?: { dir: string, size?: { @@ -3585,7 +3584,6 @@ export type AndroidDeviceLaunchBrowserOptions = { reducedMotion?: 'reduce' | 'no-preference', forcedColors?: 'active' | 'none', acceptDownloads?: boolean, - _debugName?: string, recordVideo?: { dir: string, size?: { diff --git a/packages/playwright-core/src/protocol/protocol.yml b/packages/playwright-core/src/protocol/protocol.yml index 2270390f03..0832ecff6e 100644 --- a/packages/playwright-core/src/protocol/protocol.yml +++ b/packages/playwright-core/src/protocol/protocol.yml @@ -403,7 +403,6 @@ ContextOptions: - none acceptDownloads: boolean? baseURL: string? - _debugName: string? recordVideo: type: object? properties: @@ -819,6 +818,8 @@ BrowserContext: screenshots: boolean? tracingStartChunk: + parameters: + title: string? tracingStopChunk: parameters: @@ -2892,7 +2893,6 @@ AndroidDevice: - active - none acceptDownloads: boolean? - _debugName: string? recordVideo: type: object? properties: diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 4c834b4207..9dced3cbb6 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -329,7 +329,6 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { forcedColors: tOptional(tEnum(['active', 'none'])), acceptDownloads: tOptional(tBoolean), baseURL: tOptional(tString), - _debugName: tOptional(tString), recordVideo: tOptional(tObject({ dir: tString, size: tOptional(tObject({ @@ -389,7 +388,6 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { forcedColors: tOptional(tEnum(['active', 'none'])), acceptDownloads: tOptional(tBoolean), baseURL: tOptional(tString), - _debugName: tOptional(tString), recordVideo: tOptional(tObject({ dir: tString, size: tOptional(tObject({ @@ -505,7 +503,9 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { snapshots: tOptional(tBoolean), screenshots: tOptional(tBoolean), }); - scheme.BrowserContextTracingStartChunkParams = tOptional(tObject({})); + scheme.BrowserContextTracingStartChunkParams = tObject({ + title: tOptional(tString), + }); scheme.BrowserContextTracingStopChunkParams = tObject({ save: tBoolean, skipCompress: tBoolean, @@ -1343,7 +1343,6 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { reducedMotion: tOptional(tEnum(['reduce', 'no-preference'])), forcedColors: tOptional(tEnum(['active', 'none'])), acceptDownloads: tOptional(tBoolean), - _debugName: tOptional(tString), recordVideo: tOptional(tObject({ dir: tString, size: tOptional(tObject({ diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts index ae30ecb0dc..b92f82a436 100644 --- a/packages/playwright-core/src/server/browserContext.ts +++ b/packages/playwright-core/src/server/browserContext.ts @@ -428,8 +428,6 @@ export function validateBrowserContextOptions(options: types.BrowserContextOptio if (debugMode() === 'inspector') options.bypassCSP = true; verifyGeolocation(options.geolocation); - if (!options._debugName) - options._debugName = createGuid(); } export function verifyGeolocation(geolocation?: types.Geolocation) { diff --git a/packages/playwright-core/src/server/trace/common/traceEvents.ts b/packages/playwright-core/src/server/trace/common/traceEvents.ts index 34723a69cc..bea151955d 100644 --- a/packages/playwright-core/src/server/trace/common/traceEvents.ts +++ b/packages/playwright-core/src/server/trace/common/traceEvents.ts @@ -24,13 +24,13 @@ export type BrowserContextEventOptions = { viewport?: Size, deviceScaleFactor?: number, isMobile?: boolean, - _debugName?: string, }; export type ContextCreatedTraceEvent = { version: number, type: 'context-options', browserName: string, + title?: string, options: BrowserContextEventOptions }; diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts index 8975151238..f9a445f9dd 100644 --- a/packages/playwright-core/src/server/trace/recorder/tracing.ts +++ b/packages/playwright-core/src/server/trace/recorder/tracing.ts @@ -107,7 +107,7 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha this._harTracer.start(); } - async startChunk() { + async startChunk(options: { title?: string } = {}) { if (this._state && this._state.recording) await this.stopChunk(false, false); @@ -124,7 +124,7 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha this._appendTraceOperation(async () => { await mkdirIfNeeded(state.traceFile); - await fs.promises.appendFile(state.traceFile, JSON.stringify(this._contextCreatedEvent) + '\n'); + await fs.promises.appendFile(state.traceFile, JSON.stringify({ ...this._contextCreatedEvent, title: options.title }) + '\n'); }); this._context.instrumentation.addListener(this); diff --git a/packages/playwright-core/src/server/types.ts b/packages/playwright-core/src/server/types.ts index 1b42a8a807..8e99ff60cf 100644 --- a/packages/playwright-core/src/server/types.ts +++ b/packages/playwright-core/src/server/types.ts @@ -272,7 +272,6 @@ export type BrowserContextOptions = { strictSelectors?: boolean, proxy?: ProxySettings, baseURL?: string, - _debugName?: string, }; export type EnvArray = { name: string, value: string }[]; diff --git a/packages/playwright-core/src/web/traceViewer/entries.ts b/packages/playwright-core/src/web/traceViewer/entries.ts index 96cf4554f2..900304de08 100644 --- a/packages/playwright-core/src/web/traceViewer/entries.ts +++ b/packages/playwright-core/src/web/traceViewer/entries.ts @@ -21,6 +21,7 @@ export type ContextEntry = { startTime: number; endTime: number; browserName: string; + title?: string; options: trace.BrowserContextEventOptions; pages: PageEntry[]; resources: ResourceSnapshot[]; @@ -46,7 +47,6 @@ export function createEmptyContext(): ContextEntry { deviceScaleFactor: 1, isMobile: false, viewport: { width: 1280, height: 800 }, - _debugName: '', }, pages: [], resources: [], diff --git a/packages/playwright-core/src/web/traceViewer/traceModel.ts b/packages/playwright-core/src/web/traceViewer/traceModel.ts index 320633aa37..56edc1eeda 100644 --- a/packages/playwright-core/src/web/traceViewer/traceModel.ts +++ b/packages/playwright-core/src/web/traceViewer/traceModel.ts @@ -103,6 +103,7 @@ export class TraceModel { switch (event.type) { case 'context-options': { this.contextEntry.browserName = event.browserName; + this.contextEntry.title = event.title; this.contextEntry.options = event.options; break; } diff --git a/packages/playwright-core/src/web/traceViewer/ui/timeline.css b/packages/playwright-core/src/web/traceViewer/ui/timeline.css index 6c35c8c092..e44eb288c8 100644 --- a/packages/playwright-core/src/web/traceViewer/ui/timeline.css +++ b/packages/playwright-core/src/web/traceViewer/ui/timeline.css @@ -22,6 +22,7 @@ flex-direction: column; padding: 20px 0 5px; cursor: text; + user-select: none; } .timeline-divider { diff --git a/packages/playwright-core/src/web/traceViewer/ui/workbench.css b/packages/playwright-core/src/web/traceViewer/ui/workbench.css index ba898de856..b43fd9663a 100644 --- a/packages/playwright-core/src/web/traceViewer/ui/workbench.css +++ b/packages/playwright-core/src/web/traceViewer/ui/workbench.css @@ -52,7 +52,6 @@ .workbench { contain: size; - user-select: none; } .workbench .header { @@ -86,6 +85,10 @@ margin-left: 16px; } +.workbench .title { + margin-left: 16px; +} + .workbench .spacer { flex: auto; } diff --git a/packages/playwright-core/src/web/traceViewer/ui/workbench.tsx b/packages/playwright-core/src/web/traceViewer/ui/workbench.tsx index 552cf8ace3..9df17fdaca 100644 --- a/packages/playwright-core/src/web/traceViewer/ui/workbench.tsx +++ b/packages/playwright-core/src/web/traceViewer/ui/workbench.tsx @@ -106,6 +106,7 @@ export const Workbench: React.FunctionComponent<{
🎭
Playwright
+ {contextEntry.title &&
{contextEntry.title}
}
diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 7d544aec6c..f8ad28fb91 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -14399,12 +14399,17 @@ export interface Tracing { * Whether to include source files for trace actions. */ sources?: boolean; + + /** + * Trace name to be shown in the Trace Viewer. + */ + title?: string; }): Promise; /** * Start a new trace chunk. If you'd like to record multiple traces on the same [BrowserContext], use * [tracing.start([options])](https://playwright.dev/docs/api/class-tracing#tracing-start) once, and then create multiple - * trace chunks with [tracing.startChunk()](https://playwright.dev/docs/api/class-tracing#tracing-start-chunk) and + * trace chunks with [tracing.startChunk([options])](https://playwright.dev/docs/api/class-tracing#tracing-start-chunk) and * [tracing.stopChunk([options])](https://playwright.dev/docs/api/class-tracing#tracing-stop-chunk). * * ```js @@ -14423,8 +14428,14 @@ export interface Tracing { * await context.tracing.stopChunk({ path: 'trace2.zip' }); * ``` * + * @param options */ - startChunk(): Promise; + startChunk(options?: { + /** + * Trace name to be shown in the Trace Viewer. + */ + title?: string; + }): Promise; /** * Stop tracing. @@ -14438,15 +14449,16 @@ export interface Tracing { }): Promise; /** - * Stop the trace chunk. See [tracing.startChunk()](https://playwright.dev/docs/api/class-tracing#tracing-start-chunk) for - * more details about multiple trace chunks. + * Stop the trace chunk. See + * [tracing.startChunk([options])](https://playwright.dev/docs/api/class-tracing#tracing-start-chunk) for more details + * about multiple trace chunks. * @param options */ stopChunk(options?: { /** * Export trace collected since the last - * [tracing.startChunk()](https://playwright.dev/docs/api/class-tracing#tracing-start-chunk) call into the file with the - * given path. + * [tracing.startChunk([options])](https://playwright.dev/docs/api/class-tracing#tracing-start-chunk) call into the file + * with the given path. */ path?: string; }): Promise; diff --git a/packages/playwright-test/src/index.ts b/packages/playwright-test/src/index.ts index 0bbc5be97b..a579acb90f 100644 --- a/packages/playwright-test/src/index.ts +++ b/packages/playwright-test/src/index.ts @@ -186,11 +186,12 @@ export const test = _baseTest.extend({ context.setDefaultTimeout(actionTimeout || 0); context.setDefaultNavigationTimeout(navigationTimeout || actionTimeout || 0); if (captureTrace) { + const title = [path.relative(testInfo.project.testDir, testInfo.file) + ':' + testInfo.line, ...testInfo.titlePath.slice(1)].join(' › '); if (!(context.tracing as any)[kTracingStarted]) { - await context.tracing.start({ screenshots: true, snapshots: true, sources: true }); + await context.tracing.start({ screenshots: true, snapshots: true, sources: true, title }); (context.tracing as any)[kTracingStarted] = true; } else { - await context.tracing.startChunk(); + await context.tracing.startChunk({ title }); } } else { (context.tracing as any)[kTracingStarted] = false; diff --git a/packages/playwright-test/src/workerRunner.ts b/packages/playwright-test/src/workerRunner.ts index c6361e0396..955b7835b5 100644 --- a/packages/playwright-test/src/workerRunner.ts +++ b/packages/playwright-test/src/workerRunner.ts @@ -249,6 +249,7 @@ export class WorkerRunner extends EventEmitter { project: this._project.config, config: this._loader.fullConfig(), title: test.title, + titlePath: test.titlePath(), file: test.location.file, line: test.location.line, column: test.location.column, diff --git a/packages/playwright-test/types/test.d.ts b/packages/playwright-test/types/test.d.ts index a8cdbe3425..1cd4336df2 100644 --- a/packages/playwright-test/types/test.d.ts +++ b/packages/playwright-test/types/test.d.ts @@ -1101,6 +1101,10 @@ export interface TestInfo { * The title of the currently running test as passed to `test(title, testFunction)`. */ title: string; + /** + * The full title path starting with the project. + */ + titlePath: string[]; /** * Absolute path to a file where the currently running test is declared. */ diff --git a/tests/playwright-test/reporter-html.spec.ts b/tests/playwright-test/reporter-html.spec.ts index cb5dfc542e..ddc71cc0cf 100644 --- a/tests/playwright-test/reporter-html.spec.ts +++ b/tests/playwright-test/reporter-html.spec.ts @@ -221,3 +221,24 @@ test('should show trace source', async ({ runInlineTest, page, showReport }) => ]); await expect(page.locator('.stack-trace-frame.selected')).toContainText('a.test.js'); }); + +test('should show trace title', async ({ runInlineTest, page, showReport }) => { + const result = await runInlineTest({ + 'playwright.config.js': ` + module.exports = { use: { trace: 'on' } }; + `, + 'a.test.js': ` + const { test } = pwt; + test('passes', async ({ page }) => { + await page.evaluate('2 + 2'); + }); + `, + }, { reporter: 'dot,html' }); + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(1); + + await showReport(); + await page.click('text=passes'); + await page.click('img'); + await expect(page.locator('.workbench .title')).toHaveText('a.test.js:6 › passes'); +}); diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index b426a16a22..9637ff6929 100644 --- a/utils/generate_types/overrides-test.d.ts +++ b/utils/generate_types/overrides-test.d.ts @@ -178,6 +178,7 @@ export interface TestInfo { workerIndex: number; title: string; + titlePath: string[]; file: string; line: number; column: number;