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