chore: push dispatcher guid into object, reuse it in trace (#6250)

This commit is contained in:
Pavel Feldman 2021-04-20 23:03:56 -07:00 committed by GitHub
parent 06b0619260
commit 85e2db2416
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 193 additions and 161 deletions

View File

@ -131,7 +131,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
return;
}
await this._context._setRequestInterceptor((route, request) => {
this._dispatchEvent('route', { route: new RouteDispatcher(this._scope, route), request: RequestDispatcher.from(this._scope, request) });
this._dispatchEvent('route', { route: RouteDispatcher.from(this._scope, route), request: RequestDispatcher.from(this._scope, request) });
});
}

View File

@ -17,14 +17,14 @@
import { ConsoleMessage } from '../server/console';
import * as channels from '../protocol/channels';
import { Dispatcher, DispatcherScope } from './dispatcher';
import { createHandle } from './elementHandlerDispatcher';
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
export class ConsoleMessageDispatcher extends Dispatcher<ConsoleMessage, channels.ConsoleMessageInitializer> implements channels.ConsoleMessageChannel {
constructor(scope: DispatcherScope, message: ConsoleMessage) {
super(scope, message, 'ConsoleMessage', {
type: message.type(),
text: message.text(),
args: message.args().map(a => createHandle(scope, a)),
args: message.args().map(a => ElementHandleDispatcher.fromJSHandle(scope, a)),
location: message.location(),
});
}

View File

@ -18,7 +18,7 @@ import { EventEmitter } from 'events';
import * as channels from '../protocol/channels';
import { serializeError } from '../protocol/serializers';
import { createScheme, Validator, ValidationError } from '../protocol/validator';
import { assert, createGuid, debugAssert, isUnderTest, monotonicTime } from '../utils/utils';
import { assert, debugAssert, isUnderTest, monotonicTime } from '../utils/utils';
import { tOptional } from '../protocol/validatorPrimitives';
import { kBrowserOrContextClosedError } from '../utils/errors';
import { CallMetadata, SdkObject } from '../server/instrumentation';
@ -41,7 +41,7 @@ export function lookupNullableDispatcher<DispatcherType>(object: any | null): Di
return object ? lookupDispatcher(object) : undefined;
}
export class Dispatcher<Type, Initializer> extends EventEmitter implements channels.Channel {
export class Dispatcher<Type extends { guid: string }, Initializer> extends EventEmitter implements channels.Channel {
private _connection: DispatcherConnection;
private _isScope: boolean;
// Parent is always "isScope".
@ -55,7 +55,7 @@ export class Dispatcher<Type, Initializer> extends EventEmitter implements chann
readonly _scope: Dispatcher<any, any>;
_object: Type;
constructor(parent: Dispatcher<any, any> | DispatcherConnection, object: Type, type: string, initializer: Initializer, isScope?: boolean, guid = type + '@' + createGuid()) {
constructor(parent: Dispatcher<any, any> | DispatcherConnection, object: Type, type: string, initializer: Initializer, isScope?: boolean) {
super();
this._connection = parent instanceof DispatcherConnection ? parent : parent._connection;
@ -63,6 +63,7 @@ export class Dispatcher<Type, Initializer> extends EventEmitter implements chann
this._parent = parent instanceof DispatcherConnection ? undefined : parent;
this._scope = isScope ? this : this._parent!;
const guid = object.guid;
assert(!this._connection._dispatchers.has(guid));
this._connection._dispatchers.set(guid, this);
if (this._parent) {
@ -120,9 +121,9 @@ export class Dispatcher<Type, Initializer> extends EventEmitter implements chann
}
export type DispatcherScope = Dispatcher<any, any>;
class Root extends Dispatcher<{}, {}> {
class Root extends Dispatcher<{ guid: '' }, {}> {
constructor(connection: DispatcherConnection) {
super(connection, {}, '', {}, true, '');
super(connection, { guid: '' }, '', {}, true);
}
}
@ -201,8 +202,8 @@ export class DispatcherConnection {
let callMetadata: CallMetadata = {
id,
...validMetadata,
pageId: sdkObject?.attribution.page?.uniqueId,
frameId: sdkObject?.attribution.frame?.uniqueId,
pageId: sdkObject?.attribution.page?.guid,
frameId: sdkObject?.attribution.frame?.guid,
startTime: monotonicTime(),
endTime: 0,
type: dispatcher._type,

View File

@ -20,7 +20,7 @@ import * as channels from '../protocol/channels';
import { BrowserContextDispatcher } from './browserContextDispatcher';
import { PageDispatcher } from './pageDispatcher';
import { parseArgument, serializeResult } from './jsHandleDispatcher';
import { createHandle } from './elementHandlerDispatcher';
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
export class ElectronDispatcher extends Dispatcher<Electron, channels.ElectronInitializer> implements channels.ElectronChannel {
constructor(scope: DispatcherScope, electron: Electron) {
@ -44,7 +44,7 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
electronApplication.on(ElectronApplication.Events.Window, (page: ElectronPage) => {
this._dispatchEvent('window', {
page: lookupDispatcher<PageDispatcher>(page),
browserWindow: createHandle(this._scope, page.browserWindow),
browserWindow: ElementHandleDispatcher.fromJSHandle(this._scope, page.browserWindow),
});
});
}
@ -57,7 +57,7 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
async evaluateExpressionHandle(params: channels.ElectronApplicationEvaluateExpressionHandleParams): Promise<channels.ElectronApplicationEvaluateExpressionHandleResult> {
const handle = this._object._nodeElectronHandle!;
const result = await handle.evaluateExpressionAndWaitForSignals(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg));
return { handle: createHandle(this._scope, result) };
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, result) };
}
async close(): Promise<void> {

View File

@ -17,25 +17,32 @@
import { ElementHandle } from '../server/dom';
import * as js from '../server/javascript';
import * as channels from '../protocol/channels';
import { DispatcherScope, lookupNullableDispatcher } from './dispatcher';
import { DispatcherScope, existingDispatcher, lookupNullableDispatcher } from './dispatcher';
import { JSHandleDispatcher, serializeResult, parseArgument } from './jsHandleDispatcher';
import { FrameDispatcher } from './frameDispatcher';
import { CallMetadata } from '../server/instrumentation';
export function createHandle(scope: DispatcherScope, handle: js.JSHandle): JSHandleDispatcher {
return handle.asElement() ? new ElementHandleDispatcher(scope, handle.asElement()!) : new JSHandleDispatcher(scope, handle);
}
export class ElementHandleDispatcher extends JSHandleDispatcher implements channels.ElementHandleChannel {
readonly _elementHandle: ElementHandle;
static createNullable(scope: DispatcherScope, handle: ElementHandle | null): ElementHandleDispatcher | undefined {
if (!handle)
return undefined;
return new ElementHandleDispatcher(scope, handle);
static from(scope: DispatcherScope, handle: ElementHandle): ElementHandleDispatcher {
return existingDispatcher<ElementHandleDispatcher>(handle) || new ElementHandleDispatcher(scope, handle);
}
constructor(scope: DispatcherScope, elementHandle: ElementHandle) {
static fromNullable(scope: DispatcherScope, handle: ElementHandle | null): ElementHandleDispatcher | undefined {
if (!handle)
return undefined;
return existingDispatcher<ElementHandleDispatcher>(handle) || new ElementHandleDispatcher(scope, handle);
}
static fromJSHandle(scope: DispatcherScope, handle: js.JSHandle): JSHandleDispatcher {
const result = existingDispatcher<JSHandleDispatcher>(handle);
if (result)
return result;
return handle.asElement() ? new ElementHandleDispatcher(scope, handle.asElement()!) : new JSHandleDispatcher(scope, handle);
}
private constructor(scope: DispatcherScope, elementHandle: ElementHandle) {
super(scope, elementHandle);
this._elementHandle = elementHandle;
}
@ -162,12 +169,12 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements chann
async querySelector(params: channels.ElementHandleQuerySelectorParams, metadata: CallMetadata): Promise<channels.ElementHandleQuerySelectorResult> {
const handle = await this._elementHandle.$(params.selector);
return { element: handle ? new ElementHandleDispatcher(this._scope, handle) : undefined };
return { element: ElementHandleDispatcher.fromNullable(this._scope, handle) };
}
async querySelectorAll(params: channels.ElementHandleQuerySelectorAllParams, metadata: CallMetadata): Promise<channels.ElementHandleQuerySelectorAllResult> {
const elements = await this._elementHandle.$$(params.selector);
return { elements: elements.map(e => new ElementHandleDispatcher(this._scope, e)) };
return { elements: elements.map(e => ElementHandleDispatcher.from(this._scope, e)) };
}
async evalOnSelector(params: channels.ElementHandleEvalOnSelectorParams, metadata: CallMetadata): Promise<channels.ElementHandleEvalOnSelectorResult> {
@ -183,6 +190,6 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements chann
}
async waitForSelector(params: channels.ElementHandleWaitForSelectorParams, metadata: CallMetadata): Promise<channels.ElementHandleWaitForSelectorResult> {
return { element: ElementHandleDispatcher.createNullable(this._scope, await this._elementHandle.waitForSelector(metadata, params.selector, params)) };
return { element: ElementHandleDispatcher.fromNullable(this._scope, await this._elementHandle.waitForSelector(metadata, params.selector, params)) };
}
}

View File

@ -17,7 +17,7 @@
import { Frame, NavigationEvent } from '../server/frames';
import * as channels from '../protocol/channels';
import { Dispatcher, DispatcherScope, lookupNullableDispatcher, existingDispatcher } from './dispatcher';
import { ElementHandleDispatcher, createHandle } from './elementHandlerDispatcher';
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
import { parseArgument, serializeResult } from './jsHandleDispatcher';
import { ResponseDispatcher, RequestDispatcher } from './networkDispatchers';
import { CallMetadata } from '../server/instrumentation';
@ -57,7 +57,7 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameInitializer
}
async frameElement(): Promise<channels.FrameFrameElementResult> {
return { element: new ElementHandleDispatcher(this._scope, await this._frame.frameElement()) };
return { element: ElementHandleDispatcher.from(this._scope, await this._frame.frameElement()) };
}
async evaluateExpression(params: channels.FrameEvaluateExpressionParams, metadata: CallMetadata): Promise<channels.FrameEvaluateExpressionResult> {
@ -65,11 +65,11 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameInitializer
}
async evaluateExpressionHandle(params: channels.FrameEvaluateExpressionHandleParams, metadata: CallMetadata): Promise<channels.FrameEvaluateExpressionHandleResult> {
return { handle: createHandle(this._scope, await this._frame.evaluateExpressionHandleAndWaitForSignals(params.expression, params.isFunction, parseArgument(params.arg), params.world)) };
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, await this._frame.evaluateExpressionHandleAndWaitForSignals(params.expression, params.isFunction, parseArgument(params.arg), params.world)) };
}
async waitForSelector(params: channels.FrameWaitForSelectorParams, metadata: CallMetadata): Promise<channels.FrameWaitForSelectorResult> {
return { element: ElementHandleDispatcher.createNullable(this._scope, await this._frame.waitForSelector(metadata, params.selector, params)) };
return { element: ElementHandleDispatcher.fromNullable(this._scope, await this._frame.waitForSelector(metadata, params.selector, params)) };
}
async dispatchEvent(params: channels.FrameDispatchEventParams, metadata: CallMetadata): Promise<void> {
@ -85,12 +85,12 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameInitializer
}
async querySelector(params: channels.FrameQuerySelectorParams, metadata: CallMetadata): Promise<channels.FrameQuerySelectorResult> {
return { element: ElementHandleDispatcher.createNullable(this._scope, await this._frame.$(params.selector)) };
return { element: ElementHandleDispatcher.fromNullable(this._scope, await this._frame.$(params.selector)) };
}
async querySelectorAll(params: channels.FrameQuerySelectorAllParams, metadata: CallMetadata): Promise<channels.FrameQuerySelectorAllResult> {
const elements = await this._frame.$$(params.selector);
return { elements: elements.map(e => new ElementHandleDispatcher(this._scope, e)) };
return { elements: elements.map(e => ElementHandleDispatcher.from(this._scope, e)) };
}
async content(): Promise<channels.FrameContentResult> {
@ -102,11 +102,11 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameInitializer
}
async addScriptTag(params: channels.FrameAddScriptTagParams, metadata: CallMetadata): Promise<channels.FrameAddScriptTagResult> {
return { element: new ElementHandleDispatcher(this._scope, await this._frame.addScriptTag(params)) };
return { element: ElementHandleDispatcher.from(this._scope, await this._frame.addScriptTag(params)) };
}
async addStyleTag(params: channels.FrameAddStyleTagParams, metadata: CallMetadata): Promise<channels.FrameAddStyleTagResult> {
return { element: new ElementHandleDispatcher(this._scope, await this._frame.addStyleTag(params)) };
return { element: ElementHandleDispatcher.from(this._scope, await this._frame.addStyleTag(params)) };
}
async click(params: channels.FrameClickParams, metadata: CallMetadata): Promise<void> {
@ -201,7 +201,7 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameInitializer
}
async waitForFunction(params: channels.FrameWaitForFunctionParams, metadata: CallMetadata): Promise<channels.FrameWaitForFunctionResult> {
return { handle: createHandle(this._scope, await this._frame._waitForFunctionExpression(metadata, params.expression, params.isFunction, parseArgument(params.arg), params)) };
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, await this._frame._waitForFunctionExpression(metadata, params.expression, params.isFunction, parseArgument(params.arg), params)) };
}
async title(params: channels.FrameTitleParams, metadata: CallMetadata): Promise<channels.FrameTitleResult> {

View File

@ -17,12 +17,12 @@
import * as js from '../server/javascript';
import * as channels from '../protocol/channels';
import { Dispatcher, DispatcherScope } from './dispatcher';
import { createHandle } from './elementHandlerDispatcher';
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
import { parseSerializedValue, serializeValue } from '../protocol/serializers';
export class JSHandleDispatcher extends Dispatcher<js.JSHandle, channels.JSHandleInitializer> implements channels.JSHandleChannel {
constructor(scope: DispatcherScope, jsHandle: js.JSHandle) {
protected constructor(scope: DispatcherScope, jsHandle: js.JSHandle) {
// Do not call this directly, use createHandle() instead.
super(scope, jsHandle, jsHandle.asElement() ? 'ElementHandle' : 'JSHandle', {
preview: jsHandle.toString(),
@ -36,19 +36,19 @@ export class JSHandleDispatcher extends Dispatcher<js.JSHandle, channels.JSHandl
async evaluateExpressionHandle(params: channels.JSHandleEvaluateExpressionHandleParams): Promise<channels.JSHandleEvaluateExpressionHandleResult> {
const jsHandle = await this._object.evaluateExpressionAndWaitForSignals(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg));
return { handle: createHandle(this._scope, jsHandle) };
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, jsHandle) };
}
async getProperty(params: channels.JSHandleGetPropertyParams): Promise<channels.JSHandleGetPropertyResult> {
const jsHandle = await this._object.getProperty(params.name);
return { handle: createHandle(this._scope, jsHandle) };
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, jsHandle) };
}
async getPropertyList(): Promise<channels.JSHandleGetPropertyListResult> {
const map = await this._object.getProperties();
const properties = [];
for (const [name, value] of map)
properties.push({ name, value: createHandle(this._scope, value) });
properties.push({ name, value: ElementHandleDispatcher.fromJSHandle(this._scope, value) });
return { properties };
}

View File

@ -51,7 +51,16 @@ export class RequestDispatcher extends Dispatcher<Request, channels.RequestIniti
export class ResponseDispatcher extends Dispatcher<Response, channels.ResponseInitializer> implements channels.ResponseChannel {
constructor(scope: DispatcherScope, response: Response) {
static from(scope: DispatcherScope, response: Response): ResponseDispatcher {
const result = existingDispatcher<ResponseDispatcher>(response);
return result || new ResponseDispatcher(scope, response);
}
static fromNullable(scope: DispatcherScope, response: Response | null): ResponseDispatcher | undefined {
return response ? ResponseDispatcher.from(scope, response) : undefined;
}
private constructor(scope: DispatcherScope, response: Response) {
super(scope, response, 'Response', {
// TODO: responses in popups can point to non-reported requests.
request: RequestDispatcher.from(scope, response.request()),
@ -75,7 +84,16 @@ export class ResponseDispatcher extends Dispatcher<Response, channels.ResponseIn
export class RouteDispatcher extends Dispatcher<Route, channels.RouteInitializer> implements channels.RouteChannel {
constructor(scope: DispatcherScope, route: Route) {
static from(scope: DispatcherScope, route: Route): RouteDispatcher {
const result = existingDispatcher<RouteDispatcher>(route);
return result || new RouteDispatcher(scope, route);
}
static fromNullable(scope: DispatcherScope, route: Route | null): RouteDispatcher | undefined {
return route ? RouteDispatcher.from(scope, route) : undefined;
}
private constructor(scope: DispatcherScope, route: Route) {
super(scope, route, 'Route', {
// Context route can point to a non-reported request.
request: RequestDispatcher.from(scope, route.request())

View File

@ -26,7 +26,7 @@ import { DialogDispatcher } from './dialogDispatcher';
import { FrameDispatcher } from './frameDispatcher';
import { RequestDispatcher, ResponseDispatcher, RouteDispatcher, WebSocketDispatcher } from './networkDispatchers';
import { serializeResult, parseArgument } from './jsHandleDispatcher';
import { ElementHandleDispatcher, createHandle } from './elementHandlerDispatcher';
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
import { FileChooser } from '../server/fileChooser';
import { CRCoverage } from '../server/chromium/crCoverage';
import { JSHandle } from '../server/javascript';
@ -34,6 +34,7 @@ import { CallMetadata } from '../server/instrumentation';
import { Artifact } from '../server/artifact';
import { ArtifactDispatcher } from './artifactDispatcher';
import { Download } from '../server/download';
import { createGuid } from '../utils/utils';
export class PageDispatcher extends Dispatcher<Page, channels.PageInitializer> implements channels.PageChannel {
private _page: Page;
@ -67,7 +68,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageInitializer> i
this._dispatchEvent('download', { url: download.url, suggestedFilename: download.suggestedFilename(), artifact: new ArtifactDispatcher(scope, download.artifact) });
});
this._page.on(Page.Events.FileChooser, (fileChooser: FileChooser) => this._dispatchEvent('fileChooser', {
element: new ElementHandleDispatcher(this._scope, fileChooser.element()),
element: ElementHandleDispatcher.from(this._scope, fileChooser.element()),
isMultiple: fileChooser.isMultiple()
}));
page.on(Page.Events.FrameAttached, frame => this._onFrameAttached(frame));
@ -84,7 +85,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageInitializer> i
request: RequestDispatcher.from(scope, request),
responseEndTiming: request._responseEndTiming
}));
page.on(Page.Events.Response, response => this._dispatchEvent('response', { response: new ResponseDispatcher(this._scope, response) }));
page.on(Page.Events.Response, response => this._dispatchEvent('response', { response: ResponseDispatcher.from(this._scope, response) }));
page.on(Page.Events.WebSocket, webSocket => this._dispatchEvent('webSocket', { webSocket: new WebSocketDispatcher(this._scope, webSocket) }));
page.on(Page.Events.Worker, worker => this._dispatchEvent('worker', { worker: new WorkerDispatcher(this._scope, worker) }));
page.on(Page.Events.Video, (artifact: Artifact) => this._dispatchEvent('video', { artifact: existingDispatcher<ArtifactDispatcher>(artifact) }));
@ -145,7 +146,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageInitializer> i
return;
}
await this._page._setClientRequestInterceptor((route, request) => {
this._dispatchEvent('route', { route: new RouteDispatcher(this._scope, route), request: RequestDispatcher.from(this._scope, request) });
this._dispatchEvent('route', { route: RouteDispatcher.from(this._scope, route), request: RequestDispatcher.from(this._scope, request) });
});
}
@ -263,21 +264,21 @@ export class WorkerDispatcher extends Dispatcher<Worker, channels.WorkerInitiali
}
async evaluateExpressionHandle(params: channels.WorkerEvaluateExpressionHandleParams, metadata: CallMetadata): Promise<channels.WorkerEvaluateExpressionHandleResult> {
return { handle: createHandle(this._scope, await this._object.evaluateExpressionHandle(params.expression, params.isFunction, parseArgument(params.arg))) };
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, await this._object.evaluateExpressionHandle(params.expression, params.isFunction, parseArgument(params.arg))) };
}
}
export class BindingCallDispatcher extends Dispatcher<{}, channels.BindingCallInitializer> implements channels.BindingCallChannel {
export class BindingCallDispatcher extends Dispatcher<{ guid: string }, channels.BindingCallInitializer> implements channels.BindingCallChannel {
private _resolve: ((arg: any) => void) | undefined;
private _reject: ((error: any) => void) | undefined;
private _promise: Promise<any>;
constructor(scope: DispatcherScope, name: string, needsHandle: boolean, source: { context: BrowserContext, page: Page, frame: Frame }, args: any[]) {
super(scope, {}, 'BindingCall', {
super(scope, { guid: createGuid() }, 'BindingCall', {
frame: lookupDispatcher<FrameDispatcher>(source.frame),
name,
args: needsHandle ? undefined : args.map(serializeResult),
handle: needsHandle ? createHandle(scope, args[0] as JSHandle) : undefined,
handle: needsHandle ? ElementHandleDispatcher.fromJSHandle(scope, args[0] as JSHandle) : undefined,
});
this._promise = new Promise((resolve, reject) => {
this._resolve = resolve;

View File

@ -38,6 +38,6 @@ export class PlaywrightDispatcher extends Dispatcher<Playwright, channels.Playwr
deviceDescriptors,
selectors: customSelectors || new SelectorsDispatcher(scope, playwright.selectors),
preLaunchedBrowser,
}, false, 'Playwright');
}, false);
}
}

View File

@ -17,18 +17,19 @@
import * as channels from '../protocol/channels';
import { Dispatcher, DispatcherScope } from './dispatcher';
import * as stream from 'stream';
import { createGuid } from '../utils/utils';
export class StreamDispatcher extends Dispatcher<stream.Readable, channels.StreamInitializer> implements channels.StreamChannel {
export class StreamDispatcher extends Dispatcher<{ guid: string, stream: stream.Readable }, channels.StreamInitializer> implements channels.StreamChannel {
constructor(scope: DispatcherScope, stream: stream.Readable) {
super(scope, stream, 'Stream', {});
super(scope, { guid: createGuid(), stream }, 'Stream', {});
}
async read(params: channels.StreamReadParams): Promise<channels.StreamReadResult> {
const buffer = this._object.read(Math.min(this._object.readableLength, params.size || this._object.readableLength));
const buffer = this._object.stream.read(Math.min(this._object.stream.readableLength, params.size || this._object.stream.readableLength));
return { binary: buffer ? buffer.toString('base64') : '' };
}
async close() {
this._object.destroy();
this._object.stream.destroy();
}
}

View File

@ -50,6 +50,7 @@ export interface DeviceBackend {
}
export interface SocketBackend extends EventEmitter {
guid: string;
write(data: Buffer): Promise<void>;
close(): void;
}
@ -61,7 +62,7 @@ export class Android extends SdkObject {
readonly _playwrightOptions: PlaywrightOptions;
constructor(backend: Backend, playwrightOptions: PlaywrightOptions) {
super(playwrightOptions.rootSdkObject);
super(playwrightOptions.rootSdkObject, 'android');
this._backend = backend;
this._playwrightOptions = playwrightOptions;
this._timeoutSettings = new TimeoutSettings();
@ -115,7 +116,7 @@ export class AndroidDevice extends SdkObject {
private _isClosed = false;
constructor(android: Android, backend: DeviceBackend, model: string) {
super(android);
super(android, 'android-device');
this._android = android;
this._backend = backend;
this.model = model;

View File

@ -19,6 +19,7 @@ import debug from 'debug';
import * as net from 'net';
import { EventEmitter } from 'events';
import { Backend, DeviceBackend, SocketBackend } from './android';
import { createGuid } from '../../utils/utils';
export class AdbBackend implements Backend {
async devices(): Promise<DeviceBackend[]> {
@ -99,6 +100,7 @@ function encodeMessage(message: string): Buffer {
}
class BufferedSocketWrapper extends EventEmitter implements SocketBackend {
readonly guid = createGuid();
private _socket: net.Socket;
private _buffer = Buffer.from([]);
private _isSocket = false;
@ -149,7 +151,7 @@ class BufferedSocketWrapper extends EventEmitter implements SocketBackend {
await this._connectPromise;
assert(!this._isSocket, 'Can not read by length in socket mode');
while (this._buffer.length < length)
await new Promise(f => this._notifyReader = f);
await new Promise<void>(f => this._notifyReader = f);
const result = this._buffer.slice(0, length);
this._buffer = this._buffer.slice(length);
debug('pw:adb:recv')(result.toString().substring(0, 100) + '...');
@ -158,7 +160,7 @@ class BufferedSocketWrapper extends EventEmitter implements SocketBackend {
async readAll(): Promise<Buffer> {
while (!this._isClosed)
await new Promise(f => this._notifyReader = f);
await new Promise<void>(f => this._notifyReader = f);
return this._buffer;
}

View File

@ -16,10 +16,11 @@
import fs from 'fs';
import * as util from 'util';
import { SdkObject } from './instrumentation';
type SaveCallback = (localPath: string, error?: string) => Promise<void>;
export class Artifact {
export class Artifact extends SdkObject {
private _localPath: string;
private _unaccessibleErrorMessage: string | undefined;
private _finishedCallback: () => void;
@ -29,7 +30,8 @@ export class Artifact {
private _deleted = false;
private _failureError: string | null = null;
constructor(localPath: string, unaccessibleErrorMessage?: string) {
constructor(parent: SdkObject, localPath: string, unaccessibleErrorMessage?: string) {
super(parent, 'artifact');
this._localPath = localPath;
this._unaccessibleErrorMessage = unaccessibleErrorMessage;
this._finishedCallback = () => {};

View File

@ -66,7 +66,7 @@ export abstract class Browser extends SdkObject {
readonly _idToVideo = new Map<string, { context: BrowserContext, artifact: Artifact }>();
constructor(options: BrowserOptions) {
super(options.rootSdkObject);
super(options.rootSdkObject, 'browser');
this.attribution.browser = this;
this.options = options;
}
@ -97,7 +97,7 @@ export abstract class Browser extends SdkObject {
}
_videoStarted(context: BrowserContext, videoId: string, path: string, pageOrError: Promise<Page | Error>) {
const artifact = new Artifact(path);
const artifact = new Artifact(context, path);
this._idToVideo.set(videoId, { context, artifact });
context.emit(BrowserContext.Events.VideoStarted, artifact);
pageOrError.then(page => {

View File

@ -53,7 +53,7 @@ export abstract class BrowserContext extends SdkObject {
private _origins = new Set<string>();
constructor(browser: Browser, options: types.BrowserContextOptions, browserContextId: string | undefined) {
super(browser);
super(browser, 'browser-context');
this.attribution.context = this;
this._browser = browser;
this._options = options;

View File

@ -44,7 +44,7 @@ export abstract class BrowserType extends SdkObject {
readonly _playwrightOptions: PlaywrightOptions;
constructor(browserName: registry.BrowserName, playwrightOptions: PlaywrightOptions) {
super(playwrightOptions.rootSdkObject);
super(playwrightOptions.rootSdkObject, 'browser-type');
this.attribution.browserType = this;
this._playwrightOptions = playwrightOptions;
this._name = browserName;

View File

@ -137,9 +137,11 @@ export class CRSession extends EventEmitter {
off: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
removeListener: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
once: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
readonly guid: string;
constructor(connection: CRConnection, rootSessionId: string, targetType: string, sessionId: string) {
super();
this.guid = `cdp-session@${sessionId}`;
this.setMaxListeners(0);
this._connection = connection;
this._rootSessionId = rootSessionId;

View File

@ -799,7 +799,7 @@ class FrameSession {
lineNumber: lineNumber || 0,
columnNumber: 0,
};
this._page.emit(Page.Events.Console, new ConsoleMessage(level, text, [], location));
this._page.emit(Page.Events.Console, new ConsoleMessage(this._page, level, text, [], location));
}
}

View File

@ -14,16 +14,18 @@
* limitations under the License.
*/
import { SdkObject } from './instrumentation';
import * as js from './javascript';
import { ConsoleMessageLocation } from './types';
export class ConsoleMessage {
export class ConsoleMessage extends SdkObject {
private _type: string;
private _text?: string;
private _args: js.JSHandle[];
private _location: ConsoleMessageLocation;
constructor(type: string, text: string | undefined, args: js.JSHandle[], location?: ConsoleMessageLocation) {
constructor(parent: SdkObject, type: string, text: string | undefined, args: js.JSHandle[], location?: ConsoleMessageLocation) {
super(parent, 'console-message');
this._type = type;
this._text = text;
this._args = args;

View File

@ -32,7 +32,7 @@ export class Dialog extends SdkObject {
private _defaultValue: string;
constructor(page: Page, type: string, message: string, onHandle: OnHandle, defaultValue?: string) {
super(page);
super(page, 'dialog');
this._page = page;
this._type = type;
this._message = message;

View File

@ -27,7 +27,7 @@ export class Download {
constructor(page: Page, downloadsPath: string, uuid: string, url: string, suggestedFilename?: string) {
const unaccessibleErrorMessage = !page._browserContext._options.acceptDownloads ? 'Pass { acceptDownloads: true } when you are creating your browser context.' : undefined;
this.artifact = new Artifact(path.join(downloadsPath, uuid), unaccessibleErrorMessage);
this.artifact = new Artifact(page, path.join(downloadsPath, uuid), unaccessibleErrorMessage);
this._page = page;
this.url = url;
this._suggestedFilename = suggestedFilename;

View File

@ -64,7 +64,7 @@ export class ElectronApplication extends SdkObject {
readonly _timeoutSettings = new TimeoutSettings();
constructor(parent: SdkObject, browser: CRBrowser, nodeConnection: CRConnection) {
super(parent);
super(parent, 'electron-app');
this._browserContext = browser._defaultContext as CRBrowserContext;
this._browserContext.on(BrowserContext.Events.Close, () => {
// Emit application closed after context closed.
@ -122,7 +122,7 @@ export class Electron extends SdkObject {
private _playwrightOptions: PlaywrightOptions;
constructor(playwrightOptions: PlaywrightOptions) {
super(playwrightOptions.rootSdkObject);
super(playwrightOptions.rootSdkObject, 'electron');
this._playwrightOptions = playwrightOptions;
}

View File

@ -24,7 +24,7 @@ import { Page } from './page';
import * as types from './types';
import { BrowserContext } from './browserContext';
import { Progress, ProgressController } from './progress';
import { assert, createGuid, makeWaitForNextTask } from '../utils/utils';
import { assert, makeWaitForNextTask } from '../utils/utils';
import { debugLogger } from '../utils/debugLogger';
import { CallMetadata, internalCallMetadata, SdkObject } from './instrumentation';
import { ElementStateWithoutStable } from './injected/injectedScript';
@ -411,11 +411,9 @@ export class Frame extends SdkObject {
private _setContentCounter = 0;
readonly _detachedPromise: Promise<void>;
private _detachedCallback = () => {};
readonly uniqueId: string;
constructor(page: Page, id: string, parentFrame: Frame | null) {
super(page);
this.uniqueId = parentFrame ? `frame@${createGuid()}` : page.uniqueId;
super(page, 'frame');
this.attribution.frame = this;
this._id = id;
this._page = page;

View File

@ -16,6 +16,7 @@
import { EventEmitter } from 'events';
import { Point, StackFrame } from '../common/types';
import { createGuid } from '../utils/utils';
import type { Browser } from './browser';
import type { BrowserContext } from './browserContext';
import type { BrowserType } from './browserType';
@ -50,11 +51,13 @@ export type CallMetadata = {
};
export class SdkObject extends EventEmitter {
guid: string;
attribution: Attribution;
instrumentation: Instrumentation;
protected constructor(parent: SdkObject) {
protected constructor(parent: SdkObject, guidPrefix?: string, guid?: string) {
super();
this.guid = guid || `${guidPrefix || ''}@${createGuid()}`;
this.setMaxListeners(0);
this.attribution = { ...parent.attribution };
this.instrumentation = parent.instrumentation;

View File

@ -56,7 +56,7 @@ export class ExecutionContext extends SdkObject {
private _utilityScriptPromise: Promise<JSHandle> | undefined;
constructor(parent: SdkObject, delegate: ExecutionContextDelegate) {
super(parent);
super(parent, 'execution-context');
this._delegate = delegate;
}
@ -104,7 +104,7 @@ export class JSHandle<T = any> extends SdkObject {
private _previewCallback: ((preview: string) => void) | undefined;
constructor(context: ExecutionContext, type: string, objectId?: ObjectId, value?: any) {
super(context);
super(context, 'handle');
this._context = context;
this._objectId = objectId;
this._value = value;

View File

@ -99,7 +99,7 @@ export class Request extends SdkObject {
constructor(routeDelegate: RouteDelegate | null, frame: frames.Frame, redirectedFrom: Request | null, documentId: string | undefined,
url: string, resourceType: string, method: string, postData: Buffer | null, headers: types.HeadersArray) {
super(frame);
super(frame, 'request');
assert(!url.startsWith('data:'), 'Data urls should not fire requests');
assert(!(routeDelegate && redirectedFrom), 'Should not be able to intercept redirects');
this._routeDelegate = routeDelegate;
@ -210,7 +210,7 @@ export class Route extends SdkObject {
private _handled = false;
constructor(request: Request, delegate: RouteDelegate) {
super(request.frame());
super(request.frame(), 'route');
this._request = request;
this._delegate = delegate;
}
@ -277,7 +277,7 @@ export class Response extends SdkObject {
private _timing: ResourceTiming;
constructor(request: Request, status: number, statusText: string, headers: types.HeadersArray, timing: ResourceTiming, getResponseBodyCallback: GetResponseBodyCallback) {
super(request.frame());
super(request.frame(), 'response');
this._request = request;
this._timing = timing;
this._status = status;
@ -357,7 +357,7 @@ export class WebSocket extends SdkObject {
};
constructor(parent: SdkObject, url: string) {
super(parent);
super(parent, 'ws');
this._url = url;
}

View File

@ -28,7 +28,7 @@ import { ConsoleMessage } from './console';
import * as accessibility from './accessibility';
import { FileChooser } from './fileChooser';
import { Progress, ProgressController } from './progress';
import { assert, createGuid, isError } from '../utils/utils';
import { assert, isError } from '../utils/utils';
import { debugLogger } from '../utils/debugLogger';
import { Selectors } from './selectors';
import { CallMetadata, SdkObject } from './instrumentation';
@ -145,14 +145,12 @@ export class Page extends SdkObject {
private _serverRequestInterceptor: network.RouteHandler | undefined;
_ownedContext: BrowserContext | undefined;
readonly selectors: Selectors;
readonly uniqueId: string;
_pageIsError: Error | undefined;
_video: Artifact | null = null;
_opener: Page | undefined;
constructor(delegate: PageDelegate, browserContext: BrowserContext) {
super(browserContext);
this.uniqueId = 'page@' + createGuid();
super(browserContext, 'page');
this.attribution.page = this;
this._delegate = delegate;
this._closedCallback = () => {};
@ -296,7 +294,7 @@ export class Page extends SdkObject {
}
_addConsoleMessage(type: string, args: js.JSHandle[], location: types.ConsoleMessageLocation, text?: string) {
const message = new ConsoleMessage(type, text, args, location);
const message = new ConsoleMessage(this, type, text, args, location);
const intercepted = this._frameManager.interceptConsoleMessage(message);
if (intercepted || !this.listenerCount(Page.Events.Console))
args.forEach(arg => arg.dispose());
@ -519,7 +517,7 @@ export class Worker extends SdkObject {
_existingExecutionContext: js.ExecutionContext | null = null;
constructor(parent: SdkObject, url: string) {
super(parent);
super(parent, 'worker');
this._url = url;
this._executionContextCallback = () => {};
this._executionContextPromise = new Promise(x => this._executionContextCallback = x);

View File

@ -46,7 +46,7 @@ export class Playwright extends SdkObject {
listeners.push(new InspectorController());
}
const instrumentation = multiplexInstrumentation(listeners);
super({ attribution: {}, instrumentation } as any);
super({ attribution: {}, instrumentation } as any, undefined, 'Playwright');
this.options = {
registry: new Registry(path.join(__dirname, '..', '..')),
rootSdkObject: this,

View File

@ -19,6 +19,7 @@ import * as frames from './frames';
import * as js from './javascript';
import * as types from './types';
import { ParsedSelector, parseSelector } from './common/selectorParser';
import { createGuid } from '../utils/utils';
export type SelectorInfo = {
parsed: ParsedSelector,
@ -29,6 +30,7 @@ export type SelectorInfo = {
export class Selectors {
readonly _builtinEngines: Set<string>;
readonly _engines: Map<string, { source: string, contentScript: boolean }>;
readonly guid = `selectors@${createGuid()}`;
constructor() {
// Note: keep in sync with InjectedScript class.

View File

@ -156,10 +156,9 @@ export class SnapshotServer {
response.statusCode = 200;
response.setHeader('Cache-Control', 'public, max-age=31536000');
response.setHeader('Content-Type', 'application/json');
const [ pageId, query ] = request.url!.substring('/snapshot/'.length).split('?');
const [ pageOrFrameId, query ] = request.url!.substring('/snapshot/'.length).split('?');
const parsed: any = querystring.parse(query);
const snapshot = parsed.name ? this._snapshotStorage.snapshotByName(pageId, parsed.name) : this._snapshotStorage.snapshotByTime(pageId, parsed.time);
const snapshot = this._snapshotStorage.snapshotByName(pageOrFrameId, parsed.name);
const snapshotData: any = snapshot ? snapshot.render() : { html: '' };
response.end(JSON.stringify(snapshotData));
return true;

View File

@ -25,8 +25,7 @@ export interface SnapshotStorage {
resources(): ResourceSnapshot[];
resourceContent(sha1: string): Buffer | undefined;
resourceById(resourceId: string): ResourceSnapshot | undefined;
snapshotByName(frameId: string, snapshotName: string): SnapshotRenderer | undefined;
snapshotByTime(frameId: string, timestamp: number): SnapshotRenderer | undefined;
snapshotByName(pageOrFrameId: string, snapshotName: string): SnapshotRenderer | undefined;
}
export abstract class BaseSnapshotStorage extends EventEmitter implements SnapshotStorage {
@ -64,6 +63,8 @@ export abstract class BaseSnapshotStorage extends EventEmitter implements Snapsh
renderer: [],
};
this._frameSnapshots.set(snapshot.frameId, frameSnapshots);
if (snapshot.isMainFrame)
this._frameSnapshots.set(snapshot.pageId, frameSnapshots);
}
frameSnapshots.raw.push(snapshot);
const renderer = new SnapshotRenderer(new Map(this._contextResources), frameSnapshots.raw, frameSnapshots.raw.length - 1);
@ -81,17 +82,9 @@ export abstract class BaseSnapshotStorage extends EventEmitter implements Snapsh
return this._resources.slice();
}
snapshotByName(frameId: string, snapshotName: string): SnapshotRenderer | undefined {
return this._frameSnapshots.get(frameId)?.renderer.find(r => r.snapshotName === snapshotName);
}
snapshotByTime(frameId: string, timestamp: number): SnapshotRenderer | undefined {
let result: SnapshotRenderer | undefined = undefined;
for (const snapshot of this._frameSnapshots.get(frameId)?.renderer.values() || []) {
if (timestamp && snapshot.snapshot().timestamp <= timestamp)
result = snapshot;
}
return result;
snapshotByName(pageOrFrameId: string, snapshotName: string): SnapshotRenderer | undefined {
const snapshot = this._frameSnapshots.get(pageOrFrameId);
return snapshot?.renderer.find(r => r.snapshotName === snapshotName);
}
}

View File

@ -60,6 +60,7 @@ export type FrameSnapshot = {
html: NodeSnapshot,
resourceOverrides: ResourceOverride[],
viewport: { width: number, height: number },
isMainFrame: boolean,
};
export type ContextResources = Map<string, { resourceId: string, frameId: string }[]>;

View File

@ -61,8 +61,8 @@ export class Snapshotter {
await this._context.exposeBinding(this._snapshotBinding, false, (source, data: SnapshotData) => {
const snapshot: FrameSnapshot = {
snapshotName: data.snapshotName,
pageId: source.page.uniqueId,
frameId: source.frame.uniqueId,
pageId: source.page.guid,
frameId: source.frame.guid,
frameUrl: data.url,
doctype: data.doctype,
html: data.html,
@ -71,6 +71,7 @@ export class Snapshotter {
pageTimestamp: data.timestamp,
collectionTime: data.collectionTime,
resourceOverrides: [],
isMainFrame: source.page.mainFrame() === source.frame
};
for (const { url, content } of data.resourceOverrides) {
if (typeof content === 'string') {
@ -167,8 +168,8 @@ export class Snapshotter {
const body = await response.body().catch(e => debugLogger.log('error', e));
const responseSha1 = body ? calculateSha1(body) : 'none';
const resource: ResourceSnapshot = {
pageId: page.uniqueId,
frameId: response.frame().uniqueId,
pageId: page.guid,
frameId: response.frame().guid,
resourceId: 'resource@' + createGuid(),
url,
contentType,
@ -203,7 +204,7 @@ export class Snapshotter {
const context = await parent._mainContext();
await context?.evaluate(({ snapshotStreamer, frameElement, frameId }) => {
(window as any)[snapshotStreamer].markIframe(frameElement, frameId);
}, { snapshotStreamer: this._snapshotStreamer, frameElement, frameId: frame.uniqueId });
}, { snapshotStreamer: this._snapshotStreamer, frameElement, frameId: frame.guid });
frameElement.dispose();
} catch (e) {
}

View File

@ -130,7 +130,7 @@ class ContextTracer {
}
private _onPage(page: Page) {
const pageId = page.uniqueId;
const pageId = page.guid;
const event: trace.PageCreatedTraceEvent = {
timestamp: monotonicTime(),
@ -197,7 +197,7 @@ class ContextTracer {
const sha1 = calculateSha1(params.buffer);
const event: trace.ScreencastFrameTraceEvent = {
type: 'page-screencast-frame',
pageId: page.uniqueId,
pageId: page.guid,
contextId: this._contextId,
sha1,
pageTimestamp: params.timestamp,

View File

@ -23,15 +23,15 @@ it('should scope context handles', async ({browserType, browserOptions, server})
const GOLDEN_PRECONDITION = {
_guid: '',
objects: [
{ _guid: 'Android', objects: [] },
{ _guid: 'BrowserType', objects: [] },
{ _guid: 'BrowserType', objects: [] },
{ _guid: 'BrowserType', objects: [
{ _guid: 'Browser', objects: [] }
{ _guid: 'android', objects: [] },
{ _guid: 'browser-type', objects: [] },
{ _guid: 'browser-type', objects: [] },
{ _guid: 'browser-type', objects: [
{ _guid: 'browser', objects: [] }
] },
{ _guid: 'Electron', objects: [] },
{ _guid: 'electron', objects: [] },
{ _guid: 'Playwright', objects: [] },
{ _guid: 'Selectors', objects: [] },
{ _guid: 'selectors', objects: [] },
]
};
await expectScopeState(browser, GOLDEN_PRECONDITION);
@ -42,23 +42,23 @@ it('should scope context handles', async ({browserType, browserOptions, server})
await expectScopeState(browser, {
_guid: '',
objects: [
{ _guid: 'Android', objects: [] },
{ _guid: 'BrowserType', objects: [] },
{ _guid: 'BrowserType', objects: [] },
{ _guid: 'BrowserType', objects: [
{ _guid: 'Browser', objects: [
{ _guid: 'BrowserContext', objects: [
{ _guid: 'Frame', objects: [] },
{ _guid: 'Page', objects: [
{ _guid: 'Request', objects: [] },
{ _guid: 'Response', objects: [] },
{ _guid: 'android', objects: [] },
{ _guid: 'browser-type', objects: [] },
{ _guid: 'browser-type', objects: [] },
{ _guid: 'browser-type', objects: [
{ _guid: 'browser', objects: [
{ _guid: 'browser-context', objects: [
{ _guid: 'frame', objects: [] },
{ _guid: 'page', objects: [
{ _guid: 'request', objects: [] },
{ _guid: 'response', objects: [] },
]},
]},
] },
] },
{ _guid: 'Electron', objects: [] },
{ _guid: 'electron', objects: [] },
{ _guid: 'Playwright', objects: [] },
{ _guid: 'Selectors', objects: [] },
{ _guid: 'selectors', objects: [] },
]
});
@ -74,15 +74,15 @@ it('should scope CDPSession handles', async ({browserType, browserOptions, brows
const GOLDEN_PRECONDITION = {
_guid: '',
objects: [
{ _guid: 'Android', objects: [] },
{ _guid: 'BrowserType', objects: [] },
{ _guid: 'BrowserType', objects: [] },
{ _guid: 'BrowserType', objects: [
{ _guid: 'Browser', objects: [] }
{ _guid: 'android', objects: [] },
{ _guid: 'browser-type', objects: [] },
{ _guid: 'browser-type', objects: [] },
{ _guid: 'browser-type', objects: [
{ _guid: 'browser', objects: [] }
] },
{ _guid: 'Electron', objects: [] },
{ _guid: 'electron', objects: [] },
{ _guid: 'Playwright', objects: [] },
{ _guid: 'Selectors', objects: [] },
{ _guid: 'selectors', objects: [] },
]
};
await expectScopeState(browserType, GOLDEN_PRECONDITION);
@ -91,17 +91,17 @@ it('should scope CDPSession handles', async ({browserType, browserOptions, brows
await expectScopeState(browserType, {
_guid: '',
objects: [
{ _guid: 'Android', objects: [] },
{ _guid: 'BrowserType', objects: [] },
{ _guid: 'BrowserType', objects: [] },
{ _guid: 'BrowserType', objects: [
{ _guid: 'Browser', objects: [
{ _guid: 'CDPSession', objects: [] },
{ _guid: 'android', objects: [] },
{ _guid: 'browser-type', objects: [] },
{ _guid: 'browser-type', objects: [] },
{ _guid: 'browser-type', objects: [
{ _guid: 'browser', objects: [
{ _guid: 'cdp-session', objects: [] },
] },
] },
{ _guid: 'Electron', objects: [] },
{ _guid: 'electron', objects: [] },
{ _guid: 'Playwright', objects: [] },
{ _guid: 'Selectors', objects: [] },
{ _guid: 'selectors', objects: [] },
]
});
@ -115,13 +115,13 @@ it('should scope browser handles', async ({browserType, browserOptions}) => {
const GOLDEN_PRECONDITION = {
_guid: '',
objects: [
{ _guid: 'Android', objects: [] },
{ _guid: 'BrowserType', objects: [] },
{ _guid: 'BrowserType', objects: [] },
{ _guid: 'BrowserType', objects: [] },
{ _guid: 'Electron', objects: [] },
{ _guid: 'android', objects: [] },
{ _guid: 'browser-type', objects: [] },
{ _guid: 'browser-type', objects: [] },
{ _guid: 'browser-type', objects: [] },
{ _guid: 'electron', objects: [] },
{ _guid: 'Playwright', objects: [] },
{ _guid: 'Selectors', objects: [] },
{ _guid: 'selectors', objects: [] },
]
};
await expectScopeState(browserType, GOLDEN_PRECONDITION);
@ -131,20 +131,20 @@ it('should scope browser handles', async ({browserType, browserOptions}) => {
await expectScopeState(browserType, {
_guid: '',
objects: [
{ _guid: 'Android', objects: [] },
{ _guid: 'BrowserType', objects: [] },
{ _guid: 'BrowserType', objects: [] },
{ _guid: 'BrowserType', objects: [
{ _guid: 'android', objects: [] },
{ _guid: 'browser-type', objects: [] },
{ _guid: 'browser-type', objects: [] },
{ _guid: 'browser-type', objects: [
{
_guid: 'Browser', objects: [
{ _guid: 'BrowserContext', objects: [] }
_guid: 'browser', objects: [
{ _guid: 'browser-context', objects: [] }
]
},
]
},
{ _guid: 'Electron', objects: [] },
{ _guid: 'electron', objects: [] },
{ _guid: 'Playwright', objects: [] },
{ _guid: 'Selectors', objects: [] },
{ _guid: 'selectors', objects: [] },
]
});