chore(tracing): add tracing to APIRequestContext (#11502)
This commit is contained in:
parent
8a7e4f9814
commit
ab9d5a0dc4
|
|
@ -64,7 +64,7 @@ export class Browser extends ChannelOwner<channels.BrowserChannel> implements ap
|
||||||
this._contexts.add(context);
|
this._contexts.add(context);
|
||||||
context._logger = options.logger || this._logger;
|
context._logger = options.logger || this._logger;
|
||||||
context._setBrowserType(this._browserType);
|
context._setBrowserType(this._browserType);
|
||||||
context._localUtils = this._localUtils;
|
context.tracing._localUtils = this._localUtils;
|
||||||
await this._browserType._onDidCreateContext?.(context);
|
await this._browserType._onDidCreateContext?.(context);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,14 +38,12 @@ import type { BrowserType } from './browserType';
|
||||||
import { Artifact } from './artifact';
|
import { Artifact } from './artifact';
|
||||||
import { APIRequestContext } from './fetch';
|
import { APIRequestContext } from './fetch';
|
||||||
import { createInstrumentation } from './clientInstrumentation';
|
import { createInstrumentation } from './clientInstrumentation';
|
||||||
import { LocalUtils } from './localUtils';
|
|
||||||
|
|
||||||
export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel> implements api.BrowserContext {
|
export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel> implements api.BrowserContext {
|
||||||
_pages = new Set<Page>();
|
_pages = new Set<Page>();
|
||||||
private _routes: network.RouteHandler[] = [];
|
private _routes: network.RouteHandler[] = [];
|
||||||
readonly _browser: Browser | null = null;
|
readonly _browser: Browser | null = null;
|
||||||
private _browserType: BrowserType | undefined;
|
private _browserType: BrowserType | undefined;
|
||||||
_localUtils!: LocalUtils;
|
|
||||||
readonly _bindings = new Map<string, (source: structs.BindingSource, ...args: any[]) => any>();
|
readonly _bindings = new Map<string, (source: structs.BindingSource, ...args: any[]) => any>();
|
||||||
_timeoutSettings = new TimeoutSettings();
|
_timeoutSettings = new TimeoutSettings();
|
||||||
_ownerPage: Page | undefined;
|
_ownerPage: Page | undefined;
|
||||||
|
|
@ -71,7 +69,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
||||||
if (parent instanceof Browser)
|
if (parent instanceof Browser)
|
||||||
this._browser = parent;
|
this._browser = parent;
|
||||||
this._isChromium = this._browser?._name === 'chromium';
|
this._isChromium = this._browser?._name === 'chromium';
|
||||||
this.tracing = new Tracing(this);
|
this.tracing = Tracing.from(initializer.tracing);
|
||||||
this.request = APIRequestContext.from(initializer.APIRequestContext);
|
this.request = APIRequestContext.from(initializer.APIRequestContext);
|
||||||
|
|
||||||
this._channel.on('bindingCall', ({ binding }) => this._onBinding(BindingCall.from(binding)));
|
this._channel.on('bindingCall', ({ binding }) => this._onBinding(BindingCall.from(binding)));
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
|
||||||
context._options = contextParams;
|
context._options = contextParams;
|
||||||
context._logger = logger;
|
context._logger = logger;
|
||||||
context._setBrowserType(this);
|
context._setBrowserType(this);
|
||||||
context._localUtils = this._playwright._utils;
|
context.tracing._localUtils = this._playwright._utils;
|
||||||
await this._onDidCreateContext?.(context);
|
await this._onDidCreateContext?.(context);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ import { EventEmitter } from 'events';
|
||||||
import { JsonPipe } from './jsonPipe';
|
import { JsonPipe } from './jsonPipe';
|
||||||
import { APIRequestContext } from './fetch';
|
import { APIRequestContext } from './fetch';
|
||||||
import { LocalUtils } from './localUtils';
|
import { LocalUtils } from './localUtils';
|
||||||
|
import { Tracing } from './tracing';
|
||||||
|
|
||||||
class Root extends ChannelOwner<channels.RootChannel> {
|
class Root extends ChannelOwner<channels.RootChannel> {
|
||||||
constructor(connection: Connection) {
|
constructor(connection: Connection) {
|
||||||
|
|
@ -254,6 +255,9 @@ export class Connection extends EventEmitter {
|
||||||
case 'Selectors':
|
case 'Selectors':
|
||||||
result = new SelectorsOwner(parent, type, guid, initializer);
|
result = new SelectorsOwner(parent, type, guid, initializer);
|
||||||
break;
|
break;
|
||||||
|
case 'Tracing':
|
||||||
|
result = new Tracing(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
case 'WebSocket':
|
case 'WebSocket':
|
||||||
result = new WebSocket(parent, type, guid, initializer);
|
result = new WebSocket(parent, type, guid, initializer);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import { RawHeaders } from './network';
|
||||||
import { FilePayload, Headers, StorageState } from './types';
|
import { FilePayload, Headers, StorageState } from './types';
|
||||||
import { Playwright } from './playwright';
|
import { Playwright } from './playwright';
|
||||||
import { createInstrumentation } from './clientInstrumentation';
|
import { createInstrumentation } from './clientInstrumentation';
|
||||||
|
import { Tracing } from './tracing';
|
||||||
|
|
||||||
export type FetchOptions = {
|
export type FetchOptions = {
|
||||||
params?: { [key: string]: string; },
|
params?: { [key: string]: string; },
|
||||||
|
|
@ -71,6 +72,7 @@ export class APIRequest implements api.APIRequest {
|
||||||
extraHTTPHeaders: options.extraHTTPHeaders ? headersObjectToArray(options.extraHTTPHeaders) : undefined,
|
extraHTTPHeaders: options.extraHTTPHeaders ? headersObjectToArray(options.extraHTTPHeaders) : undefined,
|
||||||
storageState,
|
storageState,
|
||||||
})).request);
|
})).request);
|
||||||
|
context._tracing._localUtils = this._playwright._utils;
|
||||||
this._contexts.add(context);
|
this._contexts.add(context);
|
||||||
await this._onDidCreateContext?.(context);
|
await this._onDidCreateContext?.(context);
|
||||||
return context;
|
return context;
|
||||||
|
|
@ -79,6 +81,7 @@ export class APIRequest implements api.APIRequest {
|
||||||
|
|
||||||
export class APIRequestContext extends ChannelOwner<channels.APIRequestContextChannel> implements api.APIRequestContext {
|
export class APIRequestContext extends ChannelOwner<channels.APIRequestContextChannel> implements api.APIRequestContext {
|
||||||
private _request?: APIRequest;
|
private _request?: APIRequest;
|
||||||
|
readonly _tracing: Tracing;
|
||||||
|
|
||||||
static from(channel: channels.APIRequestContextChannel): APIRequestContext {
|
static from(channel: channels.APIRequestContextChannel): APIRequestContext {
|
||||||
return (channel as any)._object;
|
return (channel as any)._object;
|
||||||
|
|
@ -88,6 +91,7 @@ export class APIRequestContext extends ChannelOwner<channels.APIRequestContextCh
|
||||||
super(parent, type, guid, initializer, createInstrumentation());
|
super(parent, type, guid, initializer, createInstrumentation());
|
||||||
if (parent instanceof APIRequest)
|
if (parent instanceof APIRequest)
|
||||||
this._request = parent;
|
this._request = parent;
|
||||||
|
this._tracing = Tracing.from(initializer.tracing);
|
||||||
}
|
}
|
||||||
|
|
||||||
async dispose(): Promise<void> {
|
async dispose(): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -17,24 +17,29 @@
|
||||||
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';
|
||||||
import { BrowserContext } from './browserContext';
|
import { ChannelOwner } from './channelOwner';
|
||||||
|
import { LocalUtils } from './localUtils';
|
||||||
|
|
||||||
export class Tracing implements api.Tracing {
|
export class Tracing extends ChannelOwner<channels.TracingChannel> implements api.Tracing {
|
||||||
private _context: BrowserContext;
|
_localUtils!: LocalUtils;
|
||||||
|
|
||||||
constructor(channel: BrowserContext) {
|
static from(channel: channels.TracingChannel): Tracing {
|
||||||
this._context = channel;
|
return (channel as any)._object;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.TracingInitializer) {
|
||||||
|
super(parent, type, guid, initializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
async start(options: { name?: string, title?: string, snapshots?: boolean, screenshots?: boolean, sources?: boolean } = {}) {
|
async start(options: { name?: string, title?: string, snapshots?: boolean, screenshots?: boolean, sources?: boolean } = {}) {
|
||||||
await this._context._wrapApiCall(async () => {
|
await this._wrapApiCall(async () => {
|
||||||
await this._context._channel.tracingStart(options);
|
await this._channel.tracingStart(options);
|
||||||
await this._context._channel.tracingStartChunk({ title: options.title });
|
await this._channel.tracingStartChunk({ title: options.title });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async startChunk(options: { title?: string } = {}) {
|
async startChunk(options: { title?: string } = {}) {
|
||||||
await this._context._channel.tracingStartChunk(options);
|
await this._channel.tracingStartChunk(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async stopChunk(options: { path?: string } = {}) {
|
async stopChunk(options: { path?: string } = {}) {
|
||||||
|
|
@ -42,16 +47,16 @@ export class Tracing implements api.Tracing {
|
||||||
}
|
}
|
||||||
|
|
||||||
async stop(options: { path?: string } = {}) {
|
async stop(options: { path?: string } = {}) {
|
||||||
await this._context._wrapApiCall(async () => {
|
await this._wrapApiCall(async () => {
|
||||||
await this._doStopChunk(options.path);
|
await this._doStopChunk(options.path);
|
||||||
await this._context._channel.tracingStop();
|
await this._channel.tracingStop();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _doStopChunk(filePath: string | undefined) {
|
private async _doStopChunk(filePath: string | undefined) {
|
||||||
const isLocal = !this._context._connection.isRemote();
|
const isLocal = !this._connection.isRemote();
|
||||||
|
|
||||||
let mode: channels.BrowserContextTracingStopChunkParams['mode'] = 'doNotSave';
|
let mode: channels.TracingTracingStopChunkParams['mode'] = 'doNotSave';
|
||||||
if (filePath) {
|
if (filePath) {
|
||||||
if (isLocal)
|
if (isLocal)
|
||||||
mode = 'compressTraceAndSources';
|
mode = 'compressTraceAndSources';
|
||||||
|
|
@ -59,7 +64,7 @@ export class Tracing implements api.Tracing {
|
||||||
mode = 'compressTrace';
|
mode = 'compressTrace';
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await this._context._channel.tracingStopChunk({ mode });
|
const result = await this._channel.tracingStopChunk({ mode });
|
||||||
if (!filePath) {
|
if (!filePath) {
|
||||||
// Not interested in artifacts.
|
// Not interested in artifacts.
|
||||||
return;
|
return;
|
||||||
|
|
@ -76,6 +81,6 @@ export class Tracing implements api.Tracing {
|
||||||
|
|
||||||
// Add local sources to the remote trace if necessary.
|
// Add local sources to the remote trace if necessary.
|
||||||
if (result.sourceEntries?.length)
|
if (result.sourceEntries?.length)
|
||||||
await this._context._localUtils.zip(filePath, result.sourceEntries);
|
await this._localUtils.zip(filePath, result.sourceEntries);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import { CallMetadata } from '../server/instrumentation';
|
||||||
import { ArtifactDispatcher } from './artifactDispatcher';
|
import { ArtifactDispatcher } from './artifactDispatcher';
|
||||||
import { Artifact } from '../server/artifact';
|
import { Artifact } from '../server/artifact';
|
||||||
import { Request, Response } from '../server/network';
|
import { Request, Response } from '../server/network';
|
||||||
|
import { TracingDispatcher } from './tracingDispatcher';
|
||||||
|
|
||||||
export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channels.BrowserContextChannel> implements channels.BrowserContextChannel {
|
export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channels.BrowserContextChannel> implements channels.BrowserContextChannel {
|
||||||
_type_EventTarget = true;
|
_type_EventTarget = true;
|
||||||
|
|
@ -37,6 +38,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
||||||
super(scope, context, 'BrowserContext', {
|
super(scope, context, 'BrowserContext', {
|
||||||
isChromium: context._browser.options.isChromium,
|
isChromium: context._browser.options.isChromium,
|
||||||
APIRequestContext: APIRequestContextDispatcher.from(scope, context.fetchRequest),
|
APIRequestContext: APIRequestContextDispatcher.from(scope, context.fetchRequest),
|
||||||
|
tracing: TracingDispatcher.from(scope, context.tracing),
|
||||||
}, true);
|
}, true);
|
||||||
this._context = context;
|
this._context = context;
|
||||||
// Note: when launching persistent context, dispatcher is created very late,
|
// Note: when launching persistent context, dispatcher is created very late,
|
||||||
|
|
@ -189,23 +191,6 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
||||||
return { session: new CDPSessionDispatcher(this._scope, await crBrowserContext.newCDPSession((params.page ? params.page as PageDispatcher : params.frame as FrameDispatcher)._object)) };
|
return { session: new CDPSessionDispatcher(this._scope, await crBrowserContext.newCDPSession((params.page ? params.page as PageDispatcher : params.frame as FrameDispatcher)._object)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
async tracingStart(params: channels.BrowserContextTracingStartParams): Promise<channels.BrowserContextTracingStartResult> {
|
|
||||||
await this._context.tracing.start(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
async tracingStartChunk(params: channels.BrowserContextTracingStartChunkParams): Promise<channels.BrowserContextTracingStartChunkResult> {
|
|
||||||
await this._context.tracing.startChunk(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
async tracingStopChunk(params: channels.BrowserContextTracingStopChunkParams): Promise<channels.BrowserContextTracingStopChunkResult> {
|
|
||||||
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> {
|
|
||||||
await this._context.tracing.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
async harExport(params: channels.BrowserContextHarExportParams): Promise<channels.BrowserContextHarExportResult> {
|
async harExport(params: channels.BrowserContextHarExportParams): Promise<channels.BrowserContextHarExportResult> {
|
||||||
const artifact = await this._context._harRecorder?.export();
|
const artifact = await this._context._harRecorder?.export();
|
||||||
if (!artifact)
|
if (!artifact)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import { CallMetadata } from '../server/instrumentation';
|
||||||
import { Request, Response, Route, WebSocket } from '../server/network';
|
import { Request, Response, Route, WebSocket } from '../server/network';
|
||||||
import { Dispatcher, DispatcherScope, existingDispatcher, lookupNullableDispatcher } from './dispatcher';
|
import { Dispatcher, DispatcherScope, existingDispatcher, lookupNullableDispatcher } from './dispatcher';
|
||||||
import { FrameDispatcher } from './frameDispatcher';
|
import { FrameDispatcher } from './frameDispatcher';
|
||||||
|
import { TracingDispatcher } from './tracingDispatcher';
|
||||||
|
|
||||||
export class RequestDispatcher extends Dispatcher<Request, channels.RequestChannel> implements channels.RequestChannel {
|
export class RequestDispatcher extends Dispatcher<Request, channels.RequestChannel> implements channels.RequestChannel {
|
||||||
_type_Request: boolean;
|
_type_Request: boolean;
|
||||||
|
|
@ -163,7 +164,9 @@ export class APIRequestContextDispatcher extends Dispatcher<APIRequestContext, c
|
||||||
}
|
}
|
||||||
|
|
||||||
private constructor(scope: DispatcherScope, request: APIRequestContext) {
|
private constructor(scope: DispatcherScope, request: APIRequestContext) {
|
||||||
super(scope, request, 'APIRequestContext', {}, true);
|
super(scope, request, 'APIRequestContext', {
|
||||||
|
tracing: TracingDispatcher.from(scope, request.tracing()),
|
||||||
|
}, true);
|
||||||
request.once(APIRequestContext.Events.Dispose, () => {
|
request.once(APIRequestContext.Events.Dispose, () => {
|
||||||
if (!this._disposed)
|
if (!this._disposed)
|
||||||
super._dispose();
|
super._dispose();
|
||||||
|
|
@ -175,7 +178,7 @@ export class APIRequestContextDispatcher extends Dispatcher<APIRequestContext, c
|
||||||
}
|
}
|
||||||
|
|
||||||
async dispose(params?: channels.APIRequestContextDisposeParams): Promise<void> {
|
async dispose(params?: channels.APIRequestContextDisposeParams): Promise<void> {
|
||||||
this._object.dispose();
|
await this._object.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetch(params: channels.APIRequestContextFetchParams, metadata: CallMetadata): Promise<channels.APIRequestContextFetchResult> {
|
async fetch(params: channels.APIRequestContextFetchParams, metadata: CallMetadata): Promise<channels.APIRequestContextFetchResult> {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as channels from '../protocol/channels';
|
||||||
|
import { Tracing } from '../server/trace/recorder/tracing';
|
||||||
|
import { ArtifactDispatcher } from './artifactDispatcher';
|
||||||
|
import { Dispatcher, DispatcherScope, existingDispatcher } from './dispatcher';
|
||||||
|
|
||||||
|
export class TracingDispatcher extends Dispatcher<Tracing, channels.TracingChannel> implements channels.TracingChannel {
|
||||||
|
_type_Tracing = true;
|
||||||
|
|
||||||
|
static from(scope: DispatcherScope, tracing: Tracing): TracingDispatcher {
|
||||||
|
const result = existingDispatcher<TracingDispatcher>(tracing);
|
||||||
|
return result || new TracingDispatcher(scope, tracing);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(scope: DispatcherScope, tracing: Tracing) {
|
||||||
|
super(scope, tracing, 'Tracing', {}, true);
|
||||||
|
tracing.on(Tracing.Events.Dispose, () => this._dispose());
|
||||||
|
}
|
||||||
|
|
||||||
|
async tracingStart(params: channels.TracingTracingStartParams): Promise<channels.TracingTracingStartResult> {
|
||||||
|
await this._object.start(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
async tracingStartChunk(params: channels.TracingTracingStartChunkParams): Promise<channels.TracingTracingStartChunkResult> {
|
||||||
|
await this._object.startChunk(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
async tracingStopChunk(params: channels.TracingTracingStopChunkParams): Promise<channels.TracingTracingStopChunkResult> {
|
||||||
|
const { artifact, sourceEntries } = await this._object.stopChunk(params);
|
||||||
|
return { artifact: artifact ? new ArtifactDispatcher(this._scope, artifact) : undefined, sourceEntries };
|
||||||
|
}
|
||||||
|
|
||||||
|
async tracingStop(params: channels.TracingTracingStopParams): Promise<channels.TracingTracingStopResult> {
|
||||||
|
await this._object.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -32,6 +32,7 @@ export type InitializerTraits<T> =
|
||||||
T extends CDPSessionChannel ? CDPSessionInitializer :
|
T extends CDPSessionChannel ? CDPSessionInitializer :
|
||||||
T extends StreamChannel ? StreamInitializer :
|
T extends StreamChannel ? StreamInitializer :
|
||||||
T extends ArtifactChannel ? ArtifactInitializer :
|
T extends ArtifactChannel ? ArtifactInitializer :
|
||||||
|
T extends TracingChannel ? TracingInitializer :
|
||||||
T extends DialogChannel ? DialogInitializer :
|
T extends DialogChannel ? DialogInitializer :
|
||||||
T extends BindingCallChannel ? BindingCallInitializer :
|
T extends BindingCallChannel ? BindingCallInitializer :
|
||||||
T extends ConsoleMessageChannel ? ConsoleMessageInitializer :
|
T extends ConsoleMessageChannel ? ConsoleMessageInitializer :
|
||||||
|
|
@ -66,6 +67,7 @@ export type EventsTraits<T> =
|
||||||
T extends CDPSessionChannel ? CDPSessionEvents :
|
T extends CDPSessionChannel ? CDPSessionEvents :
|
||||||
T extends StreamChannel ? StreamEvents :
|
T extends StreamChannel ? StreamEvents :
|
||||||
T extends ArtifactChannel ? ArtifactEvents :
|
T extends ArtifactChannel ? ArtifactEvents :
|
||||||
|
T extends TracingChannel ? TracingEvents :
|
||||||
T extends DialogChannel ? DialogEvents :
|
T extends DialogChannel ? DialogEvents :
|
||||||
T extends BindingCallChannel ? BindingCallEvents :
|
T extends BindingCallChannel ? BindingCallEvents :
|
||||||
T extends ConsoleMessageChannel ? ConsoleMessageEvents :
|
T extends ConsoleMessageChannel ? ConsoleMessageEvents :
|
||||||
|
|
@ -100,6 +102,7 @@ export type EventTargetTraits<T> =
|
||||||
T extends CDPSessionChannel ? CDPSessionEventTarget :
|
T extends CDPSessionChannel ? CDPSessionEventTarget :
|
||||||
T extends StreamChannel ? StreamEventTarget :
|
T extends StreamChannel ? StreamEventTarget :
|
||||||
T extends ArtifactChannel ? ArtifactEventTarget :
|
T extends ArtifactChannel ? ArtifactEventTarget :
|
||||||
|
T extends TracingChannel ? TracingEventTarget :
|
||||||
T extends DialogChannel ? DialogEventTarget :
|
T extends DialogChannel ? DialogEventTarget :
|
||||||
T extends BindingCallChannel ? BindingCallEventTarget :
|
T extends BindingCallChannel ? BindingCallEventTarget :
|
||||||
T extends ConsoleMessageChannel ? ConsoleMessageEventTarget :
|
T extends ConsoleMessageChannel ? ConsoleMessageEventTarget :
|
||||||
|
|
@ -262,7 +265,9 @@ export type FormField = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------- APIRequestContext -----------
|
// ----------- APIRequestContext -----------
|
||||||
export type APIRequestContextInitializer = {};
|
export type APIRequestContextInitializer = {
|
||||||
|
tracing: TracingChannel,
|
||||||
|
};
|
||||||
export interface APIRequestContextEventTarget {
|
export interface APIRequestContextEventTarget {
|
||||||
}
|
}
|
||||||
export interface APIRequestContextChannel extends APIRequestContextEventTarget, Channel {
|
export interface APIRequestContextChannel extends APIRequestContextEventTarget, Channel {
|
||||||
|
|
@ -506,6 +511,7 @@ export type PlaywrightNewRequestParams = {
|
||||||
cookies: NetworkCookie[],
|
cookies: NetworkCookie[],
|
||||||
origins: OriginStorage[],
|
origins: OriginStorage[],
|
||||||
},
|
},
|
||||||
|
tracesDir?: string,
|
||||||
};
|
};
|
||||||
export type PlaywrightNewRequestOptions = {
|
export type PlaywrightNewRequestOptions = {
|
||||||
baseURL?: string,
|
baseURL?: string,
|
||||||
|
|
@ -527,6 +533,7 @@ export type PlaywrightNewRequestOptions = {
|
||||||
cookies: NetworkCookie[],
|
cookies: NetworkCookie[],
|
||||||
origins: OriginStorage[],
|
origins: OriginStorage[],
|
||||||
},
|
},
|
||||||
|
tracesDir?: string,
|
||||||
};
|
};
|
||||||
export type PlaywrightNewRequestResult = {
|
export type PlaywrightNewRequestResult = {
|
||||||
request: APIRequestContextChannel,
|
request: APIRequestContextChannel,
|
||||||
|
|
@ -1010,6 +1017,7 @@ export interface EventTargetEvents {
|
||||||
export type BrowserContextInitializer = {
|
export type BrowserContextInitializer = {
|
||||||
isChromium: boolean,
|
isChromium: boolean,
|
||||||
APIRequestContext: APIRequestContextChannel,
|
APIRequestContext: APIRequestContextChannel,
|
||||||
|
tracing: TracingChannel,
|
||||||
};
|
};
|
||||||
export interface BrowserContextEventTarget {
|
export interface BrowserContextEventTarget {
|
||||||
on(event: 'bindingCall', callback: (params: BrowserContextBindingCallEvent) => void): this;
|
on(event: 'bindingCall', callback: (params: BrowserContextBindingCallEvent) => void): this;
|
||||||
|
|
@ -1046,10 +1054,6 @@ export interface BrowserContextChannel extends BrowserContextEventTarget, EventT
|
||||||
pause(params?: BrowserContextPauseParams, metadata?: Metadata): Promise<BrowserContextPauseResult>;
|
pause(params?: BrowserContextPauseParams, metadata?: Metadata): Promise<BrowserContextPauseResult>;
|
||||||
recorderSupplementEnable(params: BrowserContextRecorderSupplementEnableParams, metadata?: Metadata): Promise<BrowserContextRecorderSupplementEnableResult>;
|
recorderSupplementEnable(params: BrowserContextRecorderSupplementEnableParams, metadata?: Metadata): Promise<BrowserContextRecorderSupplementEnableResult>;
|
||||||
newCDPSession(params: BrowserContextNewCDPSessionParams, metadata?: Metadata): Promise<BrowserContextNewCDPSessionResult>;
|
newCDPSession(params: BrowserContextNewCDPSessionParams, metadata?: Metadata): Promise<BrowserContextNewCDPSessionResult>;
|
||||||
tracingStart(params: BrowserContextTracingStartParams, metadata?: Metadata): Promise<BrowserContextTracingStartResult>;
|
|
||||||
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>;
|
harExport(params?: BrowserContextHarExportParams, metadata?: Metadata): Promise<BrowserContextHarExportResult>;
|
||||||
}
|
}
|
||||||
export type BrowserContextBindingCallEvent = {
|
export type BrowserContextBindingCallEvent = {
|
||||||
|
|
@ -1249,39 +1253,6 @@ export type BrowserContextNewCDPSessionOptions = {
|
||||||
export type BrowserContextNewCDPSessionResult = {
|
export type BrowserContextNewCDPSessionResult = {
|
||||||
session: CDPSessionChannel,
|
session: CDPSessionChannel,
|
||||||
};
|
};
|
||||||
export type BrowserContextTracingStartParams = {
|
|
||||||
name?: string,
|
|
||||||
snapshots?: boolean,
|
|
||||||
screenshots?: boolean,
|
|
||||||
sources?: boolean,
|
|
||||||
};
|
|
||||||
export type BrowserContextTracingStartOptions = {
|
|
||||||
name?: string,
|
|
||||||
snapshots?: boolean,
|
|
||||||
screenshots?: boolean,
|
|
||||||
sources?: boolean,
|
|
||||||
};
|
|
||||||
export type BrowserContextTracingStartResult = void;
|
|
||||||
export type BrowserContextTracingStartChunkParams = {
|
|
||||||
title?: string,
|
|
||||||
};
|
|
||||||
export type BrowserContextTracingStartChunkOptions = {
|
|
||||||
title?: string,
|
|
||||||
};
|
|
||||||
export type BrowserContextTracingStartChunkResult = void;
|
|
||||||
export type BrowserContextTracingStopChunkParams = {
|
|
||||||
mode: 'doNotSave' | 'compressTrace' | 'compressTraceAndSources',
|
|
||||||
};
|
|
||||||
export type BrowserContextTracingStopChunkOptions = {
|
|
||||||
|
|
||||||
};
|
|
||||||
export type BrowserContextTracingStopChunkResult = {
|
|
||||||
artifact?: ArtifactChannel,
|
|
||||||
sourceEntries?: NameValue[],
|
|
||||||
};
|
|
||||||
export type BrowserContextTracingStopParams = {};
|
|
||||||
export type BrowserContextTracingStopOptions = {};
|
|
||||||
export type BrowserContextTracingStopResult = void;
|
|
||||||
export type BrowserContextHarExportParams = {};
|
export type BrowserContextHarExportParams = {};
|
||||||
export type BrowserContextHarExportOptions = {};
|
export type BrowserContextHarExportOptions = {};
|
||||||
export type BrowserContextHarExportResult = {
|
export type BrowserContextHarExportResult = {
|
||||||
|
|
@ -3224,6 +3195,54 @@ export type DialogDismissResult = void;
|
||||||
export interface DialogEvents {
|
export interface DialogEvents {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------- Tracing -----------
|
||||||
|
export type TracingInitializer = {};
|
||||||
|
export interface TracingEventTarget {
|
||||||
|
}
|
||||||
|
export interface TracingChannel extends TracingEventTarget, Channel {
|
||||||
|
_type_Tracing: boolean;
|
||||||
|
tracingStart(params: TracingTracingStartParams, metadata?: Metadata): Promise<TracingTracingStartResult>;
|
||||||
|
tracingStartChunk(params: TracingTracingStartChunkParams, metadata?: Metadata): Promise<TracingTracingStartChunkResult>;
|
||||||
|
tracingStopChunk(params: TracingTracingStopChunkParams, metadata?: Metadata): Promise<TracingTracingStopChunkResult>;
|
||||||
|
tracingStop(params?: TracingTracingStopParams, metadata?: Metadata): Promise<TracingTracingStopResult>;
|
||||||
|
}
|
||||||
|
export type TracingTracingStartParams = {
|
||||||
|
name?: string,
|
||||||
|
snapshots?: boolean,
|
||||||
|
screenshots?: boolean,
|
||||||
|
sources?: boolean,
|
||||||
|
};
|
||||||
|
export type TracingTracingStartOptions = {
|
||||||
|
name?: string,
|
||||||
|
snapshots?: boolean,
|
||||||
|
screenshots?: boolean,
|
||||||
|
sources?: boolean,
|
||||||
|
};
|
||||||
|
export type TracingTracingStartResult = void;
|
||||||
|
export type TracingTracingStartChunkParams = {
|
||||||
|
title?: string,
|
||||||
|
};
|
||||||
|
export type TracingTracingStartChunkOptions = {
|
||||||
|
title?: string,
|
||||||
|
};
|
||||||
|
export type TracingTracingStartChunkResult = void;
|
||||||
|
export type TracingTracingStopChunkParams = {
|
||||||
|
mode: 'doNotSave' | 'compressTrace' | 'compressTraceAndSources',
|
||||||
|
};
|
||||||
|
export type TracingTracingStopChunkOptions = {
|
||||||
|
|
||||||
|
};
|
||||||
|
export type TracingTracingStopChunkResult = {
|
||||||
|
artifact?: ArtifactChannel,
|
||||||
|
sourceEntries?: NameValue[],
|
||||||
|
};
|
||||||
|
export type TracingTracingStopParams = {};
|
||||||
|
export type TracingTracingStopOptions = {};
|
||||||
|
export type TracingTracingStopResult = void;
|
||||||
|
|
||||||
|
export interface TracingEvents {
|
||||||
|
}
|
||||||
|
|
||||||
// ----------- Artifact -----------
|
// ----------- Artifact -----------
|
||||||
export type ArtifactInitializer = {
|
export type ArtifactInitializer = {
|
||||||
absolutePath: string,
|
absolutePath: string,
|
||||||
|
|
|
||||||
|
|
@ -230,6 +230,9 @@ FormField:
|
||||||
APIRequestContext:
|
APIRequestContext:
|
||||||
type: interface
|
type: interface
|
||||||
|
|
||||||
|
initializer:
|
||||||
|
tracing: Tracing
|
||||||
|
|
||||||
commands:
|
commands:
|
||||||
|
|
||||||
fetch:
|
fetch:
|
||||||
|
|
@ -538,6 +541,7 @@ Playwright:
|
||||||
origins:
|
origins:
|
||||||
type: array
|
type: array
|
||||||
items: OriginStorage
|
items: OriginStorage
|
||||||
|
tracesDir: string?
|
||||||
|
|
||||||
returns:
|
returns:
|
||||||
request: APIRequestContext
|
request: APIRequestContext
|
||||||
|
|
@ -707,6 +711,7 @@ BrowserContext:
|
||||||
initializer:
|
initializer:
|
||||||
isChromium: boolean
|
isChromium: boolean
|
||||||
APIRequestContext: APIRequestContext
|
APIRequestContext: APIRequestContext
|
||||||
|
tracing: Tracing
|
||||||
|
|
||||||
commands:
|
commands:
|
||||||
|
|
||||||
|
|
@ -822,34 +827,6 @@ BrowserContext:
|
||||||
returns:
|
returns:
|
||||||
session: CDPSession
|
session: CDPSession
|
||||||
|
|
||||||
tracingStart:
|
|
||||||
parameters:
|
|
||||||
name: string?
|
|
||||||
snapshots: boolean?
|
|
||||||
screenshots: boolean?
|
|
||||||
sources: boolean?
|
|
||||||
|
|
||||||
tracingStartChunk:
|
|
||||||
parameters:
|
|
||||||
title: string?
|
|
||||||
|
|
||||||
tracingStopChunk:
|
|
||||||
parameters:
|
|
||||||
mode:
|
|
||||||
type: enum
|
|
||||||
literals:
|
|
||||||
- doNotSave
|
|
||||||
- compressTrace
|
|
||||||
- compressTraceAndSources
|
|
||||||
returns:
|
|
||||||
# The artifact may be missing if the browser closes while tracing is beeing stopped.
|
|
||||||
artifact: Artifact?
|
|
||||||
sourceEntries:
|
|
||||||
type: array?
|
|
||||||
items: NameValue
|
|
||||||
|
|
||||||
tracingStop:
|
|
||||||
|
|
||||||
harExport:
|
harExport:
|
||||||
returns:
|
returns:
|
||||||
artifact: Artifact
|
artifact: Artifact
|
||||||
|
|
@ -2530,6 +2507,39 @@ Dialog:
|
||||||
dismiss:
|
dismiss:
|
||||||
|
|
||||||
|
|
||||||
|
Tracing:
|
||||||
|
type: interface
|
||||||
|
|
||||||
|
commands:
|
||||||
|
|
||||||
|
tracingStart:
|
||||||
|
parameters:
|
||||||
|
name: string?
|
||||||
|
snapshots: boolean?
|
||||||
|
screenshots: boolean?
|
||||||
|
sources: boolean?
|
||||||
|
|
||||||
|
tracingStartChunk:
|
||||||
|
parameters:
|
||||||
|
title: string?
|
||||||
|
|
||||||
|
tracingStopChunk:
|
||||||
|
parameters:
|
||||||
|
mode:
|
||||||
|
type: enum
|
||||||
|
literals:
|
||||||
|
- doNotSave
|
||||||
|
- compressTrace
|
||||||
|
- compressTraceAndSources
|
||||||
|
returns:
|
||||||
|
# The artifact may be missing if the browser closes while tracing is beeing stopped.
|
||||||
|
artifact: Artifact?
|
||||||
|
sourceEntries:
|
||||||
|
type: array?
|
||||||
|
items: NameValue
|
||||||
|
|
||||||
|
tracingStop:
|
||||||
|
|
||||||
|
|
||||||
Artifact:
|
Artifact:
|
||||||
type: interface
|
type: interface
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||||
cookies: tArray(tType('NetworkCookie')),
|
cookies: tArray(tType('NetworkCookie')),
|
||||||
origins: tArray(tType('OriginStorage')),
|
origins: tArray(tType('OriginStorage')),
|
||||||
})),
|
})),
|
||||||
|
tracesDir: tOptional(tString),
|
||||||
});
|
});
|
||||||
scheme.PlaywrightHideHighlightParams = tOptional(tObject({}));
|
scheme.PlaywrightHideHighlightParams = tOptional(tObject({}));
|
||||||
scheme.SelectorsRegisterParams = tObject({
|
scheme.SelectorsRegisterParams = tObject({
|
||||||
|
|
@ -500,19 +501,6 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||||
page: tOptional(tChannel('Page')),
|
page: tOptional(tChannel('Page')),
|
||||||
frame: tOptional(tChannel('Frame')),
|
frame: tOptional(tChannel('Frame')),
|
||||||
});
|
});
|
||||||
scheme.BrowserContextTracingStartParams = tObject({
|
|
||||||
name: tOptional(tString),
|
|
||||||
snapshots: tOptional(tBoolean),
|
|
||||||
screenshots: tOptional(tBoolean),
|
|
||||||
sources: tOptional(tBoolean),
|
|
||||||
});
|
|
||||||
scheme.BrowserContextTracingStartChunkParams = tObject({
|
|
||||||
title: tOptional(tString),
|
|
||||||
});
|
|
||||||
scheme.BrowserContextTracingStopChunkParams = tObject({
|
|
||||||
mode: tEnum(['doNotSave', 'compressTrace', 'compressTraceAndSources']),
|
|
||||||
});
|
|
||||||
scheme.BrowserContextTracingStopParams = tOptional(tObject({}));
|
|
||||||
scheme.BrowserContextHarExportParams = tOptional(tObject({}));
|
scheme.BrowserContextHarExportParams = tOptional(tObject({}));
|
||||||
scheme.PageSetDefaultNavigationTimeoutNoReplyParams = tObject({
|
scheme.PageSetDefaultNavigationTimeoutNoReplyParams = tObject({
|
||||||
timeout: tOptional(tNumber),
|
timeout: tOptional(tNumber),
|
||||||
|
|
@ -1167,6 +1155,19 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||||
promptText: tOptional(tString),
|
promptText: tOptional(tString),
|
||||||
});
|
});
|
||||||
scheme.DialogDismissParams = tOptional(tObject({}));
|
scheme.DialogDismissParams = tOptional(tObject({}));
|
||||||
|
scheme.TracingTracingStartParams = tObject({
|
||||||
|
name: tOptional(tString),
|
||||||
|
snapshots: tOptional(tBoolean),
|
||||||
|
screenshots: tOptional(tBoolean),
|
||||||
|
sources: tOptional(tBoolean),
|
||||||
|
});
|
||||||
|
scheme.TracingTracingStartChunkParams = tObject({
|
||||||
|
title: tOptional(tString),
|
||||||
|
});
|
||||||
|
scheme.TracingTracingStopChunkParams = tObject({
|
||||||
|
mode: tEnum(['doNotSave', 'compressTrace', 'compressTraceAndSources']),
|
||||||
|
});
|
||||||
|
scheme.TracingTracingStopParams = tOptional(tObject({}));
|
||||||
scheme.ArtifactPathAfterFinishedParams = tOptional(tObject({}));
|
scheme.ArtifactPathAfterFinishedParams = tOptional(tObject({}));
|
||||||
scheme.ArtifactSaveAsParams = tObject({
|
scheme.ArtifactSaveAsParams = tObject({
|
||||||
path: tString,
|
path: tString,
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ export abstract class BrowserContext extends SdkObject {
|
||||||
if (this._options.recordHar)
|
if (this._options.recordHar)
|
||||||
this._harRecorder = new HarRecorder(this, { ...this._options.recordHar, path: path.join(this._browser.options.artifactsDir, `${createGuid()}.har`) });
|
this._harRecorder = new HarRecorder(this, { ...this._options.recordHar, path: path.join(this._browser.options.artifactsDir, `${createGuid()}.har`) });
|
||||||
|
|
||||||
this.tracing = new Tracing(this);
|
this.tracing = new Tracing(this, browser.options.tracesDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
isPersistentContext(): boolean {
|
isPersistentContext(): boolean {
|
||||||
|
|
@ -138,6 +138,7 @@ export abstract class BrowserContext extends SdkObject {
|
||||||
this._closedStatus = 'closed';
|
this._closedStatus = 'closed';
|
||||||
this._deleteAllDownloads();
|
this._deleteAllDownloads();
|
||||||
this._downloads.clear();
|
this._downloads.clear();
|
||||||
|
this.tracing.dispose();
|
||||||
if (this._isPersistentContext)
|
if (this._isPersistentContext)
|
||||||
this._onClosePersistent();
|
this._onClosePersistent();
|
||||||
this._closePromiseFulfill!(new Error('Context closed'));
|
this._closePromiseFulfill!(new Error('Context closed'));
|
||||||
|
|
@ -283,7 +284,7 @@ export abstract class BrowserContext extends SdkObject {
|
||||||
this._closedStatus = 'closing';
|
this._closedStatus = 'closing';
|
||||||
|
|
||||||
await this._harRecorder?.flush();
|
await this._harRecorder?.flush();
|
||||||
await this.tracing.dispose();
|
await this.tracing.flush();
|
||||||
|
|
||||||
// Cleanup.
|
// Cleanup.
|
||||||
const promises: Promise<void>[] = [];
|
const promises: Promise<void>[] = [];
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
import * as https from 'https';
|
import * as https from 'https';
|
||||||
import { HttpsProxyAgent } from 'https-proxy-agent';
|
import { HttpsProxyAgent } from 'https-proxy-agent';
|
||||||
import { Progress, ProgressController } from './progress';
|
|
||||||
import { SocksProxyAgent } from 'socks-proxy-agent';
|
import { SocksProxyAgent } from 'socks-proxy-agent';
|
||||||
import { pipeline, Readable, Transform } from 'stream';
|
import { pipeline, Readable, Transform } from 'stream';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
|
|
@ -31,6 +30,8 @@ import { CookieStore, domainMatches } from './cookieStore';
|
||||||
import { MultipartFormData } from './formData';
|
import { MultipartFormData } from './formData';
|
||||||
import { CallMetadata, SdkObject } from './instrumentation';
|
import { CallMetadata, SdkObject } from './instrumentation';
|
||||||
import { Playwright } from './playwright';
|
import { Playwright } from './playwright';
|
||||||
|
import { Progress, ProgressController } from './progress';
|
||||||
|
import { Tracing } from './trace/recorder/tracing';
|
||||||
import * as types from './types';
|
import * as types from './types';
|
||||||
import { HeadersArray, ProxySettings } from './types';
|
import { HeadersArray, ProxySettings } from './types';
|
||||||
|
|
||||||
|
|
@ -101,7 +102,9 @@ export abstract class APIRequestContext extends SdkObject {
|
||||||
this.fetchLog.delete(fetchUid);
|
this.fetchLog.delete(fetchUid);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract dispose(): void;
|
abstract tracing(): Tracing;
|
||||||
|
|
||||||
|
abstract dispose(): Promise<void>;
|
||||||
|
|
||||||
abstract _defaultOptions(): FetchRequestOptions;
|
abstract _defaultOptions(): FetchRequestOptions;
|
||||||
abstract _addCookies(cookies: types.NetworkCookie[]): Promise<void>;
|
abstract _addCookies(cookies: types.NetworkCookie[]): Promise<void>;
|
||||||
|
|
@ -404,7 +407,11 @@ export class BrowserContextAPIRequestContext extends APIRequestContext {
|
||||||
context.once(BrowserContext.Events.Close, () => this._disposeImpl());
|
context.once(BrowserContext.Events.Close, () => this._disposeImpl());
|
||||||
}
|
}
|
||||||
|
|
||||||
override dispose() {
|
override tracing() {
|
||||||
|
return this._context.tracing;
|
||||||
|
}
|
||||||
|
|
||||||
|
override async dispose() {
|
||||||
this.fetchResponses.clear();
|
this.fetchResponses.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -438,9 +445,11 @@ export class GlobalAPIRequestContext extends APIRequestContext {
|
||||||
private readonly _cookieStore: CookieStore = new CookieStore();
|
private readonly _cookieStore: CookieStore = new CookieStore();
|
||||||
private readonly _options: FetchRequestOptions;
|
private readonly _options: FetchRequestOptions;
|
||||||
private readonly _origins: channels.OriginStorage[] | undefined;
|
private readonly _origins: channels.OriginStorage[] | undefined;
|
||||||
|
private readonly _tracing: Tracing;
|
||||||
|
|
||||||
constructor(playwright: Playwright, options: channels.PlaywrightNewRequestOptions) {
|
constructor(playwright: Playwright, options: channels.PlaywrightNewRequestOptions) {
|
||||||
super(playwright);
|
super(playwright);
|
||||||
|
this.attribution.context = this;
|
||||||
const timeoutSettings = new TimeoutSettings();
|
const timeoutSettings = new TimeoutSettings();
|
||||||
if (options.timeout !== undefined)
|
if (options.timeout !== undefined)
|
||||||
timeoutSettings.setDefaultTimeout(options.timeout);
|
timeoutSettings.setDefaultTimeout(options.timeout);
|
||||||
|
|
@ -464,10 +473,17 @@ export class GlobalAPIRequestContext extends APIRequestContext {
|
||||||
proxy,
|
proxy,
|
||||||
timeoutSettings,
|
timeoutSettings,
|
||||||
};
|
};
|
||||||
|
this._tracing = new Tracing(this, options.tracesDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
override dispose() {
|
override tracing() {
|
||||||
|
return this._tracing;
|
||||||
|
}
|
||||||
|
|
||||||
|
override async dispose() {
|
||||||
|
await this._tracing.flush();
|
||||||
|
await this._tracing.deleteTmpTracesDir();
|
||||||
|
this._tracing.dispose();
|
||||||
this._disposeImpl();
|
this._disposeImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { createGuid } from '../utils/utils';
|
import { createGuid } from '../utils/utils';
|
||||||
|
import type { APIRequestContext } from './fetch';
|
||||||
import type { Browser } from './browser';
|
import type { Browser } from './browser';
|
||||||
import type { BrowserContext } from './browserContext';
|
import type { BrowserContext } from './browserContext';
|
||||||
import type { BrowserType } from './browserType';
|
import type { BrowserType } from './browserType';
|
||||||
|
|
@ -27,7 +28,7 @@ export type Attribution = {
|
||||||
isInternal: boolean,
|
isInternal: boolean,
|
||||||
browserType?: BrowserType;
|
browserType?: BrowserType;
|
||||||
browser?: Browser;
|
browser?: Browser;
|
||||||
context?: BrowserContext;
|
context?: BrowserContext | APIRequestContext;
|
||||||
page?: Page;
|
page?: Page;
|
||||||
frame?: Frame;
|
frame?: Frame;
|
||||||
};
|
};
|
||||||
|
|
@ -50,7 +51,7 @@ export class SdkObject extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Instrumentation {
|
export interface Instrumentation {
|
||||||
addListener(listener: InstrumentationListener, context: BrowserContext | null): void;
|
addListener(listener: InstrumentationListener, context: BrowserContext | APIRequestContext | null): void;
|
||||||
removeListener(listener: InstrumentationListener): void;
|
removeListener(listener: InstrumentationListener): void;
|
||||||
onBeforeCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void>;
|
onBeforeCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void>;
|
||||||
onBeforeInputAction(sdkObject: SdkObject, metadata: CallMetadata, element: ElementHandle): Promise<void>;
|
onBeforeInputAction(sdkObject: SdkObject, metadata: CallMetadata, element: ElementHandle): Promise<void>;
|
||||||
|
|
@ -72,11 +73,11 @@ export interface InstrumentationListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createInstrumentation(): Instrumentation {
|
export function createInstrumentation(): Instrumentation {
|
||||||
const listeners = new Map<InstrumentationListener, BrowserContext | null>();
|
const listeners = new Map<InstrumentationListener, BrowserContext | APIRequestContext | null>();
|
||||||
return new Proxy({}, {
|
return new Proxy({}, {
|
||||||
get: (obj: any, prop: string) => {
|
get: (obj: any, prop: string) => {
|
||||||
if (prop === 'addListener')
|
if (prop === 'addListener')
|
||||||
return (listener: InstrumentationListener, context: BrowserContext | null) => listeners.set(listener, context);
|
return (listener: InstrumentationListener, context: BrowserContext | APIRequestContext | null) => listeners.set(listener, context);
|
||||||
if (prop === 'removeListener')
|
if (prop === 'removeListener')
|
||||||
return (listener: InstrumentationListener) => listeners.delete(listener);
|
return (listener: InstrumentationListener) => listeners.delete(listener);
|
||||||
if (!prop.startsWith('on'))
|
if (!prop.startsWith('on'))
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
import { APIRequestContext } from '../../fetch';
|
||||||
import { Artifact } from '../../artifact';
|
import { Artifact } from '../../artifact';
|
||||||
import { BrowserContext } from '../../browserContext';
|
import { BrowserContext } from '../../browserContext';
|
||||||
import * as har from './har';
|
import * as har from './har';
|
||||||
|
|
@ -32,7 +33,7 @@ export class HarRecorder {
|
||||||
private _tracer: HarTracer;
|
private _tracer: HarTracer;
|
||||||
private _entries: har.Entry[] = [];
|
private _entries: har.Entry[] = [];
|
||||||
|
|
||||||
constructor(context: BrowserContext, options: HarOptions) {
|
constructor(context: BrowserContext | APIRequestContext, options: HarOptions) {
|
||||||
this._artifact = new Artifact(context, options.path);
|
this._artifact = new Artifact(context, options.path);
|
||||||
this._options = options;
|
this._options = options;
|
||||||
this._tracer = new HarTracer(context, this, {
|
this._tracer = new HarTracer(context, this, {
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ type HarTracerOptions = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export class HarTracer {
|
export class HarTracer {
|
||||||
private _context: BrowserContext;
|
private _context: BrowserContext | APIRequestContext;
|
||||||
private _barrierPromises = new Set<Promise<void>>();
|
private _barrierPromises = new Set<Promise<void>>();
|
||||||
private _delegate: HarTracerDelegate;
|
private _delegate: HarTracerDelegate;
|
||||||
private _options: HarTracerOptions;
|
private _options: HarTracerOptions;
|
||||||
|
|
@ -49,7 +49,7 @@ export class HarTracer {
|
||||||
private _started = false;
|
private _started = false;
|
||||||
private _entrySymbol: symbol;
|
private _entrySymbol: symbol;
|
||||||
|
|
||||||
constructor(context: BrowserContext, delegate: HarTracerDelegate, options: HarTracerOptions) {
|
constructor(context: BrowserContext | APIRequestContext, delegate: HarTracerDelegate, options: HarTracerOptions) {
|
||||||
this._context = context;
|
this._context = context;
|
||||||
this._delegate = delegate;
|
this._delegate = delegate;
|
||||||
this._options = options;
|
this._options = options;
|
||||||
|
|
@ -60,15 +60,20 @@ export class HarTracer {
|
||||||
if (this._started)
|
if (this._started)
|
||||||
return;
|
return;
|
||||||
this._started = true;
|
this._started = true;
|
||||||
|
const apiRequest = this._context instanceof APIRequestContext ? this._context : this._context.fetchRequest;
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
|
eventsHelper.addEventListener(apiRequest, APIRequestContext.Events.Request, (event: APIRequestEvent) => this._onAPIRequest(event)),
|
||||||
|
eventsHelper.addEventListener(apiRequest, APIRequestContext.Events.RequestFinished, (event: APIRequestFinishedEvent) => this._onAPIRequestFinished(event)),
|
||||||
|
];
|
||||||
|
if (this._context instanceof BrowserContext) {
|
||||||
|
this._eventListeners.push(
|
||||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.Page, (page: Page) => this._ensurePageEntry(page)),
|
eventsHelper.addEventListener(this._context, BrowserContext.Events.Page, (page: Page) => this._ensurePageEntry(page)),
|
||||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.Request, (request: network.Request) => this._onRequest(request)),
|
eventsHelper.addEventListener(this._context, BrowserContext.Events.Request, (request: network.Request) => this._onRequest(request)),
|
||||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestFinished, ({ request, response }) => this._onRequestFinished(request, response).catch(() => {})),
|
eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestFinished, ({ request, response }) => this._onRequestFinished(request, response).catch(() => {})),
|
||||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestFailed, request => this._onRequestFailed(request)),
|
eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestFailed, request => this._onRequestFailed(request)),
|
||||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.Response, (response: network.Response) => this._onResponse(response)),
|
eventsHelper.addEventListener(this._context, BrowserContext.Events.Response, (response: network.Response) => this._onResponse(response)));
|
||||||
eventsHelper.addEventListener(this._context.fetchRequest, APIRequestContext.Events.Request, (event: APIRequestEvent) => this._onAPIRequest(event)),
|
}
|
||||||
eventsHelper.addEventListener(this._context.fetchRequest, APIRequestContext.Events.RequestFinished, (event: APIRequestFinishedEvent) => this._onAPIRequestFinished(event)),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _entryForRequest(request: network.Request | APIRequestEvent): har.Entry | undefined {
|
private _entryForRequest(request: network.Request | APIRequestEvent): har.Entry | undefined {
|
||||||
|
|
@ -370,6 +375,7 @@ export class HarTracer {
|
||||||
eventsHelper.removeEventListeners(this._eventListeners);
|
eventsHelper.removeEventListeners(this._eventListeners);
|
||||||
this._barrierPromises.clear();
|
this._barrierPromises.clear();
|
||||||
|
|
||||||
|
const context = this._context instanceof BrowserContext ? this._context : undefined;
|
||||||
const log: har.Log = {
|
const log: har.Log = {
|
||||||
version: '1.2',
|
version: '1.2',
|
||||||
creator: {
|
creator: {
|
||||||
|
|
@ -377,8 +383,8 @@ export class HarTracer {
|
||||||
version: require('../../../../package.json')['version'],
|
version: require('../../../../package.json')['version'],
|
||||||
},
|
},
|
||||||
browser: {
|
browser: {
|
||||||
name: this._context._browser.options.name,
|
name: context?._browser.options.name || '',
|
||||||
version: this._context._browser.version()
|
version: context?._browser.version() || ''
|
||||||
},
|
},
|
||||||
pages: Array.from(this._pageEntries.values()),
|
pages: Array.from(this._pageEntries.values()),
|
||||||
entries: [],
|
entries: [],
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,14 @@
|
||||||
|
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
import { APIRequestContext } from '../../fetch';
|
||||||
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, BrowserContextTracingStopChunkParams } from '../../../protocol/channels';
|
import { commandsWithTracingSnapshots, TracingTracingStopChunkParams } 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 { assert, calculateSha1, createGuid, mkdirIfNeeded, monotonicTime, removeFolders } from '../../../utils/utils';
|
||||||
import { Artifact } from '../../artifact';
|
import { Artifact } from '../../artifact';
|
||||||
import { BrowserContext } from '../../browserContext';
|
import { BrowserContext } from '../../browserContext';
|
||||||
import { ElementHandle } from '../../dom';
|
import { ElementHandle } from '../../dom';
|
||||||
|
|
@ -47,6 +48,8 @@ type RecordingState = {
|
||||||
traceName: string,
|
traceName: string,
|
||||||
networkFile: string,
|
networkFile: string,
|
||||||
traceFile: string,
|
traceFile: string,
|
||||||
|
tracesDir: string,
|
||||||
|
resourcesDir: string,
|
||||||
filesCount: number,
|
filesCount: number,
|
||||||
networkSha1s: Set<string>,
|
networkSha1s: Set<string>,
|
||||||
traceSha1s: Set<string>,
|
traceSha1s: Set<string>,
|
||||||
|
|
@ -56,25 +59,28 @@ type RecordingState = {
|
||||||
|
|
||||||
const kScreencastOptions = { width: 800, height: 600, quality: 90 };
|
const kScreencastOptions = { width: 800, height: 600, quality: 90 };
|
||||||
|
|
||||||
export class Tracing implements InstrumentationListener, SnapshotterDelegate, HarTracerDelegate {
|
export class Tracing extends SdkObject implements InstrumentationListener, SnapshotterDelegate, HarTracerDelegate {
|
||||||
|
static Events = {
|
||||||
|
Dispose: 'dispose',
|
||||||
|
};
|
||||||
|
|
||||||
private _writeChain = Promise.resolve();
|
private _writeChain = Promise.resolve();
|
||||||
private _snapshotter: Snapshotter;
|
private _snapshotter?: Snapshotter;
|
||||||
private _harTracer: HarTracer;
|
private _harTracer: HarTracer;
|
||||||
private _screencastListeners: RegisteredListener[] = [];
|
private _screencastListeners: RegisteredListener[] = [];
|
||||||
private _pendingCalls = new Map<string, { sdkObject: SdkObject, metadata: CallMetadata, beforeSnapshot: Promise<void>, actionSnapshot?: Promise<void>, afterSnapshot?: Promise<void> }>();
|
private _pendingCalls = new Map<string, { sdkObject: SdkObject, metadata: CallMetadata, beforeSnapshot: Promise<void>, actionSnapshot?: Promise<void>, afterSnapshot?: Promise<void> }>();
|
||||||
private _context: BrowserContext;
|
private _context: BrowserContext | APIRequestContext;
|
||||||
private _resourcesDir: string;
|
|
||||||
private _state: RecordingState | undefined;
|
private _state: RecordingState | undefined;
|
||||||
private _isStopping = false;
|
private _isStopping = false;
|
||||||
private _tracesDir: string;
|
private _precreatedTracesDir: string | undefined;
|
||||||
|
private _tracesTmpDir: string | undefined;
|
||||||
private _allResources = new Set<string>();
|
private _allResources = new Set<string>();
|
||||||
private _contextCreatedEvent: trace.ContextCreatedTraceEvent;
|
private _contextCreatedEvent: trace.ContextCreatedTraceEvent;
|
||||||
|
|
||||||
constructor(context: BrowserContext) {
|
constructor(context: BrowserContext | APIRequestContext, tracesDir: string | undefined) {
|
||||||
|
super(context, 'Tracing');
|
||||||
this._context = context;
|
this._context = context;
|
||||||
this._tracesDir = context._browser.options.tracesDir;
|
this._precreatedTracesDir = tracesDir;
|
||||||
this._resourcesDir = path.join(this._tracesDir, 'resources');
|
|
||||||
this._snapshotter = new Snapshotter(context, this);
|
|
||||||
this._harTracer = new HarTracer(context, this, {
|
this._harTracer = new HarTracer(context, this, {
|
||||||
content: 'sha1',
|
content: 'sha1',
|
||||||
waitForContentOnStop: false,
|
waitForContentOnStop: false,
|
||||||
|
|
@ -83,14 +89,20 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha
|
||||||
this._contextCreatedEvent = {
|
this._contextCreatedEvent = {
|
||||||
version: VERSION,
|
version: VERSION,
|
||||||
type: 'context-options',
|
type: 'context-options',
|
||||||
browserName: this._context._browser.options.name,
|
browserName: '',
|
||||||
options: this._context._options,
|
options: {},
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
wallTime: 0,
|
wallTime: 0,
|
||||||
};
|
};
|
||||||
|
if (context instanceof BrowserContext) {
|
||||||
|
this._snapshotter = new Snapshotter(context, this);
|
||||||
|
assert(tracesDir, 'tracesDir must be specified for BrowserContext');
|
||||||
|
this._contextCreatedEvent.browserName = context._browser.options.name;
|
||||||
|
this._contextCreatedEvent.options = context._options;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
start(options: TracerOptions) {
|
async start(options: TracerOptions) {
|
||||||
if (this._isStopping)
|
if (this._isStopping)
|
||||||
throw new Error('Cannot start tracing while stopping');
|
throw new Error('Cannot start tracing while stopping');
|
||||||
if (this._state) {
|
if (this._state) {
|
||||||
|
|
@ -99,14 +111,18 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha
|
||||||
throw new Error('Tracing has been already started with different options');
|
throw new Error('Tracing has been already started with different options');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: passing the same name for two contexts makes them write into a single file
|
// TODO: passing the same name for two contexts makes them write into a single file
|
||||||
// and conflict.
|
// and conflict.
|
||||||
const traceName = options.name || createGuid();
|
const traceName = options.name || createGuid();
|
||||||
const traceFile = path.join(this._tracesDir, traceName + '.trace');
|
// Init the state synchrounously.
|
||||||
const networkFile = path.join(this._tracesDir, traceName + '.network');
|
this._state = { options, traceName, traceFile: '', networkFile: '', tracesDir: '', resourcesDir: '', filesCount: 0, traceSha1s: new Set(), networkSha1s: new Set(), sources: new Set(), recording: false };
|
||||||
this._state = { options, traceName, traceFile, networkFile, filesCount: 0, traceSha1s: new Set(), networkSha1s: new Set(), sources: new Set(), recording: false };
|
const state = this._state;
|
||||||
this._writeChain = fs.promises.mkdir(this._resourcesDir, { recursive: true }).then(() => fs.promises.writeFile(networkFile, ''));
|
|
||||||
|
state.tracesDir = await this._createTracesDirIfNeeded();
|
||||||
|
state.resourcesDir = path.join(state.tracesDir, 'resources');
|
||||||
|
state.traceFile = path.join(state.tracesDir, traceName + '.trace');
|
||||||
|
state.networkFile = path.join(state.tracesDir, traceName + '.network');
|
||||||
|
this._writeChain = fs.promises.mkdir(state.resourcesDir, { recursive: true }).then(() => fs.promises.writeFile(state.networkFile, ''));
|
||||||
if (options.snapshots)
|
if (options.snapshots)
|
||||||
this._harTracer.start();
|
this._harTracer.start();
|
||||||
}
|
}
|
||||||
|
|
@ -123,7 +139,7 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha
|
||||||
const state = this._state;
|
const state = this._state;
|
||||||
const suffix = state.filesCount ? `-${state.filesCount}` : ``;
|
const suffix = state.filesCount ? `-${state.filesCount}` : ``;
|
||||||
state.filesCount++;
|
state.filesCount++;
|
||||||
state.traceFile = path.join(this._tracesDir, `${state.traceName}${suffix}.trace`);
|
state.traceFile = path.join(state.tracesDir, `${state.traceName}${suffix}.trace`);
|
||||||
state.recording = true;
|
state.recording = true;
|
||||||
|
|
||||||
this._appendTraceOperation(async () => {
|
this._appendTraceOperation(async () => {
|
||||||
|
|
@ -135,10 +151,12 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha
|
||||||
if (state.options.screenshots)
|
if (state.options.screenshots)
|
||||||
this._startScreencast();
|
this._startScreencast();
|
||||||
if (state.options.snapshots)
|
if (state.options.snapshots)
|
||||||
await this._snapshotter.start();
|
await this._snapshotter?.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _startScreencast() {
|
private _startScreencast() {
|
||||||
|
if (!(this._context instanceof BrowserContext))
|
||||||
|
return;
|
||||||
for (const page of this._context.pages())
|
for (const page of this._context.pages())
|
||||||
this._startScreencastInPage(page);
|
this._startScreencastInPage(page);
|
||||||
this._screencastListeners.push(
|
this._screencastListeners.push(
|
||||||
|
|
@ -148,6 +166,8 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha
|
||||||
|
|
||||||
private _stopScreencast() {
|
private _stopScreencast() {
|
||||||
eventsHelper.removeEventListeners(this._screencastListeners);
|
eventsHelper.removeEventListeners(this._screencastListeners);
|
||||||
|
if (!(this._context instanceof BrowserContext))
|
||||||
|
return;
|
||||||
for (const page of this._context.pages())
|
for (const page of this._context.pages())
|
||||||
page.setScreencastOptions(null);
|
page.setScreencastOptions(null);
|
||||||
}
|
}
|
||||||
|
|
@ -164,12 +184,29 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha
|
||||||
this._state = undefined;
|
this._state = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
async dispose() {
|
async deleteTmpTracesDir() {
|
||||||
this._snapshotter.dispose();
|
if (this._tracesTmpDir)
|
||||||
|
await removeFolders([this._tracesTmpDir]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _createTracesDirIfNeeded() {
|
||||||
|
if (this._precreatedTracesDir)
|
||||||
|
return this._precreatedTracesDir;
|
||||||
|
this._tracesTmpDir = await fs.promises.mkdtemp('playwright-tracing-');
|
||||||
|
return this._tracesTmpDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
async flush() {
|
||||||
|
this._snapshotter?.dispose();
|
||||||
await this._writeChain;
|
await this._writeChain;
|
||||||
}
|
}
|
||||||
|
|
||||||
async stopChunk(params: BrowserContextTracingStopChunkParams): Promise<{ artifact: Artifact | null, sourceEntries: NameValue[] | undefined }> {
|
async dispose() {
|
||||||
|
this._snapshotter?.dispose();
|
||||||
|
this.emit(Tracing.Events.Dispose);
|
||||||
|
}
|
||||||
|
|
||||||
|
async stopChunk(params: TracingTracingStopChunkParams): 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;
|
||||||
|
|
@ -200,7 +237,7 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.options.snapshots)
|
if (state.options.snapshots)
|
||||||
await this._snapshotter.stop();
|
await this._snapshotter?.stop();
|
||||||
|
|
||||||
// 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.
|
||||||
|
|
@ -216,7 +253,7 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha
|
||||||
entries.push({ name: 'trace.trace', value: state.traceFile });
|
entries.push({ name: 'trace.trace', value: state.traceFile });
|
||||||
entries.push({ name: 'trace.network', value: networkFile });
|
entries.push({ name: 'trace.network', value: networkFile });
|
||||||
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(state.resourcesDir, sha1) });
|
||||||
|
|
||||||
let sourceEntries: NameValue[] | undefined;
|
let sourceEntries: NameValue[] | undefined;
|
||||||
if (state.sources.size) {
|
if (state.sources.size) {
|
||||||
|
|
@ -260,6 +297,8 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha
|
||||||
}
|
}
|
||||||
|
|
||||||
async _captureSnapshot(name: 'before' | 'after' | 'action' | 'event', sdkObject: SdkObject, metadata: CallMetadata, element?: ElementHandle) {
|
async _captureSnapshot(name: 'before' | 'after' | 'action' | 'event', sdkObject: SdkObject, metadata: CallMetadata, element?: ElementHandle) {
|
||||||
|
if (!this._snapshotter)
|
||||||
|
return;
|
||||||
if (!sdkObject.attribution.page)
|
if (!sdkObject.attribution.page)
|
||||||
return;
|
return;
|
||||||
if (!this._snapshotter.started())
|
if (!this._snapshotter.started())
|
||||||
|
|
@ -376,8 +415,8 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha
|
||||||
if (this._allResources.has(sha1))
|
if (this._allResources.has(sha1))
|
||||||
return;
|
return;
|
||||||
this._allResources.add(sha1);
|
this._allResources.add(sha1);
|
||||||
|
const resourcePath = path.join(this._state!.resourcesDir, sha1);
|
||||||
this._appendTraceOperation(async () => {
|
this._appendTraceOperation(async () => {
|
||||||
const resourcePath = path.join(this._resourcesDir, sha1);
|
|
||||||
try {
|
try {
|
||||||
// Perhaps we've already written this resource?
|
// Perhaps we've already written this resource?
|
||||||
await fs.promises.access(resourcePath);
|
await fs.promises.access(resourcePath);
|
||||||
|
|
@ -394,7 +433,9 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha
|
||||||
let error: Error | undefined;
|
let error: Error | undefined;
|
||||||
let result: T | undefined;
|
let result: T | undefined;
|
||||||
this._writeChain = this._writeChain.then(async () => {
|
this._writeChain = this._writeChain.then(async () => {
|
||||||
if (!this._context._browser.isConnected())
|
// This check is here because closing the browser removes the tracesDir and tracing
|
||||||
|
// dies trying to archive.
|
||||||
|
if (this._context instanceof BrowserContext && !this._context._browser.isConnected())
|
||||||
return;
|
return;
|
||||||
try {
|
try {
|
||||||
result = await cb();
|
result = await cb();
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,8 @@ it('should scope context handles', async ({ browserType, server }) => {
|
||||||
{ _guid: 'request', objects: [] },
|
{ _guid: 'request', objects: [] },
|
||||||
{ _guid: 'response', objects: [] },
|
{ _guid: 'response', objects: [] },
|
||||||
] },
|
] },
|
||||||
{ _guid: 'fetchRequest', objects: [] }
|
{ _guid: 'fetchRequest', objects: [] },
|
||||||
|
{ _guid: 'Tracing', objects: [] }
|
||||||
] },
|
] },
|
||||||
] },
|
] },
|
||||||
{ _guid: 'electron', objects: [] },
|
{ _guid: 'electron', objects: [] },
|
||||||
|
|
@ -153,7 +154,8 @@ it('should scope browser handles', async ({ browserType }) => {
|
||||||
{
|
{
|
||||||
_guid: 'browser', objects: [
|
_guid: 'browser', objects: [
|
||||||
{ _guid: 'browser-context', objects: [] },
|
{ _guid: 'browser-context', objects: [] },
|
||||||
{ _guid: 'fetchRequest', objects: [] }
|
{ _guid: 'fetchRequest', objects: [] },
|
||||||
|
{ _guid: 'Tracing', objects: [] }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,7 @@ DEPS['src/server/electron/'] = [...DEPS['src/server/'], 'src/server/chromium/'];
|
||||||
|
|
||||||
DEPS['src/server/playwright.ts'] = [...DEPS['src/server/'], 'src/server/chromium/', 'src/server/webkit/', 'src/server/firefox/', 'src/server/android/', 'src/server/electron/'];
|
DEPS['src/server/playwright.ts'] = [...DEPS['src/server/'], 'src/server/chromium/', 'src/server/webkit/', 'src/server/firefox/', 'src/server/android/', 'src/server/electron/'];
|
||||||
DEPS['src/server/browserContext.ts'] = [...DEPS['src/server/'], 'src/server/trace/recorder/tracing.ts'];
|
DEPS['src/server/browserContext.ts'] = [...DEPS['src/server/'], 'src/server/trace/recorder/tracing.ts'];
|
||||||
|
DEPS['src/server/fetch.ts'] = [...DEPS['src/server/'], 'src/server/trace/recorder/tracing.ts'];
|
||||||
DEPS['src/cli/driver.ts'] = DEPS['src/inProcessFactory.ts'] = DEPS['src/browserServerImpl.ts'] = ['src/**'];
|
DEPS['src/cli/driver.ts'] = DEPS['src/inProcessFactory.ts'] = DEPS['src/browserServerImpl.ts'] = ['src/**'];
|
||||||
|
|
||||||
// Tracing is a client/server plugin, nothing should depend on it.
|
// Tracing is a client/server plugin, nothing should depend on it.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue