mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: extract debugger model from inspector (#6261)
This commit is contained in:
parent
34e03fc77d
commit
fe4fba4a16
@ -144,7 +144,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
|||||||
}
|
}
|
||||||
|
|
||||||
async recorderSupplementEnable(params: channels.BrowserContextRecorderSupplementEnableParams): Promise<void> {
|
async recorderSupplementEnable(params: channels.BrowserContextRecorderSupplementEnableParams): Promise<void> {
|
||||||
await RecorderSupplement.getOrCreate(this._context, params);
|
await RecorderSupplement.show(this._context, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
async pause(params: channels.BrowserContextPauseParams, metadata: CallMetadata) {
|
async pause(params: channels.BrowserContextPauseParams, metadata: CallMetadata) {
|
||||||
|
@ -28,6 +28,7 @@ import { InspectorController } from './supplements/inspectorController';
|
|||||||
import { WebKit } from './webkit/webkit';
|
import { WebKit } from './webkit/webkit';
|
||||||
import { Registry } from '../utils/registry';
|
import { Registry } from '../utils/registry';
|
||||||
import { InstrumentationListener, multiplexInstrumentation, SdkObject } from './instrumentation';
|
import { InstrumentationListener, multiplexInstrumentation, SdkObject } from './instrumentation';
|
||||||
|
import { Debugger } from './supplements/debugger';
|
||||||
|
|
||||||
export class Playwright extends SdkObject {
|
export class Playwright extends SdkObject {
|
||||||
readonly selectors: Selectors;
|
readonly selectors: Selectors;
|
||||||
@ -41,6 +42,7 @@ export class Playwright extends SdkObject {
|
|||||||
constructor(isInternal: boolean) {
|
constructor(isInternal: boolean) {
|
||||||
const listeners: InstrumentationListener[] = [];
|
const listeners: InstrumentationListener[] = [];
|
||||||
if (!isInternal) {
|
if (!isInternal) {
|
||||||
|
listeners.push(new Debugger());
|
||||||
listeners.push(new Tracer());
|
listeners.push(new Tracer());
|
||||||
listeners.push(new HarTracer());
|
listeners.push(new HarTracer());
|
||||||
listeners.push(new InspectorController());
|
listeners.push(new InspectorController());
|
||||||
|
129
src/server/supplements/debugger.ts
Normal file
129
src/server/supplements/debugger.ts
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/**
|
||||||
|
* 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 { EventEmitter } from 'events';
|
||||||
|
import { debugMode, isUnderTest, monotonicTime } from '../../utils/utils';
|
||||||
|
import { BrowserContext } from '../browserContext';
|
||||||
|
import { CallMetadata, InstrumentationListener, SdkObject } from '../instrumentation';
|
||||||
|
import * as consoleApiSource from '../../generated/consoleApiSource';
|
||||||
|
|
||||||
|
export class Debugger implements InstrumentationListener {
|
||||||
|
async onContextCreated(context: BrowserContext): Promise<void> {
|
||||||
|
ContextDebugger.getOrCreate(context);
|
||||||
|
if (debugMode() === 'console')
|
||||||
|
await context.extendInjectedScript(consoleApiSource.source);
|
||||||
|
}
|
||||||
|
|
||||||
|
async onBeforeCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
||||||
|
await ContextDebugger.lookup(sdkObject.attribution.context!)?.onBeforeCall(sdkObject, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
async onBeforeInputAction(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
||||||
|
await ContextDebugger.lookup(sdkObject.attribution.context!)?.onBeforeInputAction(sdkObject, metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const symbol = Symbol('ContextDebugger');
|
||||||
|
|
||||||
|
export class ContextDebugger extends EventEmitter {
|
||||||
|
private _pauseOnNextStatement = false;
|
||||||
|
private _pausedCallsMetadata = new Map<CallMetadata, { resolve: () => void, sdkObject: SdkObject }>();
|
||||||
|
private _enabled: boolean;
|
||||||
|
|
||||||
|
static Events = {
|
||||||
|
PausedStateChanged: 'pausedstatechanged'
|
||||||
|
};
|
||||||
|
|
||||||
|
static getOrCreate(context: BrowserContext): ContextDebugger {
|
||||||
|
let contextDebugger = (context as any)[symbol] as ContextDebugger;
|
||||||
|
if (!contextDebugger) {
|
||||||
|
contextDebugger = new ContextDebugger();
|
||||||
|
(context as any)[symbol] = contextDebugger;
|
||||||
|
}
|
||||||
|
return contextDebugger;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this._enabled = debugMode() === 'inspector';
|
||||||
|
if (this._enabled)
|
||||||
|
this.pauseOnNextStatement();
|
||||||
|
}
|
||||||
|
|
||||||
|
static lookup(context?: BrowserContext): ContextDebugger | undefined {
|
||||||
|
if (!context)
|
||||||
|
return;
|
||||||
|
return (context as any)[symbol] as ContextDebugger | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async onBeforeCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
||||||
|
if (shouldPauseOnCall(sdkObject, metadata) || (this._pauseOnNextStatement && shouldPauseOnStep(sdkObject, metadata)))
|
||||||
|
await this.pause(sdkObject, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
async onBeforeInputAction(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
||||||
|
if (this._enabled && this._pauseOnNextStatement)
|
||||||
|
await this.pause(sdkObject, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
async pause(sdkObject: SdkObject, metadata: CallMetadata) {
|
||||||
|
this._enabled = true;
|
||||||
|
metadata.pauseStartTime = monotonicTime();
|
||||||
|
const result = new Promise<void>(resolve => {
|
||||||
|
this._pausedCallsMetadata.set(metadata, { resolve, sdkObject });
|
||||||
|
});
|
||||||
|
this.emit(ContextDebugger.Events.PausedStateChanged);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
resume(step: boolean) {
|
||||||
|
this._pauseOnNextStatement = step;
|
||||||
|
const endTime = monotonicTime();
|
||||||
|
for (const [metadata, { resolve }] of this._pausedCallsMetadata) {
|
||||||
|
metadata.pauseEndTime = endTime;
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
this._pausedCallsMetadata.clear();
|
||||||
|
this.emit(ContextDebugger.Events.PausedStateChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
pauseOnNextStatement() {
|
||||||
|
this._pauseOnNextStatement = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
isPaused(metadata?: CallMetadata): boolean {
|
||||||
|
if (metadata)
|
||||||
|
return this._pausedCallsMetadata.has(metadata);
|
||||||
|
return !!this._pausedCallsMetadata.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
pausedDetails(): { metadata: CallMetadata, sdkObject: SdkObject }[] {
|
||||||
|
const result: { metadata: CallMetadata, sdkObject: SdkObject }[] = [];
|
||||||
|
for (const [metadata, { sdkObject }] of this._pausedCallsMetadata)
|
||||||
|
result.push({ metadata, sdkObject });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldPauseOnCall(sdkObject: SdkObject, metadata: CallMetadata): boolean {
|
||||||
|
if (!sdkObject.attribution.browser?.options.headful && !isUnderTest())
|
||||||
|
return false;
|
||||||
|
return metadata.method === 'pause';
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldPauseOnStep(sdkObject: SdkObject, metadata: CallMetadata): boolean {
|
||||||
|
return metadata.method === 'goto' || metadata.method === 'close';
|
||||||
|
}
|
@ -18,54 +18,36 @@ import { BrowserContext } from '../browserContext';
|
|||||||
import { RecorderSupplement } from './recorderSupplement';
|
import { RecorderSupplement } from './recorderSupplement';
|
||||||
import { debugLogger } from '../../utils/debugLogger';
|
import { debugLogger } from '../../utils/debugLogger';
|
||||||
import { CallMetadata, InstrumentationListener, SdkObject } from '../instrumentation';
|
import { CallMetadata, InstrumentationListener, SdkObject } from '../instrumentation';
|
||||||
import { debugMode, isUnderTest } from '../../utils/utils';
|
import { ContextDebugger } from './debugger';
|
||||||
import * as consoleApiSource from '../../generated/consoleApiSource';
|
|
||||||
|
|
||||||
export class InspectorController implements InstrumentationListener {
|
export class InspectorController implements InstrumentationListener {
|
||||||
async onContextCreated(context: BrowserContext): Promise<void> {
|
async onContextCreated(context: BrowserContext): Promise<void> {
|
||||||
if (debugMode() === 'inspector')
|
const contextDebugger = ContextDebugger.lookup(context)!;
|
||||||
await RecorderSupplement.getOrCreate(context, { pauseOnNextStatement: true });
|
if (contextDebugger.isPaused())
|
||||||
else if (debugMode() === 'console')
|
RecorderSupplement.show(context, {}).catch(() => {});
|
||||||
await context.extendInjectedScript(consoleApiSource.source);
|
contextDebugger.on(ContextDebugger.Events.PausedStateChanged, () => {
|
||||||
|
RecorderSupplement.show(context, {}).catch(() => {});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async onBeforeCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
async onBeforeCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
||||||
const context = sdkObject.attribution.context;
|
const recorder = await RecorderSupplement.lookup(sdkObject.attribution.context);
|
||||||
if (!context)
|
recorder?.onBeforeCall(sdkObject, metadata);
|
||||||
return;
|
|
||||||
|
|
||||||
if (shouldOpenInspector(sdkObject, metadata))
|
|
||||||
await RecorderSupplement.getOrCreate(context, { pauseOnNextStatement: true });
|
|
||||||
|
|
||||||
const recorder = await RecorderSupplement.getNoCreate(context);
|
|
||||||
await recorder?.onBeforeCall(sdkObject, metadata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onAfterCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
async onAfterCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
||||||
if (!sdkObject.attribution.context)
|
const recorder = await RecorderSupplement.lookup(sdkObject.attribution.context);
|
||||||
return;
|
recorder?.onAfterCall(sdkObject, metadata);
|
||||||
const recorder = await RecorderSupplement.getNoCreate(sdkObject.attribution.context);
|
|
||||||
await recorder?.onAfterCall(sdkObject, metadata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onBeforeInputAction(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
async onBeforeInputAction(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
||||||
if (!sdkObject.attribution.context)
|
const recorder = await RecorderSupplement.lookup(sdkObject.attribution.context);
|
||||||
return;
|
recorder?.onBeforeInputAction(sdkObject, metadata);
|
||||||
const recorder = await RecorderSupplement.getNoCreate(sdkObject.attribution.context);
|
|
||||||
await recorder?.onBeforeInputAction(sdkObject, metadata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onCallLog(logName: string, message: string, sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
async onCallLog(logName: string, message: string, sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
||||||
debugLogger.log(logName as any, message);
|
debugLogger.log(logName as any, message);
|
||||||
if (!sdkObject.attribution.context)
|
const recorder = await RecorderSupplement.lookup(sdkObject.attribution.context);
|
||||||
return;
|
|
||||||
const recorder = await RecorderSupplement.getNoCreate(sdkObject.attribution.context);
|
|
||||||
recorder?.updateCallLog([metadata]);
|
recorder?.updateCallLog([metadata]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldOpenInspector(sdkObject: SdkObject, metadata: CallMetadata): boolean {
|
|
||||||
if (!sdkObject.attribution.browser?.options.headful && !isUnderTest())
|
|
||||||
return false;
|
|
||||||
return metadata.method === 'pause';
|
|
||||||
}
|
|
||||||
|
@ -32,9 +32,10 @@ import { RecorderApp } from './recorder/recorderApp';
|
|||||||
import { CallMetadata, internalCallMetadata, SdkObject } from '../instrumentation';
|
import { CallMetadata, internalCallMetadata, SdkObject } from '../instrumentation';
|
||||||
import { Point } from '../../common/types';
|
import { Point } from '../../common/types';
|
||||||
import { CallLog, CallLogStatus, EventData, Mode, Source, UIState } from './recorder/recorderTypes';
|
import { CallLog, CallLogStatus, EventData, Mode, Source, UIState } from './recorder/recorderTypes';
|
||||||
import { isUnderTest, monotonicTime } from '../../utils/utils';
|
import { isUnderTest } from '../../utils/utils';
|
||||||
import { InMemorySnapshotter } from '../snapshot/inMemorySnapshotter';
|
import { InMemorySnapshotter } from '../snapshot/inMemorySnapshotter';
|
||||||
import { metadataToCallLog } from './recorder/recorderUtils';
|
import { metadataToCallLog } from './recorder/recorderUtils';
|
||||||
|
import { ContextDebugger } from './debugger';
|
||||||
|
|
||||||
type BindingSource = { frame: Frame, page: Page };
|
type BindingSource = { frame: Frame, page: Page };
|
||||||
|
|
||||||
@ -52,16 +53,15 @@ export class RecorderSupplement {
|
|||||||
private _recorderApp: RecorderApp | null = null;
|
private _recorderApp: RecorderApp | null = null;
|
||||||
private _params: channels.BrowserContextRecorderSupplementEnableParams;
|
private _params: channels.BrowserContextRecorderSupplementEnableParams;
|
||||||
private _currentCallsMetadata = new Map<CallMetadata, SdkObject>();
|
private _currentCallsMetadata = new Map<CallMetadata, SdkObject>();
|
||||||
private _pausedCallsMetadata = new Map<CallMetadata, () => void>();
|
|
||||||
private _pauseOnNextStatement: boolean;
|
|
||||||
private _recorderSources: Source[];
|
private _recorderSources: Source[];
|
||||||
private _userSources = new Map<string, Source>();
|
private _userSources = new Map<string, Source>();
|
||||||
private _snapshotter: InMemorySnapshotter;
|
private _snapshotter: InMemorySnapshotter;
|
||||||
private _hoveredSnapshot: { callLogId: number, phase: 'before' | 'after' | 'action' } | undefined;
|
private _hoveredSnapshot: { callLogId: number, phase: 'before' | 'after' | 'action' } | undefined;
|
||||||
private _snapshots = new Set<string>();
|
private _snapshots = new Set<string>();
|
||||||
private _allMetadatas = new Map<number, CallMetadata>();
|
private _allMetadatas = new Map<number, CallMetadata>();
|
||||||
|
private _contextDebugger: ContextDebugger;
|
||||||
|
|
||||||
static getOrCreate(context: BrowserContext, params: channels.BrowserContextRecorderSupplementEnableParams = {}): Promise<RecorderSupplement> {
|
static show(context: BrowserContext, params: channels.BrowserContextRecorderSupplementEnableParams = {}): Promise<RecorderSupplement> {
|
||||||
let recorderPromise = (context as any)[symbol] as Promise<RecorderSupplement>;
|
let recorderPromise = (context as any)[symbol] as Promise<RecorderSupplement>;
|
||||||
if (!recorderPromise) {
|
if (!recorderPromise) {
|
||||||
const recorder = new RecorderSupplement(context, params);
|
const recorder = new RecorderSupplement(context, params);
|
||||||
@ -71,15 +71,17 @@ export class RecorderSupplement {
|
|||||||
return recorderPromise;
|
return recorderPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
static getNoCreate(context: BrowserContext): Promise<RecorderSupplement> | undefined {
|
static lookup(context: BrowserContext | undefined): Promise<RecorderSupplement> | undefined {
|
||||||
|
if (!context)
|
||||||
|
return;
|
||||||
return (context as any)[symbol] as Promise<RecorderSupplement> | undefined;
|
return (context as any)[symbol] as Promise<RecorderSupplement> | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(context: BrowserContext, params: channels.BrowserContextRecorderSupplementEnableParams) {
|
constructor(context: BrowserContext, params: channels.BrowserContextRecorderSupplementEnableParams) {
|
||||||
this._context = context;
|
this._context = context;
|
||||||
|
this._contextDebugger = ContextDebugger.getOrCreate(context);
|
||||||
this._params = params;
|
this._params = params;
|
||||||
this._mode = params.startRecording ? 'recording' : 'none';
|
this._mode = params.startRecording ? 'recording' : 'none';
|
||||||
this._pauseOnNextStatement = !!params.pauseOnNextStatement;
|
|
||||||
const language = params.language || context._options.sdkLanguage;
|
const language = params.language || context._options.sdkLanguage;
|
||||||
|
|
||||||
const languages = new Set([
|
const languages = new Set([
|
||||||
@ -150,21 +152,21 @@ export class RecorderSupplement {
|
|||||||
}
|
}
|
||||||
if (data.event === 'callLogHovered') {
|
if (data.event === 'callLogHovered') {
|
||||||
this._hoveredSnapshot = undefined;
|
this._hoveredSnapshot = undefined;
|
||||||
if (this._isPaused() && data.params.callLogId)
|
if (this._contextDebugger.isPaused() && data.params.callLogId)
|
||||||
this._hoveredSnapshot = data.params;
|
this._hoveredSnapshot = data.params;
|
||||||
this._refreshOverlay();
|
this._refreshOverlay();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.event === 'step') {
|
if (data.event === 'step') {
|
||||||
this._resume(true);
|
this._contextDebugger.resume(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.event === 'resume') {
|
if (data.event === 'resume') {
|
||||||
this._resume(false);
|
this._contextDebugger.resume(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.event === 'pause') {
|
if (data.event === 'pause') {
|
||||||
this._pauseOnNextStatement = true;
|
this._contextDebugger.pauseOnNextStatement();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.event === 'clear') {
|
if (data.event === 'clear') {
|
||||||
@ -175,7 +177,7 @@ export class RecorderSupplement {
|
|||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
recorderApp.setMode(this._mode),
|
recorderApp.setMode(this._mode),
|
||||||
recorderApp.setPaused(!!this._pausedCallsMetadata.size),
|
recorderApp.setPaused(this._contextDebugger.isPaused()),
|
||||||
this._pushAllSources()
|
this._pushAllSources()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -231,28 +233,29 @@ export class RecorderSupplement {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await this._context.exposeBinding('_playwrightResume', false, () => {
|
await this._context.exposeBinding('_playwrightResume', false, () => {
|
||||||
this._resume(false).catch(() => {});
|
this._contextDebugger.resume(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
const snapshotBaseUrl = await this._snapshotter.initialize() + '/snapshot/';
|
const snapshotBaseUrl = await this._snapshotter.initialize() + '/snapshot/';
|
||||||
await this._context.extendInjectedScript(recorderSource.source, { isUnderTest: isUnderTest(), snapshotBaseUrl });
|
await this._context.extendInjectedScript(recorderSource.source, { isUnderTest: isUnderTest(), snapshotBaseUrl });
|
||||||
await this._context.extendInjectedScript(consoleApiSource.source);
|
await this._context.extendInjectedScript(consoleApiSource.source);
|
||||||
|
|
||||||
|
if (this._contextDebugger.isPaused())
|
||||||
|
this._pausedStateChanged();
|
||||||
|
this._contextDebugger.on(ContextDebugger.Events.PausedStateChanged, () => this._pausedStateChanged());
|
||||||
|
|
||||||
(this._context as any).recorderAppForTest = recorderApp;
|
(this._context as any).recorderAppForTest = recorderApp;
|
||||||
}
|
}
|
||||||
|
|
||||||
async pause(metadata: CallMetadata) {
|
_pausedStateChanged() {
|
||||||
const result = new Promise<void>(f => {
|
// If we are called upon page.pause, we don't have metadatas, populate them.
|
||||||
this._pausedCallsMetadata.set(metadata, f);
|
for (const { metadata, sdkObject } of this._contextDebugger.pausedDetails()) {
|
||||||
});
|
if (!this._currentCallsMetadata.has(metadata))
|
||||||
this._recorderApp!.setPaused(true);
|
this.onBeforeCall(sdkObject, metadata);
|
||||||
metadata.pauseStartTime = monotonicTime();
|
|
||||||
this._updateUserSources();
|
|
||||||
this.updateCallLog([metadata]);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
this._recorderApp!.setPaused(this._contextDebugger.isPaused());
|
||||||
_isPaused(): boolean {
|
this._updateUserSources();
|
||||||
return !!this._pausedCallsMetadata.size;
|
this.updateCallLog([...this._currentCallsMetadata.keys()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setMode(mode: Mode) {
|
private _setMode(mode: Mode) {
|
||||||
@ -263,21 +266,6 @@ export class RecorderSupplement {
|
|||||||
this._context.pages()[0].bringToFront().catch(() => {});
|
this._context.pages()[0].bringToFront().catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _resume(step: boolean) {
|
|
||||||
this._pauseOnNextStatement = step;
|
|
||||||
this._recorderApp?.setPaused(false);
|
|
||||||
|
|
||||||
const endTime = monotonicTime();
|
|
||||||
for (const [metadata, callback] of this._pausedCallsMetadata) {
|
|
||||||
metadata.pauseEndTime = endTime;
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
this._pausedCallsMetadata.clear();
|
|
||||||
|
|
||||||
this._updateUserSources();
|
|
||||||
this.updateCallLog([...this._currentCallsMetadata.keys()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _refreshOverlay() {
|
private _refreshOverlay() {
|
||||||
for (const page of this._context.pages())
|
for (const page of this._context.pages())
|
||||||
page.mainFrame().evaluateExpression('window._playwrightRefreshOverlay()', false, undefined, 'main').catch(() => {});
|
page.mainFrame().evaluateExpression('window._playwrightRefreshOverlay()', false, undefined, 'main').catch(() => {});
|
||||||
@ -410,7 +398,7 @@ export class RecorderSupplement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async onBeforeCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
onBeforeCall(sdkObject: SdkObject, metadata: CallMetadata) {
|
||||||
if (this._mode === 'recording')
|
if (this._mode === 'recording')
|
||||||
return;
|
return;
|
||||||
this._captureSnapshot(sdkObject, metadata, 'before');
|
this._captureSnapshot(sdkObject, metadata, 'before');
|
||||||
@ -418,21 +406,18 @@ export class RecorderSupplement {
|
|||||||
this._allMetadatas.set(metadata.id, metadata);
|
this._allMetadatas.set(metadata.id, metadata);
|
||||||
this._updateUserSources();
|
this._updateUserSources();
|
||||||
this.updateCallLog([metadata]);
|
this.updateCallLog([metadata]);
|
||||||
if (shouldPauseOnCall(sdkObject, metadata) || (this._pauseOnNextStatement && shouldPauseOnStep(sdkObject, metadata)))
|
|
||||||
await this.pause(metadata);
|
|
||||||
if (metadata.params && metadata.params.selector) {
|
if (metadata.params && metadata.params.selector) {
|
||||||
this._highlightedSelector = metadata.params.selector;
|
this._highlightedSelector = metadata.params.selector;
|
||||||
await this._recorderApp?.setSelector(this._highlightedSelector);
|
this._recorderApp?.setSelector(this._highlightedSelector).catch(() => {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async onAfterCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
onAfterCall(sdkObject: SdkObject, metadata: CallMetadata) {
|
||||||
if (this._mode === 'recording')
|
if (this._mode === 'recording')
|
||||||
return;
|
return;
|
||||||
this._captureSnapshot(sdkObject, metadata, 'after');
|
this._captureSnapshot(sdkObject, metadata, 'after');
|
||||||
if (!metadata.error)
|
if (!metadata.error)
|
||||||
this._currentCallsMetadata.delete(metadata);
|
this._currentCallsMetadata.delete(metadata);
|
||||||
this._pausedCallsMetadata.delete(metadata);
|
|
||||||
this._updateUserSources();
|
this._updateUserSources();
|
||||||
this.updateCallLog([metadata]);
|
this.updateCallLog([metadata]);
|
||||||
}
|
}
|
||||||
@ -456,7 +441,7 @@ export class RecorderSupplement {
|
|||||||
this._userSources.set(file, source);
|
this._userSources.set(file, source);
|
||||||
}
|
}
|
||||||
if (line) {
|
if (line) {
|
||||||
const paused = this._pausedCallsMetadata.has(metadata);
|
const paused = this._contextDebugger.isPaused(metadata);
|
||||||
source.highlight.push({ line, type: metadata.error ? 'error' : (paused ? 'paused' : 'running') });
|
source.highlight.push({ line, type: metadata.error ? 'error' : (paused ? 'paused' : 'running') });
|
||||||
source.revealLine = line;
|
source.revealLine = line;
|
||||||
fileToSelect = source.file;
|
fileToSelect = source.file;
|
||||||
@ -471,12 +456,10 @@ export class RecorderSupplement {
|
|||||||
this._recorderApp?.setSources([...this._recorderSources, ...this._userSources.values()]);
|
this._recorderApp?.setSources([...this._recorderSources, ...this._userSources.values()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onBeforeInputAction(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
onBeforeInputAction(sdkObject: SdkObject, metadata: CallMetadata) {
|
||||||
if (this._mode === 'recording')
|
if (this._mode === 'recording')
|
||||||
return;
|
return;
|
||||||
this._captureSnapshot(sdkObject, metadata, 'action');
|
this._captureSnapshot(sdkObject, metadata, 'action');
|
||||||
if (this._pauseOnNextStatement)
|
|
||||||
await this.pause(metadata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCallLog(metadatas: CallMetadata[]) {
|
updateCallLog(metadatas: CallMetadata[]) {
|
||||||
@ -489,7 +472,7 @@ export class RecorderSupplement {
|
|||||||
let status: CallLogStatus = 'done';
|
let status: CallLogStatus = 'done';
|
||||||
if (this._currentCallsMetadata.has(metadata))
|
if (this._currentCallsMetadata.has(metadata))
|
||||||
status = 'in-progress';
|
status = 'in-progress';
|
||||||
if (this._pausedCallsMetadata.has(metadata))
|
if (this._contextDebugger.isPaused(metadata))
|
||||||
status = 'paused';
|
status = 'paused';
|
||||||
logs.push(metadataToCallLog(metadata, status, this._snapshots));
|
logs.push(metadataToCallLog(metadata, status, this._snapshots));
|
||||||
}
|
}
|
||||||
@ -514,13 +497,3 @@ function languageForFile(file: string) {
|
|||||||
return 'csharp';
|
return 'csharp';
|
||||||
return 'javascript';
|
return 'javascript';
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldPauseOnCall(sdkObject: SdkObject, metadata: CallMetadata): boolean {
|
|
||||||
if (!sdkObject.attribution.browser?.options.headful && !isUnderTest())
|
|
||||||
return false;
|
|
||||||
return metadata.method === 'pause';
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldPauseOnStep(sdkObject: SdkObject, metadata: CallMetadata): boolean {
|
|
||||||
return metadata.method === 'goto' || metadata.method === 'close';
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user