chore: allow highlighting aria template from extension (#33594)
This commit is contained in:
Родитель
a8af7cc435
Коммит
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);
|
||||||
|
});
|
||||||
|
|
Загрузка…
Ссылка в новой задаче