chore: allow highlighting aria template from extension (#33594)
This commit is contained in:
		
							parent
							
								
									a8af7cc435
								
							
						
					
					
						commit
						4817483ff2
					
				|  | @ -422,7 +422,8 @@ scheme.DebugControllerSetRecorderModeParams = tObject({ | ||||||
| }); | }); | ||||||
| scheme.DebugControllerSetRecorderModeResult = tOptional(tObject({})); | scheme.DebugControllerSetRecorderModeResult = tOptional(tObject({})); | ||||||
| scheme.DebugControllerHighlightParams = tObject({ | scheme.DebugControllerHighlightParams = tObject({ | ||||||
|   selector: tString, |   selector: tOptional(tString), | ||||||
|  |   ariaTemplate: tOptional(tString), | ||||||
| }); | }); | ||||||
| scheme.DebugControllerHighlightResult = tOptional(tObject({})); | scheme.DebugControllerHighlightResult = tOptional(tObject({})); | ||||||
| scheme.DebugControllerHideHighlightParams = tOptional(tObject({})); | scheme.DebugControllerHideHighlightParams = tOptional(tObject({})); | ||||||
|  |  | ||||||
|  | @ -15,12 +15,16 @@ | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| import { parseYamlTemplate } from '../utils/isomorphic/ariaSnapshot'; | import { parseYamlTemplate } from '../utils/isomorphic/ariaSnapshot'; | ||||||
| import type { AriaTemplateNode } from '@isomorphic/ariaSnapshot'; | import type { AriaTemplateNode, ParsedYaml } from '@isomorphic/ariaSnapshot'; | ||||||
| import { yaml } from '../utilsBundle'; | import { yaml } from '../utilsBundle'; | ||||||
| 
 | 
 | ||||||
| export function parseAriaSnapshot(text: string): AriaTemplateNode { | export function parseAriaSnapshot(text: string): AriaTemplateNode { | ||||||
|   const fragment = yaml.parse(text); |   return parseYamlTemplate(parseYamlForAriaSnapshot(text)); | ||||||
|   if (!Array.isArray(fragment)) | } | ||||||
|     throw new Error('Expected object key starting with "- ":\n\n' + text + '\n'); | 
 | ||||||
|   return parseYamlTemplate(fragment); | export function parseYamlForAriaSnapshot(text: string): ParsedYaml { | ||||||
|  |   const parsed = yaml.parse(text); | ||||||
|  |   if (!Array.isArray(parsed)) | ||||||
|  |     throw new Error('Expected object key starting with "- ":\n\n' + text + '\n'); | ||||||
|  |   return parsed; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -24,6 +24,7 @@ import type { Playwright } from './playwright'; | ||||||
| import { Recorder } from './recorder'; | import { Recorder } from './recorder'; | ||||||
| import { EmptyRecorderApp } from './recorder/recorderApp'; | import { EmptyRecorderApp } from './recorder/recorderApp'; | ||||||
| import { asLocator, type Language } from '../utils'; | import { asLocator, type Language } from '../utils'; | ||||||
|  | import { parseYamlForAriaSnapshot } from './ariaSnapshot'; | ||||||
| 
 | 
 | ||||||
| const internalMetadata = serverSideCallMetadata(); | const internalMetadata = serverSideCallMetadata(); | ||||||
| 
 | 
 | ||||||
|  | @ -142,9 +143,13 @@ export class DebugController extends SdkObject { | ||||||
|     this._autoCloseTimer = setTimeout(heartBeat, 30000); |     this._autoCloseTimer = setTimeout(heartBeat, 30000); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async highlight(selector: string) { |   async highlight(params: { selector?: string, ariaTemplate?: string }) { | ||||||
|     for (const recorder of await this._allRecorders()) |     for (const recorder of await this._allRecorders()) { | ||||||
|       recorder.setHighlightedSelector(this._sdkLanguage, selector); |       if (params.ariaTemplate) | ||||||
|  |         recorder.setHighlightedAriaTemplate(parseYamlForAriaSnapshot(params.ariaTemplate)); | ||||||
|  |       else if (params.selector) | ||||||
|  |         recorder.setHighlightedSelector(this._sdkLanguage, params.selector); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async hideHighlight() { |   async hideHighlight() { | ||||||
|  |  | ||||||
|  | @ -68,7 +68,7 @@ export class DebugControllerDispatcher extends Dispatcher<DebugController, chann | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async highlight(params: channels.DebugControllerHighlightParams) { |   async highlight(params: channels.DebugControllerHighlightParams) { | ||||||
|     await this._object.highlight(params.selector); |     await this._object.highlight(params); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async hideHighlight() { |   async hideHighlight() { | ||||||
|  |  | ||||||
|  | @ -40,7 +40,7 @@ export class Recorder implements InstrumentationListener, IRecorder { | ||||||
|   readonly handleSIGINT: boolean | undefined; |   readonly handleSIGINT: boolean | undefined; | ||||||
|   private _context: BrowserContext; |   private _context: BrowserContext; | ||||||
|   private _mode: Mode; |   private _mode: Mode; | ||||||
|   private _highlightedElement: { selector?: string, ariaSnapshot?: ParsedYaml } = {}; |   private _highlightedElement: { selector?: string, ariaTemplate?: ParsedYaml } = {}; | ||||||
|   private _overlayState: OverlayState = { offsetX: 0 }; |   private _overlayState: OverlayState = { offsetX: 0 }; | ||||||
|   private _recorderApp: IRecorderApp | null = null; |   private _recorderApp: IRecorderApp | null = null; | ||||||
|   private _currentCallsMetadata = new Map<CallMetadata, SdkObject>(); |   private _currentCallsMetadata = new Map<CallMetadata, SdkObject>(); | ||||||
|  | @ -107,8 +107,8 @@ export class Recorder implements InstrumentationListener, IRecorder { | ||||||
|       if (data.event === 'highlightRequested') { |       if (data.event === 'highlightRequested') { | ||||||
|         if (data.params.selector) |         if (data.params.selector) | ||||||
|           this.setHighlightedSelector(this._currentLanguage, data.params.selector); |           this.setHighlightedSelector(this._currentLanguage, data.params.selector); | ||||||
|         if (data.params.ariaSnapshot) |         if (data.params.ariaTemplate) | ||||||
|           this.setHighlightedAriaSnapshot(data.params.ariaSnapshot); |           this.setHighlightedAriaTemplate(data.params.ariaTemplate); | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|       if (data.event === 'step') { |       if (data.event === 'step') { | ||||||
|  | @ -169,7 +169,7 @@ export class Recorder implements InstrumentationListener, IRecorder { | ||||||
|         mode: this._mode, |         mode: this._mode, | ||||||
|         actionPoint, |         actionPoint, | ||||||
|         actionSelector, |         actionSelector, | ||||||
|         ariaTemplate: this._highlightedElement.ariaSnapshot, |         ariaTemplate: this._highlightedElement.ariaTemplate, | ||||||
|         language: this._currentLanguage, |         language: this._currentLanguage, | ||||||
|         testIdAttributeName: this._contextRecorder.testIdAttributeName(), |         testIdAttributeName: this._contextRecorder.testIdAttributeName(), | ||||||
|         overlay: this._overlayState, |         overlay: this._overlayState, | ||||||
|  | @ -245,8 +245,8 @@ export class Recorder implements InstrumentationListener, IRecorder { | ||||||
|     this._refreshOverlay(); |     this._refreshOverlay(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   setHighlightedAriaSnapshot(ariaSnapshot: ParsedYaml) { |   setHighlightedAriaTemplate(ariaTemplate: ParsedYaml) { | ||||||
|     this._highlightedElement = { ariaSnapshot }; |     this._highlightedElement = { ariaTemplate }; | ||||||
|     this._refreshOverlay(); |     this._refreshOverlay(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -741,10 +741,12 @@ export type DebugControllerSetRecorderModeOptions = { | ||||||
| }; | }; | ||||||
| export type DebugControllerSetRecorderModeResult = void; | export type DebugControllerSetRecorderModeResult = void; | ||||||
| export type DebugControllerHighlightParams = { | export type DebugControllerHighlightParams = { | ||||||
|   selector: string, |   selector?: string, | ||||||
|  |   ariaTemplate?: string, | ||||||
| }; | }; | ||||||
| export type DebugControllerHighlightOptions = { | export type DebugControllerHighlightOptions = { | ||||||
| 
 |   selector?: string, | ||||||
|  |   ariaTemplate?: string, | ||||||
| }; | }; | ||||||
| export type DebugControllerHighlightResult = void; | export type DebugControllerHighlightResult = void; | ||||||
| export type DebugControllerHideHighlightParams = {}; | export type DebugControllerHideHighlightParams = {}; | ||||||
|  |  | ||||||
|  | @ -791,7 +791,8 @@ DebugController: | ||||||
| 
 | 
 | ||||||
|     highlight: |     highlight: | ||||||
|       parameters: |       parameters: | ||||||
|         selector: string |         selector: string? | ||||||
|  |         ariaTemplate: string? | ||||||
| 
 | 
 | ||||||
|     hideHighlight: |     hideHighlight: | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -120,7 +120,7 @@ export const Recorder: React.FC<RecorderProps> = ({ | ||||||
|     setAriaSnapshotErrors(errors); |     setAriaSnapshotErrors(errors); | ||||||
|     setAriaSnapshot(ariaSnapshot); |     setAriaSnapshot(ariaSnapshot); | ||||||
|     if (!errors.length) |     if (!errors.length) | ||||||
|       window.dispatch({ event: 'highlightRequested', params: { ariaSnapshot: fragment } }); |       window.dispatch({ event: 'highlightRequested', params: { ariaTemplate: fragment } }); | ||||||
|   }, [mode]); |   }, [mode]); | ||||||
|   const isRecording = mode === 'recording' || mode === 'recording-inspecting'; |   const isRecording = mode === 'recording' || mode === 'recording-inspecting'; | ||||||
|   const locatorPlaceholder = isRecording ? '// Unavailable while recording' : (locator ? undefined : '// Pick element or type locator'); |   const locatorPlaceholder = isRecording ? '// Unavailable while recording' : (locator ? undefined : '// Pick element or type locator'); | ||||||
|  |  | ||||||
|  | @ -20,6 +20,7 @@ import { createGuid } from '../../packages/playwright-core/lib/utils/crypto'; | ||||||
| import { Backend } from '../config/debugControllerBackend'; | import { Backend } from '../config/debugControllerBackend'; | ||||||
| import type { Browser, BrowserContext } from '@playwright/test'; | import type { Browser, BrowserContext } from '@playwright/test'; | ||||||
| import type * as channels from '@protocol/channels'; | import type * as channels from '@protocol/channels'; | ||||||
|  | import { roundBox } from '../page/pageTest'; | ||||||
| 
 | 
 | ||||||
| type BrowserWithReuse = Browser & { _newContextForReuse: () => Promise<BrowserContext> }; | type BrowserWithReuse = Browser & { _newContextForReuse: () => Promise<BrowserContext> }; | ||||||
| type Fixtures = { | type Fixtures = { | ||||||
|  | @ -279,3 +280,20 @@ test('should highlight inside iframe', async ({ backend, connectedBrowser }, tes | ||||||
|   await expect(highlight).toHaveCount(1); |   await expect(highlight).toHaveCount(1); | ||||||
|   await expect(page.locator('x-pw-highlight')).toHaveCount(1); |   await expect(page.locator('x-pw-highlight')).toHaveCount(1); | ||||||
| }); | }); | ||||||
|  | 
 | ||||||
|  | test('should highlight aria template', async ({ backend, connectedBrowser }, testInfo) => { | ||||||
|  |   const context = await connectedBrowser._newContextForReuse(); | ||||||
|  |   const page = await context.newPage(); | ||||||
|  |   await backend.navigate({ url: `data:text/html,<button>Submit</button>` }); | ||||||
|  | 
 | ||||||
|  |   const button = page.getByRole('button'); | ||||||
|  |   const highlight = page.locator('x-pw-highlight'); | ||||||
|  | 
 | ||||||
|  |   await backend.highlight({ ariaTemplate: `- button "Submit2"` }); | ||||||
|  |   await expect(highlight).toHaveCount(0); | ||||||
|  | 
 | ||||||
|  |   await backend.highlight({ ariaTemplate: `- button "Submit"` }); | ||||||
|  |   const box1 = roundBox(await button.boundingBox()); | ||||||
|  |   const box2 = roundBox(await highlight.boundingBox()); | ||||||
|  |   expect(box1).toEqual(box2); | ||||||
|  | }); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue