mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(inspector): remove snapshots (#6909)
This commit is contained in:
parent
a96491cbbb
commit
233f1874da
@ -50,11 +50,8 @@ export class Recorder {
|
||||
private _actionPoint: Point | undefined;
|
||||
private _actionSelector: string | undefined;
|
||||
private _params: { isUnderTest: boolean; };
|
||||
private _snapshotIframe: HTMLIFrameElement | undefined;
|
||||
private _snapshotUrl: string | undefined;
|
||||
private _snapshotBaseUrl: string;
|
||||
|
||||
constructor(injectedScript: InjectedScript, params: { isUnderTest: boolean, snapshotBaseUrl: string }) {
|
||||
constructor(injectedScript: InjectedScript, params: { isUnderTest: boolean }) {
|
||||
this._params = params;
|
||||
this._injectedScript = injectedScript;
|
||||
this._outerGlassPaneElement = document.createElement('x-pw-glass');
|
||||
@ -66,7 +63,6 @@ export class Recorder {
|
||||
this._outerGlassPaneElement.style.zIndex = '2147483647';
|
||||
this._outerGlassPaneElement.style.pointerEvents = 'none';
|
||||
this._outerGlassPaneElement.style.display = 'flex';
|
||||
this._snapshotBaseUrl = params.snapshotBaseUrl;
|
||||
|
||||
this._tooltipElement = document.createElement('x-pw-tooltip');
|
||||
this._actionPointElement = document.createElement('x-pw-action-point');
|
||||
@ -160,28 +156,6 @@ export class Recorder {
|
||||
document.documentElement.appendChild(this._outerGlassPaneElement);
|
||||
}
|
||||
|
||||
private _createSnapshotIframeIfNeeded(): HTMLIFrameElement | undefined {
|
||||
if (this._snapshotIframe)
|
||||
return this._snapshotIframe;
|
||||
if (window.top === window) {
|
||||
this._snapshotIframe = document.createElement('iframe');
|
||||
this._snapshotIframe.src = this._snapshotBaseUrl;
|
||||
this._snapshotIframe.style.background = '#ff000060';
|
||||
this._snapshotIframe.style.position = 'fixed';
|
||||
this._snapshotIframe.style.top = '0';
|
||||
this._snapshotIframe.style.right = '0';
|
||||
this._snapshotIframe.style.bottom = '0';
|
||||
this._snapshotIframe.style.left = '0';
|
||||
this._snapshotIframe.style.border = 'none';
|
||||
this._snapshotIframe.style.width = '100%';
|
||||
this._snapshotIframe.style.height = '100%';
|
||||
this._snapshotIframe.style.zIndex = '2147483647';
|
||||
this._snapshotIframe.style.visibility = 'hidden';
|
||||
document.documentElement.appendChild(this._snapshotIframe);
|
||||
}
|
||||
return this._snapshotIframe;
|
||||
}
|
||||
|
||||
private async _pollRecorderMode() {
|
||||
const pollPeriod = 1000;
|
||||
if (this._pollRecorderModeTimer)
|
||||
@ -192,7 +166,7 @@ export class Recorder {
|
||||
return;
|
||||
}
|
||||
|
||||
const { mode, actionPoint, actionSelector, snapshotUrl } = state;
|
||||
const { mode, actionPoint, actionSelector } = state;
|
||||
if (mode !== this._mode) {
|
||||
this._mode = mode;
|
||||
this._clearHighlight();
|
||||
@ -221,18 +195,6 @@ export class Recorder {
|
||||
this._updateHighlight();
|
||||
this._actionSelector = actionSelector;
|
||||
}
|
||||
if (snapshotUrl !== this._snapshotUrl) {
|
||||
this._snapshotUrl = snapshotUrl;
|
||||
const snapshotIframe = this._createSnapshotIframeIfNeeded();
|
||||
if (snapshotIframe) {
|
||||
if (!snapshotUrl) {
|
||||
snapshotIframe.style.visibility = 'hidden';
|
||||
} else {
|
||||
snapshotIframe.style.visibility = 'visible';
|
||||
snapshotIframe.contentWindow?.postMessage({ snapshotUrl }, '*');
|
||||
}
|
||||
}
|
||||
}
|
||||
this._pollRecorderModeTimer = setTimeout(() => this._pollRecorderMode(), pollPeriod);
|
||||
}
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ import { Point } from '../../../common/types';
|
||||
export type Mode = 'inspecting' | 'recording' | 'none';
|
||||
|
||||
export type EventData = {
|
||||
event: 'clear' | 'resume' | 'step' | 'pause' | 'setMode' | 'selectorUpdated' | 'callLogHovered';
|
||||
event: 'clear' | 'resume' | 'step' | 'pause' | 'setMode' | 'selectorUpdated';
|
||||
params: any;
|
||||
};
|
||||
|
||||
@ -27,7 +27,6 @@ export type UIState = {
|
||||
mode: Mode;
|
||||
actionPoint?: Point;
|
||||
actionSelector?: string;
|
||||
snapshotUrl?: string;
|
||||
};
|
||||
|
||||
export type CallLogStatus = 'in-progress' | 'done' | 'error' | 'paused';
|
||||
@ -44,11 +43,6 @@ export type CallLog = {
|
||||
url?: string,
|
||||
selector?: string,
|
||||
};
|
||||
snapshots: {
|
||||
before: boolean,
|
||||
action: boolean,
|
||||
after: boolean,
|
||||
}
|
||||
};
|
||||
|
||||
export type SourceHighlight = {
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
import { CallMetadata } from '../../instrumentation';
|
||||
import { CallLog, CallLogStatus } from './recorderTypes';
|
||||
|
||||
export function metadataToCallLog(metadata: CallMetadata, status: CallLogStatus, snapshots: Set<string>): CallLog {
|
||||
export function metadataToCallLog(metadata: CallMetadata, status: CallLogStatus): CallLog {
|
||||
const title = metadata.apiName || metadata.method;
|
||||
if (metadata.error)
|
||||
status = 'error';
|
||||
@ -38,23 +38,6 @@ export function metadataToCallLog(metadata: CallMetadata, status: CallLogStatus,
|
||||
error: metadata.error,
|
||||
params,
|
||||
duration,
|
||||
snapshots: {
|
||||
before: showBeforeSnapshot(metadata) && snapshots.has(`before@${metadata.id}`),
|
||||
action: showActionSnapshot(metadata) && snapshots.has(`action@${metadata.id}`),
|
||||
after: showAfterSnapshot(metadata) && snapshots.has(`after@${metadata.id}`),
|
||||
}
|
||||
};
|
||||
return callLog;
|
||||
}
|
||||
|
||||
function showBeforeSnapshot(metadata: CallMetadata): boolean {
|
||||
return metadata.method === 'close';
|
||||
}
|
||||
|
||||
function showActionSnapshot(metadata: CallMetadata): boolean {
|
||||
return ['click', 'dblclick', 'check', 'uncheck', 'fill', 'press'].includes(metadata.method);
|
||||
}
|
||||
|
||||
function showAfterSnapshot(metadata: CallMetadata): boolean {
|
||||
return ['goto', 'click', 'dblclick', 'dblclick', 'check', 'uncheck', 'fill', 'press'].includes(metadata.method);
|
||||
}
|
||||
|
||||
@ -33,7 +33,6 @@ import { CallMetadata, InstrumentationListener, internalCallMetadata, SdkObject
|
||||
import { Point } from '../../common/types';
|
||||
import { CallLog, CallLogStatus, EventData, Mode, Source, UIState } from './recorder/recorderTypes';
|
||||
import { isUnderTest } from '../../utils/utils';
|
||||
import { InMemorySnapshotter } from '../snapshot/inMemorySnapshotter';
|
||||
import { metadataToCallLog } from './recorder/recorderUtils';
|
||||
import { Debugger } from './debugger';
|
||||
|
||||
@ -56,9 +55,6 @@ export class RecorderSupplement implements InstrumentationListener {
|
||||
private _currentCallsMetadata = new Map<CallMetadata, SdkObject>();
|
||||
private _recorderSources: Source[];
|
||||
private _userSources = new Map<string, Source>();
|
||||
private _snapshotter: InMemorySnapshotter;
|
||||
private _hoveredSnapshot: { callLogId: string, phase: 'before' | 'after' | 'action' } | undefined;
|
||||
private _snapshots = new Set<string>();
|
||||
private _allMetadatas = new Map<string, CallMetadata>();
|
||||
private _debugger: Debugger;
|
||||
|
||||
@ -129,14 +125,12 @@ export class RecorderSupplement implements InstrumentationListener {
|
||||
});
|
||||
}
|
||||
this._generator = generator;
|
||||
this._snapshotter = new InMemorySnapshotter(context);
|
||||
}
|
||||
|
||||
async install() {
|
||||
const recorderApp = await RecorderApp.open(this._context);
|
||||
this._recorderApp = recorderApp;
|
||||
recorderApp.once('close', () => {
|
||||
this._snapshotter.dispose().catch(() => {});
|
||||
this._recorderApp = null;
|
||||
});
|
||||
recorderApp.on('event', (data: EventData) => {
|
||||
@ -150,13 +144,6 @@ export class RecorderSupplement implements InstrumentationListener {
|
||||
this._refreshOverlay();
|
||||
return;
|
||||
}
|
||||
if (data.event === 'callLogHovered') {
|
||||
this._hoveredSnapshot = undefined;
|
||||
if (this._debugger.isPaused() && data.params.callLogId)
|
||||
this._hoveredSnapshot = data.params;
|
||||
this._refreshOverlay();
|
||||
return;
|
||||
}
|
||||
if (data.event === 'step') {
|
||||
this._debugger.resume(true);
|
||||
return;
|
||||
@ -202,26 +189,18 @@ export class RecorderSupplement implements InstrumentationListener {
|
||||
(source: BindingSource, action: actions.Action) => this._recordAction(source.frame, action), 'utility');
|
||||
|
||||
await this._context.exposeBinding('_playwrightRecorderState', false, source => {
|
||||
let snapshotUrl: string | undefined;
|
||||
let actionSelector = this._highlightedSelector;
|
||||
let actionPoint: Point | undefined;
|
||||
if (this._hoveredSnapshot) {
|
||||
const metadata = this._allMetadatas.get(this._hoveredSnapshot.callLogId)!;
|
||||
snapshotUrl = `${metadata.pageId}?name=${this._hoveredSnapshot.phase}@${this._hoveredSnapshot.callLogId}`;
|
||||
actionPoint = this._hoveredSnapshot.phase === 'action' ? metadata?.point : undefined;
|
||||
} else {
|
||||
for (const [metadata, sdkObject] of this._currentCallsMetadata) {
|
||||
if (source.page === sdkObject.attribution.page) {
|
||||
actionPoint = metadata.point || actionPoint;
|
||||
actionSelector = actionSelector || metadata.params.selector;
|
||||
}
|
||||
for (const [metadata, sdkObject] of this._currentCallsMetadata) {
|
||||
if (source.page === sdkObject.attribution.page) {
|
||||
actionPoint = metadata.point || actionPoint;
|
||||
actionSelector = actionSelector || metadata.params.selector;
|
||||
}
|
||||
}
|
||||
const uiState: UIState = {
|
||||
mode: this._mode,
|
||||
actionPoint,
|
||||
actionSelector,
|
||||
snapshotUrl,
|
||||
};
|
||||
return uiState;
|
||||
}, 'utility');
|
||||
@ -236,8 +215,7 @@ export class RecorderSupplement implements InstrumentationListener {
|
||||
this._debugger.resume(false);
|
||||
}, 'main');
|
||||
|
||||
const snapshotBaseUrl = await this._snapshotter.initialize() + '/snapshot/';
|
||||
await this._context.extendInjectedScript('utility', recorderSource.source, { isUnderTest: isUnderTest(), snapshotBaseUrl });
|
||||
await this._context.extendInjectedScript('utility', recorderSource.source, { isUnderTest: isUnderTest() });
|
||||
await this._context.extendInjectedScript('main', consoleApiSource.source);
|
||||
|
||||
if (this._debugger.isPaused())
|
||||
@ -391,18 +369,9 @@ export class RecorderSupplement implements InstrumentationListener {
|
||||
this._generator.signal(pageAlias, page.mainFrame(), { name: 'dialog', dialogAlias: String(++this._lastDialogOrdinal) });
|
||||
}
|
||||
|
||||
_captureSnapshot(sdkObject: SdkObject, metadata: CallMetadata, phase: 'before' | 'after' | 'action') {
|
||||
if (sdkObject.attribution.page) {
|
||||
const snapshotName = `${phase}@${metadata.id}`;
|
||||
this._snapshots.add(snapshotName);
|
||||
this._snapshotter.captureSnapshot(sdkObject.attribution.page, snapshotName);
|
||||
}
|
||||
}
|
||||
|
||||
async onBeforeCall(sdkObject: SdkObject, metadata: CallMetadata) {
|
||||
if (this._mode === 'recording')
|
||||
return;
|
||||
this._captureSnapshot(sdkObject, metadata, 'before');
|
||||
this._currentCallsMetadata.set(metadata, sdkObject);
|
||||
this._allMetadatas.set(metadata.id, metadata);
|
||||
this._updateUserSources();
|
||||
@ -416,7 +385,6 @@ export class RecorderSupplement implements InstrumentationListener {
|
||||
async onAfterCall(sdkObject: SdkObject, metadata: CallMetadata) {
|
||||
if (this._mode === 'recording')
|
||||
return;
|
||||
this._captureSnapshot(sdkObject, metadata, 'after');
|
||||
if (!metadata.error)
|
||||
this._currentCallsMetadata.delete(metadata);
|
||||
this._updateUserSources();
|
||||
@ -458,9 +426,6 @@ export class RecorderSupplement implements InstrumentationListener {
|
||||
}
|
||||
|
||||
async onBeforeInputAction(sdkObject: SdkObject, metadata: CallMetadata) {
|
||||
if (this._mode === 'recording')
|
||||
return;
|
||||
this._captureSnapshot(sdkObject, metadata, 'action');
|
||||
}
|
||||
|
||||
async onCallLog(logName: string, message: string, sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {
|
||||
@ -479,7 +444,7 @@ export class RecorderSupplement implements InstrumentationListener {
|
||||
status = 'in-progress';
|
||||
if (this._debugger.isPaused(metadata))
|
||||
status = 'paused';
|
||||
logs.push(metadataToCallLog(metadata, status, this._snapshots));
|
||||
logs.push(metadataToCallLog(metadata, status));
|
||||
}
|
||||
this._recorderApp?.updateCallLogs(logs);
|
||||
}
|
||||
|
||||
@ -48,6 +48,7 @@
|
||||
|
||||
.call-log-call .codicon {
|
||||
padding: 0 4px;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.call-log .codicon-check {
|
||||
@ -66,6 +67,12 @@
|
||||
color: red;
|
||||
}
|
||||
|
||||
.call-log-details {
|
||||
flex: 0 1 auto;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.call-log-url {
|
||||
color: var(--blue);
|
||||
}
|
||||
@ -75,6 +82,7 @@
|
||||
}
|
||||
|
||||
.call-log-time {
|
||||
flex: none;
|
||||
margin-left: 4px;
|
||||
color: var(--gray);
|
||||
}
|
||||
|
||||
@ -21,12 +21,10 @@ import { msToString } from '../uiUtils';
|
||||
|
||||
export interface CallLogProps {
|
||||
log: CallLog[],
|
||||
onHover: (callLog: CallLog | undefined, phase?: 'before' | 'after' | 'action') => void
|
||||
}
|
||||
|
||||
export const CallLogView: React.FC<CallLogProps> = ({
|
||||
log,
|
||||
onHover,
|
||||
}) => {
|
||||
const messagesEndRef = React.createRef<HTMLDivElement>();
|
||||
const [expandOverrides, setExpandOverrides] = React.useState<Map<string, boolean>>(new Map());
|
||||
@ -47,14 +45,10 @@ export const CallLogView: React.FC<CallLogProps> = ({
|
||||
setExpandOverrides(newOverrides);
|
||||
}}></span>
|
||||
{ callLog.title }
|
||||
{ callLog.params.url ? <span>(<span className='call-log-url'>{callLog.params.url}</span>)</span> : undefined }
|
||||
{ callLog.params.selector ? <span>(<span className='call-log-selector'>{callLog.params.selector}</span>)</span> : undefined }
|
||||
{ callLog.params.url ? <span className='call-log-details'>(<span className='call-log-url' title={callLog.params.url}>{callLog.params.url}</span>)</span> : undefined }
|
||||
{ callLog.params.selector ? <span className='call-log-details'>(<span className='call-log-selector' title={callLog.params.selector}>{callLog.params.selector}</span>)</span> : undefined }
|
||||
<span className={'codicon ' + iconClass(callLog)}></span>
|
||||
{ typeof callLog.duration === 'number' ? <span className='call-log-time'>— {msToString(callLog.duration)}</span> : undefined}
|
||||
{ <div style={{flex: 'auto'}}></div> }
|
||||
<span className={'codicon codicon-vm-outline preview' + (callLog.snapshots.before ? '' : ' invisible')} onMouseEnter={() => onHover(callLog, 'before')} onMouseLeave={() => onHover(undefined)}></span>
|
||||
<span className={'codicon codicon-vm-running preview' + (callLog.snapshots.action ? '' : ' invisible')} onMouseEnter={() => onHover(callLog, 'action')} onMouseLeave={() => onHover(undefined)}></span>
|
||||
<span className={'codicon codicon-vm-active preview' + (callLog.snapshots.after ? '' : ' invisible')} onMouseEnter={() => onHover(callLog, 'after')} onMouseLeave={() => onHover(undefined)}></span>
|
||||
</div>
|
||||
{ (isExpanded ? callLog.messages : []).map((message, i) => {
|
||||
return <div className='call-log-message' key={i}>
|
||||
|
||||
@ -121,9 +121,7 @@ export const Recorder: React.FC<RecorderProps> = ({
|
||||
window.dispatch({ event: 'selectorUpdated', params: { selector: event.target.value } });
|
||||
}} />
|
||||
</Toolbar>
|
||||
<CallLogView log={Array.from(log.values())} onHover={(callLog, phase) => {
|
||||
window.dispatch({ event: 'callLogHovered', params: { callLogId: callLog?.id, phase } });
|
||||
}}/>
|
||||
<CallLogView log={Array.from(log.values())}/>
|
||||
</div>
|
||||
</SplitView>
|
||||
</div>;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user