From 8e68967b914fe2b0591db48921b5f6eceeb8c5f6 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Wed, 13 Nov 2024 16:06:08 -0800 Subject: [PATCH] chore: allow highlighting aria template from extension --- .../playwright-core/src/protocol/validator.ts | 3 ++- .../playwright-core/src/server/ariaSnapshot.ts | 12 ++++++++---- .../src/server/debugController.ts | 11 ++++++++--- .../dispatchers/debugControllerDispatcher.ts | 2 +- .../playwright-core/src/server/recorder.ts | 12 ++++++------ packages/protocol/src/channels.ts | 6 ++++-- packages/protocol/src/protocol.yml | 3 ++- packages/recorder/src/recorder.tsx | 2 +- tests/library/debug-controller.spec.ts | 18 ++++++++++++++++++ 9 files changed, 50 insertions(+), 19 deletions(-) diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 9cfe7149a43d5..7fe92a7947c71 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -422,7 +422,8 @@ scheme.DebugControllerSetRecorderModeParams = tObject({ }); scheme.DebugControllerSetRecorderModeResult = tOptional(tObject({})); scheme.DebugControllerHighlightParams = tObject({ - selector: tString, + selector: tOptional(tString), + ariaTemplate: tOptional(tString), }); scheme.DebugControllerHighlightResult = tOptional(tObject({})); scheme.DebugControllerHideHighlightParams = tOptional(tObject({})); diff --git a/packages/playwright-core/src/server/ariaSnapshot.ts b/packages/playwright-core/src/server/ariaSnapshot.ts index d2cf79d65301d..516688fef3def 100644 --- a/packages/playwright-core/src/server/ariaSnapshot.ts +++ b/packages/playwright-core/src/server/ariaSnapshot.ts @@ -15,12 +15,16 @@ */ import { parseYamlTemplate } from '../utils/isomorphic/ariaSnapshot'; -import type { AriaTemplateNode } from '@isomorphic/ariaSnapshot'; +import type { AriaTemplateNode, ParsedYaml } from '@isomorphic/ariaSnapshot'; import { yaml } from '../utilsBundle'; export function parseAriaSnapshot(text: string): AriaTemplateNode { - const fragment = yaml.parse(text); - if (!Array.isArray(fragment)) + return parseYamlTemplate(parseYamlForAriaSnapshot(text)); +} + +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 parseYamlTemplate(fragment); + return parsed; } diff --git a/packages/playwright-core/src/server/debugController.ts b/packages/playwright-core/src/server/debugController.ts index a9a79a49e2cb8..251dce7e037bd 100644 --- a/packages/playwright-core/src/server/debugController.ts +++ b/packages/playwright-core/src/server/debugController.ts @@ -24,6 +24,7 @@ import type { Playwright } from './playwright'; import { Recorder } from './recorder'; import { EmptyRecorderApp } from './recorder/recorderApp'; import { asLocator, type Language } from '../utils'; +import { parseYamlForAriaSnapshot } from './ariaSnapshot'; const internalMetadata = serverSideCallMetadata(); @@ -142,9 +143,13 @@ export class DebugController extends SdkObject { this._autoCloseTimer = setTimeout(heartBeat, 30000); } - async highlight(selector: string) { - for (const recorder of await this._allRecorders()) - recorder.setHighlightedSelector(this._sdkLanguage, selector); + async highlight(params: { selector?: string, ariaTemplate?: string }) { + for (const recorder of await this._allRecorders()) { + if (params.ariaTemplate) + recorder.setHighlightedAriaTemplate(parseYamlForAriaSnapshot(params.ariaTemplate)); + else if (params.selector) + recorder.setHighlightedSelector(this._sdkLanguage, params.selector); + } } async hideHighlight() { diff --git a/packages/playwright-core/src/server/dispatchers/debugControllerDispatcher.ts b/packages/playwright-core/src/server/dispatchers/debugControllerDispatcher.ts index 34c4d3b4ca79c..77d7b503abfe4 100644 --- a/packages/playwright-core/src/server/dispatchers/debugControllerDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/debugControllerDispatcher.ts @@ -68,7 +68,7 @@ export class DebugControllerDispatcher extends Dispatcher(); @@ -107,8 +107,8 @@ export class Recorder implements InstrumentationListener, IRecorder { if (data.event === 'highlightRequested') { if (data.params.selector) this.setHighlightedSelector(this._currentLanguage, data.params.selector); - if (data.params.ariaSnapshot) - this.setHighlightedAriaSnapshot(data.params.ariaSnapshot); + if (data.params.ariaTemplate) + this.setHighlightedAriaTemplate(data.params.ariaTemplate); return; } if (data.event === 'step') { @@ -169,7 +169,7 @@ export class Recorder implements InstrumentationListener, IRecorder { mode: this._mode, actionPoint, actionSelector, - ariaTemplate: this._highlightedElement.ariaSnapshot, + ariaTemplate: this._highlightedElement.ariaTemplate, language: this._currentLanguage, testIdAttributeName: this._contextRecorder.testIdAttributeName(), overlay: this._overlayState, @@ -245,8 +245,8 @@ export class Recorder implements InstrumentationListener, IRecorder { this._refreshOverlay(); } - setHighlightedAriaSnapshot(ariaSnapshot: ParsedYaml) { - this._highlightedElement = { ariaSnapshot }; + setHighlightedAriaTemplate(ariaTemplate: ParsedYaml) { + this._highlightedElement = { ariaTemplate }; this._refreshOverlay(); } diff --git a/packages/protocol/src/channels.ts b/packages/protocol/src/channels.ts index 9311e969af5cb..27c4242fe53d4 100644 --- a/packages/protocol/src/channels.ts +++ b/packages/protocol/src/channels.ts @@ -741,10 +741,12 @@ export type DebugControllerSetRecorderModeOptions = { }; export type DebugControllerSetRecorderModeResult = void; export type DebugControllerHighlightParams = { - selector: string, + selector?: string, + ariaTemplate?: string, }; export type DebugControllerHighlightOptions = { - + selector?: string, + ariaTemplate?: string, }; export type DebugControllerHighlightResult = void; export type DebugControllerHideHighlightParams = {}; diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index 7893bb10930b5..d9597c2295b2a 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -791,7 +791,8 @@ DebugController: highlight: parameters: - selector: string + selector: string? + ariaTemplate: string? hideHighlight: diff --git a/packages/recorder/src/recorder.tsx b/packages/recorder/src/recorder.tsx index 8b3a654defc74..50fc5174ecd52 100644 --- a/packages/recorder/src/recorder.tsx +++ b/packages/recorder/src/recorder.tsx @@ -120,7 +120,7 @@ export const Recorder: React.FC = ({ setAriaSnapshotErrors(errors); setAriaSnapshot(ariaSnapshot); if (!errors.length) - window.dispatch({ event: 'highlightRequested', params: { ariaSnapshot: fragment } }); + window.dispatch({ event: 'highlightRequested', params: { ariaTemplate: fragment } }); }, [mode]); const isRecording = mode === 'recording' || mode === 'recording-inspecting'; const locatorPlaceholder = isRecording ? '// Unavailable while recording' : (locator ? undefined : '// Pick element or type locator'); diff --git a/tests/library/debug-controller.spec.ts b/tests/library/debug-controller.spec.ts index b1a93a0138429..528b4fcec8c3c 100644 --- a/tests/library/debug-controller.spec.ts +++ b/tests/library/debug-controller.spec.ts @@ -20,6 +20,7 @@ import { createGuid } from '../../packages/playwright-core/lib/utils/crypto'; import { Backend } from '../config/debugControllerBackend'; import type { Browser, BrowserContext } from '@playwright/test'; import type * as channels from '@protocol/channels'; +import { roundBox } from '../page/pageTest'; type BrowserWithReuse = Browser & { _newContextForReuse: () => Promise }; type Fixtures = { @@ -279,3 +280,20 @@ test('should highlight inside iframe', async ({ backend, connectedBrowser }, tes await expect(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,` }); + + 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); +});