mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore(tracing): include events in the trace (#6285)
This commit is contained in:
parent
6d38b10603
commit
0ed328f6de
@ -77,7 +77,7 @@ export class Dispatcher<Type extends { guid: string }, Initializer> extends Even
|
|||||||
|
|
||||||
(object as any)[dispatcherSymbol] = this;
|
(object as any)[dispatcherSymbol] = this;
|
||||||
if (this._parent)
|
if (this._parent)
|
||||||
this._connection.sendMessageToClient(this._parent._guid, '__create__', { type, initializer, guid });
|
this._connection.sendMessageToClient(this._parent._guid, type, '__create__', { type, initializer, guid });
|
||||||
}
|
}
|
||||||
|
|
||||||
_dispatchEvent(method: string, params: Dispatcher<any, any> | any = {}) {
|
_dispatchEvent(method: string, params: Dispatcher<any, any> | any = {}) {
|
||||||
@ -87,7 +87,8 @@ export class Dispatcher<Type extends { guid: string }, Initializer> extends Even
|
|||||||
// Just ignore this event outside of tests.
|
// Just ignore this event outside of tests.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._connection.sendMessageToClient(this._guid, method, params);
|
const sdkObject = this._object instanceof SdkObject ? this._object : undefined;
|
||||||
|
this._connection.sendMessageToClient(this._guid, this._type, method, params, sdkObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
_dispose() {
|
_dispose() {
|
||||||
@ -105,7 +106,7 @@ export class Dispatcher<Type extends { guid: string }, Initializer> extends Even
|
|||||||
this._dispatchers.clear();
|
this._dispatchers.clear();
|
||||||
|
|
||||||
if (this._isScope)
|
if (this._isScope)
|
||||||
this._connection.sendMessageToClient(this._guid, '__dispose__', {});
|
this._connection.sendMessageToClient(this._guid, this._type, '__dispose__', {});
|
||||||
}
|
}
|
||||||
|
|
||||||
_debugScopeState(): any {
|
_debugScopeState(): any {
|
||||||
@ -135,8 +136,25 @@ export class DispatcherConnection {
|
|||||||
private _validateMetadata: (metadata: any) => { stack?: StackFrame[] };
|
private _validateMetadata: (metadata: any) => { stack?: StackFrame[] };
|
||||||
private _waitOperations = new Map<string, CallMetadata>();
|
private _waitOperations = new Map<string, CallMetadata>();
|
||||||
|
|
||||||
sendMessageToClient(guid: string, method: string, params: any) {
|
sendMessageToClient(guid: string, type: string, method: string, params: any, sdkObject?: SdkObject) {
|
||||||
this.onmessage({ guid, method, params: this._replaceDispatchersWithGuids(params) });
|
params = this._replaceDispatchersWithGuids(params);
|
||||||
|
if (sdkObject) {
|
||||||
|
const eventMetadata: CallMetadata = {
|
||||||
|
id: `event@${++lastEventId}`,
|
||||||
|
objectId: sdkObject?.guid,
|
||||||
|
pageId: sdkObject?.attribution.page?.guid,
|
||||||
|
frameId: sdkObject?.attribution.frame?.guid,
|
||||||
|
startTime: monotonicTime(),
|
||||||
|
endTime: 0,
|
||||||
|
type,
|
||||||
|
method,
|
||||||
|
params: params || {},
|
||||||
|
log: [],
|
||||||
|
snapshots: []
|
||||||
|
};
|
||||||
|
sdkObject.instrumentation.onEvent(sdkObject, eventMetadata);
|
||||||
|
}
|
||||||
|
this.onmessage({ guid, method, params });
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -200,8 +218,9 @@ export class DispatcherConnection {
|
|||||||
|
|
||||||
const sdkObject = dispatcher._object instanceof SdkObject ? dispatcher._object : undefined;
|
const sdkObject = dispatcher._object instanceof SdkObject ? dispatcher._object : undefined;
|
||||||
let callMetadata: CallMetadata = {
|
let callMetadata: CallMetadata = {
|
||||||
id,
|
id: `call@${id}`,
|
||||||
...validMetadata,
|
...validMetadata,
|
||||||
|
objectId: sdkObject?.guid,
|
||||||
pageId: sdkObject?.attribution.page?.guid,
|
pageId: sdkObject?.attribution.page?.guid,
|
||||||
frameId: sdkObject?.attribution.frame?.guid,
|
frameId: sdkObject?.attribution.frame?.guid,
|
||||||
startTime: monotonicTime(),
|
startTime: monotonicTime(),
|
||||||
@ -210,6 +229,7 @@ export class DispatcherConnection {
|
|||||||
method,
|
method,
|
||||||
params: params || {},
|
params: params || {},
|
||||||
log: [],
|
log: [],
|
||||||
|
snapshots: []
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -295,3 +315,5 @@ function formatLogRecording(log: string[]): string {
|
|||||||
const rightLength = headerLength - header.length - leftLength;
|
const rightLength = headerLength - header.length - leftLength;
|
||||||
return `\n${'='.repeat(leftLength)}${header}${'='.repeat(rightLength)}\n${log.join('\n')}\n${'='.repeat(headerLength)}`;
|
return `\n${'='.repeat(leftLength)}${header}${'='.repeat(rightLength)}\n${log.join('\n')}\n${'='.repeat(headerLength)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let lastEventId = 0;
|
||||||
|
@ -132,11 +132,8 @@ export class FrameManager {
|
|||||||
if (progress)
|
if (progress)
|
||||||
progress.cleanupWhenAborted(() => this._signalBarriers.delete(barrier));
|
progress.cleanupWhenAborted(() => this._signalBarriers.delete(barrier));
|
||||||
const result = await action();
|
const result = await action();
|
||||||
if (source === 'input') {
|
if (source === 'input')
|
||||||
await this._page._delegate.inputActionEpilogue();
|
await this._page._delegate.inputActionEpilogue();
|
||||||
if (progress)
|
|
||||||
await progress.afterInputAction();
|
|
||||||
}
|
|
||||||
await barrier.waitFor();
|
await barrier.waitFor();
|
||||||
this._signalBarriers.delete(barrier);
|
this._signalBarriers.delete(barrier);
|
||||||
// Resolve in the next task, after all waitForNavigations.
|
// Resolve in the next task, after all waitForNavigations.
|
||||||
|
@ -33,7 +33,7 @@ export type Attribution = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type CallMetadata = {
|
export type CallMetadata = {
|
||||||
id: number;
|
id: string;
|
||||||
startTime: number;
|
startTime: number;
|
||||||
endTime: number;
|
endTime: number;
|
||||||
pauseStartTime?: number;
|
pauseStartTime?: number;
|
||||||
@ -44,8 +44,10 @@ export type CallMetadata = {
|
|||||||
apiName?: string;
|
apiName?: string;
|
||||||
stack?: StackFrame[];
|
stack?: StackFrame[];
|
||||||
log: string[];
|
log: string[];
|
||||||
|
snapshots: { title: string, snapshotName: string }[];
|
||||||
error?: string;
|
error?: string;
|
||||||
point?: Point;
|
point?: Point;
|
||||||
|
objectId?: string;
|
||||||
pageId?: string;
|
pageId?: string;
|
||||||
frameId?: string;
|
frameId?: string;
|
||||||
};
|
};
|
||||||
@ -71,9 +73,10 @@ export interface Instrumentation {
|
|||||||
|
|
||||||
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>;
|
||||||
onAfterInputAction(sdkObject: SdkObject, metadata: CallMetadata): Promise<void>;
|
|
||||||
onCallLog(logName: string, message: string, sdkObject: SdkObject, metadata: CallMetadata): void;
|
onCallLog(logName: string, message: string, sdkObject: SdkObject, metadata: CallMetadata): void;
|
||||||
onAfterCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void>;
|
onAfterCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void>;
|
||||||
|
|
||||||
|
onEvent(sdkObject: SdkObject, metadata: CallMetadata): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InstrumentationListener {
|
export interface InstrumentationListener {
|
||||||
@ -83,9 +86,10 @@ export interface InstrumentationListener {
|
|||||||
|
|
||||||
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>;
|
||||||
onAfterInputAction?(sdkObject: SdkObject, metadata: CallMetadata): Promise<void>;
|
|
||||||
onCallLog?(logName: string, message: string, sdkObject: SdkObject, metadata: CallMetadata): void;
|
onCallLog?(logName: string, message: string, sdkObject: SdkObject, metadata: CallMetadata): void;
|
||||||
onAfterCall?(sdkObject: SdkObject, metadata: CallMetadata): Promise<void>;
|
onAfterCall?(sdkObject: SdkObject, metadata: CallMetadata): Promise<void>;
|
||||||
|
|
||||||
|
onEvent?(sdkObject: SdkObject, metadata: CallMetadata): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function multiplexInstrumentation(listeners: InstrumentationListener[]): Instrumentation {
|
export function multiplexInstrumentation(listeners: InstrumentationListener[]): Instrumentation {
|
||||||
@ -103,12 +107,13 @@ export function multiplexInstrumentation(listeners: InstrumentationListener[]):
|
|||||||
|
|
||||||
export function internalCallMetadata(): CallMetadata {
|
export function internalCallMetadata(): CallMetadata {
|
||||||
return {
|
return {
|
||||||
id: 0,
|
id: '',
|
||||||
startTime: 0,
|
startTime: 0,
|
||||||
endTime: 0,
|
endTime: 0,
|
||||||
type: 'Internal',
|
type: 'Internal',
|
||||||
method: '',
|
method: '',
|
||||||
params: {},
|
params: {},
|
||||||
log: [],
|
log: [],
|
||||||
|
snapshots: []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@ export interface Progress {
|
|||||||
cleanupWhenAborted(cleanup: () => any): void;
|
cleanupWhenAborted(cleanup: () => any): void;
|
||||||
throwIfAborted(): void;
|
throwIfAborted(): void;
|
||||||
beforeInputAction(element: ElementHandle): Promise<void>;
|
beforeInputAction(element: ElementHandle): Promise<void>;
|
||||||
afterInputAction(): Promise<void>;
|
|
||||||
metadata: CallMetadata;
|
metadata: CallMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,9 +90,6 @@ export class ProgressController {
|
|||||||
beforeInputAction: async (element: ElementHandle) => {
|
beforeInputAction: async (element: ElementHandle) => {
|
||||||
await this.instrumentation.onBeforeInputAction(this.sdkObject, this.metadata, element);
|
await this.instrumentation.onBeforeInputAction(this.sdkObject, this.metadata, element);
|
||||||
},
|
},
|
||||||
afterInputAction: async () => {
|
|
||||||
await this.instrumentation.onAfterInputAction(this.sdkObject, this.metadata);
|
|
||||||
},
|
|
||||||
metadata: this.metadata
|
metadata: this.metadata
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ export type UIState = {
|
|||||||
export type CallLogStatus = 'in-progress' | 'done' | 'error' | 'paused';
|
export type CallLogStatus = 'in-progress' | 'done' | 'error' | 'paused';
|
||||||
|
|
||||||
export type CallLog = {
|
export type CallLog = {
|
||||||
id: number;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
messages: string[];
|
messages: string[];
|
||||||
status: CallLogStatus;
|
status: CallLogStatus;
|
||||||
|
@ -56,9 +56,9 @@ export class RecorderSupplement {
|
|||||||
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: string, 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<string, CallMetadata>();
|
||||||
private _contextDebugger: ContextDebugger;
|
private _contextDebugger: ContextDebugger;
|
||||||
|
|
||||||
static show(context: BrowserContext, params: channels.BrowserContextRecorderSupplementEnableParams = {}): Promise<RecorderSupplement> {
|
static show(context: BrowserContext, params: channels.BrowserContextRecorderSupplementEnableParams = {}): Promise<RecorderSupplement> {
|
||||||
|
@ -60,7 +60,7 @@ export type ScreencastFrameTraceEvent = {
|
|||||||
|
|
||||||
export type ActionTraceEvent = {
|
export type ActionTraceEvent = {
|
||||||
timestamp: number,
|
timestamp: number,
|
||||||
type: 'action',
|
type: 'action' | 'event',
|
||||||
contextId: string,
|
contextId: string,
|
||||||
metadata: CallMetadata,
|
metadata: CallMetadata,
|
||||||
snapshots?: { title: string, snapshotName: string }[],
|
snapshots?: { title: string, snapshotName: string }[],
|
||||||
|
@ -54,26 +54,20 @@ export class Tracer implements InstrumentationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async onBeforeInputAction(sdkObject: SdkObject, metadata: CallMetadata, element: ElementHandle): Promise<void> {
|
async onBeforeInputAction(sdkObject: SdkObject, metadata: CallMetadata, element: ElementHandle): Promise<void> {
|
||||||
this._contextTracers.get(sdkObject.attribution.context!)?._captureSnapshot('action', sdkObject, metadata, element);
|
this._contextTracers.get(sdkObject.attribution.context!)?.onBeforeInputAction(sdkObject, metadata, element);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onBeforeCall(sdkObject: SdkObject, metadata: CallMetadata, element?: ElementHandle): Promise<void> {
|
async onBeforeCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
||||||
this._contextTracers.get(sdkObject.attribution.context!)?._captureSnapshot('before', sdkObject, metadata, element);
|
this._contextTracers.get(sdkObject.attribution.context!)?.onBeforeCall(sdkObject, metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onAfterCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
async onAfterCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
||||||
this._contextTracers.get(sdkObject.attribution.context!)?._captureSnapshot('after', sdkObject, metadata);
|
|
||||||
this._contextTracers.get(sdkObject.attribution.context!)?.onAfterCall(sdkObject, metadata);
|
this._contextTracers.get(sdkObject.attribution.context!)?.onAfterCall(sdkObject, metadata);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const snapshotsSymbol = Symbol('snapshots');
|
async onEvent(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
||||||
|
this._contextTracers.get(sdkObject.attribution.context!)?.onEvent(sdkObject, metadata);
|
||||||
// This is an official way to pass snapshots between onBefore/AfterInputAction and onAfterCall.
|
}
|
||||||
function snapshotsForMetadata(metadata: CallMetadata): { title: string, snapshotName: string }[] {
|
|
||||||
if (!(metadata as any)[snapshotsSymbol])
|
|
||||||
(metadata as any)[snapshotsSymbol] = [];
|
|
||||||
return (metadata as any)[snapshotsSymbol];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ContextTracer {
|
class ContextTracer {
|
||||||
@ -82,6 +76,7 @@ class ContextTracer {
|
|||||||
private _snapshotter: PersistentSnapshotter;
|
private _snapshotter: PersistentSnapshotter;
|
||||||
private _eventListeners: RegisteredListener[];
|
private _eventListeners: RegisteredListener[];
|
||||||
private _disposed = false;
|
private _disposed = false;
|
||||||
|
private _pendingCalls = new Map<string, { sdkObject: SdkObject, metadata: CallMetadata }>();
|
||||||
|
|
||||||
constructor(context: BrowserContext, resourcesDir: string, tracePrefix: string) {
|
constructor(context: BrowserContext, resourcesDir: string, tracePrefix: string) {
|
||||||
const traceFile = tracePrefix + '-actions.trace';
|
const traceFile = tracePrefix + '-actions.trace';
|
||||||
@ -108,23 +103,45 @@ class ContextTracer {
|
|||||||
await this._snapshotter.start(false);
|
await this._snapshotter.start(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _captureSnapshot(name: 'before' | 'after' | 'action', sdkObject: SdkObject, metadata: CallMetadata, element?: ElementHandle): Promise<void> {
|
_captureSnapshot(name: 'before' | 'after' | 'action' | 'event', sdkObject: SdkObject, metadata: CallMetadata, element?: ElementHandle) {
|
||||||
if (!sdkObject.attribution.page)
|
if (!sdkObject.attribution.page)
|
||||||
return;
|
return;
|
||||||
const snapshotName = `${name}@${metadata.id}`;
|
const snapshotName = `${name}@${metadata.id}`;
|
||||||
snapshotsForMetadata(metadata).push({ title: name, snapshotName });
|
metadata.snapshots.push({ title: name, snapshotName });
|
||||||
this._snapshotter.captureSnapshot(sdkObject.attribution.page, snapshotName, element);
|
this._snapshotter.captureSnapshot(sdkObject.attribution.page, snapshotName, element);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onAfterCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
onBeforeCall(sdkObject: SdkObject, metadata: CallMetadata) {
|
||||||
|
this._captureSnapshot('before', sdkObject, metadata);
|
||||||
|
this._pendingCalls.set(metadata.id, { sdkObject, metadata });
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeInputAction(sdkObject: SdkObject, metadata: CallMetadata, element: ElementHandle) {
|
||||||
|
this._captureSnapshot('action', sdkObject, metadata, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAfterCall(sdkObject: SdkObject, metadata: CallMetadata) {
|
||||||
|
this._captureSnapshot('after', sdkObject, metadata);
|
||||||
if (!sdkObject.attribution.page)
|
if (!sdkObject.attribution.page)
|
||||||
return;
|
return;
|
||||||
const event: trace.ActionTraceEvent = {
|
const event: trace.ActionTraceEvent = {
|
||||||
timestamp: monotonicTime(),
|
timestamp: metadata.startTime,
|
||||||
type: 'action',
|
type: 'action',
|
||||||
contextId: this._contextId,
|
contextId: this._contextId,
|
||||||
metadata,
|
metadata,
|
||||||
snapshots: snapshotsForMetadata(metadata),
|
};
|
||||||
|
this._appendTraceEvent(event);
|
||||||
|
this._pendingCalls.delete(metadata.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
onEvent(sdkObject: SdkObject, metadata: CallMetadata) {
|
||||||
|
if (!sdkObject.attribution.page)
|
||||||
|
return;
|
||||||
|
const event: trace.ActionTraceEvent = {
|
||||||
|
timestamp: metadata.startTime,
|
||||||
|
type: 'event',
|
||||||
|
contextId: this._contextId,
|
||||||
|
metadata,
|
||||||
};
|
};
|
||||||
this._appendTraceEvent(event);
|
this._appendTraceEvent(event);
|
||||||
}
|
}
|
||||||
@ -226,6 +243,8 @@ class ContextTracer {
|
|||||||
this._disposed = true;
|
this._disposed = true;
|
||||||
helper.removeEventListeners(this._eventListeners);
|
helper.removeEventListeners(this._eventListeners);
|
||||||
await this._snapshotter.dispose();
|
await this._snapshotter.dispose();
|
||||||
|
for (const { sdkObject, metadata } of this._pendingCalls.values())
|
||||||
|
this.onAfterCall(sdkObject, metadata);
|
||||||
const event: trace.ContextDestroyedTraceEvent = {
|
const event: trace.ContextDestroyedTraceEvent = {
|
||||||
timestamp: monotonicTime(),
|
timestamp: monotonicTime(),
|
||||||
type: 'context-destroyed',
|
type: 'context-destroyed',
|
||||||
|
@ -84,9 +84,8 @@ export class TraceModel {
|
|||||||
case 'action': {
|
case 'action': {
|
||||||
const metadata = event.metadata;
|
const metadata = event.metadata;
|
||||||
const { pageEntry } = this.pageEntries.get(metadata.pageId!)!;
|
const { pageEntry } = this.pageEntries.get(metadata.pageId!)!;
|
||||||
const actionId = event.contextId + '/' + metadata.pageId + '/' + pageEntry.actions.length;
|
|
||||||
const action: ActionEntry = {
|
const action: ActionEntry = {
|
||||||
actionId,
|
actionId: metadata.id,
|
||||||
resources: [],
|
resources: [],
|
||||||
...event,
|
...event,
|
||||||
};
|
};
|
||||||
@ -106,28 +105,6 @@ export class TraceModel {
|
|||||||
contextEntry.startTime = Math.min(contextEntry.startTime, event.timestamp);
|
contextEntry.startTime = Math.min(contextEntry.startTime, event.timestamp);
|
||||||
contextEntry.endTime = Math.max(contextEntry.endTime, event.timestamp);
|
contextEntry.endTime = Math.max(contextEntry.endTime, event.timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
actionById(actionId: string): { context: ContextEntry, page: PageEntry, action: ActionEntry } {
|
|
||||||
const [contextId, pageId, actionIndex] = actionId.split('/');
|
|
||||||
const context = this.contextEntries.get(contextId)!;
|
|
||||||
const page = context.pages.find(entry => entry.created.pageId === pageId)!;
|
|
||||||
const action = page.actions[+actionIndex];
|
|
||||||
return { context, page, action };
|
|
||||||
}
|
|
||||||
|
|
||||||
findPage(pageId: string): { contextEntry: ContextEntry | undefined, pageEntry: PageEntry | undefined } {
|
|
||||||
let contextEntry;
|
|
||||||
let pageEntry;
|
|
||||||
for (const c of this.contextEntries.values()) {
|
|
||||||
for (const p of c.pages) {
|
|
||||||
if (p.created.pageId === pageId) {
|
|
||||||
contextEntry = c;
|
|
||||||
pageEntry = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { contextEntry, pageEntry };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ContextEntry = {
|
export type ContextEntry = {
|
||||||
|
@ -29,7 +29,7 @@ export const CallLogView: React.FC<CallLogProps> = ({
|
|||||||
onHover,
|
onHover,
|
||||||
}) => {
|
}) => {
|
||||||
const messagesEndRef = React.createRef<HTMLDivElement>();
|
const messagesEndRef = React.createRef<HTMLDivElement>();
|
||||||
const [expandOverrides, setExpandOverrides] = React.useState<Map<number, boolean>>(new Map());
|
const [expandOverrides, setExpandOverrides] = React.useState<Map<string, boolean>>(new Map());
|
||||||
React.useLayoutEffect(() => {
|
React.useLayoutEffect(() => {
|
||||||
if (log.find(callLog => callLog.reveal))
|
if (log.find(callLog => callLog.reveal))
|
||||||
messagesEndRef.current?.scrollIntoView({ block: 'center', inline: 'nearest' });
|
messagesEndRef.current?.scrollIntoView({ block: 'center', inline: 'nearest' });
|
||||||
|
@ -33,14 +33,14 @@ export const Main: React.FC = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const [sources, setSources] = React.useState<Source[]>([]);
|
const [sources, setSources] = React.useState<Source[]>([]);
|
||||||
const [paused, setPaused] = React.useState(false);
|
const [paused, setPaused] = React.useState(false);
|
||||||
const [log, setLog] = React.useState(new Map<number, CallLog>());
|
const [log, setLog] = React.useState(new Map<string, CallLog>());
|
||||||
const [mode, setMode] = React.useState<Mode>('none');
|
const [mode, setMode] = React.useState<Mode>('none');
|
||||||
|
|
||||||
window.playwrightSetMode = setMode;
|
window.playwrightSetMode = setMode;
|
||||||
window.playwrightSetSources = setSources;
|
window.playwrightSetSources = setSources;
|
||||||
window.playwrightSetPaused = setPaused;
|
window.playwrightSetPaused = setPaused;
|
||||||
window.playwrightUpdateLogs = callLogs => {
|
window.playwrightUpdateLogs = callLogs => {
|
||||||
const newLog = new Map<number, CallLog>(log);
|
const newLog = new Map<string, CallLog>(log);
|
||||||
for (const callLog of callLogs) {
|
for (const callLog of callLogs) {
|
||||||
callLog.reveal = !log.has(callLog.id);
|
callLog.reveal = !log.has(callLog.id);
|
||||||
newLog.set(callLog.id, callLog);
|
newLog.set(callLog.id, callLog);
|
||||||
|
@ -34,7 +34,7 @@ declare global {
|
|||||||
export interface RecorderProps {
|
export interface RecorderProps {
|
||||||
sources: Source[],
|
sources: Source[],
|
||||||
paused: boolean,
|
paused: boolean,
|
||||||
log: Map<number, CallLog>,
|
log: Map<string, CallLog>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
initialSelector?: string,
|
initialSelector?: string,
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ export const ActionList: React.FC<ActionListProps> = ({
|
|||||||
onMouseLeave={() => (highlightedAction === actionEntry) && onHighlighted(undefined)}
|
onMouseLeave={() => (highlightedAction === actionEntry) && onHighlighted(undefined)}
|
||||||
>
|
>
|
||||||
<div className={'action-error codicon codicon-issues'} hidden={!metadata.error} />
|
<div className={'action-error codicon codicon-issues'} hidden={!metadata.error} />
|
||||||
<div className='action-title'>{metadata.apiName}</div>
|
<div className='action-title'>{metadata.apiName || metadata.method}</div>
|
||||||
{metadata.params.selector && <div className='action-selector' title={metadata.params.selector}>{metadata.params.selector}</div>}
|
{metadata.params.selector && <div className='action-selector' title={metadata.params.selector}>{metadata.params.selector}</div>}
|
||||||
{metadata.method === 'goto' && metadata.params.url && <div className='action-url' title={metadata.params.url}>{metadata.params.url}</div>}
|
{metadata.method === 'goto' && metadata.params.url && <div className='action-url' title={metadata.params.url}>{metadata.params.url}</div>}
|
||||||
</div>;
|
</div>;
|
||||||
|
@ -68,7 +68,7 @@ export const SourceTab: React.FunctionComponent<{
|
|||||||
return value;
|
return value;
|
||||||
}, [stackInfo, selectedFrame], '');
|
}, [stackInfo, selectedFrame], '');
|
||||||
|
|
||||||
const targetLine = typeof stackInfo === 'string' ? 0 : stackInfo.frames[selectedFrame].line || 0;
|
const targetLine = typeof stackInfo === 'string' ? 0 : stackInfo.frames[selectedFrame]?.line || 0;
|
||||||
|
|
||||||
const targetLineRef = React.createRef<HTMLDivElement>();
|
const targetLineRef = React.createRef<HTMLDivElement>();
|
||||||
React.useLayoutEffect(() => {
|
React.useLayoutEffect(() => {
|
||||||
|
@ -44,6 +44,7 @@ export const Workbench: React.FunctionComponent<{
|
|||||||
const actions: ActionEntry[] = [];
|
const actions: ActionEntry[] = [];
|
||||||
for (const page of context.pages)
|
for (const page of context.pages)
|
||||||
actions.push(...page.actions);
|
actions.push(...page.actions);
|
||||||
|
actions.sort((a, b) => a.timestamp - b.timestamp);
|
||||||
return actions;
|
return actions;
|
||||||
}, [context]);
|
}, [context]);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user