chore: update trace event on action merge (#32860)
This commit is contained in:
parent
908b0de5d4
commit
11014145ce
|
|
@ -139,7 +139,7 @@ export class Recorder implements InstrumentationListener, IRecorder {
|
||||||
});
|
});
|
||||||
this._contextRecorder.on(ContextRecorder.Events.Change, (data: { sources: Source[], actions: actions.ActionInContext[] }) => {
|
this._contextRecorder.on(ContextRecorder.Events.Change, (data: { sources: Source[], actions: actions.ActionInContext[] }) => {
|
||||||
this._recorderSources = data.sources;
|
this._recorderSources = data.sources;
|
||||||
recorderApp.setActions(data.actions);
|
recorderApp.setActions(data.actions, data.sources);
|
||||||
this._pushAllSources();
|
this._pushAllSources();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ export class ContextRecorder extends EventEmitter {
|
||||||
name: 'closePage',
|
name: 'closePage',
|
||||||
signals: [],
|
signals: [],
|
||||||
},
|
},
|
||||||
timestamp: monotonicTime()
|
startTime: monotonicTime()
|
||||||
});
|
});
|
||||||
this._pageAliases.delete(page);
|
this._pageAliases.delete(page);
|
||||||
});
|
});
|
||||||
|
|
@ -195,7 +195,7 @@ export class ContextRecorder extends EventEmitter {
|
||||||
url: page.mainFrame().url(),
|
url: page.mainFrame().url(),
|
||||||
signals: [],
|
signals: [],
|
||||||
},
|
},
|
||||||
timestamp: monotonicTime()
|
startTime: monotonicTime()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -232,7 +232,7 @@ export class ContextRecorder extends EventEmitter {
|
||||||
frame: frameDescription,
|
frame: frameDescription,
|
||||||
action,
|
action,
|
||||||
description: undefined,
|
description: undefined,
|
||||||
timestamp: monotonicTime()
|
startTime: monotonicTime()
|
||||||
};
|
};
|
||||||
await this._delegate.rewriteActionInContext?.(this._pageAliases, actionInContext);
|
await this._delegate.rewriteActionInContext?.(this._pageAliases, actionInContext);
|
||||||
return actionInContext;
|
return actionInContext;
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ export class EmptyRecorderApp extends EventEmitter implements IRecorderApp {
|
||||||
async setSelector(selector: string, userGesture?: boolean): Promise<void> {}
|
async setSelector(selector: string, userGesture?: boolean): Promise<void> {}
|
||||||
async updateCallLogs(callLogs: CallLog[]): Promise<void> {}
|
async updateCallLogs(callLogs: CallLog[]): Promise<void> {}
|
||||||
async setSources(sources: Source[]): Promise<void> {}
|
async setSources(sources: Source[]): Promise<void> {}
|
||||||
async setActions(actions: actions.ActionInContext[]): Promise<void> {}
|
async setActions(actions: actions.ActionInContext[], sources: Source[]): Promise<void> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RecorderApp extends EventEmitter implements IRecorderApp {
|
export class RecorderApp extends EventEmitter implements IRecorderApp {
|
||||||
|
|
@ -155,7 +155,7 @@ export class RecorderApp extends EventEmitter implements IRecorderApp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async setActions(actions: actions.ActionInContext[]): Promise<void> {
|
async setActions(actions: actions.ActionInContext[], sources: Source[]): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async setSelector(selector: string, userGesture?: boolean): Promise<void> {
|
async setSelector(selector: string, userGesture?: boolean): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -75,9 +75,12 @@ export class RecorderCollection extends EventEmitter {
|
||||||
this._fireChange();
|
this._fireChange();
|
||||||
const error = await callback?.(callMetadata).catch((e: Error) => e);
|
const error = await callback?.(callMetadata).catch((e: Error) => e);
|
||||||
callMetadata.endTime = monotonicTime();
|
callMetadata.endTime = monotonicTime();
|
||||||
|
actionInContext.endTime = callMetadata.endTime;
|
||||||
callMetadata.error = error ? serializeError(error) : undefined;
|
callMetadata.error = error ? serializeError(error) : undefined;
|
||||||
// Do not wait for onAfterCall so that performAction returned immediately after the action.
|
// Do not wait for onAfterCall so that performAction returned immediately after the action.
|
||||||
mainFrame.instrumentation.onAfterCall(mainFrame, callMetadata).catch(() => {});
|
mainFrame.instrumentation.onAfterCall(mainFrame, callMetadata).then(() => {
|
||||||
|
this._fireChange();
|
||||||
|
}).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
signal(pageAlias: string, frame: Frame, signal: Signal) {
|
signal(pageAlias: string, frame: Frame, signal: Signal) {
|
||||||
|
|
@ -94,7 +97,7 @@ export class RecorderCollection extends EventEmitter {
|
||||||
generateGoto = true;
|
generateGoto = true;
|
||||||
else if (lastAction.action.name !== 'click' && lastAction.action.name !== 'press')
|
else if (lastAction.action.name !== 'click' && lastAction.action.name !== 'press')
|
||||||
generateGoto = true;
|
generateGoto = true;
|
||||||
else if (timestamp - lastAction.timestamp > signalThreshold)
|
else if (timestamp - lastAction.startTime > signalThreshold)
|
||||||
generateGoto = true;
|
generateGoto = true;
|
||||||
|
|
||||||
if (generateGoto) {
|
if (generateGoto) {
|
||||||
|
|
@ -108,7 +111,8 @@ export class RecorderCollection extends EventEmitter {
|
||||||
url: frame.url(),
|
url: frame.url(),
|
||||||
signals: [],
|
signals: [],
|
||||||
},
|
},
|
||||||
timestamp
|
startTime: timestamp,
|
||||||
|
endTime: timestamp,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ export interface IRecorderApp extends EventEmitter {
|
||||||
setSelector(selector: string, userGesture?: boolean): Promise<void>;
|
setSelector(selector: string, userGesture?: boolean): Promise<void>;
|
||||||
updateCallLogs(callLogs: CallLog[]): Promise<void>;
|
updateCallLogs(callLogs: CallLog[]): Promise<void>;
|
||||||
setSources(sources: Source[]): Promise<void>;
|
setSources(sources: Source[]): Promise<void>;
|
||||||
setActions(actions: actions.ActionInContext[]): Promise<void>;
|
setActions(actions: actions.ActionInContext[], sources: Source[]): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IRecorderAppFactory = (recorder: IRecorder) => Promise<IRecorderApp>;
|
export type IRecorderAppFactory = (recorder: IRecorder) => Promise<IRecorderApp>;
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,8 @@ export class RecorderInTraceViewer extends EventEmitter implements IRecorderApp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async setActions(actions: actions.ActionInContext[]): Promise<void> {
|
async setActions(actions: actions.ActionInContext[], sources: Source[]): Promise<void> {
|
||||||
this._transport.deliverEvent('setActions', { actions });
|
this._transport.deliverEvent('setActions', { actions, sources });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,12 +76,11 @@ export function callMetadataForAction(pageAliases: Map<Page, string>, actionInCo
|
||||||
|
|
||||||
const callMetadata: CallMetadata = {
|
const callMetadata: CallMetadata = {
|
||||||
id: `call@${createGuid()}`,
|
id: `call@${createGuid()}`,
|
||||||
stepId: `recorder@${createGuid()}`,
|
|
||||||
apiName: 'page.' + method,
|
apiName: 'page.' + method,
|
||||||
objectId: mainFrame.guid,
|
objectId: mainFrame.guid,
|
||||||
pageId: mainFrame._page.guid,
|
pageId: mainFrame._page.guid,
|
||||||
frameId: mainFrame.guid,
|
frameId: mainFrame.guid,
|
||||||
startTime: actionInContext.timestamp,
|
startTime: actionInContext.startTime,
|
||||||
endTime: 0,
|
endTime: 0,
|
||||||
type: 'Frame',
|
type: 'Frame',
|
||||||
method,
|
method,
|
||||||
|
|
@ -102,7 +101,9 @@ export function collapseActions(actions: actions.ActionInContext[]): actions.Act
|
||||||
result.push(action);
|
result.push(action);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
const startTime = result[result.length - 1].startTime;
|
||||||
result[result.length - 1] = action;
|
result[result.length - 1] = action;
|
||||||
|
result[result.length - 1].startTime = startTime;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -153,5 +153,6 @@ export type ActionInContext = {
|
||||||
frame: FrameDescription;
|
frame: FrameDescription;
|
||||||
description?: string;
|
description?: string;
|
||||||
action: Action;
|
action: Action;
|
||||||
timestamp: number;
|
startTime: number;
|
||||||
|
endTime?: number;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -17,18 +17,17 @@
|
||||||
import { CodeMirrorWrapper } from '@web/components/codeMirrorWrapper';
|
import { CodeMirrorWrapper } from '@web/components/codeMirrorWrapper';
|
||||||
import type { Language } from '@web/components/codeMirrorWrapper';
|
import type { Language } from '@web/components/codeMirrorWrapper';
|
||||||
import { ToolbarButton } from '@web/components/toolbarButton';
|
import { ToolbarButton } from '@web/components/toolbarButton';
|
||||||
import { copy, useSetting } from '@web/uiUtils';
|
import { copy } from '@web/uiUtils';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import './sourceTab.css';
|
import './sourceTab.css';
|
||||||
|
|
||||||
export const InspectorTab: React.FunctionComponent<{
|
export const InspectorTab: React.FunctionComponent<{
|
||||||
|
showScreenshot: boolean,
|
||||||
sdkLanguage: Language,
|
sdkLanguage: Language,
|
||||||
setIsInspecting: (isInspecting: boolean) => void,
|
setIsInspecting: (isInspecting: boolean) => void,
|
||||||
highlightedLocator: string,
|
highlightedLocator: string,
|
||||||
setHighlightedLocator: (locator: string) => void,
|
setHighlightedLocator: (locator: string) => void,
|
||||||
}> = ({ sdkLanguage, setIsInspecting, highlightedLocator, setHighlightedLocator }) => {
|
}> = ({ showScreenshot, sdkLanguage, setIsInspecting, highlightedLocator, setHighlightedLocator }) => {
|
||||||
const [showScreenshot] = useSetting('screenshot-instead-of-snapshot', false);
|
|
||||||
|
|
||||||
return <div className='vbox' style={{ backgroundColor: 'var(--vscode-sideBar-background)' }}>
|
return <div className='vbox' style={{ backgroundColor: 'var(--vscode-sideBar-background)' }}>
|
||||||
<div style={{ margin: '10px 0px 10px 10px', color: 'var(--vscode-editorCodeLens-foreground)', flex: 'none' }}>Locator</div>
|
<div style={{ margin: '10px 0px 10px 10px', color: 'var(--vscode-editorCodeLens-foreground)', flex: 'none' }}>Locator</div>
|
||||||
<div style={{ margin: '0 10px 10px', flex: 'auto' }}>
|
<div style={{ margin: '0 10px 10px', flex: 'auto' }}>
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,8 @@ export const BackendProvider: React.FunctionComponent<React.PropsWithChildren<{
|
||||||
}>> = ({ guid, children }) => {
|
}>> = ({ guid, children }) => {
|
||||||
const [connection, setConnection] = React.useState<Connection | undefined>(undefined);
|
const [connection, setConnection] = React.useState<Connection | undefined>(undefined);
|
||||||
const [mode, setMode] = React.useState<Mode>('none');
|
const [mode, setMode] = React.useState<Mode>('none');
|
||||||
const [actions, setActions] = React.useState<actionTypes.ActionInContext[]>([]);
|
const [actions, setActions] = React.useState<{ actions: actionTypes.ActionInContext[], sources: Source[] }>({ actions: [], sources: [] });
|
||||||
const [sources, setSources] = React.useState<Source[]>([]);
|
const callbacks = React.useRef({ setMode, setActions });
|
||||||
const callbacks = React.useRef({ setMode, setActions, setSources });
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const wsURL = new URL(`../${guid}`, window.location.toString());
|
const wsURL = new URL(`../${guid}`, window.location.toString());
|
||||||
|
|
@ -40,8 +39,8 @@ export const BackendProvider: React.FunctionComponent<React.PropsWithChildren<{
|
||||||
}, [guid]);
|
}, [guid]);
|
||||||
|
|
||||||
const backend = React.useMemo(() => {
|
const backend = React.useMemo(() => {
|
||||||
return connection ? { mode, actions, sources, connection } : undefined;
|
return connection ? { mode, actions: actions.actions, sources: actions.sources, connection } : undefined;
|
||||||
}, [actions, mode, sources, connection]);
|
}, [actions, mode, connection]);
|
||||||
|
|
||||||
return <BackendContext.Provider value={backend}>
|
return <BackendContext.Provider value={backend}>
|
||||||
{children}
|
{children}
|
||||||
|
|
@ -56,8 +55,7 @@ export type Backend = {
|
||||||
|
|
||||||
type ConnectionCallbacks = {
|
type ConnectionCallbacks = {
|
||||||
setMode: (mode: Mode) => void;
|
setMode: (mode: Mode) => void;
|
||||||
setActions: (actions: actionTypes.ActionInContext[]) => void;
|
setActions: (data: { actions: actionTypes.ActionInContext[], sources: Source[] }) => void;
|
||||||
setSources: (sources: Source[]) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Connection {
|
class Connection {
|
||||||
|
|
@ -111,14 +109,10 @@ class Connection {
|
||||||
const { mode } = params as { mode: Mode };
|
const { mode } = params as { mode: Mode };
|
||||||
this._options.setMode(mode);
|
this._options.setMode(mode);
|
||||||
}
|
}
|
||||||
if (method === 'setSources') {
|
|
||||||
const { sources } = params as { sources: Source[] };
|
|
||||||
this._options.setSources(sources);
|
|
||||||
(window as any).playwrightSourcesEchoForTest = sources;
|
|
||||||
}
|
|
||||||
if (method === 'setActions') {
|
if (method === 'setActions') {
|
||||||
const { actions } = params as { actions: actionTypes.ActionInContext[] };
|
const { actions, sources } = params as { actions: actionTypes.ActionInContext[], sources: Source[] };
|
||||||
this._options.setActions(actions.filter(a => a.action.name !== 'openPage' && a.action.name !== 'closePage'));
|
this._options.setActions({ actions: actions.filter(a => a.action.name !== 'openPage' && a.action.name !== 'closePage'), sources });
|
||||||
|
(window as any).playwrightSourcesEchoForTest = sources;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,17 +54,25 @@ export const Workbench: React.FunctionComponent = () => {
|
||||||
const backend = React.useContext(BackendContext);
|
const backend = React.useContext(BackendContext);
|
||||||
const model = React.useContext(ModelContext);
|
const model = React.useContext(ModelContext);
|
||||||
const [fileId, setFileId] = React.useState<string | undefined>();
|
const [fileId, setFileId] = React.useState<string | undefined>();
|
||||||
const [selectedCallTime, setSelectedCallTime] = React.useState<number | undefined>(undefined);
|
const [selectedStartTime, setSelectedStartTime] = React.useState<number | undefined>(undefined);
|
||||||
const [isInspecting, setIsInspecting] = React.useState(false);
|
const [isInspecting, setIsInspecting] = React.useState(false);
|
||||||
const [highlightedLocator, setHighlightedLocator] = React.useState<string>('');
|
const [highlightedLocatorInProperties, setHighlightedLocatorInProperties] = React.useState<string>('');
|
||||||
|
const [highlightedLocatorInTrace, setHighlightedLocatorInTrace] = React.useState<string>('');
|
||||||
|
const [traceCallId, setTraceCallId] = React.useState<string | undefined>();
|
||||||
|
|
||||||
const setSelectedAction = React.useCallback((action: actionTypes.ActionInContext | undefined) => {
|
const setSelectedAction = React.useCallback((action: actionTypes.ActionInContext | undefined) => {
|
||||||
setSelectedCallTime(action?.timestamp);
|
setSelectedStartTime(action?.startTime);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const selectedAction = React.useMemo(() => {
|
const selectedAction = React.useMemo(() => {
|
||||||
return backend?.actions.find(a => a.timestamp === selectedCallTime);
|
return backend?.actions.find(a => a.startTime === selectedStartTime);
|
||||||
}, [backend?.actions, selectedCallTime]);
|
}, [backend?.actions, selectedStartTime]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const callId = model?.actions.find(a => a.endTime && a.endTime === selectedAction?.endTime)?.callId;
|
||||||
|
if (callId)
|
||||||
|
setTraceCallId(callId);
|
||||||
|
}, [model, selectedAction]);
|
||||||
|
|
||||||
const source = React.useMemo(() => backend?.sources.find(s => s.id === fileId) || backend?.sources[0], [backend?.sources, fileId]);
|
const source = React.useMemo(() => backend?.sources.find(s => s.id === fileId) || backend?.sources[0], [backend?.sources, fileId]);
|
||||||
const sourceLocation = React.useMemo(() => {
|
const sourceLocation = React.useMemo(() => {
|
||||||
|
|
@ -95,6 +103,17 @@ export const Workbench: React.FunctionComponent = () => {
|
||||||
return { boundaries };
|
return { boundaries };
|
||||||
}, [model]);
|
}, [model]);
|
||||||
|
|
||||||
|
const locatorPickedInTrace = React.useCallback((locator: string) => {
|
||||||
|
setHighlightedLocatorInProperties(locator);
|
||||||
|
setHighlightedLocatorInTrace('');
|
||||||
|
setIsInspecting(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const locatorTypedInProperties = React.useCallback((locator: string) => {
|
||||||
|
setHighlightedLocatorInTrace(locator);
|
||||||
|
setHighlightedLocatorInProperties(locator);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const actionList = <ActionListView
|
const actionList = <ActionListView
|
||||||
sdkLanguage={sdkLanguage}
|
sdkLanguage={sdkLanguage}
|
||||||
actions={backend?.actions || []}
|
actions={backend?.actions || []}
|
||||||
|
|
@ -129,25 +148,23 @@ export const Workbench: React.FunctionComponent = () => {
|
||||||
<SourceChooser fileId={fileId} sources={backend?.sources || []} setFileId={fileId => {
|
<SourceChooser fileId={fileId} sources={backend?.sources || []} setFileId={fileId => {
|
||||||
setFileId(fileId);
|
setFileId(fileId);
|
||||||
}} />
|
}} />
|
||||||
<ToolbarButton icon='clear-all' title='Clear' onClick={() => {
|
|
||||||
}}></ToolbarButton>
|
|
||||||
<ToolbarButton icon='color-mode' title='Toggle color mode' toggled={false} onClick={() => toggleTheme()}></ToolbarButton>
|
<ToolbarButton icon='color-mode' title='Toggle color mode' toggled={false} onClick={() => toggleTheme()}></ToolbarButton>
|
||||||
</Toolbar>;
|
</Toolbar>;
|
||||||
|
|
||||||
const sidebarTabbedPane = <TabbedPane tabs={[actionsTab]} />;
|
const sidebarTabbedPane = <TabbedPane tabs={[actionsTab]} />;
|
||||||
const traceView = <TraceView
|
const traceView = <TraceView
|
||||||
sdkLanguage={sdkLanguage}
|
sdkLanguage={sdkLanguage}
|
||||||
callTime={selectedCallTime || 0}
|
callId={traceCallId}
|
||||||
isInspecting={isInspecting}
|
isInspecting={isInspecting}
|
||||||
setIsInspecting={setIsInspecting}
|
setIsInspecting={setIsInspecting}
|
||||||
highlightedLocator={highlightedLocator}
|
highlightedLocator={highlightedLocatorInTrace}
|
||||||
setHighlightedLocator={setHighlightedLocator} />;
|
setHighlightedLocator={locatorPickedInTrace} />;
|
||||||
const propertiesView = <PropertiesView
|
const propertiesView = <PropertiesView
|
||||||
sdkLanguage={sdkLanguage}
|
sdkLanguage={sdkLanguage}
|
||||||
boundaries={boundaries}
|
boundaries={boundaries}
|
||||||
setIsInspecting={setIsInspecting}
|
setIsInspecting={setIsInspecting}
|
||||||
highlightedLocator={highlightedLocator}
|
highlightedLocator={highlightedLocatorInProperties}
|
||||||
setHighlightedLocator={setHighlightedLocator}
|
setHighlightedLocator={locatorTypedInProperties}
|
||||||
sourceLocation={sourceLocation} />;
|
sourceLocation={sourceLocation} />;
|
||||||
|
|
||||||
return <div className='vbox workbench'>
|
return <div className='vbox workbench'>
|
||||||
|
|
@ -196,6 +213,7 @@ const PropertiesView: React.FunctionComponent<{
|
||||||
id: 'inspector',
|
id: 'inspector',
|
||||||
title: 'Locator',
|
title: 'Locator',
|
||||||
render: () => <InspectorTab
|
render: () => <InspectorTab
|
||||||
|
showScreenshot={false}
|
||||||
sdkLanguage={sdkLanguage}
|
sdkLanguage={sdkLanguage}
|
||||||
setIsInspecting={setIsInspecting}
|
setIsInspecting={setIsInspecting}
|
||||||
highlightedLocator={highlightedLocator}
|
highlightedLocator={highlightedLocator}
|
||||||
|
|
@ -240,14 +258,14 @@ const PropertiesView: React.FunctionComponent<{
|
||||||
|
|
||||||
const TraceView: React.FunctionComponent<{
|
const TraceView: React.FunctionComponent<{
|
||||||
sdkLanguage: Language,
|
sdkLanguage: Language,
|
||||||
callTime: number;
|
callId: string | undefined,
|
||||||
isInspecting: boolean;
|
isInspecting: boolean;
|
||||||
setIsInspecting: (value: boolean) => void;
|
setIsInspecting: (value: boolean) => void;
|
||||||
highlightedLocator: string;
|
highlightedLocator: string;
|
||||||
setHighlightedLocator: (locator: string) => void;
|
setHighlightedLocator: (locator: string) => void;
|
||||||
}> = ({
|
}> = ({
|
||||||
sdkLanguage,
|
sdkLanguage,
|
||||||
callTime,
|
callId,
|
||||||
isInspecting,
|
isInspecting,
|
||||||
setIsInspecting,
|
setIsInspecting,
|
||||||
highlightedLocator,
|
highlightedLocator,
|
||||||
|
|
@ -255,8 +273,8 @@ const TraceView: React.FunctionComponent<{
|
||||||
}) => {
|
}) => {
|
||||||
const model = React.useContext(ModelContext);
|
const model = React.useContext(ModelContext);
|
||||||
const action = React.useMemo(() => {
|
const action = React.useMemo(() => {
|
||||||
return model?.actions.find(a => a.startTime === callTime);
|
return model?.actions.find(a => a.callId === callId);
|
||||||
}, [model, callTime]);
|
}, [model, callId]);
|
||||||
|
|
||||||
const snapshot = React.useMemo(() => {
|
const snapshot = React.useMemo(() => {
|
||||||
const snapshot = collectSnapshots(action);
|
const snapshot = collectSnapshots(action);
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,7 @@ export const Workbench: React.FunctionComponent<{
|
||||||
id: 'inspector',
|
id: 'inspector',
|
||||||
title: 'Locator',
|
title: 'Locator',
|
||||||
render: () => <InspectorTab
|
render: () => <InspectorTab
|
||||||
|
showScreenshot={showScreenshot}
|
||||||
sdkLanguage={sdkLanguage}
|
sdkLanguage={sdkLanguage}
|
||||||
setIsInspecting={setIsInspecting}
|
setIsInspecting={setIsInspecting}
|
||||||
highlightedLocator={highlightedLocator}
|
highlightedLocator={highlightedLocator}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue