mirror of
				https://github.com/microsoft/playwright.git
				synced 2025-06-26 21:40:17 +00:00 
			
		
		
		
	chore(recorder): glue the overlay to the top (#28021)
This commit is contained in:
		
							parent
							
								
									a9c4406439
								
							
						
					
					
						commit
						eeda25c47f
					
				| @ -658,13 +658,15 @@ class Overlay { | ||||
|   private _recordToggle: HTMLElement; | ||||
|   private _pickLocatorToggle: HTMLElement; | ||||
|   private _assertToggle: HTMLElement; | ||||
|   private _position: { x: number, y: number } = { x: 0, y: 0 }; | ||||
|   private _dragState: { position: { x: number, y: number }, dragStart: { x: number, y: number } } | undefined; | ||||
|   private _offsetX = 0; | ||||
|   private _dragState: { offsetX: number, dragStart: { x: number, y: number } } | undefined; | ||||
|   private _measure: { width: number, height: number } = { width: 0, height: 0 }; | ||||
| 
 | ||||
|   constructor(private _recorder: Recorder) { | ||||
|     const document = this._recorder.injectedScript.document; | ||||
|     this._overlayElement = document.createElement('x-pw-overlay'); | ||||
|     this._overlayElement.style.top = '0'; | ||||
|     this._overlayElement.style.position = 'absolute'; | ||||
| 
 | ||||
|     const shadow = this._overlayElement.attachShadow({ mode: 'closed' }); | ||||
|     const styleElement = document.createElement('style'); | ||||
| @ -682,23 +684,7 @@ class Overlay { | ||||
|         background-color: hsla(0 0% 100% / .9); | ||||
|         font-family: 'Dank Mono', 'Operator Mono', Inconsolata, 'Fira Mono', 'SF Mono', Monaco, 'Droid Sans Mono', 'Source Code Pro', monospace; | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         margin: 10px; | ||||
|         padding: 3px 0; | ||||
|         border-radius: 17px; | ||||
|       } | ||||
| 
 | ||||
|       x-pw-drag-handle { | ||||
|         cursor: grab; | ||||
|         padding: 6px 9px; | ||||
|       } | ||||
|       x-pw-drag-handle > div { | ||||
|         height: 1px; | ||||
|         margin-top: 2px; | ||||
|         background: rgb(148 148 148 / 90%); | ||||
|       } | ||||
|       x-pw-drag-handle:active { | ||||
|         cursor: grabbing; | ||||
|         border-radius: 3px; | ||||
|       } | ||||
| 
 | ||||
|       x-pw-separator { | ||||
| @ -712,7 +698,7 @@ class Overlay { | ||||
|         height: 28px; | ||||
|         width: 28px; | ||||
|         margin: 2px 4px; | ||||
|         border-radius: 50%; | ||||
|         border-radius: 3px; | ||||
|       } | ||||
|       x-pw-tool-item:not(.disabled):hover { | ||||
|         background-color: hsl(0, 0%, 86%); | ||||
| @ -738,7 +724,28 @@ class Overlay { | ||||
|       x-pw-tool-item.record.active > div { | ||||
|         background-color: #a1260d; | ||||
|       } | ||||
| 
 | ||||
|       x-pw-tool-gripper { | ||||
|         height: 28px; | ||||
|         width: 24px; | ||||
|         margin: 2px 0; | ||||
|         cursor: grab; | ||||
|       } | ||||
|       x-pw-tool-gripper:active { | ||||
|         cursor: grabbing; | ||||
|       } | ||||
|       x-pw-tool-gripper > div { | ||||
|         width: 100%; | ||||
|         height: 100%; | ||||
|         -webkit-mask-repeat: no-repeat; | ||||
|         -webkit-mask-position: center; | ||||
|         -webkit-mask-size: 20px; | ||||
|         mask-repeat: no-repeat; | ||||
|         mask-position: center; | ||||
|         mask-size: 16px; | ||||
|         -webkit-mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path d='M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z' /></svg>"); | ||||
|         mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path d='M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z' /></svg>"); | ||||
|         background-color: #555555; | ||||
|       } | ||||
|       x-pw-tool-item.record > div { | ||||
|         /* codicon: circle-large-filled */ | ||||
|         -webkit-mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path d='M8 1a6.8 6.8 0 0 1 1.86.253 6.899 6.899 0 0 1 3.083 1.805 6.903 6.903 0 0 1 1.804 3.083C14.916 6.738 15 7.357 15 8s-.084 1.262-.253 1.86a6.9 6.9 0 0 1-.704 1.674 7.157 7.157 0 0 1-2.516 2.509 6.966 6.966 0 0 1-1.668.71A6.984 6.984 0 0 1 8 15a6.984 6.984 0 0 1-1.86-.246 7.098 7.098 0 0 1-1.674-.711 7.3 7.3 0 0 1-1.415-1.094 7.295 7.295 0 0 1-1.094-1.415 7.098 7.098 0 0 1-.71-1.675A6.985 6.985 0 0 1 1 8c0-.643.082-1.262.246-1.86a6.968 6.968 0 0 1 .711-1.667 7.156 7.156 0 0 1 2.509-2.516 6.895 6.895 0 0 1 1.675-.704A6.808 6.808 0 0 1 8 1z'/></svg>"); | ||||
| @ -754,17 +761,19 @@ class Overlay { | ||||
|         -webkit-mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path fill-rule='evenodd' clip-rule='evenodd' d='M15.62 3.596L7.815 12.81l-.728-.033L4 8.382l.754-.53 2.744 3.907L14.917 3l.703.596z'/><path fill-rule='evenodd' clip-rule='evenodd' d='M7.234 8.774l4.386-5.178L10.917 3l-4.23 4.994.547.78zm-1.55.403l.548.78-.547-.78zm-1.617 1.91l.547.78-.799.943-.728-.033L0 8.382l.754-.53 2.744 3.907.57-.672z'/></svg>"); | ||||
|         mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path fill-rule='evenodd' clip-rule='evenodd' d='M15.62 3.596L7.815 12.81l-.728-.033L4 8.382l.754-.53 2.744 3.907L14.917 3l.703.596z'/><path fill-rule='evenodd' clip-rule='evenodd' d='M7.234 8.774l4.386-5.178L10.917 3l-4.23 4.994.547.78zm-1.55.403l.548.78-.547-.78zm-1.617 1.91l.547.78-.799.943-.728-.033L0 8.382l.754-.53 2.744 3.907.57-.672z'/></svg>"); | ||||
|       } | ||||
|       x-pw-tool-item.close > div { | ||||
|         /* codicon: close */ | ||||
|         -webkit-mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path fill-rule='evenodd' clip-rule='evenodd' d='M8 8.707l3.646 3.647.708-.707L8.707 8l3.647-3.646-.707-.708L8 7.293 4.354 3.646l-.707.708L7.293 8l-3.646 3.646.707.708L8 8.707z'/></svg>"); | ||||
|         mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path fill-rule='evenodd' clip-rule='evenodd' d='M8 8.707l3.646 3.647.708-.707L8.707 8l3.647-3.646-.707-.708L8 7.293 4.354 3.646l-.707.708L7.293 8l-3.646 3.646.707.708L8 8.707z'/></svg>"); | ||||
|       } | ||||
|     `;
 | ||||
|     shadow.appendChild(styleElement); | ||||
| 
 | ||||
|     const toolsListElement = document.createElement('x-pw-tools-list'); | ||||
|     shadow.appendChild(toolsListElement); | ||||
| 
 | ||||
|     const dragHandle = document.createElement('x-pw-tool-gripper'); | ||||
|     dragHandle.addEventListener('mousedown', event => { | ||||
|       this._dragState = { offsetX: this._offsetX, dragStart: { x: event.clientX, y: 0 } }; | ||||
|     }); | ||||
|     dragHandle.appendChild(document.createElement('div')); | ||||
|     toolsListElement.appendChild(dragHandle); | ||||
| 
 | ||||
|     this._recordToggle = this._recorder.injectedScript.document.createElement('x-pw-tool-item'); | ||||
|     this._recordToggle.title = 'Record'; | ||||
|     this._recordToggle.classList.add('record'); | ||||
| @ -774,15 +783,6 @@ class Overlay { | ||||
|     }); | ||||
|     toolsListElement.appendChild(this._recordToggle); | ||||
| 
 | ||||
|     const dragHandle = document.createElement('x-pw-drag-handle'); | ||||
|     dragHandle.addEventListener('mousedown', event => { | ||||
|       this._dragState = { position: this._position, dragStart: { x: event.clientX, y: event.clientY } }; | ||||
|     }); | ||||
|     dragHandle.append(document.createElement('div')); | ||||
|     dragHandle.append(document.createElement('div')); | ||||
|     dragHandle.append(document.createElement('div')); | ||||
|     toolsListElement.appendChild(dragHandle); | ||||
| 
 | ||||
|     this._pickLocatorToggle = this._recorder.injectedScript.document.createElement('x-pw-tool-item'); | ||||
|     this._pickLocatorToggle.title = 'Pick locator'; | ||||
|     this._pickLocatorToggle.classList.add('pick-locator'); | ||||
| @ -809,16 +809,6 @@ class Overlay { | ||||
|     }); | ||||
|     toolsListElement.appendChild(this._assertToggle); | ||||
| 
 | ||||
|     const closeButton = this._recorder.injectedScript.document.createElement('x-pw-tool-item'); | ||||
|     closeButton.title = 'Hide this overlay'; | ||||
|     closeButton.classList.add('close'); | ||||
|     closeButton.appendChild(this._recorder.injectedScript.document.createElement('div')); | ||||
|     closeButton.addEventListener('click', () => { | ||||
|       this._overlayElement.style.display = 'none'; | ||||
|       this._recorder.delegate.setOverlayState?.({ position: this._position, visible: false }); | ||||
|     }); | ||||
|     toolsListElement.appendChild(closeButton); | ||||
| 
 | ||||
|     this._updateVisualPosition(); | ||||
|   } | ||||
| 
 | ||||
| @ -836,16 +826,14 @@ class Overlay { | ||||
|     this._pickLocatorToggle.classList.toggle('active', state.mode === 'inspecting' || state.mode === 'recording-inspecting'); | ||||
|     this._assertToggle.classList.toggle('active', state.mode === 'assertingText'); | ||||
|     this._assertToggle.classList.toggle('disabled', state.mode === 'none' || state.mode === 'inspecting'); | ||||
|     if (this._position.x !== state.overlay.position.x || this._position.y !== state.overlay.position.y) { | ||||
|       this._position = state.overlay.position; | ||||
|     if (this._offsetX !== state.overlay.offsetX) { | ||||
|       this._offsetX = state.overlay.offsetX; | ||||
|       this._updateVisualPosition(); | ||||
|     } | ||||
|     this._overlayElement.style.display = state.overlay.visible ? 'block' : 'none'; | ||||
|   } | ||||
| 
 | ||||
|   private _updateVisualPosition() { | ||||
|     this._overlayElement.style.left = this._position.x + 'px'; | ||||
|     this._overlayElement.style.top = this._position.y + 'px'; | ||||
|     this._overlayElement.style.left = (this._recorder.injectedScript.window.innerWidth / 2 + this._offsetX) + 'px'; | ||||
|   } | ||||
| 
 | ||||
|   onMouseMove(event: MouseEvent) { | ||||
| @ -854,14 +842,11 @@ class Overlay { | ||||
|       return false; | ||||
|     } | ||||
|     if (this._dragState) { | ||||
|       this._position = { | ||||
|         x: this._dragState.position.x + event.clientX - this._dragState.dragStart.x, | ||||
|         y: this._dragState.position.y + event.clientY - this._dragState.dragStart.y, | ||||
|       }; | ||||
|       this._position.x = Math.max(0, Math.min(this._recorder.injectedScript.window.innerWidth - this._measure.width, this._position.x)); | ||||
|       this._position.y = Math.max(0, Math.min(this._recorder.injectedScript.window.innerHeight - this._measure.height, this._position.y)); | ||||
|       this._offsetX = this._dragState.offsetX + event.clientX - this._dragState.dragStart.x; | ||||
|       this._offsetX = Math.min(this._recorder.injectedScript.window.innerWidth / 2 - 10 - this._measure.width, this._offsetX); | ||||
|       this._offsetX = Math.max(10 - this._recorder.injectedScript.window.innerWidth / 2, this._offsetX); | ||||
|       this._updateVisualPosition(); | ||||
|       this._recorder.delegate.setOverlayState?.({ position: this._position, visible: true }); | ||||
|       this._recorder.delegate.setOverlayState?.({ offsetX: this._offsetX }); | ||||
|       consumeEvent(event); | ||||
|       return true; | ||||
|     } | ||||
| @ -869,6 +854,14 @@ class Overlay { | ||||
|   } | ||||
| 
 | ||||
|   onMouseUp(event: MouseEvent) { | ||||
|     if (this._dragState) { | ||||
|       consumeEvent(event); | ||||
|       return true; | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   onClick(event: MouseEvent) { | ||||
|     if (this._dragState) { | ||||
|       this._dragState = undefined; | ||||
|       consumeEvent(event); | ||||
| @ -887,7 +880,7 @@ export class Recorder { | ||||
|   private _highlight: Highlight; | ||||
|   private _overlay: Overlay | undefined; | ||||
|   private _styleElement: HTMLStyleElement; | ||||
|   state: UIState = { mode: 'none', testIdAttributeName: 'data-testid', language: 'javascript', overlay: { position: { x: 0, y: 0 }, visible: true } }; | ||||
|   state: UIState = { mode: 'none', testIdAttributeName: 'data-testid', language: 'javascript', overlay: { offsetX: 0 } }; | ||||
|   readonly document: Document; | ||||
|   delegate: RecorderDelegate = {}; | ||||
| 
 | ||||
| @ -991,6 +984,8 @@ export class Recorder { | ||||
|   private _onClick(event: MouseEvent) { | ||||
|     if (!event.isTrusted) | ||||
|       return; | ||||
|     if (this._overlay?.onClick(event)) | ||||
|       return; | ||||
|     if (this._ignoreOverlayEvent(event)) | ||||
|       return; | ||||
|     this._currentTool.onClick?.(event); | ||||
|  | ||||
| @ -55,7 +55,7 @@ export class Recorder implements InstrumentationListener { | ||||
|   private _context: BrowserContext; | ||||
|   private _mode: Mode; | ||||
|   private _highlightedSelector = ''; | ||||
|   private _overlayState: OverlayState = { position: { x: 0, y: 0 }, visible: true }; | ||||
|   private _overlayState: OverlayState = { offsetX: 0 }; | ||||
|   private _recorderApp: IRecorderApp | null = null; | ||||
|   private _currentCallsMetadata = new Map<CallMetadata, SdkObject>(); | ||||
|   private _recorderSources: Source[] = []; | ||||
| @ -101,7 +101,7 @@ export class Recorder implements InstrumentationListener { | ||||
| 
 | ||||
|     if (isUnderTest()) { | ||||
|       // Most of our tests put elements at the top left, so get out of the way.
 | ||||
|       this._overlayState.position = { x: 350, y: 350 }; | ||||
|       this._overlayState.offsetX = 200; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| @ -123,12 +123,6 @@ export class Recorder implements InstrumentationListener { | ||||
|         this.setMode(data.params.mode); | ||||
|         return; | ||||
|       } | ||||
|       if (data.event === 'setOverlayVisible') { | ||||
|         this._overlayState.visible = data.params.visible; | ||||
|         this._recorderApp?.setOverlayVisible(this._overlayState.visible); | ||||
|         this._refreshOverlay(); | ||||
|         return; | ||||
|       } | ||||
|       if (data.event === 'selectorUpdated') { | ||||
|         this.setHighlightedSelector(this._currentLanguage, data.params.selector); | ||||
|         return; | ||||
| @ -219,7 +213,6 @@ export class Recorder implements InstrumentationListener { | ||||
|       if (frame.parentFrame()) | ||||
|         return; | ||||
|       this._overlayState = state; | ||||
|       this._recorderApp?.setOverlayVisible(state.visible); | ||||
|     }); | ||||
| 
 | ||||
|     await this._context.exposeBinding('__pw_resume', false, () => { | ||||
|  | ||||
| @ -46,7 +46,6 @@ export interface IRecorderApp extends EventEmitter { | ||||
|   close(): Promise<void>; | ||||
|   setPaused(paused: boolean): Promise<void>; | ||||
|   setMode(mode: Mode): Promise<void>; | ||||
|   setOverlayVisible(visible: boolean): Promise<void>; | ||||
|   setFileIfNeeded(file: string): Promise<void>; | ||||
|   setSelector(selector: string, userGesture?: boolean): Promise<void>; | ||||
|   updateCallLogs(callLogs: CallLog[]): Promise<void>; | ||||
| @ -57,7 +56,6 @@ export class EmptyRecorderApp extends EventEmitter implements IRecorderApp { | ||||
|   async close(): Promise<void> {} | ||||
|   async setPaused(paused: boolean): Promise<void> {} | ||||
|   async setMode(mode: Mode): Promise<void> {} | ||||
|   async setOverlayVisible(visible: boolean): Promise<void> {} | ||||
|   async setFileIfNeeded(file: string): Promise<void> {} | ||||
|   async setSelector(selector: string, userGesture?: boolean): Promise<void> {} | ||||
|   async updateCallLogs(callLogs: CallLog[]): Promise<void> {} | ||||
| @ -147,12 +145,6 @@ export class RecorderApp extends EventEmitter implements IRecorderApp { | ||||
|     }).toString(), { isFunction: true }, mode).catch(() => {}); | ||||
|   } | ||||
| 
 | ||||
|   async setOverlayVisible(visible: boolean): Promise<void> { | ||||
|     await this._page.mainFrame().evaluateExpression(((visible: boolean) => { | ||||
|       window.playwrightSetOverlayVisible(visible); | ||||
|     }).toString(), { isFunction: true }, visible).catch(() => {}); | ||||
|   } | ||||
| 
 | ||||
|   async setFileIfNeeded(file: string): Promise<void> { | ||||
|     await this._page.mainFrame().evaluateExpression(((file: string) => { | ||||
|       window.playwrightSetFileIfNeeded(file); | ||||
|  | ||||
| @ -25,12 +25,10 @@ export const Main: React.FC = ({ | ||||
|   const [paused, setPaused] = React.useState(false); | ||||
|   const [log, setLog] = React.useState(new Map<string, CallLog>()); | ||||
|   const [mode, setMode] = React.useState<Mode>('none'); | ||||
|   const [overlayVisible, setOverlayVisible] = React.useState<boolean>(true); | ||||
| 
 | ||||
|   window.playwrightSetMode = setMode; | ||||
|   window.playwrightSetSources = setSources; | ||||
|   window.playwrightSetPaused = setPaused; | ||||
|   window.playwrightSetOverlayVisible = setOverlayVisible; | ||||
|   window.playwrightUpdateLogs = callLogs => { | ||||
|     const newLog = new Map<string, CallLog>(log); | ||||
|     for (const callLog of callLogs) { | ||||
| @ -41,5 +39,5 @@ export const Main: React.FC = ({ | ||||
|   }; | ||||
| 
 | ||||
|   window.playwrightSourcesEchoForTest = sources; | ||||
|   return <Recorder sources={sources} paused={paused} log={log} mode={mode} overlayVisible={overlayVisible}/>; | ||||
|   return <Recorder sources={sources} paused={paused} log={log} mode={mode} />; | ||||
| }; | ||||
|  | ||||
| @ -40,7 +40,6 @@ export interface RecorderProps { | ||||
|   paused: boolean, | ||||
|   log: Map<string, CallLog>, | ||||
|   mode: Mode, | ||||
|   overlayVisible: boolean, | ||||
| } | ||||
| 
 | ||||
| export const Recorder: React.FC<RecorderProps> = ({ | ||||
| @ -48,7 +47,6 @@ export const Recorder: React.FC<RecorderProps> = ({ | ||||
|   paused, | ||||
|   log, | ||||
|   mode, | ||||
|   overlayVisible, | ||||
| }) => { | ||||
|   const [fileId, setFileId] = React.useState<string | undefined>(); | ||||
|   const [selectedTab, setSelectedTab] = React.useState<string>('log'); | ||||
| @ -156,9 +154,6 @@ export const Recorder: React.FC<RecorderProps> = ({ | ||||
|         window.dispatch({ event: 'clear' }); | ||||
|       }}></ToolbarButton> | ||||
|       <ToolbarButton icon='color-mode' title='Toggle color mode' toggled={false} onClick={() => toggleTheme()}></ToolbarButton> | ||||
|       <ToolbarButton icon='editor-layout' title='Toggle overlay' toggled={overlayVisible} onClick={() => { | ||||
|         window.dispatch({ event: 'setOverlayVisible', params: { visible: !overlayVisible } }); | ||||
|       }}></ToolbarButton> | ||||
|     </Toolbar> | ||||
|     <SplitView sidebarSize={200} sidebarHidden={mode === 'recording'}> | ||||
|       <CodeMirrorWrapper text={source.text} language={source.language} highlight={source.highlight} revealLine={source.revealLine} readOnly={true} lineNumbers={true}/> | ||||
|  | ||||
| @ -21,13 +21,12 @@ export type Point = { x: number, y: number }; | ||||
| export type Mode = 'inspecting' | 'recording' | 'none' | 'assertingText' | 'recording-inspecting'; | ||||
| 
 | ||||
| export type EventData = { | ||||
|   event: 'clear' | 'resume' | 'step' | 'pause' | 'setMode' | 'selectorUpdated' | 'fileChanged' | 'setOverlayVisible'; | ||||
|   event: 'clear' | 'resume' | 'step' | 'pause' | 'setMode' | 'selectorUpdated' | 'fileChanged'; | ||||
|   params: any; | ||||
| }; | ||||
| 
 | ||||
| export type OverlayState = { | ||||
|   position: Point; | ||||
|   visible: boolean; | ||||
|   offsetX: number; | ||||
| }; | ||||
| 
 | ||||
| export type UIState = { | ||||
|  | ||||
| @ -242,7 +242,7 @@ export const InspectModeController: React.FunctionComponent<{ | ||||
|         actionSelector: actionSelector.startsWith(frameSelector) ? actionSelector.substring(frameSelector.length).trim() : undefined, | ||||
|         language: sdkLanguage, | ||||
|         testIdAttributeName, | ||||
|         overlay: { position: { x: 0, y: 0 }, visible: false }, | ||||
|         overlay: { offsetX: 0 }, | ||||
|       }, { | ||||
|         async setSelector(selector: string) { | ||||
|           setHighlightedLocator(asLocator(sdkLanguage, frameSelector + selector, false /* isFrameLocator */, true /* playSafe */)); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Pavel Feldman
						Pavel Feldman