chore: make stacks rendering live in ui mode (#21728)
Co-authored-by: Max Schmitt <max@schmitt.mx>
This commit is contained in:
parent
f37f38e553
commit
ecd0f927f4
|
|
@ -71,7 +71,7 @@ export class Connection extends EventEmitter {
|
|||
private _localUtils?: LocalUtils;
|
||||
// Some connections allow resolving in-process dispatchers.
|
||||
toImpl: ((client: ChannelOwner) => any) | undefined;
|
||||
private _stackCollectors = new Set<channels.ClientSideCallMetadata[]>();
|
||||
private _tracingCount = 0;
|
||||
|
||||
constructor(localUtils?: LocalUtils) {
|
||||
super();
|
||||
|
|
@ -103,12 +103,11 @@ export class Connection extends EventEmitter {
|
|||
return this._objects.get(guid)!;
|
||||
}
|
||||
|
||||
startCollectingCallMetadata(collector: channels.ClientSideCallMetadata[]) {
|
||||
this._stackCollectors.add(collector);
|
||||
}
|
||||
|
||||
stopCollectingCallMetadata(collector: channels.ClientSideCallMetadata[]) {
|
||||
this._stackCollectors.delete(collector);
|
||||
async setIsTracing(isTracing: boolean) {
|
||||
if (isTracing)
|
||||
this._tracingCount++;
|
||||
else
|
||||
this._tracingCount--;
|
||||
}
|
||||
|
||||
async sendMessageToServer(object: ChannelOwner, type: string, method: string, params: any, stackTrace: ParsedStackTrace | null, wallTime: number | undefined): Promise<any> {
|
||||
|
|
@ -121,12 +120,11 @@ export class Connection extends EventEmitter {
|
|||
const converted = { id, guid, method, params };
|
||||
// Do not include metadata in debug logs to avoid noise.
|
||||
debugLogger.log('channel:command', converted);
|
||||
for (const collector of this._stackCollectors)
|
||||
collector.push({ stack: frames, id: id });
|
||||
const location = frames[0] ? { file: frames[0].file, line: frames[0].line, column: frames[0].column } : undefined;
|
||||
const metadata: channels.Metadata = { wallTime, apiName, location, internal: !apiName };
|
||||
this.onmessage({ ...converted, metadata });
|
||||
|
||||
if (this._tracingCount && frames && type !== 'LocalUtils')
|
||||
this._localUtils?._channel.addStackToTracingNoReply({ callData: { stack: frames, id } }).catch(() => {});
|
||||
return await new Promise((resolve, reject) => this._callbacks.set(id, { resolve, reject, stackTrace, type, method }));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ import { ChannelOwner } from './channelOwner';
|
|||
|
||||
export class Tracing extends ChannelOwner<channels.TracingChannel> implements api.Tracing {
|
||||
private _includeSources = false;
|
||||
private _metadataCollector: channels.ClientSideCallMetadata[] = [];
|
||||
_tracesDir: string | undefined;
|
||||
private _stacksId: string | undefined;
|
||||
private _isTracing = false;
|
||||
|
||||
static from(channel: channels.TracingChannel): Tracing {
|
||||
return (channel as any)._object;
|
||||
|
|
@ -34,18 +35,26 @@ export class Tracing extends ChannelOwner<channels.TracingChannel> implements ap
|
|||
|
||||
async start(options: { name?: string, title?: string, snapshots?: boolean, screenshots?: boolean, sources?: boolean } = {}) {
|
||||
this._includeSources = !!options.sources;
|
||||
await this._wrapApiCall(async () => {
|
||||
const traceName = await this._wrapApiCall(async () => {
|
||||
await this._channel.tracingStart(options);
|
||||
await this._channel.tracingStartChunk({ name: options.name, title: options.title });
|
||||
const response = await this._channel.tracingStartChunk({ name: options.name, title: options.title });
|
||||
return response.traceName;
|
||||
});
|
||||
this._metadataCollector = [];
|
||||
this._connection.startCollectingCallMetadata(this._metadataCollector);
|
||||
await this._startCollectingStacks(traceName);
|
||||
}
|
||||
|
||||
async startChunk(options: { name?: string, title?: string } = {}) {
|
||||
await this._channel.tracingStartChunk(options);
|
||||
this._metadataCollector = [];
|
||||
this._connection.startCollectingCallMetadata(this._metadataCollector);
|
||||
const { traceName } = await this._channel.tracingStartChunk(options);
|
||||
await this._startCollectingStacks(traceName);
|
||||
}
|
||||
|
||||
private async _startCollectingStacks(traceName: string) {
|
||||
if (!this._isTracing) {
|
||||
this._isTracing = true;
|
||||
this._connection.setIsTracing(true);
|
||||
}
|
||||
const result = await this._connection.localUtils()._channel.tracingStarted({ tracesDir: this._tracesDir, traceName });
|
||||
this._stacksId = result.stacksId;
|
||||
}
|
||||
|
||||
async stopChunk(options: { path?: string } = {}) {
|
||||
|
|
@ -60,12 +69,16 @@ export class Tracing extends ChannelOwner<channels.TracingChannel> implements ap
|
|||
}
|
||||
|
||||
private async _doStopChunk(filePath: string | undefined) {
|
||||
this._connection.stopCollectingCallMetadata(this._metadataCollector);
|
||||
const metadata = this._metadataCollector;
|
||||
this._metadataCollector = [];
|
||||
if (this._isTracing) {
|
||||
this._isTracing = false;
|
||||
this._connection.setIsTracing(false);
|
||||
}
|
||||
|
||||
if (!filePath) {
|
||||
await this._channel.tracingStopChunk({ mode: 'discard' });
|
||||
// Not interested in artifacts.
|
||||
await this._channel.tracingStopChunk({ mode: 'discard' });
|
||||
if (this._stacksId)
|
||||
await this._connection.localUtils()._channel.traceDiscarded({ stacksId: this._stacksId });
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -73,23 +86,24 @@ export class Tracing extends ChannelOwner<channels.TracingChannel> implements ap
|
|||
|
||||
if (isLocal) {
|
||||
const result = await this._channel.tracingStopChunk({ mode: 'entries' });
|
||||
await this._connection.localUtils()._channel.zip({ zipFile: filePath, entries: result.entries!, metadata, mode: 'write', includeSources: this._includeSources });
|
||||
await this._connection.localUtils()._channel.zip({ zipFile: filePath, entries: result.entries!, mode: 'write', stacksId: this._stacksId, includeSources: this._includeSources });
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await this._channel.tracingStopChunk({ mode: 'archive' });
|
||||
|
||||
// The artifact may be missing if the browser closed while stopping tracing.
|
||||
if (!result.artifact)
|
||||
if (!result.artifact) {
|
||||
if (this._stacksId)
|
||||
await this._connection.localUtils()._channel.traceDiscarded({ stacksId: this._stacksId });
|
||||
return;
|
||||
}
|
||||
|
||||
// Save trace to the final local file.
|
||||
const artifact = Artifact.from(result.artifact);
|
||||
await artifact.saveAs(filePath);
|
||||
await artifact.delete();
|
||||
|
||||
// Add local sources to the remote trace if necessary.
|
||||
if (metadata.length)
|
||||
await this._connection.localUtils()._channel.zip({ zipFile: filePath, entries: [], metadata, mode: 'append', includeSources: this._includeSources });
|
||||
await this._connection.localUtils()._channel.zip({ zipFile: filePath, entries: [], mode: 'append', stacksId: this._stacksId, includeSources: this._includeSources });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,8 +220,8 @@ scheme.LocalUtilsInitializer = tOptional(tObject({}));
|
|||
scheme.LocalUtilsZipParams = tObject({
|
||||
zipFile: tString,
|
||||
entries: tArray(tType('NameValue')),
|
||||
stacksId: tOptional(tString),
|
||||
mode: tEnum(['write', 'append']),
|
||||
metadata: tArray(tType('ClientSideCallMetadata')),
|
||||
includeSources: tBoolean,
|
||||
});
|
||||
scheme.LocalUtilsZipResult = tOptional(tObject({}));
|
||||
|
|
@ -268,6 +268,21 @@ scheme.LocalUtilsConnectParams = tObject({
|
|||
scheme.LocalUtilsConnectResult = tObject({
|
||||
pipe: tChannel(['JsonPipe']),
|
||||
});
|
||||
scheme.LocalUtilsTracingStartedParams = tObject({
|
||||
tracesDir: tOptional(tString),
|
||||
traceName: tString,
|
||||
});
|
||||
scheme.LocalUtilsTracingStartedResult = tObject({
|
||||
stacksId: tString,
|
||||
});
|
||||
scheme.LocalUtilsAddStackToTracingNoReplyParams = tObject({
|
||||
callData: tType('ClientSideCallMetadata'),
|
||||
});
|
||||
scheme.LocalUtilsAddStackToTracingNoReplyResult = tOptional(tObject({}));
|
||||
scheme.LocalUtilsTraceDiscardedParams = tObject({
|
||||
stacksId: tString,
|
||||
});
|
||||
scheme.LocalUtilsTraceDiscardedResult = tOptional(tObject({}));
|
||||
scheme.RootInitializer = tOptional(tObject({}));
|
||||
scheme.RootInitializeParams = tObject({
|
||||
sdkLanguage: tEnum(['javascript', 'python', 'java', 'csharp']),
|
||||
|
|
@ -2095,7 +2110,9 @@ scheme.TracingTracingStartChunkParams = tObject({
|
|||
name: tOptional(tString),
|
||||
title: tOptional(tString),
|
||||
});
|
||||
scheme.TracingTracingStartChunkResult = tOptional(tObject({}));
|
||||
scheme.TracingTracingStartChunkResult = tObject({
|
||||
traceName: tString,
|
||||
});
|
||||
scheme.TracingTracingStopChunkParams = tObject({
|
||||
mode: tEnum(['archive', 'discard', 'entries']),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,9 +17,10 @@
|
|||
import type EventEmitter from 'events';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import type * as channels from '@protocol/channels';
|
||||
import { ManualPromise } from '../../utils/manualPromise';
|
||||
import { assert, calculateSha1, createGuid } from '../../utils';
|
||||
import { assert, calculateSha1, createGuid, removeFolders } from '../../utils';
|
||||
import type { RootDispatcher } from './dispatcher';
|
||||
import { Dispatcher } from './dispatcher';
|
||||
import { yazl, yauzl } from '../../zipBundle';
|
||||
|
|
@ -42,7 +43,13 @@ import { serializeClientSideCallMetadata } from '../../utils';
|
|||
|
||||
export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.LocalUtilsChannel, RootDispatcher> implements channels.LocalUtilsChannel {
|
||||
_type_LocalUtils: boolean;
|
||||
private _harBakends = new Map<string, HarBackend>();
|
||||
private _harBackends = new Map<string, HarBackend>();
|
||||
private _stackSessions = new Map<string, {
|
||||
file: string,
|
||||
writer: Promise<void>,
|
||||
tmpDir: string | undefined,
|
||||
callStacks: channels.ClientSideCallMetadata[]
|
||||
}>();
|
||||
|
||||
constructor(scope: RootDispatcher, playwright: Playwright) {
|
||||
const localUtils = new SdkObject(playwright, 'localUtils', 'localUtils');
|
||||
|
|
@ -67,12 +74,21 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
|
|||
addFile(entry.value, entry.name);
|
||||
|
||||
// Add stacks and the sources.
|
||||
zipFile.addBuffer(Buffer.from(JSON.stringify(serializeClientSideCallMetadata(params.metadata))), 'trace.stacks');
|
||||
const stackSession = params.stacksId ? this._stackSessions.get(params.stacksId) : undefined;
|
||||
if (stackSession?.callStacks.length) {
|
||||
await stackSession.writer;
|
||||
if (process.env.PW_LIVE_TRACE_STACKS) {
|
||||
zipFile.addFile(stackSession.file, 'trace.stacks');
|
||||
} else {
|
||||
const buffer = Buffer.from(JSON.stringify(serializeClientSideCallMetadata(stackSession.callStacks)));
|
||||
zipFile.addBuffer(buffer, 'trace.stacks');
|
||||
}
|
||||
}
|
||||
|
||||
// Collect sources from stacks.
|
||||
if (params.includeSources) {
|
||||
const sourceFiles = new Set<string>();
|
||||
for (const { stack } of params.metadata) {
|
||||
for (const { stack } of stackSession?.callStacks || []) {
|
||||
if (!stack)
|
||||
continue;
|
||||
for (const { file } of stack)
|
||||
|
|
@ -88,7 +104,9 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
|
|||
zipFile.end(undefined, () => {
|
||||
zipFile.outputStream.pipe(fs.createWriteStream(params.zipFile)).on('close', () => promise.resolve());
|
||||
});
|
||||
return promise;
|
||||
await promise;
|
||||
await this._deleteStackSession(params.stacksId);
|
||||
return;
|
||||
}
|
||||
|
||||
// File already exists. Repack and add new entries.
|
||||
|
|
@ -121,7 +139,8 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
|
|||
});
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
await promise;
|
||||
await this._deleteStackSession(params.stacksId);
|
||||
}
|
||||
|
||||
async harOpen(params: channels.LocalUtilsHarOpenParams, metadata: CallMetadata): Promise<channels.LocalUtilsHarOpenResult> {
|
||||
|
|
@ -139,21 +158,21 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
|
|||
const harFile = JSON.parse(await fs.promises.readFile(params.file, 'utf-8')) as har.HARFile;
|
||||
harBackend = new HarBackend(harFile, path.dirname(params.file), null);
|
||||
}
|
||||
this._harBakends.set(harBackend.id, harBackend);
|
||||
this._harBackends.set(harBackend.id, harBackend);
|
||||
return { harId: harBackend.id };
|
||||
}
|
||||
|
||||
async harLookup(params: channels.LocalUtilsHarLookupParams, metadata: CallMetadata): Promise<channels.LocalUtilsHarLookupResult> {
|
||||
const harBackend = this._harBakends.get(params.harId);
|
||||
const harBackend = this._harBackends.get(params.harId);
|
||||
if (!harBackend)
|
||||
return { action: 'error', message: `Internal error: har was not opened` };
|
||||
return await harBackend.lookup(params.url, params.method, params.headers, params.postData, params.isNavigationRequest);
|
||||
}
|
||||
|
||||
async harClose(params: channels.LocalUtilsHarCloseParams, metadata: CallMetadata): Promise<void> {
|
||||
const harBackend = this._harBakends.get(params.harId);
|
||||
const harBackend = this._harBackends.get(params.harId);
|
||||
if (harBackend) {
|
||||
this._harBakends.delete(harBackend.id);
|
||||
this._harBackends.delete(harBackend.id);
|
||||
harBackend.dispose();
|
||||
}
|
||||
}
|
||||
|
|
@ -213,6 +232,40 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
|
|||
}, params.timeout || 0);
|
||||
}
|
||||
|
||||
async tracingStarted(params: channels.LocalUtilsTracingStartedParams, metadata?: CallMetadata | undefined): Promise<channels.LocalUtilsTracingStartedResult> {
|
||||
let tmpDir = undefined;
|
||||
if (!params.tracesDir)
|
||||
tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'playwright-tracing-'));
|
||||
const traceStacksFile = path.join(params.tracesDir || tmpDir!, params.traceName + '.stacks');
|
||||
this._stackSessions.set(traceStacksFile, { callStacks: [], file: traceStacksFile, writer: Promise.resolve(), tmpDir });
|
||||
return { stacksId: traceStacksFile };
|
||||
}
|
||||
|
||||
async traceDiscarded(params: channels.LocalUtilsTraceDiscardedParams, metadata?: CallMetadata | undefined): Promise<void> {
|
||||
await this._deleteStackSession(params.stacksId);
|
||||
}
|
||||
|
||||
async addStackToTracingNoReply(params: channels.LocalUtilsAddStackToTracingNoReplyParams, metadata?: CallMetadata | undefined): Promise<void> {
|
||||
for (const session of this._stackSessions.values()) {
|
||||
session.callStacks.push(params.callData);
|
||||
if (process.env.PW_LIVE_TRACE_STACKS) {
|
||||
session.writer = session.writer.then(() => {
|
||||
const buffer = Buffer.from(JSON.stringify(serializeClientSideCallMetadata(session.callStacks)));
|
||||
return fs.promises.writeFile(session.file, buffer);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _deleteStackSession(stacksId?: string) {
|
||||
const session = stacksId ? this._stackSessions.get(stacksId) : undefined;
|
||||
if (!session)
|
||||
return;
|
||||
await session.writer;
|
||||
if (session.tmpDir)
|
||||
await removeFolders([session.tmpDir]);
|
||||
this._stackSessions.delete(stacksId!);
|
||||
}
|
||||
}
|
||||
|
||||
const redirectStatus = [301, 302, 303, 307, 308];
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export class TracingDispatcher extends Dispatcher<Tracing, channels.TracingChann
|
|||
}
|
||||
|
||||
async tracingStartChunk(params: channels.TracingTracingStartChunkParams): Promise<channels.TracingTracingStartChunkResult> {
|
||||
await this._object.startChunk(params);
|
||||
return await this._object.startChunk(params);
|
||||
}
|
||||
|
||||
async tracingStopChunk(params: channels.TracingTracingStopChunkParams): Promise<channels.TracingTracingStopChunkResult> {
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ type RecordingState = {
|
|||
traceFile: string,
|
||||
tracesDir: string,
|
||||
resourcesDir: string,
|
||||
filesCount: number,
|
||||
chunkOrdinal: number,
|
||||
networkSha1s: Set<string>,
|
||||
traceSha1s: Set<string>,
|
||||
recording: boolean;
|
||||
|
|
@ -132,7 +132,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
|
|||
// and conflict.
|
||||
const traceName = options.name || createGuid();
|
||||
// Init the state synchronously.
|
||||
this._state = { options, traceName, traceFile: '', networkFile: '', tracesDir: '', resourcesDir: '', filesCount: 0, traceSha1s: new Set(), networkSha1s: new Set(), recording: false };
|
||||
this._state = { options, traceName, traceFile: '', networkFile: '', tracesDir: '', resourcesDir: '', chunkOrdinal: 0, traceSha1s: new Set(), networkSha1s: new Set(), recording: false };
|
||||
const state = this._state;
|
||||
|
||||
state.tracesDir = await this._createTracesDirIfNeeded();
|
||||
|
|
@ -144,7 +144,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
|
|||
this._harTracer.start();
|
||||
}
|
||||
|
||||
async startChunk(options: { name?: string, title?: string } = {}) {
|
||||
async startChunk(options: { name?: string, title?: string } = {}): Promise<{ traceName: string }> {
|
||||
if (this._state && this._state.recording)
|
||||
await this.stopChunk({ mode: 'discard' });
|
||||
|
||||
|
|
@ -154,13 +154,14 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
|
|||
throw new Error('Cannot start a trace chunk while stopping');
|
||||
|
||||
const state = this._state;
|
||||
const suffix = state.filesCount ? `-${state.filesCount}` : ``;
|
||||
state.filesCount++;
|
||||
const suffix = state.chunkOrdinal ? `-${state.chunkOrdinal}` : ``;
|
||||
state.chunkOrdinal++;
|
||||
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');
|
||||
|
|
@ -171,6 +172,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
|
|||
this._startScreencast();
|
||||
if (state.options.snapshots)
|
||||
await this._snapshotter?.start();
|
||||
return { traceName: state.traceName };
|
||||
}
|
||||
|
||||
private _startScreencast() {
|
||||
|
|
@ -194,6 +196,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
|
|||
private async _changeTraceName(state: RecordingState, name: string) {
|
||||
await this._appendTraceOperation(async () => {
|
||||
const oldNetworkFile = state.networkFile;
|
||||
state.traceName = name;
|
||||
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.
|
||||
|
|
@ -258,7 +261,8 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
|
|||
return {};
|
||||
|
||||
// Network file survives across chunks, make a snapshot before returning the resulting entries.
|
||||
const networkFile = path.join(state.networkFile, '..', createGuid());
|
||||
const suffix = state.chunkOrdinal ? `-${state.chunkOrdinal}` : ``;
|
||||
const networkFile = path.join(state.tracesDir, state.traceName + `${suffix}.network`);
|
||||
await fs.promises.copyFile(state.networkFile, networkFile);
|
||||
|
||||
const entries: NameValue[] = [];
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import * as fs from 'fs';
|
|||
import * as path from 'path';
|
||||
import type { APIRequestContext, BrowserContext, BrowserContextOptions, LaunchOptions, Page, Tracing, Video } from 'playwright-core';
|
||||
import * as playwrightLibrary from 'playwright-core';
|
||||
import { createGuid, debugMode, removeFolders, addInternalStackPrefix, mergeTraceFiles, saveTraceFile } from 'playwright-core/lib/utils';
|
||||
import { createGuid, debugMode, addInternalStackPrefix, mergeTraceFiles, saveTraceFile, removeFolders } from 'playwright-core/lib/utils';
|
||||
import type { Fixtures, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, ScreenshotMode, TestInfo, TestType, TraceMode, VideoMode } from '../types/test';
|
||||
import type { TestInfoImpl } from './worker/testInfo';
|
||||
import { rootTestType } from './common/testType';
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ class UIMode {
|
|||
|
||||
constructor(config: FullConfigInternal) {
|
||||
this._config = config;
|
||||
process.env.PW_LIVE_TRACE_STACKS = '1';
|
||||
config._internal.configCLIOverrides.forbidOnly = false;
|
||||
config._internal.configCLIOverrides.globalTimeout = 0;
|
||||
config._internal.configCLIOverrides.repeatEach = 0;
|
||||
|
|
|
|||
|
|
@ -402,16 +402,19 @@ export interface LocalUtilsChannel extends LocalUtilsEventTarget, Channel {
|
|||
harClose(params: LocalUtilsHarCloseParams, metadata?: CallMetadata): Promise<LocalUtilsHarCloseResult>;
|
||||
harUnzip(params: LocalUtilsHarUnzipParams, metadata?: CallMetadata): Promise<LocalUtilsHarUnzipResult>;
|
||||
connect(params: LocalUtilsConnectParams, metadata?: CallMetadata): Promise<LocalUtilsConnectResult>;
|
||||
tracingStarted(params: LocalUtilsTracingStartedParams, metadata?: CallMetadata): Promise<LocalUtilsTracingStartedResult>;
|
||||
addStackToTracingNoReply(params: LocalUtilsAddStackToTracingNoReplyParams, metadata?: CallMetadata): Promise<LocalUtilsAddStackToTracingNoReplyResult>;
|
||||
traceDiscarded(params: LocalUtilsTraceDiscardedParams, metadata?: CallMetadata): Promise<LocalUtilsTraceDiscardedResult>;
|
||||
}
|
||||
export type LocalUtilsZipParams = {
|
||||
zipFile: string,
|
||||
entries: NameValue[],
|
||||
stacksId?: string,
|
||||
mode: 'write' | 'append',
|
||||
metadata: ClientSideCallMetadata[],
|
||||
includeSources: boolean,
|
||||
};
|
||||
export type LocalUtilsZipOptions = {
|
||||
|
||||
stacksId?: string,
|
||||
};
|
||||
export type LocalUtilsZipResult = void;
|
||||
export type LocalUtilsHarOpenParams = {
|
||||
|
|
@ -476,6 +479,30 @@ export type LocalUtilsConnectOptions = {
|
|||
export type LocalUtilsConnectResult = {
|
||||
pipe: JsonPipeChannel,
|
||||
};
|
||||
export type LocalUtilsTracingStartedParams = {
|
||||
tracesDir?: string,
|
||||
traceName: string,
|
||||
};
|
||||
export type LocalUtilsTracingStartedOptions = {
|
||||
tracesDir?: string,
|
||||
};
|
||||
export type LocalUtilsTracingStartedResult = {
|
||||
stacksId: string,
|
||||
};
|
||||
export type LocalUtilsAddStackToTracingNoReplyParams = {
|
||||
callData: ClientSideCallMetadata,
|
||||
};
|
||||
export type LocalUtilsAddStackToTracingNoReplyOptions = {
|
||||
|
||||
};
|
||||
export type LocalUtilsAddStackToTracingNoReplyResult = void;
|
||||
export type LocalUtilsTraceDiscardedParams = {
|
||||
stacksId: string,
|
||||
};
|
||||
export type LocalUtilsTraceDiscardedOptions = {
|
||||
|
||||
};
|
||||
export type LocalUtilsTraceDiscardedResult = void;
|
||||
|
||||
export interface LocalUtilsEvents {
|
||||
}
|
||||
|
|
@ -3756,7 +3783,9 @@ export type TracingTracingStartChunkOptions = {
|
|||
name?: string,
|
||||
title?: string,
|
||||
};
|
||||
export type TracingTracingStartChunkResult = void;
|
||||
export type TracingTracingStartChunkResult = {
|
||||
traceName: string,
|
||||
};
|
||||
export type TracingTracingStopChunkParams = {
|
||||
mode: 'archive' | 'discard' | 'entries',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -500,14 +500,12 @@ LocalUtils:
|
|||
entries:
|
||||
type: array
|
||||
items: NameValue
|
||||
stacksId: string?
|
||||
mode:
|
||||
type: enum
|
||||
literals:
|
||||
- write
|
||||
- append
|
||||
metadata:
|
||||
type: array
|
||||
items: ClientSideCallMetadata
|
||||
includeSources: boolean
|
||||
|
||||
harOpen:
|
||||
|
|
@ -563,6 +561,21 @@ LocalUtils:
|
|||
returns:
|
||||
pipe: JsonPipe
|
||||
|
||||
tracingStarted:
|
||||
parameters:
|
||||
tracesDir: string?
|
||||
traceName: string
|
||||
returns:
|
||||
stacksId: string
|
||||
|
||||
addStackToTracingNoReply:
|
||||
parameters:
|
||||
callData: ClientSideCallMetadata
|
||||
|
||||
traceDiscarded:
|
||||
parameters:
|
||||
stacksId: string
|
||||
|
||||
Root:
|
||||
type: interface
|
||||
|
||||
|
|
@ -2927,6 +2940,8 @@ Tracing:
|
|||
parameters:
|
||||
name: string?
|
||||
title: string?
|
||||
returns:
|
||||
traceName: string
|
||||
|
||||
tracingStopChunk:
|
||||
parameters:
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ import { test, expect } from './playwright-test-fixtures';
|
|||
import { parseTrace } from '../config/utils';
|
||||
import fs from 'fs';
|
||||
|
||||
test.describe.configure({ mode: 'parallel' });
|
||||
|
||||
test('should stop tracing with trace: on-first-retry, when not retrying', async ({ runInlineTest }, testInfo) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.ts': `
|
||||
|
|
|
|||
Loading…
Reference in a new issue