chore: use api selectors in codegen hover (#17855)

This commit is contained in:
Pavel Feldman 2022-10-05 16:59:34 -08:00 committed by GitHub
parent ed6ecbca2a
commit f2685cab95
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 58 additions and 22 deletions

View File

@ -90,8 +90,8 @@ class ProtocolHandler {
this._controller.on(DebugController.Events.BrowsersChanged, browsers => {
process.send!({ method: 'browsersChanged', params: { browsers } });
});
this._controller.on(DebugController.Events.InspectRequested, selector => {
process.send!({ method: 'inspectRequested', params: { selector } });
this._controller.on(DebugController.Events.InspectRequested, ({ selector, locators }) => {
process.send!({ method: 'inspectRequested', params: { selector, locators } });
});
}

View File

@ -334,6 +334,7 @@ scheme.RecorderSource = tObject({
scheme.DebugControllerInitializer = tOptional(tObject({}));
scheme.DebugControllerInspectRequestedEvent = tObject({
selector: tString,
locators: tArray(tType('NameValue')),
});
scheme.DebugControllerBrowsersChangedEvent = tObject({
browsers: tArray(tObject({

View File

@ -23,6 +23,9 @@ import type { InstrumentationListener } from './instrumentation';
import type { Playwright } from './playwright';
import { Recorder } from './recorder';
import { EmptyRecorderApp } from './recorder/recorderApp';
import { asLocator } from './isomorphic/locatorGenerators';
import type { Language } from './isomorphic/locatorGenerators';
import type { NameValue } from '../common/types';
const internalMetadata = serverSideCallMetadata();
@ -215,7 +218,8 @@ class InspectingRecorderApp extends EmptyRecorderApp {
}
override async setSelector(selector: string): Promise<void> {
this._debugController.emit(DebugController.Events.InspectRequested, selector);
const locators: NameValue[] = ['javascript', 'python', 'java', 'csharp'].map(l => ({ name: l, value: asLocator(l as Language, selector) }));
this._debugController.emit(DebugController.Events.InspectRequested, { selector, locators });
}
override async setSources(sources: Source[]): Promise<void> {

View File

@ -28,8 +28,8 @@ export class DebugControllerDispatcher extends Dispatcher<DebugController, chann
this._object.on(DebugController.Events.BrowsersChanged, browsers => {
this._dispatchEvent('browsersChanged', { browsers });
});
this._object.on(DebugController.Events.InspectRequested, selector => {
this._dispatchEvent('inspectRequested', { selector });
this._object.on(DebugController.Events.InspectRequested, ({ selector, locators }) => {
this._dispatchEvent('inspectRequested', { selector, locators });
});
this._object.on(DebugController.Events.SourcesChanged, sources => {
this._dispatchEvent('sourcesChanged', { sources });

View File

@ -17,6 +17,8 @@
import { stringifySelector } from '../isomorphic/selectorParser';
import type { ParsedSelector } from '../isomorphic/selectorParser';
import type { InjectedScript } from './injectedScript';
import { asLocator } from '../isomorphic/locatorGenerators';
import type { Language } from '../isomorphic/locatorGenerators';
type HighlightEntry = {
targetElement: Element,
@ -35,6 +37,7 @@ export class Highlight {
private _isUnderTest: boolean;
private _injectedScript: InjectedScript;
private _rafRequest: number | undefined;
private _language: Language = 'javascript';
constructor(injectedScript: InjectedScript) {
this._injectedScript = injectedScript;
@ -102,6 +105,10 @@ export class Highlight {
document.documentElement.appendChild(this._glassPaneElement);
}
setLanguage(language: Language) {
this._language = language;
}
runHighlightOnRaf(selector: ParsedSelector) {
if (this._rafRequest)
cancelAnimationFrame(this._rafRequest);
@ -145,7 +152,7 @@ export class Highlight {
color = '#dc6f6f7f';
else
color = elements.length > 1 ? '#f6b26b7f' : '#6fa8dc7f';
this._innerUpdateHighlight(elements, { color, tooltipText: selector });
this._innerUpdateHighlight(elements, { color, tooltipText: selector ? asLocator(this._language, selector) : '' });
}
maskElements(elements: Element[]) {

View File

@ -94,7 +94,8 @@ class Recorder {
return;
}
const { mode, actionPoint, actionSelector } = state;
const { mode, actionPoint, actionSelector, language } = state;
this._highlight.setLanguage(language);
if (mode !== this._mode) {
this._mode = mode;
this._clearHighlight();

View File

@ -19,7 +19,7 @@ import type { CSSComplexSelectorList } from '../isomorphic/cssParser';
import { parseAttributeSelector, parseSelector, stringifySelector } from '../isomorphic/selectorParser';
import type { ParsedSelector } from '../isomorphic/selectorParser';
type Language = 'javascript' | 'python' | 'java' | 'csharp';
export type Language = 'javascript' | 'python' | 'java' | 'csharp';
export type LocatorType = 'default' | 'role' | 'text' | 'label' | 'placeholder' | 'alt' | 'title' | 'test-id' | 'nth' | 'first' | 'last' | 'has-text';
export type LocatorBase = 'page' | 'locator' | 'frame-locator';

View File

@ -40,7 +40,7 @@ import { metadataToCallLog } from './recorder/recorderUtils';
import { Debugger } from './debugger';
import { EventEmitter } from 'events';
import { raceAgainstTimeout } from '../utils/timeoutRunner';
import type { LanguageGenerator } from './recorder/language';
import type { Language, LanguageGenerator } from './recorder/language';
type BindingSource = { frame: Frame, page: Page };
@ -59,6 +59,7 @@ export class Recorder implements InstrumentationListener {
private _handleSIGINT: boolean | undefined;
private _recorderAppFactory: (recorder: Recorder) => Promise<IRecorderApp>;
private _omitCallTracking = false;
private _currentLanguage: Language;
static showInspector(context: BrowserContext) {
Recorder.show(context, {}).catch(() => {});
@ -83,6 +84,7 @@ export class Recorder implements InstrumentationListener {
this._debugger = Debugger.lookup(context)!;
this._handleSIGINT = params.handleSIGINT;
context.instrumentation.addListener(this, context);
this._currentLanguage = this._contextRecorder.languageName();
}
private static async defaultRecorderAppFactory(recorder: Recorder) {
@ -111,6 +113,11 @@ export class Recorder implements InstrumentationListener {
this._debugger.resume(true);
return;
}
if (data.event === 'fileChanged') {
this._currentLanguage = this._contextRecorder.languageName(data.params.file);
this._refreshOverlay();
return;
}
if (data.event === 'resume') {
this._debugger.resume(false);
return;
@ -155,6 +162,7 @@ export class Recorder implements InstrumentationListener {
mode: this._mode,
actionPoint,
actionSelector,
language: this._currentLanguage
};
return uiState;
});
@ -381,6 +389,14 @@ class ContextRecorder extends EventEmitter {
this._generator?.restart();
}
languageName(id?: string): Language {
for (const lang of this._orderedLanguages) {
if (!id || lang.id === id)
return lang.highlighter;
}
return 'javascript';
}
async install() {
this._context.on(BrowserContext.Events.Page, page => this._onPage(page));
for (const page of this._context.pages())

View File

@ -15,7 +15,7 @@
*/
import type { BrowserContextOptions } from '../../..';
import type { LanguageGenerator, LanguageGeneratorOptions } from './language';
import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './language';
import { sanitizeDeviceOptions, toSignalMap } from './language';
import type { ActionInContext } from './codeGenerator';
import type { Action } from './recorderActions';
@ -31,7 +31,7 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
id: string;
groupName = '.NET C#';
name: string;
highlighter = 'csharp';
highlighter = 'csharp' as Language;
_mode: CSharpLanguageMode;
constructor(mode: CSharpLanguageMode) {

View File

@ -15,7 +15,7 @@
*/
import type { BrowserContextOptions } from '../../..';
import type { LanguageGenerator, LanguageGeneratorOptions } from './language';
import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './language';
import { toSignalMap } from './language';
import type { ActionInContext } from './codeGenerator';
import type { Action } from './recorderActions';
@ -30,7 +30,7 @@ export class JavaLanguageGenerator implements LanguageGenerator {
id = 'java';
groupName = 'Java';
name = 'Library';
highlighter = 'java';
highlighter = 'java' as Language;
generateAction(actionInContext: ActionInContext): string {
const action = actionInContext.action;

View File

@ -15,7 +15,7 @@
*/
import type { BrowserContextOptions } from '../../..';
import type { LanguageGenerator, LanguageGeneratorOptions } from './language';
import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './language';
import { sanitizeDeviceOptions, toSignalMap } from './language';
import type { ActionInContext } from './codeGenerator';
import type { Action } from './recorderActions';
@ -29,7 +29,7 @@ export class JavaScriptLanguageGenerator implements LanguageGenerator {
id: string;
groupName = 'Node.js';
name: string;
highlighter = 'javascript';
highlighter = 'javascript' as Language;
private _isTest: boolean;
constructor(isTest: boolean) {

View File

@ -15,8 +15,10 @@
*/
import type { BrowserContextOptions, LaunchOptions } from '../../..';
import type { Language } from '../isomorphic/locatorGenerators';
import type { ActionInContext } from './codeGenerator';
import type { Action, DialogSignal, DownloadSignal, NavigationSignal, PopupSignal } from './recorderActions';
export type { Language } from '../isomorphic/locatorGenerators';
export type LanguageGeneratorOptions = {
browserName: string;
@ -33,7 +35,7 @@ export interface LanguageGenerator {
id: string;
groupName: string;
name: string;
highlighter: string;
highlighter: Language;
generateHeader(options: LanguageGeneratorOptions): string;
generateAction(actionInContext: ActionInContext): string;
generateFooter(saveStorage: string | undefined): string;

View File

@ -15,7 +15,7 @@
*/
import type { BrowserContextOptions } from '../../..';
import type { LanguageGenerator, LanguageGeneratorOptions } from './language';
import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './language';
import { sanitizeDeviceOptions, toSignalMap } from './language';
import type { ActionInContext } from './codeGenerator';
import type { Action } from './recorderActions';
@ -29,7 +29,7 @@ export class PythonLanguageGenerator implements LanguageGenerator {
id: string;
groupName = 'Python';
name: string;
highlighter = 'python';
highlighter = 'python' as Language;
private _awaitPrefix: '' | 'await ';
private _asyncPrefix: '' | 'async ';

View File

@ -32,8 +32,7 @@ export function toTitleCase(name: string) {
}
export function toSnakeCase(name: string): string {
const toSnakeCaseRegex = /((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))/g;
return name.replace(toSnakeCaseRegex, `_$1`).toLowerCase();
return name.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase();
}
export function cssEscape(s: string): string {

View File

@ -610,6 +610,7 @@ export interface DebugControllerChannel extends DebugControllerEventTarget, Chan
}
export type DebugControllerInspectRequestedEvent = {
selector: string,
locators: NameValue[],
};
export type DebugControllerBrowsersChangedEvent = {
browsers: {

View File

@ -693,6 +693,9 @@ DebugController:
inspectRequested:
parameters:
selector: string
locators:
type: array
items: NameValue
browsersChanged:
parameters:

View File

@ -132,6 +132,7 @@ export const Recorder: React.FC<RecorderProps> = ({
<div>Target:</div>
<select className='recorder-chooser' hidden={!sources.length} value={fileId} onChange={event => {
setFileId(event.target.selectedOptions[0].value);
window.dispatch({ event: 'fileChanged', params: { file: event.target.selectedOptions[0].value } });
}}>{renderSourceOptions(sources)}</select>
<ToolbarButton icon='clear-all' title='Clear' disabled={!source || !source.text} onClick={() => {
window.dispatch({ event: 'clear' });

View File

@ -19,7 +19,7 @@ export type Point = { x: number, y: number };
export type Mode = 'inspecting' | 'recording' | 'none';
export type EventData = {
event: 'clear' | 'resume' | 'step' | 'pause' | 'setMode' | 'selectorUpdated';
event: 'clear' | 'resume' | 'step' | 'pause' | 'setMode' | 'selectorUpdated' | 'fileChanged';
params: any;
};
@ -27,6 +27,7 @@ export type UIState = {
mode: Mode;
actionPoint?: Point;
actionSelector?: string;
language: 'javascript' | 'python' | 'java' | 'csharp';
};
export type CallLogStatus = 'in-progress' | 'done' | 'error' | 'paused';

View File

@ -27,7 +27,7 @@ it('should highlight locator', async ({ page, isAndroid }) => {
const textPromise = waitForTestLog<string>(page, 'Highlight text for test: ');
const boxPromise = waitForTestLog<{ x: number, y: number, width: number, height: number }>(page, 'Highlight box for test: ');
await page.locator('input').highlight();
expect(await textPromise).toBe('input');
expect(await textPromise).toBe('locator(\'input\')');
let box1 = await page.locator('input').boundingBox();
let box2 = await boxPromise;