mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: create trace.zip in driver for local runs (#10868)
This commit is contained in:
parent
a436cc8aa0
commit
a2a8967bed
@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import * as api from '../../types/types';
|
||||
import * as channels from '../protocol/channels';
|
||||
import { Artifact } from './artifact';
|
||||
@ -52,23 +51,27 @@ export class Tracing implements api.Tracing {
|
||||
private async _doStopChunk(channel: channels.BrowserContextChannel, filePath: string | undefined) {
|
||||
const isLocal = !this._context._connection.isRemote();
|
||||
|
||||
const result = await channel.tracingStopChunk({ save: !!filePath, skipCompress: isLocal });
|
||||
let mode: channels.BrowserContextTracingStopChunkParams['mode'] = 'doNotSave';
|
||||
if (filePath) {
|
||||
if (isLocal)
|
||||
mode = 'compressTraceAndSources';
|
||||
else
|
||||
mode = 'compressTrace';
|
||||
}
|
||||
|
||||
const result = await channel.tracingStopChunk({ mode });
|
||||
if (!filePath) {
|
||||
// Not interested in artifacts.
|
||||
return;
|
||||
}
|
||||
|
||||
if (filePath && fs.existsSync(filePath))
|
||||
await fs.promises.unlink(filePath);
|
||||
|
||||
if (!isLocal) {
|
||||
// We run against remote Playwright, compress on remote side.
|
||||
// Save trace to the final local file.
|
||||
const artifact = Artifact.from(result.artifact!);
|
||||
await artifact.saveAs(filePath);
|
||||
await artifact.delete();
|
||||
}
|
||||
|
||||
if (isLocal || result.sourceEntries.length)
|
||||
await this._context._localUtils.zip(filePath, result.sourceEntries.concat(result.entries));
|
||||
// Add local sources to the remote trace if necessary.
|
||||
if (result.sourceEntries?.length)
|
||||
await this._context._localUtils.zip(filePath, result.sourceEntries);
|
||||
}
|
||||
}
|
||||
|
@ -198,8 +198,8 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
||||
}
|
||||
|
||||
async tracingStopChunk(params: channels.BrowserContextTracingStopChunkParams): Promise<channels.BrowserContextTracingStopChunkResult> {
|
||||
const { artifact, entries, sourceEntries } = await this._context.tracing.stopChunk(params.save, params.skipCompress);
|
||||
return { artifact: artifact ? new ArtifactDispatcher(this._scope, artifact) : undefined, entries, sourceEntries };
|
||||
const { artifact, sourceEntries } = await this._context.tracing.stopChunk(params);
|
||||
return { artifact: artifact ? new ArtifactDispatcher(this._scope, artifact) : undefined, sourceEntries };
|
||||
}
|
||||
|
||||
async tracingStop(params: channels.BrowserContextTracingStopParams): Promise<channels.BrowserContextTracingStopResult> {
|
||||
|
@ -1266,16 +1266,14 @@ export type BrowserContextTracingStartChunkOptions = {
|
||||
};
|
||||
export type BrowserContextTracingStartChunkResult = void;
|
||||
export type BrowserContextTracingStopChunkParams = {
|
||||
save: boolean,
|
||||
skipCompress: boolean,
|
||||
mode: 'doNotSave' | 'compressTrace' | 'compressTraceAndSources',
|
||||
};
|
||||
export type BrowserContextTracingStopChunkOptions = {
|
||||
|
||||
};
|
||||
export type BrowserContextTracingStopChunkResult = {
|
||||
artifact?: ArtifactChannel,
|
||||
entries: NameValue[],
|
||||
sourceEntries: NameValue[],
|
||||
sourceEntries?: NameValue[],
|
||||
};
|
||||
export type BrowserContextTracingStopParams = {};
|
||||
export type BrowserContextTracingStopOptions = {};
|
||||
|
@ -833,15 +833,16 @@ BrowserContext:
|
||||
|
||||
tracingStopChunk:
|
||||
parameters:
|
||||
save: boolean
|
||||
skipCompress: boolean
|
||||
mode:
|
||||
type: enum
|
||||
literals:
|
||||
- doNotSave
|
||||
- compressTrace
|
||||
- compressTraceAndSources
|
||||
returns:
|
||||
artifact: Artifact?
|
||||
entries:
|
||||
type: array
|
||||
items: NameValue
|
||||
sourceEntries:
|
||||
type: array
|
||||
type: array?
|
||||
items: NameValue
|
||||
|
||||
tracingStop:
|
||||
|
@ -509,8 +509,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||
title: tOptional(tString),
|
||||
});
|
||||
scheme.BrowserContextTracingStopChunkParams = tObject({
|
||||
save: tBoolean,
|
||||
skipCompress: tBoolean,
|
||||
mode: tEnum(['doNotSave', 'compressTrace', 'compressTraceAndSources']),
|
||||
});
|
||||
scheme.BrowserContextTracingStopParams = tOptional(tObject({}));
|
||||
scheme.BrowserContextHarExportParams = tOptional(tObject({}));
|
||||
|
@ -19,7 +19,7 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
import yazl from 'yazl';
|
||||
import { NameValue } from '../../../common/types';
|
||||
import { commandsWithTracingSnapshots } from '../../../protocol/channels';
|
||||
import { commandsWithTracingSnapshots, BrowserContextTracingStopChunkParams } from '../../../protocol/channels';
|
||||
import { ManualPromise } from '../../../utils/async';
|
||||
import { eventsHelper, RegisteredListener } from '../../../utils/eventsHelper';
|
||||
import { calculateSha1, createGuid, mkdirIfNeeded, monotonicTime } from '../../../utils/utils';
|
||||
@ -113,7 +113,7 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha
|
||||
|
||||
async startChunk(options: { title?: string } = {}) {
|
||||
if (this._state && this._state.recording)
|
||||
await this.stopChunk(false, false);
|
||||
await this.stopChunk({ mode: 'doNotSave' });
|
||||
|
||||
if (!this._state)
|
||||
throw new Error('Must start tracing before starting a new chunk');
|
||||
@ -169,16 +169,16 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha
|
||||
await this._writeChain;
|
||||
}
|
||||
|
||||
async stopChunk(save: boolean, skipCompress: boolean): Promise<{ artifact: Artifact | null, entries: NameValue[], sourceEntries: NameValue[] }> {
|
||||
async stopChunk(params: BrowserContextTracingStopChunkParams): Promise<{ artifact: Artifact | null, sourceEntries: NameValue[] | undefined }> {
|
||||
if (this._isStopping)
|
||||
throw new Error(`Tracing is already stopping`);
|
||||
this._isStopping = true;
|
||||
|
||||
if (!this._state || !this._state.recording) {
|
||||
this._isStopping = false;
|
||||
if (save)
|
||||
if (params.mode !== 'doNotSave')
|
||||
throw new Error(`Must start tracing before stopping`);
|
||||
return { artifact: null, entries: [], sourceEntries: [] };
|
||||
return { artifact: null, sourceEntries: [] };
|
||||
}
|
||||
|
||||
const state = this._state!;
|
||||
@ -205,8 +205,8 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha
|
||||
// Chain the export operation against write operations,
|
||||
// so that neither trace files nor sha1s change during the export.
|
||||
return await this._appendTraceOperation(async () => {
|
||||
if (!save)
|
||||
return { artifact: null, entries: [], sourceEntries: [] };
|
||||
if (params.mode === 'doNotSave')
|
||||
return { artifact: null, sourceEntries: undefined };
|
||||
|
||||
// Har files a live, make a snapshot before returning the resulting entries.
|
||||
const networkFile = path.join(state.networkFile, '..', createGuid());
|
||||
@ -218,19 +218,27 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha
|
||||
for (const sha1 of new Set([...state.traceSha1s, ...state.networkSha1s]))
|
||||
entries.push({ name: path.join('resources', sha1), value: path.join(this._resourcesDir, sha1) });
|
||||
|
||||
const sourceEntries: NameValue[] = [];
|
||||
for (const value of state.sources)
|
||||
sourceEntries.push({ name: 'resources/src@' + calculateSha1(value) + '.txt', value });
|
||||
let sourceEntries: NameValue[] | undefined;
|
||||
if (state.sources.size) {
|
||||
sourceEntries = [];
|
||||
for (const value of state.sources) {
|
||||
const entry = { name: 'resources/src@' + calculateSha1(value) + '.txt', value };
|
||||
if (params.mode === 'compressTraceAndSources')
|
||||
entries.push(entry);
|
||||
else
|
||||
sourceEntries.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
const artifact = skipCompress ? null : await this._exportZip(entries, state).catch(() => null);
|
||||
return { artifact, entries, sourceEntries };
|
||||
const artifact = await this._exportZip(entries, state).catch(() => null);
|
||||
return { artifact, sourceEntries };
|
||||
}).finally(() => {
|
||||
// Only reset trace sha1s, network resources are preserved between chunks.
|
||||
state.traceSha1s = new Set();
|
||||
state.sources = new Set();
|
||||
this._isStopping = false;
|
||||
state.recording = false;
|
||||
}) || { artifact: null, entries: [], sourceEntries: [] };
|
||||
}) || { artifact: null, sourceEntries: undefined };
|
||||
}
|
||||
|
||||
private async _exportZip(entries: NameValue[], state: RecordingState): Promise<Artifact | null> {
|
||||
|
Loading…
x
Reference in New Issue
Block a user