feat(trace-viewer): show test name (#9957)

This commit is contained in:
Pavel Feldman 2021-11-01 20:23:35 -08:00 committed by GitHub
parent 3673776330
commit 56ca3a18f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 91 additions and 37 deletions

View File

@ -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

View File

@ -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]>

View File

@ -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);
});
}

View File

@ -192,7 +192,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
}
async tracingStartChunk(params: channels.BrowserContextTracingStartChunkParams): Promise<channels.BrowserContextTracingStartChunkResult> {
await this._context.tracing.startChunk();
await this._context.tracing.startChunk(params);
}
async tracingStopChunk(params: channels.BrowserContextTracingStopChunkParams): Promise<channels.BrowserContextTracingStopChunkResult> {

View File

@ -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<BrowserContextRecorderSupplementEnableResult>;
newCDPSession(params: BrowserContextNewCDPSessionParams, metadata?: Metadata): Promise<BrowserContextNewCDPSessionResult>;
tracingStart(params: BrowserContextTracingStartParams, metadata?: Metadata): Promise<BrowserContextTracingStartResult>;
tracingStartChunk(params?: BrowserContextTracingStartChunkParams, metadata?: Metadata): Promise<BrowserContextTracingStartChunkResult>;
tracingStartChunk(params: BrowserContextTracingStartChunkParams, metadata?: Metadata): Promise<BrowserContextTracingStartChunkResult>;
tracingStopChunk(params: BrowserContextTracingStopChunkParams, metadata?: Metadata): Promise<BrowserContextTracingStopChunkResult>;
tracingStop(params?: BrowserContextTracingStopParams, metadata?: Metadata): Promise<BrowserContextTracingStopResult>;
harExport(params?: BrowserContextHarExportParams, metadata?: Metadata): Promise<BrowserContextHarExportResult>;
@ -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?: {

View File

@ -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:

View File

@ -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({

View File

@ -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) {

View File

@ -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
};

View File

@ -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);

View File

@ -272,7 +272,6 @@ export type BrowserContextOptions = {
strictSelectors?: boolean,
proxy?: ProxySettings,
baseURL?: string,
_debugName?: string,
};
export type EnvArray = { name: string, value: string }[];

View File

@ -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: '<empty>',
},
pages: [],
resources: [],

View File

@ -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;
}

View File

@ -22,6 +22,7 @@
flex-direction: column;
padding: 20px 0 5px;
cursor: text;
user-select: none;
}
.timeline-divider {

View File

@ -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;
}

View File

@ -106,6 +106,7 @@ export const Workbench: React.FunctionComponent<{
<div className='hbox header'>
<div className='logo'>🎭</div>
<div className='product'>Playwright</div>
{contextEntry.title && <div className='title'>{contextEntry.title}</div>}
<div className='spacer'></div>
</div>
<div style={{ background: 'white', paddingLeft: '20px', flex: 'none', borderBottom: '1px solid #ddd' }}>

View File

@ -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<void>;
/**
* 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<void>;
startChunk(options?: {
/**
* Trace name to be shown in the Trace Viewer.
*/
title?: string;
}): Promise<void>;
/**
* Stop tracing.
@ -14438,15 +14449,16 @@ export interface Tracing {
}): Promise<void>;
/**
* 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<void>;

View File

@ -186,11 +186,12 @@ export const test = _baseTest.extend<TestFixtures, WorkerAndFileFixtures>({
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;

View File

@ -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,

View File

@ -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.
*/

View File

@ -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');
});

View File

@ -178,6 +178,7 @@ export interface TestInfo {
workerIndex: number;
title: string;
titlePath: string[];
file: string;
line: number;
column: number;