feat: support name option in tracing.startChunk() (#21692)

This commit is contained in:
Dmitry Gozman 2023-03-15 17:34:56 -07:00 committed by GitHub
parent bde2e90973
commit 40a6eff8f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 88 additions and 4 deletions

View File

@ -267,6 +267,13 @@ await context.Tracing.StopChunkAsync(new()
Trace name to be shown in the Trace Viewer.
### option: Tracing.startChunk.name
* since: v1.32
- `name` <[string]>
If specified, the trace is going to be saved into the file with the
given name inside the [`option: tracesDir`] folder specified in [`method: BrowserType.launch`].
## async method: Tracing.stop
* since: v1.12

View File

@ -34,13 +34,13 @@ export class Tracing extends ChannelOwner<channels.TracingChannel> implements ap
this._includeSources = !!options.sources;
await this._wrapApiCall(async () => {
await this._channel.tracingStart(options);
await this._channel.tracingStartChunk({ title: options.title });
await this._channel.tracingStartChunk({ name: options.name, title: options.title });
});
this._metadataCollector = [];
this._connection.startCollectingCallMetadata(this._metadataCollector);
}
async startChunk(options: { title?: string } = {}) {
async startChunk(options: { name?: string, title?: string } = {}) {
await this._channel.tracingStartChunk(options);
this._metadataCollector = [];
this._connection.startCollectingCallMetadata(this._metadataCollector);

View File

@ -2092,6 +2092,7 @@ scheme.TracingTracingStartParams = tObject({
});
scheme.TracingTracingStartResult = tOptional(tObject({}));
scheme.TracingTracingStartChunkParams = tObject({
name: tOptional(tString),
title: tOptional(tString),
});
scheme.TracingTracingStartChunkResult = tOptional(tObject({}));

View File

@ -125,6 +125,8 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
const o = this._state.options;
if (!o.screenshots !== !options.screenshots || !o.snapshots !== !options.snapshots)
throw new Error('Tracing has been already started with different options');
if (options.name && options.name !== this._state.traceName)
await this._changeTraceName(this._state, options.name);
return;
}
// TODO: passing the same name for two contexts makes them write into a single file
@ -143,7 +145,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
this._harTracer.start();
}
async startChunk(options: { title?: string } = {}) {
async startChunk(options: { name?: string, title?: string } = {}) {
if (this._state && this._state.recording)
await this.stopChunk({ mode: 'discard' });
@ -158,6 +160,8 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
state.traceFile = path.join(state.tracesDir, `${state.traceName}${suffix}.trace`);
state.recording = true;
if (options.name && options.name !== this._state.traceName)
this._changeTraceName(this._state, options.name);
this._appendTraceOperation(async () => {
await mkdirIfNeeded(state.traceFile);
await fs.promises.appendFile(state.traceFile, JSON.stringify({ ...this._contextCreatedEvent, title: options.title, wallTime: Date.now() }) + '\n');
@ -188,6 +192,16 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
page.setScreencastOptions(null);
}
private async _changeTraceName(state: RecordingState, name: string) {
await this._appendTraceOperation(async () => {
const oldNetworkFile = state.networkFile;
state.traceFile = path.join(state.tracesDir, name + '.trace');
state.networkFile = path.join(state.tracesDir, name + '.network');
// Network file survives across chunks, so make a copy with the new name.
await fs.promises.copyFile(oldNetworkFile, state.networkFile);
});
}
async stop() {
if (!this._state)
return;
@ -257,7 +271,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
if (params.mode === 'discard')
return {};
// Har files are live, make a snapshot before returning the resulting entries.
// Network file survives across chunks, make a snapshot before returning the resulting entries.
const networkFile = path.join(state.networkFile, '..', createGuid());
await fs.promises.copyFile(state.networkFile, networkFile);

View File

@ -18262,6 +18262,13 @@ export interface Tracing {
* @param options
*/
startChunk(options?: {
/**
* If specified, the trace is going to be saved into the file with the given name inside the `tracesDir` folder
* specified in
* [browserType.launch([options])](https://playwright.dev/docs/api/class-browsertype#browser-type-launch).
*/
name?: string;
/**
* Trace name to be shown in the Trace Viewer.
*/

View File

@ -3749,9 +3749,11 @@ export type TracingTracingStartOptions = {
};
export type TracingTracingStartResult = void;
export type TracingTracingStartChunkParams = {
name?: string,
title?: string,
};
export type TracingTracingStartChunkOptions = {
name?: string,
title?: string,
};
export type TracingTracingStartChunkResult = void;

View File

@ -2925,6 +2925,7 @@ Tracing:
tracingStartChunk:
parameters:
name: string?
title: string?
tracingStopChunk:

View File

@ -180,6 +180,58 @@ test('should collect two traces', async ({ context, page, server }, testInfo) =>
}
});
test('should respect tracesDir and name', async ({ browserType, server }, testInfo) => {
const tracesDir = testInfo.outputPath('traces');
const browser = await browserType.launch({ tracesDir });
const context = await browser.newContext();
const page = await context.newPage();
await context.tracing.start({ name: 'name1', snapshots: true });
await page.goto(server.PREFIX + '/one-style.html');
await context.tracing.stopChunk({ path: testInfo.outputPath('trace1.zip') });
expect(fs.existsSync(path.join(tracesDir, 'name1.trace'))).toBe(true);
expect(fs.existsSync(path.join(tracesDir, 'name1.network'))).toBe(true);
await context.tracing.startChunk({ name: 'name2' });
await page.goto(server.PREFIX + '/har.html');
await context.tracing.stop({ path: testInfo.outputPath('trace2.zip') });
expect(fs.existsSync(path.join(tracesDir, 'name2.trace'))).toBe(true);
expect(fs.existsSync(path.join(tracesDir, 'name2.network'))).toBe(true);
await browser.close();
function resourceNames(resources: Map<string, Buffer>) {
return [...resources.keys()].map(file => {
return file.replace(/^resources\/.*\.(html|css)$/, 'resources/XXX.$1');
}).sort();
}
{
const { resources, actions } = await parseTrace(testInfo.outputPath('trace1.zip'));
expect(actions).toEqual(['page.goto']);
expect(resourceNames(resources)).toEqual([
'resources/XXX.css',
'resources/XXX.html',
'trace.network',
'trace.stacks',
'trace.trace',
]);
}
{
const { resources, actions } = await parseTrace(testInfo.outputPath('trace2.zip'));
expect(actions).toEqual(['page.goto']);
expect(resourceNames(resources)).toEqual([
'resources/XXX.css',
'resources/XXX.html',
'resources/XXX.html',
'trace.network',
'trace.stacks',
'trace.trace',
]);
}
});
test('should not include trace resources from the provious chunks', async ({ context, page, server, browserName }, testInfo) => {
test.skip(browserName !== 'chromium', 'The number of screenshots is flaky in non-Chromium');
await context.tracing.start({ screenshots: true, snapshots: true, sources: true });